From a9c006f9c3d2d2edc8a348368da5cf4fbfc40cb2 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Tue, 20 Oct 2026 21:09:09 +0800 Subject: [PATCH] refactor(agent): own model selection on each agent --- apps/cli/composition.md | 3 - apps/cli/tests/built-bin.e2e.ts | 1 - apps/cli/tests/fixtures/dsh-badge/snapshot.ts | 3 +- benchmarks/package.json | 1 - .../session-open/session-open.worker.ts | 2 - docs/architecture.i18n.yaml | 4 +- docs/architecture.md | 2 +- docs/architecture.zh.md | 2 +- docs/capability-seams.i18n.yaml | 4 +- docs/capability-seams.md | 9 - docs/capability-seams.zh.md | 12 -- docs/config-catalog.md | 21 +-- docs/config-catalog.zh.md | 21 +-- docs/event-producer-consumer.i18n.yaml | 6 +- docs/event-producer-consumer.md | 28 +-- docs/event-producer-consumer.zh.md | 28 +-- docs/module-graph.md | 24 +-- docs/module-graph.zh.md | 24 +-- docs/persistence-catalog.i18n.yaml | 12 +- docs/persistence-catalog.md | 6 +- docs/persistence-catalog.zh.md | 6 +- docs/persistence-schema.json | 2 +- docs/subsystems/core.i18n.yaml | 4 +- docs/subsystems/core.md | 69 +++---- docs/subsystems/core.zh.md | 69 +++---- .../session-model-selection/guide.i18n.yaml | 8 +- .../session-model-selection/guide.md | 14 +- .../session-model-selection/guide.zh.md | 14 +- packages/acp/acp/package.json | 6 +- packages/acp/acp/src/index.ts | 3 +- packages/acp/acp/src/model-control.ts | 12 +- packages/acp/acp/src/session.ts | 6 +- packages/acp/acp/tests/bridge.spec.ts | 2 +- packages/acp/acp/tests/model-control.spec.ts | 11 +- packages/acp/acp/tsconfig.json | 3 - .../api/job-controller/tests/host-harness.ts | 3 +- .../job-controller/tests/observe.host.spec.ts | 3 +- .../api/session-controller/README.i18n.yaml | 4 +- packages/api/session-controller/README.md | 2 +- packages/api/session-controller/README.zh.md | 2 +- packages/api/session-controller/package.json | 6 +- packages/api/session-controller/src/agent.ts | 6 +- .../api/session-controller/src/commands.ts | 4 +- packages/api/session-controller/src/index.ts | 2 - packages/api/session-controller/src/types.ts | 2 +- .../tests/archived-session-gate.host.spec.ts | 3 +- .../commands-queue-attachment.host.spec.ts | 3 +- .../tests/commands-upload-file.host.spec.ts | 8 +- .../tests/controller.host.spec.ts | 2 - .../tests/session-cold.host.spec.ts | 58 +++--- .../tests/session-fork.host.spec.ts | 4 +- .../tests/session-models.host.spec.ts | 31 ++-- .../tests/session-projections.host.spec.ts | 4 - .../session-controller/tsconfig.client.json | 79 ++------ .../api/session-controller/tsconfig.host.json | 3 - packages/bundle/base/cordis.patch.yml | 3 - packages/bundle/base/package.json | 4 +- packages/bundle/base/tsconfig.json | 3 - packages/bundle/headless/package.json | 6 +- packages/bundle/headless/src/index.ts | 20 +- .../bundle/headless/tests/headless.spec.ts | 10 +- packages/bundle/headless/tsconfig.json | 3 - packages/bundle/sdk-minimal/cordis.patch.yml | 2 - packages/bundle/sdk-minimal/package.json | 6 +- .../sdk-minimal/tests/sdk-minimal.spec.ts | 1 - packages/bundle/sdk-minimal/tsconfig.json | 3 - packages/client/ui-conversation/package.json | 3 +- .../ui-conversation/src/client/input/hub.ts | 2 +- .../ui-conversation/tsconfig.client.json | 3 - .../client/ui-model-selection/package.json | 3 +- .../src/client/directory.ts | 2 +- .../tests/browser-plugin.client.spec.ts | 2 +- .../client/ui-model-selection/tsconfig.json | 3 - .../tests/compaction-loop-repro.spec.ts | 4 +- .../context/agent-instructions/package.json | 3 +- .../tests/agent-instructions.spec.ts | 2 - packages/context/time-context/package.json | 1 - .../time-context/tests/time-context.spec.ts | 8 +- .../tmux-context/tests/tmux-context.spec.ts | 3 +- packages/core/README.i18n.yaml | 4 +- packages/core/README.md | 1 - packages/core/README.zh.md | 1 - .../core/agent-default-model/README.i18n.yaml | 8 +- packages/core/agent-default-model/README.md | 4 +- .../core/agent-default-model/README.zh.md | 4 +- packages/core/agent-loop/README.i18n.yaml | 8 +- packages/core/agent-loop/README.md | 4 +- packages/core/agent-loop/README.zh.md | 4 +- packages/core/agent-loop/package.json | 6 +- packages/core/agent-loop/src/agent.ts | 14 +- packages/core/agent-loop/src/index.ts | 30 ++- .../agent-loop/tests/agent-initiator.spec.ts | 4 - packages/core/agent-loop/tests/agent.spec.ts | 2 - packages/core/agent-loop/tests/cancel.spec.ts | 3 - .../tests/config-session-id.spec.ts | 12 +- .../tests/contract-regressions.spec.ts | 10 +- .../agent-loop/tests/coverage-edges.spec.ts | 2 - packages/core/agent-loop/tests/inbox.spec.ts | 2 + .../agent-loop/tests/interception.spec.ts | 2 - packages/core/agent-loop/tests/loop.spec.ts | 8 +- packages/core/agent-loop/tests/model.spec.ts | 101 +++++++++++ .../core/agent-loop/tests/properties.spec.ts | 2 - .../agent-loop/tests/request-cache.e2e.ts | 2 - .../agent-loop/tests/request-error.spec.ts | 2 - .../agent-loop/tests/request-freeze.spec.ts | 6 +- .../tests/request-reconstruction.spec.ts | 18 +- packages/core/agent-loop/tests/resume.spec.ts | 6 - .../agent-loop/tests/scope-lifecycle.spec.ts | 2 - .../core/agent-loop/tests/settings.spec.ts | 2 - .../agent-loop/tests/shutdown-drain.spec.ts | 2 - .../tests/system-prompt-admission.spec.ts | 28 ++- .../core/agent-loop/tests/tool-calls.spec.ts | 4 - .../core/agent-loop/tests/tool-order.spec.ts | 2 - .../agent-loop/tests/tool-updates.spec.ts | 2 - packages/core/agent-loop/tsconfig.json | 3 - packages/core/agent-model/README.i18n.yaml | 46 ----- packages/core/agent-model/README.md | 105 ----------- packages/core/agent-model/README.zh.md | 105 ----------- packages/core/agent-model/package.json | 55 ------ packages/core/agent-model/src/index.ts | 52 ------ packages/core/agent-model/src/notice.ts | 60 ------ packages/core/agent-model/src/projection.ts | 43 ----- packages/core/agent-model/src/selection.ts | 35 ---- packages/core/agent-model/src/service.ts | 77 -------- packages/core/agent-model/src/types.ts | 31 ---- packages/core/agent-model/tsconfig.json | 30 --- packages/core/agent/README.i18n.yaml | 40 ++-- packages/core/agent/README.md | 38 +++- packages/core/agent/README.zh.md | 36 +++- packages/core/agent/package.json | 1 - packages/core/agent/src/index.ts | 2 + packages/core/agent/src/model-selection.ts | 171 ++++++++++++++++++ packages/core/agent/src/runtime-types.ts | 25 ++- packages/core/agent/src/types.ts | 15 ++ packages/core/agent/tests/agent.spec.ts | 2 + .../agent/tests/archive-admission.spec.ts | 2 + .../tests/model-selection.spec.ts | 139 +++----------- packages/core/tools/package.json | 3 +- packages/core/tools/tests/tools.spec.ts | 2 - .../tool-present/tests/present.spec.ts | 3 +- .../browser-use-runtime/package.json | 3 +- .../browser-use-runtime/tests/mcp.spec.ts | 4 +- .../tests/resources.spec.ts | 3 +- .../browser-use-stagehand-native/package.json | 1 - .../tests/loader-composition.spec.ts | 3 +- .../claude-code-mods/package.json | 1 - .../tests/loader-composition.spec.ts | 3 +- .../client-ui-agent-team/package.json | 2 +- .../src/client/TeamAction.tsx | 2 +- .../client-ui-agent-team/tsconfig.json | 56 ++---- .../computer-use-cua-driver-mcp/package.json | 1 - .../tests/composition.spec.ts | 3 +- .../package.json | 1 - .../tests/loader-composition.spec.ts | 3 +- packages/experimental/cua-macos/package.json | 1 - .../cua-macos/tests/large-image-ptc.spec.ts | 3 +- .../tests/loader-composition.spec.ts | 3 +- .../tests/loader-composition.spec.ts | 5 +- .../tool-terminal/tests/tools.spec.ts | 5 +- .../experimental/tool-worktree/package.json | 3 +- .../tests/loader-composition.spec.ts | 3 +- .../experimental/worktree/tests/harness.ts | 5 +- .../extensions/tool-cordis/src/api-catalog.ts | 23 --- .../tests/command-feedback.spec.ts | 3 +- .../tests/loader-composition.spec.ts | 3 +- .../tests/tools.spec.ts | 3 +- .../command-goal/tests/command-goal.spec.ts | 3 +- packages/goal/goal/tests/goal.spec.ts | 3 +- packages/goal/goal/tests/projection.spec.ts | 3 +- .../goal/tool-goal/tests/tool-goal.spec.ts | 3 +- .../tool-ask-user/tests/tool-ask-user.spec.ts | 2 + packages/jobs/jobs-local/tests/events.spec.ts | 3 +- packages/jobs/jobs-local/tests/jobs.spec.ts | 3 +- packages/llm/llm-deepseek/package.json | 1 - .../tests/account-routing.spec.ts | 7 +- .../llm/llm-deepseek/tests/adapter.spec.ts | 3 +- .../llm-deepseek/tests/fixtures/cordis.yml | 1 - packages/llm/llm-retry/package.json | 3 +- .../tests/loader-composition.spec.ts | 4 +- packages/llm/llm-retry/tests/retry.spec.ts | 6 +- packages/plan/plan-mode/package.json | 3 +- .../plan/plan-mode/tests/integration.spec.ts | 2 - .../preset/agent-preset-registry/package.json | 3 +- .../agent-preset-registry/tests/harness.ts | 2 - .../references/packages.md | 1 - packages/schedule/schedule/tests/harness.ts | 5 +- packages/sdk/server/package.json | 2 - packages/sdk/server/src/index.ts | 3 +- packages/sdk/server/src/server.ts | 11 +- .../sdk/server/tests/plugin-shape.spec.ts | 2 +- packages/sdk/server/tests/resume.spec.ts | 34 +++- packages/sdk/server/tests/server.spec.ts | 2 - packages/sdk/server/tsconfig.json | 3 - packages/session/session-title/package.json | 3 +- .../tests/loader-composition.spec.ts | 3 +- .../tool-bash-persistent/tests/tools.spec.ts | 3 +- .../shell/tool-bash/tests/background.spec.ts | 4 +- .../tests/loader-composition.spec.ts | 3 +- .../tool-pwsh-persistent/tests/tools.spec.ts | 3 +- .../shell/tool-pwsh/tests/background.spec.ts | 4 +- packages/skill/skill-office/package.json | 3 +- .../tests/progressive-loading.spec.ts | 2 - .../skill/tool-skill/tests/tool-skill.spec.ts | 4 +- .../subagent-fork-in-process/package.json | 3 +- .../tests/subagent-fork-in-process.spec.ts | 3 +- .../subagent-spawn-in-process/package.json | 3 +- .../tests/subagent-spawn-in-process.spec.ts | 3 +- packages/subagent/subagent/package.json | 3 +- .../subagent/tests/archive-admission.spec.ts | 3 +- .../tests/external-activation-helpers.ts | 3 +- .../subagent/tests/list-children.spec.ts | 3 - .../subagent/subagent/tests/service.spec.ts | 5 +- packages/subagent/tool-subagent/package.json | 3 +- .../terminal-bash/tests/index.spec.ts | 11 +- .../terminal-bash/tests/local.spec.ts | 5 +- .../terminal/terminal/tests/service.spec.ts | 3 +- .../agent-loop-testkit/README.i18n.yaml | 6 +- .../test-support/agent-loop-testkit/README.md | 7 +- .../agent-loop-testkit/README.zh.md | 5 +- .../agent-loop-testkit/package.json | 6 +- .../agent-loop-testkit/src/index.ts | 5 +- .../agent-loop-testkit/src/model.ts | 13 ++ .../agent-loop-testkit/tsconfig.json | 3 - .../test-support/loader-smoke/package.json | 1 - .../tests/fixtures/cli-mock-llm.ts | 7 +- .../tests/loader-composition.spec.ts | 5 +- .../tool-workflow/tests/tool-workflow.spec.ts | 8 +- pnpm-lock.yaml | 125 +------------ python/sdk-runtime/package.json | 1 - scripts/gen-cordis-catalog.ts | 1 - scripts/gen-doc-graphs.ts | 10 +- scripts/no-unknown-casts.baseline.json | 4 +- scripts/type-equiv.manifest.json | 9 +- .../model-switch-compaction/driver.mjs | 4 +- .../model-switch-driver.mjs | 10 +- tsconfig.base.json | 3 - tsconfig.host.json | 1 - 237 files changed, 1051 insertions(+), 1734 deletions(-) create mode 100644 packages/core/agent-loop/tests/model.spec.ts delete mode 100644 packages/core/agent-model/README.i18n.yaml delete mode 100644 packages/core/agent-model/README.md delete mode 100644 packages/core/agent-model/README.zh.md delete mode 100644 packages/core/agent-model/package.json delete mode 100644 packages/core/agent-model/src/index.ts delete mode 100644 packages/core/agent-model/src/notice.ts delete mode 100644 packages/core/agent-model/src/projection.ts delete mode 100644 packages/core/agent-model/src/selection.ts delete mode 100644 packages/core/agent-model/src/service.ts delete mode 100644 packages/core/agent-model/src/types.ts delete mode 100644 packages/core/agent-model/tsconfig.json create mode 100644 packages/core/agent/src/model-selection.ts rename packages/core/{agent-model => agent}/tests/model-selection.spec.ts (59%) create mode 100644 packages/test-support/agent-loop-testkit/src/model.ts diff --git a/apps/cli/composition.md b/apps/cli/composition.md index 334b388fba..cda1e0bb96 100644 --- a/apps/cli/composition.md +++ b/apps/cli/composition.md @@ -40,8 +40,6 @@ flowchart LR cfg --> plugin_dsh_base_user_questions plugin_dsh_base_agent["agent
@deepseek-ai/dsh-agent"] cfg --> plugin_dsh_base_agent - plugin_dsh_base_agent_model["agent-model
@deepseek-ai/dsh-agent-model"] - cfg --> plugin_dsh_base_agent_model plugin_dsh_base_plugin_package_inventory_deepseek["plugin-package-inventory-deepseek
@deepseek-ai/dsh-plugin-package-inventory-deepseek"] cfg --> plugin_dsh_base_plugin_package_inventory_deepseek plugin_dsh_base_agent_default_model["agent-default-model
@deepseek-ai/dsh-agent-default-model"] @@ -220,7 +218,6 @@ flowchart LR | `session-title-llm` | `@deepseek-ai/dsh-session-title-first-prompt-llm` | | `user-questions` | `@deepseek-ai/dsh-user-questions` | | `agent` | `@deepseek-ai/dsh-agent` | -| `agent-model` | `@deepseek-ai/dsh-agent-model` | | `plugin-package-inventory-deepseek` | `@deepseek-ai/dsh-plugin-package-inventory-deepseek` | | `agent-default-model` | `@deepseek-ai/dsh-agent-default-model` | | `jobs` | `@deepseek-ai/dsh-jobs-local` | diff --git a/apps/cli/tests/built-bin.e2e.ts b/apps/cli/tests/built-bin.e2e.ts index c27a10446a..cd8b5aad9c 100644 --- a/apps/cli/tests/built-bin.e2e.ts +++ b/apps/cli/tests/built-bin.e2e.ts @@ -1270,7 +1270,6 @@ describe.skipIf(!existsSync(dshBin))('dsh BUILT bin (node lib/bin.js, no tsx)', ['tools', '@deepseek-ai/dsh-tools'], ['mcp-resources', '@deepseek-ai/dsh-mcp-resources'], ['agent', '@deepseek-ai/dsh-agent'], - ['agent-model', '@deepseek-ai/dsh-agent-model'], ['llm-retry', '@deepseek-ai/dsh-llm-retry'], ['jobs', '@deepseek-ai/dsh-jobs-local'], ['agent-loop', '@deepseek-ai/dsh-agent-loop'], diff --git a/apps/cli/tests/fixtures/dsh-badge/snapshot.ts b/apps/cli/tests/fixtures/dsh-badge/snapshot.ts index e6d245d40a..ebd3bdec6b 100644 --- a/apps/cli/tests/fixtures/dsh-badge/snapshot.ts +++ b/apps/cli/tests/fixtures/dsh-badge/snapshot.ts @@ -5,7 +5,7 @@ import { boot, loadOverlayPatches } from '@deepseek-ai/dsh-app-boot' import { SessionId } from '@deepseek-ai/dsh-session' import type {} from '@deepseek-ai/dsh-skill' import type {} from '@deepseek-ai/dsh-tools' -import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' const overlayPath = process.argv[2] if (overlayPath === undefined) throw new Error('dsh-badge snapshot requires an overlay path') @@ -31,6 +31,7 @@ try { id: agentId, options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'idle', send: () => {}, diff --git a/benchmarks/package.json b/benchmarks/package.json index 5f03e3096b..d36d79bfdb 100644 --- a/benchmarks/package.json +++ b/benchmarks/package.json @@ -12,7 +12,6 @@ "@deepseek-ai/dsh-llm-replay": "workspace:*", "@deepseek-ai/cordis": "workspace:~", "@deepseek-ai/dsh-agent": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*", "@deepseek-ai/dsh-agent-loop": "workspace:*", "@deepseek-ai/dsh-agent-loop-testkit": "workspace:*", "@deepseek-ai/dsh-agent-preset-registry": "workspace:*", diff --git a/benchmarks/session-open/session-open.worker.ts b/benchmarks/session-open/session-open.worker.ts index 97407cc2aa..5e0b163aa1 100644 --- a/benchmarks/session-open/session-open.worker.ts +++ b/benchmarks/session-open/session-open.worker.ts @@ -4,7 +4,6 @@ import { performance } from 'node:perf_hooks' import { scheduler } from 'node:timers/promises' import { Context } from '@deepseek-ai/cordis' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit' import { agentPresetProjectionDefinition } from '@deepseek-ai/dsh-agent-preset-registry' import SessionStore, { @@ -162,7 +161,6 @@ class SessionBenchmarkHost { else { await ctx.plugin(SessionProjectionRegistry) await ctx.plugin(AgentRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SessionStore) } await installProjectionSet(ctx) diff --git a/docs/architecture.i18n.yaml b/docs/architecture.i18n.yaml index 87d1ed6356..9118bb6bf5 100644 --- a/docs/architecture.i18n.yaml +++ b/docs/architecture.i18n.yaml @@ -27,8 +27,8 @@ en: f270fd4ca334d417 zh: 3c789548e555508e /deepseek-harness-architecture/session-log: - en: 5e5803bf920e775c - zh: c159b74a74aa34fa + en: 8a7f19b95d426c99 + zh: d5bb0538404e20ea /deepseek-harness-architecture/capability-seams: en: a5bc20c3f2a1b370 zh: 91773a281410b2f4 diff --git a/docs/architecture.md b/docs/architecture.md index 5be5b58cac..8b5100bfaa 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -123,7 +123,7 @@ Session consumers know only the current logical format. Header-only `stat` and ` **Model-visible means logged.** Every model request must be reconstructable from the log. New model-visible inputs require session events. Plugins changing existing message content register [pure message projections](subsystems/session.md#plugin-owned-message-projections); detached readers supply the same definitions explicitly. Tool changes are capability-independent; [Session tool history](../packages/core/session/README.md) supplies provider declarations. -**Projection seam.** `dsh-session-projection` owns `ctx.sessionProjections`: registered units fold committed events incrementally, and carriers batch cropped client views with `snapshot()`. Domain services await registration during activation, retain private handles, and expose business methods for other plugins. Releasing registration removes global participation; retained handles continue folding Session events. `AgentRegistry` owns turn and step state and exposes `turnBoundaryOf(session)` ([registration lifecycle](../packages/session/session-projection/README.md)). `dsh-agent-model` owns `modelSelection`. +**Projection seam.** `dsh-session-projection` owns `ctx.sessionProjections`: registered units fold committed events incrementally, and carriers batch cropped client views with `snapshot()`. Domain services await registration during activation, retain private handles, and expose business methods for other plugins. Releasing registration removes global participation; retained handles continue folding Session events. `AgentRegistry` owns turn and step state and exposes `turnBoundaryOf(session)` ([registration lifecycle](../packages/session/session-projection/README.md)). `AgentLoop` owns `modelSelection` and constructs each Agent’s `model` instance alongside its inbox. [Working directories](subsystems/working-directory.md) supply user context and execution paths without changing original project identity, sandbox write roots, or existing process directories. diff --git a/docs/architecture.zh.md b/docs/architecture.zh.md index 41e92b4649..e287ea08d0 100644 --- a/docs/architecture.zh.md +++ b/docs/architecture.zh.md @@ -127,7 +127,7 @@ Session 消费方只了解当前逻辑格式。仅 header 的 `stat` 与 `list` **模型可见即已记录。** 每个模型请求都必须能从日志重建。新增模型可见输入需要会话事件。修改现有消息内容的插件注册[纯消息投影](subsystems/session.zh.md#plugin-owned-message-projections),独立读取器显式传入相同的处理器。 工具变更不依赖能力;[Session 工具历史](../packages/core/session/README.zh.md)提供提供方声明。 -**投影 seam。** `dsh-session-projection` 提供 `ctx.sessionProjections`:已注册单元增量折叠已提交事件,载体通过 `snapshot()` 批量取得裁剪后的客户端视图。领域服务在激活时等待注册完成,在内部保留句柄,并向其他插件提供业务方法。释放注册会移除全局参与关系;已有句柄仍可继续折叠 Session 事件。`AgentRegistry` 持有轮次和步骤状态,并提供 `turnBoundaryOf(session)`([注册生命周期](../packages/session/session-projection/README.zh.md))。 `dsh-agent-model` 持有 `modelSelection`。 +**投影 seam。** `dsh-session-projection` 提供 `ctx.sessionProjections`:已注册单元增量折叠已提交事件,载体通过 `snapshot()` 批量取得裁剪后的客户端视图。领域服务在激活时等待注册完成,在内部保留句柄,并向其他插件提供业务方法。释放注册会移除全局参与关系;已有句柄仍可继续折叠 Session 事件。`AgentRegistry` 持有轮次和步骤状态,并提供 `turnBoundaryOf(session)`([注册生命周期](../packages/session/session-projection/README.zh.md))。 `AgentLoop` 持有 `modelSelection`,并与 inbox 一起构建每个 Agent 的 `model` 实例。 [工作目录](subsystems/working-directory.zh.md) 提供用户上下文和执行路径,不改变原始项目标识、沙箱写入根目录或已有进程目录。 diff --git a/docs/capability-seams.i18n.yaml b/docs/capability-seams.i18n.yaml index 0df642a708..ae1d83cdf3 100644 --- a/docs/capability-seams.i18n.yaml +++ b/docs/capability-seams.i18n.yaml @@ -6,5 +6,5 @@ en: 41d772192075e133 zh: 5ef3da69b4de1d8a /capability-seams-and-core-services: - en: b6d09bcd8359cbd2 - zh: b64ec0217ca2bd3f + en: 753b3da3abc6aeea + zh: aab02ec16c3f744b diff --git a/docs/capability-seams.md b/docs/capability-seams.md index 14bd7d869b..f2b52ade47 100644 --- a/docs/capability-seams.md +++ b/docs/capability-seams.md @@ -191,8 +191,6 @@ flowchart LR pkg_skill_office["skill-office"] svc_agents["ctx.agents
Agent service"] pkg_acp["acp"] - pkg_agent_model["agent-model"] - svc_agentModel["ctx.agentModel
Session model selection"] svc_agentDefaultModel["ctx.agentDefaultModel
Default Agent model selection"] svc_agentLoop["ctx.agentLoop
Concrete loop driver"] pkg_base["base"] @@ -302,7 +300,6 @@ flowchart LR pkg_agent --> svc_agents pkg_agent_default_model --> svc_agentDefaultModel pkg_agent_loop --> svc_agentLoop - pkg_agent_model --> svc_agentModel pkg_agent_preset_registry --> svc_agentPresets pkg_api_gateway --> svc_typertGateway pkg_api_job_controller --> svc_jobController @@ -463,11 +460,6 @@ flowchart LR svc_agentDefaultModel --> pkg_headless svc_agentLoop --> pkg_base svc_agentLoop --> pkg_sdk_minimal - svc_agentModel --> pkg_acp - svc_agentModel --> pkg_agent_loop - svc_agentModel --> pkg_api_session_controller - svc_agentModel --> pkg_headless - svc_agentModel --> pkg_sdk_jsonrpc_server svc_agentTeams --> pkg_experimental_tool_agent_team svc_agents --> pkg_acp svc_agents --> pkg_agent_loop @@ -694,7 +686,6 @@ flowchart LR | `ctx.sessionProjectionCache` | `core` | [`session-projection-cache`](../packages/session/session-projection-cache) | - | [`api-session-controller`](../packages/api/session-controller), [`session-query`](../packages/session-query/session-query), [`session-reference`](../packages/context/session-reference) | - | Durably checkpoints projection unit states per session (throttled + turn/end/detach mandatory points), serves cached projection views, and accelerates prepared-Session projection hydration. | | `ctx.skills` | `seam` | [`skill`](../packages/skill/skill) | [`sandbox-windows-acl`](../packages/sandbox/sandbox-windows-acl), [`skill-badge`](../packages/experimental/skill-badge), [`skill-filesystem`](../packages/skill/skill-filesystem), [`skill-office`](../packages/skill/skill-office) | [`tool-skill`](../packages/skill/tool-skill) | - | Merges provider skill catalogs; tool-skill renders the session-prefix catalog and loads complete skill bodies. | | `ctx.agents` | `core` | [`agent`](../packages/core/agent) | - | [`agent-loop`](../packages/core/agent-loop), [`acp`](../packages/acp/acp), [`subagent`](../packages/subagent/subagent) | - | Owns live Agent handles, the create/resume factory seam, and process-local initiator propagation. | -| `ctx.agentModel` | `core` | [`agent-model`](../packages/core/agent-model) | - | [`agent-loop`](../packages/core/agent-loop), [`api-session-controller`](../packages/api/session-controller), [`acp`](../packages/acp/acp), [`sdk-jsonrpc-server`](../packages/sdk/server), [`headless`](../packages/bundle/headless) | - | Reads and records Session model choices; owns the modelSelection projection and optional scoped route-switch notices. | | `ctx.agentDefaultModel` | `core` | [`agent-default-model`](../packages/core/agent-default-model) | - | [`api-session-controller`](../packages/api/session-controller), [`headless`](../packages/bundle/headless) | - | Reads the default ModelSelection from volatile Config and saves selections through the profile editor. | | `ctx.agentLoop` | `bundle` | [`agent-loop`](../packages/core/agent-loop) | - | [`base`](../packages/bundle/base), [`sdk-minimal`](../packages/bundle/sdk-minimal) | - | The one concrete loop plugin; extension packages depend on dsh-agent events and services, not on this package. | | `ctx.schedule` | `core` | [`schedule`](../packages/schedule/schedule) | - | - | - | Stores tasks independently of Session activation and queues due messages in the original Session. | diff --git a/docs/capability-seams.zh.md b/docs/capability-seams.zh.md index 4c26fd907e..fede454e27 100644 --- a/docs/capability-seams.zh.md +++ b/docs/capability-seams.zh.md @@ -193,10 +193,6 @@ flowchart LR pkg_skill_office["skill-office"] svc_agents["ctx.agents
Agent service"] pkg_acp["acp"] - pkg_agent_model["agent-model"] - svc_agentModel["ctx.agentModel
Session model selection"] - pkg_agent_controller["agent-controller"] - svc_agentController["ctx.agentController
Outside-message delivery"] svc_agentDefaultModel["ctx.agentDefaultModel
Default Agent model selection"] svc_agentLoop["ctx.agentLoop
Concrete loop driver"] pkg_base["base"] @@ -306,7 +302,6 @@ flowchart LR pkg_agent --> svc_agents pkg_agent_default_model --> svc_agentDefaultModel pkg_agent_loop --> svc_agentLoop - pkg_agent_model --> svc_agentModel pkg_agent_preset_registry --> svc_agentPresets pkg_api_gateway --> svc_typertGateway pkg_api_job_controller --> svc_jobController @@ -467,11 +462,6 @@ flowchart LR svc_agentDefaultModel --> pkg_headless svc_agentLoop --> pkg_base svc_agentLoop --> pkg_sdk_minimal - svc_agentModel --> pkg_acp - svc_agentModel --> pkg_agent_loop - svc_agentModel --> pkg_api_session_controller - svc_agentModel --> pkg_headless - svc_agentModel --> pkg_sdk_jsonrpc_server svc_agentTeams --> pkg_experimental_tool_agent_team svc_agents --> pkg_acp svc_agents --> pkg_agent_loop @@ -698,8 +688,6 @@ flowchart LR | `ctx.sessionProjectionCache` | `core` | [`session-projection-cache`](../packages/session/session-projection-cache) | - | [`api-session-controller`](../packages/api/session-controller), [`session-query`](../packages/session-query/session-query), [`session-reference`](../packages/context/session-reference) | - | 按会话持久保存投影单元状态的检查点(节流检查点,以及轮次/结束/分离时的必选检查点),提供缓存投影视图,并加速 prepared Session 的投影恢复。 | | `ctx.skills` | `seam` | [`skill`](../packages/skill/skill) | [`sandbox-windows-acl`](../packages/sandbox/sandbox-windows-acl), [`skill-badge`](../packages/experimental/skill-badge), [`skill-filesystem`](../packages/skill/skill-filesystem), [`skill-office`](../packages/skill/skill-office) | [`tool-skill`](../packages/skill/tool-skill) | - | 合并提供方的 skill(技能)目录;tool-skill 渲染会话前缀目录,并加载完整的 skill 正文。 | | `ctx.agents` | `core` | [`agent`](../packages/core/agent) | - | [`agent-loop`](../packages/core/agent-loop), [`acp`](../packages/acp/acp), [`subagent`](../packages/subagent/subagent) | - | 拥有实时 Agent 句柄、创建/恢复工厂 seam,以及进程本地的发起方传播。 | -| `ctx.agentModel` | `core` | [`agent-model`](../packages/core/agent-model) | - | [`agent-loop`](../packages/core/agent-loop), [`api-session-controller`](../packages/api/session-controller), [`acp`](../packages/acp/acp), [`sdk-jsonrpc-server`](../packages/sdk/server), [`headless`](../packages/bundle/headless) | - | 读取并记录 Session 模型选择;持有 modelSelection 投影与可选的作用域内路由切换提示。 | -| `ctx.agentController` | `core` | [`agent-controller`](../packages/core/agent-controller) | - | [`api-session-controller`](../packages/api/session-controller) | - | 准备发送方代表他人送来的消息,经 agent-controller/admission 阶段接纳后放入收件箱,每个 Agent 逐条处理。 | | `ctx.agentDefaultModel` | `core` | [`agent-default-model`](../packages/core/agent-default-model) | - | [`api-session-controller`](../packages/api/session-controller), [`headless`](../packages/bundle/headless) | - | Reads the default ModelSelection from volatile Config and saves selections through the profile editor. | | `ctx.agentLoop` | `bundle` | [`agent-loop`](../packages/core/agent-loop) | - | [`base`](../packages/bundle/base), [`sdk-minimal`](../packages/bundle/sdk-minimal) | - | 唯一的具体循环插件;扩展包依赖 dsh-agent 的事件和服务,而不依赖此包。 | | `ctx.schedule` | `core` | [`schedule`](../packages/schedule/schedule) | - | - | - | 独立于 Session 的加载状态存储任务,并将到期消息排入原 Session。 | diff --git a/docs/config-catalog.md b/docs/config-catalog.md index 20ba54be0b..5773764d81 100644 --- a/docs/config-catalog.md +++ b/docs/config-catalog.md @@ -14,9 +14,9 @@ Each package entry labels its data with three identifiers: `inject` lists the se ## `@deepseek-ai/dsh-acp` -- `inject`: `agents` · `llm` · `sessionPersistence` · `agentModel` · `sessions` +- `inject`: `agents` · `llm` · `sessionPersistence` · `sessions` - `refs`: `Stream` (`@agentclientprotocol/sdk`) -- `source`: [`packages/acp/acp/src/index.ts:75`](../packages/acp/acp/src/index.ts) +- `source`: [`packages/acp/acp/src/index.ts:74`](../packages/acp/acp/src/index.ts) ```ts config-catalog /** Plugin config: the provider/model selection used for each ACP-created agent. */ @@ -94,9 +94,9 @@ export interface Config { ## `@deepseek-ai/dsh-agent-loop` -- `inject`: `agentModel` · `agents` · `sessions` · `llm` · `tools` · `systemPrompt` · `sessionProjections` +- `inject`: `agents` · `sessions` · `llm` · `tools` · `systemPrompt` · `sessionProjections` - `refs`: [`AgentOptions`](subsystems/core.md) · [`SessionId`](subsystems/core.md) · `Volatile` (`@deepseek-ai/cosmokit`) -- `source`: [`packages/core/agent-loop/src/index.ts:251`](../packages/core/agent-loop/src/index.ts) +- `source`: [`packages/core/agent-loop/src/index.ts:253`](../packages/core/agent-loop/src/index.ts) ```ts config-catalog /** Agent-loop plugin configuration. */ @@ -225,8 +225,8 @@ export interface Config { ## `@deepseek-ai/dsh-api-session-controller` -- `inject`: `agentDefaultModel` · `agents` · `attachments` · `fileUploads` · `fs` · `llm` · `remoteAccess` · `sessions` · `sessionProjections` · `agentModel` · `sessionQuery` · `typert` · `workspaceRegistry` -- `source`: [`packages/api/session-controller/src/index.ts:94`](../packages/api/session-controller/src/index.ts) +- `inject`: `agentDefaultModel` · `agents` · `attachments` · `fileUploads` · `fs` · `llm` · `remoteAccess` · `sessions` · `sessionProjections` · `sessionQuery` · `typert` · `workspaceRegistry` +- `source`: [`packages/api/session-controller/src/index.ts:93`](../packages/api/session-controller/src/index.ts) ```ts config-catalog /** Session Controller deployment policy. */ @@ -1488,9 +1488,9 @@ export interface Config { ## `@deepseek-ai/dsh-headless` -- `inject`: `agentDefaultModel` · `agents` · `agentModel` · `sessions` +- `inject`: `agentDefaultModel` · `agents` · `sessions` - `refs`: [`ApprovalPolicy`](subsystems/approval.md) -- `source`: [`packages/bundle/headless/src/index.ts:49`](../packages/bundle/headless/src/index.ts) +- `source`: [`packages/bundle/headless/src/index.ts:48`](../packages/bundle/headless/src/index.ts) ```ts config-catalog /** Plugin config: the task and run options resolved from this app's injected provider service. */ @@ -2841,9 +2841,9 @@ export interface Config { ## `@deepseek-ai/dsh-sdk-jsonrpc-server` -- `inject`: `agents` · `agentModel` · `workingDirectory` +- `inject`: `agents` · `workingDirectory` - `refs`: `Readable` (`node:stream`) · `Writable` (`node:stream`) -- `source`: [`packages/sdk/server/src/index.ts:25`](../packages/sdk/server/src/index.ts) +- `source`: [`packages/sdk/server/src/index.ts:24`](../packages/sdk/server/src/index.ts) ```ts config-catalog /** JSON-RPC deployment config plus runtime-only test hooks. */ @@ -4698,7 +4698,6 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co | --- | --- | --- | | `@deepseek-ai/dsh-acp-app` | `cmdlineArgs` | [`packages/bundle/acp-app/src/index.ts`](../packages/bundle/acp-app/src/index.ts) | | `@deepseek-ai/dsh-agent` | `sessionProjections` | [`packages/core/agent/src/index.ts`](../packages/core/agent/src/index.ts) | -| `@deepseek-ai/dsh-agent-model` | `sessionProjections` | [`packages/core/agent-model/src/index.ts`](../packages/core/agent-model/src/index.ts) | | `@deepseek-ai/dsh-api-account-controller` | `deepseekAccount` · `agents` | [`packages/api/account-controller/src/index.ts`](../packages/api/account-controller/src/index.ts) | | `@deepseek-ai/dsh-api-remote-access` | — | [`packages/api/remote-access/src/index.ts`](../packages/api/remote-access/src/index.ts) | | `@deepseek-ai/dsh-api-remotes` | `typertGateway` | [`packages/api/remotes/src/index.ts`](../packages/api/remotes/src/index.ts) | diff --git a/docs/config-catalog.zh.md b/docs/config-catalog.zh.md index fc74bbad87..1fe844236e 100644 --- a/docs/config-catalog.zh.md +++ b/docs/config-catalog.zh.md @@ -16,9 +16,9 @@ ## `@deepseek-ai/dsh-acp` -- `inject`: `agents` · `llm` · `sessionPersistence` · `agentModel` · `sessions` +- `inject`: `agents` · `llm` · `sessionPersistence` · `sessions` - `refs`: `Stream` (`@agentclientprotocol/sdk`) -- `source`: [`packages/acp/acp/src/index.ts:75`](../packages/acp/acp/src/index.ts) +- `source`: [`packages/acp/acp/src/index.ts:74`](../packages/acp/acp/src/index.ts) ```ts config-catalog /** Plugin config: the provider/model selection used for each ACP-created agent. */ @@ -96,9 +96,9 @@ export interface Config { ## `@deepseek-ai/dsh-agent-loop` -- `inject`: `agentModel` · `agents` · `sessions` · `llm` · `tools` · `systemPrompt` · `sessionProjections` +- `inject`: `agents` · `sessions` · `llm` · `tools` · `systemPrompt` · `sessionProjections` - `refs`: [`AgentOptions`](subsystems/core.zh.md) · [`SessionId`](subsystems/core.zh.md) · `Volatile` (`@deepseek-ai/cosmokit`) -- `source`: [`packages/core/agent-loop/src/index.ts:251`](../packages/core/agent-loop/src/index.ts) +- `source`: [`packages/core/agent-loop/src/index.ts:253`](../packages/core/agent-loop/src/index.ts) ```ts config-catalog /** Agent-loop plugin configuration. */ @@ -227,8 +227,8 @@ export interface Config { ## `@deepseek-ai/dsh-api-session-controller` -- `inject`: `agentDefaultModel` · `agents` · `attachments` · `fileUploads` · `fs` · `llm` · `remoteAccess` · `sessions` · `sessionProjections` · `agentModel` · `sessionQuery` · `typert` · `workspaceRegistry` -- `source`: [`packages/api/session-controller/src/index.ts:94`](../packages/api/session-controller/src/index.ts) +- `inject`: `agentDefaultModel` · `agents` · `attachments` · `fileUploads` · `fs` · `llm` · `remoteAccess` · `sessions` · `sessionProjections` · `sessionQuery` · `typert` · `workspaceRegistry` +- `source`: [`packages/api/session-controller/src/index.ts:93`](../packages/api/session-controller/src/index.ts) ```ts config-catalog /** Session Controller deployment policy. */ @@ -1490,9 +1490,9 @@ export interface Config { ## `@deepseek-ai/dsh-headless` -- `inject`: `agentDefaultModel` · `agents` · `agentModel` · `sessions` +- `inject`: `agentDefaultModel` · `agents` · `sessions` - `refs`: [`ApprovalPolicy`](subsystems/approval.zh.md) -- `source`: [`packages/bundle/headless/src/index.ts:49`](../packages/bundle/headless/src/index.ts) +- `source`: [`packages/bundle/headless/src/index.ts:48`](../packages/bundle/headless/src/index.ts) ```ts config-catalog /** Plugin config: the task and run options resolved from this app's injected provider service. */ @@ -2843,9 +2843,9 @@ export interface Config { ## `@deepseek-ai/dsh-sdk-jsonrpc-server` -- `inject`: `agents` · `agentModel` · `workingDirectory` +- `inject`: `agents` · `workingDirectory` - `refs`: `Readable` (`node:stream`) · `Writable` (`node:stream`) -- `source`: [`packages/sdk/server/src/index.ts:25`](../packages/sdk/server/src/index.ts) +- `source`: [`packages/sdk/server/src/index.ts:24`](../packages/sdk/server/src/index.ts) ```ts config-catalog /** JSON-RPC deployment config plus runtime-only test hooks. */ @@ -4700,7 +4700,6 @@ export interface Config { | --- | --- | --- | | `@deepseek-ai/dsh-acp-app` | `cmdlineArgs` | [`packages/bundle/acp-app/src/index.ts`](../packages/bundle/acp-app/src/index.ts) | | `@deepseek-ai/dsh-agent` | `sessionProjections` | [`packages/core/agent/src/index.ts`](../packages/core/agent/src/index.ts) | -| `@deepseek-ai/dsh-agent-model` | `sessionProjections` | [`packages/core/agent-model/src/index.ts`](../packages/core/agent-model/src/index.ts) | | `@deepseek-ai/dsh-api-account-controller` | `deepseekAccount` · `agents` | [`packages/api/account-controller/src/index.ts`](../packages/api/account-controller/src/index.ts) | | `@deepseek-ai/dsh-api-remote-access` | — | [`packages/api/remote-access/src/index.ts`](../packages/api/remote-access/src/index.ts) | | `@deepseek-ai/dsh-api-remotes` | `typertGateway` | [`packages/api/remotes/src/index.ts`](../packages/api/remotes/src/index.ts) | diff --git a/docs/event-producer-consumer.i18n.yaml b/docs/event-producer-consumer.i18n.yaml index d7ba2597de..f419955c56 100644 --- a/docs/event-producer-consumer.i18n.yaml +++ b/docs/event-producer-consumer.i18n.yaml @@ -8,6 +8,6 @@ /event-producer-and-consumer-matrix: en: 19f30fda81e73345 zh: a8505006244f5619 -/event-producer-and-consumer-matrix/events-outside-the-current-harness-api-catalog: - en: 6a87a4da78bd7170 - zh: 45b1dc20d153587f +/event-producer-and-consumer-matrix/non-harness-or-undeclared-event-strings-seen-in-package-source: + en: 96dc0ff1580e5741 + zh: 96dc0ff1580e5741 diff --git a/docs/event-producer-consumer.md b/docs/event-producer-consumer.md index 9a27c318e5..4f4d4be18c 100644 --- a/docs/event-producer-consumer.md +++ b/docs/event-producer-consumer.md @@ -8,19 +8,19 @@ This matrix shows which packages dispatch each harness-owned event and which pac | Event | Mode | Declared in | Dispatchers | Listeners | | --- | --- | --- | --- | --- | -| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:197`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | - | +| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:199`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | - | | `agent-preset/selected` | `emit` | [`packages/preset/agent-preset-registry/src/types.ts:66`](../packages/preset/agent-preset-registry/src/types.ts) | [`agent-preset-registry`](../packages/preset/agent-preset-registry) (`emit`) | [`cua`](../packages/experimental/cua), `remotes` | -| `agent/assistant-stream` | `emit` | [`packages/core/agent/src/runtime-types.ts:376`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`headless`](../packages/bundle/headless), `session-controller` | -| `agent/created` | `serial` | [`packages/core/agent/src/runtime-types.ts:267`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`serial`) | `agent-team`, `browser-use-runtime`, `claude-code-mods`, [`cua`](../packages/experimental/cua), [`file-reference-local`](../packages/context/file-reference-local), [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`loader-smoke`](../packages/test-support/loader-smoke), `session-controller`, `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) | -| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:276`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), `claude-code-mods`, [`file-reference-local`](../packages/context/file-reference-local), [`goal-round-driver`](../packages/goal/goal-round-driver), `session-controller`, [`subagent`](../packages/subagent/subagent), `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) | -| `agent/error` | `emit` | [`packages/core/agent/src/runtime-types.ts:406`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`deepseek-account`](../packages/credentials/deepseek-account), [`goal-round-driver`](../packages/goal/goal-round-driver), [`headless`](../packages/bundle/headless), `server`, `session-controller`, [`session-telemetry`](../packages/session/session-telemetry), [`subagent`](../packages/subagent/subagent) | -| `agent/inbox/claimed` | `emit` | [`packages/core/agent/src/runtime-types.ts:305`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-jobs`](../packages/jobs/tool-jobs), [`user-questions`](../packages/interaction/user-questions) | -| `agent/inbox/discarded` | `emit` | [`packages/core/agent/src/runtime-types.ts:313`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`user-questions`](../packages/interaction/user-questions) | -| `agent/inbox/inserted` | `emit` | [`packages/core/agent/src/runtime-types.ts:294`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent) | -| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:327`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent-instructions`](../packages/context/agent-instructions), [`agent-model`](../packages/core/agent-model), `claude-code-mods`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `session-controller`, [`session-reference`](../packages/context/session-reference), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill) | -| `agent/request-error` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:366`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compaction-basic`](../packages/compaction/compaction-basic), [`compaction-image-offload`](../packages/compaction/compaction-image-offload), [`llm-retry`](../packages/llm/llm-retry) | -| `agent/status` | `emit` | [`packages/core/agent/src/runtime-types.ts:286`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), `agent-team`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), `server`, `session-controller` | -| `agent/turn-stopping` | `serial` | [`packages/core/agent/src/runtime-types.ts:394`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`workspace-changes`](../packages/deliverables/workspace-changes) | +| `agent/assistant-stream` | `emit` | [`packages/core/agent/src/runtime-types.ts:399`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`headless`](../packages/bundle/headless), `session-controller` | +| `agent/created` | `serial` | [`packages/core/agent/src/runtime-types.ts:290`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`serial`) | `agent-team`, `browser-use-runtime`, `claude-code-mods`, [`cua`](../packages/experimental/cua), [`file-reference-local`](../packages/context/file-reference-local), [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`loader-smoke`](../packages/test-support/loader-smoke), `session-controller`, `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) | +| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:299`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), `claude-code-mods`, [`file-reference-local`](../packages/context/file-reference-local), [`goal-round-driver`](../packages/goal/goal-round-driver), `session-controller`, [`subagent`](../packages/subagent/subagent), `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) | +| `agent/error` | `emit` | [`packages/core/agent/src/runtime-types.ts:429`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`deepseek-account`](../packages/credentials/deepseek-account), [`goal-round-driver`](../packages/goal/goal-round-driver), [`headless`](../packages/bundle/headless), `server`, `session-controller`, [`session-telemetry`](../packages/session/session-telemetry), [`subagent`](../packages/subagent/subagent) | +| `agent/inbox/claimed` | `emit` | [`packages/core/agent/src/runtime-types.ts:328`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-jobs`](../packages/jobs/tool-jobs), [`user-questions`](../packages/interaction/user-questions) | +| `agent/inbox/discarded` | `emit` | [`packages/core/agent/src/runtime-types.ts:336`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`user-questions`](../packages/interaction/user-questions) | +| `agent/inbox/inserted` | `emit` | [`packages/core/agent/src/runtime-types.ts:317`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent) | +| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:350`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), `claude-code-mods`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `session-controller`, [`session-reference`](../packages/context/session-reference), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill) | +| `agent/request-error` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:389`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compaction-basic`](../packages/compaction/compaction-basic), [`compaction-image-offload`](../packages/compaction/compaction-image-offload), [`llm-retry`](../packages/llm/llm-retry) | +| `agent/status` | `emit` | [`packages/core/agent/src/runtime-types.ts:309`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), `agent-team`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), `server`, `session-controller` | +| `agent/turn-stopping` | `serial` | [`packages/core/agent/src/runtime-types.ts:417`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`workspace-changes`](../packages/deliverables/workspace-changes) | | `api-session/activity` | `emit` | [`packages/api/session-controller/src/types.ts:599`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` | | `api-session/added` | `emit` | [`packages/api/session-controller/src/types.ts:579`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` | | `api-session/error` | `emit` | [`packages/api/session-controller/src/types.ts:606`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` | @@ -93,7 +93,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac | `workspace/session-stop` | `parallel` | [`packages/workspace/workspace/src/index.ts:147`](../packages/workspace/workspace/src/index.ts) | [`workspace`](../packages/workspace/workspace) (`parallel`) | [`agent`](../packages/core/agent), [`jobs`](../packages/jobs/jobs), [`schedule`](../packages/schedule/schedule), [`subagent`](../packages/subagent/subagent) | -## Events outside the current Harness API catalog +## Non-harness or undeclared event strings seen in package source | Event string | Dispatchers | Listeners | @@ -103,7 +103,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac | `internal/dispatch` | - | [`terminal-bash`](../packages/terminal/terminal-bash) | | `internal/plugin` | - | `computer-use-cua-driver-native`, `inspector`, `loader`, [`lsp-stdio`](../packages/lsp/lsp-stdio), [`mcp-client`](../packages/mcp/mcp-client), `modules` | | `internal/service` | - | `gateway` | -| `internal/status` | - | [`agent`](../packages/core/agent), [`agent-model`](../packages/core/agent-model), `inspector`, `translator`, [`web`](../packages/web/web) | +| `internal/status` | - | [`agent`](../packages/core/agent), `inspector`, `translator`, [`web`](../packages/web/web) | | `internal/update` | - | [`app-boot`](../packages/boot/app-boot) | | `loader/volatile-update` | - | [`cua`](../packages/experimental/cua), [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai), [`plugin-market`](../packages/boot/plugin-market), `product-analytics`, `speech-to-text` | | `slots/changed` | `ui-renderer` (`emit`) | - | diff --git a/docs/event-producer-consumer.zh.md b/docs/event-producer-consumer.zh.md index 14f5eae48f..4b0813635c 100644 --- a/docs/event-producer-consumer.zh.md +++ b/docs/event-producer-consumer.zh.md @@ -10,19 +10,19 @@ | Event | Mode | Declared in | Dispatchers | Listeners | | --- | --- | --- | --- | --- | -| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:197`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | - | +| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:199`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | - | | `agent-preset/selected` | `emit` | [`packages/preset/agent-preset-registry/src/types.ts:66`](../packages/preset/agent-preset-registry/src/types.ts) | [`agent-preset-registry`](../packages/preset/agent-preset-registry) (`emit`) | [`cua`](../packages/experimental/cua), `remotes` | -| `agent/assistant-stream` | `emit` | [`packages/core/agent/src/runtime-types.ts:376`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`headless`](../packages/bundle/headless), `session-controller` | -| `agent/created` | `serial` | [`packages/core/agent/src/runtime-types.ts:267`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`serial`) | `agent-team`, `browser-use-runtime`, `claude-code-mods`, [`cua`](../packages/experimental/cua), [`file-reference-local`](../packages/context/file-reference-local), [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`loader-smoke`](../packages/test-support/loader-smoke), `session-controller`, `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) | -| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:276`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), `claude-code-mods`, [`file-reference-local`](../packages/context/file-reference-local), [`goal-round-driver`](../packages/goal/goal-round-driver), `session-controller`, [`subagent`](../packages/subagent/subagent), `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) | -| `agent/error` | `emit` | [`packages/core/agent/src/runtime-types.ts:406`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`deepseek-account`](../packages/credentials/deepseek-account), [`goal-round-driver`](../packages/goal/goal-round-driver), [`headless`](../packages/bundle/headless), `server`, `session-controller`, [`session-telemetry`](../packages/session/session-telemetry), [`subagent`](../packages/subagent/subagent) | -| `agent/inbox/claimed` | `emit` | [`packages/core/agent/src/runtime-types.ts:305`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-jobs`](../packages/jobs/tool-jobs), [`user-questions`](../packages/interaction/user-questions) | -| `agent/inbox/discarded` | `emit` | [`packages/core/agent/src/runtime-types.ts:313`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`user-questions`](../packages/interaction/user-questions) | -| `agent/inbox/inserted` | `emit` | [`packages/core/agent/src/runtime-types.ts:294`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent) | -| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:327`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent-instructions`](../packages/context/agent-instructions), [`agent-model`](../packages/core/agent-model), `claude-code-mods`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `session-controller`, [`session-reference`](../packages/context/session-reference), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill) | -| `agent/request-error` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:366`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compaction-basic`](../packages/compaction/compaction-basic), [`compaction-image-offload`](../packages/compaction/compaction-image-offload), [`llm-retry`](../packages/llm/llm-retry) | -| `agent/status` | `emit` | [`packages/core/agent/src/runtime-types.ts:286`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), `agent-team`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), `server`, `session-controller` | -| `agent/turn-stopping` | `serial` | [`packages/core/agent/src/runtime-types.ts:394`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`workspace-changes`](../packages/deliverables/workspace-changes) | +| `agent/assistant-stream` | `emit` | [`packages/core/agent/src/runtime-types.ts:399`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`headless`](../packages/bundle/headless), `session-controller` | +| `agent/created` | `serial` | [`packages/core/agent/src/runtime-types.ts:290`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`serial`) | `agent-team`, `browser-use-runtime`, `claude-code-mods`, [`cua`](../packages/experimental/cua), [`file-reference-local`](../packages/context/file-reference-local), [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`loader-smoke`](../packages/test-support/loader-smoke), `session-controller`, `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) | +| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:299`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), `claude-code-mods`, [`file-reference-local`](../packages/context/file-reference-local), [`goal-round-driver`](../packages/goal/goal-round-driver), `session-controller`, [`subagent`](../packages/subagent/subagent), `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) | +| `agent/error` | `emit` | [`packages/core/agent/src/runtime-types.ts:429`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`deepseek-account`](../packages/credentials/deepseek-account), [`goal-round-driver`](../packages/goal/goal-round-driver), [`headless`](../packages/bundle/headless), `server`, `session-controller`, [`session-telemetry`](../packages/session/session-telemetry), [`subagent`](../packages/subagent/subagent) | +| `agent/inbox/claimed` | `emit` | [`packages/core/agent/src/runtime-types.ts:328`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-jobs`](../packages/jobs/tool-jobs), [`user-questions`](../packages/interaction/user-questions) | +| `agent/inbox/discarded` | `emit` | [`packages/core/agent/src/runtime-types.ts:336`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`user-questions`](../packages/interaction/user-questions) | +| `agent/inbox/inserted` | `emit` | [`packages/core/agent/src/runtime-types.ts:317`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent) | +| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:350`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), `claude-code-mods`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `session-controller`, [`session-reference`](../packages/context/session-reference), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill) | +| `agent/request-error` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:389`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compaction-basic`](../packages/compaction/compaction-basic), [`compaction-image-offload`](../packages/compaction/compaction-image-offload), [`llm-retry`](../packages/llm/llm-retry) | +| `agent/status` | `emit` | [`packages/core/agent/src/runtime-types.ts:309`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), `agent-team`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), `server`, `session-controller` | +| `agent/turn-stopping` | `serial` | [`packages/core/agent/src/runtime-types.ts:417`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`workspace-changes`](../packages/deliverables/workspace-changes) | | `api-session/activity` | `emit` | [`packages/api/session-controller/src/types.ts:599`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` | | `api-session/added` | `emit` | [`packages/api/session-controller/src/types.ts:579`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` | | `api-session/error` | `emit` | [`packages/api/session-controller/src/types.ts:606`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` | @@ -95,7 +95,7 @@ | `workspace/session-stop` | `parallel` | [`packages/workspace/workspace/src/index.ts:147`](../packages/workspace/workspace/src/index.ts) | [`workspace`](../packages/workspace/workspace) (`parallel`) | [`agent`](../packages/core/agent), [`jobs`](../packages/jobs/jobs), [`schedule`](../packages/schedule/schedule), [`subagent`](../packages/subagent/subagent) | -## 当前 Harness API 目录之外的事件 +## Non-harness or undeclared event strings seen in package source | Event string | Dispatchers | Listeners | @@ -105,7 +105,7 @@ | `internal/dispatch` | - | [`terminal-bash`](../packages/terminal/terminal-bash) | | `internal/plugin` | - | `computer-use-cua-driver-native`, `inspector`, `loader`, [`lsp-stdio`](../packages/lsp/lsp-stdio), [`mcp-client`](../packages/mcp/mcp-client), `modules` | | `internal/service` | - | `gateway` | -| `internal/status` | - | [`agent`](../packages/core/agent), [`agent-model`](../packages/core/agent-model), `inspector`, `translator`, [`web`](../packages/web/web) | +| `internal/status` | - | [`agent`](../packages/core/agent), `inspector`, `translator`, [`web`](../packages/web/web) | | `internal/update` | - | [`app-boot`](../packages/boot/app-boot) | | `loader/volatile-update` | - | [`cua`](../packages/experimental/cua), [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai), [`plugin-market`](../packages/boot/plugin-market), `product-analytics`, `speech-to-text` | | `slots/changed` | `ui-renderer` (`emit`) | - | diff --git a/docs/module-graph.md b/docs/module-graph.md index 6e39d1fbf5..1566f893e7 100644 --- a/docs/module-graph.md +++ b/docs/module-graph.md @@ -43,7 +43,6 @@ flowchart TD pkg_agent["agent"] pkg_agent_default_model["agent-default-model"] pkg_agent_loop["agent-loop"] - pkg_agent_model["agent-model"] pkg_agent_tool_presentation["agent-tool-presentation"] pkg_scope["scope"] pkg_session["session"] @@ -643,10 +642,6 @@ flowchart TD pkg_llm_retry --> pkg_timeout pkg_agent_default_model --> pkg_agent pkg_agent_default_model --> pkg_llm - pkg_agent_model --> pkg_agent - pkg_agent_model --> pkg_llm - pkg_agent_model --> pkg_session - pkg_agent_model --> pkg_session_projection pkg_goal --> pkg_agent pkg_goal --> pkg_brand pkg_goal --> pkg_llm @@ -825,7 +820,6 @@ flowchart TD pkg_terminal_bash --> pkg_terminal pkg_terminal_bash --> pkg_working_directory pkg_agent_loop --> pkg_agent - pkg_agent_loop --> pkg_agent_model pkg_agent_loop --> pkg_llm pkg_agent_loop --> pkg_scope pkg_agent_loop --> pkg_session @@ -1148,7 +1142,6 @@ flowchart TD pkg_subprocess_ssh --> pkg_subprocess_local pkg_agent_loop_testkit --> pkg_agent pkg_agent_loop_testkit --> pkg_agent_loop - pkg_agent_loop_testkit --> pkg_agent_model pkg_agent_loop_testkit --> pkg_fs_local pkg_agent_loop_testkit --> pkg_llm pkg_agent_loop_testkit --> pkg_session @@ -1187,7 +1180,6 @@ flowchart TD pkg_session_query_sqlite --> pkg_session_persistence pkg_session_query_sqlite --> pkg_session_query pkg_acp --> pkg_agent - pkg_acp --> pkg_agent_model pkg_acp --> pkg_attachment pkg_acp --> pkg_llm pkg_acp --> pkg_mcp_client @@ -1280,7 +1272,6 @@ flowchart TD pkg_tool_subagent_control --> pkg_tools pkg_api_session_controller --> pkg_agent pkg_api_session_controller --> pkg_agent_default_model - pkg_api_session_controller --> pkg_agent_model pkg_api_session_controller --> pkg_agent_preset_registry pkg_api_session_controller --> pkg_api_gateway pkg_api_session_controller --> pkg_attachment @@ -1316,7 +1307,6 @@ flowchart TD pkg_plugin_market --> pkg_typert_protocol pkg_headless --> pkg_agent pkg_headless --> pkg_agent_default_model - pkg_headless --> pkg_agent_model pkg_headless --> pkg_fs pkg_headless --> pkg_llm pkg_headless --> pkg_permission_presets @@ -1435,7 +1425,6 @@ flowchart TD pkg_tool_schedule --> pkg_subagent pkg_tool_schedule --> pkg_tools pkg_sdk_jsonrpc_server --> pkg_agent - pkg_sdk_jsonrpc_server --> pkg_agent_model pkg_sdk_jsonrpc_server --> pkg_attachment pkg_sdk_jsonrpc_server --> pkg_llm pkg_sdk_jsonrpc_server --> pkg_llm_deepseek_api_key @@ -1663,7 +1652,6 @@ flowchart TD | [`llm-deepseek-api-key`](../packages/llm/llm-deepseek-api-key) | `llm` | [`credentials`](../packages/credentials/credentials), [`launch-environment`](../packages/util/launch-environment), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek) | | [`llm-retry`](../packages/llm/llm-retry) | `llm` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`timeout`](../packages/util/timeout) | | [`agent-default-model`](../packages/core/agent-default-model) | `core` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm) | -| [`agent-model`](../packages/core/agent-model) | `core` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection) | | [`goal`](../packages/goal/goal) | `goal` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`typert-protocol`](../packages/typert/protocol) | | [`file-reference`](../packages/context/file-reference) | `context` | [`agent`](../packages/core/agent) | | [`harness-dev-context`](../packages/context/harness-dev-context) | `context` | [`agent`](../packages/core/agent), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt) | @@ -1701,7 +1689,7 @@ flowchart TD | [`bash-sandbox`](../packages/shell/bash-sandbox) | `shell` | [`bash-local`](../packages/shell/bash-local), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell) | | [`pwsh-sandbox`](../packages/shell/pwsh-sandbox) | `shell` | [`pwsh-local`](../packages/shell/pwsh-local), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell) | | [`terminal-bash`](../packages/terminal/terminal-bash) | `terminal` | [`agent`](../packages/core/agent), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`subprocess`](../packages/subprocess/subprocess), [`terminal`](../packages/terminal/terminal), [`working-directory`](../packages/session/working-directory) | -| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`agent-model`](../packages/core/agent-model), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) | +| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) | | [`agent-tool-presentation`](../packages/core/agent-tool-presentation) | `core` | [`tools`](../packages/core/tools) | | [`tool-goal`](../packages/goal/tool-goal) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) | | [`tool-fs`](../packages/fs/tool-fs) | `fs` | [`attachment`](../packages/attachment/attachment), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval), [`working-directory`](../packages/session/working-directory) | @@ -1762,11 +1750,11 @@ flowchart TD | [`fs-ssh`](../packages/ssh/fs-ssh) | `ssh` | [`fs`](../packages/fs/fs), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`ssh`](../packages/ssh/ssh) | | [`sandbox-ssh`](../packages/ssh/sandbox-ssh) | `ssh` | [`sandbox`](../packages/sandbox/sandbox), [`ssh`](../packages/ssh/ssh) | | [`subprocess-ssh`](../packages/ssh/subprocess-ssh) | `ssh` | [`ssh`](../packages/ssh/ssh), [`subprocess`](../packages/subprocess/subprocess), [`subprocess-local`](../packages/subprocess/subprocess-local) | -| [`agent-loop-testkit`](../packages/test-support/agent-loop-testkit) | `test-support` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`agent-model`](../packages/core/agent-model), [`fs-local`](../packages/fs/fs-local), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) | +| [`agent-loop-testkit`](../packages/test-support/agent-loop-testkit) | `test-support` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`fs-local`](../packages/fs/fs-local), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) | | [`subagent`](../packages/subagent/subagent) | `subagent` | [`agent`](../packages/core/agent), [`agent-preset-registry`](../packages/preset/agent-preset-registry), [`attachment`](../packages/attachment/attachment), [`llm`](../packages/llm/llm), [`permission-presets`](../packages/interaction/permission-presets), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`session-query`](../packages/session-query/session-query), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol), [`user-approval`](../packages/interaction/user-approval), [`util-time`](../packages/util/time), [`working-directory`](../packages/session/working-directory), [`workspace`](../packages/workspace/workspace) | | [`spill-policy`](../packages/spill/spill-policy) | `spill` | [`attachment`](../packages/attachment/attachment), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`output-retention`](../packages/util/output-retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`token-meter`](../packages/llm/token-meter), [`tools`](../packages/core/tools) | | [`session-query-sqlite`](../packages/session-query/session-query-sqlite) | `session-query` | [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-query`](../packages/session-query/session-query) | -| [`acp`](../packages/acp/acp) | `acp` | [`agent`](../packages/core/agent), [`agent-model`](../packages/core/agent-model), [`attachment`](../packages/attachment/attachment), [`llm`](../packages/llm/llm), [`mcp-client`](../packages/mcp/mcp-client), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`token-meter`](../packages/llm/token-meter), [`user-approval`](../packages/interaction/user-approval) | +| [`acp`](../packages/acp/acp) | `acp` | [`agent`](../packages/core/agent), [`attachment`](../packages/attachment/attachment), [`llm`](../packages/llm/llm), [`mcp-client`](../packages/mcp/mcp-client), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`token-meter`](../packages/llm/token-meter), [`user-approval`](../packages/interaction/user-approval) | | [`plugin-manager`](../packages/boot/plugin-manager) | `boot` | [`brand`](../packages/util/brand), [`hmr`](../packages/boot/hmr), [`host-plugin-inventory`](../packages/host/plugin-inventory), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`subprocess`](../packages/subprocess/subprocess), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol), [`user-approval`](../packages/interaction/user-approval) | | [`compaction-tool-result-pruner`](../packages/compaction/compaction-tool-result-pruner) | `compaction` | [`compaction`](../packages/compaction/compaction), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) | | [`experimental-browser-use-runtime`](../packages/experimental/browser-use-runtime) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`mcp-client`](../packages/mcp/mcp-client), [`scope`](../packages/core/scope), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) | @@ -1781,9 +1769,9 @@ flowchart TD | [`subagent-spawn-in-process`](../packages/subagent/subagent-spawn-in-process) | `subagent` | [`subagent`](../packages/subagent/subagent) | | [`tool-subagent`](../packages/subagent/tool-subagent) | `subagent` | [`agent`](../packages/core/agent), [`agent-preset-registry`](../packages/preset/agent-preset-registry), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) | | [`tool-subagent-control`](../packages/subagent/tool-subagent-control) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) | -| [`api-session-controller`](../packages/api/session-controller) | `api` | [`agent`](../packages/core/agent), [`agent-controller`](../packages/core/agent-controller), [`agent-default-model`](../packages/core/agent-default-model), [`agent-model`](../packages/core/agent-model), [`agent-preset-registry`](../packages/preset/agent-preset-registry), [`api-gateway`](../packages/api/gateway), [`attachment`](../packages/attachment/attachment), [`client-connection`](../packages/client/connection), [`client-file-upload`](../packages/client/file-upload), [`commands`](../packages/interaction/commands), [`credentials`](../packages/credentials/credentials), [`file-reference`](../packages/context/file-reference), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`native-command`](../packages/util/native-command), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-query`](../packages/session-query/session-query), [`session-title`](../packages/session/session-title), [`settings`](../packages/settings/settings), [`skill`](../packages/skill/skill), [`subagent`](../packages/subagent/subagent), [`typert-protocol`](../packages/typert/protocol), [`typert-registry`](../packages/typert/registry), [`util-time`](../packages/util/time), [`util-values`](../packages/util/values), [`util-workspace-path`](../packages/util/workspace-path), [`working-directory`](../packages/session/working-directory), [`workspace`](../packages/workspace/workspace) | +| [`api-session-controller`](../packages/api/session-controller) | `api` | [`agent`](../packages/core/agent), [`agent-default-model`](../packages/core/agent-default-model), [`agent-preset-registry`](../packages/preset/agent-preset-registry), [`api-gateway`](../packages/api/gateway), [`attachment`](../packages/attachment/attachment), [`client-connection`](../packages/client/connection), [`client-file-upload`](../packages/client/file-upload), [`commands`](../packages/interaction/commands), [`credentials`](../packages/credentials/credentials), [`file-reference`](../packages/context/file-reference), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`native-command`](../packages/util/native-command), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-query`](../packages/session-query/session-query), [`session-title`](../packages/session/session-title), [`settings`](../packages/settings/settings), [`skill`](../packages/skill/skill), [`subagent`](../packages/subagent/subagent), [`typert-protocol`](../packages/typert/protocol), [`typert-registry`](../packages/typert/registry), [`util-time`](../packages/util/time), [`util-values`](../packages/util/values), [`util-workspace-path`](../packages/util/workspace-path), [`working-directory`](../packages/session/working-directory), [`workspace`](../packages/workspace/workspace) | | [`plugin-market`](../packages/boot/plugin-market) | `boot` | [`plugin-manager`](../packages/boot/plugin-manager), [`settings`](../packages/settings/settings), [`subprocess`](../packages/subprocess/subprocess), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol) | -| [`headless`](../packages/bundle/headless) | `bundle` | [`agent`](../packages/core/agent), [`agent-default-model`](../packages/core/agent-default-model), [`agent-model`](../packages/core/agent-model), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`permission-presets`](../packages/interaction/permission-presets), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`session-query`](../packages/session-query/session-query), [`subagent`](../packages/subagent/subagent), [`user-approval`](../packages/interaction/user-approval) | +| [`headless`](../packages/bundle/headless) | `bundle` | [`agent`](../packages/core/agent), [`agent-default-model`](../packages/core/agent-default-model), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`permission-presets`](../packages/interaction/permission-presets), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`session-query`](../packages/session-query/session-query), [`subagent`](../packages/subagent/subagent), [`user-approval`](../packages/interaction/user-approval) | | [`compaction-basic`](../packages/compaction/compaction-basic) | `compaction` | [`agent`](../packages/core/agent), [`commands`](../packages/interaction/commands), [`compaction`](../packages/compaction/compaction), [`compaction-tool-result-pruner`](../packages/compaction/compaction-tool-result-pruner), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) | | [`session-reference`](../packages/context/session-reference) | `context` | [`agent`](../packages/core/agent), [`compaction`](../packages/compaction/compaction), [`llm`](../packages/llm/llm), [`output-retention`](../packages/util/output-retention), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-query`](../packages/session-query/session-query), [`session-title`](../packages/session/session-title), [`spill`](../packages/spill/spill), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`typert-protocol`](../packages/typert/protocol) | | [`experimental-agent-team`](../packages/experimental/agent-team) | `experimental` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`subagent`](../packages/subagent/subagent) | @@ -1799,5 +1787,5 @@ flowchart TD | [`client-test-runtime`](../packages/test-support/client-runtime) | `test-support` | [`api-gateway`](../packages/api/gateway), [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`attachment`](../packages/attachment/attachment), [`client-connection`](../packages/client/connection), [`client-hmr`](../packages/client/hmr), [`client-modules`](../packages/client/modules), [`client-store`](../packages/client/store), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-slots`](../packages/client/ui-slots), [`client-web`](../packages/client/web), [`remote-mock`](../packages/test-support/remote-mock), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`typert-protocol`](../packages/typert/protocol) | | [`subagent-dsh-sdk`](../packages/subagent/subagent-dsh-sdk) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`sdk-client`](../packages/sdk/client), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess) | | [`tool-schedule`](../packages/schedule/tool-schedule) | `schedule` | [`llm`](../packages/llm/llm), [`schedule`](../packages/schedule/schedule), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) | -| [`sdk-jsonrpc-server`](../packages/sdk/server) | `sdk` | [`agent`](../packages/core/agent), [`agent-model`](../packages/core/agent-model), [`attachment`](../packages/attachment/attachment), [`llm`](../packages/llm/llm), [`llm-deepseek-api-key`](../packages/llm/llm-deepseek-api-key), [`schedule`](../packages/schedule/schedule), [`scope`](../packages/core/scope), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`user-questions`](../packages/interaction/user-questions), [`working-directory`](../packages/session/working-directory) | +| [`sdk-jsonrpc-server`](../packages/sdk/server) | `sdk` | [`agent`](../packages/core/agent), [`attachment`](../packages/attachment/attachment), [`llm`](../packages/llm/llm), [`llm-deepseek-api-key`](../packages/llm/llm-deepseek-api-key), [`schedule`](../packages/schedule/schedule), [`scope`](../packages/core/scope), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`user-questions`](../packages/interaction/user-questions), [`working-directory`](../packages/session/working-directory) | diff --git a/docs/module-graph.zh.md b/docs/module-graph.zh.md index 9e699620e4..57e96d7a4f 100644 --- a/docs/module-graph.zh.md +++ b/docs/module-graph.zh.md @@ -45,7 +45,6 @@ flowchart TD pkg_agent["agent"] pkg_agent_default_model["agent-default-model"] pkg_agent_loop["agent-loop"] - pkg_agent_model["agent-model"] pkg_agent_tool_presentation["agent-tool-presentation"] pkg_scope["scope"] pkg_session["session"] @@ -645,10 +644,6 @@ flowchart TD pkg_llm_retry --> pkg_timeout pkg_agent_default_model --> pkg_agent pkg_agent_default_model --> pkg_llm - pkg_agent_model --> pkg_agent - pkg_agent_model --> pkg_llm - pkg_agent_model --> pkg_session - pkg_agent_model --> pkg_session_projection pkg_goal --> pkg_agent pkg_goal --> pkg_brand pkg_goal --> pkg_llm @@ -827,7 +822,6 @@ flowchart TD pkg_terminal_bash --> pkg_terminal pkg_terminal_bash --> pkg_working_directory pkg_agent_loop --> pkg_agent - pkg_agent_loop --> pkg_agent_model pkg_agent_loop --> pkg_llm pkg_agent_loop --> pkg_scope pkg_agent_loop --> pkg_session @@ -1150,7 +1144,6 @@ flowchart TD pkg_subprocess_ssh --> pkg_subprocess_local pkg_agent_loop_testkit --> pkg_agent pkg_agent_loop_testkit --> pkg_agent_loop - pkg_agent_loop_testkit --> pkg_agent_model pkg_agent_loop_testkit --> pkg_fs_local pkg_agent_loop_testkit --> pkg_llm pkg_agent_loop_testkit --> pkg_session @@ -1189,7 +1182,6 @@ flowchart TD pkg_session_query_sqlite --> pkg_session_persistence pkg_session_query_sqlite --> pkg_session_query pkg_acp --> pkg_agent - pkg_acp --> pkg_agent_model pkg_acp --> pkg_attachment pkg_acp --> pkg_llm pkg_acp --> pkg_mcp_client @@ -1282,7 +1274,6 @@ flowchart TD pkg_tool_subagent_control --> pkg_tools pkg_api_session_controller --> pkg_agent pkg_api_session_controller --> pkg_agent_default_model - pkg_api_session_controller --> pkg_agent_model pkg_api_session_controller --> pkg_agent_preset_registry pkg_api_session_controller --> pkg_api_gateway pkg_api_session_controller --> pkg_attachment @@ -1318,7 +1309,6 @@ flowchart TD pkg_plugin_market --> pkg_typert_protocol pkg_headless --> pkg_agent pkg_headless --> pkg_agent_default_model - pkg_headless --> pkg_agent_model pkg_headless --> pkg_fs pkg_headless --> pkg_llm pkg_headless --> pkg_permission_presets @@ -1437,7 +1427,6 @@ flowchart TD pkg_tool_schedule --> pkg_subagent pkg_tool_schedule --> pkg_tools pkg_sdk_jsonrpc_server --> pkg_agent - pkg_sdk_jsonrpc_server --> pkg_agent_model pkg_sdk_jsonrpc_server --> pkg_attachment pkg_sdk_jsonrpc_server --> pkg_llm pkg_sdk_jsonrpc_server --> pkg_llm_deepseek_api_key @@ -1665,7 +1654,6 @@ flowchart TD | [`llm-deepseek-api-key`](../packages/llm/llm-deepseek-api-key) | `llm` | [`credentials`](../packages/credentials/credentials), [`launch-environment`](../packages/util/launch-environment), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek) | | [`llm-retry`](../packages/llm/llm-retry) | `llm` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`timeout`](../packages/util/timeout) | | [`agent-default-model`](../packages/core/agent-default-model) | `core` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm) | -| [`agent-model`](../packages/core/agent-model) | `core` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection) | | [`goal`](../packages/goal/goal) | `goal` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`typert-protocol`](../packages/typert/protocol) | | [`file-reference`](../packages/context/file-reference) | `context` | [`agent`](../packages/core/agent) | | [`harness-dev-context`](../packages/context/harness-dev-context) | `context` | [`agent`](../packages/core/agent), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt) | @@ -1703,7 +1691,7 @@ flowchart TD | [`bash-sandbox`](../packages/shell/bash-sandbox) | `shell` | [`bash-local`](../packages/shell/bash-local), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell) | | [`pwsh-sandbox`](../packages/shell/pwsh-sandbox) | `shell` | [`pwsh-local`](../packages/shell/pwsh-local), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell) | | [`terminal-bash`](../packages/terminal/terminal-bash) | `terminal` | [`agent`](../packages/core/agent), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`subprocess`](../packages/subprocess/subprocess), [`terminal`](../packages/terminal/terminal), [`working-directory`](../packages/session/working-directory) | -| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`agent-model`](../packages/core/agent-model), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) | +| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) | | [`agent-tool-presentation`](../packages/core/agent-tool-presentation) | `core` | [`tools`](../packages/core/tools) | | [`tool-goal`](../packages/goal/tool-goal) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) | | [`tool-fs`](../packages/fs/tool-fs) | `fs` | [`attachment`](../packages/attachment/attachment), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval), [`working-directory`](../packages/session/working-directory) | @@ -1764,11 +1752,11 @@ flowchart TD | [`fs-ssh`](../packages/ssh/fs-ssh) | `ssh` | [`fs`](../packages/fs/fs), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`ssh`](../packages/ssh/ssh) | | [`sandbox-ssh`](../packages/ssh/sandbox-ssh) | `ssh` | [`sandbox`](../packages/sandbox/sandbox), [`ssh`](../packages/ssh/ssh) | | [`subprocess-ssh`](../packages/ssh/subprocess-ssh) | `ssh` | [`ssh`](../packages/ssh/ssh), [`subprocess`](../packages/subprocess/subprocess), [`subprocess-local`](../packages/subprocess/subprocess-local) | -| [`agent-loop-testkit`](../packages/test-support/agent-loop-testkit) | `test-support` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`agent-model`](../packages/core/agent-model), [`fs-local`](../packages/fs/fs-local), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) | +| [`agent-loop-testkit`](../packages/test-support/agent-loop-testkit) | `test-support` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`fs-local`](../packages/fs/fs-local), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) | | [`subagent`](../packages/subagent/subagent) | `subagent` | [`agent`](../packages/core/agent), [`agent-preset-registry`](../packages/preset/agent-preset-registry), [`attachment`](../packages/attachment/attachment), [`llm`](../packages/llm/llm), [`permission-presets`](../packages/interaction/permission-presets), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`session-query`](../packages/session-query/session-query), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol), [`user-approval`](../packages/interaction/user-approval), [`util-time`](../packages/util/time), [`working-directory`](../packages/session/working-directory), [`workspace`](../packages/workspace/workspace) | | [`spill-policy`](../packages/spill/spill-policy) | `spill` | [`attachment`](../packages/attachment/attachment), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`output-retention`](../packages/util/output-retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`token-meter`](../packages/llm/token-meter), [`tools`](../packages/core/tools) | | [`session-query-sqlite`](../packages/session-query/session-query-sqlite) | `session-query` | [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-query`](../packages/session-query/session-query) | -| [`acp`](../packages/acp/acp) | `acp` | [`agent`](../packages/core/agent), [`agent-model`](../packages/core/agent-model), [`attachment`](../packages/attachment/attachment), [`llm`](../packages/llm/llm), [`mcp-client`](../packages/mcp/mcp-client), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`token-meter`](../packages/llm/token-meter), [`user-approval`](../packages/interaction/user-approval) | +| [`acp`](../packages/acp/acp) | `acp` | [`agent`](../packages/core/agent), [`attachment`](../packages/attachment/attachment), [`llm`](../packages/llm/llm), [`mcp-client`](../packages/mcp/mcp-client), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`token-meter`](../packages/llm/token-meter), [`user-approval`](../packages/interaction/user-approval) | | [`plugin-manager`](../packages/boot/plugin-manager) | `boot` | [`brand`](../packages/util/brand), [`hmr`](../packages/boot/hmr), [`host-plugin-inventory`](../packages/host/plugin-inventory), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`subprocess`](../packages/subprocess/subprocess), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol), [`user-approval`](../packages/interaction/user-approval) | | [`compaction-tool-result-pruner`](../packages/compaction/compaction-tool-result-pruner) | `compaction` | [`compaction`](../packages/compaction/compaction), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) | | [`experimental-browser-use-runtime`](../packages/experimental/browser-use-runtime) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`mcp-client`](../packages/mcp/mcp-client), [`scope`](../packages/core/scope), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) | @@ -1783,9 +1771,9 @@ flowchart TD | [`subagent-spawn-in-process`](../packages/subagent/subagent-spawn-in-process) | `subagent` | [`subagent`](../packages/subagent/subagent) | | [`tool-subagent`](../packages/subagent/tool-subagent) | `subagent` | [`agent`](../packages/core/agent), [`agent-preset-registry`](../packages/preset/agent-preset-registry), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) | | [`tool-subagent-control`](../packages/subagent/tool-subagent-control) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) | -| [`api-session-controller`](../packages/api/session-controller) | `api` | [`agent`](../packages/core/agent), [`agent-controller`](../packages/core/agent-controller), [`agent-default-model`](../packages/core/agent-default-model), [`agent-model`](../packages/core/agent-model), [`agent-preset-registry`](../packages/preset/agent-preset-registry), [`api-gateway`](../packages/api/gateway), [`attachment`](../packages/attachment/attachment), [`client-connection`](../packages/client/connection), [`client-file-upload`](../packages/client/file-upload), [`commands`](../packages/interaction/commands), [`credentials`](../packages/credentials/credentials), [`file-reference`](../packages/context/file-reference), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`native-command`](../packages/util/native-command), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-query`](../packages/session-query/session-query), [`session-title`](../packages/session/session-title), [`settings`](../packages/settings/settings), [`skill`](../packages/skill/skill), [`subagent`](../packages/subagent/subagent), [`typert-protocol`](../packages/typert/protocol), [`typert-registry`](../packages/typert/registry), [`util-time`](../packages/util/time), [`util-values`](../packages/util/values), [`util-workspace-path`](../packages/util/workspace-path), [`working-directory`](../packages/session/working-directory), [`workspace`](../packages/workspace/workspace) | +| [`api-session-controller`](../packages/api/session-controller) | `api` | [`agent`](../packages/core/agent), [`agent-default-model`](../packages/core/agent-default-model), [`agent-preset-registry`](../packages/preset/agent-preset-registry), [`api-gateway`](../packages/api/gateway), [`attachment`](../packages/attachment/attachment), [`client-connection`](../packages/client/connection), [`client-file-upload`](../packages/client/file-upload), [`commands`](../packages/interaction/commands), [`credentials`](../packages/credentials/credentials), [`file-reference`](../packages/context/file-reference), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`native-command`](../packages/util/native-command), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-query`](../packages/session-query/session-query), [`session-title`](../packages/session/session-title), [`settings`](../packages/settings/settings), [`skill`](../packages/skill/skill), [`subagent`](../packages/subagent/subagent), [`typert-protocol`](../packages/typert/protocol), [`typert-registry`](../packages/typert/registry), [`util-time`](../packages/util/time), [`util-values`](../packages/util/values), [`util-workspace-path`](../packages/util/workspace-path), [`working-directory`](../packages/session/working-directory), [`workspace`](../packages/workspace/workspace) | | [`plugin-market`](../packages/boot/plugin-market) | `boot` | [`plugin-manager`](../packages/boot/plugin-manager), [`settings`](../packages/settings/settings), [`subprocess`](../packages/subprocess/subprocess), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol) | -| [`headless`](../packages/bundle/headless) | `bundle` | [`agent`](../packages/core/agent), [`agent-default-model`](../packages/core/agent-default-model), [`agent-model`](../packages/core/agent-model), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`permission-presets`](../packages/interaction/permission-presets), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`session-query`](../packages/session-query/session-query), [`subagent`](../packages/subagent/subagent), [`user-approval`](../packages/interaction/user-approval) | +| [`headless`](../packages/bundle/headless) | `bundle` | [`agent`](../packages/core/agent), [`agent-default-model`](../packages/core/agent-default-model), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`permission-presets`](../packages/interaction/permission-presets), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`session-query`](../packages/session-query/session-query), [`subagent`](../packages/subagent/subagent), [`user-approval`](../packages/interaction/user-approval) | | [`compaction-basic`](../packages/compaction/compaction-basic) | `compaction` | [`agent`](../packages/core/agent), [`commands`](../packages/interaction/commands), [`compaction`](../packages/compaction/compaction), [`compaction-tool-result-pruner`](../packages/compaction/compaction-tool-result-pruner), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) | | [`session-reference`](../packages/context/session-reference) | `context` | [`agent`](../packages/core/agent), [`compaction`](../packages/compaction/compaction), [`llm`](../packages/llm/llm), [`output-retention`](../packages/util/output-retention), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-query`](../packages/session-query/session-query), [`session-title`](../packages/session/session-title), [`spill`](../packages/spill/spill), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`typert-protocol`](../packages/typert/protocol) | | [`experimental-agent-team`](../packages/experimental/agent-team) | `experimental` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`subagent`](../packages/subagent/subagent) | @@ -1801,5 +1789,5 @@ flowchart TD | [`client-test-runtime`](../packages/test-support/client-runtime) | `test-support` | [`api-gateway`](../packages/api/gateway), [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`attachment`](../packages/attachment/attachment), [`client-connection`](../packages/client/connection), [`client-hmr`](../packages/client/hmr), [`client-modules`](../packages/client/modules), [`client-store`](../packages/client/store), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-slots`](../packages/client/ui-slots), [`client-web`](../packages/client/web), [`remote-mock`](../packages/test-support/remote-mock), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`typert-protocol`](../packages/typert/protocol) | | [`subagent-dsh-sdk`](../packages/subagent/subagent-dsh-sdk) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`sdk-client`](../packages/sdk/client), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess) | | [`tool-schedule`](../packages/schedule/tool-schedule) | `schedule` | [`llm`](../packages/llm/llm), [`schedule`](../packages/schedule/schedule), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) | -| [`sdk-jsonrpc-server`](../packages/sdk/server) | `sdk` | [`agent`](../packages/core/agent), [`agent-model`](../packages/core/agent-model), [`attachment`](../packages/attachment/attachment), [`llm`](../packages/llm/llm), [`llm-deepseek-api-key`](../packages/llm/llm-deepseek-api-key), [`schedule`](../packages/schedule/schedule), [`scope`](../packages/core/scope), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`user-questions`](../packages/interaction/user-questions), [`working-directory`](../packages/session/working-directory) | +| [`sdk-jsonrpc-server`](../packages/sdk/server) | `sdk` | [`agent`](../packages/core/agent), [`attachment`](../packages/attachment/attachment), [`llm`](../packages/llm/llm), [`llm-deepseek-api-key`](../packages/llm/llm-deepseek-api-key), [`schedule`](../packages/schedule/schedule), [`scope`](../packages/core/scope), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`user-questions`](../packages/interaction/user-questions), [`working-directory`](../packages/session/working-directory) | diff --git a/docs/persistence-catalog.i18n.yaml b/docs/persistence-catalog.i18n.yaml index 1c5c0c0c43..eea593cfed 100644 --- a/docs/persistence-catalog.i18n.yaml +++ b/docs/persistence-catalog.i18n.yaml @@ -21,8 +21,8 @@ en: 9188862058c6986b zh: 9188862058c6986b /session-persistence-event-catalog/events/agent/agent-inbox-spliced-log-only: - en: 825bef0fa148788c - zh: 26c4e5d564c7af14 + en: 481727894895b43b + zh: 098bc9788bf88968 /session-persistence-event-catalog/events/agent-preset: en: 49891ca9be5ac123 zh: 49891ca9be5ac123 @@ -141,8 +141,8 @@ en: 9edf4ef31e60e880 zh: 9edf4ef31e60e880 /session-persistence-event-catalog/events/model/model-selection-log-only: - en: 7cec7fcca2fde7be - zh: 5952ada542599c75 + en: c2a60ec7d297ed79 + zh: a2f6b2d3c733ce96 /session-persistence-event-catalog/events/permission: en: 19917bc5438e60af zh: 19917bc5438e60af @@ -1524,8 +1524,8 @@ en: 8a9199b1784c70f5 zh: 76c98bc74686fda0 /session-persistence-event-catalog/resolved-persistence-types/inserted-outcome-removedcount-start: - en: 60afe0b0ffb06f19 - zh: b223f77cb78ee702 + en: 346d884a7a74e48d + zh: 646ac1021f9ca920 /session-persistence-event-catalog/resolved-persistence-types/interrupted-message-step-stream: en: ec10cd8e9c202a81 zh: e08f859bcb13f0d4 diff --git a/docs/persistence-catalog.md b/docs/persistence-catalog.md index 4b9a908211..422c3d3aa2 100644 --- a/docs/persistence-catalog.md +++ b/docs/persistence-catalog.md @@ -185,7 +185,7 @@ Sources: [`packages/core/session/src/types.ts:432`](../packages/core/session/src } ``` -Source: [`packages/core/agent/src/types.ts:95`](../packages/core/agent/src/types.ts) +Source: [`packages/core/agent/src/types.ts:110`](../packages/core/agent/src/types.ts) ### `agent-preset/*` @@ -700,7 +700,7 @@ Source: [`packages/llm/llm-retry/src/types.ts:11`](../packages/llm/llm-retry/src 'model/selection': ModelSelection ``` -Source: [`packages/core/agent/src/types.ts:106`](../packages/core/agent/src/types.ts) +Source: [`packages/core/agent/src/types.ts:121`](../packages/core/agent/src/types.ts) ### `permission/*` @@ -6377,7 +6377,7 @@ Sources: [`packages/core/session/src/types.ts:428`](../packages/core/session/src SHA-256: `fa106c5b33b657e8ef16e8cbf39f3b7d44e5cbcdf4f9303d9db6410f28951e42` -Sources: [`packages/core/agent/src/types.ts:95`](../packages/core/agent/src/types.ts) +Sources: [`packages/core/agent/src/types.ts:110`](../packages/core/agent/src/types.ts) | Property | Presence | Type | |---|---|---| diff --git a/docs/persistence-catalog.zh.md b/docs/persistence-catalog.zh.md index 0771335fcf..2691342415 100644 --- a/docs/persistence-catalog.zh.md +++ b/docs/persistence-catalog.zh.md @@ -187,7 +187,7 @@ export type SessionEvent = { } ``` -来源:[`packages/core/agent/src/types.ts:95`](../packages/core/agent/src/types.ts) +来源:[`packages/core/agent/src/types.ts:110`](../packages/core/agent/src/types.ts) ### `agent-preset/*` @@ -702,7 +702,7 @@ export type SessionEvent = { 'model/selection': ModelSelection ``` -来源:[`packages/core/agent/src/types.ts:106`](../packages/core/agent/src/types.ts) +来源:[`packages/core/agent/src/types.ts:121`](../packages/core/agent/src/types.ts) ### `permission/*` @@ -6379,7 +6379,7 @@ SHA-256: `17d1afb770d9941936130996da00dc86782cfef731d8d6526162c301256a4ac3` SHA-256: `fa106c5b33b657e8ef16e8cbf39f3b7d44e5cbcdf4f9303d9db6410f28951e42` -来源:[`packages/core/agent/src/types.ts:95`](../packages/core/agent/src/types.ts) +来源:[`packages/core/agent/src/types.ts:110`](../packages/core/agent/src/types.ts) | 属性 | 存在性 | 类型 | |---|---|---| diff --git a/docs/persistence-schema.json b/docs/persistence-schema.json index ccf0fb1830..3e4f457107 100644 --- a/docs/persistence-schema.json +++ b/docs/persistence-schema.json @@ -26079,7 +26079,7 @@ "digest": "fa106c5b33b657e8ef16e8cbf39f3b7d44e5cbcdf4f9303d9db6410f28951e42", "names": [], "sources": [ - "packages/core/agent/src/types.ts:95" + "packages/core/agent/src/types.ts:110" ] }, { diff --git a/docs/subsystems/core.i18n.yaml b/docs/subsystems/core.i18n.yaml index 8bcc8a91f6..d93bf7cccf 100644 --- a/docs/subsystems/core.i18n.yaml +++ b/docs/subsystems/core.i18n.yaml @@ -12,8 +12,8 @@ en: ab09a38b4e1f0883 zh: 81e2d14c4036ab6c /core/the-agent-handle: - en: 5eb475191d723505 - zh: 5f4dd4fa9e9a88f0 + en: 6ee15fec81bc0686 + zh: b6f3fcd48823ea75 /core/initiating-agent: en: 6c8c47ac78de2dde zh: c9d527a25a85d1d9 diff --git a/docs/subsystems/core.md b/docs/subsystems/core.md index 57e41b9836..e63ce6fb7d 100644 --- a/docs/subsystems/core.md +++ b/docs/subsystems/core.md @@ -65,6 +65,8 @@ interface Agent { readonly options: AgentOptions /** The live session this agent drives; its log is the durable source of truth. */ readonly session: Session + /** Agent-owned access to durable model selection for future steps. */ + readonly model: Model /** Agent-owned access to durable pending work. */ readonly inbox: Inbox /** The current lifecycle state, mirrored on every `agent/status` transition. */ @@ -212,7 +214,31 @@ interface AgentOptions { Step preparation requires `provider` and `model` before prompt assembly. The deprecated `agent/request` hook may change request controls but must preserve that route. An explicit `reasoningEffort` seeds the first request on that route; exact-model resolution validates it, while omission allows the adapter default to materialize. When present, `maxTokens` must be a positive safe integer and caps every conversation-model request; omission allows the exact-model adapter default to materialize before the request header, or otherwise leaves provider behavior unchanged. An agent-scoped `deployment:persona-prefix` prompt section may shadow the global default persona. -`dsh-agent-model` registers `modelSelection` on `ctx.sessionProjections`. `model/selection` records a complete choice without entering model history. A matching request header clears `pending`; the latest request remains `lastUsed`. Adapter-defaulted effort is omitted so later requests resolve the default again. `next` is null only before either a choice or a request is recorded; in that case `AgentOptions` supplies the route. The [model-selection package](../../packages/core/agent-model/README.md) describes selection precedence and resume policy. +`AgentLoop` registers `modelSelection` on `ctx.sessionProjections` and exposes selection operations through each `agent.model` instance. `model/selection` records a complete choice without entering model history. A matching request header clears `pending`; the latest request remains `lastUsed`. Adapter-defaulted effort is omitted so later requests resolve the default again. `next` is null only before either a choice or a request is recorded; in that case `AgentOptions` supplies the route. The [Agent model API](../../packages/core/agent/README.md) describes selection precedence and resume policy. + +```ts type-equiv +/** Agent-owned access to the model choice for future steps. */ +interface Model { + /** + * Resolve the choice for the next step to begin preparation. Recorded choices + * take precedence over the last request and then the Agent's live options. + * This read neither starts a step nor changes an already-prepared step. + * @returns a detached choice, or undefined when no source names both provider and model. + */ + current(): Readonly | undefined + + /** + * Replace the next step's complete selection. Omitted effort follows adapter + * defaults without inheriting a previous effort or resolving defaults here. + * An equal recorded choice is a no-op; an options-only fallback is + * always recorded. Callers own model availability and capability validation. + * The append uses the Session's existing persistence and failure guarantees. + * @param selection - complete provider, model, and optional explicit effort. + */ + select(selection: Readonly): void + +} +``` ```ts type-equiv /** Complete provider, model, and optional reasoning effort chosen for one Session's model steps. */ @@ -228,7 +254,7 @@ interface ModelSelection { ```ts type-equiv /** - * Durable model-selection fold for one Session. An effort the adapter + * Durable model-selection state for one Session. An effort the adapter * defaulted on a recorded request is omitted, so later requests resolve the * default again instead of pinning it as a choice. */ @@ -544,45 +570,6 @@ Types: [SessionHeader](persistence.md) Source: [`packages/core/agent-loop/src/index.ts`](../../packages/core/agent-loop/src/index.ts) - - -### `ctx.agentModel` — `AgentModel` - -Read and record Session model choices without resolving adapter defaults. - -```ts cordis-catalog -/** - * Resolve the choice for the next step to begin preparation. Recorded choices - * take precedence over the last request and then the Agent's live options. - * This read neither starts a step nor changes an already-prepared step. - * @param agent - Agent whose Session and fallback options are read. - * @returns a detached choice, or undefined when no source names both provider and model. - */ -resolveForNextStep(agent: Agent): Readonly | undefined - -/** - * Replace the next step's complete selection. Omitted effort follows adapter - * defaults. An equal recorded choice is a no-op; an options-only fallback is - * always recorded. Callers own model availability and capability validation. - * The append uses the Session's existing persistence and failure guarantees. - * @param agent - Agent whose Session receives the model/selection event. - * @param selection - complete provider, model, and optional explicit effort. - */ -select(agent: Agent, selection: Readonly): void - -/** - * Apply an explicit deployment route, including on Session resume. An omitted - * effort inherits the current explicit effort only for the same route; - * otherwise it follows adapter defaults. Call only when the entry point - * explicitly overrides recorded selection, not for ordinary resume fallback. - * @param agent - Agent whose Session receives the configured choice. - * @param selection - complete configured provider/model and optional effort. - */ -applyConfiguredRoute(agent: Agent, selection: Readonly): void -``` - -Source: [`packages/core/agent-model/src/service.ts`](../../packages/core/agent-model/src/service.ts) - ### `ctx.agentPresets` — `AgentPresetRegistry` diff --git a/docs/subsystems/core.zh.md b/docs/subsystems/core.zh.md index 230cfb8331..1187ce6f9d 100644 --- a/docs/subsystems/core.zh.md +++ b/docs/subsystems/core.zh.md @@ -69,6 +69,8 @@ interface Agent { readonly options: AgentOptions /** The live session this agent drives; its log is the durable source of truth. */ readonly session: Session + /** Agent-owned access to durable model selection for future steps. */ + readonly model: Model /** Agent-owned access to durable pending work. */ readonly inbox: Inbox /** The current lifecycle state, mirrored on every `agent/status` transition. */ @@ -216,7 +218,31 @@ interface AgentOptions { 步骤准备在提示词组装前要求 `provider` 与 `model` 都存在。已弃用的 `agent/request` hook 可以更改请求控制参数,但必须保留该路由。显式 `reasoningEffort` 会为该路由的首次请求提供初始值;确切模型解析会校验该值,省略时则允许填入适配器默认值。提供 `maxTokens` 时,它必须是正安全整数,并限制每次对话模型请求的输出;省略时,系统会在写入请求 header 前填入确切模型的适配器默认值,否则提供方行为保持不变。agent 作用域的 `deployment:persona-prefix` 提示词段落可以遮蔽全局默认 persona。 -`dsh-agent-model` 在 `ctx.sessionProjections` 上注册 `modelSelection`。`model/selection` 记录完整选择,但不进入模型历史。匹配的请求头会清除 `pending`,最近请求仍保留为 `lastUsed`。适配器默认的推理强度会被省略,使后续请求重新解析默认值。只有尚未记录选择或请求时,`next` 才为 null,此时由 `AgentOptions` 提供路由。[模型选择包](../../packages/core/agent-model/README.zh.md)说明选择优先级与恢复策略。 +`AgentLoop` 在 `ctx.sessionProjections` 上注册 `modelSelection`,并通过每个 `agent.model` 实例提供选择操作。`model/selection` 记录完整选择,但不进入模型历史。匹配的请求头会清除 `pending`,最近请求仍保留为 `lastUsed`。适配器默认的推理强度会被省略,使后续请求重新解析默认值。只有尚未记录选择或请求时,`next` 才为 null,此时由 `AgentOptions` 提供路由。[Agent 模型 API](../../packages/core/agent/README.zh.md)说明选择优先级与恢复策略。 + +```ts type-equiv +/** Agent-owned access to the model choice for future steps. */ +interface Model { + /** + * Resolve the choice for the next step to begin preparation. Recorded choices + * take precedence over the last request and then the Agent's live options. + * This read neither starts a step nor changes an already-prepared step. + * @returns a detached choice, or undefined when no source names both provider and model. + */ + current(): Readonly | undefined + + /** + * Replace the next step's complete selection. Omitted effort follows adapter + * defaults without inheriting a previous effort or resolving defaults here. + * An equal recorded choice is a no-op; an options-only fallback is + * always recorded. Callers own model availability and capability validation. + * The append uses the Session's existing persistence and failure guarantees. + * @param selection - complete provider, model, and optional explicit effort. + */ + select(selection: Readonly): void + +} +``` ```ts type-equiv /** Complete provider, model, and optional reasoning effort chosen for one Session's model steps. */ @@ -232,7 +258,7 @@ interface ModelSelection { ```ts type-equiv /** - * Durable model-selection fold for one Session. An effort the adapter + * Durable model-selection state for one Session. An effort the adapter * defaulted on a recorded request is omitted, so later requests resolve the * default again instead of pinning it as a choice. */ @@ -556,45 +582,6 @@ Types: [SessionHeader](persistence.zh.md) Source: [`packages/core/agent-loop/src/index.ts`](../../packages/core/agent-loop/src/index.ts) - - -### `ctx.agentModel` — `AgentModel` - -Read and record Session model choices without resolving adapter defaults. - -```ts cordis-catalog -/** - * Resolve the choice for the next step to begin preparation. Recorded choices - * take precedence over the last request and then the Agent's live options. - * This read neither starts a step nor changes an already-prepared step. - * @param agent - Agent whose Session and fallback options are read. - * @returns a detached choice, or undefined when no source names both provider and model. - */ -resolveForNextStep(agent: Agent): Readonly | undefined - -/** - * Replace the next step's complete selection. Omitted effort follows adapter - * defaults. An equal recorded choice is a no-op; an options-only fallback is - * always recorded. Callers own model availability and capability validation. - * The append uses the Session's existing persistence and failure guarantees. - * @param agent - Agent whose Session receives the model/selection event. - * @param selection - complete provider, model, and optional explicit effort. - */ -select(agent: Agent, selection: Readonly): void - -/** - * Apply an explicit deployment route, including on Session resume. An omitted - * effort inherits the current explicit effort only for the same route; - * otherwise it follows adapter defaults. Call only when the entry point - * explicitly overrides recorded selection, not for ordinary resume fallback. - * @param agent - Agent whose Session receives the configured choice. - * @param selection - complete configured provider/model and optional effort. - */ -applyConfiguredRoute(agent: Agent, selection: Readonly): void -``` - -Source: [`packages/core/agent-model/src/service.ts`](../../packages/core/agent-model/src/service.ts) - ### `ctx.agentPresets` — `AgentPresetRegistry` diff --git a/docs/upgrade-guide/v0.2.1-alpha.4/session-model-selection/guide.i18n.yaml b/docs/upgrade-guide/v0.2.1-alpha.4/session-model-selection/guide.i18n.yaml index 646e6edb57..f8172fbf7f 100644 --- a/docs/upgrade-guide/v0.2.1-alpha.4/session-model-selection/guide.i18n.yaml +++ b/docs/upgrade-guide/v0.2.1-alpha.4/session-model-selection/guide.i18n.yaml @@ -9,8 +9,8 @@ en: ac99540fa45af789 zh: 68b33ea8d0063615 /session-model-selection-applies-at-the-next-step/change: - en: 438e301c5fed424b - zh: 164d70fed66fcd45 + en: 5ab837106ca3bd0c + zh: 5a6d2e65f3cdb191 /session-model-selection-applies-at-the-next-step/migration: - en: 32a29602f2487011 - zh: ae596527ef1c97b3 + en: 5fdebcef986b671a + zh: 5ec5bfc9cf6fa443 diff --git a/docs/upgrade-guide/v0.2.1-alpha.4/session-model-selection/guide.md b/docs/upgrade-guide/v0.2.1-alpha.4/session-model-selection/guide.md index 3d3c7b08ab..dd24132dfd 100644 --- a/docs/upgrade-guide/v0.2.1-alpha.4/session-model-selection/guide.md +++ b/docs/upgrade-guide/v0.2.1-alpha.4/session-model-selection/guide.md @@ -11,18 +11,18 @@ English | [中文](guide.zh.md) ACP model and reasoning changes take effect at the next model step, including a tool continuation in the current turn. Previously, ACP pinned the selection for the complete prompt turn. A prepared step and its retries retain their captured selection. Image admission checks the route selected when admission begins; a later option change can select a different route before the step starts. -`dsh-agent-model` provides `ctx.agentModel` and owns the durable `modelSelection` projection. The API Session controller no longer registers it. Programmatic resume restores recorded choices, including unused selections, before falling back to `AgentOptions`. Configured Agent-loop resumes, headless adoption, and SDK resume record their deployment route; an omitted effort preserves saved explicit effort only for the same route. +`AgentLoop` owns the durable `modelSelection` projection and constructs an `AgentModel` instance on each Agent’s `model` property. The API Session controller no longer registers it. Programmatic resume restores recorded choices, including unused selections, before falling back to `AgentOptions`. Configured Agent-loop resumes, headless adoption, and SDK resume record their deployment route; an omitted effort preserves saved explicit effort only for the same route. -Plugins must select provider, model, and optional explicit reasoning effort before prompt assembly. `agent/request` is deprecated. Existing listeners still run on each main-loop request attempt and can adjust other controls, but cannot change provider or model, or add, change, or clear reasoning effort. Absent effort still resolves through adapter defaults on each attempt. Auxiliary calls such as compaction do not dispatch this event. `installModelSelection` and `ModelSelectionRef` are removed. +Plugins must select provider, model, and optional explicit reasoning effort before prompt assembly. `agent/request` is deprecated. Existing listeners still run on each main-loop request attempt and can adjust other controls, but cannot change provider or model, or add, change, or clear reasoning effort. Absent effort still resolves through adapter defaults on each attempt. Auxiliary calls such as compaction do not dispatch this event. `installModelSelection` now installs only the switch-notice listener; its selection argument and `ModelSelectionRef` are removed. -The client projection and Host state types move to `dsh-agent-model/types`; the API package no longer exports them. +The client projection and Host state types move to `dsh-agent/types`; the API package no longer exports them. ## Migration -1. In custom Cordis compositions, mount `dsh-session-projection` and `dsh-agent-model`. Consumers inject `agentModel`; shipped bundles include it. -2. Replace mutable references and selection hooks with `ctx.agentModel.select(agent, selection)` after validation. Read with `resolveForNextStep(agent)`. Replace switch-notice helpers by importing the `@deepseek-ai/dsh-agent-model/notice` plugin and awaiting `agentCtx.plugin(ModelSwitchNotice)` in Agent setup. Do not add `agent/request` listeners. +1. In custom Cordis compositions, mount `dsh-session-projection` with `dsh-agent-loop`. The loop constructs `agent.model`; consumers need no separate model-selection service. +2. Replace mutable references and selection hooks with `agent.model.select(selection)` after validation. Read with `current()`. Keep `installModelSelection(agentCtx)` in Agent setup for switch notices and remove its old selection argument. Do not add `agent/request` listeners. 3. Set the output cap through `AgentOptions.maxTokens`. Other per-request control overrides have no replacement API; retain an existing listener where that behavior is still required. -4. To override a saved route during programmatic resume, call `ctx.agentModel.applyConfiguredRoute(agent, selection)` in the Agent setup callback. Supplying `agentOptions` alone provides only a fallback for a Session without a selection or request. +4. To override a saved route during programmatic resume, merge the configured selection with the saved explicit effort only when the route is unchanged and the configuration omits effort, then call `agent.model.select(selection)` in the Agent setup callback. Supplying `agentOptions` alone provides only a fallback for a Session without a selection or request. 5. Pass the captured `Readonly` in `agent/pre-step.callConfig`, including explicit effort when selected; `AssembleContext.callConfig` carries the same selection. Direct `SessionReferenceResolver.prepare()` callers pass the route as the fourth argument, before the optional signal. 6. ACP controllers that require one model throughout a turn must wait for the active prompt to finish before changing its options. Verify that a mid-turn change is recorded as `model/selection`, the next step uses it, and resume retains an unused choice. -7. Import `ModelSelectionProjection` from `@deepseek-ai/dsh-agent-model/types`, replacing Agent/API package aliases. `ModelSelection` remains in `dsh-agent/types`. Replace named Host state imports with `SessionProjectionStateMap['modelSelection']` after importing the new `/types` module; ordinary reads use `resolveForNextStep()`. Effort values use `ReasoningEffortId` from `dsh-llm/brand`. +7. Import `ModelSelectionProjection` from `@deepseek-ai/dsh-agent/types`, replacing API package aliases. `ModelSelection` remains in `dsh-agent/types`. Import `ModelSelectionProjectionState` from `@deepseek-ai/dsh-agent` only when implementing a driver; ordinary reads use `agent.model.current()`. Effort values use `ReasoningEffortId` from `dsh-llm/brand`. diff --git a/docs/upgrade-guide/v0.2.1-alpha.4/session-model-selection/guide.zh.md b/docs/upgrade-guide/v0.2.1-alpha.4/session-model-selection/guide.zh.md index 14fd511f0e..302c77d618 100644 --- a/docs/upgrade-guide/v0.2.1-alpha.4/session-model-selection/guide.zh.md +++ b/docs/upgrade-guide/v0.2.1-alpha.4/session-model-selection/guide.zh.md @@ -11,18 +11,18 @@ description: "Session 模型选择在步骤准备时固定,投影类型移至 ACP(Agent Client Protocol)的模型与推理强度变更从下一个模型步骤开始生效,包括当前轮次的工具后续步骤。此前,ACP 会在整个提示词轮次中固定选择。已准备的步骤及其重试保留捕获的选择。图片准入检查准入开始时选择的路由;后续选项变更可能在步骤开始前选中另一条路由。 -`dsh-agent-model` 提供 `ctx.agentModel`,并拥有持久 `modelSelection` 投影。API Session 控制器不再注册该投影。程序化恢复优先还原记录中的选择,包括尚未使用的选择,然后才回退到 `AgentOptions`。配置式 Agent-loop 恢复、headless 接管与 SDK 恢复会记录各自的部署路由;省略推理强度时,仅在路由相同的情况下保留已保存的显式强度。 +`AgentLoop` 拥有持久 `modelSelection` 投影,并为每个 Agent 的 `model` 属性构建一个 `AgentModel` 实例。API Session 控制器不再注册该投影。程序化恢复优先还原记录中的选择,包括尚未使用的选择,然后才回退到 `AgentOptions`。配置式 Agent-loop 恢复、headless 接管与 SDK 恢复会记录各自的部署路由;省略推理强度时,仅在路由相同的情况下保留已保存的显式强度。 -插件必须在提示词组装前选定提供方、模型及可选的显式推理强度。`agent/request` 已弃用。现有监听器仍在主循环的每次请求尝试中运行,可以调整其他控制参数,但不能改变提供方或模型,也不能添加、修改或清除推理强度。未指定的强度仍在每次尝试时通过适配器默认值解析。压缩等辅助调用不会分发该事件。`installModelSelection` 与 `ModelSelectionRef` 已移除。 +插件必须在提示词组装前选定提供方、模型及可选的显式推理强度。`agent/request` 已弃用。现有监听器仍在主循环的每次请求尝试中运行,可以调整其他控制参数,但不能改变提供方或模型,也不能添加、修改或清除推理强度。未指定的强度仍在每次尝试时通过适配器默认值解析。压缩等辅助调用不会分发该事件。`installModelSelection` 现在只安装切换提示监听器;其选择参数与 `ModelSelectionRef` 已移除。 -客户端投影与 Host 状态类型移至 `dsh-agent-model/types`;API 包不再导出这些类型。 +客户端投影与 Host 状态类型移至 `dsh-agent/types`;API 包不再导出这些类型。 ## 迁移 -1. 在自定义 Cordis 组合中挂载 `dsh-session-projection` 与 `dsh-agent-model`。消费方注入 `agentModel`;随附 bundle 已包含该服务。 -2. 将可变引用与选择 hook 替换为校验后的 `ctx.agentModel.select(agent, selection)`,使用 `resolveForNextStep(agent)` 读取选择。将切换提示 helper 替换为导入 `@deepseek-ai/dsh-agent-model/notice` 插件,并在 Agent setup 中等待 `agentCtx.plugin(ModelSwitchNotice)`。不要添加 `agent/request` 监听器。 +1. 在自定义 Cordis 组合中将 `dsh-session-projection` 与 `dsh-agent-loop` 一起挂载。循环构建 `agent.model`;消费方不需要独立的模型选择服务。 +2. 将可变引用与选择 hook 替换为校验后的 `agent.model.select(selection)`,使用 `current()` 读取选择。在 Agent setup 中保留 `installModelSelection(agentCtx)` 以提供切换提示,并移除原来的选择参数。不要添加 `agent/request` 监听器。 3. 通过 `AgentOptions.maxTokens` 设置输出上限。其他逐请求控制参数覆盖没有替代 API;仍需要该行为时,保留现有监听器。 -4. 要在程序化恢复时覆盖已保存路由,在 Agent setup 回调中调用 `ctx.agentModel.applyConfiguredRoute(agent, selection)`。仅提供 `agentOptions` 时,它只为没有选择或请求记录的 Session 提供回退值。 +4. 要在程序化恢复时覆盖已保存路由,先形成配置选择,仅在路由不变且配置省略强度时保留已保存的显式强度,再在 Agent setup 回调中调用 `agent.model.select(selection)`。仅提供 `agentOptions` 时,它只为没有选择或请求记录的 Session 提供回退值。 5. 通过 `agent/pre-step.callConfig` 传入捕获的 `Readonly`,包含已选定的显式强度;`AssembleContext.callConfig` 携带同一选择。直接调用 `SessionReferenceResolver.prepare()` 时,将路由作为第四个参数,放在可选信号之前。 6. 要求整轮使用同一个模型的 ACP 控制器,必须等待活跃提示词完成后再更改选项。验证轮次中的变更被记录为 `model/selection`、下一个步骤使用该选择,且恢复保留尚未使用的选择。 -7. 从 `@deepseek-ai/dsh-agent-model/types` 导入 `ModelSelectionProjection`,替换 Agent/API 包别名。`ModelSelection` 仍位于 `dsh-agent/types`。导入新 `/types` 模块后,将具名 Host 状态导入替换为 `SessionProjectionStateMap['modelSelection']`;普通读取使用 `resolveForNextStep()`。强度值使用 `dsh-llm/brand` 中的 `ReasoningEffortId`。 +7. 从 `@deepseek-ai/dsh-agent/types` 导入 `ModelSelectionProjection`,替换 API 包别名。`ModelSelection` 仍位于 `dsh-agent/types`。仅在实现驱动器时从 `@deepseek-ai/dsh-agent` 导入 `ModelSelectionProjectionState`;普通读取使用 `agent.model.current()`。强度值使用 `dsh-llm/brand` 中的 `ReasoningEffortId`。 diff --git a/packages/acp/acp/package.json b/packages/acp/acp/package.json index 78f04639cd..791d834e43 100644 --- a/packages/acp/acp/package.json +++ b/packages/acp/acp/package.json @@ -40,8 +40,7 @@ "@deepseek-ai/dsh-session": "workspace:*", "@deepseek-ai/dsh-session-persistence": "workspace:*", "@deepseek-ai/dsh-token-meter": "workspace:*", - "@deepseek-ai/dsh-user-approval": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-user-approval": "workspace:*" }, "peerDependenciesMeta": { "@deepseek-ai/dsh-token-meter": { @@ -63,7 +62,6 @@ "@deepseek-ai/dsh-subagent": "workspace:*", "@deepseek-ai/dsh-token-meter": "workspace:*", "@deepseek-ai/dsh-tools": "workspace:*", - "@deepseek-ai/dsh-user-approval": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-user-approval": "workspace:*" } } diff --git a/packages/acp/acp/src/index.ts b/packages/acp/acp/src/index.ts index 32211c1856..c05ccbf3e5 100644 --- a/packages/acp/acp/src/index.ts +++ b/packages/acp/acp/src/index.ts @@ -16,7 +16,6 @@ import { realpath } from 'node:fs/promises' import { isAbsolute, resolve } from 'node:path' import { Readable, Writable } from 'node:stream' import Schema from '@deepseek-ai/schemastery' -import type {} from '@deepseek-ai/dsh-agent-model' import { brandString } from '@deepseek-ai/dsh-brand' import { errorChain } from '@deepseek-ai/dsh-llm' import { @@ -59,7 +58,7 @@ const DEFAULT_SESSION_LIST_PAGE_SIZE = 100 export const name = 'acp' /** Core services required by the standard automation controls. */ -export const inject = ['agents', 'llm', 'sessionPersistence', 'agentModel', 'sessions'] +export const inject = ['agents', 'llm', 'sessionPersistence', 'sessions'] /** Preserve invalid-parameter detail in the SDK wire error message. */ function invalidParams(detail: string): RequestError { diff --git a/packages/acp/acp/src/model-control.ts b/packages/acp/acp/src/model-control.ts index 95541c9d54..3982d32438 100644 --- a/packages/acp/acp/src/model-control.ts +++ b/packages/acp/acp/src/model-control.ts @@ -1,8 +1,7 @@ /** Standard ACP session configuration over one Agent's model selection. */ import type { SessionConfigOption, SessionConfigValueId } from '@agentclientprotocol/sdk' -import { type Agent, type ModelSelection } from '@deepseek-ai/dsh-agent' -import type { AgentModel } from '@deepseek-ai/dsh-agent-model' +import { type Model, type ModelSelection } from '@deepseek-ai/dsh-agent' import { ReasoningEffortId, type LlmCallConfig, type LlmRuntime } from '@deepseek-ai/dsh-llm' const MODEL_CONFIG_ID = 'model' @@ -39,8 +38,7 @@ export class AcpModelControl { constructor( private readonly llm: LlmRuntime, - private readonly agentModel: AgentModel, - private readonly agent: Agent, + private readonly model: Model, ) {} /** @@ -48,7 +46,7 @@ export class AcpModelControl { * @returns a detached selection, or undefined when neither the Session nor the deployment names a route. */ current(): ModelSelection | undefined { - return this.agentModel.resolveForNextStep(this.agent) + return this.model.current() } /** @@ -77,7 +75,7 @@ export class AcpModelControl { const selected = state.choices.get(value) if (selected === undefined) throw new AcpModelConfigError(`unknown model option: ${value}`) await this.resolveSelection(selected, signal) - this.agentModel.select(this.agent, selected) + this.model.select(selected) } else if (configId === REASONING_CONFIG_ID) { const info = await this.llm.resolveModelInfo(current.provider, current.model, signal) const providerDefault = value === PROVIDER_DEFAULT_REASONING_VALUE @@ -88,7 +86,7 @@ export class AcpModelControl { ) { throw new AcpModelConfigError(`unknown reasoning effort for ${current.provider}/${current.model}: ${value}`) } - this.agentModel.select(this.agent, await this.resolveSelection({ + this.model.select(await this.resolveSelection({ provider: current.provider, model: current.model, ...providerDefault ? {} : { reasoningEffort: ReasoningEffortId(value) }, diff --git a/packages/acp/acp/src/session.ts b/packages/acp/acp/src/session.ts index b8243e71ce..4fd1e0f730 100644 --- a/packages/acp/acp/src/session.ts +++ b/packages/acp/acp/src/session.ts @@ -11,7 +11,7 @@ import { type StopReason, } from '@agentclientprotocol/sdk' import type { Agent, AgentHandle, AgentOptions, AgentStatus } from '@deepseek-ai/dsh-agent' -import * as ModelSwitchNotice from '@deepseek-ai/dsh-agent-model/notice' +import { installModelSelection } from '@deepseek-ai/dsh-agent' import { createUserMessage, errorChain, type UserMessage } from '@deepseek-ai/dsh-llm' import { type Session, type SessionEvent, type SessionId, type TurnEndReason } from '@deepseek-ai/dsh-session' import { AcpContentError, admitAcpPrompt } from './content.ts' @@ -80,7 +80,7 @@ function internalError(detail: string): RequestError { * @param options - workspace and MCP servers requested for the session. */ async function composeAgent(agentCtx: Context, options: AcpSessionBuildOptions): Promise { - await agentCtx.plugin(ModelSwitchNotice) + installModelSelection(agentCtx) await mountAcpMcpServers(agentCtx, options.mcpServers, options.cwd) } @@ -103,7 +103,7 @@ export class AcpSession { private readonly notify: (notification: SessionNotification) => Promise, ) { this.agent = handle.agent - this.modelControl = new AcpModelControl(ctx.llm, ctx.agentModel, handle.agent) + this.modelControl = new AcpModelControl(ctx.llm, handle.agent.model) this.disposeAgent = () => handle.dispose() } diff --git a/packages/acp/acp/tests/bridge.spec.ts b/packages/acp/acp/tests/bridge.spec.ts index 9b3d21a1ea..b0911a4f9b 100644 --- a/packages/acp/acp/tests/bridge.spec.ts +++ b/packages/acp/acp/tests/bridge.spec.ts @@ -862,7 +862,7 @@ describe('automation-only ACP bridge', () => { expect(created.configOptions).toEqual([]) const agent = harness.ctx.agents.get(SessionId(created.sessionId))! - harness.ctx.agentModel.select(agent, { provider: 'mock', model: 'mock' }) + agent.model.select({ provider: 'mock', model: 'mock' }) await expect(harness.client.prompt({ sessionId: created.sessionId, prompt: [{ type: 'text', text: 'route me' }], diff --git a/packages/acp/acp/tests/model-control.spec.ts b/packages/acp/acp/tests/model-control.spec.ts index 701b39b72e..22eaf45537 100644 --- a/packages/acp/acp/tests/model-control.spec.ts +++ b/packages/acp/acp/tests/model-control.spec.ts @@ -1,6 +1,6 @@ import { Context } from '@deepseek-ai/cordis' -import AgentRegistry, { type Agent, type ModelSelection } from '@deepseek-ai/dsh-agent' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' +import { AgentModel, modelSelectionProjectionDefinition } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { type ModelSelection } from '@deepseek-ai/dsh-agent' import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { describe, expect, it, onTestFinished, vi } from 'vitest' @@ -40,11 +40,12 @@ async function createControl(runtime: LlmRuntime, initial?: ModelSelection) { onTestFinished(() => ctx.fiber.dispose()) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const session = ctx.sessions.create(SessionId('model-control')) - const agent = { id: session.id, session, options: initial ?? {} } as Agent - return { control: new AcpModelControl(runtime, ctx.agentModel, agent), session } + const options = initial ?? {} + const projection = await ctx.sessionProjections.register(modelSelectionProjectionDefinition) + const model = new AgentModel(() => projection.stateOf(session), session, options) + return { control: new AcpModelControl(runtime, model), session } } describe('ACP model configuration control', () => { diff --git a/packages/acp/acp/tsconfig.json b/packages/acp/acp/tsconfig.json index 92a0e595f2..ea1239b9f1 100644 --- a/packages/acp/acp/tsconfig.json +++ b/packages/acp/acp/tsconfig.json @@ -40,9 +40,6 @@ }, { "path": "../../interaction/user-approval" - }, - { - "path": "../../core/agent-model" } ] } diff --git a/packages/api/job-controller/tests/host-harness.ts b/packages/api/job-controller/tests/host-harness.ts index b416929a87..78e6e2ba8c 100644 --- a/packages/api/job-controller/tests/host-harness.ts +++ b/packages/api/job-controller/tests/host-harness.ts @@ -1,7 +1,7 @@ import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { Context } from '@deepseek-ai/cordis' import AgentRegistry from '@deepseek-ai/dsh-agent' -import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import type { Agent } from '@deepseek-ai/dsh-agent' import type { JobAppendOptions, JobHandle, JobOutcome } from '@deepseek-ai/dsh-jobs' import LocalJobRegistry from '@deepseek-ai/dsh-jobs-local' @@ -26,6 +26,7 @@ export async function registerAgent(ctx: Context, rawId: string): Promise { id: session.id, options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'idle', ctx, diff --git a/packages/api/session-controller/README.i18n.yaml b/packages/api/session-controller/README.i18n.yaml index 39bc26808c..bfca49ba57 100644 --- a/packages/api/session-controller/README.i18n.yaml +++ b/packages/api/session-controller/README.i18n.yaml @@ -21,8 +21,8 @@ en: 41bdda1ac32df558 zh: aa5d0e325924066b /session-controller/session-media-references: - en: 2f5524fbfc9446e4 - zh: f72c0bc9607007ae + en: 9449c5c3a53031c6 + zh: 68d8ad0d3632983a /session-controller/configuration: en: 5ae2b340c630897f zh: 1ffbd61ecf302ec2 diff --git a/packages/api/session-controller/README.md b/packages/api/session-controller/README.md index cd0eafc42d..1ac2148792 100644 --- a/packages/api/session-controller/README.md +++ b/packages/api/session-controller/README.md @@ -90,7 +90,7 @@ References keep local Session data, scoped Contexts, and history streams alive, GUI model selection requires the exact provider/model pair in the available catalog; unavailable choices reject with `session/model-unavailable`. Prompt admission retains the saved route without a catalog availability gate, so request execution reports missing credentials or unavailable models. `initializeDefaultModel()` saves the first available account model after login when no other provider has a configured API key; credential checks use configured references independently of model availability. A provider with no available models rejects initialization with `session/provider-models-unavailable`. Availability never substitutes another model or rewrites a Session selection. -A successful `selectModel` response acknowledges the recorded Session selection without waiting for the default profile setting to save. The Agent registry owns its projection; a change applies to the next step while a prepared step retains its captured choice. Default saves run in the background in submission order; a failure logs a warning and leaves the Session selection intact. A Session with no recorded selection or request reads the last successfully saved default through live `AgentOptions` accessors. An explicit choice is recorded even when it equals that default. +A successful `selectModel` response acknowledges the recorded Session selection without waiting for the default profile setting to save. The loop registers its projection; a change applies to the next step while a prepared step retains its captured choice. Default saves run in the background in submission order; a failure logs a warning and leaves the Session selection intact. A Session with no recorded selection or request reads the last successfully saved default through live `AgentOptions` accessors. An explicit choice is recorded even when it equals that default. ----- diff --git a/packages/api/session-controller/README.zh.md b/packages/api/session-controller/README.zh.md index 56bde2dcdb..1a89a27cb5 100644 --- a/packages/api/session-controller/README.zh.md +++ b/packages/api/session-controller/README.zh.md @@ -90,7 +90,7 @@ Client 的首次 `follow`、重连首屏与 `loadOlder()` 至少请求 50 条以 GUI 模型选择要求确切提供方/模型对出现在可用目录中;不可用的选择以 `session/model-unavailable` 拒绝。提示词准入保留已保存的路由,不按目录可用性阻断发送,由请求执行报告凭据缺失或模型不可用。`initializeDefaultModel()` 在账号登录后、其他提供方均未配置 API key 时,将第一个可用账号模型保存为默认模型;凭据检查使用已配置的引用,不依赖模型是否可用。提供方没有可用模型时,初始化以 `session/provider-models-unavailable` 拒绝。可用性变化不会替换模型或改写会话选择。 -`selectModel` 成功返回表示会话模型选择已记录,不等待默认 profile 设置保存。Agent 注册表拥有该投影;变更从下一个步骤开始生效,已准备的步骤保留捕获的选择。默认设置在后台按提交顺序保存;保存失败会记录警告,并保留会话选择。尚未记录选择或请求的会话通过 `AgentOptions` 的动态访问器读取最近一次成功保存的默认值。显式选择即使与默认值相同也会记录。 +`selectModel` 成功返回表示会话模型选择已记录,不等待默认 profile 设置保存。循环注册该投影;变更从下一个步骤开始生效,已准备的步骤保留捕获的选择。默认设置在后台按提交顺序保存;保存失败会记录警告,并保留会话选择。尚未记录选择或请求的会话通过 `AgentOptions` 的动态访问器读取最近一次成功保存的默认值。显式选择即使与默认值相同也会记录。 ----- diff --git a/packages/api/session-controller/package.json b/packages/api/session-controller/package.json index dd06e8e65c..faf27a4fa3 100644 --- a/packages/api/session-controller/package.json +++ b/packages/api/session-controller/package.json @@ -108,8 +108,7 @@ "@deepseek-ai/dsh-util-values": "workspace:*", "@deepseek-ai/dsh-util-workspace-path": "workspace:*", "@deepseek-ai/dsh-workspace": "workspace:*", - "@deepseek-ai/dsh-working-directory": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-working-directory": "workspace:*" }, "peerDependenciesMeta": { "@deepseek-ai/dsh-session-persistence": { @@ -167,7 +166,6 @@ "@deepseek-ai/dsh-util-workspace-path": "workspace:*", "@deepseek-ai/dsh-workspace": "workspace:*", "@types/mime-types": "^3.0.1", - "@deepseek-ai/dsh-working-directory": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-working-directory": "workspace:*" } } diff --git a/packages/api/session-controller/src/agent.ts b/packages/api/session-controller/src/agent.ts index e583fdee35..4467fefc0f 100644 --- a/packages/api/session-controller/src/agent.ts +++ b/packages/api/session-controller/src/agent.ts @@ -5,7 +5,7 @@ import { mkdir } from 'node:fs/promises' import type { Context } from '@deepseek-ai/cordis' import type { Agent, AgentOptions, AgentSetup } from '@deepseek-ai/dsh-agent' import type {} from '@deepseek-ai/dsh-agent-default-model' -import * as ModelSwitchNotice from '@deepseek-ai/dsh-agent-model/notice' +import { installModelSelection } from '@deepseek-ai/dsh-agent' import type {} from '@deepseek-ai/dsh-agent-preset-registry' import { brandString } from '@deepseek-ai/dsh-brand' import type { Session, SessionId } from '@deepseek-ai/dsh-session' @@ -306,13 +306,13 @@ export class ApiSessionAgentController { async composeAgent(presetId: string | undefined): Promise { const presets = this.ctx.get('agentPresets') if (presets === undefined) { - return { setup: async (agentCtx) => { await agentCtx.plugin(ModelSwitchNotice) } } + return { setup: (agentCtx) => { installModelSelection(agentCtx) } } } const resolvedId = (await presets.resolve(presetId)).id return { agentPreset: resolvedId, setup: async (agentCtx) => { - await agentCtx.plugin(ModelSwitchNotice) + installModelSelection(agentCtx) await presets.mount(agentCtx, resolvedId) }, } diff --git a/packages/api/session-controller/src/commands.ts b/packages/api/session-controller/src/commands.ts index 1d051de604..8f55c2f609 100644 --- a/packages/api/session-controller/src/commands.ts +++ b/packages/api/session-controller/src/commands.ts @@ -173,7 +173,7 @@ export class SessionCommandController { ? {} : { reasoningEffort: resolved.reasoningEffort }), } - this.ctx.agentModel.select(agent, selected) + agent.model.select(selected) void this.ctx.agentDefaultModel.saveSelection(selected).catch((error: unknown) => { this.ctx.logger.warn( `session-controller: model selection changed for the Session but the default was not saved: ${String(error)}`, @@ -323,7 +323,7 @@ export class SessionCommandController { const admit = async (): Promise => { try { if (hasImage) { - const current = this.ctx.agentModel.resolveForNextStep(agent) + const current = agent.model.current() if (current !== undefined) { const model = await this.ctx.llm.resolveModelInfo(current.provider, current.model) if (model.inputModalities !== undefined && !model.inputModalities.includes('image')) { diff --git a/packages/api/session-controller/src/index.ts b/packages/api/session-controller/src/index.ts index 7806e151e6..5eeb7ae03d 100644 --- a/packages/api/session-controller/src/index.ts +++ b/packages/api/session-controller/src/index.ts @@ -3,7 +3,6 @@ import { hostname } from 'node:os' import { resolve } from 'node:path' import type { Agent } from '@deepseek-ai/dsh-agent' -import type {} from '@deepseek-ai/dsh-agent-model' import type {} from '@deepseek-ai/dsh-fs' import { Context, Service } from '@deepseek-ai/cordis' import z from '@deepseek-ai/schemastery' @@ -127,7 +126,6 @@ export class SessionController extends TypertRemoteService { 'remoteAccess', 'sessions', 'sessionProjections', - 'agentModel', 'sessionQuery', 'typert', 'workspaceRegistry', diff --git a/packages/api/session-controller/src/types.ts b/packages/api/session-controller/src/types.ts index a3e4755c86..f25a35d4ae 100644 --- a/packages/api/session-controller/src/types.ts +++ b/packages/api/session-controller/src/types.ts @@ -1,6 +1,6 @@ /** Browser-safe request, result, and lifecycle vocabulary for the Session Remote service. */ -import type {} from '@deepseek-ai/dsh-agent-model/types' +import type {} from '@deepseek-ai/dsh-agent/types' import type { NativeFileApplication } from '@deepseek-ai/dsh-native-command/types' import type { AttachmentIdType, ImageAttachmentLimits, ImageAttachmentRef, ImageMediaType, diff --git a/packages/api/session-controller/tests/archived-session-gate.host.spec.ts b/packages/api/session-controller/tests/archived-session-gate.host.spec.ts index a75f12e9e0..c191a30965 100644 --- a/packages/api/session-controller/tests/archived-session-gate.host.spec.ts +++ b/packages/api/session-controller/tests/archived-session-gate.host.spec.ts @@ -2,7 +2,7 @@ import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { Context } from '@deepseek-ai/cordis' import AgentRegistry from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' -import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' import type { Session } from '@deepseek-ai/dsh-session' import type {} from '@deepseek-ai/dsh-workspace' @@ -31,6 +31,7 @@ async function liveAgent(ctx: Context, session: Session): Promise { id: session.id, options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'idle', ctx, diff --git a/packages/api/session-controller/tests/commands-queue-attachment.host.spec.ts b/packages/api/session-controller/tests/commands-queue-attachment.host.spec.ts index 1043dee44d..1601cd61f3 100644 --- a/packages/api/session-controller/tests/commands-queue-attachment.host.spec.ts +++ b/packages/api/session-controller/tests/commands-queue-attachment.host.spec.ts @@ -17,7 +17,7 @@ import { subagentIdentityProjectionDefinition } from '@deepseek-ai/dsh-subagent/ import { describe, expect, it, vi } from 'vitest' import { ApiSessionAgentController } from '../src/agent.ts' import { SessionCommandController } from '../src/commands.ts' -import { createInboxStub } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, createInboxStub } from '@deepseek-ai/dsh-agent-loop-testkit' import { installSessionReadTestServices, testSessionPersistence } from './test-remote.ts' declare module '@deepseek-ai/dsh-llm' { @@ -92,6 +92,7 @@ async function commandHarness( send: vi.fn(), inject: vi.fn(), session, + model: unsupportedModel(), inbox, status: 'running', ctx, diff --git a/packages/api/session-controller/tests/commands-upload-file.host.spec.ts b/packages/api/session-controller/tests/commands-upload-file.host.spec.ts index 109107f603..d0507036b2 100644 --- a/packages/api/session-controller/tests/commands-upload-file.host.spec.ts +++ b/packages/api/session-controller/tests/commands-upload-file.host.spec.ts @@ -1,9 +1,9 @@ +import { AgentModel, modelSelectionProjectionDefinition } from '@deepseek-ai/dsh-agent' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { Context, FiberState } from '@deepseek-ai/cordis' import AgentRegistry from '@deepseek-ai/dsh-agent' import { createInboxStub } from '@deepseek-ai/dsh-agent-loop-testkit' import type { Agent } from '@deepseek-ai/dsh-agent' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import AttachmentStore, { AttachmentId } from '@deepseek-ai/dsh-attachment' import type { FileAttachmentRef, ImageAttachmentRef, SaveFileAttachment, SaveFileStreamAttachment, @@ -38,7 +38,6 @@ async function uploadHarness(origin?: 'subagent'): Promise<{ onTestFinished(() => ctx.fiber.dispose()) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) await ctx.plugin(CommandRuntime) const session = ctx.sessions.create(SESSION, { @@ -46,14 +45,17 @@ async function uploadHarness(origin?: 'subagent'): Promise<{ }) const inbox = createInboxStub() const followup = vi.fn() + const options = { provider: 'fixture', model: 'fixture-model' } + const projection = await ctx.sessionProjections.register(modelSelectionProjectionDefinition) const agent = { id: session.id, - options: { provider: 'fixture', model: 'fixture-model' }, + options, whenIdle: vi.fn(() => Promise.resolve()), runMaintenance: async () => { throw new Error('unexpected maintenance') }, send: vi.fn(), inject: vi.fn(), session, + model: new AgentModel(() => projection.stateOf(session), session, options), inbox, status: 'idle', ctx, diff --git a/packages/api/session-controller/tests/controller.host.spec.ts b/packages/api/session-controller/tests/controller.host.spec.ts index 83fdacaa38..2920093d54 100644 --- a/packages/api/session-controller/tests/controller.host.spec.ts +++ b/packages/api/session-controller/tests/controller.host.spec.ts @@ -2,7 +2,6 @@ import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { Context } from '@deepseek-ai/cordis' import AgentRegistry from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import { createUserMessage } from '@deepseek-ai/dsh-llm' import SessionStore, { SESSION_FORMAT_VERSION, SessionId, SessionLogOffset } from '@deepseek-ai/dsh-session' import type { SessionEvent, SessionHeader } from '@deepseek-ai/dsh-session' @@ -26,7 +25,6 @@ describe('SessionController facade', () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const sessionId = SessionId('controller-session') const header: SessionHeader = { diff --git a/packages/api/session-controller/tests/session-cold.host.spec.ts b/packages/api/session-controller/tests/session-cold.host.spec.ts index eb4bac5d2d..1bc9297103 100644 --- a/packages/api/session-controller/tests/session-cold.host.spec.ts +++ b/packages/api/session-controller/tests/session-cold.host.spec.ts @@ -3,6 +3,7 @@ * metadata-only listing, Agent-free history reads, subagent ownership * isolation, and prompt failure mapping. */ +import { AgentModel, modelSelectionProjectionDefinition } from '@deepseek-ai/dsh-agent' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { SESSION_FORMAT_VERSION, SessionLogOffset, SessionSeq } from '@deepseek-ai/dsh-session' @@ -17,8 +18,7 @@ import { createUserMessage, MessageId } from '@deepseek-ai/dsh-llm' import { snapshotSubagentDescriptor } from '@deepseek-ai/dsh-subagent' import { createInboxStub, mountAgentLoopTestDependencies, mountAgentLoopTestHarness } from '@deepseek-ai/dsh-agent-loop-testkit' import type { Agent, Inbox } from '@deepseek-ai/dsh-agent' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' -import type { SessionEvent, SessionHeader, SessionId } from '@deepseek-ai/dsh-session' +import type { Session, SessionEvent, SessionHeader, SessionId } from '@deepseek-ai/dsh-session' import AttachmentStore from '@deepseek-ai/dsh-attachment' import type { SessionPromptRequest, SessionRequestId } from '../src/types.ts' import { @@ -84,7 +84,6 @@ describe('sessions.list cold merge', () => { try { await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const historical = header('format-historical', 100) const current = header('format-current', 200) @@ -251,7 +250,6 @@ describe('attached updatedAt tracks human prompts', () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const remote = await createSessionTestRemote(ctx, { defaultModelSelection: () => ({ provider: 'p', model: 'm' }), cwd: '/tmp' }) await new Promise(resolve => setTimeout(resolve, 0)) @@ -397,7 +395,6 @@ describe('Remote Agent and Session lookup policy', () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const remote = await createSessionTestRemote(ctx, { defaultModelSelection: () => ({ provider: 'p', model: 'm' }), @@ -419,7 +416,6 @@ describe('Remote Agent and Session lookup policy', () => { await ctx.plugin(TypertRegistry) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const sessionId = sid('session-remote-cold') const meta = header(sessionId, 1000) @@ -461,7 +457,6 @@ describe('Remote Agent and Session lookup policy', () => { await ctx.plugin(TypertRegistry) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const coldId = sid('session-remote-cold-child') const coldMeta = header(coldId, 1000, { @@ -507,7 +502,6 @@ describe('Remote Agent and Session lookup policy', () => { await ctx.plugin(TypertRegistry) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const sessionId = sid('session-remote-resumed-child') const meta = header(sessionId, 1000) @@ -544,7 +538,6 @@ describe('subagent ownership fence', () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const sessionId = sid('session-child') const meta = header('session-child', 1000, { @@ -624,7 +617,6 @@ describe('subagent ownership fence', () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const sessionId = sid('session-legacy-child') const meta = header('session-legacy-child', 1000, { @@ -663,7 +655,6 @@ describe('subagent ownership fence', () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const parentSession = ctx.sessions.create(sid('session-parent'), { meta: { cwd: '/proj' } }) const parent = { id: parentSession.id, session: parentSession, status: 'idle', ctx } as Agent @@ -724,7 +715,6 @@ describe('subagent ownership fence', () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const session = ctx.sessions.create(sid('session-ordinary-fork'), { seed: [{ @@ -737,9 +727,14 @@ describe('subagent ownership fence', () => { inheritedEventCount: SessionLogOffset(1), }) const followup = vi.fn() - const agent = { - id: session.id, session, inbox: inboxFor(), status: 'idle', ctx, followup, - } as unknown as Agent + const agent: Agent = { + id: session.id, session, model: await modelFor(ctx, session), + options: { provider: 'p', model: 'm' }, + inbox: inboxFor(), status: 'idle', ctx, followup, + cancel: vi.fn(), whenIdle: () => Promise.resolve(), + runMaintenance: async () => { throw new Error('unexpected maintenance') }, + send: vi.fn(), inject: vi.fn(), steer: vi.fn(), + } await ctx.agents.register(agent) const remote = await createSessionTestRemote(ctx, { defaultModelSelection: () => ({ provider: 'p', model: 'm' }), cwd: '/tmp' }) @@ -756,13 +751,17 @@ describe('subagent ownership fence', () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const session = ctx.sessions.create(sid('session-browser-zone'), { meta: { cwd: '/proj' } }) const followup = vi.fn() - const agent = { - id: session.id, session, inbox: inboxFor(), status: 'idle', ctx, followup, - } as unknown as Agent + const agent: Agent = { + id: session.id, session, model: await modelFor(ctx, session), + options: { provider: 'p', model: 'm' }, + inbox: inboxFor(), status: 'idle', ctx, followup, + cancel: vi.fn(), whenIdle: () => Promise.resolve(), + runMaintenance: async () => { throw new Error('unexpected maintenance') }, + send: vi.fn(), inject: vi.fn(), steer: vi.fn(), + } await ctx.agents.register(agent) const remote = await createSessionTestRemote(ctx, { defaultModelSelection: () => ({ provider: 'p', model: 'm' }), @@ -829,7 +828,6 @@ describe('degenerate composition (no persistence, no factory)', () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const remote = await createSessionTestRemote(ctx, { defaultModelSelection: () => ({ provider: 'p', model: 'm' }), cwd: '/tmp' }) @@ -852,7 +850,6 @@ describe('degenerate composition (no persistence, no factory)', () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const inspect = vi.fn() const stat = vi.fn(() => Promise.resolve(undefined)) @@ -875,25 +872,31 @@ describe('degenerate composition (no persistence, no factory)', () => { }) }) +async function modelFor(ctx: Context, session: Session): Promise { + const projection = await ctx.sessionProjections.register(modelSelectionProjectionDefinition) + return new AgentModel(() => projection.stateOf(session), session, { provider: 'p', model: 'm' }) +} + describe('sessions.prompt synchronous rejection', () => { it('rejects content without non-whitespace text or an attachment before delivery or Session events', async () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const session = ctx.sessions.create(sid('session-empty-prompt')) + const options = { provider: 'p', model: 'm' } const followup = vi.fn() const steer = vi.fn() await ctx.agents.register({ id: session.id, - options: { provider: 'p', model: 'm' }, + options, whenIdle: vi.fn(() => Promise.resolve()), runMaintenance: async () => { throw new Error('unexpected maintenance') }, send: vi.fn(), inject: vi.fn(), cancel: vi.fn(), session, + model: await modelFor(ctx, session), inbox: inboxFor(), status: 'idle', ctx, @@ -984,7 +987,6 @@ describe('sessions.prompt synchronous rejection', () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const session = ctx.sessions.create(sid('session-throwing')) // A live structural stub whose delivery verbs throw synchronously, the @@ -992,12 +994,17 @@ describe('sessions.prompt synchronous rejection', () => { await ctx.agents.register({ id: session.id, session, + model: await modelFor(ctx, session), inbox: inboxFor(), status: 'idle', ctx, + options: { provider: 'p', model: 'm' }, + cancel: vi.fn(), whenIdle: () => Promise.resolve(), + runMaintenance: async () => { throw new Error('unexpected maintenance') }, + send: vi.fn(), inject: vi.fn(), followup: () => { throw new Error('agent "session-throwing" lifecycle disposed') }, steer: () => { throw new Error('agent "session-throwing" lifecycle disposed') }, - } as unknown as Agent) + }) const remote = await createSessionTestRemote(ctx, { defaultModelSelection: () => ({ provider: 'p', model: 'm' }), cwd: '/tmp' }) for (const mode of ['queue', 'steer'] as const) { @@ -1019,7 +1026,6 @@ describe('sessions.prompt synchronous rejection', () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const sessionId = sid('race-resume') const meta: SessionHeader = header('race-resume', 1000) diff --git a/packages/api/session-controller/tests/session-fork.host.spec.ts b/packages/api/session-controller/tests/session-fork.host.spec.ts index 7b7955883f..12a0c3f527 100644 --- a/packages/api/session-controller/tests/session-fork.host.spec.ts +++ b/packages/api/session-controller/tests/session-fork.host.spec.ts @@ -6,7 +6,6 @@ import { Context } from '@deepseek-ai/cordis' import AgentRegistry from '@deepseek-ai/dsh-agent' import type { Agent, AgentHandle, CreateAgentOptions } from '@deepseek-ai/dsh-agent' import { mountAgentLoopTestDependencies, mountAgentLoopTestHarness } from '@deepseek-ai/dsh-agent-loop-testkit' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import { BasicCompactionEngine } from '@deepseek-ai/dsh-compaction-basic' import { MockAdapter, textResponse } from '../../../core/agent-loop/tests/mock-adapter.ts' import { ToolCallId, createMessage, createUserMessage, ReasoningEffortId } from '@deepseek-ai/dsh-llm' @@ -34,7 +33,6 @@ async function composed(workspaces: readonly Workspace[] = []): Promise await ctx.plugin(SessionStore) await ctx.plugin(SystemPrompt, { personaPrefix: '' }) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) installSessionReadTestServices(ctx) ctx.provide('workspaceRegistry', { list: () => workspaces, archivedSessionIds: [] } as never) @@ -665,7 +663,7 @@ describe('sessions.fork', () => { const child = ctx.agents.get(response.value.sessionId) if (child === undefined) throw new Error('fork did not publish the child agent') expect(child.session.snapshotEvents().slice(0, child.session.inheritedEventCount)).toEqual(inherited) - expect(ctx.agentModel.resolveForNextStep(child)).toEqual({ + expect(child.model.current()).toEqual({ provider: 'inherited-provider', model: 'inherited-model', reasoningEffort: 'high', }) await ctx.fiber.dispose() diff --git a/packages/api/session-controller/tests/session-models.host.spec.ts b/packages/api/session-controller/tests/session-models.host.spec.ts index e10d59034f..017e3a5d55 100644 --- a/packages/api/session-controller/tests/session-models.host.spec.ts +++ b/packages/api/session-controller/tests/session-models.host.spec.ts @@ -4,6 +4,7 @@ * catalog injection, unavailable-model refusals, and the prompt-assembly * boundary for a running selection change. */ +import { AgentModel, modelSelectionProjectionDefinition } from '@deepseek-ai/dsh-agent' import { createInboxStub } from '@deepseek-ai/dsh-agent-loop-testkit' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' @@ -11,7 +12,6 @@ import { describe, expect, it, vi, onTestFinished } from 'vitest' import { Context } from '@deepseek-ai/cordis' import AgentRegistry from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import AttachmentStore from '@deepseek-ai/dsh-attachment' import LlmRuntime, { LlmAdapter, ReasoningEffortId } from '@deepseek-ai/dsh-llm' import type { @@ -99,7 +99,6 @@ async function harness(logged?: { await ctx.plugin(SystemPrompt, { personaPrefix: '' }) await ctx.plugin(LlmRuntime) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) ctx.llm.registerAdapter(['deepseek-official'], new CatalogAdapter('DeepSeek', [ { provider: 'deepseek-official', id: 'deepseek-chat', name: 'DeepSeek Chat' }, @@ -128,13 +127,16 @@ async function harness(logged?: { reason: 'initial', }) } + const projection = await ctx.sessionProjections.register(modelSelectionProjectionDefinition) + const options = { + get provider() { return ctx.agentDefaultModel.currentSelection().provider }, + get model() { return ctx.agentDefaultModel.currentSelection().model }, + get reasoningEffort() { return ctx.agentDefaultModel.currentSelection().reasoningEffort }, + } const agent = { id: session.id, - options: { - get provider() { return ctx.agentDefaultModel.currentSelection().provider }, - get model() { return ctx.agentDefaultModel.currentSelection().model }, - get reasoningEffort() { return ctx.agentDefaultModel.currentSelection().reasoningEffort }, - }, + options, + model: new AgentModel(() => projection.stateOf(session), session, options), session, status: 'running', ctx, @@ -520,7 +522,7 @@ describe('Web session model selection', () => { model: 'deepseek-reasoner', reasoningEffort: 'max', }) - expect(ctx.agentModel.resolveForNextStep(agent)).toEqual(selected.selected) + expect(agent.model.current()).toEqual(selected.selected) expect(agent.session.snapshotEvents().at(-1)).toMatchObject({ type: 'model/selection', data: selected.selected, }) @@ -581,7 +583,10 @@ describe('Web session model selection', () => { ctx.agents.setFactory({ async createAgent(ownerCtx, options) { const session = ctx.sessions.create(options.sessionId) - const agent: Agent = { ...template, id: session.id, session, ctx: ownerCtx, options: options.agentOptions ?? {} } + const agentOptions = options.agentOptions ?? {} + const projection = await ownerCtx.sessionProjections.register(modelSelectionProjectionDefinition) + const model = new AgentModel(() => projection.stateOf(session), session, agentOptions) + const agent: Agent = { ...template, id: session.id, session, ctx: ownerCtx, options: agentOptions, model } await options.setup?.(ownerCtx, agent) await ctx.agents.register(agent) return { agent, dispose: () => Promise.resolve() } @@ -591,9 +596,9 @@ describe('Web session model selection', () => { const first = expectValue(await remote.create({ cwd: '/tmp' })) const second = expectValue(await remote.create({ cwd: '/tmp' })) const agent = ctx.agents.get(first.sessionId)! - expect(ctx.agentModel.resolveForNextStep(agent)).toEqual(stored) + expect(agent.model.current()).toEqual(stored) stored = { provider: 'deepseek-official', model: 'deepseek-reasoner' } - expect(ctx.agentModel.resolveForNextStep(agent)).toEqual(stored) + expect(agent.model.current()).toEqual(stored) const selection = { ...stored, reasoningEffort: ReasoningEffortId('high') } // The explicit selection is identical to the live fallback, including effort. stored = selection @@ -601,7 +606,7 @@ describe('Web session model selection', () => { expect(agent.session.snapshotEvents().filter(event => event.type === 'model/selection')) .toMatchObject([{ data: selection }]) expectValue(await remote.selectModel({ sessionId: second.sessionId, provider: 'deepseek-official', model: 'deepseek-chat' })) - expect(ctx.agentModel.resolveForNextStep(agent)).toEqual(selection) + expect(agent.model.current()).toEqual(selection) expect(currentSelection(ctx, first.sessionId)).toEqual(selection) }) @@ -634,7 +639,7 @@ describe('Web session model selection', () => { cwd: '/tmp', }) - expect(ctx.agentModel.resolveForNextStep(agent)) + expect(agent.model.current()) .toEqual({ provider: 'deepseek-official', model: 'deepseek-chat' }) await ctx.fiber.dispose() }) diff --git a/packages/api/session-controller/tests/session-projections.host.spec.ts b/packages/api/session-controller/tests/session-projections.host.spec.ts index 9dfbf545de..db37e8bcb8 100644 --- a/packages/api/session-controller/tests/session-projections.host.spec.ts +++ b/packages/api/session-controller/tests/session-projections.host.spec.ts @@ -31,7 +31,6 @@ import { mountAgentLoopTestDependencies, mountAgentLoopTestHarness, } from '@deepseek-ai/dsh-agent-loop-testkit' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import { createSessionTestRemote, testSessionPersistence, type TestSessionRemote } from './test-remote.ts' const ownedContexts = new Set() @@ -163,7 +162,6 @@ describe('session.history projections block', () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const parent = ctx.sessions.create(SessionId('wire-seed-parent'), { meta: { cwd: '/workspace' } }) parent.append('turn/start', { turn: 1 }) @@ -581,7 +579,6 @@ describe('session.list projections column', () => { await ctx.plugin(StorageDomain, { backend: 'json' }) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) await ctx.sessionProjections.register(privatePromptUnit()) await ctx.plugin(SessionProjectionCache, { writeEveryEvents: 100, writeIntervalMs: 60_000 }) @@ -637,7 +634,6 @@ describe('session.list projections column', () => { await ctx.plugin(StorageDomain, { backend: 'json' }) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) await ctx.sessionProjections.register(titleProjectionDefinition) await ctx.plugin(SessionProjectionCache, { writeEveryEvents: 100, writeIntervalMs: 60_000 }) diff --git a/packages/api/session-controller/tsconfig.client.json b/packages/api/session-controller/tsconfig.client.json index d49ddb1620..a1375420ca 100644 --- a/packages/api/session-controller/tsconfig.client.json +++ b/packages/api/session-controller/tsconfig.client.json @@ -11,65 +11,24 @@ "src/remote-events.ts" ], "references": [ - { - "path": "../../../vendor/cordis" - }, - { - "path": "../gateway/tsconfig.client.json" - }, - { - "path": "../../attachment/attachment" - }, - { - "path": "../../client/connection/tsconfig.client.json" - }, - { - "path": "../../client/file-upload/tsconfig.client.json" - }, - { - "path": "../../client/store" - }, - { - "path": "../../context/file-reference" - }, - { - "path": "../../core/session" - }, - { - "path": "../../jobs/jobs" - }, - { - "path": "../../llm/llm" - }, - { - "path": "../../session/session-projection" - }, - { - "path": "../../session/session-title" - }, - { - "path": "../../subagent/subagent" - }, - { - "path": "../../util/brand" - }, - { - "path": "../../util/crypto" - }, - { - "path": "../../util/workspace-path" - }, - { - "path": "../../workspace/workspace" - }, - { - "path": "../../typert/protocol" - }, - { - "path": "../../util/native-command" - }, - { - "path": "../../core/agent-model" - } + { "path": "../../../vendor/cordis" }, + { "path": "../gateway/tsconfig.client.json" }, + { "path": "../../attachment/attachment" }, + { "path": "../../client/connection/tsconfig.client.json" }, + { "path": "../../client/file-upload/tsconfig.client.json" }, + { "path": "../../client/store" }, + { "path": "../../context/file-reference" }, + { "path": "../../core/session" }, + { "path": "../../jobs/jobs" }, + { "path": "../../llm/llm" }, + { "path": "../../session/session-projection" }, + { "path": "../../session/session-title" }, + { "path": "../../subagent/subagent" }, + { "path": "../../util/brand" }, + { "path": "../../util/crypto" }, + { "path": "../../util/workspace-path" }, + { "path": "../../workspace/workspace" }, + { "path": "../../typert/protocol" }, + { "path": "../../util/native-command" } ] } diff --git a/packages/api/session-controller/tsconfig.host.json b/packages/api/session-controller/tsconfig.host.json index dba370504c..8a5fa75166 100644 --- a/packages/api/session-controller/tsconfig.host.json +++ b/packages/api/session-controller/tsconfig.host.json @@ -114,9 +114,6 @@ }, { "path": "../../session/working-directory" - }, - { - "path": "../../core/agent-model" } ] } diff --git a/packages/bundle/base/cordis.patch.yml b/packages/bundle/base/cordis.patch.yml index 92a1db0ada..c087b81f23 100644 --- a/packages/bundle/base/cordis.patch.yml +++ b/packages/bundle/base/cordis.patch.yml @@ -82,9 +82,6 @@ - id: agent name: '@deepseek-ai/dsh-agent' - - id: agent-model - name: '@deepseek-ai/dsh-agent-model' - - id: plugin-package-inventory-deepseek name: '@deepseek-ai/dsh-plugin-package-inventory-deepseek' diff --git a/packages/bundle/base/package.json b/packages/bundle/base/package.json index 78981285ec..934043be6d 100644 --- a/packages/bundle/base/package.json +++ b/packages/bundle/base/package.json @@ -128,14 +128,12 @@ "@deepseek-ai/dsh-otel": "workspace:*", "@deepseek-ai/dsh-working-directory": "workspace:*", "@deepseek-ai/dsh-tool-working-directory": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*", "@deepseek-ai/dsh-web-deepseek-api": "workspace:*" }, "peerDependencies": { "@deepseek-ai/cordis": "workspace:~" }, "devDependencies": { - "@deepseek-ai/cordis": "workspace:~", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/cordis": "workspace:~" } } diff --git a/packages/bundle/base/tsconfig.json b/packages/bundle/base/tsconfig.json index 71452d7959..2f01995a6b 100644 --- a/packages/bundle/base/tsconfig.json +++ b/packages/bundle/base/tsconfig.json @@ -16,9 +16,6 @@ }, { "path": "../../settings/settings" - }, - { - "path": "../../core/agent-model" } ] } diff --git a/packages/bundle/headless/package.json b/packages/bundle/headless/package.json index 53c15ed10c..2247beae8e 100644 --- a/packages/bundle/headless/package.json +++ b/packages/bundle/headless/package.json @@ -58,8 +58,7 @@ "@deepseek-ai/dsh-permission-presets": "workspace:*", "@deepseek-ai/dsh-sandbox-policy": "workspace:*", "@deepseek-ai/dsh-subagent": "workspace:*", - "@deepseek-ai/dsh-user-approval": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-user-approval": "workspace:*" }, "devDependencies": { "@deepseek-ai/cordis": "workspace:~", @@ -75,8 +74,7 @@ "@deepseek-ai/dsh-permission-presets": "workspace:*", "@deepseek-ai/dsh-sandbox-policy": "workspace:*", "@deepseek-ai/dsh-subagent": "workspace:*", - "@deepseek-ai/dsh-user-approval": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-user-approval": "workspace:*" }, "peerDependenciesMeta": { "@deepseek-ai/dsh-permission-presets": { diff --git a/packages/bundle/headless/src/index.ts b/packages/bundle/headless/src/index.ts index 17b0a30dbc..6ad84ed4f6 100644 --- a/packages/bundle/headless/src/index.ts +++ b/packages/bundle/headless/src/index.ts @@ -16,8 +16,7 @@ import z from '@deepseek-ai/schemastery' import { brandString } from '@deepseek-ai/dsh-brand' import type { Agent, AgentOptions } from '@deepseek-ai/dsh-agent' import type {} from '@deepseek-ai/dsh-agent-default-model' -import type {} from '@deepseek-ai/dsh-agent-model' -import * as ModelSwitchNotice from '@deepseek-ai/dsh-agent-model/notice' +import { installModelSelection } from '@deepseek-ai/dsh-agent' import type {} from '@deepseek-ai/dsh-fs' import { createUserMessage } from '@deepseek-ai/dsh-llm' import { APPROVAL_POLICIES, setApprovalPolicy, type ApprovalPolicy } from '@deepseek-ai/dsh-user-approval' @@ -43,7 +42,7 @@ import { projectJsonRun, boundJsonLine } from './json-stream.ts' export const name = 'headless-runner' /** Core services required before the one-shot turn can start. */ -export const inject = ['agentDefaultModel', 'agents', 'agentModel', 'sessions'] +export const inject = ['agentDefaultModel', 'agents', 'sessions'] /** Plugin config: the task and run options resolved from this app's injected provider service. */ export interface Config { @@ -370,10 +369,19 @@ async function run(ctx: Context, config: Config, io: HeadlessIo): Promise // host plane and the agent reads them from the global layer. A deployment // that DOES configure one has to join it here first // (@deepseek-ai/dsh-agent-preset-registry README, "Composing a child agent"). - const setup = async (agentCtx: Context, agent: Agent): Promise => { + const setup = (agentCtx: Context, agent: Agent): void => { // A resumed Session continues with this run's configured model. - if (config.sessionId !== undefined) ctx.agentModel.applyConfiguredRoute(agent, selection) - await agentCtx.plugin(ModelSwitchNotice) + if (config.sessionId !== undefined) { + const previous = agent.model.current() + const configured = { ...selection } + if (configured.reasoningEffort === undefined && previous !== undefined + && previous.provider === configured.provider && previous.model === configured.model + && previous.reasoningEffort !== undefined) { + configured.reasoningEffort = previous.reasoningEffort + } + agent.model.select(configured) + } + installModelSelection(agentCtx) } const sessionId = brandString(config.sessionId ?? `session-${randomUUID()}`) const fs = ctx.get('fs') diff --git a/packages/bundle/headless/tests/headless.spec.ts b/packages/bundle/headless/tests/headless.spec.ts index 7a37498f44..54067fe50c 100644 --- a/packages/bundle/headless/tests/headless.spec.ts +++ b/packages/bundle/headless/tests/headless.spec.ts @@ -20,8 +20,8 @@ import SandboxPolicyService from '@deepseek-ai/dsh-sandbox-policy' import ApprovalService, { setApprovalPolicy, type ApprovalPolicy } from '@deepseek-ai/dsh-user-approval' import type { Session, SessionId, UserMessage } from '@deepseek-ai/dsh-session' import { SessionQueryError } from '@deepseek-ai/dsh-session-query' +import { AgentModel, modelSelectionProjectionDefinition } from '@deepseek-ai/dsh-agent' import { createInboxStub, provideWorkingDirectoryFixture } from '@deepseek-ai/dsh-agent-loop-testkit' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import { apply, Config } from '../src/index.ts' import SubagentRuntime from '@deepseek-ai/dsh-subagent' import { internals } from '../src/runner-internals.ts' @@ -165,9 +165,12 @@ async function bench(script: Script, options: BenchOptions = {}): Promise<{ const inbox = createInboxStub() let idle = Promise.resolve() let running = false + const agentOptions = createOptions.agentOptions ?? {} + const projection = await ownerCtx.sessionProjections.register(modelSelectionProjectionDefinition) const agent: Agent = { id: session.id, - options: createOptions.agentOptions ?? {}, + options: agentOptions, + model: new AgentModel(() => projection.stateOf(session), session, agentOptions), session, inbox, get status() { return running ? 'running' : 'idle' }, @@ -196,7 +199,6 @@ async function bench(script: Script, options: BenchOptions = {}): Promise<{ await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) await ctx.plugin(AgentDefaultModelConfig, { provider: 'test-provider', model: 'test-model' }) ctx.agents.setFactory({ @@ -876,7 +878,7 @@ describe('headless runner', () => { it.each(['test-model', 'other-model'])('restores explicit effort only for the declared route (%s)', async (savedModel) => { const test = await bench({ afterPrompt(session, message, agent) { - expect(test.ctx.agentModel.resolveForNextStep(agent)).toEqual({ + expect(agent.model.current()).toEqual({ provider: 'test-provider', model: 'test-model', ...savedModel === 'test-model' ? { reasoningEffort: 'high' } : {}, }) diff --git a/packages/bundle/headless/tsconfig.json b/packages/bundle/headless/tsconfig.json index b739c8fab1..cf9dabcd9c 100644 --- a/packages/bundle/headless/tsconfig.json +++ b/packages/bundle/headless/tsconfig.json @@ -41,9 +41,6 @@ { "path": "../../subagent/subagent" }, - { - "path": "../../core/agent-model" - }, { "path": "../../interaction/permission-presets" }, diff --git a/packages/bundle/sdk-minimal/cordis.patch.yml b/packages/bundle/sdk-minimal/cordis.patch.yml index 737e4aa5f4..afb1e0019e 100644 --- a/packages/bundle/sdk-minimal/cordis.patch.yml +++ b/packages/bundle/sdk-minimal/cordis.patch.yml @@ -126,8 +126,6 @@ - id: agent name: '@deepseek-ai/dsh-agent' - - id: agent-model - name: '@deepseek-ai/dsh-agent-model' - id: llm-retry name: '@deepseek-ai/dsh-llm-retry' diff --git a/packages/bundle/sdk-minimal/package.json b/packages/bundle/sdk-minimal/package.json index 932b086a73..f5a82a3662 100644 --- a/packages/bundle/sdk-minimal/package.json +++ b/packages/bundle/sdk-minimal/package.json @@ -68,14 +68,12 @@ "@deepseek-ai/dsh-tools": "workspace:*", "@deepseek-ai/dsh-working-directory": "workspace:*", "@deepseek-ai/dsh-tool-working-directory": "workspace:*", - "@deepseek-ai/dsh-fs-local": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-fs-local": "workspace:*" }, "peerDependencies": { "@deepseek-ai/cordis": "workspace:~" }, "devDependencies": { - "@deepseek-ai/cordis": "workspace:~", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/cordis": "workspace:~" } } diff --git a/packages/bundle/sdk-minimal/tests/sdk-minimal.spec.ts b/packages/bundle/sdk-minimal/tests/sdk-minimal.spec.ts index 713757cae9..ce122661ae 100644 --- a/packages/bundle/sdk-minimal/tests/sdk-minimal.spec.ts +++ b/packages/bundle/sdk-minimal/tests/sdk-minimal.spec.ts @@ -55,7 +55,6 @@ describe('dsh-sdk-minimal bundle', () => { ['tools', '@deepseek-ai/dsh-tools'], ['mcp-resources', '@deepseek-ai/dsh-mcp-resources'], ['agent', '@deepseek-ai/dsh-agent'], - ['agent-model', '@deepseek-ai/dsh-agent-model'], ['llm-retry', '@deepseek-ai/dsh-llm-retry'], ['jobs', '@deepseek-ai/dsh-jobs-local'], ['agent-loop', '@deepseek-ai/dsh-agent-loop'], diff --git a/packages/bundle/sdk-minimal/tsconfig.json b/packages/bundle/sdk-minimal/tsconfig.json index 3e60750a3e..f1a449634c 100644 --- a/packages/bundle/sdk-minimal/tsconfig.json +++ b/packages/bundle/sdk-minimal/tsconfig.json @@ -10,9 +10,6 @@ "references": [ { "path": "../../../vendor/cordis" - }, - { - "path": "../../core/agent-model" } ] } diff --git a/packages/client/ui-conversation/package.json b/packages/client/ui-conversation/package.json index 612cd9423b..11a24a79f7 100644 --- a/packages/client/ui-conversation/package.json +++ b/packages/client/ui-conversation/package.json @@ -96,8 +96,7 @@ "@deepseek-ai/dsh-agent": "workspace:*", "@deepseek-ai/dsh-settings": "workspace:*", "@deepseek-ai/dsh-client-shortcuts": "workspace:*", - "@deepseek-ai/dsh-client-product-analytics": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-client-product-analytics": "workspace:*" }, "files": [ "locale/*.json", diff --git a/packages/client/ui-conversation/src/client/input/hub.ts b/packages/client/ui-conversation/src/client/input/hub.ts index 0b232e7b08..704f1a4ac1 100644 --- a/packages/client/ui-conversation/src/client/input/hub.ts +++ b/packages/client/ui-conversation/src/client/input/hub.ts @@ -14,7 +14,7 @@ import type { import type { SessionId } from '@deepseek-ai/dsh-session/types' import type { TranslateNS } from '@deepseek-ai/dsh-client-locale/client' import type { InboxState } from '@deepseek-ai/dsh-agent/types' -import type { ModelSelectionProjection } from '@deepseek-ai/dsh-agent-model/types' +import type { ModelSelectionProjection } from '@deepseek-ai/dsh-agent/types' import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store' import type { DraftAttachmentId, DraftAttachmentSerializationResult, DraftInitializationOptions, DraftInitializationResult, InputTriggerController, diff --git a/packages/client/ui-conversation/tsconfig.client.json b/packages/client/ui-conversation/tsconfig.client.json index fffdd9ca09..0339eebbe6 100644 --- a/packages/client/ui-conversation/tsconfig.client.json +++ b/packages/client/ui-conversation/tsconfig.client.json @@ -104,9 +104,6 @@ }, { "path": "../product-analytics/tsconfig.client.json" - }, - { - "path": "../../core/agent-model" } ], "exclude": [ diff --git a/packages/client/ui-model-selection/package.json b/packages/client/ui-model-selection/package.json index 1b56544cf4..51277d84c6 100644 --- a/packages/client/ui-model-selection/package.json +++ b/packages/client/ui-model-selection/package.json @@ -66,8 +66,7 @@ "@deepseek-ai/dsh-client-ui-renderer": "workspace:*", "@deepseek-ai/dsh-client-ui-session": "workspace:*", "clsx": "^2.1.1", - "@deepseek-ai/dsh-client-product-analytics": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-client-product-analytics": "workspace:*" }, "files": [ "locale/*.json", diff --git a/packages/client/ui-model-selection/src/client/directory.ts b/packages/client/ui-model-selection/src/client/directory.ts index 66d07d7890..67136d39c8 100644 --- a/packages/client/ui-model-selection/src/client/directory.ts +++ b/packages/client/ui-model-selection/src/client/directory.ts @@ -4,7 +4,7 @@ * Session's durable selection projection, then submit through the same * selectModel call. A switch made in either entry updates this shared state. */ -import type { ModelSelectionProjection } from '@deepseek-ai/dsh-agent-model/types' +import type { ModelSelectionProjection } from '@deepseek-ai/dsh-agent/types' import type { TrackProductEvent } from '@deepseek-ai/dsh-client-product-analytics/client' import type { ModelCatalogFailure, ModelProviderGroup, ModelSelection, diff --git a/packages/client/ui-model-selection/tests/browser-plugin.client.spec.ts b/packages/client/ui-model-selection/tests/browser-plugin.client.spec.ts index 745ba351c6..778a1df137 100644 --- a/packages/client/ui-model-selection/tests/browser-plugin.client.spec.ts +++ b/packages/client/ui-model-selection/tests/browser-plugin.client.spec.ts @@ -16,7 +16,7 @@ import { LocaleRuntime } from '@deepseek-ai/dsh-client-locale/client' import { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-store' import { RemoteError, TestRemote } from '@deepseek-ai/dsh-client-test-runtime' import type { ModelSelection } from '@deepseek-ai/dsh-agent/types' -import type { ModelSelectionProjection } from '@deepseek-ai/dsh-agent-model/types' +import type { ModelSelectionProjection } from '@deepseek-ai/dsh-agent/types' import { ReasoningEffortId } from '@deepseek-ai/dsh-llm/brand' import type { CommandContribution, PopupSelectSpec, SelectOption } from '@deepseek-ai/dsh-client-ui-commands/client' import type { ModelSelectInjected } from '../src/client/slots.ts' diff --git a/packages/client/ui-model-selection/tsconfig.json b/packages/client/ui-model-selection/tsconfig.json index 2af4805c5e..862648d622 100644 --- a/packages/client/ui-model-selection/tsconfig.json +++ b/packages/client/ui-model-selection/tsconfig.json @@ -58,9 +58,6 @@ }, { "path": "../product-analytics/tsconfig.client.json" - }, - { - "path": "../../core/agent-model" } ] } diff --git a/packages/compaction/compaction-basic/tests/compaction-loop-repro.spec.ts b/packages/compaction/compaction-basic/tests/compaction-loop-repro.spec.ts index 8ed00e815d..012fc20bd4 100644 --- a/packages/compaction/compaction-basic/tests/compaction-loop-repro.spec.ts +++ b/packages/compaction/compaction-basic/tests/compaction-loop-repro.spec.ts @@ -212,7 +212,7 @@ describe('CBR-001: a real-loop checkpoint is a valid boundary on both sides', () provider: 'unconfigured-agent-fallback', model: 'unconfigured-agent-fallback', }) - ctx.agentModel.select(agent, { provider: 'mock', model: 'mock' }) + agent.model.select({ provider: 'mock', model: 'mock' }) agent.followup(createUserMessage({ content: [{ type: 'text', text: 'do a routed multi-step task' }], source: { kind: 'user' } })) await waitForIdle(ctx, agent) @@ -397,7 +397,7 @@ describe('context-overflow recovery across the real loop and compaction-basic', }, }) - ctx.agentModel.select(agent, { provider: 'mock', model: 'mock' }) + agent.model.select({ provider: 'mock', model: 'mock' }) agent.followup(createUserMessage({ content: [{ type: 'text', text: 'continue from history' }], source: { kind: 'user' } })) await agent.whenIdle() diff --git a/packages/context/agent-instructions/package.json b/packages/context/agent-instructions/package.json index 10373885e6..72331f71e6 100644 --- a/packages/context/agent-instructions/package.json +++ b/packages/context/agent-instructions/package.json @@ -59,7 +59,6 @@ "@deepseek-ai/dsh-system-prompt": "workspace:*", "@deepseek-ai/dsh-tool-fs": "workspace:*", "@deepseek-ai/dsh-tools": "workspace:*", - "@deepseek-ai/dsh-working-directory": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-working-directory": "workspace:*" } } diff --git a/packages/context/agent-instructions/tests/agent-instructions.spec.ts b/packages/context/agent-instructions/tests/agent-instructions.spec.ts index 965720f3c6..4355db66a8 100644 --- a/packages/context/agent-instructions/tests/agent-instructions.spec.ts +++ b/packages/context/agent-instructions/tests/agent-instructions.spec.ts @@ -58,7 +58,6 @@ import { mountAgentLoopTestDependencies, mountAgentLoopTestHarness, } from '@deepseek-ai/dsh-agent-loop-testkit' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' declare module '@deepseek-ai/dsh-llm' { interface MessageSourceMap { @@ -4279,7 +4278,6 @@ describe('dynamic nested workspace context injection', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/context/time-context/package.json b/packages/context/time-context/package.json index 1de674eea6..44f5899cbf 100644 --- a/packages/context/time-context/package.json +++ b/packages/context/time-context/package.json @@ -57,7 +57,6 @@ "@deepseek-ai/dsh-subprocess-local": "workspace:*", "@deepseek-ai/dsh-system-prompt": "workspace:*", "@deepseek-ai/dsh-tools": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*", "@deepseek-ai/dsh-util-time": "workspace:*" } } diff --git a/packages/context/time-context/tests/time-context.spec.ts b/packages/context/time-context/tests/time-context.spec.ts index 92761fba2d..0f8bf992a9 100644 --- a/packages/context/time-context/tests/time-context.spec.ts +++ b/packages/context/time-context/tests/time-context.spec.ts @@ -9,8 +9,7 @@ import AgentRegistry, { agentEvents, type Agent } from '@deepseek-ai/dsh-agent' import { defineContentToolFixture } from '@deepseek-ai/dsh-tools' import AgentLoop from '@deepseek-ai/dsh-agent-loop' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' -import { unsupportedInbox, mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' +import { unsupportedModel, unsupportedInbox, mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit' import * as timeContext from '@deepseek-ai/dsh-time-context' import type { Config } from '@deepseek-ai/dsh-time-context' @@ -41,7 +40,6 @@ afterEach(() => { async function mount(config: Config = {}) { const ctx = new Context() await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const fiber = await ctx.plugin(timeContext, config) return { ctx, fiber } @@ -52,6 +50,7 @@ function sessionAgent(session: Session, id = 'agent'): Agent { id: SessionId(id), options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'running', ctx: new Context(), @@ -385,7 +384,6 @@ describe('configuration and lifecycle', () => { it('fails loud for an invalid explicit zone or an unavailable process zone', async () => { const invalid = new Context() await invalid.plugin(SessionProjectionRegistry) - await invalid.plugin(AgentModelPlugin) await invalid.plugin(AgentRegistry) await expect(invalid.plugin(timeContext, { timeZone: 'Not/A_Real_Zone' })).rejects.toThrow( /invalid IANA timeZone/, @@ -396,7 +394,6 @@ describe('configuration and lifecycle', () => { }) const unresolved = new Context() await unresolved.plugin(SessionProjectionRegistry) - await unresolved.plugin(AgentModelPlugin) await unresolved.plugin(AgentRegistry) await expect(unresolved.plugin(timeContext, {})).rejects.toThrow(/failed to resolve the system time zone/) }) @@ -536,7 +533,6 @@ describe('real Loader export path', () => { const ctx = new Context() await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const plugin = loader.unwrapExports(timeContext) as Parameters[0] await ctx.plugin(plugin) diff --git a/packages/context/tmux-context/tests/tmux-context.spec.ts b/packages/context/tmux-context/tests/tmux-context.spec.ts index d5ae7ecbbe..722a3d8ebf 100644 --- a/packages/context/tmux-context/tests/tmux-context.spec.ts +++ b/packages/context/tmux-context/tests/tmux-context.spec.ts @@ -8,7 +8,7 @@ import { ShellExecutor } from '@deepseek-ai/dsh-shell' import type { ShellExecRequest, ShellExecSpec, ShellExecution, ShellRunResult } from '@deepseek-ai/dsh-shell' import * as tmuxContext from '@deepseek-ai/dsh-tmux-context' import type { Config } from '@deepseek-ai/dsh-tmux-context' -import { mountAgentLoopTestDependencies, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, mountAgentLoopTestDependencies, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import AgentLoop from '@deepseek-ai/dsh-agent-loop' import { MockAdapter, textResponse } from '../../../core/agent-loop/tests/mock-adapter.ts' @@ -111,6 +111,7 @@ function sessionAgent(session: Session, id = 'agent'): Agent { id: SessionId(id), options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'running', ctx: new Context(), diff --git a/packages/core/README.i18n.yaml b/packages/core/README.i18n.yaml index 141cd4008b..60936e1b9e 100644 --- a/packages/core/README.i18n.yaml +++ b/packages/core/README.i18n.yaml @@ -15,8 +15,8 @@ en: 1eb35b015a6c423f zh: a8a3dceee2c9e943 /packages-core/packages: - en: 67a6cc9ad66e7196 - zh: 77ca2120b5b19038 + en: 083b59ee474230e8 + zh: f11c7bc21bbcdbe6 /packages-core/related-documentation: en: dfe5990edf146a91 zh: b64e4b2d812286d4 diff --git a/packages/core/README.md b/packages/core/README.md index 06dde2376d..d88c679a4f 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -30,7 +30,6 @@ Use the core packages to build or extend an agent that records durable session h | [`tools/`](tools/README.md) | The tool registry and guarded execution pipeline the loop dispatches through | `ctx.tools` | | [`agent-tool-presentation/`](agent-tool-presentation/README.md) | Per-agent tool-presentation selector for presets | no ctx key | | [`agent/`](agent/README.md) | The `Agent` handle plugins program against, plus its live registry and events | `ctx.agents` | -| [`agent-model/`](agent-model/README.md) | Durable Session model selection and optional scoped switch notices | `ctx.agentModel` | | [`agent-default-model/`](agent-default-model/README.md) | The deployment default model selection entry points apply to fresh agents | `ctx.agentDefaultModel` | | [`agent-loop/`](agent-loop/README.md) | The default agent driver: creates agents and runs the turn and step lifecycle | `ctx.agentLoop` | diff --git a/packages/core/README.zh.md b/packages/core/README.zh.md index 21f4bc2b29..128ad5cbd0 100644 --- a/packages/core/README.zh.md +++ b/packages/core/README.zh.md @@ -30,7 +30,6 @@ kind: "package-group" | [`tools/`](tools/README.zh.md) | 供循环分发使用的工具注册表与带防护机制的执行流水线 | `ctx.tools` | | [`agent-tool-presentation/`](agent-tool-presentation/README.zh.md) | 为 preset 提供按 agent 的工具呈现方式选择器 | 无 ctx key | | [`agent/`](agent/README.zh.md) | 供插件编程使用的 `Agent` 句柄,以及其实时注册表与事件 | `ctx.agents` | -| [`agent-model/`](agent-model/README.zh.md) | 持久 Session 模型选择与可选的作用域切换提示 | `ctx.agentModel` | | [`agent-default-model/`](agent-default-model/README.zh.md) | 入口对全新 agent 应用的部署默认模型选择 | `ctx.agentDefaultModel` | | [`agent-loop/`](agent-loop/README.zh.md) | 默认 agent 驱动器:创建 agent 并运行轮次与步骤生命周期 | `ctx.agentLoop` | diff --git a/packages/core/agent-default-model/README.i18n.yaml b/packages/core/agent-default-model/README.i18n.yaml index 58a0c9c03c..07bb6a5cf4 100644 --- a/packages/core/agent-default-model/README.i18n.yaml +++ b/packages/core/agent-default-model/README.i18n.yaml @@ -9,8 +9,8 @@ en: 87e9d106cedbbe08 zh: 5eb47e4bd7f5b51d /deepseek-ai-dsh-agent-default-model/summary: - en: 0a14206daf366b59 - zh: 8e163fa67aa3774c + en: dc7c382d4d976acf + zh: e82750291510148f /deepseek-ai-dsh-agent-default-model/table-of-contents: en: d152484eb41ac6b4 zh: 09388d293f9be9cb @@ -45,8 +45,8 @@ en: 814ad1a657587419 zh: 5c1968a55beb1af4 /deepseek-ai-dsh-agent-default-model/known-limitations-and-deferred-work: - en: 7cafbd9a83d14852 - zh: 2c298c1a671ec00f + en: d2d7ade906d7b24a + zh: b79f12617cd1e009 /deepseek-ai-dsh-agent-default-model/known-limitations-and-deferred-work/dev-note: en: e67f633dc6fe7773 zh: 3bb0c20c93f16f98 diff --git a/packages/core/agent-default-model/README.md b/packages/core/agent-default-model/README.md index 5bc09a5ea9..3789e61713 100644 --- a/packages/core/agent-default-model/README.md +++ b/packages/core/agent-default-model/README.md @@ -9,7 +9,7 @@ English | [中文](README.zh.md) ## Summary -Give newly created agents a shared default provider and model when their sessions do not specify one. Provider, model, and reasoning effort are live Config fields. Saved selections update the active profile patch and apply to subsequent reads; per-session selection belongs to the [model-selection service](../agent-model/README.md). +Give newly created agents a shared default provider and model when their sessions do not specify one. Provider, model, and reasoning effort are live Config fields. Saved selections update the active profile patch and apply to subsequent reads; per-session selection belongs to the [Agent model API](../agent/README.md). ## Table of Contents @@ -112,7 +112,7 @@ Changing the default affects only agents that subsequently resolve from it. An e These limits define the service's scope. They are current package constraints, not a task backlog. -- **One process-wide default** — the service owns a single default; per-session model selection belongs to the [model-selection service](../agent-model/README.md). +- **One process-wide default** — the service owns a single default; per-session model selection belongs to the [Agent model API](../agent/README.md). - **Persistence requires a profile configuration editor** — without it, saving a default does not retain the selection. diff --git a/packages/core/agent-default-model/README.zh.md b/packages/core/agent-default-model/README.zh.md index 7aa22c97cc..29ddda11ce 100644 --- a/packages/core/agent-default-model/README.zh.md +++ b/packages/core/agent-default-model/README.zh.md @@ -9,7 +9,7 @@ kind: "package-reference" ## 概述 -为会话未指定模型的新 agent 提供共享默认 provider 和模型。Provider、模型和推理强度都是即时 Config 字段。保存的选择更新当前 profile patch,并用于后续读取;会话级选择由[模型选择服务](../agent-model/README.zh.md) 负责。 +为会话未指定模型的新 agent 提供共享默认 provider 和模型。Provider、模型和推理强度都是即时 Config 字段。保存的选择更新当前 profile patch,并用于后续读取;会话级选择由[Agent 模型 API](../agent/README.zh.md) 负责。 ## 目录 @@ -112,7 +112,7 @@ await ctx.agentDefaultModel.saveSelection({ provider, model, reasoningEffort: 'h 这些限制界定该服务的范围。它们是当前包约束,不是任务积压。 -- **单一的进程级默认值**——该服务只拥有一个默认值;按会话的模型选择由[模型选择服务](../agent-model/README.zh.md) 负责。 +- **单一的进程级默认值**——该服务只拥有一个默认值;按会话的模型选择由[Agent 模型 API](../agent/README.zh.md) 负责。 - **持久化需要 profile 配置编辑器**——没有编辑器时,保存默认值不会保留选择。 diff --git a/packages/core/agent-loop/README.i18n.yaml b/packages/core/agent-loop/README.i18n.yaml index 3b53e2d9b9..4c5affd5fc 100644 --- a/packages/core/agent-loop/README.i18n.yaml +++ b/packages/core/agent-loop/README.i18n.yaml @@ -33,8 +33,8 @@ en: 030202c112f89366 zh: 621d53ed755cafa3 /deepseek-ai-dsh-agent-loop/understand-the-implementation/request-headers-and-adapter-defaults: - en: 158382cfe3cbf499 - zh: b4e6de5a50fd3a65 + en: 022f90db2ebe58f6 + zh: d4374b5ffb132442 /deepseek-ai-dsh-agent-loop/understand-the-implementation/source-map: en: 2a8b93869eb5a2f9 zh: ddab8e738d188a61 @@ -45,8 +45,8 @@ en: 3c5721bc00a0bbf4 zh: 711f832343ffbb75 /deepseek-ai-dsh-agent-loop/understand-the-implementation/turn-and-step-flow: - en: 28939261ccc7defe - zh: 9d157a321307ebd4 + en: 4926502ada48be6b + zh: 7b3ab909f771d2a2 /deepseek-ai-dsh-agent-loop/understand-the-implementation/failure-and-cancellation: en: eea56903c4ff82f5 zh: c0d5abb5767780ed diff --git a/packages/core/agent-loop/README.md b/packages/core/agent-loop/README.md index 8ce8c7cf63..525ca25797 100644 --- a/packages/core/agent-loop/README.md +++ b/packages/core/agent-loop/README.md @@ -92,7 +92,7 @@ The package is the one concrete implementation of the public `Agent` contract. I ### Request headers and adapter defaults -Before prompt assembly, the loop calls `ctx.agentModel.resolveForNextStep()` and captures provider, model, and optional explicit reasoning effort for the step and its retries. `AssembleContext.callConfig` and `agent/pre-step.callConfig` carry that selection; prompt variables use its route. The deprecated `agent/request` hook remains available to existing listeners and may replace other controls such as sampling. Changing provider or model, or adding, changing, or clearing reasoning effort rejects the step before input is committed. A later recorded choice applies to the next step, including a tool continuation in the same turn. +Before prompt assembly, the loop calls `agent.model.current()` and captures provider, model, and optional explicit reasoning effort for the step and its retries. `AssembleContext.callConfig` and `agent/pre-step.callConfig` carry that selection; prompt variables use its route. The deprecated `agent/request` hook remains available to existing listeners and may replace other controls such as sampling. Changing provider or model, or adding, changing, or clearing reasoning effort rejects the step before input is committed. A later recorded choice applies to the next step, including a tool continuation in the same turn. After `agent/request`, `ctx.llm.prepareCall()` validates adapter-owned fields and resolves reasoning-effort and output-token defaults under the active turn signal. The loop retains that exact adapter through resolution, `request/header` logging, and dispatch. It writes a full header for the first request, a changed envelope (config or tools — the prompt is not part of the header), an explicit message-series start, a request after a surface replacement or image-offload decision, and resume; unchanged steps, retries, and ordinary later turns in the same series inherit the latest header, and an in-history prompt append is not a replacement, so the request that follows it inherits the header too. Beside the header, the loop logs `request/context` — provider, model, `contextWindow`, and the route's `systemPromptUpdate` mode from `prepareCall()` — only when one of those differs from the latest snapshot. Before the next waterfall, the loop removes adapter-default fields so the current route resolves them again, while explicit settings persist. An unhandled route still fails with `NO_ADAPTER`. @@ -121,7 +121,7 @@ The loop is the production acquisition point for session write handles. When `ct ### Turn and step flow -The driver owns one agent for its lifetime and runs inside `ctx.agents.withInitiator(agent, ...)`. `AgentLoop` registers the standard `inbox` projection for its service lifetime, so cold reads work before any Agent exists and after all Agents unload. Its package-internal `ReactLoopInbox` uses that shared projection for structural commands and loop-only claims. At a turn boundary it opens the durable turn, then atomically claims pending next-step input plus one queued prompt; between steps it claims only next-step input. The driver assembles the prompt and tools and runs `agent/pre-step`; the fallback projects runtime context after listeners run. A rejected decision or empty first batch opens no step. On the first attempt after acceptance, `step/start` precedes the `agent/request` waterfall and `prepareCall()`; neither async phase sees the pending system prompt or accepted users committed to history, and cancellation during either commits neither. For every attempt, the loop then synchronously reconciles the rendered prompt against the surviving `system/message` nodes using the prepared call capability, refreshes registered runtime facts without reassembling the bound prompt or tools, appends the accepted `user/message` batch only on the first attempt with its owned context candidate replaced in place, and restores removed runtime context, logs the header and context as needed, and derives and freezes the request before streaming through that bound prepared call. Retries reuse the same rendered assembly without repeating assembly, `agent/pre-step`, or user admission. Reconciliation sees pre-step and retry compaction; a broken series consolidates the prompt at the head rather than appending an update after already-committed users. The request is `header.config`, `deriveMessages()`, and `header.tools`; it carries no `system` field. Each model attempt emits one process-local `start`, emits every `chunk` only after the matching durable assistant-frame settlement, and emits exactly one terminal `end`; final assembly or message-append failure settles it as `aborted`, while `committed` follows the durable `assistant/message`. Each successful model call appends one message anchor, and a cancelled stream appends an `interrupted: true` anchor with the delivered prefix so the next request contains what the user saw. Within a step, exclusive calls form barriers and parallel-safe calls use the bounded rolling pool; policy, durable results... (line truncated to 2000 chars) +The driver owns one agent for its lifetime and runs inside `ctx.agents.withInitiator(agent, ...)`. `AgentLoop` registers the standard `inbox` projection for its service lifetime, so cold reads work before any Agent exists and after all Agents unload. It also owns `modelSelection` and constructs an `AgentModel` for each Agent, with the public [Model API](../agent/README.md#model-selection). Its package-internal `ReactLoopInbox` uses that shared projection for structural commands and loop-only claims. At a turn boundary it opens the durable turn, then atomically claims pending next-step input plus one queued prompt; between steps it claims only next-step input. The driver assembles the prompt and tools and runs `agent/pre-step`; the fallback projects runtime context after listeners run. A rejected decision or empty first batch opens no step. On the first attempt after acceptance, `step/start` precedes the `agent/request` waterfall and `prepareCall()`; neither async phase sees the pending system prompt or accepted users committed to history, and cancellation during either commits neither. For every attempt, the loop then synchronously reconciles the rendered prompt against the surviving `system/message` nodes using the prepared call capability, refreshes registered runtime facts without reassembling the bound prompt or tools, appends the accepted `user/message` batch only on the first attempt with its owned context candidate replaced in place, and restores removed runtime context, logs the header and context as needed, and derives and freezes the request before streaming through that bound prepared call. Retries reuse the same rendered assembly without repeating assembly, `agent/pre-step`, or user admission. Reconciliation sees pre-step and retry compaction; a broken series consolidates the prompt at the head rather than appending an update after already-committed users. The request is `header.config`, `deriveMessages()`, and `header.tools`; it carries no `system` field. Each model attempt emits one process-local `start`, emits every `chunk` only after the matching durable assistant-frame settlement, and emits exactly one terminal `end`; final assembly or message-append failure settles it as `aborted`, while `committed` follows the durable `assistant/message`. Each successful model call appends one message anchor, and a cancelled stream appends an `interrupted: true` anchor with the delivered prefix so the next request contains what the user saw. Within a step, exclusive calls form barriers and parallel-safe calls use the bounded rolling pool; policy, durable results... (line truncated to 2000 chars) Prompt admission uses the actual `prepareCall()` result, not the preceding `request/context`. With no system node, even an empty prompt is appended (reserving node 0 without a wire message). On an incapable route or at a new request series, a non-empty rendering is consolidated at the first system node: each non-empty later system node receives a logged per-node empty replacement, then the head is rewritten if needed. Dormant empty tails need no replacement and do not determine the effective text. Consolidation applies even when the latest effective text is unchanged. On a continuing `in-history` series, an unchanged effective prompt produces no event and a non-empty change appends. An empty rendering clears every non-empty later system node through a logged per-node empty replacement, then empties the head if needed, regardless of route or series state. No older instructions remain model-visible. An empty head with no active later system node means no prompt; repeated clears and resume leave it empty. A restored non-empty prompt follows the same route/series rule: a continuing capable route may append it, while an incapable route or new series refills the head. A step starts a series when the pre-step decision declares `startsRequestSeries` or `session.surface.contentGeneration` changed since attachment or the last request (a replacement or image-offload decision). Visible tool-schema changes force prompt consolidation only when the prepared route lacks `toolUpdate`. Routes supporting both tool and system-prompt updates can append a changed prompt and activate added tools without rewriting earlier messages. If another admission condition replaces a system node, the request starts a new series with current tool declarations. Resume and a provider or model swap alone continue the series; the prepared route still governs admission. Per-node empty replacements preserve intervening history without a surface delete operation. diff --git a/packages/core/agent-loop/README.zh.md b/packages/core/agent-loop/README.zh.md index ac2199b3da..13bfd614b5 100644 --- a/packages/core/agent-loop/README.zh.md +++ b/packages/core/agent-loop/README.zh.md @@ -92,7 +92,7 @@ const handle = await ctx.agents.create({ ### 请求 header 与适配器默认值 -循环在提示词组装前调用 `ctx.agentModel.resolveForNextStep()`,并为该步骤及其重试捕获提供方、模型及可选的显式推理强度。`AssembleContext.callConfig` 与 `agent/pre-step.callConfig` 携带该选择;提示词变量使用其中的路由。已弃用的 `agent/request` hook 仍可供现有监听器使用,可以替换采样等其他控制参数。改变提供方或模型,或添加、修改、清除推理强度,都会在提交输入前拒绝该步骤。之后记录的选择从下一个步骤开始生效,包括同一轮次中的工具后续步骤。 +循环在提示词组装前调用 `agent.model.current()`,并为该步骤及其重试捕获提供方、模型及可选的显式推理强度。`AssembleContext.callConfig` 与 `agent/pre-step.callConfig` 携带该选择;提示词变量使用其中的路由。已弃用的 `agent/request` hook 仍可供现有监听器使用,可以替换采样等其他控制参数。改变提供方或模型,或添加、修改、清除推理强度,都会在提交输入前拒绝该步骤。之后记录的选择从下一个步骤开始生效,包括同一轮次中的工具后续步骤。 `agent/request` 返回后,`ctx.llm.prepareCall()` 会在活跃轮次信号下校验适配器持有的字段,并解析推理强度和输出 token 默认值。循环会在解析、`request/header` 记录与分派期间保留同一个适配器。循环会为首次请求、变化的 envelope(配置或工具——提示词不属于 header)、显式消息序列起点、surface 替换或图片省略决定后的请求及恢复写入完整 header;同一序列内内容未变的步骤、重试与普通后续轮次继承最新 header,历史内追加提示词不是替换,因此紧随其后的请求同样继承 header。在 header 之外,循环还会记录 `request/context`——提供方、模型、`contextWindow` 以及来自 `prepareCall()` 的路由 `systemPromptUpdate` 模式——且仅在其中任何一项与最新快照不同时记录。下一次 waterfall 分发前,循环移除适配器默认字段,使当前路由重新解析它们;显式设置则保留。未处理的路由仍以 `NO_ADAPTER` 失败。 @@ -121,7 +121,7 @@ loop 在发布工厂前等待 inbox 与轮次边界注册完成,并将句柄 ### 轮次与步骤流程 -驱动器在其整个生命周期内拥有一个 agent,并在 `ctx.agents.withInitiator(agent, ...)` 内运行。`AgentLoop` 在服务生命周期内注册标准 `inbox` 投影,因此冷读取在没有 Agent 和所有 Agent 卸载后都可用。其包内部 `ReactLoopInbox` 使用该共享投影执行结构化命令与仅供 loop 使用的领取操作。在轮次边界,它先打开持久轮次,再原子领取待处理的 next-step 输入与一条排队提示词;在步骤之间则只领取 next-step 输入。驱动器组装提示词与工具,并运行 `agent/pre-step`;fallback 在监听器执行后投影运行时上下文。被拒绝的决定或空的首批输入不打开步骤。接纳后的首次尝试先记录 `step/start`,再运行 `agent/request` waterfall 与 `prepareCall()`;这两个异步阶段都看不到待提交的系统提示词与已接纳用户消息进入历史,在任一阶段取消都不会提交这两者。每次尝试时,循环随后依据已准备调用的能力,同步将渲染后的提示词与存活的 `system/message` 节点协调一致、刷新已注册运行时事实而不重新组装绑定的提示词或工具、仅在首次尝试追加已接纳的 `user/message` 批次并原位替换循环自有上下文候选、恢复被移除的运行时上下文、按需记录 header 与 context,再派生并冻结请求,通过该绑定的已准备调用发起流式请求。重试复用同一份已渲染组装结果,不重复组装、`agent/pre-step` 或用户消息准入。协调过程可见 pre-step 与重试中的压缩;序列中断时将提示词归并到头部,而非在已提交用户消息之后追加更新。请求由 `header.config`、`deriveMessages()` 与 `header.tools` 构成,不携带 `system` 字段。每次模型尝试会发出一个进程本地 `start`,仅在匹配的持久 assistant-frame 结算之后发出各个 `chunk`,并恰好发出一个终态 `end`;最终组装或消息追加失败时以 `aborted` 结算,`committed` 则出现在持久 `assistant/message` 之后。每次成功的模型调用都恰好追加一个 message 锚点,被取消的流则追加带 `interrupted: true` 的锚点并携带已交付前缀,使下一次请求包含用户看到的内容。在步骤内,独占调用形成屏障,并行安全调用使用有界滚动池;策略、持久结果与结果上下文保持模型顺序。 +驱动器在其整个生命周期内拥有一个 agent,并在 `ctx.agents.withInitiator(agent, ...)` 内运行。`AgentLoop` 在服务生命周期内注册标准 `inbox` 投影,因此冷读取在没有 Agent 和所有 Agent 卸载后都可用。它还拥有 `modelSelection`,并为每个 Agent 构建 `AgentModel`,实现公开的 [Model API](../agent/README.zh.md#model-selection)。其包内部 `ReactLoopInbox` 使用该共享投影执行结构化命令与仅供 loop 使用的领取操作。在轮次边界,它先打开持久轮次,再原子领取待处理的 next-step 输入与一条排队提示词;在步骤之间则只领取 next-step 输入。驱动器组装提示词与工具,并运行 `agent/pre-step`;fallback 在监听器执行后投影运行时上下文。被拒绝的决定或空的首批输入不打开步骤。接纳后的首次尝试先记录 `step/start`,再运行 `agent/request` waterfall 与 `prepareCall()`;这两个异步阶段都看不到待提交的系统提示词与已接纳用户消息进入历史,在任一阶段取消都不会提交这两者。每次尝试时,循环随后依据已准备调用的能力,同步将渲染后的提示词与存活的 `system/message` 节点协调一致、刷新已注册运行时事实而不重新组装绑定的提示词或工具、仅在首次尝试追加已接纳的 `user/message` 批次并原位替换循环自有上下文候选、恢复被移除的运行时上下文、按需记录 header 与 context,再派生并冻结请求,通过该绑定的已准备调用发起流式请求。重试复用同一份已渲染组装结果,不重复组装、`agent/pre-step` 或用户消息准入。协调过程可见 pre-step 与重试中的压缩;序列中断时将提示词归并到头部,而非在已提交用户消息之后追加更新。请求由 `header.config`、`deriveMessages()` 与 `header.tools` 构成,不携带 `system` 字段。每次模型尝试会发出一个进程本地 `start`,仅在匹配的持久 assistant-frame 结算之后发出各个 `chunk`,并恰好发出一个终态 `end`;最终组装或消息追加失败时以 `aborted` 结算,`committed` 则出现在持久 `assistant/message` 之后。每次成功的模型调用都恰好追加一个 message 锚点,被取消的流则追加带 `interrupted: true` 的锚点并携带已交付前缀,使下一次请求包含用户看到的内容。在步骤内,独占调用形成屏障,并行安全调用使用有界滚动池;策略、持久结果与结果上下文保持模型顺序。 提示词准入依据实际的 `prepareCall()` 结果,而非先前的 `request/context`。没有系统节点时,即使提示词为空也追加(预留第 0 号节点,但不产生协议消息)。在不具备能力的路由上或新请求序列开始时,非空渲染文本归并到首个系统节点:每个非空的后续系统节点分别收到有日志记录的空内容替换,随后按需重写头节点。未生效的空尾节点无需替换,也不决定有效文本。即使最新有效文本未变,也执行归并。延续中的 `in-history` 序列在有效提示词不变时不产生事件,非空变更则追加。无论路由或序列状态如何,空渲染文本都会通过有日志记录的逐节点空内容替换清除每个非空的后续系统节点,再按需清空头节点。模型不会继续看到旧指令。空头节点且没有生效的后续系统节点表示没有提示词;重复清除与恢复会话都保持为空。重新提供的非空提示词遵循同一路由/序列规则:延续中的具备能力路由可以追加它,不具备能力的路由或新序列则重新填充头节点。以下情况开启序列:pre-step 决定声明 `startsRequestSeries`,或 `session.surface.contentGeneration` 自附接或上次请求以来发生变化(替换或图片省略决定)。仅当已准备路由未声明 `toolUpdate` 时,可见工具 schema 变化才强制归并提示词。同时支持工具和系统提示词更新的路由可以追加变更后的提示词并激活新增工具,无需改写此前消息。如果其他准入条件替换系统节点,请求会以当前工具声明开启新序列。恢复与单纯的提供方或模型切换都延续序列;准入仍由已准备的路由决定。逐节点的空内容替换保留其间历史,无需 surface 删除操作。 diff --git a/packages/core/agent-loop/package.json b/packages/core/agent-loop/package.json index 82cbdf1419..6792353436 100644 --- a/packages/core/agent-loop/package.json +++ b/packages/core/agent-loop/package.json @@ -36,8 +36,7 @@ "@deepseek-ai/dsh-session-persistence": "workspace:*", "@deepseek-ai/dsh-session-projection": "workspace:*", "@deepseek-ai/dsh-system-prompt": "workspace:*", - "@deepseek-ai/dsh-tools": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-tools": "workspace:*" }, "dependencies": { "@deepseek-ai/dsh-brand": "workspace:*", @@ -57,7 +56,6 @@ "@deepseek-ai/dsh-session-persistence-jsonl": "workspace:*", "@deepseek-ai/dsh-session-projection": "workspace:*", "@deepseek-ai/dsh-system-prompt": "workspace:*", - "@deepseek-ai/dsh-tools": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-tools": "workspace:*" } } diff --git a/packages/core/agent-loop/src/agent.ts b/packages/core/agent-loop/src/agent.ts index e95f736c09..13d32a7c6e 100644 --- a/packages/core/agent-loop/src/agent.ts +++ b/packages/core/agent-loop/src/agent.ts @@ -11,13 +11,14 @@ import type { AgentOptions, AgentStatus, CancelOptions, - InboxTarget, ModelSelection, + ModelSelectionProjectionState, + InboxTarget, InboxState, PreStepDecision, RequestErrorAction, } from '@deepseek-ai/dsh-agent' -import { agentEvents, assembleContextFor } from '@deepseek-ai/dsh-agent' +import { AgentModel, agentEvents, assembleContextFor } from '@deepseek-ai/dsh-agent' import type { GenerateOptions, LlmCallConfig, Message, PreparedLlmCall } from '@deepseek-ai/dsh-llm' import { LlmError, @@ -98,6 +99,7 @@ function abortedCancelCause(signal: AbortSignal): AgentCancelCause | undefined { /** Drives one session through turn and step boundaries. */ export class ReactLoopAgent implements Agent { + readonly model: AgentModel readonly inbox: ReactLoopInbox private phase: Phase private activityDone: Promise = Promise.resolve() @@ -126,6 +128,7 @@ export class ReactLoopAgent implements Agent { public readonly id: SessionId, public readonly options: AgentOptions, public readonly session: Session, + readModel: () => ModelSelectionProjectionState, readInbox: () => InboxState, lastTurn: number, ) { @@ -133,6 +136,7 @@ export class ReactLoopAgent implements Agent { this.dispatch = agentEvents(loopCtx, this) this.scope = createScope(loopCtx, this) this.ctx = this.scope.ctx + this.model = new AgentModel(readModel, session, options) this.inbox = new ReactLoopInbox(readInbox, session, this.dispatch) this.phase = { kind: 'idle', lastTurn } this.runtimeContext = new RuntimeContextProjection(this.ctx, session) @@ -271,7 +275,7 @@ export class ReactLoopAgent implements Agent { if (this.phase.kind !== 'running') throw new Error(`agent "${this.id}": pre-step outside running phase`) const signal = this.phase.abort.signal const claimed = this.inbox.claim(target, position.turn) - const selection = this.loopCtx.agentModel.resolveForNextStep(this) + const selection = this.model.current() if (selection === undefined) { throw new Error(`agent "${this.id}" has no provider/model: set AgentOptions.provider and AgentOptions.model or record a model selection`) } @@ -592,10 +596,10 @@ export class ReactLoopAgent implements Agent { ) signal.throwIfAborted() if (proposedConfig.provider !== selection.provider || proposedConfig.model !== selection.model) { - throw new Error(`agent "${this.id}": agent/request proposed "${proposedConfig.provider}/${proposedConfig.model}" after step preparation fixed "${selection.provider}/${selection.model}"; use ctx.agentModel.select() before step preparation`) + throw new Error(`agent "${this.id}": agent/request proposed "${proposedConfig.provider}/${proposedConfig.model}" after step preparation fixed "${selection.provider}/${selection.model}"; use agent.model.select() before step preparation`) } if (proposedConfig.reasoningEffort !== selection.reasoningEffort) { - throw new Error(`agent "${this.id}": agent/request proposed reasoning effort "${proposedConfig.reasoningEffort ?? 'adapter default'}" after step preparation fixed "${selection.reasoningEffort ?? 'adapter default'}"; use ctx.agentModel.select() before step preparation`) + throw new Error(`agent "${this.id}": agent/request proposed reasoning effort "${proposedConfig.reasoningEffort ?? 'adapter default'}" after step preparation fixed "${selection.reasoningEffort ?? 'adapter default'}"; use agent.model.select() before step preparation`) } let config: LlmCallConfig let preparedCall: PreparedLlmCall | undefined diff --git a/packages/core/agent-loop/src/index.ts b/packages/core/agent-loop/src/index.ts index a7ab81e09c..e19bbad245 100644 --- a/packages/core/agent-loop/src/index.ts +++ b/packages/core/agent-loop/src/index.ts @@ -19,9 +19,11 @@ import type { CreateAgentOptions, ResumeAgentOptions, SessionStartSource, + ModelSelection, + ModelSelectionProjectionState, InboxState, } from '@deepseek-ai/dsh-agent' -import type {} from '@deepseek-ai/dsh-agent-model' +import { modelSelectionProjectionDefinition } from '@deepseek-ai/dsh-agent' import { errorChain, ReasoningEffortId } from '@deepseek-ai/dsh-llm' import { interruptedTurnClosers, SessionLogOffset, SessionPreparation } from '@deepseek-ai/dsh-session' import type { Session, SessionHeader, SessionId } from '@deepseek-ai/dsh-session' @@ -287,7 +289,7 @@ function validateConfiguredAgents(agents: Config['agents']): void { /** Concrete agent factory and driver service. */ export class AgentLoop extends Service implements AgentFactory { - static inject = ['agentModel', 'agents', 'sessions', 'llm', 'tools', 'systemPrompt', 'sessionProjections'] + static inject = ['agents', 'sessions', 'llm', 'tools', 'systemPrompt', 'sessionProjections'] /** Runtime schema for declarative agents. */ static Config: z<{ agents?: Config['agents']; maxParallelToolCalls?: number }, Config> = z.object({ @@ -309,6 +311,7 @@ export class AgentLoop extends Service implements AgentFactory { private readonly ownership: FactoryOwnership /** Plain holder prevents Cordis from re-tracing the factory's dependency context through a caller shadow. */ private readonly runtime: { ctx: Context } + private modelProjectionHandle!: ProjectionHandle private inboxProjectionHandle!: ProjectionHandle constructor(ctx: Context, config: Config) { @@ -326,8 +329,9 @@ export class AgentLoop extends Service implements AgentFactory { protected async [Service.init](): Promise { const ctx = this.runtime.ctx const ownership = this.ownership - // The inbox remains readable until agent teardown finishes. + // Agent projections remain readable until teardown finishes. ctx.effect(() => () => ownership.dispose(), 'agentLoop.transactions()') + this.modelProjectionHandle = await ctx.sessionProjections.register(modelSelectionProjectionDefinition) this.inboxProjectionHandle = await ctx.sessionProjections.register(inboxProjectionDefinition) ctx.effect(() => ctx.agents.setFactory(this), 'agentLoop.setFactory()') ctx.systemPrompt.variable('provider', context => context.callConfig?.provider ?? context.agent?.options.provider) @@ -368,9 +372,15 @@ export class AgentLoop extends Service implements AgentFactory { private applyConfiguredRoute(agent: Agent, options: AgentOptions): void { const { provider, model, reasoningEffort } = options - if (provider && model) this.runtime.ctx.agentModel.applyConfiguredRoute(agent, { - provider, model, ...reasoningEffort === undefined ? {} : { reasoningEffort }, - }) + if (!provider || !model) return + const previous = agent.model.current() + const selection: ModelSelection = { provider, model, ...reasoningEffort === undefined ? {} : { reasoningEffort } } + if (selection.reasoningEffort === undefined && previous !== undefined + && previous.provider === selection.provider && previous.model === selection.model + && previous.reasoningEffort !== undefined) { + selection.reasoningEffort = previous.reasoningEffort + } + agent.model.select(selection) } /** Report a contained declarative-start failure to identity-bound consumers. */ @@ -556,10 +566,16 @@ export class AgentLoop extends Service implements AgentFactory { } } const untrack = this.ownership.track(dispose) + const modelProjectionHandle = this.modelProjectionHandle const inboxProjectionHandle = this.inboxProjectionHandle try { unfollowOwner = ownerCtx.effect(function* () { - const created = machine = new ReactLoopAgent(loopCtx, id, options, session, () => inboxProjectionHandle.stateOf(session), lastTurn) + const created = machine = new ReactLoopAgent( + loopCtx, id, options, session, + () => modelProjectionHandle.stateOf(session), + () => inboxProjectionHandle.stateOf(session), + lastTurn, + ) machineReady.resolve() // Adopt the scope on its original fiber so it cannot unload before teardown. // A loop already unloading during minting owns the provisional scope itself. diff --git a/packages/core/agent-loop/tests/agent-initiator.spec.ts b/packages/core/agent-loop/tests/agent-initiator.spec.ts index 87ee982080..0b9d32ec06 100644 --- a/packages/core/agent-loop/tests/agent-initiator.spec.ts +++ b/packages/core/agent-loop/tests/agent-initiator.spec.ts @@ -2,7 +2,6 @@ import { describe, expect, it } from 'vitest' import { Context, type Fiber } from '@deepseek-ai/cordis' import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import LlmRuntime, { createUserMessage, ToolCallId, LlmAdapter } from '@deepseek-ai/dsh-llm' import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm' @@ -24,7 +23,6 @@ async function harness(adapter: LlmAdapter): Promise { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) const agentsFiber = await ctx.plugin(AgentRegistry) @@ -126,7 +124,6 @@ describe('AgentLoop initiator scope', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -388,7 +385,6 @@ describe('AgentLoop initiator scope', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/agent.spec.ts b/packages/core/agent-loop/tests/agent.spec.ts index d5b63c4e6a..180edf291d 100644 --- a/packages/core/agent-loop/tests/agent.spec.ts +++ b/packages/core/agent-loop/tests/agent.spec.ts @@ -3,7 +3,6 @@ import { describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import LlmRuntime from '@deepseek-ai/dsh-llm' import type { MessageSource } from '@deepseek-ai/dsh-llm' @@ -25,7 +24,6 @@ async function harness(adapter: MockAdapter): Promise { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/cancel.spec.ts b/packages/core/agent-loop/tests/cancel.spec.ts index 5786827d76..f6d53b0029 100644 --- a/packages/core/agent-loop/tests/cancel.spec.ts +++ b/packages/core/agent-loop/tests/cancel.spec.ts @@ -16,7 +16,6 @@ import SystemPrompt from '@deepseek-ai/dsh-system-prompt' import ToolRuntime, { defineContentToolFixture, TOOL_ABORTED_BEFORE_DISPATCH } from '@deepseek-ai/dsh-tools' import AgentRegistry, { type Agent, type AgentCancelCause } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { MockAdapter, textResponse, toolCallResponse } from './mock-adapter.ts' @@ -29,7 +28,6 @@ async function harness(adapter: MockAdapter) { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -828,7 +826,6 @@ describe('Agent.cancel()', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/config-session-id.spec.ts b/packages/core/agent-loop/tests/config-session-id.spec.ts index a15a19ec9b..b7da13c624 100644 --- a/packages/core/agent-loop/tests/config-session-id.spec.ts +++ b/packages/core/agent-loop/tests/config-session-id.spec.ts @@ -15,7 +15,6 @@ import type { SessionHandle } from '@deepseek-ai/dsh-session-persistence' import * as JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl' import AgentLoop, { CONFIGURED_AGENT_IDENTITIES_KEY } from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { MockAdapter, textResponse } from './mock-adapter.ts' @@ -41,7 +40,6 @@ async function makeCoreContext(): Promise { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -373,7 +371,6 @@ describe('config-driven session id', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -397,7 +394,6 @@ describe('config-driven session id', () => { await ctx1.plugin(LlmRuntime) await ctx1.plugin(SessionStore) await ctx1.plugin(SessionProjectionRegistry) - await ctx1.plugin(AgentModelPlugin) await ctx1.plugin(SystemPrompt) await ctx1.plugin(ToolRuntime) await ctx1.plugin(AgentRegistry) @@ -421,7 +417,6 @@ describe('config-driven session id', () => { await ctx2.plugin(LlmRuntime) await ctx2.plugin(SessionStore) await ctx2.plugin(SessionProjectionRegistry) - await ctx2.plugin(AgentModelPlugin) await ctx2.plugin(SystemPrompt) await ctx2.plugin(ToolRuntime) await ctx2.plugin(AgentRegistry) @@ -448,7 +443,6 @@ describe('config-driven session id', () => { await ctx1.plugin(LlmRuntime) await ctx1.plugin(SessionStore) await ctx1.plugin(SessionProjectionRegistry) - await ctx1.plugin(AgentModelPlugin) await ctx1.plugin(SystemPrompt) await ctx1.plugin(ToolRuntime) await ctx1.plugin(AgentRegistry) @@ -467,7 +461,6 @@ describe('config-driven session id', () => { await ctx2.plugin(LlmRuntime) await ctx2.plugin(SessionStore) await ctx2.plugin(SessionProjectionRegistry) - await ctx2.plugin(AgentModelPlugin) await ctx2.plugin(SystemPrompt) await ctx2.plugin(ToolRuntime) await ctx2.plugin(AgentRegistry) @@ -500,7 +493,7 @@ describe('config-driven session id', () => { const h1 = await ctx1.agents.create({ sessionId: SessionId('sticky-route'), agentOptions: { provider: 'mock', model: 'mock' } }) h1.agent.followup(createUserMessage({ content: [{ type: 'text', text: 'remember me' }], source: { kind: 'user' } })) await waitForIdle(ctx1, h1.agent) - ctx1.agentModel.select(h1.agent, { provider: 'mock', model: 'mock', reasoningEffort: ReasoningEffortId('high') }) + h1.agent.model.select({ provider: 'mock', model: 'mock', reasoningEffort: ReasoningEffortId('high') }) await h1.dispose() await ctx1.fiber.dispose() @@ -509,7 +502,7 @@ describe('config-driven session id', () => { await ctx2.plugin(AgentLoop, { agents: [{ id: SessionId('main'), ...route, resumeSessionId: SessionId('sticky-route') }] }) await expect.poll(() => ctx2.agents.get(SessionId('sticky-route')), { timeout: 5_000 }).toBeDefined() const resumed = ctx2.agents.get(SessionId('sticky-route'))! - expect(ctx2.agentModel.resolveForNextStep(resumed)).toEqual(expected) + expect(resumed.model.current()).toEqual(expected) expect(resumed.session.snapshotEvents().filter(event => event.type === 'model/selection')).toHaveLength(1 + appended) await ctx2.fiber.dispose() }) @@ -521,7 +514,6 @@ describe('config-driven session id', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/contract-regressions.spec.ts b/packages/core/agent-loop/tests/contract-regressions.spec.ts index 2bdd1bc67f..8a7d835d31 100644 --- a/packages/core/agent-loop/tests/contract-regressions.spec.ts +++ b/packages/core/agent-loop/tests/contract-regressions.spec.ts @@ -7,7 +7,6 @@ import SystemPrompt from '@deepseek-ai/dsh-system-prompt' import ToolRuntime, { defineContentToolFixture, type PostToolDecision } from '@deepseek-ai/dsh-tools' import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { MockAdapter, textResponse, toolCallResponse } from './mock-adapter.ts' @@ -28,7 +27,6 @@ async function harness(adapter: MockAdapter) { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -460,7 +458,7 @@ describe('adapter registration, routing, and accepted-input ownership', () => { const ctx = await harness(adapter) const agent = await ctx.agentLoop.create(SessionId('a1'), {}) - ctx.agentModel.select(agent, { provider: 'mock', model: 'mock' }) + agent.model.select({ provider: 'mock', model: 'mock' }) send(agent, 'go') await waitForIdle(ctx, agent) @@ -576,7 +574,6 @@ describe('turn numbering continues across seeded sessions', () => { await ctx2.plugin(LlmRuntime) await ctx2.plugin(SessionStore) await ctx2.plugin(SessionProjectionRegistry) - await ctx2.plugin(AgentModelPlugin) await ctx2.plugin(SystemPrompt) await ctx2.plugin(ToolRuntime) await ctx2.plugin(AgentRegistry) @@ -1293,7 +1290,6 @@ describe('disposal and cancellation during pre-step assembly', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -1344,7 +1340,6 @@ describe('disposal and cancellation during pre-step assembly', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -1395,7 +1390,6 @@ describe('disposal and cancellation during pre-step assembly', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -1442,7 +1436,6 @@ describe('disposal and cancellation during pre-step assembly', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -1491,7 +1484,6 @@ describe('disposal and cancellation during pre-step assembly', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/coverage-edges.spec.ts b/packages/core/agent-loop/tests/coverage-edges.spec.ts index 023ab48e14..38387dc3ad 100644 --- a/packages/core/agent-loop/tests/coverage-edges.spec.ts +++ b/packages/core/agent-loop/tests/coverage-edges.spec.ts @@ -9,7 +9,6 @@ import ToolRuntime, { defineContentToolFixture } from '@deepseek-ai/dsh-tools' import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { MockAdapter, textResponse, toolCallResponse } from './mock-adapter.ts' @@ -28,7 +27,6 @@ async function harness(adapter: MockAdapter) { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/inbox.spec.ts b/packages/core/agent-loop/tests/inbox.spec.ts index ba908715e0..5bfe365bd3 100644 --- a/packages/core/agent-loop/tests/inbox.spec.ts +++ b/packages/core/agent-loop/tests/inbox.spec.ts @@ -1,3 +1,4 @@ +import { unsupportedModel } from '@deepseek-ai/dsh-agent-loop-testkit' import { Context } from '@deepseek-ai/cordis' import { agentEvents, type Agent } from '@deepseek-ai/dsh-agent' import { createUserMessage, freezeMessage } from '@deepseek-ai/dsh-llm' @@ -25,6 +26,7 @@ function stubAgent(rawId: string, overrides: Partial = {}): Agent { id, options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'idle', ctx, diff --git a/packages/core/agent-loop/tests/interception.spec.ts b/packages/core/agent-loop/tests/interception.spec.ts index 31f3921675..352583df36 100644 --- a/packages/core/agent-loop/tests/interception.spec.ts +++ b/packages/core/agent-loop/tests/interception.spec.ts @@ -17,7 +17,6 @@ import AgentRegistry, { } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { MockAdapter, textResponse, toolCallResponse } from './mock-adapter.ts' @@ -46,7 +45,6 @@ async function harness(adapter: MockAdapter) { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/loop.spec.ts b/packages/core/agent-loop/tests/loop.spec.ts index 2b0b4125d8..dbe1ef6564 100644 --- a/packages/core/agent-loop/tests/loop.spec.ts +++ b/packages/core/agent-loop/tests/loop.spec.ts @@ -9,7 +9,6 @@ import ToolRuntime, { defineContentToolFixture } from '@deepseek-ai/dsh-tools' import AgentRegistry, { type Agent, type AssistantStreamFrame } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { MockAdapter, maxTokensResponse, textResponse, toolCallResponse } from './mock-adapter.ts' @@ -34,7 +33,6 @@ async function harness(adapter: MockAdapter, persona = '') { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt, { personaPrefix: persona }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -608,7 +606,7 @@ describe('agent loop', () => { const adapter = new MockAdapter([textResponse('ok')]) const ctx = await harness(adapter, 'You run on {{model}}.') const agent = await ctx.agentLoop.create(SessionId('a-recorded-model'), {}) - ctx.agentModel.select(agent, { provider: 'mock', model: 'mock' }) + agent.model.select({ provider: 'mock', model: 'mock' }) send(agent, 'hi') await waitForIdle(ctx, agent) @@ -1316,7 +1314,7 @@ describe('agent loop', () => { .toEqual([]) expect(agent.session.snapshotEvents().at(-1)).toMatchObject({ type: 'turn/end', data: { reason: { kind: 'error', error: { - message: `agent "a1": agent/request proposed "${field === 'provider' ? 'other/mock' : 'mock/other'}" after step preparation fixed "mock/mock"; use ctx.agentModel.select() before step preparation`, + message: `agent "a1": agent/request proposed "${field === 'provider' ? 'other/mock' : 'mock/other'}" after step preparation fixed "mock/mock"; use agent.model.select() before step preparation`, } } }, }) }) @@ -1905,7 +1903,6 @@ describe('agent loop', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -1936,7 +1933,6 @@ describe('agent loop', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/model.spec.ts b/packages/core/agent-loop/tests/model.spec.ts new file mode 100644 index 0000000000..6208bcd402 --- /dev/null +++ b/packages/core/agent-loop/tests/model.spec.ts @@ -0,0 +1,101 @@ +/** Per-Agent model instances share the loop's projection registration and teardown. */ +import { Context } from '@deepseek-ai/cordis' +import AgentRegistry, { AgentModel, modelSelectionProjectionDefinition } from '@deepseek-ai/dsh-agent' +import AgentLoop from '@deepseek-ai/dsh-agent-loop' +import LlmRuntime from '@deepseek-ai/dsh-llm' +import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' +import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' +import SystemPrompt from '@deepseek-ai/dsh-system-prompt' +import ToolRuntime from '@deepseek-ai/dsh-tools' +import { describe, expect, it, onTestFinished, vi } from 'vitest' + +async function dependencies() { + const ctx = new Context() + onTestFinished(() => ctx.fiber.dispose()) + await ctx.plugin(LlmRuntime) + await ctx.plugin(SessionStore) + await ctx.plugin(SessionProjectionRegistry) + await ctx.plugin(SystemPrompt) + await ctx.plugin(ToolRuntime) + await ctx.plugin(AgentRegistry) + return ctx +} + +const INITIAL = { provider: 'mock', model: 'initial' } +const SELECTED = { provider: 'mock', model: 'selected' } + +describe('Agent model ownership', () => { + it('waits for model projection registration before publishing the factory', async () => { + const ctx = await dependencies() + const ready = Promise.withResolvers() + const entered = Promise.withResolvers() + onTestFinished(() => { ready.resolve(undefined) }) + const register = ctx.sessionProjections.register.bind(ctx.sessionProjections) + vi.spyOn(ctx.sessionProjections, 'register').mockImplementation(async (definition) => { + if (definition.key === modelSelectionProjectionDefinition.key) { + entered.resolve(undefined) + await ready.promise + } + return register(definition) + }) + const loading = ctx.plugin(AgentLoop, { agents: [] }) + await entered.promise + await expect(ctx.agents.create({ sessionId: SessionId('too-early') })).rejects.toThrow('no agent factory registered') + ready.resolve(undefined) + await loading + const handle = await ctx.agents.create({ sessionId: SessionId('ready'), agentOptions: INITIAL }) + expect(handle.agent.model.current()).toEqual(INITIAL) + }) + + it('binds each model instance to its Agent and makes it available during setup', async () => { + const ctx = await dependencies() + await ctx.plugin(AgentLoop, { agents: [] }) + const first = await ctx.agents.create({ + sessionId: SessionId('first'), + agentOptions: INITIAL, + setup: (_ctx, agent) => { + expect(agent.model).toBeInstanceOf(AgentModel) + agent.model.select(SELECTED) + }, + }) + const second = await ctx.agents.create({ sessionId: SessionId('second'), agentOptions: INITIAL }) + expect(first.agent.model).not.toBe(second.agent.model) + expect(first.agent.model.current()).toEqual(SELECTED) + expect(second.agent.model.current()).toEqual(INITIAL) + expect(second.agent.session.snapshotEvents()).toEqual([]) + expect(ctx.sessionProjections.snapshot(first.agent.session).values.modelSelection) + .toEqual({ lastUsed: null, next: SELECTED }) + }) + + it.each(['loop', 'root'] as const)('retains model selection until Agent cleanup finishes on %s disposal', async (owner) => { + const ctx = await dependencies() + const loop = await ctx.plugin(AgentLoop, { agents: [] }) + const entered = Promise.withResolvers() + const release = Promise.withResolvers() + onTestFinished(() => { release.resolve(undefined) }) + const handle = await ctx.agents.create({ + sessionId: SessionId('cleanup'), + agentOptions: INITIAL, + setup: (agentCtx, agent) => { + agentCtx.effect(() => async () => { + entered.resolve(undefined) + await release.promise + agent.model.select(SELECTED) + expect(agent.model.current()).toEqual(SELECTED) + }) + }, + }) + const disposal = owner === 'loop' ? loop.dispose() : ctx.fiber.dispose() + await entered.promise + expect(handle.agent.model.current()).toEqual(INITIAL) + release.resolve(undefined) + await disposal + expect(handle.agent.session.snapshotEvents().at(-1)).toMatchObject({ type: 'model/selection', data: SELECTED }) + if (owner === 'loop') { + expect(ctx.sessionProjections.snapshot(handle.agent.session).values.modelSelection).toBeUndefined() + await ctx.plugin(AgentLoop, { agents: [] }) + expect(ctx.sessionProjections.snapshot(handle.agent.session).values.modelSelection) + .toEqual({ lastUsed: null, next: SELECTED }) + } + }) +}) diff --git a/packages/core/agent-loop/tests/properties.spec.ts b/packages/core/agent-loop/tests/properties.spec.ts index 74d9966802..daea36f629 100644 --- a/packages/core/agent-loop/tests/properties.spec.ts +++ b/packages/core/agent-loop/tests/properties.spec.ts @@ -20,7 +20,6 @@ import ToolRuntime from '@deepseek-ai/dsh-tools' import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import fc from 'fast-check' @@ -42,7 +41,6 @@ async function harness() { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/request-cache.e2e.ts b/packages/core/agent-loop/tests/request-cache.e2e.ts index 83cb03487a..e36281607d 100644 --- a/packages/core/agent-loop/tests/request-cache.e2e.ts +++ b/packages/core/agent-loop/tests/request-cache.e2e.ts @@ -8,7 +8,6 @@ import ToolRuntime, { defineContentToolFixture } from '@deepseek-ai/dsh-tools' import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import * as LlmDeepSeek from '@deepseek-ai/dsh-llm-deepseek-api-key' @@ -44,7 +43,6 @@ async function loopHarness(): Promise { await created.plugin(LlmRuntime) await created.plugin(SessionStore) await created.plugin(SessionProjectionRegistry) - await created.plugin(AgentModelPlugin) await created.plugin(SystemPrompt, { personaPrefix: SYSTEM }) await created.plugin(ToolRuntime) await created.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/request-error.spec.ts b/packages/core/agent-loop/tests/request-error.spec.ts index 60df866c46..616a817082 100644 --- a/packages/core/agent-loop/tests/request-error.spec.ts +++ b/packages/core/agent-loop/tests/request-error.spec.ts @@ -2,7 +2,6 @@ import { describe, expect, it } from 'vitest' import { Context } from '@deepseek-ai/cordis' import AgentRegistry from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import LlmRuntime, { createUserMessage, LlmError } from '@deepseek-ai/dsh-llm' import type { LlmFailure, ResolvedRetryPolicy } from '@deepseek-ai/dsh-llm' @@ -16,7 +15,6 @@ async function harness(adapter: MockAdapter): Promise { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/request-freeze.spec.ts b/packages/core/agent-loop/tests/request-freeze.spec.ts index b0b4c22ff8..6be323b1d7 100644 --- a/packages/core/agent-loop/tests/request-freeze.spec.ts +++ b/packages/core/agent-loop/tests/request-freeze.spec.ts @@ -1,3 +1,4 @@ +import { modelSelectionProjectionDefinition } from '@deepseek-ai/dsh-agent' /** Request immutability through the real loop, including adopted restore graphs. */ import { afterEach, describe, expect, it, vi } from 'vitest' @@ -75,8 +76,9 @@ describe('loop-owned request freezing', () => { expect(Object.isFrozen(assistantEvent.data.message)).toBe(false) ctx.effect(() => ctx.sessions.enter(session)) const inbox = await loopCtx.sessionProjections.register(inboxProjectionDefinition) + const model = await loopCtx.sessionProjections.register(modelSelectionProjectionDefinition) const agent = new ReactLoopAgent(loopCtx, id, { provider: 'mock', model: 'mock' }, session, - () => inbox.stateOf(session), ctx.agents.turnBoundaryOf(session).lastTurn) + () => model.stateOf(session), () => inbox.stateOf(session), ctx.agents.turnBoundaryOf(session).lastTurn) cleanups.push(async () => { agent.cancel({ kind: 'disposed' }) await agent.whenIdle() @@ -126,7 +128,7 @@ describe('loop-owned request freezing', () => { expect(freeze.mock.calls.filter(([value]) => value === userEvent.data)).toHaveLength(1) expect(freeze.mock.calls.filter(([value]) => value === replacement.data)).toHaveLength(1) const resumed = new ReactLoopAgent(loopCtx, id, { provider: 'mock', model: 'mock' }, session, - () => inbox.stateOf(session), ctx.agents.turnBoundaryOf(session).lastTurn) + () => model.stateOf(session), () => inbox.stateOf(session), ctx.agents.turnBoundaryOf(session).lastTurn) cleanups.push(async () => { resumed.cancel({ kind: 'disposed' }) await resumed.whenIdle() diff --git a/packages/core/agent-loop/tests/request-reconstruction.spec.ts b/packages/core/agent-loop/tests/request-reconstruction.spec.ts index 6bfaee04d5..93b398cd99 100644 --- a/packages/core/agent-loop/tests/request-reconstruction.spec.ts +++ b/packages/core/agent-loop/tests/request-reconstruction.spec.ts @@ -16,7 +16,6 @@ import ToolRuntime, { defineContentToolFixture } from '@deepseek-ai/dsh-tools' import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { MockAdapter, textResponse, toolCallResponse } from './mock-adapter.ts' @@ -40,7 +39,6 @@ async function harnessRoutes( await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt, { personaPrefix: persona }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -222,7 +220,7 @@ describe('request stability across the loop', () => { const agent = await ctx.agentLoop.create(SessionId('effort'), { provider: 'mock', model: 'mock' }) send(agent, 'first') await waitForIdle(ctx, agent) - ctx.agentModel.select(agent, { + agent.model.select({ provider: 'mock', model: 'mock', reasoningEffort: ReasoningEffortId('max'), }) send(agent, 'second') @@ -253,7 +251,9 @@ describe('request stability across the loop', () => { sessionId: SessionId(`effort-${model}`), seed: structuredClone(agent.session.snapshotEvents()), agentOptions: { provider: 'mock', model }, - setup: (_ctx, agent) => { resumedCtx.agentModel.applyConfiguredRoute(agent, { provider: 'mock', model }) }, + setup: (_ctx, agent) => { + if (model !== 'mock') agent.model.select({ provider: 'mock', model }) + }, }) send(resumedHandle.agent, 'resumed') await waitForIdle(resumedCtx, resumedHandle.agent) @@ -297,7 +297,7 @@ describe('request stability across the loop', () => { }) send(agent, 'first') await waitForIdle(ctx, agent) - ctx.agentModel.select(agent, { provider: 'other', model: 'other-model' }) + agent.model.select({ provider: 'other', model: 'other-model' }) send(agent, 'second') await waitForIdle(ctx, agent) @@ -325,7 +325,7 @@ describe('request stability across the loop', () => { }) send(agent, 'first') await waitForIdle(ctx, agent) - ctx.agentModel.select(agent, { provider: 'other', model: 'other-model' }) + agent.model.select({ provider: 'other', model: 'other-model' }) send(agent, 'second') await waitForIdle(ctx, agent) @@ -341,7 +341,6 @@ describe('request stability across the loop', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt, { personaPrefix: 'stable base' }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -461,7 +460,6 @@ describe('request stability across the loop', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt, { personaPrefix: 'stable base' }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -939,7 +937,7 @@ describe('request/context capacity records', () => { send(agent, 'first') await waitForIdle(ctx, agent) - ctx.agentModel.select(agent, { provider: 'mock', model: 'large' }) + agent.model.select({ provider: 'mock', model: 'large' }) send(agent, 'second') await waitForIdle(ctx, agent) @@ -966,7 +964,7 @@ describe('request/context capacity records', () => { const agent = await ctx.agentLoop.create(SessionId('capacity-clear'), { provider: 'mock', model: 'known' }) send(agent, 'first') await waitForIdle(ctx, agent) - ctx.agentModel.select(agent, { provider: 'mock', model: 'unknown' }) + agent.model.select({ provider: 'mock', model: 'unknown' }) send(agent, 'second') await waitForIdle(ctx, agent) diff --git a/packages/core/agent-loop/tests/resume.spec.ts b/packages/core/agent-loop/tests/resume.spec.ts index e5bc0a7030..287593936e 100644 --- a/packages/core/agent-loop/tests/resume.spec.ts +++ b/packages/core/agent-loop/tests/resume.spec.ts @@ -15,7 +15,6 @@ import type { SessionHandle } from '@deepseek-ai/dsh-session-persistence' import * as JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { MockAdapter, textResponse } from './mock-adapter.ts' @@ -39,7 +38,6 @@ async function mountPersistentHarness(root: string, adapter: MockAdapter, compre await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -872,7 +870,6 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume', await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -1088,7 +1085,6 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume', await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -1346,7 +1342,6 @@ describe('configured-start failure edges', () => { await configured.plugin(LlmRuntime) await configured.plugin(SessionStore) await configured.plugin(SessionProjectionRegistry) - await configured.plugin(AgentModelPlugin) await configured.plugin(SystemPrompt) await configured.plugin(ToolRuntime) await configured.plugin(AgentRegistry) @@ -1381,7 +1376,6 @@ describe('configured-start failure edges', () => { await configured.plugin(LlmRuntime) await configured.plugin(SessionStore) await configured.plugin(SessionProjectionRegistry) - await configured.plugin(AgentModelPlugin) await configured.plugin(SystemPrompt) await configured.plugin(ToolRuntime) await configured.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/scope-lifecycle.spec.ts b/packages/core/agent-loop/tests/scope-lifecycle.spec.ts index 44973329d0..02d84f2fba 100644 --- a/packages/core/agent-loop/tests/scope-lifecycle.spec.ts +++ b/packages/core/agent-loop/tests/scope-lifecycle.spec.ts @@ -10,7 +10,6 @@ import AgentRegistry, { agentEvents, assembleContextFor } from '@deepseek-ai/dsh import type { Agent } from '@deepseek-ai/dsh-agent' import { scopeOf } from '@deepseek-ai/dsh-scope' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import type { ContentBlock } from '@deepseek-ai/dsh-llm' import { MockAdapter, textResponse } from './mock-adapter.ts' @@ -20,7 +19,6 @@ async function harnessWithLoop(adapter: MockAdapter = new MockAdapter([textRespo await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt, { personaPrefix: 'You are the deployment.' }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/settings.spec.ts b/packages/core/agent-loop/tests/settings.spec.ts index f151bae742..4a0fd1c154 100644 --- a/packages/core/agent-loop/tests/settings.spec.ts +++ b/packages/core/agent-loop/tests/settings.spec.ts @@ -10,14 +10,12 @@ import AgentRegistry from '@deepseek-ai/dsh-agent' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { liveConfig } from '../../../settings/settings/tests/live-config.ts' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' async function boot() { const ctx = new Context() await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/shutdown-drain.spec.ts b/packages/core/agent-loop/tests/shutdown-drain.spec.ts index 6a77843114..d89928395b 100644 --- a/packages/core/agent-loop/tests/shutdown-drain.spec.ts +++ b/packages/core/agent-loop/tests/shutdown-drain.spec.ts @@ -14,7 +14,6 @@ import ToolRuntime from '@deepseek-ai/dsh-tools' import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent' import * as JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import { MockAdapter, textResponse } from './mock-adapter.ts' const dirs: string[] = [] @@ -35,7 +34,6 @@ async function mount(order: 'backend-first' | 'loop-first'): Promise<{ ctx: Cont await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/system-prompt-admission.spec.ts b/packages/core/agent-loop/tests/system-prompt-admission.spec.ts index feadb196fc..364a72880b 100644 --- a/packages/core/agent-loop/tests/system-prompt-admission.spec.ts +++ b/packages/core/agent-loop/tests/system-prompt-admission.spec.ts @@ -2,8 +2,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' import AgentRegistry, { type Agent, type ModelSelection } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' -import * as ModelSwitchNotice from '@deepseek-ai/dsh-agent-model/notice' +import { installModelSelection } from '@deepseek-ai/dsh-agent' import LlmRuntime, { createUserMessage, LlmError, ReasoningEffortId, type GenerateOptions } from '@deepseek-ai/dsh-llm' import { toPiContext } from '@deepseek-ai/dsh-llm-pi-ai/src/context.ts' import SessionStore, { Session, SessionId } from '@deepseek-ai/dsh-session' @@ -29,7 +28,6 @@ async function harness(capable = new MockAdapter(Array.from({ length: 8 }, () => await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt, { personaPrefix: '', personaSuffix: '' }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -43,7 +41,7 @@ async function harness(capable = new MockAdapter(Array.from({ length: 8 }, () => ...await next(), sections: [{ name: 'test', text: prompt, order: 0 }], })) const agent = await ctx.agentLoop.create(SessionId('admission'), { provider: 'capable', model: 'model' }) - await agent.ctx.plugin(ModelSwitchNotice) + installModelSelection(agent.ctx) ctx.on('llm/stream', (request, next) => { // Rebuild from copied source events, not the live projection's cached state. const subject = ctx.agents.get(request.sessionId!)! @@ -185,7 +183,7 @@ describe('prepared-route prompt admission', () => { h.setPrompt('prompt three') await send(h.agent, 'third') expect(systemTexts(h.capable.requests[2]!)).toHaveLength(3) - h.ctx.agentModel.select(h.agent, { provider, model: 'model' }) + h.agent.model.select({ provider, model: 'model' }) h.setPrompt('') await send(h.agent, 'clear') const adapter = provider === 'capable' ? h.capable : h.plain @@ -289,7 +287,7 @@ describe('prepared-route prompt admission', () => { h.setPrompt('prompt two') await send(h.agent, 'second') expect(systemTexts(h.capable.requests[1]!)).toHaveLength(2) - h.ctx.agentModel.select(h.agent, { provider: 'plain', model: 'model' }) + h.agent.model.select({ provider: 'plain', model: 'model' }) if (changed) h.setPrompt('prompt three') await send(h.agent, 'third') expectPlain(h.plain.requests[0]!, changed ? 'prompt three' : 'prompt two') @@ -307,9 +305,9 @@ describe('prepared-route prompt admission', () => { it('appends on the first capable request after a plain route', async () => { const h = await harness() - h.ctx.agentModel.select(h.agent, { provider: 'plain', model: 'model' }) + h.agent.model.select({ provider: 'plain', model: 'model' }) await send(h.agent, 'first') - h.ctx.agentModel.select(h.agent, { provider: 'capable', model: 'model' }) + h.agent.model.select({ provider: 'capable', model: 'model' }) h.setPrompt('prompt two') await send(h.agent, 'second') expect(systemTexts(h.capable.requests[0]!)).toEqual([ @@ -369,7 +367,7 @@ describe('prepared-route prompt admission', () => { sessionId: SessionId('resumed'), agentOptions: { provider: 'plain', model: 'model' }, seed: [...seed], - setup: (_ctx, agent) => { h.ctx.agentModel.applyConfiguredRoute(agent, { provider: 'plain', model: 'model' }) }, + setup: (_ctx, agent) => { agent.model.select({ provider: 'plain', model: 'model' }) }, }) await send(resumed, 'resume') expectPlain(h.plain.requests[0]!, 'prompt two') @@ -415,7 +413,7 @@ describe('prepared-route prompt admission', () => { defaultEffort: ReasoningEffortId('low'), }) const h = await harness(adapter) - h.ctx.agentModel.select(h.agent, { + h.agent.model.select({ provider: 'capable', model: 'model', ...selected === undefined ? {} : { reasoningEffort: selected }, }) h.ctx.on('agent/request', async (_payload, next) => { @@ -429,7 +427,7 @@ describe('prepared-route prompt admission', () => { expect(events.filter(event => ['request/header', 'user/message', 'system/message'].includes(event.type))).toEqual([]) expect(events.at(-1)).toMatchObject({ type: 'turn/end', data: { reason: { kind: 'error', error: { - message: `agent "admission": agent/request proposed reasoning effort "${proposed ?? 'adapter default'}" after step preparation fixed "${selected ?? 'adapter default'}"; use ctx.agentModel.select() before step preparation`, + message: `agent "admission": agent/request proposed reasoning effort "${proposed ?? 'adapter default'}" after step preparation fixed "${selected ?? 'adapter default'}"; use agent.model.select() before step preparation`, } } }, }) }) @@ -441,7 +439,7 @@ describe('prepared-route prompt admission', () => { ], { efforts: [{ id: ReasoningEffortId('high'), name: 'High' }] }) const h = await harness(adapter) const selection = { provider: 'capable', model: 'model', reasoningEffort: ReasoningEffortId('high') } - h.ctx.agentModel.select(h.agent, selection) + h.agent.model.select(selection) const prepared: (Readonly | undefined)[] = [] h.ctx.on('system-prompt/assemble', (_assembly, context, next) => { prepared.push(context.callConfig) @@ -452,7 +450,7 @@ describe('prepared-route prompt admission', () => { return next() }) h.ctx.on('agent/request-error', () => { - h.ctx.agentModel.select(h.agent, { provider: 'plain', model: 'next-model' }) + h.agent.model.select({ provider: 'plain', model: 'next-model' }) return Promise.resolve({ kind: 'retry' as const }) }) @@ -461,7 +459,7 @@ describe('prepared-route prompt admission', () => { expect(adapter.requests.map(({ provider, model, reasoningEffort }) => ({ provider, model, reasoningEffort }))) .toEqual([selection, selection]) expect(h.plain.requests).toHaveLength(0) - expect(h.ctx.agentModel.resolveForNextStep(h.agent)) + expect(h.agent.model.current()) .toEqual({ provider: 'plain', model: 'next-model' }) await send(h.agent, 'use the pending choice') @@ -476,7 +474,7 @@ describe('prepared-route prompt admission', () => { h.setPrompt('prompt two') h.ctx.on('agent/request', async (_payload, next) => { const config = await next() - h.ctx.agentModel.select(h.agent, { provider: 'plain', model: 'model' }) + h.agent.model.select({ provider: 'plain', model: 'model' }) return { ...config, temperature: 0.5, maxTokens: 100 } }) await send(h.agent, 'second') diff --git a/packages/core/agent-loop/tests/tool-calls.spec.ts b/packages/core/agent-loop/tests/tool-calls.spec.ts index 9ee3f17e61..8aa74cec2d 100644 --- a/packages/core/agent-loop/tests/tool-calls.spec.ts +++ b/packages/core/agent-loop/tests/tool-calls.spec.ts @@ -13,7 +13,6 @@ import type { ContextFormed } from '@deepseek-ai/dsh-llm' import ToolRuntime, { defineContentToolFixture, TOOL_ABORTED_BEFORE_DISPATCH, TOOL_RUNTIME_SCHEDULER, type PostToolDecision, type PreToolDecision } from '@deepseek-ai/dsh-tools' import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { MockAdapter, textResponse } from './mock-adapter.ts' import { PtcRuntime } from '@deepseek-ai/dsh-ptc-runtime' @@ -30,7 +29,6 @@ async function harness(adapter: MockAdapter, maxParallelToolCalls?: number) { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt, { personaPrefix: '' }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -338,7 +336,6 @@ describe('tool-call scheduler: rolling pool honors maxParallelToolCalls', () => await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt, { personaPrefix: '' }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -899,7 +896,6 @@ describe('PTC mode native-tool denial through the agent loop', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt, { personaPrefix: '' }) await ctx.plugin(ToolRuntime, { mode: 'ptc' }) // eslint-disable-next-line @typescript-eslint/no-explicit-any -- FakePtcRuntime is an internal test helper with an opaque type shape diff --git a/packages/core/agent-loop/tests/tool-order.spec.ts b/packages/core/agent-loop/tests/tool-order.spec.ts index b2f856927f..7b7ca87558 100644 --- a/packages/core/agent-loop/tests/tool-order.spec.ts +++ b/packages/core/agent-loop/tests/tool-order.spec.ts @@ -17,7 +17,6 @@ import ToolRuntime, { defineContentToolFixture } from '@deepseek-ai/dsh-tools' import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { MockAdapter, textResponse } from './mock-adapter.ts' @@ -26,7 +25,6 @@ async function harness(adapter: MockAdapter, toolOrder?: SystemPromptConfig['too await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt, { personaPrefix: 'stable base', ...toolOrder !== undefined ? { toolOrder } : {} }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/tool-updates.spec.ts b/packages/core/agent-loop/tests/tool-updates.spec.ts index 581fc5433c..6eca806085 100644 --- a/packages/core/agent-loop/tests/tool-updates.spec.ts +++ b/packages/core/agent-loop/tests/tool-updates.spec.ts @@ -3,7 +3,6 @@ import { afterEach, describe, expect, it } from 'vitest' import { Context } from '@deepseek-ai/cordis' import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import LlmRuntime, { createUserMessage } from '@deepseek-ai/dsh-llm' import type { GenerateOptions } from '@deepseek-ai/dsh-llm' import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' @@ -23,7 +22,6 @@ async function harness(adapter: MockAdapter) { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt, { personaPrefix: '', personaSuffix: '' }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tsconfig.json b/packages/core/agent-loop/tsconfig.json index 1a055900a1..5c846680ee 100644 --- a/packages/core/agent-loop/tsconfig.json +++ b/packages/core/agent-loop/tsconfig.json @@ -40,9 +40,6 @@ }, { "path": "../../session/session-projection" - }, - { - "path": "../../core/agent-model" } ] } diff --git a/packages/core/agent-model/README.i18n.yaml b/packages/core/agent-model/README.i18n.yaml deleted file mode 100644 index 402dc95ec1..0000000000 --- a/packages/core/agent-model/README.i18n.yaml +++ /dev/null @@ -1,46 +0,0 @@ -# Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading -# section, a hash of its English and Chinese blocks outside code blocks and generated regions. -# After editing either side, bring the other along and re-record with: -# pnpm run verify-translation-pairing --write packages/core/agent-model/README.md -/: - en: 47d6f306e5ae4f55 - zh: 022ec46c0e23194b -/deepseek-ai-dsh-agent-model: - en: 2b4c8fc77ac2b04e - zh: dd7d344a92a29cdd -/deepseek-ai-dsh-agent-model/summary: - en: 21423e931b7ebf09 - zh: f357ade62c85e9b5 -/deepseek-ai-dsh-agent-model/table-of-contents: - en: d152484eb41ac6b4 - zh: 725b47765ee8f30b -/deepseek-ai-dsh-agent-model/use-this-package: - en: f2f4388fbe782649 - zh: de4cbde4a0195353 -/deepseek-ai-dsh-agent-model/understand-the-implementation: - en: 18d882c8cc2f1ba3 - zh: bdd55c122890ee53 -/deepseek-ai-dsh-agent-model/further-exploration: - en: c519faa401a47398 - zh: 20a6e428162dcf76 -/deepseek-ai-dsh-agent-model/model-experience: - en: 215e7ba838619b7b - zh: a311da3843709f90 -/deepseek-ai-dsh-agent-model/model-experience/optional-route-switch-notice: - en: 6c60206a2618ff38 - zh: b193eed786c567ba -/deepseek-ai-dsh-agent-model/model-experience/optional-route-switch-notice/what-the-model-sees: - en: e58a8d2ac2f2c6b5 - zh: 63400baf224b841e -/deepseek-ai-dsh-agent-model/model-experience/optional-route-switch-notice/token-effect: - en: 878389d2d3feb863 - zh: 2c59e93f7f46af57 -/deepseek-ai-dsh-agent-model/model-experience/optional-route-switch-notice/kv-cache-effect: - en: 4ae01fff11ba7d69 - zh: 1c94b66a848d594e -/deepseek-ai-dsh-agent-model/known-limitations-and-deferred-work: - en: 4d18cc7cc2c58a63 - zh: d36f8f799e6e45e2 -/deepseek-ai-dsh-agent-model/known-limitations-and-deferred-work/dev-note: - en: e67f633dc6fe7773 - zh: e88aeb617ffb74ea diff --git a/packages/core/agent-model/README.md b/packages/core/agent-model/README.md deleted file mode 100644 index a2b08a05b7..0000000000 --- a/packages/core/agent-model/README.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -description: "Choose the model for a Session's next step, retain that choice across resume, and optionally announce route changes to the model." -kind: "package-reference" ---- - -# @deepseek-ai/dsh-agent-model - -English | [中文](README.zh.md) - -## Summary - -Choose a provider, model, and optional reasoning effort for a Session's next model step. Recorded choices survive resume, while a step already being prepared keeps its captured choice through retries. Empty Sessions can continue following their Agent's live defaults. An optional scoped plugin tells the model when a different route continues its conversation. - -## Table of Contents - -- [Use this package](#use-this-package) -- [Understand the implementation](#understand-the-implementation) -- [Further Exploration](#further-exploration) -- [Model Experience](#model-experience) -- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) -- [Dev Note](#dev-note) - ------ - - -## Use this package - -The base and minimal SDK bundles mount the selection provider. Custom compositions mount it alongside the Session projection registry; consumers inject `agentModel`. - -```yaml -- name: '@deepseek-ai/dsh-session' -- name: '@deepseek-ai/dsh-session-projection' -- name: '@deepseek-ai/dsh-agent-model' -``` - -The provider has no configuration fields. `ctx.agentModel.resolveForNextStep(agent)` reads the pending selection, then the latest request's selection, then `agent.options`; it returns a detached value or `undefined` when none supplies a complete route. Reading neither starts a step nor pins a choice. A Session without a selection or request continues reading its Agent's live fallback options. - -`select(agent, selection)` records the complete choice unless an equal choice is already recorded. An explicit choice equal to the options fallback is still recorded. Omitted reasoning effort follows adapter defaults; this operation does not materialize defaults, check catalog membership, or save the deployment default. Validate user choices with `llm` before recording them. Session append failures retain the Session's existing failure guarantees. - -Ordinary resume preserves recorded selection. An entry point that explicitly overrides it calls `applyConfiguredRoute(agent, selection)` during unpublished Agent setup. It requires a complete configured route and inherits an existing explicit effort only when the route is unchanged and the declaration omits effort. Use `select()` without effort to explicitly return to adapter defaults. - -To enable switch notices for one Agent, import `* as ModelSwitchNotice` from `@deepseek-ai/dsh-agent-model/notice` and await `agentCtx.plugin(ModelSwitchNotice)` inside that Agent's setup callback. The owning scope removes its listener on disposal. Other Agents receive no notice from that registration. - ------ - - -## Understand the implementation - -
-Implementation internals — click to expand - -The provider awaits registration of one pure fold before publishing its Service and retains the returned reader through consumer teardown. The Session log is authoritative; the projection registry owns fold caches, and the selection service holds no second per-Agent choice. A request header clears only its matching pending selection and omits adapter-defaulted effort from the retained choice. The loop captures a detached choice before assembly and owns that step snapshot through retries. - -- [`src/service.ts`](src/service.ts) owns selection reads, replacement, and configured-route application. -- [`src/projection.ts`](src/projection.ts) owns the durable fold and client view; [`src/types.ts`](src/types.ts) exports the fold state and client view types. -- [`src/index.ts`](src/index.ts) owns projection registration and service lifetime. -- [`src/notice.ts`](src/notice.ts) compares the prepared step route with the previous request header without reading a newer pending choice. - -
- ------ - - -## Further Exploration - -- [Agent package](../agent/README.md) — Agent handles, unpublished setup, and event vocabulary. -- [Agent-loop package](../agent-loop/README.md) — step preparation and request retries. -- [Default-model package](../agent-default-model/README.md) — live deployment defaults and profile persistence. -- [Core subsystem](../../../docs/subsystems/core.md) — selection types and generated service API. - ------ - - -## Model Experience - -### Optional route-switch notice - -#### What the model sees - -The scoped notice plugin appends `[model changed: assistant turns above this point were generated by ; the session continues with ]` to an accepted request step whose prepared route differs from the previous request. Labels include providers only for a cross-provider switch. Initial requests, effort-only changes, rejected steps, and empty no-request decisions add nothing. A failure before header persistence leaves the previous route unchanged, so the next request step receives the notice again. - -#### Token effect - -Each admitted notice adds its text to retained user-message history. Selection events themselves add no prompt text. - -#### KV Cache effect - -Notices append to history without replacing earlier messages. Switching the requested model may prevent reuse of the previous model's cache; cache availability remains provider-owned. - -## Known Limitations and Deferred Work - - - -- **Availability is caller-owned** — recording a choice does not prove an adapter is available. Entry points validate user choices, and request preparation resolves the actual adapter and defaults. -- **Deployment defaults are separate** — changing a Session does not save the default for other Sessions; that policy belongs to the entry point and default-model service. - - -### Dev Note - -
-Working context for maintainers — click to expand - -None. - -
diff --git a/packages/core/agent-model/README.zh.md b/packages/core/agent-model/README.zh.md deleted file mode 100644 index 2f282a00f3..0000000000 --- a/packages/core/agent-model/README.zh.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -description: "选择 Session 下一步骤的模型,在恢复后保留该选择,并按需向模型提示路由变化。" -kind: "package-reference" ---- - -# @deepseek-ai/dsh-agent-model - -[English](README.md) | 中文 - -## 概述 - -为 Session 的下一个模型步骤选择提供方、模型与可选的推理强度。记录的选择在恢复后保留,已经开始准备的步骤则在重试期间继续使用捕获的选择。空白 Session 可以继续跟随 Agent 的实时默认值。可选的作用域插件会在另一条路由继续对话时向模型添加提示。 - -## 目录 - -- [使用本包](#use-this-package) -- [理解实现](#understand-the-implementation) -- [进一步探索](#further-exploration) -- [模型体验](#model-experience) -- [已知限制与延后工作](#known-limitations-and-deferred-work) -- [开发备注](#dev-note) - ------ - - -## 使用本包 - -基础 bundle 与最小 SDK bundle 会挂载选择提供方。自定义组合将其与 Session 投影注册表一起挂载;消费方注入 `agentModel`。 - -```yaml -- name: '@deepseek-ai/dsh-session' -- name: '@deepseek-ai/dsh-session-projection' -- name: '@deepseek-ai/dsh-agent-model' -``` - -提供方没有配置字段。`ctx.agentModel.resolveForNextStep(agent)` 依次读取待用选择、最近请求的选择和 `agent.options`;返回独立的值,各来源均未提供完整路由时返回 `undefined`。读取不会启动步骤或固定选择。没有选择或请求记录的 Session 会继续读取 Agent 的实时回退选项。 - -`select(agent, selection)` 记录完整选择,已有相同选择时不重复记录。与 options 回退值相同的显式选择仍会记录。省略推理强度表示跟随适配器默认值;此操作不会补齐默认值、检查模型目录或保存部署默认值。记录用户选择前先使用 `llm` 校验。Session 追加失败沿用 Session 已有的失败保证。 - -普通恢复保留记录中的选择。入口若明确覆盖它,在未发布的 Agent setup 中调用 `applyConfiguredRoute(agent, selection)`。此方法要求完整的配置路由,仅在路由不变且声明省略强度时继承现有显式强度。要明确恢复为适配器默认强度,调用不带强度的 `select()`。 - -要为一个 Agent 启用切换提示,从 `@deepseek-ai/dsh-agent-model/notice` 导入 `* as ModelSwitchNotice`,并在该 Agent 的 setup 回调中等待 `agentCtx.plugin(ModelSwitchNotice)`。所属作用域在释放时移除监听器。此注册不会向其他 Agent 添加提示。 - ------ - - -## 理解实现 - -
-实现内部机制——点击展开 - -提供方等待一个纯折叠注册完成后再发布 Service,并在消费方清理期间保留返回的读取句柄。Session 日志是权威记录;投影注册表拥有折叠缓存,选择服务不再保存另一份每个 Agent 的选择。请求头仅清除与其匹配的待用选择,并从保留的选择中省略由适配器默认补齐的强度。循环在组装前捕获独立的选择,并在重试期间持有该步骤快照。 - -- [`src/service.ts`](src/service.ts) 负责选择读取、替换与配置路由应用。 -- [`src/projection.ts`](src/projection.ts) 负责持久折叠和客户端视图;[`src/types.ts`](src/types.ts) 导出折叠状态与客户端视图类型。 -- [`src/index.ts`](src/index.ts) 负责投影注册与服务生命周期。 -- [`src/notice.ts`](src/notice.ts) 比较已准备步骤的路由与上次请求头,不读取更新的待用选择。 - -
- ------ - - -## 进一步探索 - -- [Agent 包](../agent/README.zh.md)——Agent 句柄、未发布 setup 与事件词汇。 -- [Agent-loop 包](../agent-loop/README.zh.md)——步骤准备和请求重试。 -- [默认模型包](../agent-default-model/README.zh.md)——实时部署默认值与 profile 持久化。 -- [核心子系统](../../../docs/subsystems/core.zh.md)——选择类型与生成的服务 API。 - ------ - - -## 模型体验 - -### 可选的路由切换提示 - -#### 模型看到什么 - -作用域提示插件会在已接纳请求步骤的准备路由不同于上次请求时,追加 `[model changed: assistant turns above this point were generated by ; the session continues with ]`。仅跨提供方切换时在标签中包含提供方。初始请求、只改变强度、被拒绝的步骤和不发出请求的空决策均不添加提示。请求头持久化前失败会保留先前路由,所以下一个请求步骤会再次收到提示。 - -#### Token 影响 - -每条被接纳的提示都将文本加入保留的用户消息历史。选择事件本身不添加提示词文本。 - -#### KV Cache 影响 - -提示追加到历史,不替换先前消息。切换请求模型可能使先前模型的缓存无法复用;缓存是否可用仍由提供方决定。 - -## 已知限制与延后工作 - - - -- **可用性由调用方负责**——记录选择不代表适配器可用。入口校验用户选择,请求准备解析实际适配器与默认值。 -- **部署默认值独立管理**——改变一个 Session 不会为其他 Session 保存默认值;该策略属于入口与默认模型服务。 - - -### 开发备注 - -
-维护者工作上下文——点击展开 - -无。 - -
diff --git a/packages/core/agent-model/package.json b/packages/core/agent-model/package.json deleted file mode 100644 index dbff81d1f0..0000000000 --- a/packages/core/agent-model/package.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "@deepseek-ai/dsh-agent-model", - "description": "Session model selection and optional model-switch notices", - "version": "0.2.1-rc.2", - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/deepseek-ai/deepseek-harness.git", - "directory": "packages/core/agent-model" - }, - "type": "module", - "main": "lib/index.js", - "types": "lib/types/index.d.ts", - "exports": { - ".": { - "types": "./lib/types/index.d.ts", - "default": "./lib/index.js" - }, - "./types": { - "types": "./lib/types/types.d.ts", - "default": "./lib/types/types.js" - }, - "./notice": { - "types": "./lib/types/notice.d.ts", - "default": "./lib/types/notice.js" - }, - "./src/*": "./src/*", - "./package.json": "./package.json" - }, - "files": [ - "lib/index.js", - "lib/types/**/*.js", - "lib/types/**/*.d.ts" - ], - "license": "MIT", - "dependencies": { - "zod": "4.4.3" - }, - "peerDependencies": { - "@deepseek-ai/dsh-agent": "workspace:*", - "@deepseek-ai/dsh-llm": "workspace:*", - "@deepseek-ai/dsh-session": "workspace:*", - "@deepseek-ai/dsh-session-projection": "workspace:*", - "@deepseek-ai/cordis": "workspace:~" - }, - "devDependencies": { - "@deepseek-ai/dsh-agent": "workspace:*", - "@deepseek-ai/dsh-llm": "workspace:*", - "@deepseek-ai/dsh-session": "workspace:*", - "@deepseek-ai/dsh-session-projection": "workspace:*", - "@deepseek-ai/cordis": "workspace:~" - } -} diff --git a/packages/core/agent-model/src/index.ts b/packages/core/agent-model/src/index.ts deleted file mode 100644 index 3cfc653d35..0000000000 --- a/packages/core/agent-model/src/index.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Session model-selection provider: owns the projection and publishes its - * synchronous operations after registration, retaining the fold through - * dependent service teardown. - * @module @deepseek-ai/dsh-agent-model - */ -import { FiberState, type Context, type Fiber } from '@deepseek-ai/cordis' -import type { ProjectionHandle } from '@deepseek-ai/dsh-session-projection' -import { AgentModel } from './service.ts' -import { modelSelectionProjectionDefinition } from './projection.ts' -import type { ModelSelectionProjectionState } from './types.ts' - -export { AgentModel } from './service.ts' -export type { ModelSelectionProjection } from './types.ts' - -/** Session model-selection provider label. */ -export const name = 'agent-model' -/** The provider owns a fold in the Session projection registry. */ -export const inject = ['sessionProjections'] - -/** - * Await fold registration before publishing the selection service. Retain its - * reader until dependent consumers finish cleanup. - * @param ctx - provider scope owning the projection and service child. - * @returns after the selection service has activated. - */ -export async function apply(ctx: Context): Promise { - const projection = await ctx.sessionProjections.register(modelSelectionProjectionDefinition) - const runtime: { projection: ProjectionHandle | undefined } = { projection } - let service: Fiber | undefined - const draining = new Set() - ctx.effect(function* () { - // Cordis removes an independently disposing consumer before service withdrawal - // can find it. Track it until it leaves UNLOADING. `ctx.plugin()` returns a - // wrapper around the fiber, so the shared `dispose` identifies it. - yield ctx.on('internal/status', (fiber) => { - if (fiber.state !== FiberState.UNLOADING) draining.delete(fiber) - else if (service !== undefined && fiber.store?.agentModel?.fiber.dispose === service.dispose) draining.add(fiber) - }) - yield () => { - runtime.projection = undefined - return projection.dispose() - } - yield async () => { - while (draining.size > 0) await Promise.allSettled([...draining].map(fiber => fiber.await())) - } - service = ctx.plugin(AgentModel, runtime) - yield service.dispose - }, 'agentModel.lifecycle()') - // The synchronous effect assigns the child before returning. - await (service as Fiber).await() -} diff --git a/packages/core/agent-model/src/notice.ts b/packages/core/agent-model/src/notice.ts deleted file mode 100644 index 6779f46e00..0000000000 --- a/packages/core/agent-model/src/notice.ts +++ /dev/null @@ -1,60 +0,0 @@ -/** Optional model-switch notices scoped to one Agent. - * @module @deepseek-ai/dsh-agent-model/notice - */ -import type { Context } from '@deepseek-ai/cordis' -import type { PreStepDecision } from '@deepseek-ai/dsh-agent' -import type { ModelSelection } from '@deepseek-ai/dsh-agent/types' -import { boundContextSummary, createUserMessage, type ContextFormed } from '@deepseek-ai/dsh-llm' -import { sameRoute } from './selection.ts' - -declare module '@deepseek-ai/dsh-llm' { - interface MessageSourceMap { - 'model-selection': { kind: 'model-selection' } & ContextFormed - } -} - -/** Scoped model-switch notice plugin label. */ -export const name = 'agent-model-notice' - -function routeLabel(route: ModelSelection, other: ModelSelection): string { - return route.provider === other.provider ? route.model : `${route.provider}/${route.model}` -} - -function modelSwitchNotice(previous: ModelSelection, selected: ModelSelection) { - const from = routeLabel(previous, selected) - const to = routeLabel(selected, previous) - return createUserMessage({ - content: [{ - type: 'text', - text: `[model changed: assistant turns above this point were generated by ${from}; the session continues with ${to}]`, - }], - source: { - kind: 'model-selection', - form: 'notice', - summary: boundContextSummary(`${from} → ${to}`), - }, - }) -} - -/** - * Append a model-switch notice when the step's fixed route differs from the - * last request header. Effort-only changes and empty no-request decisions add - * no notice; a failure before header persistence repeats it on the next step. - * Mount this plugin in the selected Agent's unpublished setup scope. - * @param ctx - The selected Agent's scoped context. - */ -export function apply(ctx: Context): void { - ctx.on( - 'agent/pre-step', - async ({ agent, messages, signal, step, callConfig }, next): Promise => { - const decision = await next() - if (decision.kind === 'reject' || signal.aborted) return decision - // The loop skips an empty first step and an emptied offered continuation. - if (decision.messages.length === 0 && (step === 1 || messages.length > 0)) return decision - const previous = agent.session.requestHeader()?.config - if (previous === undefined || sameRoute(callConfig, previous)) return decision - return { ...decision, messages: [...decision.messages, modelSwitchNotice(previous, callConfig)] } - }, - { prepend: true }, - ) -} diff --git a/packages/core/agent-model/src/projection.ts b/packages/core/agent-model/src/projection.ts deleted file mode 100644 index 6ad16afe66..0000000000 --- a/packages/core/agent-model/src/projection.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** Durable model-selection fold and its client projection. */ -import type { ModelSelection } from '@deepseek-ai/dsh-agent/types' -import { ReasoningEffortId } from '@deepseek-ai/dsh-llm/brand' -import type { EpochHeader } from '@deepseek-ai/dsh-session' -import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection' -import { z } from 'zod' -import type { ModelSelectionProjectionState } from './types.ts' -import { sameModelSelection, toSelection } from './selection.ts' - -/** Selection a recorded request used, omitting an effort the adapter defaulted. */ -function selectionOfHeader({ config, adapterDefaults }: EpochHeader): ModelSelection { - return toSelection(config.provider, config.model, adapterDefaults?.reasoningEffort === true ? undefined : config.reasoningEffort) -} - -const selectionSchema = z.object({ - provider: z.string().min(1), - model: z.string().min(1), - reasoningEffort: z.string().min(1).transform(ReasoningEffortId).exactOptional(), -}).nullable() - -/** - * Session selection fold: recorded selections become - * the pending choice, and each request header retires a matching one. - */ -export const modelSelectionProjectionDefinition: Required> = { - key: 'modelSelection', - stateVersion: 3, - stateSchema: z.object({ lastUsed: selectionSchema, pending: selectionSchema }), - init: () => ({ lastUsed: null, pending: null }), - apply: (state, event) => { - if (event.type === 'model/selection') { - return sameModelSelection(state.pending, event.data) ? state : { ...state, pending: { ...event.data } } - } - if (event.type !== 'request/header') return state - const lastUsed = selectionOfHeader(event.data.header) - const pending = sameModelSelection(state.pending, lastUsed) ? null : state.pending - return sameModelSelection(state.lastUsed, lastUsed) && pending === state.pending ? state : { lastUsed, pending } - }, - wire: { - viewSchema: z.object({ lastUsed: selectionSchema, next: selectionSchema }), - view: state => ({ lastUsed: state.lastUsed, next: state.pending ?? state.lastUsed }), - }, -} diff --git a/packages/core/agent-model/src/selection.ts b/packages/core/agent-model/src/selection.ts deleted file mode 100644 index 782aa5a7d2..0000000000 --- a/packages/core/agent-model/src/selection.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** Pure comparisons and snapshots shared by the selection service and notice plugin. */ -import type { ModelSelection } from '@deepseek-ai/dsh-agent/types' -import type { ReasoningEffortId } from '@deepseek-ai/dsh-llm/brand' - -/** - * Build a detached selection, omitting an absent effort. - * @param provider - provider route. - * @param model - provider-owned model id. - * @param reasoningEffort - explicit effort; omission follows adapter defaults. - * @returns a detached selection. - */ -export function toSelection(provider: string, model: string, reasoningEffort: ReasoningEffortId | undefined): ModelSelection { - return { provider, model, ...reasoningEffort === undefined ? {} : { reasoningEffort } } -} - -/** - * Compare provider and model without comparing reasoning effort. - * @param left - first selection. - * @param right - second selection. - * @returns whether both selections name the same route. - */ -export function sameRoute(left: ModelSelection, right: ModelSelection): boolean { - return left.provider === right.provider && left.model === right.model -} - -/** - * Compare complete selections, treating absent effort as its own value. - * @param left - first selection, or no recorded selection. - * @param right - second selection, or no recorded selection. - * @returns whether both selections agree. - */ -export function sameModelSelection(left: ModelSelection | null, right: ModelSelection | null): boolean { - return left === right || (left !== null && right !== null - && sameRoute(left, right) && left.reasoningEffort === right.reasoningEffort) -} diff --git a/packages/core/agent-model/src/service.ts b/packages/core/agent-model/src/service.ts deleted file mode 100644 index 257f97cf98..0000000000 --- a/packages/core/agent-model/src/service.ts +++ /dev/null @@ -1,77 +0,0 @@ -/** Session selection operations; the provider owns projection registration. */ -import { Service, type Context } from '@deepseek-ai/cordis' -import type { Agent, ModelSelection } from '@deepseek-ai/dsh-agent/types' -import type { ProjectionHandle } from '@deepseek-ai/dsh-session-projection' -import type { ModelSelectionProjectionState } from './types.ts' -import { sameModelSelection, sameRoute, toSelection } from './selection.ts' - -declare module '@deepseek-ai/cordis' { - interface Context { - /** Session model selection for steps that have not begun preparation. */ - agentModel: AgentModel - } -} - -/** Read and record Session model choices without resolving adapter defaults. */ -export class AgentModel extends Service { - static inject = ['sessionProjections'] - - /** - * Provider-bound projection reader, usable while dependent callers drain. The - * plain holder keeps Cordis from re-tracing it through each caller's context. - */ - private readonly runtime: { projection: ProjectionHandle | undefined } - - constructor(ctx: Context, runtime: { projection: ProjectionHandle | undefined }) { - super(ctx, 'agentModel') - this.runtime = runtime - } - - /** - * Resolve the choice for the next step to begin preparation. Recorded choices - * take precedence over the last request and then the Agent's live options. - * This read neither starts a step nor changes an already-prepared step. - * @param agent - Agent whose Session and fallback options are read. - * @returns a detached choice, or undefined when no source names both provider and model. - */ - resolveForNextStep(agent: Agent): Readonly | undefined { - const recorded = this.recordedSelection(agent) - if (recorded !== null) return { ...recorded } - const { provider, model, reasoningEffort } = agent.options - return provider && model ? toSelection(provider, model, reasoningEffort) : undefined - } - - /** - * Replace the next step's complete selection. Omitted effort follows adapter - * defaults. An equal recorded choice is a no-op; an options-only fallback is - * always recorded. Callers own model availability and capability validation. - * The append uses the Session's existing persistence and failure guarantees. - * @param agent - Agent whose Session receives the model/selection event. - * @param selection - complete provider, model, and optional explicit effort. - */ - select(agent: Agent, selection: Readonly): void { - if (sameModelSelection(this.recordedSelection(agent), selection)) return - agent.session.append('model/selection', toSelection(selection.provider, selection.model, selection.reasoningEffort)) - } - - /** - * Apply an explicit deployment route, including on Session resume. An omitted - * effort inherits the current explicit effort only for the same route; - * otherwise it follows adapter defaults. Call only when the entry point - * explicitly overrides recorded selection, not for ordinary resume fallback. - * @param agent - Agent whose Session receives the configured choice. - * @param selection - complete configured provider/model and optional effort. - */ - applyConfiguredRoute(agent: Agent, selection: Readonly): void { - const current = this.resolveForNextStep(agent) - const inherited = current !== undefined && sameRoute(current, selection) ? current.reasoningEffort : undefined - this.select(agent, toSelection(selection.provider, selection.model, selection.reasoningEffort ?? inherited)) - } - - private recordedSelection(agent: Agent): ModelSelection | null { - const projection = this.runtime.projection - if (projection === undefined) throw new Error('agent-model: used after its provider unloaded the modelSelection projection') - const state = projection.stateOf(agent.session) - return state.pending ?? state.lastUsed - } -} diff --git a/packages/core/agent-model/src/types.ts b/packages/core/agent-model/src/types.ts deleted file mode 100644 index fc284c0a7c..0000000000 --- a/packages/core/agent-model/src/types.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** Client view and internal fold types for Session model selection. - * @module @deepseek-ai/dsh-agent-model/types - */ -import type { ModelSelection } from '@deepseek-ai/dsh-agent/types' - -/** - * Durable model-selection fold for one Session. An effort the adapter - * defaulted on a recorded request is omitted, so later requests resolve the - * default again instead of pinning it as a choice. - */ -export interface ModelSelectionProjectionState { - /** Selection used by the latest recorded model request. */ - readonly lastUsed: ModelSelection | null - /** Later recorded selection not yet used by a matching model request. */ - readonly pending: ModelSelection | null -} - -/** Client view of the durable model-selection fold. */ -export interface ModelSelectionProjection { - /** Selection used by the latest recorded model request. */ - readonly lastUsed: ModelSelection | null - /** Selection the next model step uses; null while the Agent's initial options supply the route. */ - readonly next: ModelSelection | null -} - -declare module '@deepseek-ai/dsh-session-projection/types' { - interface SessionProjectionMap { - /** Recorded model selection for the next model step and the latest request. */ - modelSelection: ModelSelectionProjection - } -} diff --git a/packages/core/agent-model/tsconfig.json b/packages/core/agent-model/tsconfig.json deleted file mode 100644 index 3227fe9b95..0000000000 --- a/packages/core/agent-model/tsconfig.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "lib/types" - }, - "include": [ - "src" - ], - "references": [ - { - "path": "../../../vendor/cosmokit" - }, - { - "path": "../../../vendor/cordis" - }, - { - "path": "../agent" - }, - { - "path": "../../llm/llm" - }, - { - "path": "../session" - }, - { - "path": "../../session/session-projection" - } - ] -} diff --git a/packages/core/agent/README.i18n.yaml b/packages/core/agent/README.i18n.yaml index bcc102587f..68af4513b0 100644 --- a/packages/core/agent/README.i18n.yaml +++ b/packages/core/agent/README.i18n.yaml @@ -15,11 +15,14 @@ en: d152484eb41ac6b4 zh: 09388d293f9be9cb /deepseek-ai-dsh-agent/use-this-package: - en: b6c8c72ca3596169 - zh: 2e6e81c529f7803a + en: 7d1ae2375bd4fbfc + zh: b6b4b2e943174cb2 /deepseek-ai-dsh-agent/use-this-package/create-or-resume-an-agent: - en: cb45db3b7202461a - zh: a002e1b07e12172e + en: 65b145c363fa6017 + zh: 9c8d830772ad0113 +/deepseek-ai-dsh-agent/use-this-package/select-a-model: + en: 85b0ff72a187110f + zh: 3f61c1568d27b8bc /deepseek-ai-dsh-agent/use-this-package/drive-an-agent-s-conversation: en: 71e258897e2e7d73 zh: abf0991012faa17e @@ -41,9 +44,12 @@ /deepseek-ai-dsh-agent/understand-the-implementation/durable-inbox: en: 8f3fe9303468d735 zh: 52162010ae1ed29e +/deepseek-ai-dsh-agent/understand-the-implementation/durable-model-selection: + en: 692ec951bbb16b33 + zh: 5a650805ae8943c3 /deepseek-ai-dsh-agent/understand-the-implementation/source-map: - en: 6c4b27402462be8c - zh: 466730834fa82810 + en: bdeb146a53d8eee6 + zh: 38024efe70a2776f /deepseek-ai-dsh-agent/understand-the-implementation/registry-and-lifecycle: en: df92e97cedd0d02e zh: f6040c145bffd933 @@ -63,8 +69,8 @@ en: 962c598672b0eed6 zh: d05b3bc936685ad3 /deepseek-ai-dsh-agent/model-experience/user-steering-and-injected-messages/what-the-model-sees: - en: 1da09e174588443f - zh: f6168d519339ac7b + en: 2383fe2ec2859ac5 + zh: 4211b6083cf58ecb /deepseek-ai-dsh-agent/model-experience/user-steering-and-injected-messages/token-effect: en: 95ed6c912ead0fba zh: d5ad3d7257f7836c @@ -78,11 +84,23 @@ en: 09a31923f42fcadf zh: c52117c2afbca7c6 /deepseek-ai-dsh-agent/model-experience/agent-scoped-request-composition/token-effect: - en: dfebf42b629656d9 + en: b25f4a4bb14fdfce zh: 9369ace52f58a579 /deepseek-ai-dsh-agent/model-experience/agent-scoped-request-composition/kv-cache-effect: - en: 07a408b32e49ecaf - zh: 9684b80cb3c0e75c + en: 0d6f538ede133159 + zh: 4f32f218e06fa432 +/deepseek-ai-dsh-agent/model-experience/optional-model-switch-notice: + en: 43e32660a38f9ea1 + zh: 334d24a453b14322 +/deepseek-ai-dsh-agent/model-experience/optional-model-switch-notice/what-the-model-sees: + en: d7de2f137ff3b9b6 + zh: 53e9098d97832b70 +/deepseek-ai-dsh-agent/model-experience/optional-model-switch-notice/token-effect: + en: e38815dd2b8cf92e + zh: ec449b3b5ce10bf1 +/deepseek-ai-dsh-agent/model-experience/optional-model-switch-notice/kv-cache-effect: + en: 2aba63100fcafc58 + zh: 7484f7981d8cc955 /deepseek-ai-dsh-agent/known-limitations-and-deferred-work: en: 00a2d12980ffe756 zh: c8bf69d06d165b36 diff --git a/packages/core/agent/README.md b/packages/core/agent/README.md index 0c643901d5..c2f9fcbf0b 100644 --- a/packages/core/agent/README.md +++ b/packages/core/agent/README.md @@ -25,7 +25,7 @@ Use `dsh-agent` to create or resume live agents, send follow-up or steering inpu ## Use this package -Mount `dsh-agent` with `dsh-session-projection`: it provides `ctx.agents`, the `Agent` handle, and `turnBoundaryOf(session)` for recorded turn and step positions. Creating or resuming an Agent also requires a driver to register a factory; the shipped driver is `dsh-agent-loop`. [Session model selection](../agent-model/README.md) is provided separately. +Mount `dsh-agent` with `dsh-session-projection`: it provides `ctx.agents`, the `Agent` handle, and `turnBoundaryOf(session)` for recorded turn and step positions. Creating or resuming an Agent also requires a driver to register a factory; the shipped driver is `dsh-agent-loop`. [Session model selection](#model-selection) is available on each handle as `agent.model`. ### Create or resume an agent @@ -42,7 +42,16 @@ await handle.dispose() // stops the loop, unregisters, removes the session, un `AgentOptions` supplies the initial provider/model route, optional adapter-owned `reasoningEffort`, and optional positive `maxTokens` output cap. The loop validates exact-model reasoning support, resolves adapter defaults, records the effective values in the request header, and applies them to each conversation request. An optional `setup(agentCtx, agent)` callback composes the agent's scoped world before it is published: `agentCtx` owns registrations, while the explicit unpublished Agent provides its Session; the Context has no reverse Agent property. Scoped tools, prompt sections, and listeners exist before any creation announcement. Setup is composition-only: drive the agent only after creation resolves. -The [model-selection service](../agent-model/README.md) resolves recorded choices before the Agent options fallback and owns explicit selection changes and configured resume overrides. + +### Select a model + +`agent.model` implements `Model`, alongside `agent.inbox`. `current()` returns a detached selection: the pending choice, then the latest request's choice, then live `agent.options`; it returns `undefined` when no source supplies both provider and model. Reading does not pin a choice or change a prepared step. + +`select({ provider, model, reasoningEffort? })` assigns the complete choice for the next step whose preparation has not begun. Omitted effort uses adapter defaults; selection neither inherits old effort nor queries defaults. An equal recorded choice is a no-op, but a choice equal only to initial options is recorded as `model/selection`. Callers validate availability and capabilities; append failures retain the Session's existing guarantees. + +Ordinary resume keeps the recorded choice. An entry point explicitly overriding the model first builds a complete selection, retaining the previous explicit effort only when the configured route is unchanged and omits effort, then calls `select()`. Web and ACP user selections call `select()` directly, so omitted effort means Auto. Selecting a Session model does not change deployment defaults. + +For scoped switch notices, call `installModelSelection(agentCtx)` from `dsh-agent` during Agent setup. The scope owns the listener and removes it on disposal. ### Drive an agent's conversation @@ -94,6 +103,10 @@ The package is built on one separation: the public `Agent` surface and registry `Inbox` exposes pending `nextTurn` and `nextStep` messages and mutates them through `append`, `prepend`, `replace`, `remove`, `clear`, and `splice`. Ordinary removals and `clear()` are durable cancellations. At a step boundary, the loop's internal implementation claims pending input through pure deletion splices. Live notifications are deliberately per-message and minimal: `agent/inbox/inserted { message }`, `agent/inbox/claimed { message, turn }`, and `agent/inbox/discarded { message }`. +### Durable model selection + +`Model` and the `modelSelection` projection types live here. The default driver creates each `AgentModel` and owns projection registration alongside its inbox. Each instance binds the Session, live options, and a projection reader; it holds no second copy of the choice. The loop captures a detached selection before step preparation and retains it across retries. + ### Source map | File | Role | @@ -102,6 +115,7 @@ The package is built on one separation: the public `Agent` surface and registry | [`src/runtime-types.ts`](src/runtime-types.ts) | `Agent`, structural `Inbox`, `AgentStatus`, and the `agent/*` event declarations | | [`src/types.ts`](src/types.ts) | `AgentOptions`, cancellation causes, and inbox projection vocabulary | | [`src/dispatch.ts`](src/dispatch.ts) | `agentEvents` fused dispatcher and `assembleContextFor(agent)` | +| [`src/model-selection.ts`](src/model-selection.ts) | `AgentModel`, its durable projection, and `installModelSelection` switch notices | | [`src/consumed-work.ts`](src/consumed-work.ts) | `foldConsumedWork(events)`: what the log's consumed work became | | [`src/archive-admission.ts`](src/archive-admission.ts) | The `turn` family of the Workspace registry's archive admission: a running turn and its user-cause cancel | @@ -143,7 +157,7 @@ The package-level contract is enough for most consumers; read these when you nee #### What the model sees -`followup`, `steer`, and `inject` feed the owning session as identified user-role messages; accepted content becomes part of the derived history the model reads on later steps. `agent/pre-step` and the other declared events let plugins reject a proposed step or add durable request material. Optional [model-switch notices](../agent-model/README.md#model-experience) are owned by the selection package. +`followup`, `steer`, and `inject` feed the owning session as identified user-role messages; accepted content becomes part of the derived history the model reads on later steps. `agent/pre-step` and the other declared events let plugins reject a proposed step or add durable request material. The optional scoped model-switch listener adds the notice described below. #### Token effect @@ -161,11 +175,25 @@ Registrations through `agent.ctx` can shadow prompt sections or tools and can in #### Token effect -Each provider/model switch adds one short retained user-role notice. Other scoped contributions affect only that agent and disappear on disposal. +Scoped contributions affect only that agent and disappear on disposal. #### KV Cache effect -The switch notice appends after the previous history, preserving that prefix, while the route change can prevent the new provider or model from reusing it. Setup or reload that changes prompt sections, tool definitions, or request listeners may invalidate reuse from the first affected request token. +Setup or reload that changes prompt sections, tool definitions, or request listeners may invalidate reuse from the first affected request token. + +### Optional model-switch notice + +#### What the model sees + +The scoped listener appends `[model changed: assistant turns above this point were generated by ; the session continues with ]` when an accepted step's fixed route differs from the last request. Labels include providers only for cross-provider switches. Initial requests, effort-only changes, rejected steps, and empty no-request decisions add nothing. Failure before header persistence causes the next request step to repeat the notice. + +#### Token effect + +Each admitted notice adds retained user-message text; selection events themselves add no prompt text. + +#### KV Cache effect + +Notices append without replacing earlier history. Switching models may prevent reuse of the previous model's cache; cache availability is provider-owned. ## Known Limitations and Deferred Work diff --git a/packages/core/agent/README.zh.md b/packages/core/agent/README.zh.md index 76df954e02..7570678fcf 100644 --- a/packages/core/agent/README.zh.md +++ b/packages/core/agent/README.zh.md @@ -25,7 +25,7 @@ kind: "package-reference" ## 使用本包 -将 `dsh-agent` 与 `dsh-session-projection` 一起挂载:它提供 `ctx.agents`、`Agent` 句柄,以及读取已记录轮次和步骤位置的 `turnBoundaryOf(session)`。创建或恢复 Agent 还需要驱动器注册工厂;随附驱动器是 `dsh-agent-loop`。 [Session 模型选择](../agent-model/README.zh.md) 由独立服务提供。 +将 `dsh-agent` 与 `dsh-session-projection` 一起挂载:它提供 `ctx.agents`、`Agent` 句柄,以及读取已记录轮次和步骤位置的 `turnBoundaryOf(session)`。创建或恢复 Agent 还需要驱动器注册工厂;随附驱动器是 `dsh-agent-loop`。 [Session 模型选择](#model-selection) 通过每个句柄的 `agent.model` 提供。 ### 创建或恢复 agent @@ -42,7 +42,16 @@ await handle.dispose() // stops the loop, unregisters, removes the session, un `AgentOptions` 提供初始提供方/模型路由、可选的由适配器定义的 `reasoningEffort`,以及可选的正数 `maxTokens` 输出上限。循环会校验确切模型的推理(reasoning)支持、解析适配器默认值、把生效值记录在请求头中,并将它们应用到每个对话请求。可选的 `setup(agentCtx, agent)` 回调会在 agent 发布之前组合其作用域世界:`agentCtx` 拥有注册,显式的未发布 Agent 则提供其 Session;Context 不含反向 Agent 属性。作用域工具、提示词段与监听器在任何创建公告之前就已存在。Setup 只做组合:创建完成后才能驱动 agent。 -[模型选择服务](../agent-model/README.zh.md) 优先解析已记录选择,再读取 Agent options 回退值,并负责显式选择变更与配置恢复覆盖。 + +### 选择模型 + +`agent.model` 实现 `Model` 接口,与 `agent.inbox` 并列。`current()` 返回选择的独立副本:依次读取待生效选择、最近请求的选择和实时 `agent.options`;没有来源同时提供 provider 与 model 时返回 `undefined`。读取不会固定选择,也不会改变已经准备的步骤。 + +`select({ provider, model, reasoningEffort? })` 完整赋值,从下一个尚未开始准备的步骤生效。省略强度表示使用适配器默认值;选择操作既不继承旧强度,也不查询默认值。与已记录选择相同则不重复记录,但仅与初始 options 相同仍会记录 `model/selection`。调用方负责校验可用性与能力;追加失败沿用 Session 现有的失败保证。 + +普通恢复保留持久化选择。明确覆盖模型的入口先形成完整选择:仅在配置未指定强度且路由不变时保留旧的显式强度,然后调用 `select()`。Web 与 ACP 用户选择直接调用 `select()`,因此省略强度表示 Auto。选择 Session 模型不会改变部署默认值。 + +要启用作用域内的切换提示,在 Agent setup 中调用 `dsh-agent` 的 `installModelSelection(agentCtx)`。作用域拥有监听器,并在释放时移除它。 ### 驱动 agent 的对话 @@ -94,6 +103,10 @@ await handle.agent.whenIdle() `Inbox` 暴露待处理的 `nextTurn` 与 `nextStep` 消息,并通过 `append`、`prepend`、`replace`、`remove`、`clear` 与 `splice` 变更它们。普通删除和 `clear()` 都是持久取消。在步骤边界,循环的内部实现会通过纯删除 splice 领取待处理输入。实时通知刻意采用逐消息的最小载荷:`agent/inbox/inserted { message }`、`agent/inbox/claimed { message, turn }` 与 `agent/inbox/discarded { message }`。 +### 持久模型选择 + +`Model` 和 `modelSelection` 投影类型在本包定义。默认驱动器与 inbox 一起创建每个 `AgentModel` 并拥有投影注册。每个实例绑定 Session、实时选项和投影读取器,不维护选择的第二份副本。循环在步骤准备前捕获独立选择,并在重试间保留它。 + ### 源码地图 | 文件 | 职责 | @@ -102,6 +115,7 @@ await handle.agent.whenIdle() | [`src/runtime-types.ts`](src/runtime-types.ts) | `Agent`、结构化 `Inbox`、`AgentStatus` 与 `agent/*` 事件声明 | | [`src/types.ts`](src/types.ts) | `AgentOptions`、取消原因与收件箱投影词汇 | | [`src/dispatch.ts`](src/dispatch.ts) | `agentEvents` 融合分发器与 `assembleContextFor(agent)` | +| [`src/model-selection.ts`](src/model-selection.ts) | `AgentModel`、持久投影与 `installModelSelection` 切换提示 | | [`src/consumed-work.ts`](src/consumed-work.ts) | `foldConsumedWork(events)`:日志消费掉的工作最终怎样了 | | [`src/archive-admission.ts`](src/archive-admission.ts) | Workspace 注册表归档准入中的 `turn` 族:运行中的回合及其用户原因取消 | @@ -143,7 +157,7 @@ await handle.agent.whenIdle() #### 模型看到什么 -`followup`、`steer` 与 `inject` 以带标识的 user 角色消息馈送所属会话;被接纳的内容成为模型在后续步骤中读取的派生历史的一部分。`agent/pre-step` 与其他已声明事件让插件能够拒绝拟进入的步骤或添加持久请求材料。可选的[模型切换提示](../agent-model/README.zh.md#model-experience) 由选择包负责。 +`followup`、`steer` 与 `inject` 以带标识的 user 角色消息馈送所属会话;被接纳的内容成为模型在后续步骤中读取的派生历史的一部分。`agent/pre-step` 与其他已声明事件让插件能够拒绝拟进入的步骤或添加持久请求材料。可选的作用域模型切换监听器会添加下文所述的提示。 #### Token 影响 @@ -165,7 +179,21 @@ await handle.agent.whenIdle() #### KV Cache 影响 -切换提示追加在先前历史之后,因此保留该前缀;路由变更可能使新的提供方或模型无法复用此前缀。改变提示词段、工具定义或请求监听器的 setup 或 reload,可能从第一个受影响的请求 token 起使复用失效。 +改变提示词段、工具定义或请求监听器的 setup 或 reload,可能从第一个受影响的请求 token 起使复用失效。 + +### 可选的模型切换提示 + +#### 模型看到什么 + +已接纳步骤的固定路由与最近请求不同时,作用域监听器追加 `[model changed: assistant turns above this point were generated by ; the session continues with ]`。只有跨提供方切换才在标签中包含提供方。首次请求、仅强度变化、被拒绝步骤和不会发起请求的空决策不添加提示。请求头持久化前失败时,下一个请求步骤会再次收到提示。 + +#### Token 影响 + +每条接纳的提示都会增加保留的用户消息文本;选择事件本身不增加提示词文本。 + +#### KV Cache 影响 + +提示只追加,不替换此前历史。切换模型可能无法复用原模型缓存;缓存可用性由提供方决定。 ## 已知限制与延期工作 diff --git a/packages/core/agent/package.json b/packages/core/agent/package.json index c0a11b506f..4346e73f12 100644 --- a/packages/core/agent/package.json +++ b/packages/core/agent/package.json @@ -33,7 +33,6 @@ "lib/types/**/*.d.ts" ], "license": "MIT", - "dependencies": {}, "peerDependencies": { "@deepseek-ai/dsh-llm": "workspace:*", "@deepseek-ai/dsh-scope": "workspace:*", diff --git a/packages/core/agent/src/index.ts b/packages/core/agent/src/index.ts index 6781393b44..24abfa6d73 100644 --- a/packages/core/agent/src/index.ts +++ b/packages/core/agent/src/index.ts @@ -20,6 +20,8 @@ import { turnBoundaryProjectionDefinition } from './projection.ts' export * from './runtime-types.ts' export * from './types.ts' +export { AgentModel, modelSelectionProjectionDefinition, installModelSelection } from './model-selection.ts' +export type { ModelSelectionProjectionState } from './model-selection.ts' export type * from './projection.ts' export * from './consumed-work.ts' export { agentCarrier, agentEvents, assembleContextFor, emitAgentEvent } from './dispatch.ts' diff --git a/packages/core/agent/src/model-selection.ts b/packages/core/agent/src/model-selection.ts new file mode 100644 index 0000000000..adde6513f2 --- /dev/null +++ b/packages/core/agent/src/model-selection.ts @@ -0,0 +1,171 @@ +/** Agent-owned model selection, its durable projection, and scoped switch notices. + * @module @deepseek-ai/dsh-agent/model-selection + */ +import type { Context } from '@deepseek-ai/cordis' +import type { Model, AgentOptions, PreStepDecision } from './runtime-types.ts' +import type { ModelSelection } from './types.ts' +import { boundContextSummary, createUserMessage, type ContextFormed } from '@deepseek-ai/dsh-llm' +import { ReasoningEffortId } from '@deepseek-ai/dsh-llm/brand' +import type { Session, EpochHeader } from '@deepseek-ai/dsh-session' +import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection' +import { z } from 'zod' + +/** + * Durable model-selection state for one Session. An effort the adapter + * defaulted on a recorded request is omitted, so later requests resolve the + * default again instead of pinning it as a choice. + */ +export interface ModelSelectionProjectionState { + /** Selection used by the latest recorded model request. */ + readonly lastUsed: ModelSelection | null + /** Later recorded selection not yet used by a matching model request. */ + readonly pending: ModelSelection | null +} + +/** + * Build a detached selection, omitting an absent effort. + * @param provider - provider route. + * @param model - provider-owned model id. + * @param reasoningEffort - explicit effort; omission follows adapter defaults. + * @returns a detached selection. + */ +function toSelection(provider: string, model: string, reasoningEffort: ReasoningEffortId | undefined): ModelSelection { + return { provider, model, ...reasoningEffort === undefined ? {} : { reasoningEffort } } +} + +/** + * Compare provider and model without comparing reasoning effort. + * @param left - first selection. + * @param right - second selection. + * @returns whether both selections name the same route. + */ +function sameRoute(left: ModelSelection, right: ModelSelection): boolean { + return left.provider === right.provider && left.model === right.model +} + +/** + * Compare complete selections, treating absent effort as its own value. + * @param left - first selection, or no recorded selection. + * @param right - second selection, or no recorded selection. + * @returns whether both selections agree. + */ +function sameModelSelection(left: ModelSelection | null, right: ModelSelection | null): boolean { + return left === right || (left !== null && right !== null + && sameRoute(left, right) && left.reasoningEffort === right.reasoningEffort) +} + +/** Selection a recorded request used, omitting an effort the adapter defaulted. */ +function selectionOfHeader({ config, adapterDefaults }: EpochHeader): ModelSelection { + return toSelection(config.provider, config.model, adapterDefaults?.reasoningEffort === true ? undefined : config.reasoningEffort) +} + +const selectionSchema = z.object({ + provider: z.string().min(1), + model: z.string().min(1), + reasoningEffort: z.string().min(1).transform(ReasoningEffortId).exactOptional(), +}).nullable() + +/** + * Session selection fold: recorded selections become + * the pending choice, and each request header retires a matching one. + */ +export const modelSelectionProjectionDefinition: Required> = { + key: 'modelSelection', + stateVersion: 3, + stateSchema: z.object({ lastUsed: selectionSchema, pending: selectionSchema }), + init: () => ({ lastUsed: null, pending: null }), + apply: (state, event) => { + if (event.type === 'model/selection') { + return sameModelSelection(state.pending, event.data) ? state : { ...state, pending: { ...event.data } } + } + if (event.type !== 'request/header') return state + const lastUsed = selectionOfHeader(event.data.header) + const pending = sameModelSelection(state.pending, lastUsed) ? null : state.pending + return sameModelSelection(state.lastUsed, lastUsed) && pending === state.pending ? state : { lastUsed, pending } + }, + wire: { + viewSchema: z.object({ lastUsed: selectionSchema, next: selectionSchema }), + view: state => ({ lastUsed: state.lastUsed, next: state.pending ?? state.lastUsed }), + }, +} + +/** + * Read and record one Agent's model choices without resolving adapter defaults. + * @param readState - driver-owned reader for this Session's selection projection. + * @param session - Session receiving durable model selections. + * @param options - live route fallback until a selection or request is recorded. + */ +export class AgentModel implements Model { + constructor( + private readonly readState: () => ModelSelectionProjectionState, + private readonly session: Session, + private readonly options: AgentOptions, + ) {} + + current(): Readonly | undefined { + const recorded = this.recordedSelection() + if (recorded !== null) return { ...recorded } + const { provider, model, reasoningEffort } = this.options + return provider && model ? toSelection(provider, model, reasoningEffort) : undefined + } + + select(selection: Readonly): void { + if (sameModelSelection(this.recordedSelection(), selection)) return + this.session.append('model/selection', toSelection(selection.provider, selection.model, selection.reasoningEffort)) + } + + private recordedSelection(): ModelSelection | null { + const state = this.readState() + return state.pending ?? state.lastUsed + } +} + +declare module '@deepseek-ai/dsh-llm' { + interface MessageSourceMap { + 'model-selection': { kind: 'model-selection' } & ContextFormed + } +} + +function routeLabel(route: ModelSelection, other: ModelSelection): string { + return route.provider === other.provider ? route.model : `${route.provider}/${route.model}` +} + +function modelSwitchNotice(previous: ModelSelection, selected: ModelSelection) { + const from = routeLabel(previous, selected) + const to = routeLabel(selected, previous) + return createUserMessage({ + content: [{ + type: 'text', + text: `[model changed: assistant turns above this point were generated by ${from}; the session continues with ${to}]`, + }], + source: { + kind: 'model-selection', + form: 'notice', + summary: boundContextSummary(`${from} → ${to}`), + }, + }) +} + +/** + * Append a model-switch notice when the step's fixed route differs from the + * last request header. Effort-only changes and empty no-request decisions add + * no notice; a failure before header persistence repeats it on the next step. + * Install in the selected Agent's unpublished setup scope. + * @param agentCtx - The selected Agent's scoped context. + * @returns Disposer for the scoped pre-step listener. + */ +export function installModelSelection(agentCtx: Context): () => void { + return agentCtx.on( + 'agent/pre-step', + async ({ agent, messages, signal, step, callConfig }, next): Promise => { + const decision = await next() + if (decision.kind === 'reject' || signal.aborted) return decision + // The loop skips an empty first step and an emptied offered continuation. + if (decision.messages.length === 0 && (step === 1 || messages.length > 0)) return decision + const previous = agent.session.requestHeader()?.config + if (previous === undefined || (callConfig.provider === previous.provider && callConfig.model === previous.model)) return decision + return { ...decision, messages: [...decision.messages, modelSwitchNotice(previous, callConfig)] } + }, + { prepend: true }, + ) +} diff --git a/packages/core/agent/src/runtime-types.ts b/packages/core/agent/src/runtime-types.ts index 13d8bc754a..0b40a07190 100644 --- a/packages/core/agent/src/runtime-types.ts +++ b/packages/core/agent/src/runtime-types.ts @@ -50,6 +50,27 @@ export interface CancelOptions { keepInbox?: boolean | undefined } +/** Agent-owned access to the model choice for future steps. */ +export interface Model { + /** + * Resolve the choice for the next step to begin preparation. Recorded choices + * take precedence over the last request and then the Agent's live options. + * This read neither starts a step nor changes an already-prepared step. + * @returns a detached choice, or undefined when no source names both provider and model. + */ + current(): Readonly | undefined + + /** + * Replace the next step's complete selection. Omitted effort follows adapter + * defaults without inheriting a previous effort or resolving defaults here. + * An equal recorded choice is a no-op; an options-only fallback is + * always recorded. Callers own model availability and capability validation. + * The append uses the Session's existing persistence and failure guarantees. + * @param selection - complete provider, model, and optional explicit effort. + */ + select(selection: Readonly): void +} + /** Agent-owned access to pending work; concrete storage belongs to the driver. */ export interface Inbox { /** Prompts awaiting individual turns. */ @@ -172,6 +193,8 @@ declare module './types.ts' { readonly options: AgentOptions /** The live session this agent drives; its log is the durable source of truth. */ readonly session: Session + /** Agent-owned access to durable model selection for future steps. */ + readonly model: Model /** Agent-owned access to durable pending work. */ readonly inbox: Inbox /** The current lifecycle state, mirrored on every `agent/status` transition. */ @@ -337,7 +360,7 @@ declare module '@deepseek-ai/cordis' { * governs prompt admission. Model-visible content must use logged channels; * this waterfall cannot mutate messages. * @deprecated Retained for existing listeners; do not add new listeners. - * Use ctx.agentModel.select() before step preparation for model and reasoning + * Use agent.model.select() before step preparation for model and reasoning * choices, and AgentOptions.maxTokens for the output cap. Other per-request * control overrides have no replacement API. * @param payload.agent - the agent making the model call. diff --git a/packages/core/agent/src/types.ts b/packages/core/agent/src/types.ts index e58e00c4d0..06d2c88ab1 100644 --- a/packages/core/agent/src/types.ts +++ b/packages/core/agent/src/types.ts @@ -85,6 +85,21 @@ export interface ModelSelection { reasoningEffort?: ReasoningEffortId } +/** Client view of the durable model-selection fold. */ +export interface ModelSelectionProjection { + /** Selection used by the latest recorded model request. */ + readonly lastUsed: ModelSelection | null + /** Selection the next model step uses; null while the Agent's initial options supply the route. */ + readonly next: ModelSelection | null +} + +declare module '@deepseek-ai/dsh-session-projection/types' { + interface SessionProjectionMap { + /** Recorded model selection for the next model step and the latest request. */ + modelSelection: ModelSelectionProjection + } +} + declare module '@deepseek-ai/dsh-session/types' { interface SessionEventMap { /** diff --git a/packages/core/agent/tests/agent.spec.ts b/packages/core/agent/tests/agent.spec.ts index 24623a1f8b..54c5111eef 100644 --- a/packages/core/agent/tests/agent.spec.ts +++ b/packages/core/agent/tests/agent.spec.ts @@ -1,3 +1,4 @@ +import { unsupportedModel } from '@deepseek-ai/dsh-agent-loop-testkit' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { describe, expect, expectTypeOf, it, onTestFinished } from 'vitest' import { Context, Service, symbols } from '@deepseek-ai/cordis' @@ -22,6 +23,7 @@ function stubAgent(rawId: string, overrides: Partial = {}): Agent { id, options: {}, session, + model: unsupportedModel(), inbox: { nextTurn: [], nextStep: [], } as never, diff --git a/packages/core/agent/tests/archive-admission.spec.ts b/packages/core/agent/tests/archive-admission.spec.ts index c25b886325..10aac5a4f5 100644 --- a/packages/core/agent/tests/archive-admission.spec.ts +++ b/packages/core/agent/tests/archive-admission.spec.ts @@ -1,3 +1,4 @@ +import { unsupportedModel } from '@deepseek-ai/dsh-agent-loop-testkit' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { describe, expect, it } from 'vitest' import { Context } from '@deepseek-ai/cordis' @@ -19,6 +20,7 @@ async function liveAgent(ctx: Context, rawId: string, initial: AgentStatus): Pro id, options: {}, session: Session.create(id), + model: unsupportedModel(), inbox: { nextTurn: [], nextStep: [] } as never, get status() { return status }, ctx, diff --git a/packages/core/agent-model/tests/model-selection.spec.ts b/packages/core/agent/tests/model-selection.spec.ts similarity index 59% rename from packages/core/agent-model/tests/model-selection.spec.ts rename to packages/core/agent/tests/model-selection.spec.ts index dda0364b9e..6c45a90ea8 100644 --- a/packages/core/agent-model/tests/model-selection.spec.ts +++ b/packages/core/agent/tests/model-selection.spec.ts @@ -1,17 +1,16 @@ -import { describe, expect, it, onTestFinished, vi } from 'vitest' +import { describe, expect, it, onTestFinished } from 'vitest' import { Context } from '@deepseek-ai/cordis' import { agentEvents, + installModelSelection, type Agent, type AgentOptions, type ModelSelection, } from '@deepseek-ai/dsh-agent' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' -import * as ModelSwitchNotice from '@deepseek-ai/dsh-agent-model/notice' import { createUserMessage, ReasoningEffortId, type UserMessage } from '@deepseek-ai/dsh-llm' import SessionStore, { Session, SessionId, type EpochHeader } from '@deepseek-ai/dsh-session' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' -import { modelSelectionProjectionDefinition } from '../src/projection.ts' +import { AgentModel, modelSelectionProjectionDefinition } from '../src/model-selection.ts' const INITIAL = { provider: 'alpha', model: 'a0' } const SIGNAL = new AbortController().signal @@ -25,17 +24,22 @@ async function harness(options: AgentOptions = INITIAL) { onTestFinished(() => ctx.fiber.dispose()) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - const fiber = await ctx.plugin(AgentModelPlugin) const projection = await ctx.sessionProjections.register(modelSelectionProjectionDefinition) - await projection.dispose() const session = ctx.sessions.create(SessionId('model-selection')) - const agent = { id: session.id, session, ctx, options } as Agent - const current = () => ctx.agentModel.resolveForNextStep(agent) - const record = (selection: ModelSelection) => { ctx.agentModel.select(agent, selection) } + const model = new AgentModel(() => projection.stateOf(session), session, options) + const unused = (): never => { throw new Error('model-selection fixture does not drive the Agent') } + const agent: Agent = { + id: session.id, session, ctx, options, model, status: 'idle', + get inbox() { return unused() }, + cancel: unused, whenIdle: unused, runMaintenance: unused, + send: unused, followup: unused, steer: unused, inject: unused, + } + const current = () => agent.model.current() + const record = (selection: ModelSelection) => { agent.model.select(selection) } const header = (config: ModelSelection, adapterDefaults?: EpochHeader['adapterDefaults']) => session.append('request/header', { header: { config, ...adapterDefaults === undefined ? {} : { adapterDefaults } }, reason: 'initial', }) - return { ctx, agent, session, fiber, current, record, header, state: () => projection.stateOf(session) } + return { ctx, agent, session, current, record, header, state: () => projection.stateOf(session) } } function expectedNotice(from: string, to: string) { @@ -63,28 +67,6 @@ async function preStep( } describe('Session model selection', () => { - it('publishes the service only after projection registration is ready', async () => { - const ctx = new Context() - const ready = Promise.withResolvers() - const entered = Promise.withResolvers() - onTestFinished(async () => { - ready.resolve(undefined) - await ctx.fiber.dispose() - }) - await ctx.plugin(SessionProjectionRegistry) - const registration = ctx.sessionProjections.register(modelSelectionProjectionDefinition) - vi.spyOn(ctx.sessionProjections, 'register').mockImplementationOnce(() => { - entered.resolve(undefined) - return ready.promise.then(() => registration) - }) - const loading = ctx.plugin(AgentModelPlugin) - await entered.promise - expect(ctx.get('agentModel')).toBeUndefined() - ready.resolve(undefined) - await loading - expect(ctx.get('agentModel')).toBeDefined() - }) - it('uses initial options only until a selection or request is recorded and returns detached values', async () => { const h = await harness({}) expect(h.current()).toBeUndefined() @@ -121,8 +103,10 @@ describe('Session model selection', () => { h.header(config) expect(h.state()?.pending).toEqual(selected) } - const restored = { ...h.agent, session: Session.create(SessionId('restored'), h.session.snapshotEvents()) } - expect(h.ctx.agentModel.resolveForNextStep(restored)).toEqual(selected) + const restored = Session.create(SessionId('restored'), h.session.snapshotEvents()) + const projection = await h.ctx.sessionProjections.register(modelSelectionProjectionDefinition) + const restoredModel = new AgentModel(() => projection.stateOf(restored), restored, {}) + expect(restoredModel.current()).toEqual(selected) h.header(selected) expect(h.state()).toEqual({ lastUsed: selected, pending: null }) expect(h.current()).toEqual(selected) @@ -153,85 +137,22 @@ describe('Session model selection', () => { expect(h.state()).toBe(state) }) - it('applies configured routes and inherits explicit effort only for the same route', async () => { - const h = await harness({}) - const declare = (options: ModelSelection) => { h.ctx.agentModel.applyConfiguredRoute(h.agent, options) } - declare({ ...INITIAL, reasoningEffort: ReasoningEffortId('high') }) - declare(INITIAL) - expect(h.current()).toEqual({ ...INITIAL, reasoningEffort: 'high' }) - expect(h.session.snapshotEvents()).toHaveLength(1) - declare({ provider: 'beta', model: 'b1' }) - expect(h.current()).toEqual({ provider: 'beta', model: 'b1' }) - declare({ provider: 'beta', model: 'b1', reasoningEffort: ReasoningEffortId('low') }) - expect(h.current()?.reasoningEffort).toBe('low') - }) - - it.each(['provider', 'root', 'consumer-then-provider', 'consumer-then-root'] as const)('keeps selection readable through %s teardown until consumer cleanup settles', async (owner) => { + it('replaces the complete selection and clears an omitted explicit effort', async () => { const h = await harness() - const entered = Promise.withResolvers() - const release = Promise.withResolvers() - onTestFinished(() => { release.resolve(undefined) }) - const retained = h.ctx.agentModel - let cleaned = false - let cleanupError: unknown - const consumer = await h.ctx.plugin({ - name: 'selection-cleanup-consumer', - inject: ['agentModel'], - apply(ctx: Context) { - const selections = ctx.agentModel - ctx.effect(() => async () => { - entered.resolve(undefined) - await release.promise - try { - selections.select(h.agent, { provider: 'beta', model: 'b1' }) - expect(selections.resolveForNextStep(h.agent)).toEqual({ provider: 'beta', model: 'b1' }) - cleaned = true - } catch (error) { - cleanupError = error - } - }) - }, - }) - const consumerDisposal = owner.startsWith('consumer-then-') ? consumer.dispose() : undefined - if (consumerDisposal !== undefined) await entered.promise - let settled = false - const disposal = (owner.endsWith('provider') ? h.fiber.dispose() : h.ctx.fiber.dispose()) - .finally(() => { settled = true }) - await entered.promise - await new Promise(resolve => setImmediate(resolve)) - try { - expect(settled).toBe(false) - expect(retained.resolveForNextStep(h.agent)).toEqual(INITIAL) - } finally { - release.resolve(undefined) - await disposal - await consumerDisposal - } - expect(cleanupError).toBeUndefined() - expect(cleaned).toBe(true) - expect(h.session.snapshotEvents().at(-1)).toMatchObject({ - type: 'model/selection', data: { provider: 'beta', model: 'b1' }, - }) - }) - - it('removes the projection on provider unload and reconstructs selection on remount', async () => { - const h = await harness() - h.record({ provider: 'beta', model: 'b1' }) - const stale = h.ctx.agentModel - await h.fiber.dispose() - expect(h.ctx.get('agentModel')).toBeUndefined() - expect(h.ctx.sessionProjections.snapshot(h.session).values.modelSelection).toBeUndefined() - expect(() => stale.resolveForNextStep(h.agent)).toThrow('used after its provider unloaded') - await h.ctx.plugin(AgentModelPlugin) - expect(h.current()).toEqual({ provider: 'beta', model: 'b1' }) + h.record({ ...INITIAL, reasoningEffort: ReasoningEffortId('high') }) + h.record(INITIAL) + expect(h.current()).toEqual(INITIAL) + expect(h.session.snapshotEvents()).toHaveLength(2) + h.record(INITIAL) + expect(h.session.snapshotEvents()).toHaveLength(2) }) }) -describe('model-switch notice plugin', () => { +describe('installModelSelection()', () => { it('announces the fixed step route, preserves request-series declarations, and repeats before a header commits', async () => { const h = await harness() h.header(INITIAL) - await h.ctx.plugin(ModelSwitchNotice) + installModelSelection(h.ctx) h.record({ provider: 'alpha', model: 'future' }) const selected = { provider: 'beta', model: 'b1' } for (let attempt = 0; attempt < 2; attempt += 1) { @@ -249,7 +170,7 @@ describe('model-switch notice plugin', () => { it('does not announce initial, same-route, effort-only, rejected, aborted, or disposed steps', async () => { const h = await harness() - const fiber = await h.ctx.plugin(ModelSwitchNotice) + const dispose = installModelSelection(h.ctx) await expect(preStep(h.agent, INITIAL)).resolves.toMatchObject({ messages: [INPUT] }) h.header(INITIAL) await expect(preStep(h.agent, INITIAL)).resolves.toMatchObject({ messages: [INPUT] }) @@ -261,14 +182,14 @@ describe('model-switch notice plugin', () => { () => Promise.resolve({ kind: 'reject' as const }), )).resolves.toEqual({ kind: 'reject' }) await expect(preStep(h.agent, selected, { signal: AbortSignal.abort() })).resolves.toMatchObject({ messages: [INPUT] }) - await fiber.dispose() + dispose() await expect(preStep(h.agent, selected)).resolves.toMatchObject({ messages: [INPUT] }) }) it('preserves empty no-call decisions and announces an empty tool continuation', async () => { const h = await harness() h.header(INITIAL) - await h.ctx.plugin(ModelSwitchNotice) + installModelSelection(h.ctx) const selected = { provider: 'alpha', model: 'a1' } await expect(preStep(h.agent, selected, { messages: [] })).resolves.toMatchObject({ messages: [] }) await expect(preStep(h.agent, selected, { messages: [], step: 2 })).resolves.toMatchObject({ messages: [] }) diff --git a/packages/core/tools/package.json b/packages/core/tools/package.json index 3de527fe30..5b7136522a 100644 --- a/packages/core/tools/package.json +++ b/packages/core/tools/package.json @@ -68,7 +68,6 @@ "@deepseek-ai/dsh-sandbox": "workspace:*", "@deepseek-ai/dsh-sandbox-policy": "workspace:*", "@deepseek-ai/dsh-working-directory": "workspace:*", - "@deepseek-ai/dsh-agent-loop-testkit": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-agent-loop-testkit": "workspace:*" } } diff --git a/packages/core/tools/tests/tools.spec.ts b/packages/core/tools/tests/tools.spec.ts index 6d500ceff6..870c72fe4c 100644 --- a/packages/core/tools/tests/tools.spec.ts +++ b/packages/core/tools/tests/tools.spec.ts @@ -7,7 +7,6 @@ import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import SystemPrompt from '@deepseek-ai/dsh-system-prompt' import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import ApprovalService, { type ApprovalOutcome, type ApprovalRequest } from '@deepseek-ai/dsh-user-approval' import ToolRuntime, { defineContentToolFixture, defineTool, JsonSchemaError, parameterSchemaSpecToJsonSchema, validateArgs, ToolArgsError, ToolNotFoundError, @@ -810,7 +809,6 @@ describe('ToolRuntime', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/deliverables/tool-present/tests/present.spec.ts b/packages/deliverables/tool-present/tests/present.spec.ts index 10d1221db4..c175c39ae8 100644 --- a/packages/deliverables/tool-present/tests/present.spec.ts +++ b/packages/deliverables/tool-present/tests/present.spec.ts @@ -1,5 +1,5 @@ /** Explicit deliveries commit only after a successful final tool result. */ -import { provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import { mkdtemp, rm, writeFile, symlink, realpath } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join, relative } from 'node:path' @@ -33,6 +33,7 @@ async function agent(ctx: Context, cwd: string | undefined): Promise { id, options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'idle', get ctx() { return scope.ctx }, diff --git a/packages/experimental/browser-use-runtime/package.json b/packages/experimental/browser-use-runtime/package.json index 4ee1ebfd9b..8d2888fff0 100644 --- a/packages/experimental/browser-use-runtime/package.json +++ b/packages/experimental/browser-use-runtime/package.json @@ -64,7 +64,6 @@ "@deepseek-ai/dsh-attachment-local": "workspace:*", "@deepseek-ai/dsh-session-persistence-jsonl": "workspace:*", "@deepseek-ai/dsh-working-directory": "workspace:*", - "@deepseek-ai/dsh-fs-local": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-fs-local": "workspace:*" } } diff --git a/packages/experimental/browser-use-runtime/tests/mcp.spec.ts b/packages/experimental/browser-use-runtime/tests/mcp.spec.ts index 04ed5d34b9..fdb9a6a749 100644 --- a/packages/experimental/browser-use-runtime/tests/mcp.spec.ts +++ b/packages/experimental/browser-use-runtime/tests/mcp.spec.ts @@ -19,7 +19,6 @@ import Agents from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' import { provideWorkingDirectoryFixture } from '@deepseek-ai/dsh-agent-loop-testkit' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import LocalFileSystem from '@deepseek-ai/dsh-fs-local' import Projections from '@deepseek-ai/dsh-session-projection' import WorkingDirectory from '@deepseek-ai/dsh-working-directory' @@ -71,7 +70,7 @@ async function load(exclusive = false, mode?: string, toolCallTimeoutMs?: number const model = new FixtureModel() const modules = new Map([ ['browserUse', BrowserUse], ['prompt', SystemPrompt], ['tools', Tools], ['llm', Llm], - ['sessions', Sessions], ['agents', Agents], ['loop', AgentLoop], ['projections', Projections], ['agentModel', AgentModelPlugin], + ['sessions', Sessions], ['agents', Agents], ['loop', AgentLoop], ['projections', Projections], ['fs', LocalFileSystem], ['workingDirectory', WorkingDirectory], ['model', { inject: ['llm'], apply(ctx: Context) { ctx.effect(() => ctx.llm.registerAdapter(['fixture'], model)) } }], ['browser', { inject: ['browserUse', 'agents', 'tools', 'systemPrompt', 'workingDirectory'], apply(ctx: Context) { @@ -150,7 +149,6 @@ it('waits for SystemPrompt and ToolRuntime before reserving the provider', async contexts.push(ctx) await ctx.plugin(BrowserUse) await ctx.plugin(Projections) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(Agents) provideWorkingDirectoryFixture(ctx) const tools = ctx.plugin(Tools) diff --git a/packages/experimental/browser-use-runtime/tests/resources.spec.ts b/packages/experimental/browser-use-runtime/tests/resources.spec.ts index 978abeb0a3..4524b98b32 100644 --- a/packages/experimental/browser-use-runtime/tests/resources.spec.ts +++ b/packages/experimental/browser-use-runtime/tests/resources.spec.ts @@ -2,7 +2,7 @@ import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { Context } from '@deepseek-ai/cordis' import AgentRegistry from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' -import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import { Session, SessionId } from '@deepseek-ai/dsh-session' import { afterEach, describe, expect, it, vi } from 'vitest' import { SessionResources } from '../src/index.ts' @@ -19,6 +19,7 @@ async function fixture() { const session = Session.create(SessionId(id)) const agent: Agent = { id: session.id, session, ctx: fiber.ctx, options: {}, status: 'idle', + model: unsupportedModel(), inbox: unsupportedInbox(), send() {}, followup() {}, inject() {}, cancel() {}, steer: () => ({ outcome: Promise.resolve({ status: 'rejected' as const }) }), runMaintenance: task => task(new AbortController().signal), diff --git a/packages/experimental/browser-use-stagehand-native/package.json b/packages/experimental/browser-use-stagehand-native/package.json index d5bce0ecfd..17146b2d56 100644 --- a/packages/experimental/browser-use-stagehand-native/package.json +++ b/packages/experimental/browser-use-stagehand-native/package.json @@ -54,7 +54,6 @@ "@deepseek-ai/cordis-plugin-loader": "workspace:~", "@deepseek-ai/dsh-agent": "workspace:*", "@deepseek-ai/dsh-agent-loop": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*", "@deepseek-ai/dsh-agent-loop-testkit": "workspace:*", "@deepseek-ai/dsh-attachment": "workspace:*", "@deepseek-ai/dsh-attachment-local": "workspace:*", diff --git a/packages/experimental/browser-use-stagehand-native/tests/loader-composition.spec.ts b/packages/experimental/browser-use-stagehand-native/tests/loader-composition.spec.ts index 7a851cf537..94479f9c8b 100644 --- a/packages/experimental/browser-use-stagehand-native/tests/loader-composition.spec.ts +++ b/packages/experimental/browser-use-stagehand-native/tests/loader-composition.spec.ts @@ -10,7 +10,6 @@ import Loader from '@deepseek-ai/cordis-plugin-loader' import Include from '@deepseek-ai/cordis-plugin-include' import AgentRegistry from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import BrowserUseRegistry from '@deepseek-ai/dsh-browser-use' import LocalAttachmentStore from '@deepseek-ai/dsh-attachment-local' import LlmRuntime, { LlmAdapter, ToolCallId, createUserMessage } from '@deepseek-ai/dsh-llm' @@ -79,7 +78,7 @@ it('loads browser tools from cordis.yml, logs browser results, and admits the sc ['@deepseek-ai/dsh-llm', LlmRuntime], ['@deepseek-ai/dsh-session', SessionStore], ['@deepseek-ai/dsh-session-projection', SessionProjectionRegistry], - ['@deepseek-ai/dsh-agent-model', AgentModelPlugin], + ['@deepseek-ai/dsh-system-prompt', SystemPrompt], ['@deepseek-ai/dsh-tools', ToolRuntime], ['@deepseek-ai/dsh-agent', AgentRegistry], diff --git a/packages/experimental/claude-code-mods/package.json b/packages/experimental/claude-code-mods/package.json index 2a84b9a398..cbf2d43624 100644 --- a/packages/experimental/claude-code-mods/package.json +++ b/packages/experimental/claude-code-mods/package.json @@ -76,7 +76,6 @@ "@deepseek-ai/cordis-plugin-loader": "workspace:~", "@deepseek-ai/dsh-agent": "workspace:*", "@deepseek-ai/dsh-agent-loop": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*", "@deepseek-ai/dsh-agent-loop-testkit": "workspace:*", "@deepseek-ai/dsh-attachment": "workspace:*", "@deepseek-ai/dsh-commands": "workspace:*", diff --git a/packages/experimental/claude-code-mods/tests/loader-composition.spec.ts b/packages/experimental/claude-code-mods/tests/loader-composition.spec.ts index 6a1c03c8fa..7cc3c64472 100644 --- a/packages/experimental/claude-code-mods/tests/loader-composition.spec.ts +++ b/packages/experimental/claude-code-mods/tests/loader-composition.spec.ts @@ -10,7 +10,6 @@ import Loader from '@deepseek-ai/cordis-plugin-loader' import Include from '@deepseek-ai/cordis-plugin-include' import AgentRegistry from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import CommandRuntime from '@deepseek-ai/dsh-commands' import LocalFileSystem from '@deepseek-ai/dsh-fs-local' import WorkingDirectory from '@deepseek-ai/dsh-working-directory' @@ -41,7 +40,7 @@ it('loads from cordis.yml, counts the model\'s tool calls, and answers /tally th ['@deepseek-ai/dsh-llm', LlmRuntime], ['@deepseek-ai/dsh-session', SessionStore], ['@deepseek-ai/dsh-session-projection', SessionProjectionRegistry], - ['@deepseek-ai/dsh-agent-model', AgentModelPlugin], + ['@deepseek-ai/dsh-system-prompt', SystemPrompt], ['@deepseek-ai/dsh-fs-local', LocalFileSystem], ['@deepseek-ai/dsh-working-directory', WorkingDirectory], diff --git a/packages/experimental/client-ui-agent-team/package.json b/packages/experimental/client-ui-agent-team/package.json index 2fea830f28..fdde630b95 100644 --- a/packages/experimental/client-ui-agent-team/package.json +++ b/packages/experimental/client-ui-agent-team/package.json @@ -63,7 +63,7 @@ "@deepseek-ai/cordis": "workspace:~", "react": "^18.2.0", "react-dom": "^18.2.0", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-agent": "workspace:*" }, "files": [ "locale/*.json", diff --git a/packages/experimental/client-ui-agent-team/src/client/TeamAction.tsx b/packages/experimental/client-ui-agent-team/src/client/TeamAction.tsx index a3b3422b96..8fbbd06cc4 100644 --- a/packages/experimental/client-ui-agent-team/src/client/TeamAction.tsx +++ b/packages/experimental/client-ui-agent-team/src/client/TeamAction.tsx @@ -1,6 +1,6 @@ import { useEffect, useLayoutEffect, useRef, useState } from 'react' import { createPortal } from 'react-dom' -import type {} from '@deepseek-ai/dsh-agent-model/types' +import type {} from '@deepseek-ai/dsh-agent/types' import type { SessionId } from '@deepseek-ai/dsh-session/types' import type { TeamMemberProjection, diff --git a/packages/experimental/client-ui-agent-team/tsconfig.json b/packages/experimental/client-ui-agent-team/tsconfig.json index 777a876db2..3ce78d6a7f 100644 --- a/packages/experimental/client-ui-agent-team/tsconfig.json +++ b/packages/experimental/client-ui-agent-team/tsconfig.json @@ -4,48 +4,20 @@ "rootDir": "src", "outDir": "lib/types" }, - "include": [ - "src" - ], + "include": ["src"], "references": [ - { - "path": "../../../vendor/cordis" - }, - { - "path": "../agent-team" - }, - { - "path": "../../api/session-controller/tsconfig.client.json" - }, - { - "path": "../../client/locale/tsconfig.client.json" - }, - { - "path": "../../client/store" - }, - { - "path": "../../client/ui-conversation/tsconfig.client.json" - }, - { - "path": "../../client/ui-primitives" - }, - { - "path": "../../client/ui-renderer" - }, - { - "path": "../../client/ui-session" - }, - { - "path": "../../client/ui-slots" - }, - { - "path": "../../client/ui-workspace" - }, - { - "path": "../../core/session" - }, - { - "path": "../../core/agent-model" - } + { "path": "../../../vendor/cordis" }, + { "path": "../agent-team" }, + { "path": "../../api/session-controller/tsconfig.client.json" }, + { "path": "../../client/locale/tsconfig.client.json" }, + { "path": "../../client/store" }, + { "path": "../../client/ui-conversation/tsconfig.client.json" }, + { "path": "../../client/ui-primitives" }, + { "path": "../../client/ui-renderer" }, + { "path": "../../client/ui-session" }, + { "path": "../../client/ui-slots" }, + { "path": "../../client/ui-workspace" }, + { "path": "../../core/agent" }, + { "path": "../../core/session" } ] } diff --git a/packages/experimental/computer-use-cua-driver-mcp/package.json b/packages/experimental/computer-use-cua-driver-mcp/package.json index 8cbdbda89d..9a8d2c49d5 100644 --- a/packages/experimental/computer-use-cua-driver-mcp/package.json +++ b/packages/experimental/computer-use-cua-driver-mcp/package.json @@ -47,7 +47,6 @@ "@deepseek-ai/dsh-session": "workspace:*", "@deepseek-ai/dsh-agent": "workspace:*", "@deepseek-ai/dsh-agent-loop": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*", "@deepseek-ai/dsh-attachment-local": "workspace:*", "@deepseek-ai/dsh-session-projection": "workspace:*" } diff --git a/packages/experimental/computer-use-cua-driver-mcp/tests/composition.spec.ts b/packages/experimental/computer-use-cua-driver-mcp/tests/composition.spec.ts index f648d10dc0..f1b93094ed 100644 --- a/packages/experimental/computer-use-cua-driver-mcp/tests/composition.spec.ts +++ b/packages/experimental/computer-use-cua-driver-mcp/tests/composition.spec.ts @@ -16,7 +16,6 @@ import type { GenerateOptions, LlmResolvedModelInfo, StreamChunk } from '@deepse import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' import AgentRegistry from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import LocalAttachmentStore from '@deepseek-ai/dsh-attachment-local' import * as Provider from '../src/index.ts' @@ -69,7 +68,7 @@ async function load(mode?: string): Promise<{ ctx: Context; root: string; model: ['@deepseek-ai/dsh-agent', AgentRegistry], ['@deepseek-ai/dsh-agent-loop', AgentLoop], ['@deepseek-ai/dsh-session-projection', SessionProjectionRegistry], - ['@deepseek-ai/dsh-agent-model', AgentModelPlugin], + ['@deepseek-ai/dsh-attachment-local', LocalAttachmentStore], ['@fixture/model', { inject: ['llm'], apply(ctx: Context) { ctx.effect(() => ctx.llm.registerAdapter(['fixture'], model)) } }], ['@deepseek-ai/dsh-experimental-computer-use-cua-driver-mcp', Provider], diff --git a/packages/experimental/computer-use-cua-driver-native/package.json b/packages/experimental/computer-use-cua-driver-native/package.json index 6e67ed76d1..a421688d35 100644 --- a/packages/experimental/computer-use-cua-driver-native/package.json +++ b/packages/experimental/computer-use-cua-driver-native/package.json @@ -46,7 +46,6 @@ "@deepseek-ai/cordis-plugin-loader": "workspace:~", "@deepseek-ai/dsh-agent": "workspace:*", "@deepseek-ai/dsh-agent-loop": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*", "@deepseek-ai/dsh-agent-loop-testkit": "workspace:*", "@deepseek-ai/dsh-attachment": "workspace:*", "@deepseek-ai/dsh-attachment-local": "workspace:*", diff --git a/packages/experimental/computer-use-cua-driver-native/tests/loader-composition.spec.ts b/packages/experimental/computer-use-cua-driver-native/tests/loader-composition.spec.ts index fc0b13dd71..4f9fba4119 100644 --- a/packages/experimental/computer-use-cua-driver-native/tests/loader-composition.spec.ts +++ b/packages/experimental/computer-use-cua-driver-native/tests/loader-composition.spec.ts @@ -10,7 +10,6 @@ import Loader from '@deepseek-ai/cordis-plugin-loader' import Include from '@deepseek-ai/cordis-plugin-include' import AgentRegistry from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import ComputerUseRegistry from '@deepseek-ai/dsh-computer-use' import LocalAttachmentStore from '@deepseek-ai/dsh-attachment-local' import LlmRuntime, { LlmAdapter, ToolCallId, createUserMessage } from '@deepseek-ai/dsh-llm' @@ -64,7 +63,7 @@ it('loads from cordis.yml and logs the native screenshot before the next model r ['@deepseek-ai/dsh-llm', LlmRuntime], ['@deepseek-ai/dsh-session', SessionStore], ['@deepseek-ai/dsh-session-projection', SessionProjectionRegistry], - ['@deepseek-ai/dsh-agent-model', AgentModelPlugin], + ['@deepseek-ai/dsh-system-prompt', SystemPrompt], ['@deepseek-ai/dsh-tools', ToolRuntime], ['@deepseek-ai/dsh-agent', AgentRegistry], diff --git a/packages/experimental/cua-macos/package.json b/packages/experimental/cua-macos/package.json index b45c167a7d..a2cfd494dd 100644 --- a/packages/experimental/cua-macos/package.json +++ b/packages/experimental/cua-macos/package.json @@ -57,7 +57,6 @@ "@deepseek-ai/cordis-plugin-loader": "workspace:~", "@deepseek-ai/dsh-agent": "workspace:*", "@deepseek-ai/dsh-agent-loop": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*", "@deepseek-ai/dsh-agent-loop-testkit": "workspace:*", "@deepseek-ai/dsh-attachment": "workspace:*", "@deepseek-ai/dsh-attachment-local": "workspace:*", diff --git a/packages/experimental/cua-macos/tests/large-image-ptc.spec.ts b/packages/experimental/cua-macos/tests/large-image-ptc.spec.ts index 005507f97c..9f8124ff99 100644 --- a/packages/experimental/cua-macos/tests/large-image-ptc.spec.ts +++ b/packages/experimental/cua-macos/tests/large-image-ptc.spec.ts @@ -8,7 +8,6 @@ import Loader from '@deepseek-ai/cordis-plugin-loader' import Include from '@deepseek-ai/cordis-plugin-include' import AgentRegistry from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment' import LocalAttachmentStore from '@deepseek-ai/dsh-attachment-local' import ComputerUseRegistry from '@deepseek-ai/dsh-computer-use' @@ -65,7 +64,7 @@ it.skipIf(process.platform === 'win32')('delivers a valid PNG larger than 4 MiB bounds: { x: 0, y: 0, width: 1024, height: 512 }, })) const modules = { - llm: LlmRuntime, session: SessionStore, projection: SessionProjectionRegistry, agentModel: AgentModelPlugin, + llm: LlmRuntime, session: SessionStore, projection: SessionProjectionRegistry, systemPrompt: SystemPrompt, tools: ToolRuntime, agents: AgentRegistry, agentLoop: AgentLoop, attachments: LocalAttachmentStore, fs: FileSystem, subprocess: SubprocessLocal, sandbox: Sandbox, sandboxPolicy: SandboxPolicy, ptc: NodePtcRuntime, diff --git a/packages/experimental/cua-macos/tests/loader-composition.spec.ts b/packages/experimental/cua-macos/tests/loader-composition.spec.ts index b216b58a62..f90bb99620 100644 --- a/packages/experimental/cua-macos/tests/loader-composition.spec.ts +++ b/packages/experimental/cua-macos/tests/loader-composition.spec.ts @@ -9,7 +9,6 @@ import Loader from '@deepseek-ai/cordis-plugin-loader' import Include from '@deepseek-ai/cordis-plugin-include' import AgentRegistry from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import ComputerUseRegistry from '@deepseek-ai/dsh-computer-use' import LocalAttachmentStore from '@deepseek-ai/dsh-attachment-local' import LlmRuntime, { LlmAdapter, ToolCallId, createUserMessage } from '@deepseek-ai/dsh-llm' @@ -77,7 +76,7 @@ it.skipIf(process.platform === 'win32')('shares one application between retained await writeFile(join(root, 'observe-helper'), '') const readEvents = async () => (await readFile(join(root, 'helper-events.jsonl'), 'utf8')).trim().split('\n').map(line => AuditEvent.parse(JSON.parse(line))) const modules = { - llm: LlmRuntime, session: SessionStore, projection: SessionProjectionRegistry, agentModel: AgentModelPlugin, + llm: LlmRuntime, session: SessionStore, projection: SessionProjectionRegistry, systemPrompt: SystemPrompt, tools: ToolRuntime, agents: AgentRegistry, agentLoop: AgentLoop, attachments: LocalAttachmentStore, computerUse: ComputerUseRegistry, subprocess: SubprocessLocal, cua: NativeProvider, diff --git a/packages/experimental/tool-terminal/tests/loader-composition.spec.ts b/packages/experimental/tool-terminal/tests/loader-composition.spec.ts index 9d2db58481..eaade6c54b 100644 --- a/packages/experimental/tool-terminal/tests/loader-composition.spec.ts +++ b/packages/experimental/tool-terminal/tests/loader-composition.spec.ts @@ -1,5 +1,5 @@ import { mkdtemp, rm, writeFile } from 'node:fs/promises' -import { provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import { tmpdir } from 'node:os' import { join } from 'node:path' import { pathToFileURL } from 'node:url' @@ -43,7 +43,8 @@ async function agent(ctx: Context): Promise { const id = SessionId('pty-loader-agent') const session = Session.create(id) const value: Agent = { - id, options: {}, session, inbox: unsupportedInbox(), + id, options: {}, session, model: unsupportedModel(), + inbox: unsupportedInbox(), status: 'idle', ctx: scope.ctx, send: () => {}, diff --git a/packages/experimental/tool-terminal/tests/tools.spec.ts b/packages/experimental/tool-terminal/tests/tools.spec.ts index 5dab87002a..ecafd132fc 100644 --- a/packages/experimental/tool-terminal/tests/tools.spec.ts +++ b/packages/experimental/tool-terminal/tests/tools.spec.ts @@ -1,6 +1,6 @@ import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { describe, expect, it } from 'vitest' -import { provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import { Context } from '@deepseek-ai/cordis' import { ToolCallId } from '@deepseek-ai/dsh-llm' import { Session, SessionId } from '@deepseek-ai/dsh-session' @@ -21,7 +21,8 @@ async function fakeAgent(ctx: Context, rawId: string): Promise { const id = SessionId(rawId) const session = Session.create(id) const agent: Agent = { - id, options: {}, session, inbox: unsupportedInbox(), + id, options: {}, session, model: unsupportedModel(), + inbox: unsupportedInbox(), status: 'idle', ctx: scope.ctx, send: () => {}, diff --git a/packages/experimental/tool-worktree/package.json b/packages/experimental/tool-worktree/package.json index 3d74115ef2..aac7cbf903 100644 --- a/packages/experimental/tool-worktree/package.json +++ b/packages/experimental/tool-worktree/package.json @@ -55,8 +55,7 @@ "@deepseek-ai/dsh-tools": "workspace:*", "@deepseek-ai/dsh-working-directory": "workspace:*", "@deepseek-ai/dsh-experimental-worktree": "workspace:*", - "@deepseek-ai/dsh-agent-loop": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-agent-loop": "workspace:*" }, "icon": "./icon.svg", "dsh": { diff --git a/packages/experimental/tool-worktree/tests/loader-composition.spec.ts b/packages/experimental/tool-worktree/tests/loader-composition.spec.ts index c35e85d32b..97577e4407 100644 --- a/packages/experimental/tool-worktree/tests/loader-composition.spec.ts +++ b/packages/experimental/tool-worktree/tests/loader-composition.spec.ts @@ -10,7 +10,6 @@ import Loader from '@deepseek-ai/cordis-plugin-loader' import Include from '@deepseek-ai/cordis-plugin-include' import AgentRegistry from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import LocalFileSystem from '@deepseek-ai/dsh-fs-local' import LlmRuntime, { ToolCallId } from '@deepseek-ai/dsh-llm' import LocalSandbox from '@deepseek-ai/dsh-sandbox-local' @@ -55,7 +54,7 @@ describe('worktree tool through real Loader composition', () => { ['@deepseek-ai/dsh-agent', AgentRegistry], ['@deepseek-ai/dsh-session', SessionStore], ['@deepseek-ai/dsh-session-projection', SessionProjectionRegistry], - ['@deepseek-ai/dsh-agent-model', AgentModelPlugin], + ['@deepseek-ai/dsh-system-prompt', SystemPrompt], ['@deepseek-ai/dsh-llm', LlmRuntime], ['@deepseek-ai/dsh-tools', ToolRuntime], diff --git a/packages/experimental/worktree/tests/harness.ts b/packages/experimental/worktree/tests/harness.ts index 369c13fa2f..e1cb3ee446 100644 --- a/packages/experimental/worktree/tests/harness.ts +++ b/packages/experimental/worktree/tests/harness.ts @@ -6,7 +6,7 @@ import { promisify } from 'node:util' import { Context } from '@deepseek-ai/cordis' import AgentRegistry from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' -import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import LocalFileSystem from '@deepseek-ai/dsh-fs-local' import SandboxProvider from '@deepseek-ai/dsh-sandbox' import type { ConfinedArgv, SandboxMode, SandboxPolicy } from '@deepseek-ai/dsh-sandbox' @@ -42,7 +42,8 @@ export function testAgent(ctx: Context, cwd: string, name = 'worktree-test'): Ag const id = SessionId(name) const session = Session.create(id, [], { version: SESSION_FORMAT_VERSION, id, createdAt: 0, isSeeded: false, cwd }) const agent: Agent = { - id, session, options: {}, status: 'idle', ctx: scope.ctx, inbox: unsupportedInbox(), + id, session, options: {}, status: 'idle', ctx: scope.ctx, model: unsupportedModel(), + inbox: unsupportedInbox(), send() {}, followup() {}, steer() {}, inject() {}, cancel() {}, whenIdle: async () => {}, runMaintenance: task => task(new AbortController().signal), } diff --git a/packages/extensions/tool-cordis/src/api-catalog.ts b/packages/extensions/tool-cordis/src/api-catalog.ts index 5bb9967a3e..b4e713afa5 100644 --- a/packages/extensions/tool-cordis/src/api-catalog.ts +++ b/packages/extensions/tool-cordis/src/api-catalog.ts @@ -130,29 +130,6 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [ }, ], }, - { - key: 'agentModel', - summary: 'Read and record Session model choices without resolving adapter defaults.', - description: 'Read and record Session model choices without resolving adapter defaults.', - methods: [ - { - signature: 'resolveForNextStep(agent: Agent): Readonly | undefined', - description: 'Resolve the choice for the next step to begin preparation. Recorded choices take precedence over the last request and then the Agent\'s live options. This read neither starts a step nor changes an already-prepared step.', - parameters: [{ name: 'agent', description: 'Agent whose Session and fallback options are read.' }], - returns: 'a detached choice, or undefined when no source names both provider and model.', - }, - { - signature: 'select(agent: Agent, selection: Readonly): void', - description: 'Replace the next step\'s complete selection. Omitted effort follows adapter defaults. An equal recorded choice is a no-op; an options-only fallback is always recorded. Callers own model availability and capability validation. The append uses the Session\'s existing persistence and failure guarantees.', - parameters: [{ name: 'agent', description: 'Agent whose Session receives the model/selection event.' }, { name: 'selection', description: 'complete provider, model, and optional explicit effort.' }], - }, - { - signature: 'applyConfiguredRoute(agent: Agent, selection: Readonly): void', - description: 'Apply an explicit deployment route, including on Session resume. An omitted effort inherits the current explicit effort only for the same route; otherwise it follows adapter defaults. Call only when the entry point explicitly overrides recorded selection, not for ordinary resume fallback.', - parameters: [{ name: 'agent', description: 'Agent whose Session receives the configured choice.' }, { name: 'selection', description: 'complete configured provider/model and optional effort.' }], - }, - ], - }, { key: 'agentPresets', summary: 'Registry of YAML-declared presets and the revisions live Agents retain.', diff --git a/packages/feedback/command-feedback/tests/command-feedback.spec.ts b/packages/feedback/command-feedback/tests/command-feedback.spec.ts index 335b9126bd..03537dbab7 100644 --- a/packages/feedback/command-feedback/tests/command-feedback.spec.ts +++ b/packages/feedback/command-feedback/tests/command-feedback.spec.ts @@ -9,7 +9,7 @@ import SessionStore, { foldSurface, Session, SessionId } from '@deepseek-ai/dsh- import * as commandFeedback from '@deepseek-ai/dsh-command-feedback' import type { FeedbackRecord } from '@deepseek-ai/dsh-command-feedback/types' import { remoteMethods } from '@deepseek-ai/dsh-typert-protocol' -import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' const { USER_ID, getOrCreateAnonymousUserId } = vi.hoisted(() => { const USER_ID = '01234567-89ab-4cde-8f01-23456789abcd' @@ -37,6 +37,7 @@ function stubAgent(ctx: Context, id: string): { agent: Agent; session: Session } id: session.id, options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), ctx: new Context(), get status() { return status }, diff --git a/packages/feedback/command-feedback/tests/loader-composition.spec.ts b/packages/feedback/command-feedback/tests/loader-composition.spec.ts index 2b4e74412f..b5ce2d1ab3 100644 --- a/packages/feedback/command-feedback/tests/loader-composition.spec.ts +++ b/packages/feedback/command-feedback/tests/loader-composition.spec.ts @@ -13,7 +13,7 @@ import CommandRuntime from '@deepseek-ai/dsh-commands' import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' import * as CommandFeedback from '@deepseek-ai/dsh-command-feedback' import { getOrCreateAnonymousUserId } from '@deepseek-ai/dsh-anonymous-user-id' -import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' let root: string | undefined let context: Context | undefined @@ -36,6 +36,7 @@ async function agent(ctx: Context): Promise { id, options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), ctx: scope.ctx, get status() { return status }, diff --git a/packages/fs/tool-str-replace-editor/tests/tools.spec.ts b/packages/fs/tool-str-replace-editor/tests/tools.spec.ts index 710939691f..208699f473 100644 --- a/packages/fs/tool-str-replace-editor/tests/tools.spec.ts +++ b/packages/fs/tool-str-replace-editor/tests/tools.spec.ts @@ -16,7 +16,7 @@ import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import SystemPrompt from '@deepseek-ai/dsh-system-prompt' import ToolRuntime from '@deepseek-ai/dsh-tools' import * as ToolStrReplaceEditor from '@deepseek-ai/dsh-tool-str-replace-editor' -import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' const contexts: Context[] = [] const roots: string[] = [] @@ -37,6 +37,7 @@ async function agent(ctx: Context, cwd: string): Promise { id, options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'idle', ctx: scope.ctx, diff --git a/packages/goal/command-goal/tests/command-goal.spec.ts b/packages/goal/command-goal/tests/command-goal.spec.ts index 1e4e65c7a1..6cb4cf83c9 100644 --- a/packages/goal/command-goal/tests/command-goal.spec.ts +++ b/packages/goal/command-goal/tests/command-goal.spec.ts @@ -9,7 +9,7 @@ import type { GoalRef } from '@deepseek-ai/dsh-goal' import SessionStore, { Session, SessionId, type SessionEvent } from '@deepseek-ai/dsh-session' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import * as commandGoal from '@deepseek-ai/dsh-command-goal' -import { createInboxStub } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, createInboxStub } from '@deepseek-ai/dsh-agent-loop-testkit' interface Harness { readonly ctx: Context @@ -28,6 +28,7 @@ function stubAgent(ctx: Context, id: string): { agent: Agent; session: Session } id: session.id, options: {}, session, + model: unsupportedModel(), inbox, ctx: new Context(), get status() { return status }, diff --git a/packages/goal/goal/tests/goal.spec.ts b/packages/goal/goal/tests/goal.spec.ts index 8948fe916b..927e60110a 100644 --- a/packages/goal/goal/tests/goal.spec.ts +++ b/packages/goal/goal/tests/goal.spec.ts @@ -14,7 +14,7 @@ import GoalService, { foldGoal, } from '@deepseek-ai/dsh-goal' import type { GoalChangeMeta, GoalRef, GoalSnapshotChangeMeta } from '@deepseek-ai/dsh-goal' -import { createInboxStub } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, createInboxStub } from '@deepseek-ai/dsh-agent-loop-testkit' import { remoteMethods } from '@deepseek-ai/dsh-typert-protocol' declare module '@deepseek-ai/dsh-llm' { @@ -59,6 +59,7 @@ function stubAgentForSession(session: Session, suppliedCtx?: Context): StubAgent id, options: {}, session, + model: unsupportedModel(), inbox, ctx: agentCtx, status: 'idle', diff --git a/packages/goal/goal/tests/projection.spec.ts b/packages/goal/goal/tests/projection.spec.ts index f71559bb8e..b2aa38622d 100644 --- a/packages/goal/goal/tests/projection.spec.ts +++ b/packages/goal/goal/tests/projection.spec.ts @@ -19,7 +19,7 @@ import type { Session } from '@deepseek-ai/dsh-session' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import GoalService, { GoalId, applyGoalProjection, foldGoal, goalProjectionDefinition } from '@deepseek-ai/dsh-goal' import type { GoalProjection, GoalProjectionState, GoalRef } from '@deepseek-ai/dsh-goal' -import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' declare module '@deepseek-ai/dsh-llm' { interface MessageSourceMap { @@ -42,6 +42,7 @@ async function liveAgent(ctx: Context, session: Session): Promise { id: session.id, options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), ctx, get status() { return status }, diff --git a/packages/goal/tool-goal/tests/tool-goal.spec.ts b/packages/goal/tool-goal/tests/tool-goal.spec.ts index 233ed1526f..13901dac1f 100644 --- a/packages/goal/tool-goal/tests/tool-goal.spec.ts +++ b/packages/goal/tool-goal/tests/tool-goal.spec.ts @@ -19,7 +19,7 @@ import SystemPrompt from '@deepseek-ai/dsh-system-prompt' import ToolRuntime from '@deepseek-ai/dsh-tools' import type { ToolExecutionResult } from '@deepseek-ai/dsh-tools' import * as toolGoal from '@deepseek-ai/dsh-tool-goal' -import { createInboxStub } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, createInboxStub } from '@deepseek-ai/dsh-agent-loop-testkit' declare module '@deepseek-ai/dsh-llm' { interface MessageSourceMap { @@ -56,6 +56,7 @@ function stubAgent(rawId: string, supplied?: Session, suppliedCtx?: Context): St id: session.id, options: {}, session, + model: unsupportedModel(), inbox, get status() { return status }, ctx: agentCtx, diff --git a/packages/interaction/tool-ask-user/tests/tool-ask-user.spec.ts b/packages/interaction/tool-ask-user/tests/tool-ask-user.spec.ts index 80d821f1f0..df4e95a0ff 100644 --- a/packages/interaction/tool-ask-user/tests/tool-ask-user.spec.ts +++ b/packages/interaction/tool-ask-user/tests/tool-ask-user.spec.ts @@ -1,3 +1,4 @@ +import { unsupportedModel } from '@deepseek-ai/dsh-agent-loop-testkit' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' @@ -87,6 +88,7 @@ function stubAgent(id: string, delegationDepth = 0): Agent { session: Session.create(agentId, [], { version: SESSION_FORMAT_VERSION, id: agentId, createdAt: 0, isSeeded: false, delegationDepth, }), + model: unsupportedModel(), inbox: { nextTurn: [], nextStep: [], clear() {}, append() {}, prepend() {}, replace: () => false, remove: () => false, splice: () => [], diff --git a/packages/jobs/jobs-local/tests/events.spec.ts b/packages/jobs/jobs-local/tests/events.spec.ts index 5a653569b9..c405c6de15 100644 --- a/packages/jobs/jobs-local/tests/events.spec.ts +++ b/packages/jobs/jobs-local/tests/events.spec.ts @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' import { Session, SessionId } from '@deepseek-ai/dsh-session' -import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import type { Agent } from '@deepseek-ai/dsh-agent' import { ScopedLayers, bindScopeParent, createScope, scopeOf } from '@deepseek-ai/dsh-scope' import type { ScopeKey } from '@deepseek-ai/dsh-scope' @@ -22,6 +22,7 @@ function agentIn(ctx: Context, rawId: string, presetScope?: ScopeKey): Agent { id, options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'idle', ctx: agentCtx, diff --git a/packages/jobs/jobs-local/tests/jobs.spec.ts b/packages/jobs/jobs-local/tests/jobs.spec.ts index bd50122f36..0027549fb1 100644 --- a/packages/jobs/jobs-local/tests/jobs.spec.ts +++ b/packages/jobs/jobs-local/tests/jobs.spec.ts @@ -14,7 +14,7 @@ import type { JobEvent, JobEventFilter, JobHandle, JobHooks, JobKind, JobOutcome, JobOutputSource, JobSpec, JobView, } from '@deepseek-ai/dsh-jobs' import LocalJobRegistry, { type Config as JobsConfig } from '@deepseek-ai/dsh-jobs-local' -import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' vi.mock('node:crypto', async (importOriginal) => { const actual = await importOriginal() @@ -49,6 +49,7 @@ function stubAgent(ctx: Context, rawId: string, presetScope?: ScopeKey): Agent { id, options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'idle' as const, ctx: agentCtx, diff --git a/packages/llm/llm-deepseek/package.json b/packages/llm/llm-deepseek/package.json index c9f03166a7..8a7ab027f0 100644 --- a/packages/llm/llm-deepseek/package.json +++ b/packages/llm/llm-deepseek/package.json @@ -69,7 +69,6 @@ "@deepseek-ai/cordis-plugin-loader": "workspace:~", "@deepseek-ai/cordis-plugin-include": "workspace:~", "@deepseek-ai/dsh-agent-loop": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*", "@deepseek-ai/dsh-session-projection": "workspace:*", "@deepseek-ai/dsh-system-prompt": "workspace:*", "@deepseek-ai/dsh-tools": "workspace:*", diff --git a/packages/llm/llm-deepseek/tests/account-routing.spec.ts b/packages/llm/llm-deepseek/tests/account-routing.spec.ts index 45e4477477..1cd411ea1d 100644 --- a/packages/llm/llm-deepseek/tests/account-routing.spec.ts +++ b/packages/llm/llm-deepseek/tests/account-routing.spec.ts @@ -5,7 +5,6 @@ import { Context } from '@deepseek-ai/cordis' import LlmRuntime, { createUserMessage, LlmAdapter, type GenerateOptions, type StreamChunk, ToolCallId } from '@deepseek-ai/dsh-llm' import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import SystemPrompt from '@deepseek-ai/dsh-system-prompt' @@ -32,7 +31,7 @@ async function harness() { const ctx = new Context() contexts.push(ctx) installAccountTaskCancellation(ctx) - for (const plugin of [LlmRuntime, SessionStore, SessionProjectionRegistry, AgentModelPlugin, SystemPrompt, ToolRuntime, AgentRegistry]) { + for (const plugin of [LlmRuntime, SessionStore, SessionProjectionRegistry, SystemPrompt, ToolRuntime, AgentRegistry]) { await ctx.plugin(plugin) } await ctx.plugin(AgentLoop, { agents: [] }) @@ -110,7 +109,7 @@ it.each(['tool', 'retry'])('keeps the active provider until a later step binds a return config }) const selectOfficial = (agent: Agent) => { - ctx.agentModel.select(agent, { provider: 'deepseek-official', model: 'deepseek-v4-flash' }) + agent.model.select({ provider: 'deepseek-official', model: 'deepseek-v4-flash' }) } ctx.on('agent/request-error', async ({ agent }, next) => { await next() @@ -280,7 +279,7 @@ it('ignores idle account history but uses the last bound route during a new turn const cancel = vi.spyOn(agent, 'cancel') ctx.emit('deepseek-account/signed-out') expect(cancel).not.toHaveBeenCalled() - ctx.agentModel.select(agent, { provider: 'deepseek-official', model: 'deepseek-v4-flash' }) + agent.model.select({ provider: 'deepseek-official', model: 'deepseek-v4-flash' }) const preparing = Promise.withResolvers() const proceed = Promise.withResolvers() ctx.on('agent/request', async (_event, next) => { diff --git a/packages/llm/llm-deepseek/tests/adapter.spec.ts b/packages/llm/llm-deepseek/tests/adapter.spec.ts index 5704a39872..f09aa74264 100644 --- a/packages/llm/llm-deepseek/tests/adapter.spec.ts +++ b/packages/llm/llm-deepseek/tests/adapter.spec.ts @@ -11,7 +11,6 @@ import LocalAttachments from '@deepseek-ai/dsh-attachment-local' import AgentRegistry from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SystemPrompt from '@deepseek-ai/dsh-system-prompt' import ToolRuntime from '@deepseek-ai/dsh-tools' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' @@ -313,7 +312,7 @@ describe('Cordis provider composition', () => { const modules = new Map([ ['@deepseek-ai/dsh-llm', LlmRuntime], ['@deepseek-ai/dsh-llm-deepseek-api-key', Messages], ['@deepseek-ai/dsh-llm-deepseek-account', AccountProvider], ['@deepseek-ai/dsh-credentials-local', LocalCredentials], - ['@deepseek-ai/dsh-agent', AgentRegistry], ['@deepseek-ai/dsh-agent-loop', AgentLoop], ['@deepseek-ai/dsh-agent-model', AgentModelPlugin], + ['@deepseek-ai/dsh-agent', AgentRegistry], ['@deepseek-ai/dsh-agent-loop', AgentLoop], ['@deepseek-ai/dsh-session', SessionStore], ['@deepseek-ai/dsh-session-projection', SessionProjectionRegistry], ['@deepseek-ai/dsh-system-prompt', SystemPrompt], ['@deepseek-ai/dsh-tools', ToolRuntime], ]) diff --git a/packages/llm/llm-deepseek/tests/fixtures/cordis.yml b/packages/llm/llm-deepseek/tests/fixtures/cordis.yml index 7c90da43a1..0d95d810dd 100644 --- a/packages/llm/llm-deepseek/tests/fixtures/cordis.yml +++ b/packages/llm/llm-deepseek/tests/fixtures/cordis.yml @@ -4,7 +4,6 @@ - name: '@deepseek-ai/dsh-system-prompt' - name: '@deepseek-ai/dsh-tools' - name: '@deepseek-ai/dsh-agent' -- name: '@deepseek-ai/dsh-agent-model' - name: '@deepseek-ai/dsh-agent-loop' config: agents: [] diff --git a/packages/llm/llm-retry/package.json b/packages/llm/llm-retry/package.json index fe5bba6716..a1a6b8df8a 100644 --- a/packages/llm/llm-retry/package.json +++ b/packages/llm/llm-retry/package.json @@ -62,7 +62,6 @@ "@deepseek-ai/dsh-system-prompt": "workspace:*", "@deepseek-ai/dsh-timeout": "workspace:*", "@deepseek-ai/dsh-tools": "workspace:*", - "@deepseek-ai/cordis": "workspace:~", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/cordis": "workspace:~" } } diff --git a/packages/llm/llm-retry/tests/loader-composition.spec.ts b/packages/llm/llm-retry/tests/loader-composition.spec.ts index d080ee079a..1781056616 100644 --- a/packages/llm/llm-retry/tests/loader-composition.spec.ts +++ b/packages/llm/llm-retry/tests/loader-composition.spec.ts @@ -8,7 +8,6 @@ import Loader from '@deepseek-ai/cordis-plugin-loader' import Include from '@deepseek-ai/cordis-plugin-include' import AgentRegistry from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import LlmRuntime, { createUserMessage, LlmAdapter, LlmError, resolveRetryPolicy } from '@deepseek-ai/dsh-llm' import type { GenerateOptions, ResolvedRetryPolicy, StreamChunk } from '@deepseek-ai/dsh-llm' import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' @@ -63,7 +62,7 @@ async function loadYaml(lines: readonly string[]): Promise { ['@deepseek-ai/dsh-llm', LlmRuntime], ['@deepseek-ai/dsh-session', SessionStore], ['@deepseek-ai/dsh-session-projection', SessionProjectionRegistry], - ['@deepseek-ai/dsh-agent-model', AgentModelPlugin], + ['@deepseek-ai/dsh-system-prompt', SystemPrompt], ['@deepseek-ai/dsh-tools', ToolRuntime], ['@deepseek-ai/dsh-agent', AgentRegistry], @@ -94,7 +93,6 @@ describe('real Loader composition', () => { "- name: '@deepseek-ai/dsh-llm'", "- name: '@deepseek-ai/dsh-session'", "- name: '@deepseek-ai/dsh-session-projection'", - "- name: '@deepseek-ai/dsh-agent-model'", "- name: '@deepseek-ai/dsh-system-prompt'", "- name: '@deepseek-ai/dsh-tools'", "- name: '@deepseek-ai/dsh-agent'", diff --git a/packages/llm/llm-retry/tests/retry.spec.ts b/packages/llm/llm-retry/tests/retry.spec.ts index 9f1be950bc..0d47fd5aca 100644 --- a/packages/llm/llm-retry/tests/retry.spec.ts +++ b/packages/llm/llm-retry/tests/retry.spec.ts @@ -20,7 +20,6 @@ import ToolRuntime, { defineContentToolFixture } from '@deepseek-ai/dsh-tools' import AgentRegistry from '@deepseek-ai/dsh-agent' import type { Agent, RequestErrorAction } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import type { Config as retryConfig, RetryInternals as retryRetryInternals } from '../src/index.ts' import * as retry from '../src/index.ts' @@ -111,7 +110,6 @@ async function harness( await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -540,7 +538,7 @@ describe('provider-routed retry policy', () => { provider: 'mock', model: 'mock', }) - context.agentModel.select(agent, { provider: 'other', model: 'mock' }) + agent.model.select({ provider: 'other', model: 'mock' }) const scheduled = waitForRetry(context, agent, 1) agent.followup(createUserMessage({ content: [{ type: 'text', text: 'reroute' }], source: { kind: 'user' } })) @@ -581,7 +579,7 @@ describe('provider-routed retry policy', () => { context.tools.register(defineContentToolFixture({ name: 'switch_provider', description: '', parameters: {}, execute: async () => { - agent.ctx.agentModel.select(agent, { provider: 'other', model: 'mock' }) + agent.model.select({ provider: 'other', model: 'mock' }) return [{ type: 'text', text: 'switched' }] }, })) diff --git a/packages/plan/plan-mode/package.json b/packages/plan/plan-mode/package.json index 81e35abc1d..b70220ad63 100644 --- a/packages/plan/plan-mode/package.json +++ b/packages/plan/plan-mode/package.json @@ -69,7 +69,6 @@ "@deepseek-ai/dsh-tools": "workspace:*", "@deepseek-ai/dsh-user-questions": "workspace:*", "@deepseek-ai/cordis": "workspace:~", - "@deepseek-ai/dsh-agent-loop-testkit": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-agent-loop-testkit": "workspace:*" } } diff --git a/packages/plan/plan-mode/tests/integration.spec.ts b/packages/plan/plan-mode/tests/integration.spec.ts index 6d0797d7c4..1068721786 100644 --- a/packages/plan/plan-mode/tests/integration.spec.ts +++ b/packages/plan/plan-mode/tests/integration.spec.ts @@ -7,7 +7,6 @@ import SystemPrompt from '@deepseek-ai/dsh-system-prompt' import ToolRuntime, { defineContentToolFixture } from '@deepseek-ai/dsh-tools' import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import PlanModeController from '@deepseek-ai/dsh-plan-mode' import UserQuestionService, { UserQuestionError } from '@deepseek-ai/dsh-user-questions' import { MockAdapter, textResponse, toolCallResponse } from '../../../core/agent-loop/tests/mock-adapter.ts' @@ -27,7 +26,6 @@ async function harness(adapter: MockAdapter): Promise { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/preset/agent-preset-registry/package.json b/packages/preset/agent-preset-registry/package.json index 552dc4f28c..41d1343608 100644 --- a/packages/preset/agent-preset-registry/package.json +++ b/packages/preset/agent-preset-registry/package.json @@ -89,7 +89,6 @@ "@deepseek-ai/dsh-sandbox-policy": "workspace:*", "@deepseek-ai/dsh-user-approval": "workspace:*", "@deepseek-ai/dsh-agent-preset": "workspace:*", - "@types/js-yaml": "^4.0.9", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@types/js-yaml": "^4.0.9" } } diff --git a/packages/preset/agent-preset-registry/tests/harness.ts b/packages/preset/agent-preset-registry/tests/harness.ts index 77dd3f2c60..c420624fe3 100644 --- a/packages/preset/agent-preset-registry/tests/harness.ts +++ b/packages/preset/agent-preset-registry/tests/harness.ts @@ -8,7 +8,6 @@ import SystemPrompt from '@deepseek-ai/dsh-system-prompt' import ToolRuntime from '@deepseek-ai/dsh-tools' import AgentRegistry from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import AgentPresets, { type PresetDefinition } from '../src/index.ts' import { liveConfig } from '../../../settings/settings/tests/live-config.ts' @@ -25,7 +24,6 @@ export async function harness(options: { live?: boolean } = {}): Promise void>(), steer() {}, inject() {}, cancel() {}, whenIdle: async () => {}, runMaintenance: operation => operation(new AbortController().signal), } diff --git a/packages/sdk/server/package.json b/packages/sdk/server/package.json index 9fa3be5261..e5bbf5a9ee 100644 --- a/packages/sdk/server/package.json +++ b/packages/sdk/server/package.json @@ -42,7 +42,6 @@ "@deepseek-ai/dsh-session": "workspace:*", "@deepseek-ai/dsh-subagent": "workspace:*", "@deepseek-ai/dsh-working-directory": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*", "@deepseek-ai/dsh-user-questions": "workspace:*" }, "devDependencies": { @@ -62,7 +61,6 @@ "@deepseek-ai/dsh-session-projection": "workspace:*", "@deepseek-ai/dsh-subagent": "workspace:*", "@deepseek-ai/dsh-working-directory": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*", "@deepseek-ai/dsh-user-questions": "workspace:*" } } diff --git a/packages/sdk/server/src/index.ts b/packages/sdk/server/src/index.ts index 55dc151475..3fa3759550 100644 --- a/packages/sdk/server/src/index.ts +++ b/packages/sdk/server/src/index.ts @@ -12,14 +12,13 @@ import type { Context } from '@deepseek-ai/cordis' import type { Readable, Writable } from 'node:stream' import Schema from '@deepseek-ai/schemastery' -import type {} from '@deepseek-ai/dsh-agent-model' import { JsonRpcLineTransport } from '@deepseek-ai/dsh-sdk-protocol' import { HarnessSdkJsonRpcServer } from './server.ts' export * from './server.ts' export const name = 'sdk-jsonrpc-server' -export const inject = ['agents', 'agentModel', 'workingDirectory'] +export const inject = ['agents', 'workingDirectory'] /** JSON-RPC deployment config plus runtime-only test hooks. */ export interface JsonRpcConfig { diff --git a/packages/sdk/server/src/server.ts b/packages/sdk/server/src/server.ts index 84dba72e36..ee0acb739f 100644 --- a/packages/sdk/server/src/server.ts +++ b/packages/sdk/server/src/server.ts @@ -451,7 +451,16 @@ export class HarnessSdkJsonRpcServer { resumeSessionId: brandString(sessionId), agentOptions, // The initialized route replaces the restored Session's model selection. - setup: (_agentCtx, agent) => { this.ctx.agentModel.applyConfiguredRoute(agent, agentOptions) }, + setup: (_agentCtx, agent) => { + const previous = agent.model.current() + const selection = { ...agentOptions } + if (selection.reasoningEffort === undefined && previous !== undefined + && previous.provider === selection.provider && previous.model === selection.model + && previous.reasoningEffort !== undefined) { + selection.reasoningEffort = previous.reasoningEffort + } + agent.model.select(selection) + }, }) : this.ctx.agents.create({ sessionId: brandString(sessionId), meta: { cwd: this.cwd }, diff --git a/packages/sdk/server/tests/plugin-shape.spec.ts b/packages/sdk/server/tests/plugin-shape.spec.ts index 629cec8ea8..d3a49f1bb2 100644 --- a/packages/sdk/server/tests/plugin-shape.spec.ts +++ b/packages/sdk/server/tests/plugin-shape.spec.ts @@ -15,7 +15,7 @@ describe('dsh-sdk-jsonrpc-server plugin export shape', () => { const unwrapped = loader.unwrapExports(jsonrpc) as Record expect(unwrapped).toBe(jsonrpc) expect(unwrapped.name).toBe('sdk-jsonrpc-server') - expect(unwrapped.inject).toEqual(['agents', 'agentModel', 'workingDirectory']) + expect(unwrapped.inject).toEqual(['agents', 'workingDirectory']) expect(unwrapped.Config).toBeDefined() expect(typeof unwrapped.apply).toBe('function') }) diff --git a/packages/sdk/server/tests/resume.spec.ts b/packages/sdk/server/tests/resume.spec.ts index 0346f1f6fd..0a4bba9c9b 100644 --- a/packages/sdk/server/tests/resume.spec.ts +++ b/packages/sdk/server/tests/resume.spec.ts @@ -5,6 +5,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' import AgentLoop from '@deepseek-ai/dsh-agent-loop' import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit' +import { ReasoningEffortId, type LlmModelReasoningInfo } from '@deepseek-ai/dsh-llm' import { SessionId, SessionSeq } from '@deepseek-ai/dsh-session' import * as JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl' import type { JsonRpcTransportPeer, SessionEventNotification } from '@deepseek-ai/dsh-sdk-protocol' @@ -22,13 +23,13 @@ async function storageRoot(): Promise { return root } -async function runtime(root: string, persistence = true) { +async function runtime(root: string, persistence = true, reasoning?: LlmModelReasoningInfo) { const ctx = new Context() cleanups.push(() => ctx.fiber.dispose()) await mountAgentLoopTestDependencies(ctx) if (persistence) await ctx.plugin(JsonlSessionPersistence, { root }) await ctx.plugin(AgentLoop, { agents: [] }) - const adapter = new MockAdapter([textResponse('remembered')]) + const adapter = new MockAdapter([textResponse('remembered')], reasoning) ctx.llm.registerAdapter(['mock'], adapter) const events: SessionEventNotification[] = [] const transport: JsonRpcTransportPeer = { @@ -49,6 +50,35 @@ async function initialized(root: string) { } describe('SDK persisted session resume', () => { + it.each([ + { model: 'mock', effort: undefined, expected: 'high' }, + { model: 'other', effort: undefined, expected: undefined }, + { model: 'mock', effort: 'low', expected: 'low' }, + ])('applies the initialized route and effort on resume: $model / $effort', async ({ model, effort, expected }) => { + const root = await storageRoot() + const first = await initialized(root) + await first.server.prompt({ sessionId: 'saved-effort', contentBlocks: [{ type: 'text', text: 'remember' }] }) + await first.server.wait({ sessionId: 'saved-effort' }) + first.ctx.agents.get(SessionId('saved-effort'))!.model.select({ + provider: 'mock', model: 'mock', reasoningEffort: ReasoningEffortId('high'), + }) + await first.server.shutdown() + await first.ctx.fiber.dispose() + + const second = await runtime(root, true, { + efforts: [{ id: ReasoningEffortId('high'), name: 'High' }, { id: ReasoningEffortId('low'), name: 'Low' }], + }) + await second.server.initialize({ + cwd: root, provider: 'mock', model, + ...effort === undefined ? {} : { reasoningEffort: ReasoningEffortId(effort) }, + }) + await second.server.resume({ sessionId: 'saved-effort' }) + expect(second.ctx.agents.get(SessionId('saved-effort'))!.model.current()).toEqual({ + provider: 'mock', model, + ...expected === undefined ? {} : { reasoningEffort: expected }, + }) + }) + it('restores due reminders through the SDK owner after initialization and shares client resume', async () => { const root = await storageRoot() const first = await initialized(root) diff --git a/packages/sdk/server/tests/server.spec.ts b/packages/sdk/server/tests/server.spec.ts index 96eae7b5db..2961c6a428 100644 --- a/packages/sdk/server/tests/server.spec.ts +++ b/packages/sdk/server/tests/server.spec.ts @@ -15,7 +15,6 @@ import { randomUUID } from 'node:crypto' import AgentRegistry, { type Agent, type AgentHandle } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import { MockAdapter, textResponse } from '../../../core/agent-loop/tests/mock-adapter.ts' import SessionStore, { SessionId, type SessionEvent } from '@deepseek-ai/dsh-session' @@ -459,7 +458,6 @@ describe('HarnessSdkJsonRpcServer', () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(AgentRegistry) const transport = new FakeTransport() const server = new HarnessSdkJsonRpcServer(ctx, transport) diff --git a/packages/sdk/server/tsconfig.json b/packages/sdk/server/tsconfig.json index 6bad0fdb6f..65a1aee58a 100644 --- a/packages/sdk/server/tsconfig.json +++ b/packages/sdk/server/tsconfig.json @@ -38,9 +38,6 @@ { "path": "../../session/working-directory" }, - { - "path": "../../core/agent-model" - }, { "path": "../../schedule/schedule" }, diff --git a/packages/session/session-title/package.json b/packages/session/session-title/package.json index a10708cdc6..5605b7b4bf 100644 --- a/packages/session/session-title/package.json +++ b/packages/session/session-title/package.json @@ -58,7 +58,6 @@ "@deepseek-ai/dsh-llm": "workspace:*", "@deepseek-ai/dsh-session": "workspace:*", "@deepseek-ai/dsh-session-persistence-jsonl": "workspace:*", - "@deepseek-ai/dsh-session-projection": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-session-projection": "workspace:*" } } diff --git a/packages/shell/tool-bash-persistent/tests/loader-composition.spec.ts b/packages/shell/tool-bash-persistent/tests/loader-composition.spec.ts index f30e87b978..b6308764d5 100644 --- a/packages/shell/tool-bash-persistent/tests/loader-composition.spec.ts +++ b/packages/shell/tool-bash-persistent/tests/loader-composition.spec.ts @@ -1,5 +1,5 @@ import { mkdtemp, rm, writeFile } from 'node:fs/promises' -import { provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import { tmpdir } from 'node:os' import { join } from 'node:path' import { pathToFileURL } from 'node:url' @@ -48,6 +48,7 @@ async function agent(ctx: Context, cwd: string): Promise { id, options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'idle', ctx: scope.ctx, diff --git a/packages/shell/tool-bash-persistent/tests/tools.spec.ts b/packages/shell/tool-bash-persistent/tests/tools.spec.ts index 0d21ea642c..b50f5e1b2a 100644 --- a/packages/shell/tool-bash-persistent/tests/tools.spec.ts +++ b/packages/shell/tool-bash-persistent/tests/tools.spec.ts @@ -1,6 +1,6 @@ import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { afterEach, describe, expect, it, vi } from 'vitest' -import { provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import { Context } from '@deepseek-ai/cordis' import { ToolCallId } from '@deepseek-ai/dsh-llm' import { SESSION_FORMAT_VERSION, Session, SessionId } from '@deepseek-ai/dsh-session' @@ -43,6 +43,7 @@ async function agent(ctx: Context, cwd: string | undefined): Promise { id, options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'idle', ctx: scope.ctx, diff --git a/packages/shell/tool-bash/tests/background.spec.ts b/packages/shell/tool-bash/tests/background.spec.ts index 3605ad89c3..bbba63a744 100644 --- a/packages/shell/tool-bash/tests/background.spec.ts +++ b/packages/shell/tool-bash/tests/background.spec.ts @@ -11,7 +11,7 @@ import SystemPrompt from '@deepseek-ai/dsh-system-prompt' import ToolRuntime, { TOOL_ABORTED } from '@deepseek-ai/dsh-tools' import AgentRegistry from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' -import { provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import LocalJobRegistry from '@deepseek-ai/dsh-jobs-local' import type { JobId } from '@deepseek-ai/dsh-jobs' import * as ToolTasks from '@deepseek-ai/dsh-tool-jobs' @@ -361,6 +361,7 @@ describe('foreground commands as jobs', () => { session: Session.create(ownerId, undefined, { version: SESSION_FORMAT_VERSION, id: ownerId, createdAt: 0, cwd: process.cwd(), isSeeded: false, }), + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'idle', ctx, @@ -478,6 +479,7 @@ describe('owned background output', () => { session: Session.create(ownerId, undefined, { version: SESSION_FORMAT_VERSION, id: ownerId, createdAt: 0, cwd: process.cwd(), isSeeded: false, }), + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'idle', ctx, diff --git a/packages/shell/tool-pwsh-persistent/tests/loader-composition.spec.ts b/packages/shell/tool-pwsh-persistent/tests/loader-composition.spec.ts index 90c29923a0..55beca2417 100644 --- a/packages/shell/tool-pwsh-persistent/tests/loader-composition.spec.ts +++ b/packages/shell/tool-pwsh-persistent/tests/loader-composition.spec.ts @@ -1,5 +1,5 @@ import { spawnSync } from 'node:child_process' -import { provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import { mkdtemp, realpath, rm, writeFile } from 'node:fs/promises' import { release, tmpdir, version } from 'node:os' import { join } from 'node:path' @@ -83,6 +83,7 @@ async function agent(ctx: Context, cwd: string): Promise { id, options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'idle', ctx: scope.ctx, diff --git a/packages/shell/tool-pwsh-persistent/tests/tools.spec.ts b/packages/shell/tool-pwsh-persistent/tests/tools.spec.ts index 0842fa670d..4ff095b348 100644 --- a/packages/shell/tool-pwsh-persistent/tests/tools.spec.ts +++ b/packages/shell/tool-pwsh-persistent/tests/tools.spec.ts @@ -1,6 +1,6 @@ import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { afterEach, describe, expect, it, vi } from 'vitest' -import { provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import { Context } from '@deepseek-ai/cordis' import { ToolCallId } from '@deepseek-ai/dsh-llm' import { SESSION_FORMAT_VERSION, Session, SessionId } from '@deepseek-ai/dsh-session' @@ -43,6 +43,7 @@ async function agent(ctx: Context, cwd: string | undefined): Promise { id, options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'idle', ctx: scope.ctx, diff --git a/packages/shell/tool-pwsh/tests/background.spec.ts b/packages/shell/tool-pwsh/tests/background.spec.ts index c869b43668..3e7f79c9bf 100644 --- a/packages/shell/tool-pwsh/tests/background.spec.ts +++ b/packages/shell/tool-pwsh/tests/background.spec.ts @@ -8,7 +8,7 @@ import SystemPrompt from '@deepseek-ai/dsh-system-prompt' import ToolRuntime, { TOOL_ABORTED } from '@deepseek-ai/dsh-tools' import AgentRegistry from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' -import { provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import LocalJobRegistry from '@deepseek-ai/dsh-jobs-local' import * as ToolTasks from '@deepseek-ai/dsh-tool-jobs' import { ShellExecutor } from '@deepseek-ai/dsh-shell' @@ -211,6 +211,7 @@ describe('owned background output (pwsh)', () => { session: Session.create(ownerId, undefined, { version: SESSION_FORMAT_VERSION, id: ownerId, createdAt: 0, cwd: process.cwd(), isSeeded: false, }), + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'idle', ctx, @@ -413,6 +414,7 @@ describe('foreground commands as jobs (pwsh)', () => { session: Session.create(ownerId, undefined, { version: SESSION_FORMAT_VERSION, id: ownerId, createdAt: 0, cwd: process.cwd(), isSeeded: false, }), + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'idle', ctx, diff --git a/packages/skill/skill-office/package.json b/packages/skill/skill-office/package.json index 11372cee46..f768d1f6f2 100644 --- a/packages/skill/skill-office/package.json +++ b/packages/skill/skill-office/package.json @@ -34,8 +34,7 @@ "@deepseek-ai/dsh-skill": "workspace:*", "@deepseek-ai/cordis": "workspace:~", "@deepseek-ai/cordis-plugin-loader": "workspace:~", - "@deepseek-ai/cordis-plugin-include": "workspace:~", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/cordis-plugin-include": "workspace:~" }, "dependencies": { "@deepseek-ai/libreoffice-kit": "^0.1.5", diff --git a/packages/skill/skill-office/tests/progressive-loading.spec.ts b/packages/skill/skill-office/tests/progressive-loading.spec.ts index 2a572c4138..b1d9ea958e 100644 --- a/packages/skill/skill-office/tests/progressive-loading.spec.ts +++ b/packages/skill/skill-office/tests/progressive-loading.spec.ts @@ -4,7 +4,6 @@ import { Context } from '@deepseek-ai/cordis' import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' import { provideWorkingDirectoryFixture } from '@deepseek-ai/dsh-agent-loop-testkit' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import LlmRuntime, { createUserMessage } from '@deepseek-ai/dsh-llm' import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' @@ -35,7 +34,6 @@ async function setup(mode: 'native' | 'ptc', names: string[]) { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt, { personaPrefix: '', personaSuffix: '' }) await ctx.plugin(ToolRuntime, { mode }) await ctx.plugin(AgentRegistry) diff --git a/packages/skill/tool-skill/tests/tool-skill.spec.ts b/packages/skill/tool-skill/tests/tool-skill.spec.ts index 3f0684abfa..edda06e871 100644 --- a/packages/skill/tool-skill/tests/tool-skill.spec.ts +++ b/packages/skill/tool-skill/tests/tool-skill.spec.ts @@ -1,6 +1,6 @@ import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { afterEach, describe, expect, it } from 'vitest' -import { provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import { mkdir, rm, writeFile } from 'node:fs/promises' import { join } from 'node:path' import { tmpdir } from 'node:os' @@ -75,6 +75,7 @@ function agentForCwd(cwd: string): Agent { id, options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'idle', send: () => {}, @@ -92,6 +93,7 @@ function sessionAgent(session: Session, id = 'tool-skill-agent'): Agent { id: SessionId(id), options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'running', ctx: new Context(), diff --git a/packages/subagent/subagent-fork-in-process/package.json b/packages/subagent/subagent-fork-in-process/package.json index f1b35e2223..a23fd1771c 100644 --- a/packages/subagent/subagent-fork-in-process/package.json +++ b/packages/subagent/subagent-fork-in-process/package.json @@ -47,7 +47,6 @@ "@deepseek-ai/dsh-subagent": "workspace:*", "@deepseek-ai/dsh-subagent-spawn-in-process": "workspace:*", "@deepseek-ai/cordis": "workspace:~", - "@deepseek-ai/dsh-session-projection": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-session-projection": "workspace:*" } } diff --git a/packages/subagent/subagent-fork-in-process/tests/subagent-fork-in-process.spec.ts b/packages/subagent/subagent-fork-in-process/tests/subagent-fork-in-process.spec.ts index 155c3b94a2..c9030f3881 100644 --- a/packages/subagent/subagent-fork-in-process/tests/subagent-fork-in-process.spec.ts +++ b/packages/subagent/subagent-fork-in-process/tests/subagent-fork-in-process.spec.ts @@ -8,7 +8,6 @@ import AgentRegistry from '@deepseek-ai/dsh-agent' import { SessionId } from '@deepseek-ai/dsh-session' import AgentLoop from '@deepseek-ai/dsh-agent-loop' import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SubagentRuntime from '@deepseek-ai/dsh-subagent' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { MockAdapter, textResponse, toolCallResponse } from '../../../core/agent-loop/tests/mock-adapter.ts' @@ -195,7 +194,7 @@ describe('dsh-subagent-fork-in-process', () => { it('unregisters the provider when its fiber is disposed (HMR safety)', async () => { const ctx = new Context() await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) + await mountWorkingDirectoryFixture(ctx) await ctx.plugin(SubagentRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/subagent/subagent-spawn-in-process/package.json b/packages/subagent/subagent-spawn-in-process/package.json index 062cca130d..dbed9bfec1 100644 --- a/packages/subagent/subagent-spawn-in-process/package.json +++ b/packages/subagent/subagent-spawn-in-process/package.json @@ -51,7 +51,6 @@ "@deepseek-ai/dsh-tool-subagent": "workspace:*", "@deepseek-ai/cordis": "workspace:~", "@deepseek-ai/dsh-session-projection": "workspace:*", - "@deepseek-ai/dsh-session-persistence-jsonl": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-session-persistence-jsonl": "workspace:*" } } diff --git a/packages/subagent/subagent-spawn-in-process/tests/subagent-spawn-in-process.spec.ts b/packages/subagent/subagent-spawn-in-process/tests/subagent-spawn-in-process.spec.ts index e9f7e02deb..04ff5f6e2b 100644 --- a/packages/subagent/subagent-spawn-in-process/tests/subagent-spawn-in-process.spec.ts +++ b/packages/subagent/subagent-spawn-in-process/tests/subagent-spawn-in-process.spec.ts @@ -8,7 +8,6 @@ import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent' import { SessionId } from '@deepseek-ai/dsh-session' import AgentLoop from '@deepseek-ai/dsh-agent-loop' import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SubagentRuntime from '@deepseek-ai/dsh-subagent' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { MockAdapter, maxTokensResponse, textResponse, toolCallResponse } from '../../../core/agent-loop/tests/mock-adapter.ts' @@ -277,7 +276,7 @@ describe('dsh-subagent-spawn-in-process', () => { it('unregisters the provider when its fiber is disposed (HMR safety)', async () => { const ctx = new Context() await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) + await mountWorkingDirectoryFixture(ctx) await ctx.plugin(SubagentRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/subagent/subagent/package.json b/packages/subagent/subagent/package.json index 2b7887b0ee..0ee71b497d 100644 --- a/packages/subagent/subagent/package.json +++ b/packages/subagent/subagent/package.json @@ -131,7 +131,6 @@ "@deepseek-ai/dsh-fs-sandbox": "workspace:*", "@deepseek-ai/dsh-tool-fs": "workspace:*", "@deepseek-ai/dsh-session-persistence-jsonl": "workspace:*", - "@deepseek-ai/dsh-working-directory": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-working-directory": "workspace:*" } } diff --git a/packages/subagent/subagent/tests/archive-admission.spec.ts b/packages/subagent/subagent/tests/archive-admission.spec.ts index 9e8c1b2b12..88385e6b0e 100644 --- a/packages/subagent/subagent/tests/archive-admission.spec.ts +++ b/packages/subagent/subagent/tests/archive-admission.spec.ts @@ -3,7 +3,7 @@ import { describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' import AgentRegistry from '@deepseek-ai/dsh-agent' import type { Agent, AgentCancelCause, AgentStatus, CancelOptions } from '@deepseek-ai/dsh-agent' -import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' import type { Session } from '@deepseek-ai/dsh-session' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' @@ -34,6 +34,7 @@ async function liveAgent(ctx: Context, session: Session, initial: AgentStatus): id: session.id, options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), get status() { return status }, ctx, diff --git a/packages/subagent/subagent/tests/external-activation-helpers.ts b/packages/subagent/subagent/tests/external-activation-helpers.ts index d83837df9e..93e86ce580 100644 --- a/packages/subagent/subagent/tests/external-activation-helpers.ts +++ b/packages/subagent/subagent/tests/external-activation-helpers.ts @@ -7,7 +7,7 @@ import type { Context } from '@deepseek-ai/cordis' import AgentRegistry from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' -import { createInboxStub } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, createInboxStub } from '@deepseek-ai/dsh-agent-loop-testkit' import type { SubagentActivation, SubagentStartRequest } from '../src/types.ts' const parents = new WeakMap>>() @@ -40,6 +40,7 @@ export async function externalTestParent(ctx: Context, cwd?: string): Promise { inbox.append(target, message) }, followup: (message) => { inbox.append('next-turn', message) }, diff --git a/packages/subagent/subagent/tests/list-children.spec.ts b/packages/subagent/subagent/tests/list-children.spec.ts index 8a11550899..2e64d9124f 100644 --- a/packages/subagent/subagent/tests/list-children.spec.ts +++ b/packages/subagent/subagent/tests/list-children.spec.ts @@ -8,7 +8,6 @@ import { Context } from '@deepseek-ai/cordis' import AgentLoop from '@deepseek-ai/dsh-agent-loop' import type { Agent } from '@deepseek-ai/dsh-agent' import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import SessionStore, { SESSION_FORMAT_VERSION, SessionId } from '@deepseek-ai/dsh-session' import type { Session } from '@deepseek-ai/dsh-session' import * as JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl' @@ -192,7 +191,6 @@ describe('SubagentRuntime.listChildren', () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(TestSessionQuery) await mountWorkingDirectoryFixture(ctx) if (ctx.get('sessionProjections') === undefined) await ctx.plugin(SessionProjectionRegistry) @@ -224,7 +222,6 @@ describe('SubagentRuntime.listChildren', () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(TestSessionQuery) await mountWorkingDirectoryFixture(ctx) if (ctx.get('sessionProjections') === undefined) await ctx.plugin(SessionProjectionRegistry) diff --git a/packages/subagent/subagent/tests/service.spec.ts b/packages/subagent/subagent/tests/service.spec.ts index b9d792f332..f47c9d636d 100644 --- a/packages/subagent/subagent/tests/service.spec.ts +++ b/packages/subagent/subagent/tests/service.spec.ts @@ -4,7 +4,6 @@ import { Context } from '@deepseek-ai/cordis' import { type Agent } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import { HarnessError, ReasoningEffortId } from '@deepseek-ai/dsh-llm' import { carrierKeyOf } from '@deepseek-ai/dsh-scope' @@ -82,7 +81,7 @@ async function service(withAgents = true): Promise<{ ctx: Context; subagents: Su await ctx.plugin(AgentLoop, { agents: [] }) } else { await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) + } await mountWorkingDirectoryFixture(ctx) await ctx.plugin(SubagentRuntime) @@ -112,7 +111,7 @@ describe('SubagentRuntime', () => { it('releases its catalog projection binding with the service fiber', async () => { const ctx = new Context() await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) + await mountWorkingDirectoryFixture(ctx) const fiber = await ctx.plugin(SubagentRuntime) const parent = Session.create(SessionId('catalog-parent')) diff --git a/packages/subagent/tool-subagent/package.json b/packages/subagent/tool-subagent/package.json index 3fafb82ab7..c6f1a2de3e 100644 --- a/packages/subagent/tool-subagent/package.json +++ b/packages/subagent/tool-subagent/package.json @@ -70,7 +70,6 @@ "@deepseek-ai/dsh-system-prompt": "workspace:*", "@deepseek-ai/dsh-tools": "workspace:*", "@deepseek-ai/cordis": "workspace:~", - "@deepseek-ai/dsh-session-projection": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-session-projection": "workspace:*" } } diff --git a/packages/terminal/terminal-bash/tests/index.spec.ts b/packages/terminal/terminal-bash/tests/index.spec.ts index f3d991dbbb..71793a5f6e 100644 --- a/packages/terminal/terminal-bash/tests/index.spec.ts +++ b/packages/terminal/terminal-bash/tests/index.spec.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from 'vitest' -import { provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import { PassThrough } from 'node:stream' import { resolve } from 'node:path' import { Context } from '@deepseek-ai/cordis' @@ -55,7 +55,8 @@ function agent(ctx: Context, cwd?: string): Agent { version: SESSION_FORMAT_VERSION, id, createdAt: 0, isSeeded: false, ...cwd === undefined ? {} : { cwd }, }) return { - id, options: {}, session, inbox: unsupportedInbox(), + id, options: {}, session, model: unsupportedModel(), + inbox: unsupportedInbox(), status: 'idle', ctx, send: () => {}, @@ -675,7 +676,8 @@ describe('terminal-bash plugin shape', () => { const session = ctx.sessions.create(SessionId('mode-owner')) const ownerFiber = await ctx.plugin(() => {}) const owner: Agent = { - id: session.id, options: {}, session, inbox: unsupportedInbox(), + id: session.id, options: {}, session, model: unsupportedModel(), + inbox: unsupportedInbox(), status: 'idle', ctx: ownerFiber.ctx, send: () => {}, @@ -726,7 +728,8 @@ describe('terminal-bash plugin shape', () => { const session = ctx.sessions.create(SessionId('pending-mode-owner')) const ownerFiber = await ctx.plugin(() => {}) const owner: Agent = { - id: session.id, options: {}, session, inbox: unsupportedInbox(), + id: session.id, options: {}, session, model: unsupportedModel(), + inbox: unsupportedInbox(), status: 'idle', ctx: ownerFiber.ctx, send: () => {}, diff --git a/packages/terminal/terminal-bash/tests/local.spec.ts b/packages/terminal/terminal-bash/tests/local.spec.ts index c12f6181ed..e1d7528685 100644 --- a/packages/terminal/terminal-bash/tests/local.spec.ts +++ b/packages/terminal/terminal-bash/tests/local.spec.ts @@ -1,5 +1,5 @@ import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' -import { provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, provideWorkingDirectoryFixture, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' import { spawnSync } from 'node:child_process' import { tmpdir } from 'node:os' import { join } from 'node:path' @@ -40,7 +40,8 @@ function stubAgent(ctx: Context, rawId: string): Agent { const scope = ctx.plugin(() => {}) const session = Session.create(id) const agent: Agent = { - id, options: {}, session, inbox: unsupportedInbox(), + id, options: {}, session, model: unsupportedModel(), + inbox: unsupportedInbox(), status: 'idle', ctx: scope.ctx, send: () => {}, diff --git a/packages/terminal/terminal/tests/service.spec.ts b/packages/terminal/terminal/tests/service.spec.ts index 7a4b4a97e8..0acd31028f 100644 --- a/packages/terminal/terminal/tests/service.spec.ts +++ b/packages/terminal/terminal/tests/service.spec.ts @@ -15,7 +15,7 @@ import type { TerminalSessionStatus, TerminalSignal, } from '@deepseek-ai/dsh-terminal' -import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' const agentScopeDisposers = new WeakMap Promise>() const ptyServiceDisposers = new WeakMap Promise>() @@ -28,6 +28,7 @@ function stubAgent(ctx: Context, rawId: string): Agent { id, options: {}, session, + model: unsupportedModel(), inbox: unsupportedInbox(), status: 'idle', ctx: scopeFiber.ctx, diff --git a/packages/test-support/agent-loop-testkit/README.i18n.yaml b/packages/test-support/agent-loop-testkit/README.i18n.yaml index bd3fbcb58b..39b845954a 100644 --- a/packages/test-support/agent-loop-testkit/README.i18n.yaml +++ b/packages/test-support/agent-loop-testkit/README.i18n.yaml @@ -3,7 +3,7 @@ # After editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/test-support/agent-loop-testkit/README.md /: - en: 56bf98b380f80f74 + en: 4ec754be137aecb6 zh: 4e26deb963e89c68 /deepseek-ai-dsh-agent-loop-testkit: en: 52b308fffde03f7e @@ -21,8 +21,8 @@ en: 55af6e8dcc16bf46 zh: 9e1a6a7f70fde817 /deepseek-ai-dsh-agent-loop-testkit/use-this-package/build-a-structural-agent-stub: - en: 0b80eeb902e129d0 - zh: 93352ffbc3154bef + en: 84a12ebbcfe2ea7d + zh: 9ef2bc1e2389cde1 /deepseek-ai-dsh-agent-loop-testkit/use-this-package/when-to-use-it: en: 7afa2a3c8ff17664 zh: 6c8cad1daf3a5df3 diff --git a/packages/test-support/agent-loop-testkit/README.md b/packages/test-support/agent-loop-testkit/README.md index ea99216afa..1f2adc22ad 100644 --- a/packages/test-support/agent-loop-testkit/README.md +++ b/packages/test-support/agent-loop-testkit/README.md @@ -1,5 +1,5 @@ --- -description: "Prerequisite mounting, production AgentLoop drivers, and explicit Inbox stubs for agent-loop tests." +description: "Prerequisite mounting, production AgentLoop drivers, and explicit Agent stubs for agent-loop tests." kind: "package-library" --- @@ -57,14 +57,15 @@ The dependency helper forwards system-prompt and tool-registry configuration thr ### Build a structural Agent stub -Use `createInboxStub()` when the test subject needs mutable pending lists but does not exercise durability, projection validation, live Inbox notifications, or the driver's claim policy. The stub implements the public queue operations with two process-local arrays and never writes to a Session. Use `unsupportedInbox()` when the test subject must not touch pending input; every mutation throws at the first unexpected dependency. +Use `createInboxStub()` when the test subject needs mutable pending lists but does not exercise durability, projection validation, live Inbox notifications, or the driver's claim policy. The stub implements the public queue operations with two process-local arrays and never writes to a Session. Use `unsupportedInbox()` when the test subject must not touch pending input; every mutation throws at the first unexpected dependency. Use `unsupportedModel()` when the test does not read or change model selection; all its operations throw. ```ts -import { createInboxStub } from '@deepseek-ai/dsh-agent-loop-testkit' +import { createInboxStub, unsupportedModel } from '@deepseek-ai/dsh-agent-loop-testkit' const agent = { // ... inbox: createInboxStub(), + model: unsupportedModel(), } ``` diff --git a/packages/test-support/agent-loop-testkit/README.zh.md b/packages/test-support/agent-loop-testkit/README.zh.md index b86b008129..fd6efd4676 100644 --- a/packages/test-support/agent-loop-testkit/README.zh.md +++ b/packages/test-support/agent-loop-testkit/README.zh.md @@ -57,14 +57,15 @@ const admitted = harness.claim(agent, 'next-turn', 1) ### 构造结构化 Agent 桩 -当测试对象需要可变的待处理列表,但不测试持久性、投影校验、实时 Inbox 通知或驱动的认领策略时,使用 `createInboxStub()`。该桩通过两个进程内数组实现公开队列操作,且绝不会写入 Session。当测试对象不应访问待处理输入时,使用 `unsupportedInbox()`;每次变更都会在首个意外依赖处抛错。 +当测试对象需要可变的待处理列表,但不测试持久性、投影校验、实时 Inbox 通知或驱动的认领策略时,使用 `createInboxStub()`。该桩通过两个进程内数组实现公开队列操作,且绝不会写入 Session。当测试对象不应访问待处理输入时,使用 `unsupportedInbox()`;每次变更都会在首个意外依赖处抛错。当测试不读取或更改模型选择时,使用 `unsupportedModel()`;它的所有操作都会抛错。 ```ts -import { createInboxStub } from '@deepseek-ai/dsh-agent-loop-testkit' +import { createInboxStub, unsupportedModel } from '@deepseek-ai/dsh-agent-loop-testkit' const agent = { // ... inbox: createInboxStub(), + model: unsupportedModel(), } ``` diff --git a/packages/test-support/agent-loop-testkit/package.json b/packages/test-support/agent-loop-testkit/package.json index 66fce7ea5b..524e810f20 100644 --- a/packages/test-support/agent-loop-testkit/package.json +++ b/packages/test-support/agent-loop-testkit/package.json @@ -36,8 +36,7 @@ "@deepseek-ai/dsh-tools": "workspace:*", "@deepseek-ai/cordis": "workspace:~", "@deepseek-ai/dsh-working-directory": "workspace:*", - "@deepseek-ai/dsh-fs-local": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-fs-local": "workspace:*" }, "dependencies": {}, "devDependencies": { @@ -50,7 +49,6 @@ "@deepseek-ai/dsh-tools": "workspace:*", "@deepseek-ai/cordis": "workspace:~", "@deepseek-ai/dsh-working-directory": "workspace:*", - "@deepseek-ai/dsh-fs-local": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*" + "@deepseek-ai/dsh-fs-local": "workspace:*" } } diff --git a/packages/test-support/agent-loop-testkit/src/index.ts b/packages/test-support/agent-loop-testkit/src/index.ts index 35872ef213..035fac6c9d 100644 --- a/packages/test-support/agent-loop-testkit/src/index.ts +++ b/packages/test-support/agent-loop-testkit/src/index.ts @@ -1,5 +1,5 @@ /** - * Shared service mounting, real AgentLoop drivers, and structural Inbox stubs + * Shared service mounting, real AgentLoop drivers, and structural Agent stubs * for agent-loop tests. Callers retain ownership of their contexts, adapters, * optional plugins, agents, and teardown. * @module @deepseek-ai/dsh-agent-loop-testkit @@ -9,7 +9,6 @@ import type { Context } from '@deepseek-ai/cordis' import AgentRegistry from '@deepseek-ai/dsh-agent' import type { Agent, AgentOptions, Inbox, InboxTarget } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' -import * as AgentModelPlugin from '@deepseek-ai/dsh-agent-model' import LlmRuntime from '@deepseek-ai/dsh-llm' import SessionStore from '@deepseek-ai/dsh-session' import type { SessionHeader, SessionId, UserMessage } from '@deepseek-ai/dsh-session' @@ -22,6 +21,7 @@ import FsLocal from '@deepseek-ai/dsh-fs-local' import WorkingDirectory from '@deepseek-ai/dsh-working-directory' export { createInboxStub, unsupportedInbox } from './inbox.ts' +export { unsupportedModel } from './model.ts' export { provideWorkingDirectoryFixture } from './working-directory-fixture.ts' interface DriverInbox extends Inbox { @@ -77,7 +77,6 @@ export async function mountAgentLoopTestDependencies( await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(AgentModelPlugin) await ctx.plugin(SystemPrompt, options.systemPrompt ?? {}) if (options.workingDirectory) { if (ctx.get('fs') === undefined) await ctx.plugin(FsLocal) diff --git a/packages/test-support/agent-loop-testkit/src/model.ts b/packages/test-support/agent-loop-testkit/src/model.ts new file mode 100644 index 0000000000..4dc4aeb734 --- /dev/null +++ b/packages/test-support/agent-loop-testkit/src/model.ts @@ -0,0 +1,13 @@ +/** Model placeholder for consumer tests that do not read or change model selection. */ +import type { Model } from '@deepseek-ai/dsh-agent' + +/** + * Create an unsupported Model placeholder for structural Agent stubs. + * @returns a Model whose operations throw at the first unexpected dependency. + */ +export function unsupportedModel(): Model { + const reject = (): never => { + throw new Error('this test Agent does not support model selection') + } + return { current: reject, select: reject } +} diff --git a/packages/test-support/agent-loop-testkit/tsconfig.json b/packages/test-support/agent-loop-testkit/tsconfig.json index becc268c16..08f41e83ce 100644 --- a/packages/test-support/agent-loop-testkit/tsconfig.json +++ b/packages/test-support/agent-loop-testkit/tsconfig.json @@ -40,9 +40,6 @@ }, { "path": "../../fs/fs-local" - }, - { - "path": "../../core/agent-model" } ] } diff --git a/packages/test-support/loader-smoke/package.json b/packages/test-support/loader-smoke/package.json index d9d5876510..87dda55aaf 100644 --- a/packages/test-support/loader-smoke/package.json +++ b/packages/test-support/loader-smoke/package.json @@ -40,7 +40,6 @@ "devDependencies": { "@deepseek-ai/cordis": "workspace:~", "@deepseek-ai/dsh-agent": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*", "@deepseek-ai/dsh-app-boot": "workspace:*", "@deepseek-ai/dsh-llm": "workspace:*", "@deepseek-ai/dsh-session": "workspace:*", diff --git a/packages/test-support/loader-smoke/tests/fixtures/cli-mock-llm.ts b/packages/test-support/loader-smoke/tests/fixtures/cli-mock-llm.ts index a783aa3204..5e4b553d69 100644 --- a/packages/test-support/loader-smoke/tests/fixtures/cli-mock-llm.ts +++ b/packages/test-support/loader-smoke/tests/fixtures/cli-mock-llm.ts @@ -1,5 +1,4 @@ import type { Context } from '@deepseek-ai/cordis' -import type {} from '@deepseek-ai/dsh-agent-model' import { ToolCallId, LlmAdapter, @@ -69,16 +68,16 @@ class CliMockAdapter extends LlmAdapter { } export const name = 'cli-mock-llm' -export const inject = ['llm', 'agentModel', 'tools'] +export const inject = ['llm', 'tools'] /** Register the keyless `cli-mock` adapter and select `off` effort for the step after a tool call. */ export function apply(ctx: Context): void { ctx.llm.registerAdapter(['cli-mock'], new CliMockAdapter()) ctx.on('tools/execute', async (exec, next) => { const result = await next() - const current = exec.agent === undefined ? undefined : ctx.agentModel.resolveForNextStep(exec.agent) + const current = exec.agent === undefined ? undefined : exec.agent.model.current() if (exec.agent !== undefined && current !== undefined) { - ctx.agentModel.select(exec.agent, { provider: current.provider, model: current.model, reasoningEffort: OFF }) + exec.agent.model.select({ provider: current.provider, model: current.model, reasoningEffort: OFF }) } return result }) diff --git a/packages/todo/tool-todo/tests/loader-composition.spec.ts b/packages/todo/tool-todo/tests/loader-composition.spec.ts index 3642d8485f..48332d8a2a 100644 --- a/packages/todo/tool-todo/tests/loader-composition.spec.ts +++ b/packages/todo/tool-todo/tests/loader-composition.spec.ts @@ -17,7 +17,7 @@ import SystemPrompt from '@deepseek-ai/dsh-system-prompt' import ToolRuntime from '@deepseek-ai/dsh-tools' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import * as ToolTodo from '@deepseek-ai/dsh-tool-todo' -import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit' let root: string | undefined let context: Context | undefined @@ -34,7 +34,8 @@ async function agent(ctx: Context): Promise { const id = SessionId('todo-loader-agent') const session = Session.create(id) const value: Agent = { - id, options: {}, session, inbox: unsupportedInbox(), + id, options: {}, session, model: unsupportedModel(), + inbox: unsupportedInbox(), status: 'idle', ctx: scope.ctx, followup: () => {}, steer: () => {}, inject: () => {}, send: () => {}, cancel() {}, runMaintenance: task => task(new AbortController().signal), diff --git a/packages/workflow/tool-workflow/tests/tool-workflow.spec.ts b/packages/workflow/tool-workflow/tests/tool-workflow.spec.ts index 229c1e6eaf..d4c2f3c094 100644 --- a/packages/workflow/tool-workflow/tests/tool-workflow.spec.ts +++ b/packages/workflow/tool-workflow/tests/tool-workflow.spec.ts @@ -1,4 +1,4 @@ -import { provideWorkingDirectoryFixture } from '@deepseek-ai/dsh-agent-loop-testkit' +import { unsupportedModel, provideWorkingDirectoryFixture } from '@deepseek-ai/dsh-agent-loop-testkit' import { describe, expect, it, onTestFinished, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' import Loader from '@deepseek-ai/cordis-plugin-loader' @@ -458,7 +458,8 @@ describe('dsh-tool-workflow', () => { const engine = ctx.workflowEngine as StubEngine const session = Session.create(SessionId('caller')) const parent: Agent = { - id: session.id, options: {}, session, inbox: unsupportedInbox(), status: 'idle', ctx, + id: session.id, options: {}, session, model: unsupportedModel(), + inbox: unsupportedInbox(), status: 'idle', ctx, send: () => {}, followup: () => {}, steer: () => {}, inject: () => {}, cancel: () => {}, runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } @@ -677,7 +678,8 @@ describe('dsh-tool-workflow', () => { await ctx.plugin(toolWorkflow, {}) const session = Session.create(SessionId('caller')) const parent: Agent = { - id: session.id, options: {}, session, inbox: unsupportedInbox(), status: 'idle', ctx, + id: session.id, options: {}, session, model: unsupportedModel(), + inbox: unsupportedInbox(), status: 'idle', ctx, send: () => {}, followup: () => {}, steer: () => {}, inject: () => {}, cancel: () => {}, runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94a65665cd..813ade486a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1021,9 +1021,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../packages/test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../packages/core/agent-model '@deepseek-ai/dsh-agent-preset-registry': specifier: workspace:* version: link:../packages/preset/agent-preset-registry @@ -1201,9 +1198,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../../test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-attachment': specifier: workspace:* version: link:../../attachment/attachment @@ -1540,9 +1534,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../../test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-agent-preset-registry': specifier: workspace:* version: link:../../preset/agent-preset-registry @@ -2289,9 +2280,6 @@ importers: '@deepseek-ai/dsh-agent-loop': specifier: workspace:* version: link:../../core/agent-loop - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-api-gateway': specifier: workspace:* version: link:../../api/gateway @@ -2603,9 +2591,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../../test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-fs': specifier: workspace:* version: link:../../fs/fs @@ -2700,9 +2685,6 @@ importers: '@deepseek-ai/dsh-agent-loop': specifier: workspace:* version: link:../../core/agent-loop - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-attachment-local': specifier: workspace:* version: link:../../attachment/attachment-local @@ -3898,9 +3880,6 @@ importers: '@deepseek-ai/dsh-agent': specifier: workspace:* version: link:../../core/agent - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-api-remotes': specifier: workspace:* version: link:../../api/remotes @@ -4550,9 +4529,6 @@ importers: '@deepseek-ai/dsh-agent': specifier: workspace:* version: link:../../core/agent - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-api-remotes': specifier: workspace:* version: link:../../api/remotes @@ -7003,9 +6979,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../../test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-fs': specifier: workspace:* version: link:../../fs/fs @@ -7220,9 +7193,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../../test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-app-boot': specifier: workspace:* version: link:../../boot/app-boot @@ -7377,9 +7347,6 @@ importers: '@deepseek-ai/dsh-agent': specifier: workspace:* version: link:../agent - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../agent-model '@deepseek-ai/dsh-llm': specifier: workspace:* version: link:../../llm/llm @@ -7411,28 +7378,6 @@ importers: specifier: workspace:* version: link:../tools - packages/core/agent-model: - dependencies: - zod: - specifier: 4.4.3 - version: 4.4.3 - devDependencies: - '@deepseek-ai/cordis': - specifier: workspace:~ - version: link:../../../vendor/cordis - '@deepseek-ai/dsh-agent': - specifier: workspace:* - version: link:../agent - '@deepseek-ai/dsh-llm': - specifier: workspace:* - version: link:../../llm/llm - '@deepseek-ai/dsh-session': - specifier: workspace:* - version: link:../session - '@deepseek-ai/dsh-session-projection': - specifier: workspace:* - version: link:../../session/session-projection - packages/core/agent-tool-presentation: dependencies: '@deepseek-ai/schemastery': @@ -7529,9 +7474,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../../test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../agent-model '@deepseek-ai/dsh-llm': specifier: workspace:* version: link:../../llm/llm @@ -8119,9 +8061,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../../test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-attachment-local': specifier: workspace:* version: link:../../attachment/attachment-local @@ -8210,9 +8149,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../../test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-attachment': specifier: workspace:* version: link:../../attachment/attachment @@ -8274,9 +8210,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../../test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-attachment': specifier: workspace:* version: link:../../attachment/attachment @@ -8343,9 +8276,9 @@ importers: '@deepseek-ai/cordis': specifier: workspace:~ version: link:../../../vendor/cordis - '@deepseek-ai/dsh-agent-model': + '@deepseek-ai/dsh-agent': specifier: workspace:* - version: link:../../core/agent-model + version: link:../../core/agent '@deepseek-ai/dsh-api-session-controller': specifier: workspace:* version: link:../../api/session-controller @@ -8612,9 +8545,6 @@ importers: '@deepseek-ai/dsh-agent-loop': specifier: workspace:* version: link:../../core/agent-loop - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-attachment-local': specifier: workspace:* version: link:../../attachment/attachment-local @@ -8670,9 +8600,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../../test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-attachment': specifier: workspace:* version: link:../../attachment/attachment @@ -8939,9 +8866,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../../test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-app-boot': specifier: workspace:* version: link:../../boot/app-boot @@ -9862,9 +9786,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../../test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-fs-local': specifier: workspace:* version: link:../../fs/fs-local @@ -11474,9 +11395,6 @@ importers: '@deepseek-ai/dsh-agent-loop': specifier: workspace:* version: link:../../core/agent-loop - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-anonymous-user-id': specifier: workspace:* version: link:../../identity/anonymous-user-id @@ -11706,9 +11624,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../../test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-brand': specifier: workspace:* version: link:../../util/brand @@ -12034,9 +11949,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../../test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-commands': specifier: workspace:* version: link:../../interaction/commands @@ -12129,9 +12041,6 @@ importers: '@deepseek-ai/dsh-agent-loop': specifier: workspace:* version: link:../../core/agent-loop - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-agent-preset': specifier: workspace:* version: link:../agent-preset @@ -12528,9 +12437,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../../test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-attachment': specifier: workspace:* version: link:../../attachment/attachment @@ -13127,9 +13033,6 @@ importers: '@deepseek-ai/dsh-agent-loop': specifier: workspace:* version: link:../../core/agent-loop - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-brand': specifier: workspace:* version: link:../../util/brand @@ -13844,9 +13747,6 @@ importers: '@deepseek-ai/cordis-plugin-loader': specifier: workspace:~ version: link:../../../vendor/loader - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-skill': specifier: workspace:* version: link:../skill @@ -14361,9 +14261,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../../test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-agent-preset-registry': specifier: workspace:* version: link:../../preset/agent-preset-registry @@ -14720,9 +14617,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../../test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-llm': specifier: workspace:* version: link:../../llm/llm @@ -14760,9 +14654,6 @@ importers: '@deepseek-ai/dsh-agent-loop-testkit': specifier: workspace:* version: link:../../test-support/agent-loop-testkit - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-bash-local': specifier: workspace:* version: link:../../shell/bash-local @@ -14815,9 +14706,6 @@ importers: '@deepseek-ai/dsh-agent': specifier: workspace:* version: link:../../core/agent - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-agent-preset-registry': specifier: workspace:* version: link:../../preset/agent-preset-registry @@ -15069,9 +14957,6 @@ importers: '@deepseek-ai/dsh-agent-loop': specifier: workspace:* version: link:../../core/agent-loop - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-fs-local': specifier: workspace:* version: link:../../fs/fs-local @@ -15255,9 +15140,6 @@ importers: '@deepseek-ai/dsh-agent': specifier: workspace:* version: link:../../core/agent - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../core/agent-model '@deepseek-ai/dsh-app-boot': specifier: workspace:* version: link:../../boot/app-boot @@ -16056,9 +15938,6 @@ importers: '@deepseek-ai/dsh-agent-loop': specifier: workspace:* version: link:../../packages/core/agent-loop - '@deepseek-ai/dsh-agent-model': - specifier: workspace:* - version: link:../../packages/core/agent-model '@deepseek-ai/dsh-agent-preset-registry': specifier: workspace:* version: link:../../packages/preset/agent-preset-registry diff --git a/python/sdk-runtime/package.json b/python/sdk-runtime/package.json index b223623068..19f2de7e0c 100644 --- a/python/sdk-runtime/package.json +++ b/python/sdk-runtime/package.json @@ -17,7 +17,6 @@ "@deepseek-ai/dsh-agent-default-model": "workspace:*", "@deepseek-ai/dsh-agent-instructions": "workspace:*", "@deepseek-ai/dsh-agent-loop": "workspace:*", - "@deepseek-ai/dsh-agent-model": "workspace:*", "@deepseek-ai/dsh-agent-preset-registry": "workspace:*", "@deepseek-ai/dsh-agent-tool-presentation": "workspace:*", "@deepseek-ai/dsh-anonymous-user-id": "workspace:*", diff --git a/scripts/gen-cordis-catalog.ts b/scripts/gen-cordis-catalog.ts index 2a89e3c996..e7de58f089 100644 --- a/scripts/gen-cordis-catalog.ts +++ b/scripts/gen-cordis-catalog.ts @@ -68,7 +68,6 @@ export const SERVICE_PAGE: Record = { mcpResources: 'mcp.md', agentLoop: 'core.md', agentDefaultModel: 'core.md', - agentModel: 'core.md', agentPresets: 'core.md', agents: 'core.md', approval: 'approval.md', diff --git a/scripts/gen-doc-graphs.ts b/scripts/gen-doc-graphs.ts index e90c5b8d67..94d7ff7c34 100644 --- a/scripts/gen-doc-graphs.ts +++ b/scripts/gen-doc-graphs.ts @@ -641,14 +641,6 @@ const SERVICE_ROLES: ServiceRole[] = [ consumers: ['agent-loop', 'acp', 'subagent'], note: 'Owns live Agent handles, the create/resume factory seam, and process-local initiator propagation.', }, - { - key: 'agentModel', - pkg: 'agent-model', - title: 'Session model selection', - mode: 'core', - consumers: ['agent-loop', 'api-session-controller', 'acp', 'sdk-jsonrpc-server', 'headless'], - note: 'Reads and records Session model choices; owns the modelSelection projection and optional scoped route-switch notices.', - }, { key: 'agentDefaultModel', pkg: 'agent-default-model', @@ -1550,7 +1542,7 @@ function renderEventRelations(pkgs: Pkg[], events: readonly EventEntry[]): strin } lines.push( '', - '## Events outside the current Harness API catalog', + '## Non-harness or undeclared event strings seen in package source', '', renderGeneratedRegion('event-producer-consumer:undeclared', extraRows.join('\n')), ) diff --git a/scripts/no-unknown-casts.baseline.json b/scripts/no-unknown-casts.baseline.json index 7d81a8aed1..b037567323 100644 --- a/scripts/no-unknown-casts.baseline.json +++ b/scripts/no-unknown-casts.baseline.json @@ -245,10 +245,8 @@ }, "packages/api/session-controller/tests/session-cold.host.spec.ts": { "23129d43b519124d7c915c0f1dfd075c1d84579c176678fa8905d0ba02403406": 1, - "36809c8a43a7b9240089e359618107af0a42e948791f0502d6366df65d43208f": 2, "4e05d4d65c076fff556c0dd813f53e90b8c98390825e7293033d8450a8f94ff0": 1, - "526a22860f8bc330caf4a3f28a28948de5c04b4e17125f04b3738808cfbd4e3e": 1, - "a6a04773270367c3ea0f54b577d3f766ff738afb6702e09caac1b3fa66a7397d": 1 + "526a22860f8bc330caf4a3f28a28948de5c04b4e17125f04b3738808cfbd4e3e": 1 }, "packages/api/session-controller/tests/session-fork.host.spec.ts": { "dbbb38fac6189a3be6f7c29f8df6ef34679f5956c168d08d4f058b5d66b24089": 1 diff --git a/scripts/type-equiv.manifest.json b/scripts/type-equiv.manifest.json index 44743d56e9..77d7385f19 100644 --- a/scripts/type-equiv.manifest.json +++ b/scripts/type-equiv.manifest.json @@ -146,6 +146,11 @@ "symbol": "Inbox", "source": "packages/core/agent/src/runtime-types.ts" }, + { + "doc": "docs/subsystems/core.md", + "symbol": "Model", + "source": "packages/core/agent/src/runtime-types.ts" + }, { "doc": "docs/subsystems/core.md", "symbol": "InboxTarget", @@ -2495,12 +2500,12 @@ { "doc": "docs/subsystems/core.md", "symbol": "ModelSelectionProjectionState", - "source": "packages/core/agent-model/src/types.ts" + "source": "packages/core/agent/src/model-selection.ts" }, { "doc": "docs/subsystems/core.md", "symbol": "ModelSelectionProjection", - "source": "packages/core/agent-model/src/types.ts" + "source": "packages/core/agent/src/types.ts" }, { "doc": "docs/subsystems/core.md", diff --git a/snapshots/session/model-switch-compaction/driver.mjs b/snapshots/session/model-switch-compaction/driver.mjs index 580f22bb4f..57b35c8fcd 100644 --- a/snapshots/session/model-switch-compaction/driver.mjs +++ b/snapshots/session/model-switch-compaction/driver.mjs @@ -1,6 +1,6 @@ /** Test-only request routing from a large model to a smaller model after tool execution. */ export const name = 'model-switch-compaction-driver' -export const inject = ['tools', 'agentModel'] +export const inject = ['tools'] /** * Select the smaller model after the first tool call, before the next step prepares. @@ -11,7 +11,7 @@ export function apply(ctx) { const result = await next() if (exec.name === 'bash' && !result.isError) { if (exec.agent === undefined) throw new Error('model-switch compaction requires a calling Agent') - ctx.agentModel.select(exec.agent, { provider: 'deepseek-official', model: 'deepseek-v4-pro' }) + exec.agent.model.select({ provider: 'deepseek-official', model: 'deepseek-v4-pro' }) } return result }) diff --git a/snapshots/session/model-switch-notice/model-switch-driver.mjs b/snapshots/session/model-switch-notice/model-switch-driver.mjs index 7c7af721ee..bf6af90769 100644 --- a/snapshots/session/model-switch-notice/model-switch-driver.mjs +++ b/snapshots/session/model-switch-notice/model-switch-driver.mjs @@ -3,7 +3,7 @@ const SELECTED = { provider: 'deepseek-official', model: 'deepseek-v4-pro', reasoningEffort: 'max' } export const name = 'model-switch-driver' -export const inject = ['tools', 'agentModel'] +export const inject = ['tools'] /** * Record an explicit initial choice and a switch after `todo_write`. @@ -11,10 +11,10 @@ export const inject = ['tools', 'agentModel'] */ export function apply(ctx) { ctx.on('agent/created', ({ agent }) => { - const initial = ctx.agentModel.resolveForNextStep(agent) + const initial = agent.model.current() if (initial === undefined) throw new Error('model-switch driver requires an initial route') - ctx.agentModel.select(agent, initial) - ctx.agentModel.select(agent, initial) + agent.model.select(initial) + agent.model.select(initial) }) ctx.on('agent/pre-step', ({ callConfig }, next) => { if (callConfig.model === SELECTED.model && callConfig.reasoningEffort !== SELECTED.reasoningEffort) { @@ -26,7 +26,7 @@ export function apply(ctx) { const result = await next() if (exec.name === 'todo_write' && !result.isError) { if (exec.agent === undefined) throw new Error('model-switch driver requires a calling Agent') - ctx.agentModel.select(exec.agent, SELECTED) + exec.agent.model.select(SELECTED) } return result }) diff --git a/tsconfig.base.json b/tsconfig.base.json index 8a75bc7d83..2ff0367a9e 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -203,9 +203,6 @@ "@deepseek-ai/dsh-tool-subagent/model-selection-settings": ["./packages/subagent/tool-subagent/src/model-selection-settings.ts"], "@deepseek-ai/dsh-user-approval/types": ["./packages/interaction/user-approval/src/types.ts"], "@deepseek-ai/dsh-user-questions/types": ["./packages/interaction/user-questions/src/types.ts"], - "@deepseek-ai/dsh-agent-model/types": ["./packages/core/agent-model/src/types.ts"], - "@deepseek-ai/dsh-agent-model/notice": ["./packages/core/agent-model/src/notice.ts"], - "@deepseek-ai/dsh-agent-model": ["./packages/core/agent-model/src"], "@deepseek-ai/dsh-agent/types": ["./packages/core/agent/src/types.ts"], "@deepseek-ai/dsh-agent/brand": ["./packages/core/agent/src/brand.ts"], "@deepseek-ai/dsh-headless/startup": ["./packages/bundle/headless/src/startup.ts"], diff --git a/tsconfig.host.json b/tsconfig.host.json index 47a0ec1633..7bb79cebbe 100644 --- a/tsconfig.host.json +++ b/tsconfig.host.json @@ -381,7 +381,6 @@ { "path": "./packages/session/session-telemetry-otel" }, { "path": "./packages/core/system-prompt" }, { "path": "./packages/core/agent" }, - { "path": "./packages/core/agent-model" }, { "path": "./packages/core/agent-default-model" }, { "path": "./packages/interaction/commands" }, { "path": "./packages/goal/goal" },