diff --git a/.agents/notes/implemented/feature/2026-09-07-deepseek-messages-adapter.i18n.yaml b/.agents/notes/implemented/feature/2026-09-07-deepseek-messages-adapter.i18n.yaml index a0c869b7a1..81ba63095f 100644 --- a/.agents/notes/implemented/feature/2026-09-07-deepseek-messages-adapter.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-09-07-deepseek-messages-adapter.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-09-07-deepseek-messages-adapter.md -2026-09-07-deepseek-messages-adapter.md: 80da9229d043246da097d9ce9458816d2b8506d9 -2026-09-07-deepseek-messages-adapter.zh.md: cc30bd6eb197d4de7d740e772e7661dc15eecb34 +2026-09-07-deepseek-messages-adapter.md: 9a92f9e95931bebfa9fa6e7e64fbd7d27ec8306c +2026-09-07-deepseek-messages-adapter.zh.md: 67f6c97c0a416cbddb7ec9d0de01e47e658036b7 diff --git a/.agents/notes/implemented/feature/2026-09-07-deepseek-messages-adapter.md b/.agents/notes/implemented/feature/2026-09-07-deepseek-messages-adapter.md index 80da9229d0..9a92f9e959 100644 --- a/.agents/notes/implemented/feature/2026-09-07-deepseek-messages-adapter.md +++ b/.agents/notes/implemented/feature/2026-09-07-deepseek-messages-adapter.md @@ -10,7 +10,7 @@ Deployments expose DeepSeek through Anthropic Messages gateways as well as chat- ## Decision -The [DeepSeek adapter](../../../../packages/llm/llm-deepseek/README.md) serves multiple protocols under one `deepseek-official` route and `llm-deepseek` settings namespace. `common/` shares configuration, the model catalog, capability resolution, and Files lifecycle; `protocols/chat-completions/` and `protocols/messages/` own serialization, stream conversion, and transport. Cordis YAML selects the implementation through `protocol`, defaulting to `messages`; shipped first-party compositions also declare this choice explicitly. The existing `PreparedAdapterCall` freezes protocol, endpoint, credential reference, and model capabilities; retries retain that generation while subsequent calls read new configuration. +The [DeepSeek adapter](../../../../packages/llm/llm-deepseek/README.md) serves multiple protocols under one `deepseek-official` route and `llm-deepseek` settings namespace. `common/` shares configuration, the model catalog, capability resolution, and Files lifecycle; `protocols/chat-completions/` and `protocols/messages/` own serialization, stream conversion, and transport. Cordis YAML selects the implementation through `protocol`, defaulting to `messages`; shipped first-party compositions inherit that default. The existing `PreparedAdapterCall` freezes protocol, endpoint, credential reference, and model capabilities; retries retain that generation while subsequent calls read new configuration. The adapter follows the [DeepSeek compatibility documentation](https://api-docs.deepseek.com/zh-cn/guides/anthropic_api) and [Anthropic streaming protocol](https://platform.claude.com/docs/en/build-with-claude/streaming). The pi-ai Anthropic implementation informed the handling of adjacent user messages, cumulative usage, fragmented tool arguments, and optional thinking signatures. DeepSeek effort uses `output_config.effort`; an Anthropic thinking token budget does not control DeepSeek effort. Both protocols forward explicit `temperature` values; DeepSeek accepts that parameter with thinking enabled and ignores its value, so callers retain their existing thinking configuration. diff --git a/.agents/notes/implemented/feature/2026-09-07-deepseek-messages-adapter.zh.md b/.agents/notes/implemented/feature/2026-09-07-deepseek-messages-adapter.zh.md index cc30bd6eb1..67f6c97c0a 100644 --- a/.agents/notes/implemented/feature/2026-09-07-deepseek-messages-adapter.zh.md +++ b/.agents/notes/implemented/feature/2026-09-07-deepseek-messages-adapter.zh.md @@ -10,7 +10,7 @@ Status: implemented ## 决策 -[DeepSeek 适配器](../../../../packages/llm/llm-deepseek/README.zh.md)通过一个 `deepseek-official` 路由和 `llm-deepseek` 设置命名空间支持多个协议。`common/` 共享配置、模型目录、能力解析和 Files 生命周期;`protocols/chat-completions/` 与 `protocols/messages/` 分别负责协议序列化、流转换和传输。`protocol` 配置在 Cordis YAML 中选择实现,默认 `messages`;随产品交付的官方组合也显式声明该选择。已有 `PreparedAdapterCall` 冻结协议、端点、凭据引用与模型能力,重试保持同一代配置,后续调用读取新配置。 +[DeepSeek 适配器](../../../../packages/llm/llm-deepseek/README.zh.md)通过一个 `deepseek-official` 路由和 `llm-deepseek` 设置命名空间支持多个协议。`common/` 共享配置、模型目录、能力解析和 Files 生命周期;`protocols/chat-completions/` 与 `protocols/messages/` 分别负责协议序列化、流转换和传输。`protocol` 配置在 Cordis YAML 中选择实现,默认 `messages`;随产品交付的官方组合继承该默认值。已有 `PreparedAdapterCall` 冻结协议、端点、凭据引用与模型能力,重试保持同一代配置,后续调用读取新配置。 适配器遵循 [DeepSeek 兼容文档](https://api-docs.deepseek.com/zh-cn/guides/anthropic_api) 和 [Anthropic 流协议](https://platform.claude.com/docs/en/build-with-claude/streaming)。pi-ai 的 Anthropic 实现为相邻用户消息、累计用量、工具参数分片和可选思考签名的处理提供参考。DeepSeek 通过 `output_config.effort` 设置思考强度;Anthropic 思考 token 预算不控制 DeepSeek 思考强度。两种协议都转发显式 `temperature` 值;DeepSeek 在启用思考时接受该参数但忽略其值,因此调用方可以保留已有思考配置。 diff --git a/apps/cli/tests/profiles/headless/credentials-snapshot.patch.yml b/apps/cli/tests/profiles/headless/credentials-snapshot.patch.yml index dd262fdd64..79757baa74 100644 --- a/apps/cli/tests/profiles/headless/credentials-snapshot.patch.yml +++ b/apps/cli/tests/profiles/headless/credentials-snapshot.patch.yml @@ -26,5 +26,4 @@ - id: llm-deepseek-keyless name: '@deepseek-ai/dsh-llm-deepseek' config: - protocol: messages baseURL: 'http://127.0.0.1:9' diff --git a/apps/cli/tests/profiles/headless/real-model.patch.yml b/apps/cli/tests/profiles/headless/real-model.patch.yml index 4c8cb63feb..e0471e17d5 100644 --- a/apps/cli/tests/profiles/headless/real-model.patch.yml +++ b/apps/cli/tests/profiles/headless/real-model.patch.yml @@ -8,7 +8,6 @@ - id: llm-deepseek config: - protocol: messages thinking: enabled reasoningEffort: max models: diff --git a/apps/cli/tests/profiles/headless/tests/fixtures/deepseek-defaults.patch.yml b/apps/cli/tests/profiles/headless/tests/fixtures/deepseek-defaults.patch.yml index d8f4ab99ac..6809f21a93 100644 --- a/apps/cli/tests/profiles/headless/tests/fixtures/deepseek-defaults.patch.yml +++ b/apps/cli/tests/profiles/headless/tests/fixtures/deepseek-defaults.patch.yml @@ -7,7 +7,6 @@ - id: llm-deepseek config: - protocol: messages baseURL: !!js process.env.DSH_SNAPSHOT_BASE_URL thinking: enabled reasoningEffort: low diff --git a/apps/cli/tests/profiles/headless/tests/harness.ts b/apps/cli/tests/profiles/headless/tests/harness.ts index 1ef8249ee9..05d4a9e814 100644 --- a/apps/cli/tests/profiles/headless/tests/harness.ts +++ b/apps/cli/tests/profiles/headless/tests/harness.ts @@ -60,7 +60,6 @@ export async function codingHarness(workdir: string, options: CodingHarnessOptio }) await ctx.plugin(AgentLoop, { agents: [] }) await ctx.plugin(LlmDeepSeek, { - protocol: 'messages', ...options.modelContextWindow === undefined ? {} : { models: [{ id: 'deepseek-v4-flash', contextWindow: options.modelContextWindow }], }, diff --git a/apps/cli/tests/profiles/headless/tests/ptc.e2e.ts b/apps/cli/tests/profiles/headless/tests/ptc.e2e.ts index 63a0ecc060..bc798fef1f 100644 --- a/apps/cli/tests/profiles/headless/tests/ptc.e2e.ts +++ b/apps/cli/tests/profiles/headless/tests/ptc.e2e.ts @@ -61,7 +61,7 @@ async function ptcModeHarness(cwd: string): Promise { await harness.plugin(ToolRuntime, { mode: 'ptc' }) await harness.plugin(AgentRegistry) await harness.plugin(AgentLoop, { agents: [] }) - await harness.plugin(LlmDeepSeek, { protocol: 'messages' }) + await harness.plugin(LlmDeepSeek) if (harness.get('subprocess') === undefined) await harness.plugin(LocalSubprocessRuntime) await harness.plugin(BashEnvPlugin) await harness.plugin(LocalBashExecutor, { cwd, timeoutMs: 30_000 }) @@ -82,7 +82,7 @@ async function workspacePtcModeHarness(): Promise { await harness.plugin(ToolFs) await harness.plugin(AgentInstructions, { maxBytes: 65536 }) await harness.plugin(AgentLoop, { agents: [] }) - await harness.plugin(LlmDeepSeek, { protocol: 'messages', models: [{ id: 'deepseek-v4-flash' }] }) + await harness.plugin(LlmDeepSeek, { models: [{ id: 'deepseek-v4-flash' }] }) await mountRuntime(harness) return harness } diff --git a/apps/web/tests/scaffold.ts b/apps/web/tests/scaffold.ts index aeabf2f2ae..ca4d801569 100644 --- a/apps/web/tests/scaffold.ts +++ b/apps/web/tests/scaffold.ts @@ -644,7 +644,7 @@ export async function launchWebScaffold(options: LaunchOptions = {}): Promise { config: { maxTokensAsSuccess: false }, }) expect(rows.find(row => row.id === 'llm-deepseek')?.config).toEqual({ - protocol: 'messages', apiKeyEnv: 'DEEPSEEK_API_KEY', defaultContextWindow: { __jsExpr: 'Number(process.env.DSH_CONTEXT_WINDOW ?? 1000000)' }, streamIdleTimeoutMs: 172800000, diff --git a/packages/bundle/web-app/README.i18n.yaml b/packages/bundle/web-app/README.i18n.yaml index 4e4da2a597..9dc338281e 100644 --- a/packages/bundle/web-app/README.i18n.yaml +++ b/packages/bundle/web-app/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/bundle/web-app/README.md -README.md: 84630f1d9142b1e2300baa0cde0513acdb602da9 -README.zh.md: 0488c74f433b0ef1d7e6b1677b7f50fd21a198a2 +README.md: 23ff6e925eb097eb99e5fa7ce8678c87c61aeb9c +README.zh.md: 79b6335798e2bf522ce2bc1e201be745bb91e085 diff --git a/packages/bundle/web-app/README.md b/packages/bundle/web-app/README.md index 84630f1d91..23ff6e925e 100644 --- a/packages/bundle/web-app/README.md +++ b/packages/bundle/web-app/README.md @@ -36,7 +36,7 @@ dsh --profile web --no-open --port 8080 After startup you see a `dsh web:` line whose root URL carries a fresh process token. Unless `--no-open` or an SSH session suppresses it, the default browser opens that URL, receives a signed cookie, and redirects to the clean root page. You know it worked when the page loads and you can chat with the agent. Two failures to expect: if the frontend is not built, startup stops with a build hint (`pnpm run build` in a checkout); if the browser cannot be opened, a credential-free diagnostic prints to stderr while the server keeps running — open the printed startup URL yourself. -**Settings → Models** displays **DeepSeek**, using `DEEPSEEK_API_KEY`. The default is `deepseek-official` / `deepseek-flash` (DeepSeek-V41-Flash). The [DeepSeek plugin](../../llm/llm-deepseek/README.md#choose-a-protocol) defaults to Chat Completions; set `protocol: messages` in Cordis YAML to switch. Web has no protocol selector. +**Settings → Models** displays **DeepSeek**, using `DEEPSEEK_API_KEY`. The default is `deepseek-official` / `deepseek-flash` (DeepSeek-V41-Flash). The [DeepSeek plugin](../../llm/llm-deepseek/README.md#choose-a-protocol) defaults to Messages; set `protocol: chat-completions` in Cordis YAML to select Chat Completions. Web has no protocol selector. Saved model selections override the composition default. Both protocols share `deepseek-official` and `llm-deepseek` settings, so switching preserves model selections and credential references. Endpoint overrides retain their values; the settings card lets users supply a compatible API address. diff --git a/packages/bundle/web-app/README.zh.md b/packages/bundle/web-app/README.zh.md index 0488c74f43..79b6335798 100644 --- a/packages/bundle/web-app/README.zh.md +++ b/packages/bundle/web-app/README.zh.md @@ -36,7 +36,7 @@ dsh --profile web --no-open --port 8080 启动后你会看到 `dsh web:` 行,其根 URL 携带新的进程 token。除非 `--no-open` 或 SSH 会话抑制,否则默认浏览器会打开该 URL、取得签名 cookie,再重定向到不含认证参数的根页面。页面加载且你可以与 agent 对话,就说明成功了。两种可预期的失败:前端未构建时,启动会以构建提示停止(checkout 中运行 `pnpm run build`);浏览器无法打开时,stderr 会打印不含凭据的诊断,但服务器会继续运行——请自行打开已打印的启动 URL。 -**设置 → 模型**显示 **DeepSeek**,使用 `DEEPSEEK_API_KEY`。默认模型为 `deepseek-official` / `deepseek-flash`(DeepSeek-V41-Flash)。[DeepSeek 插件](../../llm/llm-deepseek/README.zh.md#choose-a-protocol)默认使用 Chat Completions;在 Cordis YAML 中设置 `protocol: messages` 可切换协议。Web 不提供协议选择器。 +**设置 → 模型**显示 **DeepSeek**,使用 `DEEPSEEK_API_KEY`。默认模型为 `deepseek-official` / `deepseek-flash`(DeepSeek-V41-Flash)。[DeepSeek 插件](../../llm/llm-deepseek/README.zh.md#choose-a-protocol)默认使用 Messages;在 Cordis YAML 中设置 `protocol: chat-completions` 可选择 Chat Completions。Web 不提供协议选择器。 已保存的模型选择优先于组合默认值。两种协议共用 `deepseek-official` 与 `llm-deepseek` 设置,因此切换协议不改变模型选择或复制凭据。端点覆盖保持原值;设置卡片允许用户填写兼容的 API 地址。 diff --git a/packages/context/agent-instructions/tests/agent-instructions.e2e.ts b/packages/context/agent-instructions/tests/agent-instructions.e2e.ts index 60b3d8a196..4d91d49e29 100644 --- a/packages/context/agent-instructions/tests/agent-instructions.e2e.ts +++ b/packages/context/agent-instructions/tests/agent-instructions.e2e.ts @@ -41,7 +41,7 @@ async function harness(): Promise<{ ctx: Context; agent: Agent }> { await ctx.plugin(ToolFs) await ctx.plugin(AgentInstructions, { maxBytes: 65536 }) await ctx.plugin(AgentLoop, { agents: [] }) - await ctx.plugin(LlmDeepSeek, { protocol: 'messages', models: [{ id: 'deepseek-v4-flash' }] }) + await ctx.plugin(LlmDeepSeek, { models: [{ id: 'deepseek-v4-flash' }] }) const handle = await ctx.agents.create({ sessionId: SessionId('workspace-context-e2e-session'), meta: { cwd: workdir }, diff --git a/packages/core/agent-loop/tests/request-cache.e2e.ts b/packages/core/agent-loop/tests/request-cache.e2e.ts index e314543229..ceaa2b0b6d 100644 --- a/packages/core/agent-loop/tests/request-cache.e2e.ts +++ b/packages/core/agent-loop/tests/request-cache.e2e.ts @@ -47,7 +47,7 @@ async function loopHarness(): Promise { await created.plugin(ToolRuntime) await created.plugin(AgentRegistry) await created.plugin(AgentLoop, { agents: [] }) - await created.plugin(LlmDeepSeek, { protocol: 'messages' }) + await created.plugin(LlmDeepSeek) created.tools.register(defineContentToolFixture({ name: 'lookup', description: 'Look up the stored value for a key.', diff --git a/packages/experimental/auto-review/tests/auto-review.e2e.ts b/packages/experimental/auto-review/tests/auto-review.e2e.ts index 5be1219ba7..b730bd7c05 100644 --- a/packages/experimental/auto-review/tests/auto-review.e2e.ts +++ b/packages/experimental/auto-review/tests/auto-review.e2e.ts @@ -133,7 +133,7 @@ async function mount(ctx: Context, workspace: string, dshHome: string): Promise< systemPrompt: {}, tools: { mode: 'both' }, }) // No reasoning or output-budget override: use each shipped model's defaults. - await ctx.plugin(LlmDeepSeek, { protocol: 'messages', retryPolicy: { mode: 'normal', maxRetries: 0 } }) + await ctx.plugin(LlmDeepSeek, { retryPolicy: { mode: 'normal', maxRetries: 0 } }) await ctx.plugin(SandboxPolicyService, { mode: 'workspace-write', workspaceRoot: workspace }) await ctx.plugin(UnusedSandbox) await ctx.plugin(SandboxedFileSystem, { cwd: workspace }) diff --git a/packages/fs/tool-fs/tests/harness.ts b/packages/fs/tool-fs/tests/harness.ts index 0e6edc20e4..b556ec9270 100644 --- a/packages/fs/tool-fs/tests/harness.ts +++ b/packages/fs/tool-fs/tests/harness.ts @@ -16,7 +16,7 @@ export async function fsHarness(fsCwd: string, persona = ''): Promise { const ctx = new Context() await mountAgentLoopTestDependencies(ctx, { systemPrompt: { personaPrefix: persona } }) await ctx.plugin(AgentLoop, { agents: [] }) - await ctx.plugin(LlmDeepSeek, { protocol: 'messages' }) + await ctx.plugin(LlmDeepSeek) await ctx.plugin(LocalFileSystem, { cwd: fsCwd }) await ctx.plugin(FsPolicy) await ctx.plugin(ToolFs) diff --git a/packages/llm/llm-deepseek/README.i18n.yaml b/packages/llm/llm-deepseek/README.i18n.yaml index 11de639111..e2f3d50b08 100644 --- a/packages/llm/llm-deepseek/README.i18n.yaml +++ b/packages/llm/llm-deepseek/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/llm/llm-deepseek/README.md -README.md: 8c7d2d083a8a315ea2d7bf5190b77222901488b1 -README.zh.md: e7904b221f7573e997abba126892b83f73a5b4f6 +README.md: c03fa8ab740f9ffd315ee84b029e132bc40882b4 +README.zh.md: 80630f363aa4a065f0a2bbedc033fbca03fc641f diff --git a/packages/llm/llm-deepseek/README.md b/packages/llm/llm-deepseek/README.md index 8c7d2d083a..c03fa8ab74 100644 --- a/packages/llm/llm-deepseek/README.md +++ b/packages/llm/llm-deepseek/README.md @@ -36,7 +36,6 @@ Choose this adapter for DeepSeek's official API or a gateway that supports the s ```yaml - name: '@deepseek-ai/dsh-llm-deepseek' config: - protocol: messages # messages | chat-completions apiKeyEnv: DEEPSEEK_API_KEY # credential reference, resolved per request reasoningEffort: high # optional; off | low | high | max maxTokens: 256000 # optional per-request output cap @@ -84,7 +83,7 @@ To select Chat Completions explicitly, patch the existing plugin: protocol: chat-completions ``` -`protocol` defaults to `messages`, with official root `https://api.deepseek.com/anthropic`; `chat-completions` uses `https://api.deepseek.com`. Shipped first-party compositions explicitly select Messages. Neither protocol requires `baseURL`: its official default applies when both `baseURL` and `$DEEPSEEK_BASE_URL` are absent. Switching protocols retains endpoint overrides, so users must supply an address compatible with the selected protocol. An explicit `https://api.deepseek.com` override selects the Chat root: remove that override to use the official Messages default, or set it to `https://api.deepseek.com/anthropic`. Chat appends `/chat/completions`; Messages appends `/v1/messages`. Apart from trailing slashes, neither infers or removes custom path suffixes such as `/v1`. Both share the `llm-deepseek` settings section, `apiKeyEnv`, and `deepseek-official`, so saved model selections remain valid. +`protocol` defaults to `messages`, with official root `https://api.deepseek.com/anthropic`; `chat-completions` uses `https://api.deepseek.com`. Shipped first-party compositions inherit this default. Neither protocol requires `baseURL`: its official default applies when both `baseURL` and `$DEEPSEEK_BASE_URL` are absent. Switching protocols retains endpoint overrides, so users must supply an address compatible with the selected protocol. An explicit `https://api.deepseek.com` override selects the Chat root: remove that override to use the official Messages default, or set it to `https://api.deepseek.com/anthropic`. Chat appends `/chat/completions`; Messages appends `/v1/messages`. Apart from trailing slashes, neither infers or removes custom path suffixes such as `/v1`. Both share the `llm-deepseek` settings section, `apiKeyEnv`, and `deepseek-official`, so saved model selections remain valid. Messages sends text, thinking, tool calls, and tool results as content blocks, reasoning effort as `output_config.effort`, and images as Files references or inline base64. Models declaring `systemPromptUpdate: in-history` retain the initial top-level system and send new system snapshots after their corresponding user/tool-result turn; undeclared models use the latest snapshot as the top-level system. Replay metadata identifies the Messages format, model, and signatures. Chat requests serialize durable content without those signatures. Invalid Messages replay metadata emits a warning and omits signatures while retaining text and tool history. diff --git a/packages/llm/llm-deepseek/README.zh.md b/packages/llm/llm-deepseek/README.zh.md index e7904b221f..80630f363a 100644 --- a/packages/llm/llm-deepseek/README.zh.md +++ b/packages/llm/llm-deepseek/README.zh.md @@ -36,7 +36,6 @@ kind: "package-reference" ```yaml - name: '@deepseek-ai/dsh-llm-deepseek' config: - protocol: messages # messages | chat-completions apiKeyEnv: DEEPSEEK_API_KEY # credential reference, resolved per request reasoningEffort: high # optional; off | low | high | max maxTokens: 256000 # optional per-request output cap @@ -84,7 +83,7 @@ kind: "package-reference" protocol: chat-completions ``` -`protocol` 默认为 `messages`,官方根地址为 `https://api.deepseek.com/anthropic`;`chat-completions` 使用 `https://api.deepseek.com`。随产品交付的官方组合显式选择 Messages。两种协议都不要求填写 `baseURL`:当 `baseURL` 与 `$DEEPSEEK_BASE_URL` 均未设置时使用当前协议的官方默认值。切换协议保留已有端点覆盖,用户需要填写与选定协议兼容的地址。显式填写的 `https://api.deepseek.com` 是 Chat 根地址:删除该覆盖即可使用官方 Messages 默认值,也可以改填 `https://api.deepseek.com/anthropic`。Chat 追加 `/chat/completions`,Messages 追加 `/v1/messages`;除去末尾斜线之外,不推测或删除自定义路径中的 `/v1` 等后缀。两种协议共用 `llm-deepseek` 设置、`apiKeyEnv` 与 `deepseek-official`,因此已保存的模型选择仍然有效。 +`protocol` 默认为 `messages`,官方根地址为 `https://api.deepseek.com/anthropic`;`chat-completions` 使用 `https://api.deepseek.com`。随产品交付的官方组合继承该默认值。两种协议都不要求填写 `baseURL`:当 `baseURL` 与 `$DEEPSEEK_BASE_URL` 均未设置时使用当前协议的官方默认值。切换协议保留已有端点覆盖,用户需要填写与选定协议兼容的地址。显式填写的 `https://api.deepseek.com` 是 Chat 根地址:删除该覆盖即可使用官方 Messages 默认值,也可以改填 `https://api.deepseek.com/anthropic`。Chat 追加 `/chat/completions`,Messages 追加 `/v1/messages`;除去末尾斜线之外,不推测或删除自定义路径中的 `/v1` 等后缀。两种协议共用 `llm-deepseek` 设置、`apiKeyEnv` 与 `deepseek-official`,因此已保存的模型选择仍然有效。 Messages 以内容块发送文本、思考、工具调用和工具结果,以 `output_config.effort` 发送推理强度,并以 Files 引用或内联 base64 发送图片。声明 `systemPromptUpdate: in-history` 的模型保留初始顶层 system,在对应 user/tool-result 轮次之后发送新的 system 快照;未声明能力时,使用最新快照作为顶层 system。回放元数据记录 Messages 格式、模型和签名;Chat 请求只序列化持久化内容,不发送这些签名。无效的 Messages 回放元数据产生警告并省略签名,不丢弃文本或工具历史。 diff --git a/packages/llm/llm-deepseek/tests/messages/adapter.e2e.ts b/packages/llm/llm-deepseek/tests/messages/adapter.e2e.ts index 0c459b0adc..72e3ed6d53 100644 --- a/packages/llm/llm-deepseek/tests/messages/adapter.e2e.ts +++ b/packages/llm/llm-deepseek/tests/messages/adapter.e2e.ts @@ -2,14 +2,21 @@ * Real Messages round trips use the official root and require credentials. * System-update checks additionally require DEEPSEEK_IN_HISTORY_MODEL. */ +import { randomUUID } from 'node:crypto' import { mkdtemp, readFile, rm } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, describe, expect, it, vi } from 'vitest' import { Context, LoggerLevel } from '@deepseek-ai/cordis' +import Loader from '@deepseek-ai/cordis-plugin-loader' +import AgentRegistry from '@deepseek-ai/dsh-agent' import LocalAttachments from '@deepseek-ai/dsh-attachment-local' -import LlmRuntime, { createSystemMessage, createToolResultMessage, ReasoningEffortId } from '@deepseek-ai/dsh-llm' +import DeepSeekLlmApiExtensionRegistry from '@deepseek-ai/dsh-deepseek-llm-api-extensions' +import LlmRuntime, { BlockAssembler, createSystemMessage, createToolResultMessage, ReasoningEffortId } from '@deepseek-ai/dsh-llm' import type { Message } from '@deepseek-ai/dsh-llm' +import * as PluginPackageInventoryDeepSeek from '@deepseek-ai/dsh-plugin-package-inventory-deepseek' +import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' +import * as SessionLogDeepSeek from '@deepseek-ai/dsh-session-log-deepseek' import * as Messages from '../../src/index.ts' import { DeepSeekFilesClient, MESSAGES_FILES_BETA } from '../../src/common/files-api.ts' import { assemble, options, user } from './helpers.ts' @@ -29,7 +36,6 @@ async function boot(models?: Messages.Config['models']) { cleanups.push(() => ctx.fiber.dispose()) await ctx.plugin(LlmRuntime) await ctx.plugin(Messages, { - protocol: 'messages', baseURL: Messages.MESSAGES_BASE_URL, maxTokens: 4096, ...models === undefined ? {} : { models }, @@ -60,20 +66,24 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('DeepSeek Messages real API', () await reply('PROMPT_CLEARED') }) - it('uploads, lists, retrieves, and reuses a Files image across Messages requests', async () => { + it('uploads, lists, retrieves, reuses, and replaces a deleted Files image across Messages requests', async () => { const ctx = await boot() await ctx.plugin(LocalAttachments) const fetchImpl = globalThis.fetch const uploads: string[] = [] const bodies: string[] = [] const files = new DeepSeekFilesClient({ baseURL: Messages.MESSAGES_BASE_URL, protocol: 'messages', apiKey: process.env.DEEPSEEK_API_KEY as string, fetch: fetchImpl }) + const ownedFiles = new Set>() + cleanups.push(async () => { + for (const id of ownedFiles) await files.delete(id) + }) vi.stubGlobal('fetch', async (input: string | URL | Request, init?: RequestInit) => { const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url const response = await fetchImpl(input, init) if (url === `${Messages.MESSAGES_BASE_URL}/v1/files` && init?.method === 'POST' && response.ok) { const file = await response.clone().json() as { id: string } uploads.push(file.id) - cleanups.push(() => files.delete(Messages.DeepSeekFileId(file.id))) + ownedFiles.add(Messages.DeepSeekFileId(file.id)) } if (url === `${Messages.MESSAGES_BASE_URL}/v1/messages`) { expect(new Headers(init?.headers).get('anthropic-beta')).toBe(MESSAGES_FILES_BETA) @@ -110,6 +120,80 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('DeepSeek Messages real API', () page = await files.list({ after, limit: 1_000 }) } expect(page.data).toContainEqual(retrieved) + await files.delete(fileId) + ownedFiles.delete(fileId) + const recovered = await assemble(ctx.llm.stream(request), request.model) + expect(recovered.assembler.finish.kind).toBe('stop') + expect(recovered.message.content.filter(block => block.type === 'text').map(block => block.text).join('').toLowerCase()).toContain('red') + expect(uploads).toHaveLength(2) + expect(uploads[1]).not.toBe(fileId) + expect(bodies).toHaveLength(4) + expect(bodies[2]).toContain(`"file_id":"${fileId}"`) + expect(bodies[3]).toContain(`"file_id":"${uploads[1]}"`) + expect(bodies[3]).not.toContain('"type":"base64"') + }) + + it.each([false, true])('submits Loader package inventory and records HTTP acceptance with session-log enabled=%s', async (enabled) => { + const ctx = await boot() + await ctx.plugin(Loader) + await ctx.plugin(AgentRegistry) + await ctx.plugin(SessionStore) + await ctx.plugin(DeepSeekLlmApiExtensionRegistry) + await ctx.plugin(SessionLogDeepSeek, enabled ? { enabled: true } : {}) + ctx.baseUrl = import.meta.url + // Select the source module while Loader owns its active package entry. + ctx.loader.internal = { + version: 'v2', + async import(specifier: string) { + if (specifier !== '@deepseek-ai/dsh-plugin-package-inventory-deepseek') throw new Error(`unexpected Loader import: ${specifier}`) + return PluginPackageInventoryDeepSeek + }, + } as unknown as NonNullable + await ctx.loader.create({ name: '@deepseek-ai/dsh-plugin-package-inventory-deepseek' }) + await ctx.loader.await() + const packageIdentity = JSON.parse(await readFile(new URL('../../../plugin-package-inventory-deepseek/package.json', import.meta.url), 'utf8')) as { name: string; version: string } + const session = ctx.sessions.create(SessionId(`real-messages-extensions-${randomUUID()}`)) + session.append('turn/start', { turn: 1 }) + const fetchImpl = globalThis.fetch + let afterSeq: number = -1 + let throughSeq = 0 + let requests = 0 + vi.stubGlobal('fetch', async (input: string | URL | Request, init?: RequestInit) => { + const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url + if (url !== `${Messages.MESSAGES_BASE_URL}/v1/messages`) return fetchImpl(input, init) + if (typeof init?.body !== 'string') throw new Error('expected a JSON Messages request') + const body = JSON.parse(init.body) as Record + expect(body).toMatchObject({ dsh_plugin_packages: { + version: 1, packages: [{ name: packageIdentity.name, version: packageIdentity.version }], + } }) + if (enabled) { + expect(body).toMatchObject({ dsh_session_log: { + version: 1, sessionFormatVersion: session.header.version, session: { id: session.id }, + afterSeq, throughSeq, + events: Array.from({ length: throughSeq - afterSeq }, (_, index) => ({ seq: afterSeq + index + 1 })), + } }) + } else expect(body).not.toHaveProperty('dsh_session_log') + expect(SessionLogDeepSeek.acceptedThrough(session)).toBe(afterSeq) + const response = await fetchImpl(input, init) + expect(response.ok).toBe(true) + expect(SessionLogDeepSeek.acceptedThrough(session)).toBe(afterSeq) + requests += 1 + return response + }) + for (let run = 0; run < 2; run++) { + afterSeq = SessionLogDeepSeek.acceptedThrough(session) + throughSeq = Number(session.seq) - 1 + const assembler = new BlockAssembler() + for await (const chunk of ctx.llm.stream(options({ sessionId: session.id, reasoningEffort: ReasoningEffortId('off'), messages: [user('Reply with exactly PONG.')] }))) { + expect(SessionLogDeepSeek.acceptedThrough(session)).toBe(enabled ? throughSeq : -1) + assembler.push(chunk) + } + expect(assembler.finish.kind).toBe('stop') + expect(assembler.blocks().filter(block => block.type === 'text').map(block => block.text).join('')).toContain('PONG') + expect(requests).toBe(run + 1) + if (run === 0) session.append('step/start', { turn: 1, step: 1 }) + } + expect(session.snapshotEvents().filter(event => event.type === 'session-log-deepseek/delivery-accepted')).toHaveLength(enabled ? 2 : 0) }) it.each(['off', 'low', 'high', 'max'])('streams text with %s effort', async (effort) => { diff --git a/packages/llm/llm-deepseek/tests/messages/adapter.spec.ts b/packages/llm/llm-deepseek/tests/messages/adapter.spec.ts index 42187b65e4..c88c9b2d18 100644 --- a/packages/llm/llm-deepseek/tests/messages/adapter.spec.ts +++ b/packages/llm/llm-deepseek/tests/messages/adapter.spec.ts @@ -106,7 +106,7 @@ describe('direct Messages HTTP', () => { const prepare = vi.fn(async () => ({ fields: {}, accept })) const files = new DeepSeekFileStore() const llm = new DeepSeekMessagesAdapter({ - connection: () => Messages.resolveAdapterOptions({ protocol: 'messages', baseURL: source.url }), + connection: () => Messages.resolveAdapterOptions({ baseURL: source.url }), apiKey: () => Promise.resolve('test-key'), userId: () => 'test-user', attachments: () => undefined, imageAccess: () => undefined, files: () => files, prepareExtensions: prepare, @@ -122,11 +122,11 @@ describe('direct Messages HTTP', () => { it('freezes endpoint and defaults for a prepared call while the next call sees new settings', async () => { const first = await endpoint(), second = await endpoint() - let config = Messages.resolveAdapterOptions({ protocol: 'messages', baseURL: first.url, maxTokens: 10, models: [{ id: MODEL, systemPromptUpdate: 'in-history' }] }) + let config = Messages.resolveAdapterOptions({ baseURL: first.url, maxTokens: 10, models: [{ id: MODEL, systemPromptUpdate: 'in-history' }] }) const files = new DeepSeekFileStore() const llm = new DeepSeekMessagesAdapter({ connection: () => config, apiKey: snapshot => Promise.resolve(snapshot.maxTokens === 10 ? 'first' : 'second'), userId: () => 'user', attachments: () => undefined, imageAccess: () => undefined, files: () => files, prepareExtensions }) const prepared = await llm.prepareCall('deepseek-official', MODEL) - config = Messages.resolveAdapterOptions({ protocol: 'messages', baseURL: second.url, maxTokens: 20 }) + config = Messages.resolveAdapterOptions({ baseURL: second.url, maxTokens: 20 }) expect(prepared.model.systemPromptUpdate).toBe('in-history') expect((await llm.resolveModel('deepseek-official', MODEL)).systemPromptUpdate).toBeUndefined() await chunks(prepared.stream(options())) @@ -169,7 +169,7 @@ describe('Cordis provider composition', () => { const { ctx, home } = await context() vi.stubEnv('DEEPSEEK_API_KEY', 'test-key') await ctx.plugin(LlmRuntime) - await ctx.plugin(Messages, { protocol: 'messages', baseURL: http.url }) + await ctx.plugin(Messages, { baseURL: http.url }) const model = 'deepseek-v4-flash-vision-exp' const price = () => ctx.llm.imageRequestPricing('deepseek-official', model)! const dummy = { attachmentId: AttachmentId(`sha256:${'a'.repeat(64)}`), width: 1, height: 1, bytes: 3, mediaType: 'image/png' as const } @@ -369,7 +369,7 @@ describe('Cordis provider composition', () => { vi.stubEnv('DEEPSEEK_BASE_URL', http.url) vi.stubEnv('DEEPSEEK_API_KEY', 'env-key') await ctx.plugin(LlmRuntime) - const fiber = ctx.plugin(Messages, { protocol: 'messages' }) + const fiber = ctx.plugin(Messages) await fiber await chunks(ctx.llm.stream(options())) expect(http.requests[0]?.headers['x-api-key']).toBe('env-key') diff --git a/packages/llm/llm-deepseek/tests/messages/extensions.spec.ts b/packages/llm/llm-deepseek/tests/messages/extensions.spec.ts index 41af94634e..29c2ce10c0 100644 --- a/packages/llm/llm-deepseek/tests/messages/extensions.spec.ts +++ b/packages/llm/llm-deepseek/tests/messages/extensions.spec.ts @@ -33,7 +33,7 @@ async function boot() { cleanup.push(() => ctx.fiber.dispose()) await ctx.plugin(LlmRuntime) await ctx.plugin(DeepSeekLlmApiExtensionRegistry) - await ctx.plugin(DeepSeek, { protocol: 'messages', baseURL: 'https://messages.example.test/root' }) + await ctx.plugin(DeepSeek, { baseURL: 'https://messages.example.test/root' }) return ctx } diff --git a/packages/llm/llm-deepseek/tests/messages/files.spec.ts b/packages/llm/llm-deepseek/tests/messages/files.spec.ts index 221153695d..34aba64d37 100644 --- a/packages/llm/llm-deepseek/tests/messages/files.spec.ts +++ b/packages/llm/llm-deepseek/tests/messages/files.spec.ts @@ -36,7 +36,7 @@ function harness(config: Config = {}) { const attachments = { readImageRequest } as unknown as AttachmentStore const prepare = vi.fn(prepareExtensions) const adapter = new DeepSeekMessagesAdapter({ - connection: () => resolveAdapterOptions(Object.assign({ protocol: 'messages' as const, baseURL: 'https://gateway.example/custom' }, config)), + connection: () => resolveAdapterOptions(Object.assign({ baseURL: 'https://gateway.example/custom' }, config)), apiKey: async () => 'test-key', userId: () => 'test-user', attachments: () => attachments, imageAccess: () => ({ readonlyPath: '/workspace/image.png' }), files: () => files, prepareExtensions: prepare, }) diff --git a/packages/llm/llm-deepseek/tests/messages/fixtures/cordis.yml b/packages/llm/llm-deepseek/tests/messages/fixtures/cordis.yml index 0afe07d207..7658d5f74b 100644 --- a/packages/llm/llm-deepseek/tests/messages/fixtures/cordis.yml +++ b/packages/llm/llm-deepseek/tests/messages/fixtures/cordis.yml @@ -17,7 +17,6 @@ watch: false - name: '@deepseek-ai/dsh-llm-deepseek' config: - protocol: messages apiKeyEnv: DEEPSEEK_API_KEY baseURL: {{endpoint}} thinking: enabled diff --git a/packages/llm/llm-deepseek/tests/messages/helpers.ts b/packages/llm/llm-deepseek/tests/messages/helpers.ts index b7db5cdd97..1aff3c74f6 100644 --- a/packages/llm/llm-deepseek/tests/messages/helpers.ts +++ b/packages/llm/llm-deepseek/tests/messages/helpers.ts @@ -40,7 +40,7 @@ export async function assemble(stream: AsyncIterable, model = MODEL } export function adapter(config: Config = {}) { const files = new DeepSeekFileStore() - return new DeepSeekMessagesAdapter({ connection: () => resolveAdapterOptions(Object.assign({}, config, { protocol: 'messages' as const })), apiKey: () => Promise.resolve('test-key'), userId: () => 'test-user', attachments: () => undefined, imageAccess: () => undefined, files: () => files, prepareExtensions }) + return new DeepSeekMessagesAdapter({ connection: () => resolveAdapterOptions(config), apiKey: () => Promise.resolve('test-key'), userId: () => 'test-user', attachments: () => undefined, imageAccess: () => undefined, files: () => files, prepareExtensions }) } export async function server(reply: (response: ServerResponse, count: number) => void = response => response.end(sse(textEvents))) { const requests: { path: string; headers: IncomingHttpHeaders; body: Record }[] = [] diff --git a/packages/llm/llm-deepseek/tests/messages/serialize.spec.ts b/packages/llm/llm-deepseek/tests/messages/serialize.spec.ts index 91ff1e0175..347030f16d 100644 --- a/packages/llm/llm-deepseek/tests/messages/serialize.spec.ts +++ b/packages/llm/llm-deepseek/tests/messages/serialize.spec.ts @@ -12,14 +12,14 @@ import { readReplay, replayState } from '../../src/protocols/messages/replay.ts' import { serialize } from '../../src/protocols/messages/serialize.ts' import { MODEL, options, user } from './helpers.ts' -const connection = resolveAdapterOptions({ protocol: 'messages' }) +const connection = resolveAdapterOptions({}) const call = (id = 'a'): ContentBlock => ({ type: 'tool-call', id: ToolCallId(id), name: 'read', arguments: '{"path":"a"}' }) const assistant = (content: ContentBlock[]) => createAssistantMessage({ content, source: { provider: 'deepseek-official', model: MODEL } }) const result = (id = 'a', content: ContentBlock[] = [{ type: 'text', text: 'result' }]) => createToolResultMessage({ callId: ToolCallId(id), content, isError: false }) const body = (messages: Message[] = [user()], overrides: Partial = {}) => serialize( options({ messages, ...overrides }), connection, messages, new Map(), () => undefined, ) -const capable = resolveAdapterOptions({ protocol: 'messages', models: [{ id: MODEL, systemPromptUpdate: 'in-history' }] }) +const capable = resolveAdapterOptions({ models: [{ id: MODEL, systemPromptUpdate: 'in-history' }] }) const nativeBody = (messages: Message[]) => serialize(options({ messages }), capable, messages, new Map(), () => undefined) describe('Messages request conversion', () => { @@ -134,10 +134,10 @@ describe('Messages request conversion', () => { expect(body([user()], { purpose: 'session-title', temperature: 0 })).toMatchObject({ thinking: { type: 'disabled' }, temperature: 0 }) expect(body([user()], { temperature: 0 })).toMatchObject({ thinking: { type: 'enabled' }, temperature: 0 }) expect(() => body([user()], { reasoningEffort: ReasoningEffortId('medium') })).toThrow(/effort/) - const disabled = resolveAdapterOptions({ protocol: 'messages', thinking: 'disabled' }) + const disabled = resolveAdapterOptions({ thinking: 'disabled' }) expect(serialize(options(), disabled, [user()], new Map(), () => undefined).thinking).toEqual({ type: 'disabled' }) expect(() => serialize(options({ reasoningEffort: ReasoningEffortId('high') }), disabled, [user()], new Map(), () => undefined)).toThrow(/effort/) - const capped = resolveAdapterOptions({ protocol: 'messages', models: [{ id: MODEL, maxTokens: 321 }] }) + const capped = resolveAdapterOptions({ models: [{ id: MODEL, maxTokens: 321 }] }) expect(serialize(options(), capped, [user()], new Map(), () => undefined).max_tokens).toBe(321) }) @@ -230,8 +230,8 @@ describe('validated configuration', () => { expect(modelInfo(connection, 'deepseek-official', 'custom').systemPromptUpdate).toBeUndefined() expect(modelInfo(capable, 'deepseek-official', MODEL).systemPromptUpdate).toBe('in-history') expect(modelInfo(capable, 'deepseek-official', 'custom').systemPromptUpdate).toBeUndefined() - expect(modelInfo(resolveAdapterOptions({ protocol: 'messages', thinking: 'disabled' }), 'deepseek-official', MODEL).reasoning?.efforts).toMatchObject([{ id: 'off', name: 'Off' }]) - expect(resolveAdapterOptions({ protocol: 'messages', baseURL: 'https://example.com/anthropic///' }).baseURL).toBe('https://example.com/anthropic///') + expect(modelInfo(resolveAdapterOptions({ thinking: 'disabled' }), 'deepseek-official', MODEL).reasoning?.efforts).toMatchObject([{ id: 'off', name: 'Off' }]) + expect(resolveAdapterOptions({ baseURL: 'https://example.com/anthropic///' }).baseURL).toBe('https://example.com/anthropic///') }) it.each([ { thinking: 'disabled', reasoningEffort: 'high' }, { models: [{ id: '' }] }, @@ -242,7 +242,7 @@ describe('validated configuration', () => { { maxTokens: 0 }, { streamIdleTimeoutMs: 0 }, { models: [{ id: MODEL, systemPromptUpdate: 'unsupported' }] }, ])('rejects invalid composition input %#', (value) => { - expect(() => resolveAdapterOptions({ ...value, protocol: 'messages' } as Config)).toThrow() + expect(() => resolveAdapterOptions(value as Config)).toThrow() }) }) @@ -268,7 +268,7 @@ describe('Messages images', () => { expect(imagePricing(connection, MODEL, access).priceImages([ref])[0]?.visualTokens).toBe(0) }) it('offloads an oldest prefix using exact encoded bytes and preserves durable references', async () => { - const config = resolveAdapterOptions({ protocol: 'messages', + const config = resolveAdapterOptions({ maxInlineRequestImageBytes: 4, inlineImageOffloadByteQuantum: 1, maxImagesPerRequest: 2, imageOffloadCountQuantum: 1, }) const history = [result('a', [image, image])] diff --git a/packages/llm/llm-pi-ai/tests/adapter.e2e.ts b/packages/llm/llm-pi-ai/tests/adapter.e2e.ts index 0368a38324..b3190011cb 100644 --- a/packages/llm/llm-pi-ai/tests/adapter.e2e.ts +++ b/packages/llm/llm-pi-ai/tests/adapter.e2e.ts @@ -153,7 +153,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('llm-pi-ai e2e (real API)', () => const deepseekCtx = new Context() contexts.push(deepseekCtx) await deepseekCtx.plugin(LlmRuntime) - await deepseekCtx.plugin(LlmDeepSeek, { protocol: 'messages', thinking: 'disabled' }) + await deepseekCtx.plugin(LlmDeepSeek, { thinking: 'disabled' }) const piCtx = await harness(FLASH) diff --git a/packages/sdk/server/src/server.ts b/packages/sdk/server/src/server.ts index b9d312a070..031508993b 100644 --- a/packages/sdk/server/src/server.ts +++ b/packages/sdk/server/src/server.ts @@ -149,7 +149,7 @@ export class HarnessSdkJsonRpcServer { : ReasoningEffortId(params.reasoningEffort) if (!this.hasAdapterFor(provider)) { if (provider !== 'deepseek-official') throw new Error(`no adapter registered for provider "${provider}"`) - this.llmFiber = await this.ctx.plugin(LlmDeepSeek, { protocol: 'messages' }) + this.llmFiber = await this.ctx.plugin(LlmDeepSeek) } // Adapter presence was read from this service above; a successful fallback mount also requires it. const llm = this.ctx.get('llm') as LlmRuntime diff --git a/packages/session/session-title-first-prompt-llm/tests/provider.e2e.ts b/packages/session/session-title-first-prompt-llm/tests/provider.e2e.ts index 2be8265e50..3b16d7349b 100644 --- a/packages/session/session-title-first-prompt-llm/tests/provider.e2e.ts +++ b/packages/session/session-title-first-prompt-llm/tests/provider.e2e.ts @@ -19,7 +19,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('first-prompt title provider with const ctx = new Context() contexts.push(ctx) await ctx.plugin(LlmRuntime) - await ctx.plugin(LlmDeepSeek, { protocol: 'messages', thinking: 'disabled' }) + await ctx.plugin(LlmDeepSeek, { thinking: 'disabled' }) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) await ctx.plugin(SessionTitleService, { diff --git a/packages/subagent/subagent-spawn-in-process/tests/harness.ts b/packages/subagent/subagent-spawn-in-process/tests/harness.ts index 17749a9ce3..03442bfb11 100644 --- a/packages/subagent/subagent-spawn-in-process/tests/harness.ts +++ b/packages/subagent/subagent-spawn-in-process/tests/harness.ts @@ -28,7 +28,7 @@ export async function spawnHarness(workdir: string): Promise { systemPrompt: { personaPrefix: 'You are a coding agent. Report only when the requested work is done.' }, }) await ctx.plugin(AgentLoop, { agents: [] }) - await ctx.plugin(LlmDeepSeek, { protocol: 'messages' }) + await ctx.plugin(LlmDeepSeek) await ctx.plugin(LocalSubprocessRuntime) await ctx.plugin(BashEnvPlugin) await ctx.plugin(LocalBashExecutor, { cwd: workdir, timeoutMs: 30_000 }) diff --git a/packages/workflow/workflow-ptc/tests/workflow-ptc.e2e.ts b/packages/workflow/workflow-ptc/tests/workflow-ptc.e2e.ts index ef299ea500..a887dc8fc3 100644 --- a/packages/workflow/workflow-ptc/tests/workflow-ptc.e2e.ts +++ b/packages/workflow/workflow-ptc/tests/workflow-ptc.e2e.ts @@ -22,7 +22,7 @@ async function harness(): Promise { await mountAgentLoopTestDependencies(built) await mountPtcRuntime(built) await built.plugin(AgentLoop, { agents: [] }) - await built.plugin(LlmDeepSeek, { protocol: 'messages' }) + await built.plugin(LlmDeepSeek) await built.plugin(SubagentRuntime) await built.plugin(Spawn, { providerName: 'spawn' }) await built.plugin(PtcWorkflowEngine, { provider: 'spawn' }) diff --git a/snapshots/acp/escalation-approved/cordis.yml b/snapshots/acp/escalation-approved/cordis.yml index 43033e2ad9..85fca86823 100644 --- a/snapshots/acp/escalation-approved/cordis.yml +++ b/snapshots/acp/escalation-approved/cordis.yml @@ -5,7 +5,6 @@ - id: llm-deepseek name: '@deepseek-ai/dsh-llm-deepseek' config: - protocol: messages thinking: enabled reasoningEffort: max models: diff --git a/snapshots/sdk/bash-tool/cordis.yml b/snapshots/sdk/bash-tool/cordis.yml index 54d69e7eda..2ebc9c14c3 100644 --- a/snapshots/sdk/bash-tool/cordis.yml +++ b/snapshots/sdk/bash-tool/cordis.yml @@ -8,7 +8,6 @@ - id: llm-deepseek name: '@deepseek-ai/dsh-llm-deepseek' config: - protocol: messages thinking: enabled reasoningEffort: max diff --git a/snapshots/sdk/persistent-tools/cordis.yml b/snapshots/sdk/persistent-tools/cordis.yml index 94832fc624..78a1579766 100644 --- a/snapshots/sdk/persistent-tools/cordis.yml +++ b/snapshots/sdk/persistent-tools/cordis.yml @@ -11,7 +11,6 @@ - id: llm-deepseek name: '@deepseek-ai/dsh-llm-deepseek' config: - protocol: messages apiKeyEnv: DEEPSEEK_API_KEY streamIdleTimeoutMs: 172800000 models: diff --git a/snapshots/sdk/subagent-dsh-sdk-diagnostic/cordis.yml b/snapshots/sdk/subagent-dsh-sdk-diagnostic/cordis.yml index 4c939bee0c..63ea30da95 100644 --- a/snapshots/sdk/subagent-dsh-sdk-diagnostic/cordis.yml +++ b/snapshots/sdk/subagent-dsh-sdk-diagnostic/cordis.yml @@ -8,7 +8,6 @@ - id: llm-deepseek name: '@deepseek-ai/dsh-llm-deepseek' config: - protocol: messages thinking: enabled reasoningEffort: max diff --git a/snapshots/sdk/text-turn/cordis.yml b/snapshots/sdk/text-turn/cordis.yml index 4930df996a..d3111e59ad 100644 --- a/snapshots/sdk/text-turn/cordis.yml +++ b/snapshots/sdk/text-turn/cordis.yml @@ -10,7 +10,6 @@ - id: llm-deepseek name: '@deepseek-ai/dsh-llm-deepseek' config: - protocol: messages thinking: enabled reasoningEffort: max diff --git a/snapshots/session/deepseek-messages-system-prompt/cordis.yml b/snapshots/session/deepseek-messages-system-prompt/cordis.yml index d6a6aec80d..4102553fe8 100644 --- a/snapshots/session/deepseek-messages-system-prompt/cordis.yml +++ b/snapshots/session/deepseek-messages-system-prompt/cordis.yml @@ -1,6 +1,5 @@ - id: llm-deepseek config: - protocol: messages models: - id: deepseek-v4-flash systemPromptUpdate: in-history diff --git a/snapshots/session/deepseek-protocol-system-prompt/cordis.yml b/snapshots/session/deepseek-protocol-system-prompt/cordis.yml index d6a6aec80d..4102553fe8 100644 --- a/snapshots/session/deepseek-protocol-system-prompt/cordis.yml +++ b/snapshots/session/deepseek-protocol-system-prompt/cordis.yml @@ -1,6 +1,5 @@ - id: llm-deepseek config: - protocol: messages models: - id: deepseek-v4-flash systemPromptUpdate: in-history diff --git a/snapshots/session/empty-response-retry/cordis.yml b/snapshots/session/empty-response-retry/cordis.yml index e222a4caa3..91031aca5d 100644 --- a/snapshots/session/empty-response-retry/cordis.yml +++ b/snapshots/session/empty-response-retry/cordis.yml @@ -8,7 +8,6 @@ - id: llm-deepseek name: '@deepseek-ai/dsh-llm-deepseek' config: - protocol: messages thinking: enabled reasoningEffort: max retryPolicy: diff --git a/snapshots/session/fs-glob-sampling/cordis.yml b/snapshots/session/fs-glob-sampling/cordis.yml index 00106be249..42151991b5 100644 --- a/snapshots/session/fs-glob-sampling/cordis.yml +++ b/snapshots/session/fs-glob-sampling/cordis.yml @@ -2,7 +2,6 @@ - id: llm-deepseek name: '@deepseek-ai/dsh-llm-deepseek' config: - protocol: messages models: - id: deepseek-v4-pro diff --git a/snapshots/session/persistent-pwsh-tool-turn/cordis.yml b/snapshots/session/persistent-pwsh-tool-turn/cordis.yml index 72db5534b7..857a5cac9e 100644 --- a/snapshots/session/persistent-pwsh-tool-turn/cordis.yml +++ b/snapshots/session/persistent-pwsh-tool-turn/cordis.yml @@ -2,7 +2,6 @@ - id: llm-deepseek name: '@deepseek-ai/dsh-llm-deepseek' config: - protocol: messages models: - id: deepseek-v4-pro diff --git a/snapshots/session/pwsh-tool-turn/cordis.yml b/snapshots/session/pwsh-tool-turn/cordis.yml index ee3c796b8f..3fbcae1aa4 100644 --- a/snapshots/session/pwsh-tool-turn/cordis.yml +++ b/snapshots/session/pwsh-tool-turn/cordis.yml @@ -2,7 +2,6 @@ - id: llm-deepseek name: '@deepseek-ai/dsh-llm-deepseek' config: - protocol: messages models: - id: deepseek-v4-pro diff --git a/snapshots/session/system-prompt-in-history/cordis.yml b/snapshots/session/system-prompt-in-history/cordis.yml index 1fb9036ae8..51196acfd0 100644 --- a/snapshots/session/system-prompt-in-history/cordis.yml +++ b/snapshots/session/system-prompt-in-history/cordis.yml @@ -5,7 +5,6 @@ - id: llm-deepseek name: '@deepseek-ai/dsh-llm-deepseek' config: - protocol: messages thinking: enabled reasoningEffort: max diff --git a/snapshots/session/text-turn/cordis.yml b/snapshots/session/text-turn/cordis.yml index f2bfd961b2..e98b2f8b1b 100644 --- a/snapshots/session/text-turn/cordis.yml +++ b/snapshots/session/text-turn/cordis.yml @@ -5,7 +5,6 @@ - id: llm-deepseek name: '@deepseek-ai/dsh-llm-deepseek' config: - protocol: messages thinking: enabled reasoningEffort: max models: