diff --git a/.agents/notes/implemented/architecture/2026-07-31-claimed-pre-step-inbox-lifecycle.i18n.yaml b/.agents/notes/implemented/architecture/2026-07-31-claimed-pre-step-inbox-lifecycle.i18n.yaml index 3c9a697dd3..84deedb9f9 100644 --- a/.agents/notes/implemented/architecture/2026-07-31-claimed-pre-step-inbox-lifecycle.i18n.yaml +++ b/.agents/notes/implemented/architecture/2026-07-31-claimed-pre-step-inbox-lifecycle.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-07-31-claimed-pre-step-inbox-lifecycle.md -2026-07-31-claimed-pre-step-inbox-lifecycle.md: c46c22cde3e96fb774233d9621acf1abf4ba0c33 -2026-07-31-claimed-pre-step-inbox-lifecycle.zh.md: 51ada096a105a5c97cffd70c2d40782a71beddf7 +2026-07-31-claimed-pre-step-inbox-lifecycle.md: 8149c9df09ff69c017ba66a779d6fd3dfc314193 +2026-07-31-claimed-pre-step-inbox-lifecycle.zh.md: e855b88b26a55dd9f5c33c87a4b59d67496d00ed diff --git a/.agents/notes/implemented/architecture/2026-07-31-claimed-pre-step-inbox-lifecycle.md b/.agents/notes/implemented/architecture/2026-07-31-claimed-pre-step-inbox-lifecycle.md index c46c22cde3..8149c9df09 100644 --- a/.agents/notes/implemented/architecture/2026-07-31-claimed-pre-step-inbox-lifecycle.md +++ b/.agents/notes/implemented/architecture/2026-07-31-claimed-pre-step-inbox-lifecycle.md @@ -18,11 +18,11 @@ Before every proposed step, `Inbox.claim(target)` atomically removes the complet The durable inbox remains two `UserMessage[]` lists addressed by `MessageId`. `append`, `prepend`, and `splice` take a target, while `replace(messageId, newMessage)` and `remove(messageId)` locate the pending message across both lists before committing a normalized splice. Replacement may change identity and emits the old message as discarded followed by the new message as inserted. Every insertion emits `agent/inbox/inserted { message }`; an ordinary removal records `outcome: 'canceled'` and emits `agent/inbox/discarded { message }`. Claiming records pure deletions without an outcome and emits claimed events from Inbox itself. These live events add no placement, outcome, or batch fields. -The two event surfaces have separate consumers. Observers following one message use `agent/inbox/inserted`, `claimed`, and `discarded`. `InboxService` registers the standard `inbox` projection over the durable `agent/inbox/spliced` stream for whole-state consumers and live restoration; UI edits and removals route through an Inbox mutation method so the same projection records every change. +The two event surfaces have separate consumers. Observers following one message use `agent/inbox/inserted`, `claimed`, and `discarded`. `AgentRegistry` contributes the standard `inbox` projection over the durable `agent/inbox/spliced` stream whenever the projection registry is composed; UI edits and removals route through an Inbox mutation method so the same projection records every change. Plugins that need current-step atomic rewriting return messages from `agent/pre-step`. Plugins that only need later context may mutate `agent.inbox` directly. Workspace context uses both paths: asynchronous filesystem projections stage one replaceable `next-step` item, while the next entering pre-step folds that item or a newly composed baseline into its final batch and removes the pending copy. Rejection keeps the item queued. -The archived [addressable queue occurrence decision](../../archived/feature/2026-07-29-addressable-queue-operations.md) describes the superseded occurrence-wrapper design. `MessageId` now owns addressability, while `InboxService` registers `inbox` as the standard session projection over durable splices. The generic projection carrier serves that fold for live updates, history-tail reconnect baselines, and cold process-restart recovery without a live Agent mirror. +The archived [addressable queue occurrence decision](../../archived/feature/2026-07-29-addressable-queue-operations.md) describes the superseded occurrence-wrapper design. `MessageId` owns addressability, while `AgentRegistry` contributes `inbox` as the standard session projection over durable splices. The generic projection carrier serves that fold for live updates, history-tail reconnect baselines, and cold process-restart recovery without a live Agent mirror. ## Alternatives considered diff --git a/.agents/notes/implemented/architecture/2026-07-31-claimed-pre-step-inbox-lifecycle.zh.md b/.agents/notes/implemented/architecture/2026-07-31-claimed-pre-step-inbox-lifecycle.zh.md index 51ada096a1..e855b88b26 100644 --- a/.agents/notes/implemented/architecture/2026-07-31-claimed-pre-step-inbox-lifecycle.zh.md +++ b/.agents/notes/implemented/architecture/2026-07-31-claimed-pre-step-inbox-lifecycle.zh.md @@ -18,11 +18,11 @@ Status: implemented 持久 inbox 仍是两份通过 `MessageId` 寻址的 `UserMessage[]` 列表。`append`、`prepend` 与 `splice` 接受 target;`replace(messageId, newMessage)` 与 `remove(messageId)` 则在提交规范化 splice 前,通过 `MessageId` 跨两份列表定位待处理消息。替换可以改变标识,并先将旧消息作为 discarded 发布,再将新消息作为 inserted 发布。每次插入发出 `agent/inbox/inserted { message }`;普通删除记录 `outcome: 'canceled'` 并发出 `agent/inbox/discarded { message }`。领取记录不带 outcome 的纯删除,并由 Inbox 自行发出 claimed 事件。这些实时事件不增加 placement、outcome 或批次字段。 -两类事件接口服务不同消费方。跟踪单条消息的观察方使用 `agent/inbox/inserted`、`claimed` 与 `discarded`。`InboxService` 在持久 `agent/inbox/spliced` 流上注册标准 `inbox` 投影,供整体状态消费方与 live 恢复使用;UI 编辑与移除通过 Inbox 变更方法处理,从而让同一投影记录所有变化。 +两类事件接口服务不同消费方。跟踪单条消息的观察方使用 `agent/inbox/inserted`、`claimed` 与 `discarded`。`AgentRegistry` 会在投影注册表已组合时,在持久 `agent/inbox/spliced` 流上贡献标准 `inbox` 投影;UI 编辑与移除通过 Inbox 变更方法处理,从而让同一投影记录所有变化。 必须对当前步骤进行原子改写的插件从 `agent/pre-step` 返回消息。只需要稍后上下文的插件可以直接修改 `agent.inbox`。Workspace context 同时使用两条路径:异步文件系统投影会暂存一条可替换的 `next-step` 消息,而下一次进入步骤的 pre-step 会把该消息或新组合的基线折入最终批次,并移除仍待处理的副本。reject 会让该条目继续排队。 -已归档的[可寻址队列项决策](../../archived/feature/2026-07-29-addressable-queue-operations.md)描述了已被取代的单次出现包装层设计。现在由 `MessageId` 负责寻址,而 `InboxService` 把 `inbox` 注册为持久 splice 上的标准会话投影。通用投影传输层会将该折叠结果用于实时更新、历史尾页的重连基线和冷进程重启恢复,无需 live Agent 镜像。 +已归档的[可寻址队列项决策](../../archived/feature/2026-07-29-addressable-queue-operations.md)描述了已被取代的单次出现包装层设计。`MessageId` 负责寻址,而 `AgentRegistry` 把 `inbox` 作为持久 splice 上的标准会话投影贡献给投影注册表。通用投影传输层会将该折叠结果用于实时更新、历史尾页的重连基线和冷进程重启恢复,无需 live Agent 镜像。 ## 曾考虑的替代方案 diff --git a/apps/cli/composition.md b/apps/cli/composition.md index 08e5d4db39..4d37b9186a 100644 --- a/apps/cli/composition.md +++ b/apps/cli/composition.md @@ -30,8 +30,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_inbox["agent-inbox
@deepseek-ai/dsh-agent/inbox"] - cfg --> plugin_dsh_base_agent_inbox plugin_dsh_base_agent_default_model["agent-default-model
@deepseek-ai/dsh-agent-default-model"] cfg --> plugin_dsh_base_agent_default_model plugin_dsh_base_jobs["jobs
@deepseek-ai/dsh-jobs-local"] @@ -181,7 +179,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-inbox` | `@deepseek-ai/dsh-agent/inbox` | | `agent-default-model` | `@deepseek-ai/dsh-agent-default-model` | | `jobs` | `@deepseek-ai/dsh-jobs-local` | | `llm-retry` | `@deepseek-ai/dsh-llm-retry` | diff --git a/apps/cli/tests/fixtures/dsh-badge/snapshot.ts b/apps/cli/tests/fixtures/dsh-badge/snapshot.ts index 87254a8d2d..049123717b 100644 --- a/apps/cli/tests/fixtures/dsh-badge/snapshot.ts +++ b/apps/cli/tests/fixtures/dsh-badge/snapshot.ts @@ -1,6 +1,6 @@ import { fileURLToPath } from 'node:url' import { Context } from '@deepseek-ai/cordis' -import { agentEvents, type Agent } from '@deepseek-ai/dsh-agent' +import { agentEvents, Inbox, type Agent } from '@deepseek-ai/dsh-agent' import { CallId } from '@deepseek-ai/dsh-llm' import { boot, loadOverlayPatches } from '@deepseek-ai/dsh-app-boot' import { SessionId } from '@deepseek-ai/dsh-session' @@ -34,7 +34,7 @@ try { runMaintenance: job => job(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(agent, { inbox: ctx.inboxes.create(agent) }) + Object.assign(agent, { inbox: new Inbox(ctx, agent.session, agentEvents(ctx, agent)) }) const decision = await agentEvents(ctx, agent).waterfall( 'agent/pre-step', { messages: [], turn: 1, step: 1, signal: new AbortController().signal }, diff --git a/docs/capability-seams.i18n.yaml b/docs/capability-seams.i18n.yaml index a7b8b371b1..e696bc3423 100644 --- a/docs/capability-seams.i18n.yaml +++ b/docs/capability-seams.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/capability-seams.md -capability-seams.md: 2f6bc45f9cb60809cedf31f86fcb68785ef80b9a -capability-seams.zh.md: a9eeb06c063a86cdcd6ef3560677129d3adb2798 +capability-seams.md: a84a6d6e0836524e1f39f2fd067ae1f6570741a5 +capability-seams.zh.md: b6b0aba72729f9b9b752347ab00cd76a45c248d9 diff --git a/docs/capability-seams.md b/docs/capability-seams.md index 2f6bc45f9c..a84a6d6e08 100644 --- a/docs/capability-seams.md +++ b/docs/capability-seams.md @@ -31,7 +31,6 @@ flowchart LR pkg_subagent_inprocess["subagent-inprocess"] pkg_invariants["invariants"] pkg_message_feedback["message-feedback"] - svc_inboxes["ctx.inboxes
Durable pending-input facade"] svc_invariants["ctx.invariants
Package-owned invariant registry"] pkg_scope["scope"] pkg_typert_registry["typert-registry"] @@ -202,7 +201,6 @@ flowchart LR svc_cordisInspect["ctx.cordisInspect
Dynamic Cordis inspect registry"] pkg_acp --> svc_approval pkg_agent --> svc_agents - pkg_agent --> svc_inboxes pkg_agent_default_model --> svc_agentDefaultModel pkg_agent_loop --> svc_agentLoop pkg_agent_presets --> svc_agentPresets @@ -329,7 +327,6 @@ flowchart LR svc_e2b --> pkg_fs_e2b svc_e2b --> pkg_subprocess_e2b svc_fs --> pkg_tool_fs - svc_inboxes --> pkg_agent_loop svc_invariants --> pkg_agent svc_invariants --> pkg_agent_loop svc_invariants --> pkg_scope @@ -429,7 +426,6 @@ flowchart LR | `ctx.tokenMeter` | `core` | [`token-meter`](../packages/llm/token-meter) | - | [`compaction-basic`](../packages/compaction/compaction-basic) | - | Owns isolated per-session replay folds; pressure consumers share immutable revisioned measurements. | | `ctx.toolResultPruner` | `core` | [`compaction-tool-result-pruner`](../packages/compaction/compaction-tool-result-pruner) | - | [`compaction-basic`](../packages/compaction/compaction-basic) | - | Rewrites oversized current tool results through replayable single-node surface replacements before summary compaction. | | `ctx.sessions` | `core` | [`session`](../packages/core/session) | - | [`agent-loop`](../packages/core/agent-loop), [`agent`](../packages/core/agent), [`session-persistence`](../packages/session/session-persistence), [`session-query`](../packages/session-query/session-query), [`session-query-sqlite`](../packages/session-query/session-query-sqlite), `subagent-inprocess`, [`invariants`](../packages/runtime-diagnostics/invariants), [`message-feedback`](../packages/feedback/message-feedback) | - | Owns append-only Session instances and emits the durable session event feed. | -| `ctx.inboxes` | `core` | [`agent`](../packages/core/agent) | - | [`agent-loop`](../packages/core/agent-loop) | - | Registers the standard Inbox projection and creates command facades over its sole live state. | | `ctx.invariants` | `core` | [`invariants`](../packages/runtime-diagnostics/invariants) | - | [`session`](../packages/core/session), [`agent`](../packages/core/agent), [`scope`](../packages/core/scope), [`agent-loop`](../packages/core/agent-loop) | - | Companion subpaths register owner-local checks; the service owns selection, uniqueness, child fibers, and package-attributed failures. | | `ctx.typert` | `core` | [`typert-registry`](../packages/typert/registry) | - | [`typert-loader`](../packages/typert/loader), [`api-gateway`](../packages/api/gateway) | - | Plugins register live zod contributions directly or through dsh-typert-loader; the API gateway consumes invocation descriptors and providers, while other runtime consumers query schemas and reflection metadata at their own edges. | | `ctx.typertGateway` | `core` | [`api-gateway`](../packages/api/gateway) | - | - | - | Associates generated Remote descriptors with live Cordis services, resolves registered identities, and exposes unary calls through the shared Connection RPC carrier. | diff --git a/docs/capability-seams.zh.md b/docs/capability-seams.zh.md index a9eeb06c06..b6b0aba727 100644 --- a/docs/capability-seams.zh.md +++ b/docs/capability-seams.zh.md @@ -33,7 +33,6 @@ flowchart LR pkg_subagent_inprocess["subagent-inprocess"] pkg_invariants["invariants"] pkg_message_feedback["message-feedback"] - svc_inboxes["ctx.inboxes
Durable pending-input facade"] svc_invariants["ctx.invariants
Package-owned invariant registry"] pkg_scope["scope"] pkg_typert_registry["typert-registry"] @@ -204,7 +203,6 @@ flowchart LR svc_cordisInspect["ctx.cordisInspect
Dynamic Cordis inspect registry"] pkg_acp --> svc_approval pkg_agent --> svc_agents - pkg_agent --> svc_inboxes pkg_agent_default_model --> svc_agentDefaultModel pkg_agent_loop --> svc_agentLoop pkg_agent_presets --> svc_agentPresets @@ -331,7 +329,6 @@ flowchart LR svc_e2b --> pkg_fs_e2b svc_e2b --> pkg_subprocess_e2b svc_fs --> pkg_tool_fs - svc_inboxes --> pkg_agent_loop svc_invariants --> pkg_agent svc_invariants --> pkg_agent_loop svc_invariants --> pkg_scope @@ -431,7 +428,6 @@ flowchart LR | `ctx.tokenMeter` | `core` | [`token-meter`](../packages/llm/token-meter) | - | [`compaction-basic`](../packages/compaction/compaction-basic) | - | 拥有按会话隔离的回放折叠区;压力消费方共享不可变且带修订版本的测量结果。 | | `ctx.toolResultPruner` | `core` | [`compaction-tool-result-pruner`](../packages/compaction/compaction-tool-result-pruner) | - | [`compaction-basic`](../packages/compaction/compaction-basic) | - | 在摘要压缩前,通过可回放的单节点表层替换来改写过大的当前工具结果。 | | `ctx.sessions` | `core` | [`session`](../packages/core/session) | - | [`agent-loop`](../packages/core/agent-loop), [`agent`](../packages/core/agent), [`session-persistence`](../packages/session/session-persistence), [`session-query`](../packages/session-query/session-query), [`session-query-sqlite`](../packages/session-query/session-query-sqlite), `subagent-inprocess`, [`invariants`](../packages/runtime-diagnostics/invariants), [`message-feedback`](../packages/feedback/message-feedback) | - | 拥有仅追加的 Session 实例,并发出持久的会话事件流。 | -| `ctx.inboxes` | `core` | [`agent`](../packages/core/agent) | - | [`agent-loop`](../packages/core/agent-loop) | - | 注册标准 Inbox 投影,并在其唯一 live 状态上创建命令 facade。 | | `ctx.invariants` | `core` | [`invariants`](../packages/runtime-diagnostics/invariants) | - | [`session`](../packages/core/session), [`agent`](../packages/core/agent), [`scope`](../packages/core/scope), [`agent-loop`](../packages/core/agent-loop) | - | 配套子路径注册所属包本地的检查;该服务负责选择、唯一性、子 fiber,以及标明所属包的失败。 | | `ctx.typert` | `core` | [`typert-registry`](../packages/typert/registry) | - | [`typert-loader`](../packages/typert/loader), [`api-gateway`](../packages/api/gateway) | - | 插件直接或通过 dsh-typert-loader 注册实时 zod 贡献;API 网关消费调用描述符和提供方,其他运行时消费方则在各自边界查询 schema 与反射元数据。 | | `ctx.typertGateway` | `core` | [`api-gateway`](../packages/api/gateway) | - | - | - | 将生成的 Remote 描述符与实时 Cordis 服务关联,解析已注册的身份,并通过共享的 Connection RPC 载体提供一元调用。 | diff --git a/docs/config-catalog.i18n.yaml b/docs/config-catalog.i18n.yaml index 41dc01f906..8070261be3 100644 --- a/docs/config-catalog.i18n.yaml +++ b/docs/config-catalog.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/config-catalog.md -config-catalog.md: b35358c651454ed42a9c0cfd466f305c7d632e70 -config-catalog.zh.md: f6bb73d14e3da28e05628d7b1d7c29d053382f24 +config-catalog.md: 453065bec7ddc373627f9f632445db7e16b56f59 +config-catalog.zh.md: 2c2a9d84f051e79437493490af63cea70358c300 diff --git a/docs/config-catalog.md b/docs/config-catalog.md index b35358c651..453065bec7 100644 --- a/docs/config-catalog.md +++ b/docs/config-catalog.md @@ -136,7 +136,7 @@ Source: [`packages/context/agent-instructions/src/config.ts:18`](../packages/con ## `@deepseek-ai/dsh-agent-loop` -Requires: `agents` · `inboxes` · `sessions` · `llm` · `tools` · `systemPrompt` +Requires: `agents` · `sessionProjections` · `sessions` · `llm` · `tools` · `systemPrompt` ```ts config-catalog /** Agent-loop plugin configuration. */ @@ -292,7 +292,7 @@ export interface GoalConfig { Depends on: [`AgentLoopConfig`](#deepseek-aidsh-agent-loop) · [`GoalDomainConfig`](#deepseek-aidsh-goal) · [`InvariantConfig`](#deepseek-aidsh-invariants) · [`JobsConfig`](#deepseek-aidsh-jobs-local) · [`SessionTitleConfig`](#deepseek-aidsh-session-title) · [`SkillFileSystem`](../packages/skill/skill-filesystem/src/index.ts) · [`SkillRegistryConfig`](#deepseek-aidsh-skill) · [`SystemPromptConfig`](#deepseek-aidsh-system-prompt) · [`toolBash`](../packages/shell/tool-bash/src/index.ts) · [`toolGoal`](../packages/goal/tool-goal/src/index.ts) · [`toolJobs`](../packages/jobs/tool-jobs/src/index.ts) · [`ToolsConfig`](#deepseek-aidsh-tools) · [`toolSkill`](../packages/skill/tool-skill/src/index.ts) · [`workspaceContext`](../packages/context/agent-instructions/src/index.ts) -Source: [`packages/examples/agent-spine-demo/src/index.ts:94`](../packages/examples/agent-spine-demo/src/index.ts) +Source: [`packages/examples/agent-spine-demo/src/index.ts:93`](../packages/examples/agent-spine-demo/src/index.ts) diff --git a/docs/config-catalog.zh.md b/docs/config-catalog.zh.md index f6bb73d14e..2c2a9d84f0 100644 --- a/docs/config-catalog.zh.md +++ b/docs/config-catalog.zh.md @@ -138,7 +138,7 @@ export interface Config { ## `@deepseek-ai/dsh-agent-loop` -需要:`agents` · `inboxes` · `sessions` · `llm` · `tools` · `systemPrompt` +需要:`agents` · `sessionProjections` · `sessions` · `llm` · `tools` · `systemPrompt` ```ts config-catalog /** Agent-loop plugin configuration. */ @@ -294,7 +294,7 @@ export interface GoalConfig { 依赖:[`AgentLoopConfig`](#deepseek-aidsh-agent-loop) · [`GoalDomainConfig`](#deepseek-aidsh-goal) · [`InvariantConfig`](#deepseek-aidsh-invariants) · [`JobsConfig`](#deepseek-aidsh-jobs-local) · [`SessionTitleConfig`](#deepseek-aidsh-session-title) · [`SkillFileSystem`](../packages/skill/skill-filesystem/src/index.ts) · [`SkillRegistryConfig`](#deepseek-aidsh-skill) · [`SystemPromptConfig`](#deepseek-aidsh-system-prompt) · [`toolBash`](../packages/shell/tool-bash/src/index.ts) · [`toolGoal`](../packages/goal/tool-goal/src/index.ts) · [`toolJobs`](../packages/jobs/tool-jobs/src/index.ts) · [`ToolsConfig`](#deepseek-aidsh-tools) · [`toolSkill`](../packages/skill/tool-skill/src/index.ts) · [`workspaceContext`](../packages/context/agent-instructions/src/index.ts) -来源:[`packages/examples/agent-spine-demo/src/index.ts:94`](../packages/examples/agent-spine-demo/src/index.ts) +来源:[`packages/examples/agent-spine-demo/src/index.ts:93`](../packages/examples/agent-spine-demo/src/index.ts) diff --git a/docs/module-graph.i18n.yaml b/docs/module-graph.i18n.yaml index 358a4e3b08..1d36b48b41 100644 --- a/docs/module-graph.i18n.yaml +++ b/docs/module-graph.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/module-graph.md -module-graph.md: 4fa25899f83f2140a8e28af123ed5ff202a437e4 -module-graph.zh.md: ba604a3ee7462584cd2d2324ee90936502ee35f9 +module-graph.md: c9b0d3dd7305bf0444c777f21468c3baa27d12d9 +module-graph.zh.md: b3eaf5a47cb5267fcbaa45017ea932948eaec68d diff --git a/docs/module-graph.md b/docs/module-graph.md index 4fa25899f8..c9b0d3dd73 100644 --- a/docs/module-graph.md +++ b/docs/module-graph.md @@ -759,6 +759,7 @@ flowchart TD pkg_agent_loop --> pkg_scope pkg_agent_loop --> pkg_session pkg_agent_loop --> pkg_session_persistence + pkg_agent_loop --> pkg_session_projection pkg_agent_loop --> pkg_settings pkg_agent_loop --> pkg_system_prompt pkg_agent_loop --> pkg_tools @@ -1580,7 +1581,7 @@ flowchart TD | [`pwsh-sandbox`](../packages/shell/pwsh-sandbox) | `shell` | [`invariants`](../packages/runtime-diagnostics/invariants), [`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), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`subprocess`](../packages/subprocess/subprocess), [`terminal`](../packages/terminal/terminal) | | [`token-meter`](../packages/llm/token-meter) | `llm` | [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection) | -| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`settings`](../packages/settings/settings), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) | +| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`settings`](../packages/settings/settings), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) | | [`agent-tool-presentation`](../packages/core/agent-tool-presentation) | `core` | [`invariants`](../packages/runtime-diagnostics/invariants), [`tools`](../packages/core/tools) | | [`tool-goal`](../packages/goal/tool-goal) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`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), [`invariants`](../packages/runtime-diagnostics/invariants), [`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) | diff --git a/docs/module-graph.zh.md b/docs/module-graph.zh.md index ba604a3ee7..b3eaf5a47c 100644 --- a/docs/module-graph.zh.md +++ b/docs/module-graph.zh.md @@ -760,6 +760,7 @@ flowchart TD pkg_agent_loop --> pkg_scope pkg_agent_loop --> pkg_session pkg_agent_loop --> pkg_session_persistence + pkg_agent_loop --> pkg_session_projection pkg_agent_loop --> pkg_settings pkg_agent_loop --> pkg_system_prompt pkg_agent_loop --> pkg_tools @@ -1581,7 +1582,7 @@ flowchart TD | [`pwsh-sandbox`](../packages/shell/pwsh-sandbox) | `shell` | [`invariants`](../packages/runtime-diagnostics/invariants), [`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), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`subprocess`](../packages/subprocess/subprocess), [`terminal`](../packages/terminal/terminal) | | [`token-meter`](../packages/llm/token-meter) | `llm` | [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection) | -| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`settings`](../packages/settings/settings), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) | +| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`settings`](../packages/settings/settings), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) | | [`agent-tool-presentation`](../packages/core/agent-tool-presentation) | `core` | [`invariants`](../packages/runtime-diagnostics/invariants), [`tools`](../packages/core/tools) | | [`tool-goal`](../packages/goal/tool-goal) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`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), [`invariants`](../packages/runtime-diagnostics/invariants), [`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) | diff --git a/docs/subsystems/core.i18n.yaml b/docs/subsystems/core.i18n.yaml index 022720323d..ce3293a461 100644 --- a/docs/subsystems/core.i18n.yaml +++ b/docs/subsystems/core.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/subsystems/core.md -core.md: 6e919d02ac1c23cf4f49e92e5ce50a8983ac449d -core.zh.md: 853d9500cd7e51b02ec2f9595bd7f294f7b1d66f +core.md: b978cfdc102c93e0939dfc48c24947c45c96ee6f +core.zh.md: 0dfef5b75ef9273884afe6e038a83e7e5785d9bc diff --git a/docs/subsystems/core.md b/docs/subsystems/core.md index 6e919d02ac..b978cfdc10 100644 --- a/docs/subsystems/core.md +++ b/docs/subsystems/core.md @@ -175,7 +175,7 @@ The inbox is the delivery vocabulary — two ordered pending-message lists the a type InboxTarget = 'next-turn' | 'next-step' ``` -Every pending occurrence is its `UserMessage`; `MessageId` is the sole identity. `Inbox.append`, `prepend`, `replace`, `remove`, `clear`, `splice`, and `claim` record normalized durable `agent/inbox/spliced` mutations and reject duplicate pending ids. `replace(messageId, newMessage)` and `remove(messageId)` locate the pending message across both lists; replacement may change identity and emits the old message as discarded followed by the new message as inserted. Ordinary removals and `clear()` are cancellations. `claim(target)` removes the proposed step batch — all `next-step` input plus, at a turn boundary, one `next-turn` message — through pure deletion splices without emitting discarded notifications, then Inbox emits per-message claimed notifications. `InboxService` registers the standard `inbox` projection; its registry cell is the sole live state and the same fold serves cold consumers. Consumers following one message use the exact `agent/inbox/inserted`, `claimed`, and `discarded` notifications. +Every pending occurrence is its `UserMessage`; `MessageId` is the sole identity. `Inbox.append`, `prepend`, `replace`, `remove`, `clear`, `splice`, and `claim` record normalized durable `agent/inbox/spliced` mutations and reject duplicate pending ids. `replace(messageId, newMessage)` and `remove(messageId)` locate the pending message across both lists; replacement may change identity and emits the old message as discarded followed by the new message as inserted. Ordinary removals and `clear()` are cancellations. `claim(target)` removes the proposed step batch — all `next-step` input plus, at a turn boundary, one `next-turn` message — through pure deletion splices without emitting discarded notifications, then Inbox emits per-message claimed notifications. `AgentRegistry` contributes the standard `inbox` projection whenever the projection registry is composed; its cell is the sole live state and the same fold serves cold consumers. Consumers following one message use the exact `agent/inbox/inserted`, `claimed`, and `discarded` notifications. Cancellation: @@ -720,24 +720,7 @@ list(): Agent[] roots(): Agent[] ``` -Source: [`packages/core/agent/src/index.ts:256`](../../packages/core/agent/src/index.ts) - - - -### `ctx.inboxes` — `InboxService` - -Root Inbox service: creates live inboxes and owns their durable projection. - -```ts cordis-catalog -/** - * Restore one live Inbox for an agent and publish its committed mutations. - * @param agent - agent that owns the durable session and live Inbox events. - * @returns the restored Inbox. - */ -create(agent: Agent): Inbox -``` - -Source: [`packages/core/agent/src/inbox.ts:25`](../../packages/core/agent/src/inbox.ts) +Source: [`packages/core/agent/src/index.ts:259`](../../packages/core/agent/src/index.ts) diff --git a/docs/subsystems/core.zh.md b/docs/subsystems/core.zh.md index 853d9500cd..0dfef5b75e 100644 --- a/docs/subsystems/core.zh.md +++ b/docs/subsystems/core.zh.md @@ -179,7 +179,7 @@ inbox 即投递词汇——agent 以持久投影形式拥有的两条有序待 type InboxTarget = 'next-turn' | 'next-step' ``` -每个待处理入队项就是其 `UserMessage`;`MessageId` 是唯一标识。`Inbox.append`、`prepend`、`replace`、`remove`、`clear`、`splice` 与 `claim` 会记录规范化的持久 `agent/inbox/spliced` 变更,并拒绝重复的待处理 id。`replace(messageId, newMessage)` 与 `remove(messageId)` 通过 `MessageId` 跨两份列表定位待处理消息;替换可以改变标识,并先将旧消息作为 discarded 发布,再将新消息作为 inserted 发布。普通删除和 `clear()` 都表示取消。`claim(target)` 通过纯删除 splice 移除拟进入步骤的批次——全部 `next-step` 输入,外加轮次边界上的一条 `next-turn` 消息——且不发出 discarded 通知,随后由 Inbox 逐条发出 claimed 通知。`InboxService` 注册标准 `inbox` 投影;其注册表 cell 是唯一 live 状态,同一份折叠也服务于冷消费方。跟踪单条消息的消费方使用精确的 `agent/inbox/inserted`、`claimed` 与 `discarded` 通知。 +每个待处理入队项就是其 `UserMessage`;`MessageId` 是唯一标识。`Inbox.append`、`prepend`、`replace`、`remove`、`clear`、`splice` 与 `claim` 会记录规范化的持久 `agent/inbox/spliced` 变更,并拒绝重复的待处理 id。`replace(messageId, newMessage)` 与 `remove(messageId)` 通过 `MessageId` 跨两份列表定位待处理消息;替换可以改变标识,并先将旧消息作为 discarded 发布,再将新消息作为 inserted 发布。普通删除和 `clear()` 都表示取消。`claim(target)` 通过纯删除 splice 移除拟进入步骤的批次——全部 `next-step` 输入,外加轮次边界上的一条 `next-turn` 消息——且不发出 discarded 通知,随后由 Inbox 逐条发出 claimed 通知。`AgentRegistry` 会在投影注册表已组合时贡献标准 `inbox` 投影;其 cell 是唯一 live 状态,同一份折叠也服务于冷消费方。跟踪单条消息的消费方使用精确的 `agent/inbox/inserted`、`claimed` 与 `discarded` 通知。 取消: @@ -728,24 +728,7 @@ list(): Agent[] roots(): Agent[] ``` -Source: [`packages/core/agent/src/index.ts:256`](../../packages/core/agent/src/index.ts) - - - -### `ctx.inboxes` — `InboxService` - -Root Inbox service: creates live inboxes and owns their durable projection. - -```ts cordis-catalog -/** - * Restore one live Inbox for an agent and publish its committed mutations. - * @param agent - agent that owns the durable session and live Inbox events. - * @returns the restored Inbox. - */ -create(agent: Agent): Inbox -``` - -Source: [`packages/core/agent/src/inbox.ts:25`](../../packages/core/agent/src/inbox.ts) +Source: [`packages/core/agent/src/index.ts:259`](../../packages/core/agent/src/index.ts) diff --git a/examples/headless-agent/tests/code-mode.e2e.ts b/examples/headless-agent/tests/code-mode.e2e.ts index 64413599d6..cbab8b3f39 100644 --- a/examples/headless-agent/tests/code-mode.e2e.ts +++ b/examples/headless-agent/tests/code-mode.e2e.ts @@ -11,7 +11,6 @@ import SystemPrompt from '@deepseek-ai/dsh-system-prompt' import ToolRuntime, { RUN_CODE_NAME, defineTool } from '@deepseek-ai/dsh-tools' import type { ToolExecutionResult } from '@deepseek-ai/dsh-tools' import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent' -import InboxService from '@deepseek-ai/dsh-agent/inbox' import AgentLoop from '@deepseek-ai/dsh-agent-loop' import { LocalBashExecutor } from '@deepseek-ai/dsh-bash-local' @@ -56,7 +55,6 @@ async function codeModeHarness(cwd: string): Promise { await harness.plugin(LlmRuntime) await harness.plugin(SessionStore) await harness.plugin(SessionProjectionRegistry) - await harness.plugin(InboxService) await harness.plugin(SystemPrompt, { persona: PERSONA }) await harness.plugin(ToolRuntime, { mode: 'code' }) await harness.plugin(AgentRegistry) @@ -75,7 +73,6 @@ async function workspaceCodeModeHarness(): Promise { await harness.plugin(LlmRuntime) await harness.plugin(SessionStore) await harness.plugin(SessionProjectionRegistry) - await harness.plugin(InboxService) await harness.plugin(SystemPrompt, { persona: PERSONA }) await harness.plugin(ToolRuntime, { mode: 'code' }) await harness.plugin(AgentRegistry) diff --git a/examples/headless-agent/tests/fixtures/e2b/e2b/bin.ts b/examples/headless-agent/tests/fixtures/e2b/e2b/bin.ts index 9446fcb7b2..54e71feefd 100644 --- a/examples/headless-agent/tests/fixtures/e2b/e2b/bin.ts +++ b/examples/headless-agent/tests/fixtures/e2b/e2b/bin.ts @@ -1,7 +1,7 @@ import { readFile } from 'node:fs/promises' import { resolve } from 'node:path' import { boot } from '@deepseek-ai/dsh-app-boot' -import type { Agent } from '@deepseek-ai/dsh-agent' +import { agentEvents, Inbox, type Agent } from '@deepseek-ai/dsh-agent' import { Session, SessionId } from '@deepseek-ai/dsh-session' import type {} from '@deepseek-ai/dsh-fs-e2b' import type {} from '@deepseek-ai/dsh-bash-local' @@ -30,7 +30,7 @@ const owner: Agent = { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } -Object.assign(owner, { inbox: ctx.inboxes.create(owner) }) +Object.assign(owner, { inbox: new Inbox(ctx, owner.session, agentEvents(ctx, owner)) }) const unregisterOwner = ctx.agents.register(owner) let terminalId: Awaited>['sessionId'] | undefined try { diff --git a/packages/bundle/base/cordis.patch.yml b/packages/bundle/base/cordis.patch.yml index 6e1656e3f3..e9567d9206 100644 --- a/packages/bundle/base/cordis.patch.yml +++ b/packages/bundle/base/cordis.patch.yml @@ -58,9 +58,6 @@ - id: agent name: '@deepseek-ai/dsh-agent' - - id: agent-inbox - name: '@deepseek-ai/dsh-agent/inbox' - # The transport-independent default for Agents created by entry points. # Settings may supply a saved selection; consumers read it at creation time. - id: agent-default-model diff --git a/packages/bundle/headless/tests/headless.spec.ts b/packages/bundle/headless/tests/headless.spec.ts index 6e69d8c5d3..3cfffe7d20 100644 --- a/packages/bundle/headless/tests/headless.spec.ts +++ b/packages/bundle/headless/tests/headless.spec.ts @@ -2,8 +2,7 @@ import { afterEach, describe, expect, it } from 'vitest' import { Context } from '@deepseek-ai/cordis' -import AgentRegistry from '@deepseek-ai/dsh-agent' -import InboxService from '@deepseek-ai/dsh-agent/inbox' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent, AgentHandle, CreateAgentOptions } from '@deepseek-ai/dsh-agent' import AgentDefaultModelConfig from '@deepseek-ai/dsh-agent-default-model' import { createAssistantMessage } from '@deepseek-ai/dsh-llm' @@ -57,7 +56,6 @@ async function bench(script: Script): Promise<{ const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(AgentRegistry) await ctx.plugin(AgentDefaultModelConfig, { provider: 'test-provider', model: 'test-model' }) ctx.agents.setFactory({ @@ -86,7 +84,7 @@ async function bench(script: Script): Promise<{ inject: () => {}, whenIdle: () => idle, } satisfies Partial) - Object.assign(agent, { inbox: ctx.inboxes.create(agent) }) + Object.assign(agent, { inbox: new Inbox(ctx, agent.session, agentEvents(ctx, agent)) }) await options.setup?.(agentCtx) script.before?.(session) ctx.agents.register(agent) diff --git a/packages/context/agent-instructions/tests/agent-instructions.spec.ts b/packages/context/agent-instructions/tests/agent-instructions.spec.ts index 178667dae1..b6cab01c38 100644 --- a/packages/context/agent-instructions/tests/agent-instructions.spec.ts +++ b/packages/context/agent-instructions/tests/agent-instructions.spec.ts @@ -7,8 +7,7 @@ import Loader from '@deepseek-ai/cordis-plugin-loader' import * as workspaceContext from '@deepseek-ai/dsh-agent-instructions' import LlmRuntime, { createUserMessage, CallId, type Message, type StreamChunk } from '@deepseek-ai/dsh-llm' import SessionStore, { SessionId, type SessionEvent, type UserMessage } from '@deepseek-ai/dsh-session' -import AgentRegistry, { agentEvents, type Agent } from '@deepseek-ai/dsh-agent' -import InboxService from '@deepseek-ai/dsh-agent/inbox' +import AgentRegistry, { agentEvents, Inbox, type Agent } from '@deepseek-ai/dsh-agent' import AgentLoop from '@deepseek-ai/dsh-agent-loop' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { FileSystem, FsTargetKey, FsVersion } from '@deepseek-ai/dsh-fs' @@ -52,7 +51,7 @@ const testToolSignal = new AbortController().signal const isolatedInboxCtx = new Context() await isolatedInboxCtx.plugin(SessionStore) await isolatedInboxCtx.plugin(SessionProjectionRegistry) -await isolatedInboxCtx.plugin(InboxService) +await isolatedInboxCtx.plugin(AgentRegistry) let nextStubSession = 1 async function tempRepo(): Promise { @@ -209,7 +208,7 @@ function stubAgent(cwd?: string, seed: SessionEvent[] = []): Agent { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(agent, { inbox: agentCtx.inboxes.create(agent) }) + Object.assign(agent, { inbox: new Inbox(agentCtx, agent.session, agentEvents(agentCtx, agent)) }) return agent } @@ -2523,7 +2522,6 @@ describe('dynamic nested workspace context injection', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/context/time-context/tests/time-context.spec.ts b/packages/context/time-context/tests/time-context.spec.ts index 581bf0ca8c..1c06c7fdee 100644 --- a/packages/context/time-context/tests/time-context.spec.ts +++ b/packages/context/time-context/tests/time-context.spec.ts @@ -51,7 +51,7 @@ function sessionAgent(session: Session, id = 'agent'): Agent { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(agent, { inbox: new Inbox(agent.ctx, agent) }) + Object.assign(agent, { inbox: new Inbox(agent.ctx, agent.session, agentEvents(agent.ctx, agent)) }) return agent } diff --git a/packages/context/tmux-context/tests/tmux-context.spec.ts b/packages/context/tmux-context/tests/tmux-context.spec.ts index 6f02f08e74..9a2066eb16 100644 --- a/packages/context/tmux-context/tests/tmux-context.spec.ts +++ b/packages/context/tmux-context/tests/tmux-context.spec.ts @@ -107,7 +107,7 @@ function sessionAgent(session: Session, id = 'agent'): Agent { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(agent, { inbox: new Inbox(agent.ctx, agent) }) + Object.assign(agent, { inbox: new Inbox(agent.ctx, agent.session, agentEvents(agent.ctx, agent)) }) return agent } diff --git a/packages/core/agent-loop/package.json b/packages/core/agent-loop/package.json index 76f7884b72..a5e615b30e 100644 --- a/packages/core/agent-loop/package.json +++ b/packages/core/agent-loop/package.json @@ -37,6 +37,7 @@ "@deepseek-ai/dsh-scope": "workspace:^", "@deepseek-ai/dsh-session": "workspace:^", "@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/cordis": "workspace:^", diff --git a/packages/core/agent-loop/src/agent.ts b/packages/core/agent-loop/src/agent.ts index 1950195663..404ecdd7fe 100644 --- a/packages/core/agent-loop/src/agent.ts +++ b/packages/core/agent-loop/src/agent.ts @@ -11,12 +11,11 @@ import type { AgentOptions, AgentStatus, CancelOptions, - Inbox, InboxTarget, PreStepDecision, RequestErrorAction, } from '@deepseek-ai/dsh-agent' -import { agentEvents, assembleContextFor } from '@deepseek-ai/dsh-agent' +import { Inbox, agentEvents, assembleContextFor } from '@deepseek-ai/dsh-agent' import type { GenerateOptions, LlmCallConfig, Message, PreparedLlmCall } from '@deepseek-ai/dsh-llm' import { BlockAssembler, @@ -85,7 +84,7 @@ export class ReactLoopAgent implements Agent { public readonly session: Session, ) { this.dispatch = agentEvents(loopCtx, this) - this.inbox = loopCtx.inboxes.create(this) + this.inbox = new Inbox(loopCtx, session, this.dispatch) const lastTurn = session.events.findLast(event => event.type === 'turn/start')?.data.turn ?? 0 this.phase = { kind: 'idle', lastTurn } this.scope = createScope(loopCtx, this) diff --git a/packages/core/agent-loop/src/index.ts b/packages/core/agent-loop/src/index.ts index 79bb469f25..6aad9745b6 100644 --- a/packages/core/agent-loop/src/index.ts +++ b/packages/core/agent-loop/src/index.ts @@ -294,7 +294,7 @@ function validateConfiguredAgents(agents: Config['agents']): void { /** Concrete agent factory and driver service. */ export class AgentLoop extends Service implements AgentFactory { - static inject = ['agents', 'inboxes', 'sessions', 'llm', 'tools', 'systemPrompt'] + static inject = ['agents', 'sessionProjections', 'sessions', 'llm', 'tools', 'systemPrompt'] /** Runtime schema for declarative agents. */ static Config = z.object({ diff --git a/packages/core/agent-loop/tests/agent-initiator.spec.ts b/packages/core/agent-loop/tests/agent-initiator.spec.ts index a5ca4cd36f..b69f5a6739 100644 --- a/packages/core/agent-loop/tests/agent-initiator.spec.ts +++ b/packages/core/agent-loop/tests/agent-initiator.spec.ts @@ -1,4 +1,3 @@ -import InboxService from '@deepseek-ai/dsh-agent/inbox' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { describe, expect, it } from 'vitest' import { Context, type Fiber } from '@deepseek-ai/cordis' @@ -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(InboxService) 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(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -387,7 +384,6 @@ describe('AgentLoop initiator scope', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) 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 ea933ff374..4e6aa9b4df 100644 --- a/packages/core/agent-loop/tests/agent.spec.ts +++ b/packages/core/agent-loop/tests/agent.spec.ts @@ -1,4 +1,3 @@ -import InboxService from '@deepseek-ai/dsh-agent/inbox' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { createUserMessage } from '@deepseek-ai/dsh-llm' import { describe, expect, it, vi } from 'vitest' @@ -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(InboxService) 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 de44998536..58060a5cf9 100644 --- a/packages/core/agent-loop/tests/cancel.spec.ts +++ b/packages/core/agent-loop/tests/cancel.spec.ts @@ -1,4 +1,3 @@ -import InboxService from '@deepseek-ai/dsh-agent/inbox' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { CallId, createUserMessage } from '@deepseek-ai/dsh-llm' /** @@ -28,7 +27,6 @@ async function harness(adapter: MockAdapter) { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -674,7 +672,6 @@ describe('Agent.cancel()', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) 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 5b043f6277..96c1d03022 100644 --- a/packages/core/agent-loop/tests/config-session-id.spec.ts +++ b/packages/core/agent-loop/tests/config-session-id.spec.ts @@ -1,4 +1,3 @@ -import InboxService from '@deepseek-ai/dsh-agent/inbox' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { createUserMessage } from '@deepseek-ai/dsh-llm' import { afterEach, describe, expect, it, vi } from 'vitest' @@ -32,7 +31,6 @@ async function makeCoreContext(): Promise { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -332,7 +330,6 @@ describe('config-driven session id', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -359,7 +356,6 @@ describe('config-driven session id', () => { await ctx1.plugin(LlmRuntime) await ctx1.plugin(SessionStore) await ctx1.plugin(SessionProjectionRegistry) - await ctx1.plugin(InboxService) await ctx1.plugin(SystemPrompt) await ctx1.plugin(ToolRuntime) await ctx1.plugin(AgentRegistry) @@ -380,7 +376,6 @@ describe('config-driven session id', () => { await ctx2.plugin(LlmRuntime) await ctx2.plugin(SessionStore) await ctx2.plugin(SessionProjectionRegistry) - await ctx2.plugin(InboxService) await ctx2.plugin(SystemPrompt) await ctx2.plugin(ToolRuntime) await ctx2.plugin(AgentRegistry) @@ -406,7 +401,6 @@ describe('config-driven session id', () => { await ctx1.plugin(LlmRuntime) await ctx1.plugin(SessionStore) await ctx1.plugin(SessionProjectionRegistry) - await ctx1.plugin(InboxService) await ctx1.plugin(SystemPrompt) await ctx1.plugin(ToolRuntime) await ctx1.plugin(AgentRegistry) @@ -424,7 +418,6 @@ describe('config-driven session id', () => { await ctx2.plugin(LlmRuntime) await ctx2.plugin(SessionStore) await ctx2.plugin(SessionProjectionRegistry) - await ctx2.plugin(InboxService) await ctx2.plugin(SystemPrompt) await ctx2.plugin(ToolRuntime) await ctx2.plugin(AgentRegistry) @@ -451,7 +444,6 @@ describe('config-driven session id', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) 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 a2b93a5fc0..cd30160999 100644 --- a/packages/core/agent-loop/tests/contract-regressions.spec.ts +++ b/packages/core/agent-loop/tests/contract-regressions.spec.ts @@ -1,4 +1,3 @@ -import InboxService from '@deepseek-ai/dsh-agent/inbox' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { describe, expect, it } from 'vitest' import { Context } from '@deepseek-ai/cordis' @@ -33,7 +32,6 @@ async function harness(adapter: MockAdapter) { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -530,7 +528,6 @@ describe('turn numbering continues across seeded sessions', () => { await ctx2.plugin(LlmRuntime) await ctx2.plugin(SessionStore) await ctx2.plugin(SessionProjectionRegistry) - await ctx2.plugin(InboxService) await ctx2.plugin(SystemPrompt) await ctx2.plugin(ToolRuntime) await ctx2.plugin(AgentRegistry) @@ -683,7 +680,6 @@ describe('turn and step boundary recovery', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -1117,7 +1113,6 @@ describe('disposal and cancellation during pre-step assembly', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -1169,7 +1164,6 @@ describe('disposal and cancellation during pre-step assembly', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -1221,7 +1215,6 @@ describe('disposal and cancellation during pre-step assembly', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -1269,7 +1262,6 @@ describe('disposal and cancellation during pre-step assembly', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -1319,7 +1311,6 @@ describe('disposal and cancellation during pre-step assembly', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) 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 85cc720fed..6fc28f6400 100644 --- a/packages/core/agent-loop/tests/coverage-edges.spec.ts +++ b/packages/core/agent-loop/tests/coverage-edges.spec.ts @@ -1,4 +1,3 @@ -import InboxService from '@deepseek-ai/dsh-agent/inbox' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { describe, expect, it } from 'vitest' import { Context } from '@deepseek-ai/cordis' @@ -21,7 +20,6 @@ async function harness(adapter: MockAdapter) { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/interception.spec.ts b/packages/core/agent-loop/tests/interception.spec.ts index 60a3be7d01..5116c778ce 100644 --- a/packages/core/agent-loop/tests/interception.spec.ts +++ b/packages/core/agent-loop/tests/interception.spec.ts @@ -1,4 +1,3 @@ -import InboxService from '@deepseek-ai/dsh-agent/inbox' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' @@ -34,7 +33,6 @@ async function harness(adapter: MockAdapter) { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) 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 7515e88c91..5c23522f16 100644 --- a/packages/core/agent-loop/tests/loop.spec.ts +++ b/packages/core/agent-loop/tests/loop.spec.ts @@ -1,4 +1,3 @@ -import InboxService from '@deepseek-ai/dsh-agent/inbox' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { describe, expect, it } from 'vitest' import { Context } from '@deepseek-ai/cordis' @@ -20,7 +19,6 @@ async function harness(adapter: MockAdapter, persona = '') { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt, { persona }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -1454,7 +1452,6 @@ describe('agent loop', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -1480,7 +1477,6 @@ describe('agent loop', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/properties.spec.ts b/packages/core/agent-loop/tests/properties.spec.ts index 90e733c19a..87755f646b 100644 --- a/packages/core/agent-loop/tests/properties.spec.ts +++ b/packages/core/agent-loop/tests/properties.spec.ts @@ -1,4 +1,3 @@ -import InboxService from '@deepseek-ai/dsh-agent/inbox' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' /** * Property-based tests for the agent loop's inbox/turn scheduling (the @@ -42,7 +41,6 @@ async function harness() { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) 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 a14c5f4bae..64394855f0 100644 --- a/packages/core/agent-loop/tests/request-cache.e2e.ts +++ b/packages/core/agent-loop/tests/request-cache.e2e.ts @@ -6,7 +6,6 @@ import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' 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 InboxService from '@deepseek-ai/dsh-agent/inbox' import AgentLoop from '@deepseek-ai/dsh-agent-loop' import * as LlmDeepSeek from '@deepseek-ai/dsh-llm-deepseek' @@ -44,7 +43,6 @@ async function loopHarness(): Promise { await created.plugin(LlmRuntime) await created.plugin(SessionStore) await created.plugin(SessionProjectionRegistry) - await created.plugin(InboxService) await created.plugin(SystemPrompt, { persona: 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 776280765b..c947530b3c 100644 --- a/packages/core/agent-loop/tests/request-error.spec.ts +++ b/packages/core/agent-loop/tests/request-error.spec.ts @@ -1,4 +1,3 @@ -import InboxService from '@deepseek-ai/dsh-agent/inbox' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { describe, expect, it } from 'vitest' import { Context } from '@deepseek-ai/cordis' @@ -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(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/request-reconstruction.spec.ts b/packages/core/agent-loop/tests/request-reconstruction.spec.ts index 1b178a127f..dacca2f424 100644 --- a/packages/core/agent-loop/tests/request-reconstruction.spec.ts +++ b/packages/core/agent-loop/tests/request-reconstruction.spec.ts @@ -1,4 +1,3 @@ -import InboxService from '@deepseek-ai/dsh-agent/inbox' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' /** * Loop-level reconstructability: every request the loop sends is a pure function of the @@ -31,7 +30,6 @@ async function harnessRoutes( await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt, { persona }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -260,7 +258,6 @@ describe('request stability across the loop', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt, { persona: 'stable base' }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -380,7 +377,6 @@ describe('request stability across the loop', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt, { persona: 'stable base' }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/resume.spec.ts b/packages/core/agent-loop/tests/resume.spec.ts index d1278f77cf..4a6dfb7b01 100644 --- a/packages/core/agent-loop/tests/resume.spec.ts +++ b/packages/core/agent-loop/tests/resume.spec.ts @@ -1,4 +1,3 @@ -import InboxService from '@deepseek-ai/dsh-agent/inbox' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { createUserMessage } from '@deepseek-ai/dsh-llm' import { afterEach, describe, expect, it, vi } from 'vitest' @@ -31,7 +30,6 @@ async function mountPersistentHarness(root: string, adapter: MockAdapter): Promi await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -249,7 +247,6 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume', await ctx2.plugin(LlmRuntime) await ctx2.plugin(SessionStore) await ctx2.plugin(SessionProjectionRegistry) - await ctx2.plugin(InboxService) await ctx2.plugin(SystemPrompt) await ctx2.plugin(ToolRuntime) await ctx2.plugin(AgentRegistry) @@ -279,7 +276,6 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume', await ctx2.plugin(LlmRuntime) await ctx2.plugin(SessionStore) await ctx2.plugin(SessionProjectionRegistry) - await ctx2.plugin(InboxService) await ctx2.plugin(SystemPrompt) await ctx2.plugin(ToolRuntime) await ctx2.plugin(AgentRegistry) @@ -531,7 +527,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(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -596,7 +591,6 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume', await ctx2.plugin(LlmRuntime) await ctx2.plugin(SessionStore) await ctx2.plugin(SessionProjectionRegistry) - await ctx2.plugin(InboxService) await ctx2.plugin(SystemPrompt) await ctx2.plugin(ToolRuntime) await ctx2.plugin(AgentRegistry) @@ -630,7 +624,6 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume', await ctx2.plugin(LlmRuntime) await ctx2.plugin(SessionStore) await ctx2.plugin(SessionProjectionRegistry) - await ctx2.plugin(InboxService) await ctx2.plugin(SystemPrompt) await ctx2.plugin(ToolRuntime) await ctx2.plugin(AgentRegistry) @@ -668,7 +661,6 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume', await ctx2.plugin(LlmRuntime) await ctx2.plugin(SessionStore) await ctx2.plugin(SessionProjectionRegistry) - await ctx2.plugin(InboxService) await ctx2.plugin(SystemPrompt) await ctx2.plugin(ToolRuntime) await ctx2.plugin(AgentRegistry) @@ -703,7 +695,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(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -894,7 +885,6 @@ describe('configured-start failure edges', () => { await configured.plugin(LlmRuntime) await configured.plugin(SessionStore) await configured.plugin(SessionProjectionRegistry) - await configured.plugin(InboxService) await configured.plugin(SystemPrompt) await configured.plugin(ToolRuntime) await configured.plugin(AgentRegistry) @@ -941,7 +931,6 @@ describe('configured-start failure edges', () => { await configured.plugin(LlmRuntime) await configured.plugin(SessionStore) await configured.plugin(SessionProjectionRegistry) - await configured.plugin(InboxService) 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 9e7e441665..25fa383261 100644 --- a/packages/core/agent-loop/tests/scope-lifecycle.spec.ts +++ b/packages/core/agent-loop/tests/scope-lifecycle.spec.ts @@ -1,4 +1,3 @@ -import InboxService from '@deepseek-ai/dsh-agent/inbox' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { createUserMessage } from '@deepseek-ai/dsh-llm' import { describe, expect, it } from 'vitest' @@ -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(InboxService) await ctx.plugin(SystemPrompt, { persona: '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 5f0f98d5a9..d88182a674 100644 --- a/packages/core/agent-loop/tests/settings.spec.ts +++ b/packages/core/agent-loop/tests/settings.spec.ts @@ -1,4 +1,3 @@ -import InboxService from '@deepseek-ai/dsh-agent/inbox' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' /** The `agent-loop` settings section layered over the composition entry. */ @@ -37,7 +36,6 @@ async function boot(): Promise<{ ctx: Context; settingsFiber: Fiber; loopFiber: await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent-loop/tests/tool-calls.spec.ts b/packages/core/agent-loop/tests/tool-calls.spec.ts index 276838654e..8aaa99fd0f 100644 --- a/packages/core/agent-loop/tests/tool-calls.spec.ts +++ b/packages/core/agent-loop/tests/tool-calls.spec.ts @@ -1,4 +1,3 @@ -import InboxService from '@deepseek-ai/dsh-agent/inbox' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' /** * Exercises scheduler ordering and cancellation with deterministic gated tools. @@ -23,7 +22,6 @@ async function harness(adapter: MockAdapter, maxParallelToolCalls?: number) { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt, { persona: '' }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -283,7 +281,6 @@ describe('tool-call scheduler: rolling pool honors maxParallelToolCalls', () => await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt, { persona: '' }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -353,7 +350,6 @@ describe('tool-call scheduler: rolling pool honors maxParallelToolCalls', () => await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt, { persona: '' }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -713,7 +709,6 @@ describe('code-mode native-tool denial through the agent loop', () => { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt, { persona: '' }) await ctx.plugin(ToolRuntime, { mode: 'code' }) // eslint-disable-next-line @typescript-eslint/no-explicit-any -- FakeCodeRuntime 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 da9d59d6e0..d0e4bd29d6 100644 --- a/packages/core/agent-loop/tests/tool-order.spec.ts +++ b/packages/core/agent-loop/tests/tool-order.spec.ts @@ -1,4 +1,3 @@ -import InboxService from '@deepseek-ai/dsh-agent/inbox' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import { createUserMessage } from '@deepseek-ai/dsh-llm' /** @@ -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(InboxService) await ctx.plugin(SystemPrompt, { persona: 'stable base', ...toolOrder !== undefined ? { toolOrder } : {} }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/core/agent/README.i18n.yaml b/packages/core/agent/README.i18n.yaml index 07b8e54cc2..08c110f474 100644 --- a/packages/core/agent/README.i18n.yaml +++ b/packages/core/agent/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/core/agent/README.md -README.md: 6af7e57bdb0ae710a6d1e6e58ca2ac187a45eeb8 -README.zh.md: 1533f918e037c7dc7d131c7046d25fc259d33580 +README.md: aeddc6caf24289333a3e5b0f9b29124b1e591316 +README.zh.md: f815283ba638f0374ae30d8a8c3fe4ce6a66968b diff --git a/packages/core/agent/README.md b/packages/core/agent/README.md index 6af7e57bdb..aeddc6caf2 100644 --- a/packages/core/agent/README.md +++ b/packages/core/agent/README.md @@ -54,7 +54,7 @@ Most interception points are cooperative waterfalls. `agent/pre-step` receives a `PreStepDecision` is either `{ kind: 'reject' }` or `{ kind: 'enter', messages }`. The enter branch is the complete identified, frozen batch for the proposed step. A listener that wraps downstream entry preserves that batch unless it intentionally replaces it; additions follow the waterfall's natural return order. Claiming already removed the offered messages from the inbox, so rejection does not retain them. Messages inserted after the claim remain pending for a later boundary. -`InboxService` owns the standard `inbox` session projection. The projection registry folds durable `agent/inbox/spliced` events once and remains the sole owner of the live `{ 'next-turn', 'next-step' }` state; Inbox is a command facade that reads the registry snapshot rather than replaying or copying the fold. Inbox live notifications are deliberately per-message and minimal: `agent/inbox/inserted { message }`, `agent/inbox/claimed { message, turn }`, and `agent/inbox/discarded { message }`. Inbox emits them as it commits the corresponding mutation, without adding another lifecycle envelope. +`AgentRegistry` contributes the standard `inbox` session projection whenever the projection registry is composed. The registry folds durable `agent/inbox/spliced` events once and remains the sole owner of the live `{ 'next-turn', 'next-step' }` state; Inbox is a command facade that reads that unit rather than replaying or copying the fold. Inbox live notifications are deliberately per-message and minimal: `agent/inbox/inserted { message }`, `agent/inbox/claimed { message, turn }`, and `agent/inbox/discarded { message }`. Inbox emits them as it commits the corresponding mutation, without adding another lifecycle envelope. Turn and step boundaries and the model token stream are durable `session/event` facts rather than mirrored `agent/*` notifications. Consumers read `turn/*`, `step/*`, and `assistant/chunk` from the session feed; tool policy and outcome observation belong to the complete pipeline documented by [`dsh-tools`](../tools/README.md). diff --git a/packages/core/agent/README.zh.md b/packages/core/agent/README.zh.md index 1533f918e0..f815283ba6 100644 --- a/packages/core/agent/README.zh.md +++ b/packages/core/agent/README.zh.md @@ -54,7 +54,7 @@ Agent *创建* 由实现 `AgentFactory` 的插件(`dsh-agent-loop`)提供, `PreStepDecision` 要么是 `{ kind: 'reject' }`,要么是 `{ kind: 'enter', messages }`。enter 分支是拟进入步骤的完整、带标识且冻结的批次。包装下游 enter 的监听器会保留该批次,除非有意替换它;新增消息遵循 waterfall 的自然返回顺序。领取操作已经把候选消息从 inbox 删除,因此 reject 不会保留它们;领取后插入的消息仍等待后续边界。 -`InboxService` 拥有标准 `inbox` 会话投影。投影注册表只折叠一次持久 `agent/inbox/spliced` 事件,并继续作为 live `{ 'next-turn', 'next-step' }` 状态的唯一所有者;Inbox 是读取注册表快照的命令 facade,不会重新回放或复制折叠结果。Inbox 的实时通知刻意采用逐消息的最小载荷:`agent/inbox/inserted { message }`、`agent/inbox/claimed { message, turn }` 与 `agent/inbox/discarded { message }`。Inbox 在提交对应变更时自行发出这些通知,不引入另一层生命周期封套。 +`AgentRegistry` 会在投影注册表已组合时贡献标准 `inbox` 会话投影。注册表只折叠一次持久 `agent/inbox/spliced` 事件,并继续作为 live `{ 'next-turn', 'next-step' }` 状态的唯一所有者;Inbox 是读取该单元的命令 facade,不会重新回放或复制折叠结果。Inbox 的实时通知刻意采用逐消息的最小载荷:`agent/inbox/inserted { message }`、`agent/inbox/claimed { message, turn }` 与 `agent/inbox/discarded { message }`。Inbox 在提交对应变更时自行发出这些通知,不引入另一层生命周期封套。 轮次和步骤边界以及模型 token 流是持久 `session/event` 事实,而不是镜像的 `agent/*` 通知。消费方从会话事件流读取 `turn/*`、`step/*` 和 `assistant/chunk`;工具策略与结果观测属于 [`dsh-tools`](../tools/README.md) 记录的完整流水线。 diff --git a/packages/core/agent/package.json b/packages/core/agent/package.json index 1af7dfbe7d..e056207c36 100644 --- a/packages/core/agent/package.json +++ b/packages/core/agent/package.json @@ -18,14 +18,6 @@ "types": "./lib/types/index.d.ts", "default": "./lib/index.js" }, - "./inbox": { - "types": "./lib/types/inbox.d.ts", - "default": "./lib/types/inbox.js" - }, - "./inbox-projection": { - "types": "./lib/types/inbox-projection.d.ts", - "default": "./lib/types/inbox-projection.js" - }, "./invariant": { "types": "./lib/types/invariant.d.ts", "default": "./lib/invariant.js" diff --git a/packages/core/agent/src/inbox-projection.ts b/packages/core/agent/src/inbox-projection.ts index 24602d17a7..752bf01593 100644 --- a/packages/core/agent/src/inbox-projection.ts +++ b/packages/core/agent/src/inbox-projection.ts @@ -1,6 +1,7 @@ /** Inbox projection schema and its inferred wire value. */ import type { UserMessage } from '@deepseek-ai/dsh-llm/types' +import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection' import { z } from 'zod' /** Wire validation for pending agent input reconstructed from durable inbox splices. */ @@ -12,6 +13,30 @@ export const inboxProjectionSchema = z.object({ /** Complete pending Inbox value reconstructed from durable splices. */ export type InboxState = z.infer +/** Standard fold that reconstructs pending agent input from durable splices. */ +export const inboxProjectionDefinition = { + key: 'inbox', + stateSchema: inboxProjectionSchema, + init: (): InboxState => ({ 'next-turn': [], 'next-step': [] }), + apply(state: InboxState, event) { + if (event.type !== 'agent/inbox/spliced') return state + const splice = event.data + const next = state[splice.target].toSpliced( + splice.start, + splice.removedCount ?? 0, + ...splice.inserted, + ) + return splice.target === 'next-turn' + ? { 'next-turn': next, 'next-step': state['next-step'] } + : { 'next-turn': state['next-turn'], 'next-step': next } + }, + wire: { + viewSchema: inboxProjectionSchema, + view: (state: InboxState) => state, + }, + stateVersion: 1, +} satisfies ProjectionDefinition<'inbox', InboxState> + declare module '@deepseek-ai/dsh-session-projection/types' { interface SessionProjectionMap { /** Pending agent input reconstructed from durable inbox splices. */ diff --git a/packages/core/agent/src/inbox.ts b/packages/core/agent/src/inbox.ts index 8f25da7f3a..2ffc4bed39 100644 --- a/packages/core/agent/src/inbox.ts +++ b/packages/core/agent/src/inbox.ts @@ -1,70 +1,25 @@ /** - * Incremental projection of durable agent inbox events. + * Command facade over the durable agent Inbox projection. * * @module @deepseek-ai/dsh-agent/inbox */ -import { Context, Service } from '@deepseek-ai/cordis' +import type { Context } from '@deepseek-ai/cordis' import type { MessageId } from '@deepseek-ai/dsh-llm' -import type { SessionEventMap, UserMessage } from '@deepseek-ai/dsh-session' -import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection' -import { agentEvents } from './dispatch.ts' +// Type-only: resolves ctx.sessionProjections for the required Inbox projection. +import type {} from '@deepseek-ai/dsh-session-projection' +import type { Session, SessionEventMap, UserMessage } from '@deepseek-ai/dsh-session' import type { AgentEventDispatch } from './dispatch.ts' -import { inboxProjectionSchema } from './inbox-projection.ts' import type { InboxState } from './inbox-projection.ts' -import type { Agent } from './runtime-types.ts' import type { InboxTarget } from './types.ts' -declare module '@deepseek-ai/cordis' { - interface Context { - inboxes: InboxService - } -} - -/** Root Inbox service: creates live inboxes and owns their durable projection. */ -export class InboxService extends Service { - static inject = ['sessionProjections'] - - constructor(ctx: Context) { - super(ctx, 'inboxes') - ctx.sessionProjections.register({ - key: 'inbox', - schema: inboxProjectionSchema, - init: () => ({ 'next-turn': [], 'next-step': [] }), - apply(state, event) { - if (event.type !== 'agent/inbox/spliced') return state - const splice = event.data - const next = state[splice.target].toSpliced( - splice.start, - splice.removedCount ?? 0, - ...splice.inserted, - ) - return splice.target === 'next-turn' - ? { 'next-turn': next, 'next-step': state['next-step'] } - : { 'next-turn': state['next-turn'], 'next-step': next } - }, - view: state => state, - stateVersion: 1, - } satisfies ProjectionDefinition<'inbox', InboxState>) - } - - /** - * Restore one live Inbox for an agent and publish its committed mutations. - * @param agent - agent that owns the durable session and live Inbox events. - * @returns the restored Inbox. - */ - create(agent: Agent): Inbox { - return new Inbox(this.ctx, agent) - } -} - /** Agent-owned command facade over the standard durable Inbox projection. */ export class Inbox { - private readonly dispatch: AgentEventDispatch - - constructor(private readonly ctx: Context, private readonly agent: Agent) { - this.dispatch = agentEvents(ctx, agent) - } + constructor( + private readonly ctx: Context, + private readonly session: Session, + private readonly dispatch: AgentEventDispatch, + ) {} /** Prompts awaiting individual turns. */ get nextTurn(): readonly UserMessage[] { @@ -171,11 +126,11 @@ export class Inbox { return undefined } - /** Read the current durable projection value. */ + /** Read the current durable projection state. */ private current(): InboxState { - // InboxService registers this required projection before creating an Inbox. + // AgentLoop requires sessionProjections; AgentRegistry contributes this unit to it. // oxlint-disable-next-line typescript/no-non-null-assertion - return this.ctx.sessionProjections.snapshot(this.agent.session).values.inbox! + return this.ctx.sessionProjections.stateOf(this.session, 'inbox')! } /** Commit one normalized mutation and publish its live events. */ @@ -216,7 +171,7 @@ export class Inbox { ...(outcome === undefined ? {} : { outcome }), } const removed = inbox.slice(actualStart, actualStart + actualDeleteCount) - const event = this.agent.session.append('agent/inbox/spliced', splice) + const event = this.session.append('agent/inbox/spliced', splice) if (discardRemoved) { for (const message of removed) this.dispatch.emit('agent/inbox/discarded', { message }) } @@ -226,5 +181,3 @@ export class Inbox { return removed } } - -export default InboxService diff --git a/packages/core/agent/src/index.ts b/packages/core/agent/src/index.ts index 81052096dc..22111e437b 100644 --- a/packages/core/agent/src/index.ts +++ b/packages/core/agent/src/index.ts @@ -12,7 +12,10 @@ import { isPromise } from 'node:util/types' import { scopeTarget } from '@deepseek-ai/dsh-scope' import type { Scoped } from '@deepseek-ai/dsh-scope' import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session' +// Type-only: resolves ctx.sessionProjections for the optional Inbox projection contribution. +import type {} from '@deepseek-ai/dsh-session-projection' import type { TypertContext, TypertLookup } from '@deepseek-ai/dsh-typert-protocol' +import { inboxProjectionDefinition } from './inbox-projection.ts' import type { Agent, AgentOptions } from './runtime-types.ts' export * from './runtime-types.ts' @@ -265,6 +268,9 @@ export class AgentRegistry extends Service { constructor(ctx: Context) { super(ctx, 'agents') + ctx.inject(['sessionProjections'], (projectionCtx) => { + projectionCtx.sessionProjections.register(inboxProjectionDefinition) + }) ctx.inject(['typert'], (typeCtx) => { typeCtx.typert.lookups.register('agent', { parameter: 'agent', diff --git a/packages/core/agent/tests/agent.spec.ts b/packages/core/agent/tests/agent.spec.ts index af25e7e870..293ed93e67 100644 --- a/packages/core/agent/tests/agent.spec.ts +++ b/packages/core/agent/tests/agent.spec.ts @@ -4,10 +4,9 @@ import { createUserMessage, freezeMessage } from '@deepseek-ai/dsh-llm' import SessionStore, { Session, SessionId, type UserMessage } from '@deepseek-ai/dsh-session' import AgentRegistry, { agentEvents, - InboxService, + Inbox, } from '@deepseek-ai/dsh-agent' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' -import type {} from '@deepseek-ai/dsh-agent/inbox-projection' import TypertRegistry from '@deepseek-ai/dsh-typert-registry' import type { @@ -48,10 +47,10 @@ async function inboxAgent(rawId: string): Promise<{ ctx: Context; session: Sessi const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) + await ctx.plugin(AgentRegistry) const session = ctx.sessions.create(SessionId(rawId)) const agent = stubAgent(rawId, { ctx, session }) - Object.assign(agent, { inbox: ctx.inboxes.create(agent) }) + Object.assign(agent, { inbox: new Inbox(ctx, agent.session, agentEvents(ctx, agent)) }) return { ctx, session, agent } } @@ -65,7 +64,7 @@ describe('Inbox', () => { parentAgent.inbox.append('next-turn', inherited) const child = ctx.sessions.fork(parent, undefined, SessionId('inbox-fork-child')) const childAgent = stubAgent('inbox-fork-child', { ctx, session: child }) - Object.assign(childAgent, { inbox: ctx.inboxes.create(childAgent) }) + Object.assign(childAgent, { inbox: new Inbox(ctx, childAgent.session, agentEvents(ctx, childAgent)) }) expect(child.header.seedLength).toBe(parent.events.length) expect(childAgent.inbox.nextTurn).toEqual([inherited]) @@ -169,15 +168,15 @@ describe('Inbox', () => { expect(session.events).toHaveLength(beforeClear + 2) }) - it('registers the durable Inbox projection from the Inbox service', async () => { + it('registers the durable Inbox projection from the Agent registry', async () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - const inboxFiber = ctx.plugin(InboxService) - await inboxFiber + const agentFiber = ctx.plugin(AgentRegistry) + await agentFiber const session = ctx.sessions.create(SessionId('inbox-projection')) const agent = stubAgent('inbox-projection', { ctx, session }) - Object.assign(agent, { inbox: ctx.inboxes.create(agent) }) + Object.assign(agent, { inbox: new Inbox(ctx, agent.session, agentEvents(ctx, agent)) }) const pending = createUserMessage({ content: [{ type: 'text', text: 'pending' }], source: { kind: 'user' }, @@ -189,7 +188,7 @@ describe('Inbox', () => { 'next-turn': [pending], 'next-step': [], }) - await inboxFiber.dispose() + await agentFiber.dispose() expect(ctx.sessionProjections.snapshot(session).values).toEqual({}) }) diff --git a/packages/e2b/e2b/tests/composition.e2e.ts b/packages/e2b/e2b/tests/composition.e2e.ts index 5b17a19dbd..305aa1cb66 100644 --- a/packages/e2b/e2b/tests/composition.e2e.ts +++ b/packages/e2b/e2b/tests/composition.e2e.ts @@ -3,7 +3,7 @@ import { join, posix } from 'node:path' import { fileURLToPath } from 'node:url' import { Context } from '@deepseek-ai/cordis' import { describe, expect, it } from 'vitest' -import { Inbox } from '@deepseek-ai/dsh-agent' +import { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' import { runLoaderSmoke } from '@deepseek-ai/dsh-loader-smoke' import { @@ -96,7 +96,7 @@ describe.skipIf(!process.env.E2B_API_KEY)('E2B live Loader composition', () => { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(owner, { inbox: new Inbox(owner.ctx, owner) }) + Object.assign(owner, { inbox: new Inbox(owner.ctx, owner.session, agentEvents(owner.ctx, owner)) }) const backend = new BashTerminalBackend(ctx, { backendType: 'shell', shellDialect: 'bash', shellPath: '/bin/bash', shellArgs: ['--noprofile', '--norc', '-i'], rows: 24, cols: 80, diff --git a/packages/examples/agent-spine-demo/src/index.ts b/packages/examples/agent-spine-demo/src/index.ts index 4cf542540b..f67efff4bb 100644 --- a/packages/examples/agent-spine-demo/src/index.ts +++ b/packages/examples/agent-spine-demo/src/index.ts @@ -20,7 +20,6 @@ import ToolRuntime, { type Config as ToolsConfig } from '@deepseek-ai/dsh-tools' import SkillRegistry, { type Config as SkillRegistryConfig } from '@deepseek-ai/dsh-skill' import * as SkillFileSystem from '@deepseek-ai/dsh-skill-filesystem' import AgentRegistry from '@deepseek-ai/dsh-agent' -import InboxService from '@deepseek-ai/dsh-agent/inbox' import GoalService, { type Config as GoalDomainConfig } from '@deepseek-ai/dsh-goal' import * as goalSession from '@deepseek-ai/dsh-goal-round-driver' import * as toolGoal from '@deepseek-ai/dsh-tool-goal' @@ -238,7 +237,6 @@ export function apply(ctx: Context, config: Config): void { ctx.plugin(SkillFileSystem, Object.assign({}, config.skills?.filesystem, { dshHome })) } ctx.plugin(AgentRegistry) - ctx.plugin(InboxService) ctx.plugin(llmRetry) if (config.goals !== undefined && config.goals !== false) { ctx.plugin(GoalService, config.goals.domain ?? {}) diff --git a/packages/extensions/tool-cordis/src/api-catalog.ts b/packages/extensions/tool-cordis/src/api-catalog.ts index 739fc69a2a..86e722f825 100644 --- a/packages/extensions/tool-cordis/src/api-catalog.ts +++ b/packages/extensions/tool-cordis/src/api-catalog.ts @@ -807,19 +807,6 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [ }, ], }, - { - key: 'inboxes', - summary: 'Root Inbox service: creates live inboxes and owns their durable projection.', - description: 'Root Inbox service: creates live inboxes and owns their durable projection.', - methods: [ - { - signature: 'create(agent: Agent): Inbox', - description: 'Restore one live Inbox for an agent and publish its committed mutations.', - parameters: [{ name: 'agent', description: 'agent that owns the durable session and live Inbox events.' }], - returns: 'the restored Inbox.', - }, - ], - }, { key: 'invariants', summary: 'Package-owned invariant registry with global and regex-based selection.', @@ -2762,6 +2749,10 @@ export const TYPE_API: readonly TypeApiEntry[] = [ name: 'AgentCancelCause', declaration: 'export type AgentCancelCause = {\n readonly kind: \'user\';\n} | {\n readonly kind: \'parent\';\n} | {\n readonly kind: \'hook\';\n readonly reason: string;\n} | {\n readonly kind: \'disposed\';\n};', }, + { + name: 'AgentEventDispatch', + declaration: 'export interface AgentEventDispatch {\n emit(name: K, payload: PayloadRest): void;\n serial(name: K, payload: PayloadRest): Promise>>;\n waterfall(name: K, payload: PayloadRest, ...rest: Tail): Return;\n}', + }, { name: 'AgentFactory', declaration: 'export interface AgentFactory {\n createAgent(ownerCtx: Context, options: CreateAgentOptions): Promise;\n resume(ownerCtx: Context, options: ResumeAgentOptions): Promise;\n}', @@ -2790,6 +2781,10 @@ export const TYPE_API: readonly TypeApiEntry[] = [ name: 'AgentStatus', declaration: 'export type AgentStatus = \'idle\' | \'running\';', }, + { + name: 'AgentSubjectEvent', + declaration: 'export type AgentSubjectEvent = {\n [K in keyof Events]: Events[K] extends (this: Scoped, ...args: infer P) => unknown ? P extends [\n infer Payload,\n ...unknown[]\n ] ? Payload extends {\n agent: Agent;\n } ? K : never : never : never;\n}[keyof Events];', + }, { name: 'ApprovalOutcome', declaration: 'export type ApprovalOutcome = \'allowed-once\' | \'rejected\' | \'cancelled\' | \'unavailable\';', @@ -3300,7 +3295,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [ }, { name: 'Inbox', - declaration: 'export class Inbox {\n constructor(private readonly ctx: Context, private readonly agent: Agent);\n get nextTurn(): readonly UserMessage[];\n get nextStep(): readonly UserMessage[];\n get hasPending(): boolean;\n clear(): void;\n claim(target: InboxTarget, turn: number): UserMessage[];\n append(target: InboxTarget, message: UserMessage): void;\n prepend(target: InboxTarget, message: UserMessage): void;\n replace(messageId: MessageId, newMessage: UserMessage): boolean;\n remove(messageId: MessageId): boolean;\n splice(target: InboxTarget, start: number, deleteCount: number, inserted: UserMessage[]): UserMessage[];\n}', + declaration: 'export class Inbox {\n constructor(private readonly ctx: Context, private readonly session: Session, private readonly dispatch: AgentEventDispatch);\n get nextTurn(): readonly UserMessage[];\n get nextStep(): readonly UserMessage[];\n get hasPending(): boolean;\n clear(): void;\n claim(target: InboxTarget, turn: number): UserMessage[];\n append(target: InboxTarget, message: UserMessage): void;\n prepend(target: InboxTarget, message: UserMessage): void;\n replace(messageId: MessageId, newMessage: UserMessage): boolean;\n remove(messageId: MessageId): boolean;\n splice(target: InboxTarget, start: number, deleteCount: number, inserted: UserMessage[]): UserMessage[];\n}', }, { name: 'InboxTarget', diff --git a/packages/feedback/command-feedback/tests/command-feedback.spec.ts b/packages/feedback/command-feedback/tests/command-feedback.spec.ts index 4f58bfbdc7..90c48ffa96 100644 --- a/packages/feedback/command-feedback/tests/command-feedback.spec.ts +++ b/packages/feedback/command-feedback/tests/command-feedback.spec.ts @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' import Loader from '@deepseek-ai/cordis-plugin-loader' -import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent, AgentStatus } from '@deepseek-ai/dsh-agent' import CommandRuntime from '@deepseek-ai/dsh-commands' import SessionStore, { foldSurface, Session, SessionId } from '@deepseek-ai/dsh-session' @@ -59,7 +59,7 @@ function stubAgent(ctx: Context, id: string): { agent: Agent; session: Session } runMaintenance: task => task(new AbortController().signal), whenIdle() { return Promise.resolve() }, } - Object.assign(agent, { inbox: new Inbox(agent.ctx, agent) }) + Object.assign(agent, { inbox: new Inbox(agent.ctx, agent.session, agentEvents(agent.ctx, agent)) }) return { agent, session } } diff --git a/packages/feedback/command-feedback/tests/loader-composition.spec.ts b/packages/feedback/command-feedback/tests/loader-composition.spec.ts index 4f01449b15..831481a702 100644 --- a/packages/feedback/command-feedback/tests/loader-composition.spec.ts +++ b/packages/feedback/command-feedback/tests/loader-composition.spec.ts @@ -6,7 +6,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' import Loader from '@deepseek-ai/cordis-plugin-loader' import Include from '@deepseek-ai/cordis-plugin-include' -import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent, AgentStatus } from '@deepseek-ai/dsh-agent' import CommandRuntime from '@deepseek-ai/dsh-commands' import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' @@ -45,7 +45,7 @@ function agent(ctx: Context): Agent { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(value, { inbox: new Inbox(value.ctx, value) }) + Object.assign(value, { inbox: new Inbox(value.ctx, value.session, agentEvents(value.ctx, value)) }) ctx.agents.register(value) return value } 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 69d931673a..eb1aece0c3 100644 --- a/packages/fs/tool-str-replace-editor/tests/tools.spec.ts +++ b/packages/fs/tool-str-replace-editor/tests/tools.spec.ts @@ -6,7 +6,7 @@ import { Context } from '@deepseek-ai/cordis' import { FsVersion } from '@deepseek-ai/dsh-fs' import { CallId } from '@deepseek-ai/dsh-llm' import { Session, SessionId } from '@deepseek-ai/dsh-session' -import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' import LocalFileSystem from '@deepseek-ai/dsh-fs-local' import * as FsPolicy from '@deepseek-ai/dsh-fs-observation-policy' @@ -44,7 +44,7 @@ function agent(ctx: Context, cwd: string): Agent { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(value, { inbox: new Inbox(value.ctx, value) }) + Object.assign(value, { inbox: new Inbox(value.ctx, value.session, agentEvents(value.ctx, value)) }) ctx.agents.register(value) return value } diff --git a/packages/goal/command-goal/tests/command-goal.spec.ts b/packages/goal/command-goal/tests/command-goal.spec.ts index d03054b717..ec139886ee 100644 --- a/packages/goal/command-goal/tests/command-goal.spec.ts +++ b/packages/goal/command-goal/tests/command-goal.spec.ts @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' import Loader from '@deepseek-ai/cordis-plugin-loader' -import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent, AgentStatus } from '@deepseek-ai/dsh-agent' import CommandRuntime from '@deepseek-ai/dsh-commands' import GoalService from '@deepseek-ai/dsh-goal' @@ -36,7 +36,7 @@ function stubAgent(ctx: Context, id: string): { agent: Agent; session: Session } runMaintenance: task => task(new AbortController().signal), whenIdle() { return Promise.resolve() }, } - Object.assign(agent, { inbox: new Inbox(agent.ctx, agent) }) + Object.assign(agent, { inbox: new Inbox(agent.ctx, agent.session, agentEvents(agent.ctx, agent)) }) return { agent, session } } diff --git a/packages/goal/goal/tests/goal.spec.ts b/packages/goal/goal/tests/goal.spec.ts index 8d664dbe9c..6888c4eacc 100644 --- a/packages/goal/goal/tests/goal.spec.ts +++ b/packages/goal/goal/tests/goal.spec.ts @@ -1,7 +1,6 @@ import { describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' -import AgentRegistry, { agentEvents } from '@deepseek-ai/dsh-agent' -import InboxService from '@deepseek-ai/dsh-agent/inbox' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' import { createUserMessage, HarnessError } from '@deepseek-ai/dsh-llm' import SessionStore, { Session, SessionId, type UserMessage } from '@deepseek-ai/dsh-session' @@ -22,7 +21,7 @@ interface StubAgent { const isolatedInboxCtx = new Context() await isolatedInboxCtx.plugin(SessionStore) await isolatedInboxCtx.plugin(SessionProjectionRegistry) -await isolatedInboxCtx.plugin(InboxService) +await isolatedInboxCtx.plugin(AgentRegistry) const sessionStubs = new WeakMap() /** Number the next balanced test-fixture turn. */ @@ -59,7 +58,7 @@ function stubAgentForSession(session: Session, suppliedCtx?: Context): StubAgent runMaintenance: task => task(new AbortController().signal), whenIdle() { return Promise.resolve() }, } - Object.assign(agent, { inbox: agentCtx.inboxes.create(agent) }) + Object.assign(agent, { inbox: new Inbox(agentCtx, agent.session, agentEvents(agentCtx, agent)) }) const stub = { agent, session, @@ -84,7 +83,6 @@ async function harness(config: { defaultMaxGoalRounds?: number } = {}) { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(AgentRegistry) await ctx.plugin(GoalService, config) const stub = stubAgent(`goal-test-${Math.random()}`) @@ -195,7 +193,6 @@ describe('GoalService creation and replay', () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(AgentRegistry) await ctx.plugin(GoalService) const parent = stubAgentForSession(ctx.sessions.create(SessionId('goal-fork-parent')), ctx) @@ -451,7 +448,6 @@ describe('GoalService mutations', () => { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(AgentRegistry) await ctx.plugin(GoalService) const stub = stubAgentForSession(ctx.sessions.create(SessionId('goal-reentrant-observer')), ctx) diff --git a/packages/goal/tool-goal/tests/tool-goal.spec.ts b/packages/goal/tool-goal/tests/tool-goal.spec.ts index 7e25c3f5b7..d538fcc4f8 100644 --- a/packages/goal/tool-goal/tests/tool-goal.spec.ts +++ b/packages/goal/tool-goal/tests/tool-goal.spec.ts @@ -1,8 +1,7 @@ import { describe, expect, it } from 'vitest' import { Context } from '@deepseek-ai/cordis' import Loader from '@deepseek-ai/cordis-plugin-loader' -import AgentRegistry, { agentEvents } from '@deepseek-ai/dsh-agent' -import InboxService from '@deepseek-ai/dsh-agent/inbox' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent, AgentStatus } from '@deepseek-ai/dsh-agent' import GoalService, { GoalId } from '@deepseek-ai/dsh-goal' import type { GoalRef } from '@deepseek-ai/dsh-goal' @@ -26,7 +25,7 @@ interface StubAgent { const isolatedInboxCtx = new Context() await isolatedInboxCtx.plugin(SessionStore) await isolatedInboxCtx.plugin(SessionProjectionRegistry) -await isolatedInboxCtx.plugin(InboxService) +await isolatedInboxCtx.plugin(AgentRegistry) /** Build one registry-compatible live agent whose injections enter the durable inbox. */ function stubAgent(rawId: string, supplied?: Session, suppliedCtx?: Context): StubAgent { @@ -55,7 +54,7 @@ function stubAgent(rawId: string, supplied?: Session, suppliedCtx?: Context): St runMaintenance: task => task(new AbortController().signal), whenIdle() { return Promise.resolve() }, } - Object.assign(agent, { inbox: agentCtx.inboxes.create(agent) }) + Object.assign(agent, { inbox: new Inbox(agentCtx, agent.session, agentEvents(agentCtx, agent)) }) return { agent, session, setStatus(value) { status = value } } } @@ -87,7 +86,6 @@ async function harness(config: toolGoal.Config = {}) { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(AgentRegistry) await ctx.plugin(ToolRuntime) diff --git a/packages/host/apiproxy/tests/api-proxy-projections.spec.ts b/packages/host/apiproxy/tests/api-proxy-projections.spec.ts index 0f290621cd..e94dbb420d 100644 --- a/packages/host/apiproxy/tests/api-proxy-projections.spec.ts +++ b/packages/host/apiproxy/tests/api-proxy-projections.spec.ts @@ -10,9 +10,7 @@ import { describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' import { z } from 'zod' -import AgentRegistry from '@deepseek-ai/dsh-agent' -import InboxService from '@deepseek-ai/dsh-agent/inbox' -import type {} from '@deepseek-ai/dsh-agent/inbox-projection' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import { AttachmentStore } from '@deepseek-ai/dsh-attachment' import type { Agent } from '@deepseek-ai/dsh-agent' import { createUserMessage } from '@deepseek-ai/dsh-llm' @@ -71,7 +69,6 @@ async function harness(withRegistry: boolean): Promise<{ ctx: Context; session: await ctx.plugin(AgentRegistry) if (withRegistry) { await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) } const session = ctx.sessions.create() const agent = { @@ -89,7 +86,7 @@ async function harness(withRegistry: boolean): Promise<{ ctx: Context; session: runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } satisfies Agent - if (withRegistry) Object.assign(agent, { inbox: ctx.inboxes.create(agent) }) + if (withRegistry) Object.assign(agent, { inbox: new Inbox(ctx, agent.session, agentEvents(ctx, agent)) }) ctx.agents.register(agent) return { ctx, session } } diff --git a/packages/host/apiproxy/tests/api-proxy-workspace.spec.ts b/packages/host/apiproxy/tests/api-proxy-workspace.spec.ts index 6019da820a..9e63b7a3db 100644 --- a/packages/host/apiproxy/tests/api-proxy-workspace.spec.ts +++ b/packages/host/apiproxy/tests/api-proxy-workspace.spec.ts @@ -3,7 +3,7 @@ import { homedir, tmpdir } from 'node:os' import { join } from 'node:path' import { describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' -import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent, AgentFactory } from '@deepseek-ai/dsh-agent' import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' import type { Session } from '@deepseek-ai/dsh-session' @@ -55,7 +55,7 @@ function stubAgent(session: Session): Agent { runMaintenance: job => job(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(agent, { inbox: new Inbox(agent.ctx, agent) }) + Object.assign(agent, { inbox: new Inbox(agent.ctx, agent.session, agentEvents(agent.ctx, agent)) }) return agent } diff --git a/packages/jobs/jobs-local/tests/jobs.spec.ts b/packages/jobs/jobs-local/tests/jobs.spec.ts index b333b87222..ce7f2347dc 100644 --- a/packages/jobs/jobs-local/tests/jobs.spec.ts +++ b/packages/jobs/jobs-local/tests/jobs.spec.ts @@ -1,7 +1,7 @@ import { describe, expect, expectTypeOf, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' import { Session, SessionId } from '@deepseek-ai/dsh-session' -import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' import { bindScopeParent, createScope, scopeOf } from '@deepseek-ai/dsh-scope' import type { ScopeKey } from '@deepseek-ai/dsh-scope' @@ -45,7 +45,7 @@ function stubAgent(ctx: Context, rawId: string, presetScope?: ScopeKey): Agent { runMaintenance: (job: (signal: AbortSignal) => Promise) => job(new AbortController().signal), whenIdle() { return Promise.resolve() }, } - Object.assign(agent, { inbox: new Inbox(agent.ctx, agent) }) + Object.assign(agent, { inbox: new Inbox(agent.ctx, agent.session, agentEvents(agent.ctx, agent)) }) agentScopeDisposers.set(agent, async () => { await scopeFiber.dispose() }) return agent } diff --git a/packages/llm/llm-retry/tests/loader-composition.spec.ts b/packages/llm/llm-retry/tests/loader-composition.spec.ts index 7d869af764..b4bef4c239 100644 --- a/packages/llm/llm-retry/tests/loader-composition.spec.ts +++ b/packages/llm/llm-retry/tests/loader-composition.spec.ts @@ -7,7 +7,6 @@ import { Context } from '@deepseek-ai/cordis' import Loader from '@deepseek-ai/cordis-plugin-loader' import Include from '@deepseek-ai/cordis-plugin-include' import AgentRegistry from '@deepseek-ai/dsh-agent' -import InboxService from '@deepseek-ai/dsh-agent/inbox' import AgentLoop from '@deepseek-ai/dsh-agent-loop' import LlmRuntime, { createUserMessage, LlmAdapter, LlmError, resolveRetryPolicy } from '@deepseek-ai/dsh-llm' import type { GenerateOptions, ResolvedRetryPolicy, StreamChunk } from '@deepseek-ai/dsh-llm' @@ -66,7 +65,6 @@ async function loadYaml(lines: readonly string[]): Promise { ['@deepseek-ai/dsh-system-prompt', SystemPrompt], ['@deepseek-ai/dsh-tools', ToolRuntime], ['@deepseek-ai/dsh-agent', AgentRegistry], - ['@deepseek-ai/dsh-agent/inbox', InboxService], ['@deepseek-ai/dsh-llm-retry', retry], ['@deepseek-ai/dsh-agent-loop', AgentLoop], ]) @@ -97,7 +95,6 @@ describe('real Loader composition', () => { "- name: '@deepseek-ai/dsh-system-prompt'", "- name: '@deepseek-ai/dsh-tools'", "- name: '@deepseek-ai/dsh-agent'", - "- name: '@deepseek-ai/dsh-agent/inbox'", "- name: '@deepseek-ai/dsh-llm-retry'", "- name: '@deepseek-ai/dsh-agent-loop'", ]) diff --git a/packages/plan/plan-mode/tests/integration.spec.ts b/packages/plan/plan-mode/tests/integration.spec.ts index 959b36f2ac..68d24db709 100644 --- a/packages/plan/plan-mode/tests/integration.spec.ts +++ b/packages/plan/plan-mode/tests/integration.spec.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from 'vitest' import { Context } from '@deepseek-ai/cordis' -import InboxService from '@deepseek-ai/dsh-agent/inbox' import LlmRuntime, { createUserMessage, type StreamChunk } from '@deepseek-ai/dsh-llm' import SessionStore, { SessionId, type SessionEvent } from '@deepseek-ai/dsh-session' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' @@ -26,7 +25,6 @@ async function harness(adapter: MockAdapter): Promise { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/preset/agent-presets/tests/invariant.spec.ts b/packages/preset/agent-presets/tests/invariant.spec.ts index f1e26a79d7..24514fe0ab 100644 --- a/packages/preset/agent-presets/tests/invariant.spec.ts +++ b/packages/preset/agent-presets/tests/invariant.spec.ts @@ -1,7 +1,6 @@ import { dirname, join } from 'node:path' import { fileURLToPath, pathToFileURL } from 'node:url' import { Context } from '@deepseek-ai/cordis' -import InboxService from '@deepseek-ai/dsh-agent/inbox' import Loader from '@deepseek-ai/cordis-plugin-loader' import Include from '@deepseek-ai/cordis-plugin-include' import LlmRuntime from '@deepseek-ai/dsh-llm' @@ -30,7 +29,6 @@ async function harness(roster: Partial = {}): Promise { await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt, { persona: '' }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/preset/agent-presets/tests/mount.spec.ts b/packages/preset/agent-presets/tests/mount.spec.ts index 23a56112e7..fe7f2adc53 100644 --- a/packages/preset/agent-presets/tests/mount.spec.ts +++ b/packages/preset/agent-presets/tests/mount.spec.ts @@ -3,7 +3,6 @@ import { tmpdir } from 'node:os' import { dirname, join } from 'node:path' import { fileURLToPath, pathToFileURL } from 'node:url' import { Context } from '@deepseek-ai/cordis' -import InboxService from '@deepseek-ai/dsh-agent/inbox' import Loader from '@deepseek-ai/cordis-plugin-loader' import Include from '@deepseek-ai/cordis-plugin-include' import LlmRuntime from '@deepseek-ai/dsh-llm' @@ -48,7 +47,6 @@ async function harness(roster: Config = { default: 'standard', roots: ROOTS, inc await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt, { persona: '' }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) @@ -419,7 +417,6 @@ describe('the preset file is an input, never a persistence target', () => { await scoped.plugin(LlmRuntime) await scoped.plugin(SessionStore) await scoped.plugin(SessionProjectionRegistry) - await scoped.plugin(InboxService) await scoped.plugin(SystemPrompt, { persona: '' }) await scoped.plugin(ToolRuntime) await scoped.plugin(AgentRegistry) @@ -586,7 +583,6 @@ describe('replacing a composition', () => { await scoped.plugin(LlmRuntime) await scoped.plugin(SessionStore) await scoped.plugin(SessionProjectionRegistry) - await scoped.plugin(InboxService) await scoped.plugin(SystemPrompt, { persona: '' }) await scoped.plugin(ToolRuntime) await scoped.plugin(AgentRegistry) diff --git a/packages/preset/agent-presets/tests/settings.spec.ts b/packages/preset/agent-presets/tests/settings.spec.ts index 4b09b1b663..b1bf43b247 100644 --- a/packages/preset/agent-presets/tests/settings.spec.ts +++ b/packages/preset/agent-presets/tests/settings.spec.ts @@ -16,7 +16,6 @@ import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' import SystemPrompt from '@deepseek-ai/dsh-system-prompt' import ToolRuntime from '@deepseek-ai/dsh-tools' import AgentRegistry from '@deepseek-ai/dsh-agent' -import InboxService from '@deepseek-ai/dsh-agent/inbox' import AgentLoop from '@deepseek-ai/dsh-agent-loop' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import FileSettingsProvider from '@deepseek-ai/dsh-settings-file' @@ -46,7 +45,6 @@ async function harness( await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt, { persona: '' }) await ctx.plugin(ToolRuntime) await ctx.plugin(AgentRegistry) diff --git a/packages/schedule/schedule/tests/runtime.spec.ts b/packages/schedule/schedule/tests/runtime.spec.ts index 106725c8ad..0c5c6bce54 100644 --- a/packages/schedule/schedule/tests/runtime.spec.ts +++ b/packages/schedule/schedule/tests/runtime.spec.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' -import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent, AgentCancelCause, InboxTarget } from '@deepseek-ai/dsh-agent' import type { UserMessage } from '@deepseek-ai/dsh-llm' import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' @@ -96,7 +96,7 @@ async function harness(): Promise { steer(_message: UserMessage) {}, inject(_message: UserMessage) {}, } - Object.assign(agent, { inbox: new Inbox(agent.ctx, agent) }) + Object.assign(agent, { inbox: new Inbox(agent.ctx, agent.session, agentEvents(agent.ctx, agent)) }) const disposeAgent = ctx.agents.register(agent) ctx.on('session/event', (_session, event) => { if (event.type === 'schedule/change' && event.data.operation === 'dispatch') order.push('dispatch') diff --git a/packages/schedule/schedule/tests/tools.spec.ts b/packages/schedule/schedule/tests/tools.spec.ts index 7b387c7ef5..7343a9529c 100644 --- a/packages/schedule/schedule/tests/tools.spec.ts +++ b/packages/schedule/schedule/tests/tools.spec.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' -import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent, AgentCancelCause, InboxTarget } from '@deepseek-ai/dsh-agent' import { CallId } from '@deepseek-ai/dsh-llm' import type { UserMessage } from '@deepseek-ai/dsh-llm' @@ -39,7 +39,7 @@ function stubAgent(ctx: Context, id: string): Agent { steer(_message: UserMessage) {}, inject(_message: UserMessage) {}, } - Object.assign(agent, { inbox: new Inbox(agent.ctx, agent) }) + Object.assign(agent, { inbox: new Inbox(agent.ctx, agent.session, agentEvents(agent.ctx, agent)) }) return agent } 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 5fade7b924..e225378ddb 100644 --- a/packages/shell/tool-bash-persistent/tests/loader-composition.spec.ts +++ b/packages/shell/tool-bash-persistent/tests/loader-composition.spec.ts @@ -8,7 +8,7 @@ import Loader from '@deepseek-ai/cordis-plugin-loader' import Include from '@deepseek-ai/cordis-plugin-include' import { CallId } from '@deepseek-ai/dsh-llm' import { Session, SessionId } from '@deepseek-ai/dsh-session' -import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' import TerminalSessionService from '@deepseek-ai/dsh-terminal' import * as TerminalLocal from '@deepseek-ai/dsh-terminal-bash' @@ -55,7 +55,7 @@ function agent(ctx: Context, cwd: string): Agent { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(value, { inbox: new Inbox(value.ctx, value) }) + Object.assign(value, { inbox: new Inbox(value.ctx, value.session, agentEvents(value.ctx, value)) }) ctx.agents.register(value) return value } diff --git a/packages/shell/tool-bash-persistent/tests/tools.spec.ts b/packages/shell/tool-bash-persistent/tests/tools.spec.ts index 5277c04a04..965b3dbbc5 100644 --- a/packages/shell/tool-bash-persistent/tests/tools.spec.ts +++ b/packages/shell/tool-bash-persistent/tests/tools.spec.ts @@ -2,7 +2,7 @@ import { afterEach, describe, expect, it } from 'vitest' import { Context } from '@deepseek-ai/cordis' import { CallId } from '@deepseek-ai/dsh-llm' import { Session, SessionId } from '@deepseek-ai/dsh-session' -import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' import TerminalSessionService from '@deepseek-ai/dsh-terminal' import type { @@ -50,7 +50,7 @@ function agent(ctx: Context, cwd: string | undefined): Agent { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(value, { inbox: new Inbox(value.ctx, value) }) + Object.assign(value, { inbox: new Inbox(value.ctx, value.session, agentEvents(value.ctx, value)) }) ctx.agents.register(value) return value } 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 1a95d7fe23..57abf1d2b1 100644 --- a/packages/shell/tool-pwsh-persistent/tests/loader-composition.spec.ts +++ b/packages/shell/tool-pwsh-persistent/tests/loader-composition.spec.ts @@ -9,7 +9,7 @@ import Loader from '@deepseek-ai/cordis-plugin-loader' import Include from '@deepseek-ai/cordis-plugin-include' import { CallId } from '@deepseek-ai/dsh-llm' import { Session, SessionId } from '@deepseek-ai/dsh-session' -import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' import TerminalSessionService from '@deepseek-ai/dsh-terminal' import * as TerminalBash from '@deepseek-ai/dsh-terminal-bash' @@ -51,7 +51,7 @@ function agent(ctx: Context, cwd: string): Agent { id, options: {}, session, - inbox: new Inbox(session, { inserted: () => {}, discarded: () => {}, claimed: () => {} }), + inbox: undefined as never, status: 'idle', ctx: scope.ctx, send: () => {}, @@ -62,6 +62,7 @@ function agent(ctx: Context, cwd: string): Agent { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } + Object.assign(value, { inbox: new Inbox(value.ctx, value.session, agentEvents(value.ctx, value)) }) ctx.agents.register(value) return value } diff --git a/packages/shell/tool-pwsh-persistent/tests/tools.spec.ts b/packages/shell/tool-pwsh-persistent/tests/tools.spec.ts index 8d76abd3a6..4cc139d9d3 100644 --- a/packages/shell/tool-pwsh-persistent/tests/tools.spec.ts +++ b/packages/shell/tool-pwsh-persistent/tests/tools.spec.ts @@ -2,7 +2,7 @@ import { afterEach, describe, expect, it } from 'vitest' import { Context } from '@deepseek-ai/cordis' import { CallId } from '@deepseek-ai/dsh-llm' import { Session, SessionId } from '@deepseek-ai/dsh-session' -import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' import TerminalSessionService from '@deepseek-ai/dsh-terminal' import type { @@ -39,7 +39,7 @@ function agent(ctx: Context, cwd: string | undefined): Agent { id, options: {}, session, - inbox: new Inbox(session, { inserted: () => {}, discarded: () => {}, claimed: () => {} }), + inbox: undefined as never, status: 'idle', ctx: scope.ctx, send: () => {}, @@ -50,6 +50,7 @@ function agent(ctx: Context, cwd: string | undefined): Agent { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } + Object.assign(value, { inbox: new Inbox(value.ctx, value.session, agentEvents(value.ctx, value)) }) ctx.agents.register(value) return value } diff --git a/packages/skill/tool-skill/tests/tool-skill.spec.ts b/packages/skill/tool-skill/tests/tool-skill.spec.ts index ff9e14bf5f..6d50d54b4d 100644 --- a/packages/skill/tool-skill/tests/tool-skill.spec.ts +++ b/packages/skill/tool-skill/tests/tool-skill.spec.ts @@ -54,7 +54,7 @@ function agentForCwd(cwd: string): Agent { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(agent, { inbox: new Inbox(agent.ctx, agent) }) + Object.assign(agent, { inbox: new Inbox(agent.ctx, agent.session, agentEvents(agent.ctx, agent)) }) return agent } @@ -74,7 +74,7 @@ function sessionAgent(session: Session, id = 'tool-skill-agent'): Agent { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(agent, { inbox: new Inbox(agent.ctx, agent) }) + Object.assign(agent, { inbox: new Inbox(agent.ctx, agent.session, agentEvents(agent.ctx, agent)) }) return agent } diff --git a/packages/terminal/terminal-bash/tests/index.spec.ts b/packages/terminal/terminal-bash/tests/index.spec.ts index a4d4b79617..66331fb870 100644 --- a/packages/terminal/terminal-bash/tests/index.spec.ts +++ b/packages/terminal/terminal-bash/tests/index.spec.ts @@ -4,7 +4,7 @@ import { resolve } from 'node:path' import { Context } from '@deepseek-ai/cordis' import Loader from '@deepseek-ai/cordis-plugin-loader' import SessionStore, { Session, SessionId } from '@deepseek-ai/dsh-session' -import AgentRegistry, { Inbox, type Agent } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { agentEvents, Inbox, type Agent } from '@deepseek-ai/dsh-agent' import SandboxProvider from '@deepseek-ai/dsh-sandbox' import type { ConfinedArgv, SandboxPolicy } from '@deepseek-ai/dsh-sandbox' import SandboxPolicyService, { setSandboxMode } from '@deepseek-ai/dsh-sandbox-policy' @@ -59,7 +59,7 @@ function agent(ctx: Context, cwd?: string): Agent { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(agent, { inbox: new Inbox(agent.ctx, agent) }) + Object.assign(agent, { inbox: new Inbox(agent.ctx, agent.session, agentEvents(agent.ctx, agent)) }) return agent } @@ -530,7 +530,7 @@ describe('terminal-bash plugin shape', () => { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(owner, { inbox: new Inbox(owner.ctx, owner) }) + Object.assign(owner, { inbox: new Inbox(owner.ctx, owner.session, agentEvents(owner.ctx, owner)) }) ctx.agents.register(owner) const providerFiber = await registerStubLocalBackend(ctx, () => stubLocalSession()) const created = await ctx.terminals.spawn(owner, { type: 'stub' }) @@ -580,7 +580,7 @@ describe('terminal-bash plugin shape', () => { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(owner, { inbox: new Inbox(owner.ctx, owner) }) + Object.assign(owner, { inbox: new Inbox(owner.ctx, owner.session, agentEvents(owner.ctx, owner)) }) ctx.agents.register(owner) const gate = Promise.withResolvers() await registerStubLocalBackend(ctx, () => stubLocalSession(() => gate.promise)) diff --git a/packages/terminal/terminal-bash/tests/local.spec.ts b/packages/terminal/terminal-bash/tests/local.spec.ts index 709daf0b3a..bafeffd8b0 100644 --- a/packages/terminal/terminal-bash/tests/local.spec.ts +++ b/packages/terminal/terminal-bash/tests/local.spec.ts @@ -5,7 +5,7 @@ import { join } from 'node:path' import { afterEach, describe, expect, it } from 'vitest' import { Context } from '@deepseek-ai/cordis' import { Session, SessionId } from '@deepseek-ai/dsh-session' -import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' import TerminalSessionService from '@deepseek-ai/dsh-terminal' import type { TerminalSendOperation } from '@deepseek-ai/dsh-terminal' @@ -46,7 +46,7 @@ function stubAgent(ctx: Context, rawId: string): Agent { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(agent, { inbox: new Inbox(agent.ctx, agent) }) + Object.assign(agent, { inbox: new Inbox(agent.ctx, agent.session, agentEvents(agent.ctx, agent)) }) return agent } diff --git a/packages/terminal/terminal/tests/service.spec.ts b/packages/terminal/terminal/tests/service.spec.ts index 7b73ebfbd7..7fb957be4e 100644 --- a/packages/terminal/terminal/tests/service.spec.ts +++ b/packages/terminal/terminal/tests/service.spec.ts @@ -1,7 +1,7 @@ import { describe, expect, expectTypeOf, it } from 'vitest' import { Context } from '@deepseek-ai/cordis' import { Session, SessionId } from '@deepseek-ai/dsh-session' -import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' import TerminalSessionService, { TerminalBackendCleanupError, TerminalError, TerminalSessionId } from '@deepseek-ai/dsh-terminal' import type { @@ -37,7 +37,7 @@ function stubAgent(ctx: Context, rawId: string): Agent { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(agent, { inbox: new Inbox(agent.ctx, agent) }) + Object.assign(agent, { inbox: new Inbox(agent.ctx, agent.session, agentEvents(agent.ctx, agent)) }) agentScopeDisposers.set(agent, async () => { await scopeFiber.dispose() }) return agent } diff --git a/packages/terminal/tool-terminal/tests/loader-composition.spec.ts b/packages/terminal/tool-terminal/tests/loader-composition.spec.ts index 1262156be2..35df0d2d46 100644 --- a/packages/terminal/tool-terminal/tests/loader-composition.spec.ts +++ b/packages/terminal/tool-terminal/tests/loader-composition.spec.ts @@ -8,7 +8,7 @@ import Loader from '@deepseek-ai/cordis-plugin-loader' import Include from '@deepseek-ai/cordis-plugin-include' import { CallId } from '@deepseek-ai/dsh-llm' import { Session, SessionId } from '@deepseek-ai/dsh-session' -import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' import SystemPrompt from '@deepseek-ai/dsh-system-prompt' import ToolRuntime from '@deepseek-ai/dsh-tools' @@ -49,7 +49,7 @@ function agent(ctx: Context): Agent { runMaintenance: job => job(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(value, { inbox: new Inbox(value.ctx, value) }) + Object.assign(value, { inbox: new Inbox(value.ctx, value.session, agentEvents(value.ctx, value)) }) ctx.agents.register(value) return value } diff --git a/packages/terminal/tool-terminal/tests/tools.spec.ts b/packages/terminal/tool-terminal/tests/tools.spec.ts index 4c50898355..e2692f0f74 100644 --- a/packages/terminal/tool-terminal/tests/tools.spec.ts +++ b/packages/terminal/tool-terminal/tests/tools.spec.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest' import { Context } from '@deepseek-ai/cordis' import { CallId } from '@deepseek-ai/dsh-llm' import { Session, SessionId } from '@deepseek-ai/dsh-session' -import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' import SystemPrompt from '@deepseek-ai/dsh-system-prompt' import ToolRuntime, { renderToolsSdk } from '@deepseek-ai/dsh-tools' @@ -26,7 +26,7 @@ function fakeAgent(ctx: Context, rawId: string): Agent { runMaintenance: job => job(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(agent, { inbox: new Inbox(agent.ctx, agent) }) + Object.assign(agent, { inbox: new Inbox(agent.ctx, agent.session, agentEvents(agent.ctx, agent)) }) ctx.agents.register(agent) return agent } diff --git a/packages/test-support/agent-loop-testkit/src/index.ts b/packages/test-support/agent-loop-testkit/src/index.ts index 8b327edc4b..7867d00c1e 100644 --- a/packages/test-support/agent-loop-testkit/src/index.ts +++ b/packages/test-support/agent-loop-testkit/src/index.ts @@ -7,7 +7,6 @@ import type { Context } from '@deepseek-ai/cordis' import AgentRegistry from '@deepseek-ai/dsh-agent' -import InboxService from '@deepseek-ai/dsh-agent/inbox' import LlmRuntime from '@deepseek-ai/dsh-llm' import SessionStore from '@deepseek-ai/dsh-session' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' @@ -43,7 +42,6 @@ export async function mountAgentLoopTestDependencies( await ctx.plugin(LlmRuntime) await ctx.plugin(SessionStore) await ctx.plugin(SessionProjectionRegistry) - await ctx.plugin(InboxService) await ctx.plugin(SystemPrompt, options.systemPrompt ?? {}) await ctx.plugin(ToolRuntime, options.tools ?? {}) await ctx.plugin(AgentRegistry) diff --git a/packages/todo/tool-todo/tests/loader-composition.spec.ts b/packages/todo/tool-todo/tests/loader-composition.spec.ts index 24199f8f10..63ba14ed76 100644 --- a/packages/todo/tool-todo/tests/loader-composition.spec.ts +++ b/packages/todo/tool-todo/tests/loader-composition.spec.ts @@ -11,7 +11,7 @@ import Loader from '@deepseek-ai/cordis-plugin-loader' import Include from '@deepseek-ai/cordis-plugin-include' import { CallId } from '@deepseek-ai/dsh-llm' import { Session, SessionId } from '@deepseek-ai/dsh-session' -import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent' +import AgentRegistry, { agentEvents, Inbox } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' import SystemPrompt from '@deepseek-ai/dsh-system-prompt' import ToolRuntime from '@deepseek-ai/dsh-tools' @@ -38,7 +38,7 @@ function agent(ctx: Context): Agent { runMaintenance: task => task(new AbortController().signal), whenIdle: () => Promise.resolve(), } - Object.assign(value, { inbox: new Inbox(value.ctx, value) }) + Object.assign(value, { inbox: new Inbox(value.ctx, value.session, agentEvents(value.ctx, value)) }) ctx.agents.register(value) return value } diff --git a/scripts/gen-cordis-catalog.ts b/scripts/gen-cordis-catalog.ts index 274d0307e4..e932ff9fa6 100644 --- a/scripts/gen-cordis-catalog.ts +++ b/scripts/gen-cordis-catalog.ts @@ -54,7 +54,6 @@ export const SERVICE_PAGE: Record = { agentDefaultModel: 'core.md', agentPresets: 'core.md', agents: 'core.md', - inboxes: 'core.md', apiProxy: 'typert.md', approval: 'approval.md', attachments: 'attachment.md', diff --git a/scripts/gen-doc-graphs.ts b/scripts/gen-doc-graphs.ts index 24550a0a3c..420bad476c 100644 --- a/scripts/gen-doc-graphs.ts +++ b/scripts/gen-doc-graphs.ts @@ -139,14 +139,6 @@ const SERVICE_ROLES: ServiceRole[] = [ consumers: ['agent-loop', 'agent', 'session-persistence', 'session-query', 'session-query-sqlite', 'subagent-inprocess', 'invariants', 'message-feedback'], note: 'Owns append-only Session instances and emits the durable session event feed.', }, - { - key: 'inboxes', - pkg: 'agent', - title: 'Durable pending-input facade', - mode: 'core', - consumers: ['agent-loop'], - note: 'Registers the standard Inbox projection and creates command facades over its sole live state.', - }, { key: 'invariants', pkg: 'invariants', diff --git a/tsconfig.base.json b/tsconfig.base.json index 242e848c5e..91e623e2d1 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -98,8 +98,6 @@ "@deepseek-ai/dsh-user-questions/types": ["./packages/interaction/user-questions/src/types.ts"], "@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-agent/inbox": ["./packages/core/agent/src/inbox.ts"], - "@deepseek-ai/dsh-agent/inbox-projection": ["./packages/core/agent/src/inbox-projection.ts"], "@deepseek-ai/dsh-agent/invariant": ["./packages/core/agent/src/invariant.ts"], "@deepseek-ai/dsh-scope/invariant": ["./packages/core/scope/src/invariant.ts"], "@deepseek-ai/dsh-agent-loop/invariant": ["./packages/core/agent-loop/src/invariant.ts"],