@deepseek-ai/dsh-agent-loop-testkit 0.1.3-alpha.2 → 0.1.5-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/test-support/agent-loop-testkit/README.md
5
- README.md: 7b935594636e265bf4f808f692bba2fae0f83ddf
6
- README.zh.md: fc9dc688a1e7d4303f7744ed47c7da2d0fbbf67f
5
+ README.md: abcd5966399998fbbeea5ac7573a9ee4085e05ca
6
+ README.zh.md: 76a6a43b07acd65aa135536d41a5c9c1ec122e16
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Shared service mounting for tests that exercise the concrete AgentLoop, for test authors wiring real loop prerequisites."
2
+ description: "Prerequisite mounting, production AgentLoop drivers, and explicit Inbox stubs for agent-loop tests."
3
3
  kind: "package-library"
4
4
  ---
5
5
 
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
11
 
12
- `dsh-agent-loop-testkit` mounts the standard prerequisite services a test needs before loading the concrete `AgentLoop` the LLM runtime, session store, system-prompt registry, tool registry, and agent registry in dependency order, with one call. The loop itself, adapters, optional plugins, agents, and teardown stay in the test's hands, so each scenario keeps its own load order and topology. Use it when a test's subject is loop behavior rather than service wiring; tests that probe injection failures or partial topologies mount their dependencies directly. It registers no model-facing behavior of its own.
12
+ Use `dsh-agent-loop-testkit` to give AgentLoop tests the standard prerequisites and a production loop driver without repeating setup. The harness creates real Agents and exposes Inbox input claiming for tests of durable events, recovery, notifications, and claim behavior. For consumer tests that need only queue editing, choose the process-local Inbox stub; choose the fail-fast Inbox when pending input must never be touched. Tests still own adapters, optional plugins, load order, and context disposal, and the package adds no model-visible behavior.
13
13
 
14
14
  ## Table of Contents
15
15
 
@@ -25,31 +25,54 @@ English | [中文](README.zh.md)
25
25
  <a id="use-this-package"></a>
26
26
  ## Use this package
27
27
 
28
- This package gives an AgentLoop test a working service topology before the loop is mounted: call the helper on your test context, then mount `AgentLoop` with the configuration under test and register your adapter and optional plugins.
28
+ This package gives an AgentLoop test a working service topology and keeps the choice between production Inbox behavior and a structural stub explicit.
29
29
 
30
- ### Minimal example
30
+ ### Drive a production Agent
31
+
32
+ Use `mountAgentLoopTestHarness()` when the test covers durable Inbox events, projection recovery or validation, live Inbox notifications, or loop-driver claims. Mount any load-order-sensitive consumers after the prerequisites and before creating the Agent. The context owns the loop and every Agent returned by the harness.
31
33
 
32
34
  ```ts
33
35
  import { Context } from '@deepseek-ai/cordis'
34
- import AgentLoop from '@deepseek-ai/dsh-agent-loop'
35
- import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
36
+ import { SessionId, type UserMessage } from '@deepseek-ai/dsh-session'
37
+ import {
38
+ mountAgentLoopTestDependencies,
39
+ mountAgentLoopTestHarness,
40
+ } from '@deepseek-ai/dsh-agent-loop-testkit'
36
41
 
37
42
  const ctx = new Context()
38
43
 
39
44
  await mountAgentLoopTestDependencies(ctx)
40
- // Register the test adapter and any optional plugins here.
41
- await ctx.plugin(AgentLoop, { agents: [] })
45
+ // Register the test adapter and any load-order-sensitive plugins here.
46
+ const harness = await mountAgentLoopTestHarness(ctx)
47
+ const agent = await harness.create(SessionId('test-agent'))
48
+ declare const message: UserMessage
49
+
50
+ agent.inbox.append('next-turn', message)
51
+ const admitted = harness.claim(agent, 'next-turn', 1)
42
52
  ```
43
53
 
44
- The helper activates the LLM, session, system-prompt, tool, and agent services in dependency order and returns before the loop is mounted. System-prompt and tool-registry configuration can be forwarded through `options`; the helper provides no test defaults beyond those the services own.
54
+ The dependency helper forwards system-prompt and tool-registry configuration through `options` and provides no test defaults beyond those services' own defaults. A plugin-load failure rejects the helper call; services activated earlier in the sequence remain context-owned and unwind when the context is disposed.
55
+
56
+ ### Build a structural Agent stub
57
+
58
+ Use `createInboxStub()` when the test subject needs mutable pending lists but does not exercise durability, projection validation, live Inbox notifications, or the driver's claim policy. The stub implements the public queue operations with two process-local arrays and never writes to a Session. Use `unsupportedInbox()` when the test subject must not touch pending input; every mutation throws at the first unexpected dependency.
59
+
60
+ ```ts
61
+ import { createInboxStub } from '@deepseek-ai/dsh-agent-loop-testkit'
62
+
63
+ const agent = {
64
+ // ...
65
+ inbox: createInboxStub(),
66
+ }
67
+ ```
45
68
 
46
69
  ### When to use it
47
70
 
48
- Use the helper for tests whose subject is the loop: load order, retries, tool execution, or session behavior on a real prerequisite stack. Mount dependencies directly when a test probes service load order, injection failures, partial topologies, or teardown — the helper hides exactly the wiring such tests must control.
71
+ Use the dependency and loop helpers for tests whose subject is production loop or durable Inbox behavior. Use the structural stub for consumer-domain tests that only need queue editing. Mount dependencies directly when a test probes service injection failures or partial topologies, because the helper hides exactly the wiring those tests must control.
49
72
 
50
73
  ### What can go wrong
51
74
 
52
- A plugin-load failure rejects the helper call; services activated earlier in the sequence remain owned by your context and unwind with it. The context owns every mounted service, so dispose it after the test.
75
+ The harness mounts no LLM adapter. Register an adapter before sending work that would start a model request. Dispose the owning context after every test so Agents reach quiescence and their scoped registrations unwind.
53
76
 
54
77
  -----
55
78
 
@@ -59,11 +82,11 @@ A plugin-load failure rejects the helper call; services activated earlier in the
59
82
  <details>
60
83
  <summary>Implementation internals — click to expand</summary>
61
84
 
62
- This section explains the design of the helper; the observable behavior is fully covered in [Use this package](#use-this-package).
85
+ This section explains the design of the test utilities; the observable behavior is fully covered in [Use this package](#use-this-package).
63
86
 
64
87
  ### Design
65
88
 
66
- **Runtime invariant:** No companion is published. This test-support package owns no production event stream or mutable data; consuming test suites exercise its behavior.
89
+ `mountAgentLoopTestDependencies` mounts six service plugins in a fixed dependency order — LLM, session, session-projection registry, system-prompt registry, tool registry, then agent registry — and stops before `AgentLoop`, so the caller controls loop load order. `mountAgentLoopTestHarness` mounts the public production plugin, creates Agents through its service, and exposes the production driver's claim operation without exporting the loop's concrete Inbox class or projection definition. [`src/inbox.ts`](src/inbox.ts) contains only the process-local mutable stub and the fail-fast unsupported placeholder; it owns no projection or durable event implementation. The mounting and driver implementation lives in [`src/index.ts`](src/index.ts). No invariant companion is published because the package owns only test helpers and has no independent production observations that can diverge.
67
90
 
68
91
  </details>
69
92
 
@@ -72,11 +95,11 @@ This section explains the design of the helper; the observable behavior is fully
72
95
  <a id="further-exploration"></a>
73
96
  ## Further Exploration
74
97
 
75
- Read these pages when the package-level contract is not enough. They move from the loop to the services the helper mounts and the tests that use it.
98
+ Read these pages when the package-level behavior is not enough. They move from the loop to the services the helper mounts and the tests that use it.
76
99
 
77
- - [Agent loop package](../../core/agent-loop/README.md) — the concrete loop this helper prepares tests for.
78
- - [Session package](../../core/session/README.md) — the session store the helper mounts.
79
- - [LLM package](../../llm/llm/README.md) — the LLM runtime and adapter contract the helper mounts.
100
+ - [Agent loop package](../../core/agent-loop/README.md) — the concrete loop this helper mounts for production behavior.
101
+ - [Session package](../../core/session/README.md) — the durable event log used by production Inbox behavior.
102
+ - [LLM package](../../llm/llm/README.md) — the LLM runtime and adapter interface the helper prepares.
80
103
  - [Testing policy](../../../docs/testing.md) — the coverage tiers these tests serve.
81
104
  - [Test-support group map](../README.md) — sibling harnesses and support packages.
82
105
 
@@ -85,20 +108,22 @@ Read these pages when the package-level contract is not enough. They move from t
85
108
  <a id="model-experience"></a>
86
109
  ## Model Experience
87
110
 
88
- None, as this test-only composition helper neither drives nor modifies model requests.
111
+ None, as these test-only utilities neither assemble nor modify model requests.
89
112
 
90
113
  #### KV Cache effect
91
114
 
92
- None; this package neither assembles nor sends a provider request.
115
+ None; the package itself sends no provider request.
93
116
 
94
117
  ## Known Limitations and Deferred Work
95
118
 
96
119
  <a id="known-limitations-and-deferred-work"></a>
97
120
 
121
+ These limits define what the utilities do not share. They are current package constraints, not a task backlog.
98
122
 
99
- These limits define what the helper does not share. They are current package constraints, not a task backlog.
100
-
101
- - **Only the mandatory prerequisite spine is shared** — adapters, optional plugins, `AgentLoop`, agents, and context teardown remain caller-owned so scenario-specific ordering stays visible.
123
+ - **Only the mandatory prerequisite spine is shared** adapters, optional plugins, scenario-specific load order, and context teardown remain caller-owned.
124
+ - **The production harness has no adapter default** — tests that start the loop must register the route they exercise.
125
+ - **The mutable Inbox stub is process-local only** — use a harness-created Agent whenever durable events, projection recovery or validation, live notifications, or claim policy matter.
126
+ - **The unsupported Inbox accepts no mutations** — use the mutable stub or a harness-created Agent whenever pending input is part of the test subject.
102
127
 
103
128
  <a id="dev-note"></a>
104
129
  ### Dev Note
package/README.zh.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "为运行具体 AgentLoop 的测试挂载共享服务先决依赖,面向接线真实循环前置依赖的测试作者。"
2
+ description: " agent-loop 测试提供先决依赖挂载、生产 AgentLoop 驱动与职责明确的 Inbox 桩。"
3
3
  kind: "package-library"
4
4
  ---
5
5
 
@@ -9,7 +9,7 @@ kind: "package-library"
9
9
 
10
10
  ## 概述
11
11
 
12
- `dsh-agent-loop-testkit` 为测试在加载具体 `AgentLoop` 之前所需的全部标准先决服务——LLM(大语言模型)运行时、会话存储、系统提示词注册表、工具注册表与 agent(智能体)注册表——按依赖顺序一键挂载。loop 本身、适配器、可选插件、agent 与清理仍由测试掌控,因此每个场景都保持自己的加载顺序与拓扑。当测试对象是 loop 行为而非服务接线时使用它;针对注入失败或部分拓扑的测试会直接挂载其依赖。它自身不注册任何模型可见行为。
12
+ 使用 `dsh-agent-loop-testkit` 可以为 AgentLoop 测试准备标准先决条件和生产 loop 驱动,避免重复设置。Harness 可以创建真实 Agent,并公开 Inbox 输入认领能力,以测试持久事件、恢复、通知和认领行为。只需编辑队列的消费方测试应选择进程内 Inbox 桩;待处理输入绝不应被访问时,应选择快速失败的 Inbox。测试仍然负责适配器、可选插件、加载顺序和上下文释放,本包不会添加模型可见行为。
13
13
 
14
14
  ## 目录
15
15
 
@@ -25,31 +25,54 @@ kind: "package-library"
25
25
  <a id="use-this-package"></a>
26
26
  ## 使用本包
27
27
 
28
- 本包在 loop 挂载前为 AgentLoop 测试提供可用的服务拓扑:在测试上下文上调用此辅助函数,然后用待测配置挂载 `AgentLoop`,并注册你的适配器与可选插件。
28
+ 本包为 AgentLoop 测试提供可用的服务拓扑,并要求测试明确选择生产 Inbox 行为或结构化桩。
29
29
 
30
- ### 最小示例
30
+ ### 驱动生产 Agent
31
+
32
+ 当测试覆盖持久 Inbox 事件、投影恢复或校验、实时 Inbox 通知,或 loop 驱动的认领策略时,使用 `mountAgentLoopTestHarness()`。应在挂载先决依赖后、创建 Agent 前挂载所有对加载顺序敏感的消费方。上下文拥有 loop 以及该 harness 返回的每个 Agent。
31
33
 
32
34
  ```ts
33
35
  import { Context } from '@deepseek-ai/cordis'
34
- import AgentLoop from '@deepseek-ai/dsh-agent-loop'
35
- import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
36
+ import { SessionId, type UserMessage } from '@deepseek-ai/dsh-session'
37
+ import {
38
+ mountAgentLoopTestDependencies,
39
+ mountAgentLoopTestHarness,
40
+ } from '@deepseek-ai/dsh-agent-loop-testkit'
36
41
 
37
42
  const ctx = new Context()
38
43
 
39
44
  await mountAgentLoopTestDependencies(ctx)
40
- // Register the test adapter and any optional plugins here.
41
- await ctx.plugin(AgentLoop, { agents: [] })
45
+ // Register the test adapter and any load-order-sensitive plugins here.
46
+ const harness = await mountAgentLoopTestHarness(ctx)
47
+ const agent = await harness.create(SessionId('test-agent'))
48
+ declare const message: UserMessage
49
+
50
+ agent.inbox.append('next-turn', message)
51
+ const admitted = harness.claim(agent, 'next-turn', 1)
42
52
  ```
43
53
 
44
- 该辅助函数按依赖顺序激活 LLM、会话、系统提示词、工具与 agent 服务,并在 loop 挂载前返回。系统提示词与工具注册表配置可通过 `options` 转发;除服务自有的默认值外,本辅助函数不提供测试默认值。
54
+ 依赖辅助函数通过 `options` 转发系统提示词与工具注册表配置,除这些服务自有的默认值外不提供测试默认值。插件加载失败会使辅助函数调用被拒绝;顺序中较早激活的服务仍归上下文所有,并在上下文释放时一并解除。
55
+
56
+ ### 构造结构化 Agent 桩
57
+
58
+ 当测试对象需要可变的待处理列表,但不测试持久性、投影校验、实时 Inbox 通知或驱动的认领策略时,使用 `createInboxStub()`。该桩通过两个进程内数组实现公开队列操作,且绝不会写入 Session。当测试对象不应访问待处理输入时,使用 `unsupportedInbox()`;每次变更都会在首个意外依赖处抛错。
59
+
60
+ ```ts
61
+ import { createInboxStub } from '@deepseek-ai/dsh-agent-loop-testkit'
62
+
63
+ const agent = {
64
+ // ...
65
+ inbox: createInboxStub(),
66
+ }
67
+ ```
45
68
 
46
69
  ### 何时使用
47
70
 
48
- 当测试对象是 loop 本身——在真实先决依赖栈上的加载顺序、重试、工具执行或会话行为——时使用此辅助函数。当测试要探测服务加载顺序、注入失败、部分拓扑或清理时,请直接挂载依赖——辅助函数隐藏的正是这类测试必须控制的接线。
71
+ 当测试对象是生产 loop 或持久 Inbox 行为时,使用依赖与 loop 辅助函数。只需要编辑队列的消费方领域测试使用结构化桩。当测试探测服务注入失败或部分拓扑时,请直接挂载依赖,因为辅助函数隐藏的正是这类测试必须控制的接线。
49
72
 
50
73
  ### 可能出什么问题
51
74
 
52
- 插件加载失败会使辅助函数调用被拒绝;顺序中较早激活的服务仍归你的上下文所有,并随上下文一起解除。上下文拥有所有已挂载服务,因此测试结束后请 dispose(资源释放)它。
75
+ harness 不会挂载任何 LLM 适配器。若测试发送的任务会启动模型请求,请先注册被测路由的适配器。每个测试结束后都应释放所属上下文,使 Agent 达到静止状态并解除其作用域注册。
53
76
 
54
77
  -----
55
78
 
@@ -59,11 +82,11 @@ await ctx.plugin(AgentLoop, { agents: [] })
59
82
  <details>
60
83
  <summary>实现细节——点击展开</summary>
61
84
 
62
- 本节解释辅助函数的设计;可观察行为已在[使用本包](#use-this-package)中完整说明。
85
+ 本节解释测试辅助工具的设计;可观察行为已在[使用本包](#use-this-package)中完整说明。
63
86
 
64
87
  ### 设计
65
88
 
66
- **运行时不变式:** 不发布伴生入口。本包不持有生产事件流或可变数据;消费它的测试套件会直接检验 harness 行为。
89
+ `mountAgentLoopTestDependencies` 按固定依赖顺序——LLM、会话、会话投影注册表、系统提示词注册表、工具注册表、agent 注册表——挂载六个服务插件,并在 `AgentLoop` 之前停下,使调用方控制 loop 加载顺序。`mountAgentLoopTestHarness` 挂载公开的生产插件,通过其服务创建 Agent,并公开生产驱动的认领操作,而不导出 loop 的具体 Inbox 类或投影定义。[`src/inbox.ts`](src/inbox.ts) 仅包含进程内可变桩和快速失败且不支持操作的占位值;它不持有投影或持久事件实现。挂载与驱动实现位于 [`src/index.ts`](src/index.ts)。本包不发布 invariant companion,因为它只持有测试辅助工具,不存在可能相互偏离的独立生产观测。
67
90
 
68
91
  </details>
69
92
 
@@ -72,11 +95,11 @@ await ctx.plugin(AgentLoop, { agents: [] })
72
95
  <a id="further-exploration"></a>
73
96
  ## 进一步探索
74
97
 
75
- 当包级约定不够用时阅读以下页面。它们从 loop 逐步进入辅助函数挂载的服务以及使用它的测试。
98
+ 当包级行为不够用时阅读以下页面。它们从 loop 逐步进入辅助函数挂载的服务以及使用它的测试。
76
99
 
77
- - [Agent loop 包](../../core/agent-loop/README.zh.md)——本辅助函数为之准备测试的具体 loop。
78
- - [会话包](../../core/session/README.zh.md)——辅助函数挂载的会话存储。
79
- - [LLM 包](../../llm/llm/README.zh.md)——辅助函数挂载的 LLM 运行时与适配器约定。
100
+ - [Agent loop 包](../../core/agent-loop/README.zh.md)——本辅助函数为生产行为挂载的具体 loop。
101
+ - [会话包](../../core/session/README.zh.md)——生产 Inbox 行为使用的持久事件日志。
102
+ - [LLM 包](../../llm/llm/README.zh.md)——本辅助函数准备的 LLM 运行时与适配器接口。
80
103
  - [测试策略](../../../docs/testing.zh.md)——这些测试所服务的覆盖层级。
81
104
  - [test-support 组地图](../README.zh.md)——兄弟 harness 与支持包。
82
105
 
@@ -85,20 +108,22 @@ await ctx.plugin(AgentLoop, { agents: [] })
85
108
  <a id="model-experience"></a>
86
109
  ## 模型体验
87
110
 
88
- 无。该测试专用组合辅助函数既不驱动也不修改模型请求。
111
+ 无。这些测试专用辅助工具既不组装也不修改模型请求。
89
112
 
90
113
  #### KV Cache 影响
91
114
 
92
- 无;本包既不组装也不发送提供方请求。
115
+ 无;本包自身不发送提供方请求。
93
116
 
94
117
  ## 已知限制与延期工作
95
118
 
96
119
  <a id="known-limitations-and-deferred-work"></a>
97
120
 
121
+ 这些限制说明辅助工具不共享什么。它们是当前包约束,不是任务积压。
98
122
 
99
- 这些限制说明辅助函数不共享什么。它们是当前包约束,不是任务积压。
100
-
101
- - **只共享必需的先决主干**——适配器、可选插件、`AgentLoop`、agent 与上下文清理仍由调用方负责,以使特定场景的挂载顺序清晰可见。
123
+ - **只共享必需的先决主干**——适配器、可选插件、场景特定的加载顺序与上下文清理仍由调用方负责。
124
+ - **生产 harness 没有适配器默认值**——启动 loop 的测试必须注册其实际使用的路由。
125
+ - **可变 Inbox 桩仅存在于进程内**——只要持久事件、投影恢复或校验、实时通知或认领策略属于测试对象,就应使用 harness 创建的 Agent。
126
+ - **不支持操作的 Inbox 不接受变更**——只要待处理输入属于测试对象,就应使用可变桩或 harness 创建的 Agent。
102
127
 
103
128
  <a id="dev-note"></a>
104
129
  ### 开发备注
package/lib/index.js CHANGED
@@ -1,13 +1,90 @@
1
1
  import AgentRegistry from "@deepseek-ai/dsh-agent";
2
+ import AgentLoop from "@deepseek-ai/dsh-agent-loop";
2
3
  import LlmRuntime from "@deepseek-ai/dsh-llm";
3
4
  import SessionStore from "@deepseek-ai/dsh-session";
5
+ import SessionProjectionRegistry from "@deepseek-ai/dsh-session-projection";
4
6
  import SystemPrompt from "@deepseek-ai/dsh-system-prompt";
5
7
  import ToolRuntime from "@deepseek-ai/dsh-tools";
8
+ //#region lib/types/inbox.js
9
+ /**
10
+ * Create a mutable in-memory Inbox stub for tests that exercise only the public
11
+ * queue operations. Durable events, projection validation, and live Inbox
12
+ * notifications require a real Agent created by the AgentLoop test harness.
13
+ * @returns an Inbox backed by two process-local arrays.
14
+ */
15
+ function createInboxStub() {
16
+ const pending = {
17
+ "next-turn": [],
18
+ "next-step": []
19
+ };
20
+ const locate = (messageId) => {
21
+ for (const target of ["next-turn", "next-step"]) {
22
+ const index = pending[target].findIndex((message) => message.id === messageId);
23
+ if (index >= 0) return {
24
+ target,
25
+ index
26
+ };
27
+ }
28
+ };
29
+ return {
30
+ get nextTurn() {
31
+ return pending["next-turn"];
32
+ },
33
+ get nextStep() {
34
+ return pending["next-step"];
35
+ },
36
+ clear() {
37
+ pending["next-step"].splice(0);
38
+ pending["next-turn"].splice(0);
39
+ },
40
+ append(target, message) {
41
+ pending[target].push(message);
42
+ },
43
+ prepend(target, message) {
44
+ pending[target].unshift(message);
45
+ },
46
+ replace(messageId, message) {
47
+ const location = locate(messageId);
48
+ if (location === void 0) return false;
49
+ pending[location.target].splice(location.index, 1, message);
50
+ return true;
51
+ },
52
+ remove(messageId) {
53
+ const location = locate(messageId);
54
+ if (location === void 0) return false;
55
+ pending[location.target].splice(location.index, 1);
56
+ return true;
57
+ },
58
+ splice(target, start, deleteCount, inserted) {
59
+ return pending[target].splice(start, deleteCount, ...inserted);
60
+ }
61
+ };
62
+ }
63
+ /**
64
+ * Create an unsupported Inbox placeholder for Agent stubs whose tests do not exercise Inbox behavior.
65
+ * @returns an Inbox whose pending lists are empty and whose mutation methods throw.
66
+ */
67
+ function unsupportedInbox() {
68
+ const rejectMutation = () => {
69
+ throw new Error("this test Agent does not support Inbox mutations");
70
+ };
71
+ return {
72
+ nextTurn: [],
73
+ nextStep: [],
74
+ clear: rejectMutation,
75
+ append: rejectMutation,
76
+ prepend: rejectMutation,
77
+ replace: rejectMutation,
78
+ remove: rejectMutation,
79
+ splice: rejectMutation
80
+ };
81
+ }
82
+ //#endregion
6
83
  //#region lib/types/index.js
7
84
  /**
8
- * Shared mounting for the services required before tests load the concrete
9
- * agent loop. The caller retains ownership of the context, loop, adapters,
10
- * optional plugins, and teardown.
85
+ * Shared service mounting, real AgentLoop drivers, and structural Inbox stubs
86
+ * for agent-loop tests. Callers retain ownership of their contexts, adapters,
87
+ * optional plugins, agents, and teardown.
11
88
  * @module @deepseek-ai/dsh-agent-loop-testkit
12
89
  */
13
90
  /**
@@ -25,9 +102,25 @@ import ToolRuntime from "@deepseek-ai/dsh-tools";
25
102
  async function mountAgentLoopTestDependencies(ctx, options = {}) {
26
103
  await ctx.plugin(LlmRuntime);
27
104
  await ctx.plugin(SessionStore);
105
+ await ctx.plugin(SessionProjectionRegistry);
28
106
  await ctx.plugin(SystemPrompt, options.systemPrompt ?? {});
29
107
  await ctx.plugin(ToolRuntime, options.tools ?? {});
30
108
  await ctx.plugin(AgentRegistry);
31
109
  }
110
+ /**
111
+ * Mount the production AgentLoop and expose its narrow test-driver operations.
112
+ * Mount {@link mountAgentLoopTestDependencies} and any load-order-sensitive
113
+ * consumers before calling this helper. The context owns the loop and every
114
+ * Agent returned by the harness.
115
+ * @param ctx - test context with the AgentLoop prerequisite services active.
116
+ * @returns a driver that creates production Agents and claims their real Inbox.
117
+ */
118
+ async function mountAgentLoopTestHarness(ctx) {
119
+ await ctx.plugin(AgentLoop, { agents: [] });
120
+ return {
121
+ create: async (id, options = {}, meta = {}) => ctx.agentLoop.create(id, options, meta),
122
+ claim: (agent, target, turn) => agent.inbox.claim(target, turn)
123
+ };
124
+ }
32
125
  //#endregion
33
- export { mountAgentLoopTestDependencies };
126
+ export { createInboxStub, mountAgentLoopTestDependencies, mountAgentLoopTestHarness, unsupportedInbox };
@@ -0,0 +1,14 @@
1
+ import type { Inbox } from '@deepseek-ai/dsh-agent';
2
+ /**
3
+ * Create a mutable in-memory Inbox stub for tests that exercise only the public
4
+ * queue operations. Durable events, projection validation, and live Inbox
5
+ * notifications require a real Agent created by the AgentLoop test harness.
6
+ * @returns an Inbox backed by two process-local arrays.
7
+ */
8
+ export declare function createInboxStub(): Inbox;
9
+ /**
10
+ * Create an unsupported Inbox placeholder for Agent stubs whose tests do not exercise Inbox behavior.
11
+ * @returns an Inbox whose pending lists are empty and whose mutation methods throw.
12
+ */
13
+ export declare function unsupportedInbox(): Inbox;
14
+ //# sourceMappingURL=inbox.d.ts.map
@@ -1,12 +1,34 @@
1
1
  /**
2
- * Shared mounting for the services required before tests load the concrete
3
- * agent loop. The caller retains ownership of the context, loop, adapters,
4
- * optional plugins, and teardown.
2
+ * Shared service mounting, real AgentLoop drivers, and structural Inbox stubs
3
+ * for agent-loop tests. Callers retain ownership of their contexts, adapters,
4
+ * optional plugins, agents, and teardown.
5
5
  * @module @deepseek-ai/dsh-agent-loop-testkit
6
6
  */
7
7
  import type { Context } from '@deepseek-ai/cordis';
8
+ import type { Agent, AgentOptions, InboxTarget } from '@deepseek-ai/dsh-agent';
9
+ import type { SessionHeader, SessionId, UserMessage } from '@deepseek-ai/dsh-session';
8
10
  import type { Config as SystemPromptConfig } from '@deepseek-ai/dsh-system-prompt';
9
11
  import type { Config as ToolRuntimeConfig } from '@deepseek-ai/dsh-tools';
12
+ export { createInboxStub, unsupportedInbox } from './inbox.ts';
13
+ /** Test driver for production Agents created by a mounted AgentLoop. */
14
+ export interface AgentLoopTestHarness {
15
+ /**
16
+ * Create a production Agent and fresh Session owned by the harness context.
17
+ * @param id - shared Agent and Session identity.
18
+ * @param options - concrete loop options.
19
+ * @param meta - optional fresh-session workspace metadata.
20
+ * @returns the published production Agent after creation completes.
21
+ */
22
+ create(id: SessionId, options?: AgentOptions, meta?: Pick<SessionHeader, 'cwd'>): Promise<Agent>;
23
+ /**
24
+ * Admit pending messages through the production loop driver's claim operation.
25
+ * @param agent - Agent returned by this harness's `create` method.
26
+ * @param target - boundary whose pending input is admitted.
27
+ * @param turn - turn that owns the admitted messages.
28
+ * @returns next-step messages followed by one next-turn message when requested.
29
+ */
30
+ claim(agent: Agent, target: InboxTarget, turn: number): UserMessage[];
31
+ }
10
32
  /** Configuration forwarded to the prerequisite service plugins. */
11
33
  export interface AgentLoopTestDependenciesOptions {
12
34
  /** Configuration for the system-prompt registry. */
@@ -27,4 +49,13 @@ export interface AgentLoopTestDependenciesOptions {
27
49
  * @returns after every prerequisite service has activated.
28
50
  */
29
51
  export declare function mountAgentLoopTestDependencies(ctx: Context, options?: AgentLoopTestDependenciesOptions): Promise<void>;
52
+ /**
53
+ * Mount the production AgentLoop and expose its narrow test-driver operations.
54
+ * Mount {@link mountAgentLoopTestDependencies} and any load-order-sensitive
55
+ * consumers before calling this helper. The context owns the loop and every
56
+ * Agent returned by the harness.
57
+ * @param ctx - test context with the AgentLoop prerequisite services active.
58
+ * @returns a driver that creates production Agents and claims their real Inbox.
59
+ */
60
+ export declare function mountAgentLoopTestHarness(ctx: Context): Promise<AgentLoopTestHarness>;
30
61
  //# sourceMappingURL=index.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-agent-loop-testkit",
3
- "description": "Shared prerequisite mounting for tests that exercise the concrete agent loop",
4
- "version": "0.1.3-alpha.2",
3
+ "description": "Prerequisite mounting, production AgentLoop drivers, and Inbox stubs for tests",
4
+ "version": "0.1.5-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -27,20 +27,24 @@
27
27
  ],
28
28
  "license": "MIT",
29
29
  "peerDependencies": {
30
- "@deepseek-ai/dsh-agent": "^0.1.3-alpha.2",
31
- "@deepseek-ai/dsh-llm": "^0.1.3-alpha.2",
32
- "@deepseek-ai/dsh-session": "^0.1.3-alpha.2",
33
- "@deepseek-ai/dsh-system-prompt": "^0.1.3-alpha.2",
34
- "@deepseek-ai/dsh-tools": "^0.1.3-alpha.2",
30
+ "@deepseek-ai/dsh-agent": "^0.1.5-alpha.2",
31
+ "@deepseek-ai/dsh-agent-loop": "^0.1.5-alpha.2",
32
+ "@deepseek-ai/dsh-session": "^0.1.5-alpha.2",
33
+ "@deepseek-ai/dsh-llm": "^0.1.5-alpha.2",
34
+ "@deepseek-ai/dsh-session-projection": "^0.1.5-alpha.2",
35
+ "@deepseek-ai/dsh-system-prompt": "^0.1.5-alpha.2",
36
+ "@deepseek-ai/dsh-tools": "^0.1.5-alpha.2",
35
37
  "@deepseek-ai/cordis": "^4.0.2"
36
38
  },
39
+ "dependencies": {},
37
40
  "devDependencies": {
38
- "@deepseek-ai/dsh-agent": "^0.1.3-alpha.2",
39
- "@deepseek-ai/dsh-llm": "^0.1.3-alpha.2",
40
- "@deepseek-ai/dsh-session": "^0.1.3-alpha.2",
41
- "@deepseek-ai/dsh-system-prompt": "^0.1.3-alpha.2",
42
- "@deepseek-ai/dsh-agent-loop": "^0.1.3-alpha.2",
43
- "@deepseek-ai/dsh-tools": "^0.1.3-alpha.2",
44
- "@deepseek-ai/cordis": "^4.0.2"
41
+ "@deepseek-ai/dsh-agent": "^0.1.5-alpha.2",
42
+ "@deepseek-ai/dsh-agent-loop": "^0.1.5-alpha.2",
43
+ "@deepseek-ai/dsh-llm": "^0.1.5-alpha.2",
44
+ "@deepseek-ai/dsh-session": "^0.1.5-alpha.2",
45
+ "@deepseek-ai/dsh-session-projection": "^0.1.5-alpha.2",
46
+ "@deepseek-ai/dsh-tools": "^0.1.5-alpha.2",
47
+ "@deepseek-ai/cordis": "^4.0.2",
48
+ "@deepseek-ai/dsh-system-prompt": "^0.1.5-alpha.2"
45
49
  }
46
50
  }