refactor(agent): remove inbox service

This commit is contained in:
_Kerman
2026-08-31 13:34:22 +08:00
parent df2bae9639
commit bd3b651ea8
86 changed files with 160 additions and 344 deletions
@@ -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
@@ -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
@@ -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 镜像。
## 曾考虑的替代方案
-3
View File
@@ -30,8 +30,6 @@ flowchart LR
cfg --> plugin_dsh_base_user_questions
plugin_dsh_base_agent["agent<br/>@deepseek-ai/dsh-agent"]
cfg --> plugin_dsh_base_agent
plugin_dsh_base_agent_inbox["agent-inbox<br/>@deepseek-ai/dsh-agent/inbox"]
cfg --> plugin_dsh_base_agent_inbox
plugin_dsh_base_agent_default_model["agent-default-model<br/>@deepseek-ai/dsh-agent-default-model"]
cfg --> plugin_dsh_base_agent_default_model
plugin_dsh_base_jobs["jobs<br/>@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` |
+2 -2
View File
@@ -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 },
+2 -2
View File
@@ -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
-4
View File
@@ -31,7 +31,6 @@ flowchart LR
pkg_subagent_inprocess["subagent-inprocess"]
pkg_invariants["invariants"]
pkg_message_feedback["message-feedback"]
svc_inboxes["ctx.inboxes<br/>Durable pending-input facade"]
svc_invariants["ctx.invariants<br/>Package-owned invariant registry"]
pkg_scope["scope"]
pkg_typert_registry["typert-registry"]
@@ -202,7 +201,6 @@ flowchart LR
svc_cordisInspect["ctx.cordisInspect<br/>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. |
-4
View File
@@ -33,7 +33,6 @@ flowchart LR
pkg_subagent_inprocess["subagent-inprocess"]
pkg_invariants["invariants"]
pkg_message_feedback["message-feedback"]
svc_inboxes["ctx.inboxes<br/>Durable pending-input facade"]
svc_invariants["ctx.invariants<br/>Package-owned invariant registry"]
pkg_scope["scope"]
pkg_typert_registry["typert-registry"]
@@ -204,7 +203,6 @@ flowchart LR
svc_cordisInspect["ctx.cordisInspect<br/>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 载体提供一元调用。 |
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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)
<a id="deepseek-aidsh-agent-tool-presentation"></a>
+2 -2
View File
@@ -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)
<a id="deepseek-aidsh-agent-tool-presentation"></a>
+2 -2
View File
@@ -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
+2 -1
View File
@@ -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) |
+2 -1
View File
@@ -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) |
+2 -2
View File
@@ -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
+2 -19
View File
@@ -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)
<a id="ctxinboxes--inboxservice"></a>
### `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)
<a id="agent-events"></a>
+2 -19
View File
@@ -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)
<a id="ctxinboxes--inboxservice"></a>
### `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)
<a id="agent-events"></a>
@@ -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<Context> {
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<Context> {
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)
+2 -2
View File
@@ -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<ReturnType<typeof ctx.terminals.spawn>>['sessionId'] | undefined
try {
-3
View File
@@ -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
@@ -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<Agent>)
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)
@@ -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<string> {
@@ -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)
@@ -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
}
@@ -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
}
+1
View File
@@ -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:^",
+2 -3
View File
@@ -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)
+1 -1
View File
@@ -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({
@@ -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<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)
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)
@@ -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<Context> {
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)
@@ -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)
@@ -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<Context> {
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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<Context> {
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)
@@ -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<Context> {
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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
@@ -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)
+2 -2
View File
@@ -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
+1 -1
View File
@@ -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).
+1 -1
View File
@@ -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) 记录的完整流水线。
-8
View File
@@ -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"
@@ -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<typeof inboxProjectionSchema>
/** 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. */
+14 -61
View File
@@ -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
+6
View File
@@ -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',
+9 -10
View File
@@ -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({})
})
+2 -2
View File
@@ -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,
@@ -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 ?? {})
@@ -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<K extends AgentSubjectEvent>(name: K, payload: PayloadRest<K>): void;\n serial<K extends AgentSubjectEvent>(name: K, payload: PayloadRest<K>): Promise<Awaited<Return<Events[K]>>>;\n waterfall<K extends AgentSubjectEvent>(name: K, payload: PayloadRest<K>, ...rest: Tail<K>): Return<Events[K]>;\n}',
},
{
name: 'AgentFactory',
declaration: 'export interface AgentFactory {\n createAgent(ownerCtx: Context, options: CreateAgentOptions): Promise<AgentHandle>;\n resume(ownerCtx: Context, options: ResumeAgentOptions): Promise<AgentHandle>;\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<Agent>, ...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',
@@ -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 }
}
@@ -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
}
@@ -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
}
@@ -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 }
}
+3 -7
View File
@@ -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<Session, StubAgent>()
/** 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)
@@ -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)
@@ -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 }
}
@@ -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
}
+2 -2
View File
@@ -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: <T>(job: (signal: AbortSignal) => Promise<T>) => 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
}
@@ -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<Context> {
['@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'",
])
@@ -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<Context> {
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)
@@ -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<Config> = {}): Promise<Context> {
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)
@@ -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)
@@ -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)
@@ -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<RuntimeHarness> {
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')
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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<undefined>()
await registerStubLocalBackend(ctx, () => stubLocalSession(() => gate.promise))
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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)
@@ -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
}
-1
View File
@@ -54,7 +54,6 @@ export const SERVICE_PAGE: Record<string, string> = {
agentDefaultModel: 'core.md',
agentPresets: 'core.md',
agents: 'core.md',
inboxes: 'core.md',
apiProxy: 'typert.md',
approval: 'approval.md',
attachments: 'attachment.md',
-8
View File
@@ -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',
-2
View File
@@ -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"],