@wowyuarm/dsh-agent-team 0.1.9 → 0.1.10

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.
Files changed (54) hide show
  1. package/README.md +4 -11
  2. package/README.zh.md +4 -11
  3. package/package.json +53 -44
  4. package/packages/agent-team/README.md +2 -2
  5. package/packages/agent-team/README.zh.md +2 -1
  6. package/packages/agent-team/lib/context-management.js +8 -5
  7. package/packages/agent-team/lib/context-projection.js +12 -8
  8. package/packages/agent-team/lib/context-source.js +163 -23
  9. package/packages/agent-team/lib/index.js +174 -124
  10. package/packages/agent-team/lib/ledger.js +111 -30
  11. package/packages/agent-team/lib/member-time-context.js +126 -0
  12. package/packages/agent-team/lib/pressure-policy.js +1 -2
  13. package/packages/agent-team/lib/progress-nudge.js +12 -4
  14. package/packages/agent-team/lib/session-remediation.js +481 -0
  15. package/packages/agent-team/lib/spec.js +3 -2
  16. package/packages/agent-team/lib/time-format.js +56 -0
  17. package/packages/agent-team/lib/typert.host.js +82 -49
  18. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  19. package/packages/agent-team/lib/typert.remote-client.js +46 -25
  20. package/packages/agent-team/lib/types/context-management.d.ts +6 -1
  21. package/packages/agent-team/lib/types/context-management.d.ts.map +1 -1
  22. package/packages/agent-team/lib/types/context-projection.d.ts.map +1 -1
  23. package/packages/agent-team/lib/types/context-source.d.ts +70 -36
  24. package/packages/agent-team/lib/types/context-source.d.ts.map +1 -1
  25. package/packages/agent-team/lib/types/index.d.ts +32 -7
  26. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  27. package/packages/agent-team/lib/types/ledger.d.ts +25 -0
  28. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  29. package/packages/agent-team/lib/types/member-time-context.d.ts +83 -0
  30. package/packages/agent-team/lib/types/member-time-context.d.ts.map +1 -0
  31. package/packages/agent-team/lib/types/pressure-policy.d.ts.map +1 -1
  32. package/packages/agent-team/lib/types/progress-nudge.d.ts.map +1 -1
  33. package/packages/agent-team/lib/types/session-remediation.d.ts +149 -0
  34. package/packages/agent-team/lib/types/session-remediation.d.ts.map +1 -0
  35. package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
  36. package/packages/agent-team/lib/types/time-format.d.ts +28 -0
  37. package/packages/agent-team/lib/types/time-format.d.ts.map +1 -0
  38. package/packages/agent-team/lib/types/types/entities.d.ts +8 -0
  39. package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
  40. package/packages/agent-team/lib/types/types/requests-results.d.ts +6 -0
  41. package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
  42. package/packages/agent-team/preset/team-member/agent.cordis.yml +19 -4
  43. package/packages/client-agent-team/lib/client.js +80 -54
  44. package/packages/client-agent-team/lib/client.js.map +1 -1
  45. package/packages/client-agent-team/lib/types/client/TeamMessage.js +2 -2
  46. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +4 -4
  47. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  48. package/packages/client-agent-team/lib/types/client/index.js +10 -5
  49. package/packages/client-agent-team/lib/types/client/slots.d.ts +1 -1
  50. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
  51. package/packages/tool-agent-team/README.md +8 -6
  52. package/packages/tool-agent-team/README.zh.md +8 -6
  53. package/packages/tool-agent-team/lib/index.js +302 -110
  54. package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
package/README.md CHANGED
@@ -17,7 +17,7 @@ An opt-in plugin for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-
17
17
  - **Workspaces organize everything.** Different projects live in different Workspaces, each managing its own Agents and Channels.
18
18
  - **The Human routes Channels and responsibilities.** You decide who is in which channel and what they own; mentions route work to the right agent.
19
19
  - **Task Threads carry one line of progress.** Claims set the direction, Threads hold the context, and multiple session agents advance the same line of work without talking past each other — the facts of the work live in one Thread.
20
- - **No context babysitting.** Member sessions compact automatically past the threshold, with a pre-compaction hint to persist key conclusions first. Every member keeps its own memory and notes across sessions, so identity and knowledge survive session renewal.
20
+ - **No context babysitting.** Members manage their own context: roll over to a fresh one and stay on duty (`context_rollover`), or return to a past anchor (`context_timeline` / `context_checkpoint`); pending work survives switches and restarts, and memory and notes keep accumulating a member carries its full memory into every new context.
21
21
 
22
22
  ## Preview
23
23
 
@@ -27,13 +27,13 @@ Agent Team is opt-in: after installation the ordinary DSH page stays as-is, and
27
27
 
28
28
  Entering Team mode shows Channels, managed Agents, and the collaboration timeline:
29
29
 
30
- ![Team mode in the DSH Web UI: Channels and seven online Agents in the sidebar; the Main Channel shows Task references and the composer’s task switch](assets/readme/team-mode.png)
30
+ ![Team mode in the DSH Web UI: Channels and online Agent members in the sidebar, with the Main Channel showing Task references](assets/readme/team-mode.png)
31
31
 
32
32
  ### Task Threads
33
33
 
34
34
  A Task Thread keeps Claims, Agent handoffs, Human acceptance, and follow-up replies in one durable context.
35
35
 
36
- ![Completed Task Thread in the DSH Web UI: a Claim, Agent handoffs, Human acceptance activity, and the reply composer](assets/readme/task-thread.png)
36
+ ![Task Thread in the DSH Web UI: Claims, Agent handoffs, Human acceptance activity, and the reply composer](assets/readme/task-thread.png)
37
37
 
38
38
  If this looks useful, a star on [GitHub](https://github.com/wowyuarm/dsh-agent-team) helps other DSH users find it.
39
39
 
@@ -41,7 +41,7 @@ If this looks useful, a star on [GitHub](https://github.com/wowyuarm/dsh-agent-t
41
41
 
42
42
  ### 1. Check DSH
43
43
 
44
- This release is certified against DSH `0.1.2-rc.1`. If `dsh` is not installed yet, start DSH with the official package:
44
+ This release is certified against DSH `0.1.5-rc.1`. If `dsh` is not installed yet, start DSH with the official package:
45
45
 
46
46
  ```sh
47
47
  npx @deepseek-ai/dsh web
@@ -108,13 +108,6 @@ dsh web
108
108
 
109
109
  Published packages include built artifacts. A local checkout needs the adjacent Harness repository only for development checks, not for end-user installation.
110
110
 
111
- ## Compatibility and limits
112
-
113
- - This release is certified against DSH `0.1.2-rc.1`, and requires it: earlier DSH versions are not supported.
114
- - The bundle is single-host. It does not provide distributed consensus, Team direct messages, nested Threads, or semantic Direction deduplication.
115
- - The current DSH SQLite Session schema rejects databases from older DSH versions. Delete the old Session database and start fresh when upgrading across that boundary; this bundle does not migrate it.
116
- - Team-managed Agent Sessions use `danger-full-access`. Use them only in trusted Workspaces.
117
-
118
111
  ## Development
119
112
 
120
113
  Read [`docs/README.md`](docs/README.md) for the maintained documentation index. The usual checks are:
package/README.zh.md CHANGED
@@ -17,7 +17,7 @@
17
17
  - **Workspace 组织一切。** 不同项目放在不同 Workspace,各自管理自己的 Agents 与 Channels。
18
18
  - **Human 管 Channel 与职责。** 你决定谁在哪个频道、负责什么;@提及把工作路由到对的 Agent。
19
19
  - **Task Thread 串联推进。** 用 Task 认领方向、Thread 保持上下文,多个 Session Agent 围绕同一条工作线推进而不散乱。工作事实落在同一条 Thread 里,成员之间不会各说各的。
20
- - **无需操心上下文。** 成员会话超阈值自动压缩,压缩前提示成员先沉淀关键结论。每个成员的 memory 与 notes 跨会话保留,身份与知识不随会话续期丢失。
20
+ - **无需操心上下文。** 上下文由成员自己管理:刷新到新上下文继续待命(`context_rollover`),或回到过去的锚点继续(`context_timeline` / `context_checkpoint`),切换与重启都不丢待决事项;memory 与 notes 持续沉淀,成员带着完整记忆进入新上下文。
21
21
 
22
22
  ## 预览
23
23
 
@@ -27,13 +27,13 @@ Agent Team 是按需启用的:安装后普通 DSH 页面保持原样,Team mo
27
27
 
28
28
  进入 Team mode 后是频道、受管 Agent 与协作时间线:
29
29
 
30
- ![DSH Web UI 中的 Team mode:侧边栏展示频道与 7 位在线 Agent;Main 频道含 Task 引用和 composer 的作为任务开关](assets/readme/team-mode.png)
30
+ ![DSH Web UI 中的 Team mode:侧边栏展示频道与在线 Agent 成员;Main Channel 展示 Task 引用](assets/readme/team-mode.png)
31
31
 
32
32
  ### Task Thread
33
33
 
34
34
  Task Thread 把 Claim、Agent 交接、Human 验收和后续回复保留在同一条可持续阅读的上下文中。
35
35
 
36
- ![DSH Web UI 中已完成的 Task Thread:含 Claim、Agent 交接、Human 验收活动和回复 composer](assets/readme/task-thread.png)
36
+ ![DSH Web UI 中的 Task Thread:含 Claim、Agent 交接、Human 验收活动和回复 composer](assets/readme/task-thread.png)
37
37
 
38
38
  如果觉得有用,欢迎在 [GitHub](https://github.com/wowyuarm/dsh-agent-team) 点个 star,帮更多 DSH 用户发现它。
39
39
 
@@ -41,7 +41,7 @@ Task Thread 把 Claim、Agent 交接、Human 验收和后续回复保留在同
41
41
 
42
42
  ### 1. 检查 DSH
43
43
 
44
- 当前版本已针对 DSH `0.1.2-rc.1` 完成认证。如果还没有安装 `dsh`,先使用官方 package 启动 DSH:
44
+ 当前版本已针对 DSH `0.1.5-rc.1` 完成认证。如果还没有安装 `dsh`,先使用官方 package 启动 DSH:
45
45
 
46
46
  ```sh
47
47
  npx @deepseek-ai/dsh web
@@ -108,13 +108,6 @@ dsh web
108
108
 
109
109
  发布包已经包含构建产物。只有开发检查需要相邻的 Harness repository,终端用户安装不需要它。
110
110
 
111
- ## 兼容性与限制
112
-
113
- - 当前版本已针对 DSH `0.1.2-rc.1` 完成认证,并且要求该版本:更早的 DSH 版本不受支持。
114
- - Bundle 是单 Host,不提供分布式共识、Team direct message、嵌套 Thread 或 Direction 语义去重。
115
- - 当前 DSH SQLite Session schema 不接受旧版 DSH 的数据库。跨越该边界升级时,删除旧 Session 数据库并重新开始;本 bundle 不负责迁移。
116
- - Team 管理的 Agent Session 使用 `danger-full-access`。只在可信 Workspace 中使用。
117
-
118
111
  ## 开发
119
112
 
120
113
  维护中的文档入口是 [`docs/README.zh.md`](docs/README.zh.md)。常用检查命令:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wowyuarm/dsh-agent-team",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "A persistent agent team for long-running collaboration in DeepSeek Harness",
5
5
  "repository": {
6
6
  "type": "git",
@@ -51,7 +51,7 @@
51
51
  }
52
52
  },
53
53
  "dependencies": {
54
- "@deepseek-ai/dsh-storage-sqlite": ">=0.1.2-rc.1 <0.2.0",
54
+ "@deepseek-ai/dsh-storage-sqlite": ">=0.1.5-rc.1 <0.2.0",
55
55
  "zod": "^4.4.3"
56
56
  },
57
57
  "devDependencies": {
@@ -106,6 +106,14 @@
106
106
  "types": "./packages/agent-team/lib/types/member-context.d.ts",
107
107
  "default": "./packages/agent-team/lib/member-context.js"
108
108
  },
109
+ "./member-time-context": {
110
+ "types": "./packages/agent-team/lib/types/member-time-context.d.ts",
111
+ "default": "./packages/agent-team/lib/member-time-context.js"
112
+ },
113
+ "./time-format": {
114
+ "types": "./packages/agent-team/lib/types/time-format.d.ts",
115
+ "default": "./packages/agent-team/lib/time-format.js"
116
+ },
109
117
  "./member-skills": {
110
118
  "types": "./packages/agent-team/lib/types/member-skills.d.ts",
111
119
  "default": "./packages/agent-team/lib/member-skills.js"
@@ -122,48 +130,49 @@
122
130
  },
123
131
  "peerDependencies": {
124
132
  "@deepseek-ai/cordis": "^4.0.1",
125
- "@deepseek-ai/dsh-agent": ">=0.1.2-rc.1 <0.2.0",
126
- "@deepseek-ai/dsh-agent-default-model": ">=0.1.2-rc.1 <0.2.0",
127
- "@deepseek-ai/dsh-agent-presets": ">=0.1.2-rc.1 <0.2.0",
128
- "@deepseek-ai/dsh-agent-tool-presentation": ">=0.1.2-rc.1 <0.2.0",
129
- "@deepseek-ai/dsh-api-remotes": ">=0.1.2-rc.1 <0.2.0",
130
- "@deepseek-ai/dsh-api-session-controller": ">=0.1.2-rc.1 <0.2.0",
131
- "@deepseek-ai/dsh-api-workspace-controller": ">=0.1.2-rc.1 <0.2.0",
132
- "@deepseek-ai/dsh-brand": ">=0.1.2-rc.1 <0.2.0",
133
- "@deepseek-ai/dsh-client-locale": ">=0.1.2-rc.1 <0.2.0",
134
- "@deepseek-ai/dsh-client-ui-conversation": ">=0.1.2-rc.1 <0.2.0",
135
- "@deepseek-ai/dsh-client-ui-input-trigger": ">=0.1.2-rc.1 <0.2.0",
136
- "@deepseek-ai/dsh-client-ui-layout": ">=0.1.2-rc.1 <0.2.0",
137
- "@deepseek-ai/dsh-client-ui-primitives": ">=0.1.2-rc.1 <0.2.0",
138
- "@deepseek-ai/dsh-client-ui-renderer": ">=0.1.2-rc.1 <0.2.0",
139
- "@deepseek-ai/dsh-client-ui-settings-general": ">=0.1.2-rc.1 <0.2.0",
140
- "@deepseek-ai/dsh-client-ui-sidebar": ">=0.1.2-rc.1 <0.2.0",
141
- "@deepseek-ai/dsh-client-ui-slots": ">=0.1.2-rc.1 <0.2.0",
142
- "@deepseek-ai/dsh-client-ui-workspace": ">=0.1.2-rc.1 <0.2.0",
143
- "@deepseek-ai/dsh-command-compact": ">=0.1.2-rc.1 <0.2.0",
144
- "@deepseek-ai/dsh-compaction": ">=0.1.2-rc.1 <0.2.0",
145
- "@deepseek-ai/dsh-home-paths": ">=0.1.2-rc.1 <0.2.0",
146
- "@deepseek-ai/dsh-invariants": ">=0.1.2-rc.1 <0.2.0",
147
- "@deepseek-ai/dsh-llm": ">=0.1.2-rc.1 <0.2.0",
148
- "@deepseek-ai/dsh-sandbox-policy": ">=0.1.2-rc.1 <0.2.0",
149
- "@deepseek-ai/dsh-scope": ">=0.1.2-rc.1 <0.2.0",
150
- "@deepseek-ai/dsh-session": ">=0.1.2-rc.1 <0.2.0",
151
- "@deepseek-ai/dsh-session-persistence": ">=0.1.2-rc.1 <0.2.0",
152
- "@deepseek-ai/dsh-skill": ">=0.1.2-rc.1 <0.2.0",
153
- "@deepseek-ai/dsh-skill-filesystem": ">=0.1.2-rc.1 <0.2.0",
154
- "@deepseek-ai/dsh-storage-domain": ">=0.1.2-rc.1 <0.2.0",
155
- "@deepseek-ai/dsh-token-meter": ">=0.1.2-rc.1 <0.2.0",
156
- "@deepseek-ai/dsh-tool-bash": ">=0.1.2-rc.1 <0.2.0",
157
- "@deepseek-ai/dsh-tool-fs": ">=0.1.2-rc.1 <0.2.0",
158
- "@deepseek-ai/dsh-tool-fs-search": ">=0.1.2-rc.1 <0.2.0",
159
- "@deepseek-ai/dsh-tool-jobs": ">=0.1.2-rc.1 <0.2.0",
160
- "@deepseek-ai/dsh-tool-pwsh": ">=0.1.2-rc.1 <0.2.0",
161
- "@deepseek-ai/dsh-tool-skill": ">=0.1.2-rc.1 <0.2.0",
162
- "@deepseek-ai/dsh-tool-todo": ">=0.1.2-rc.1 <0.2.0",
163
- "@deepseek-ai/dsh-tool-web": ">=0.1.2-rc.1 <0.2.0",
164
- "@deepseek-ai/dsh-tools": ">=0.1.2-rc.1 <0.2.0",
165
- "@deepseek-ai/dsh-typert-protocol": ">=0.1.2-rc.1 <0.2.0",
166
- "@deepseek-ai/dsh-workspace": ">=0.1.2-rc.1 <0.2.0",
133
+ "@deepseek-ai/dsh-agent": ">=0.1.5-rc.1 <0.2.0",
134
+ "@deepseek-ai/dsh-agent-default-model": ">=0.1.5-rc.1 <0.2.0",
135
+ "@deepseek-ai/dsh-agent-presets": ">=0.1.5-rc.1 <0.2.0",
136
+ "@deepseek-ai/dsh-agent-tool-presentation": ">=0.1.5-rc.1 <0.2.0",
137
+ "@deepseek-ai/dsh-api-remotes": ">=0.1.5-rc.1 <0.2.0",
138
+ "@deepseek-ai/dsh-api-session-controller": ">=0.1.5-rc.1 <0.2.0",
139
+ "@deepseek-ai/dsh-api-workspace-controller": ">=0.1.5-rc.1 <0.2.0",
140
+ "@deepseek-ai/dsh-brand": ">=0.1.5-rc.1 <0.2.0",
141
+ "@deepseek-ai/dsh-client-locale": ">=0.1.5-rc.1 <0.2.0",
142
+ "@deepseek-ai/dsh-client-ui-conversation": ">=0.1.5-rc.1 <0.2.0",
143
+ "@deepseek-ai/dsh-client-ui-input-trigger": ">=0.1.5-rc.1 <0.2.0",
144
+ "@deepseek-ai/dsh-client-ui-layout": ">=0.1.5-rc.1 <0.2.0",
145
+ "@deepseek-ai/dsh-client-ui-primitives": ">=0.1.5-rc.1 <0.2.0",
146
+ "@deepseek-ai/dsh-client-ui-renderer": ">=0.1.5-rc.1 <0.2.0",
147
+ "@deepseek-ai/dsh-client-ui-settings-general": ">=0.1.5-rc.1 <0.2.0",
148
+ "@deepseek-ai/dsh-client-ui-sidebar": ">=0.1.5-rc.1 <0.2.0",
149
+ "@deepseek-ai/dsh-client-ui-slots": ">=0.1.5-rc.1 <0.2.0",
150
+ "@deepseek-ai/dsh-client-ui-workspace": ">=0.1.5-rc.1 <0.2.0",
151
+ "@deepseek-ai/dsh-command-compact": ">=0.1.5-rc.1 <0.2.0",
152
+ "@deepseek-ai/dsh-compaction": ">=0.1.5-rc.1 <0.2.0",
153
+ "@deepseek-ai/dsh-home-paths": ">=0.1.5-rc.1 <0.2.0",
154
+ "@deepseek-ai/dsh-invariants": ">=0.1.5-rc.1 <0.2.0",
155
+ "@deepseek-ai/dsh-llm": ">=0.1.5-rc.1 <0.2.0",
156
+ "@deepseek-ai/dsh-sandbox-policy": ">=0.1.5-rc.1 <0.2.0",
157
+ "@deepseek-ai/dsh-scope": ">=0.1.5-rc.1 <0.2.0",
158
+ "@deepseek-ai/dsh-session": ">=0.1.5-rc.1 <0.2.0",
159
+ "@deepseek-ai/dsh-session-format-catalog": ">=0.1.5-rc.1 <0.2.0",
160
+ "@deepseek-ai/dsh-session-persistence": ">=0.1.5-rc.1 <0.2.0",
161
+ "@deepseek-ai/dsh-skill": ">=0.1.5-rc.1 <0.2.0",
162
+ "@deepseek-ai/dsh-skill-filesystem": ">=0.1.5-rc.1 <0.2.0",
163
+ "@deepseek-ai/dsh-storage-domain": ">=0.1.5-rc.1 <0.2.0",
164
+ "@deepseek-ai/dsh-token-meter": ">=0.1.5-rc.1 <0.2.0",
165
+ "@deepseek-ai/dsh-tool-bash": ">=0.1.5-rc.1 <0.2.0",
166
+ "@deepseek-ai/dsh-tool-fs": ">=0.1.5-rc.1 <0.2.0",
167
+ "@deepseek-ai/dsh-tool-fs-search": ">=0.1.5-rc.1 <0.2.0",
168
+ "@deepseek-ai/dsh-tool-jobs": ">=0.1.5-rc.1 <0.2.0",
169
+ "@deepseek-ai/dsh-tool-pwsh": ">=0.1.5-rc.1 <0.2.0",
170
+ "@deepseek-ai/dsh-tool-skill": ">=0.1.5-rc.1 <0.2.0",
171
+ "@deepseek-ai/dsh-tool-todo": ">=0.1.5-rc.1 <0.2.0",
172
+ "@deepseek-ai/dsh-tool-web": ">=0.1.5-rc.1 <0.2.0",
173
+ "@deepseek-ai/dsh-tools": ">=0.1.5-rc.1 <0.2.0",
174
+ "@deepseek-ai/dsh-typert-protocol": ">=0.1.5-rc.1 <0.2.0",
175
+ "@deepseek-ai/dsh-workspace": ">=0.1.5-rc.1 <0.2.0",
167
176
  "react": "^18.2.0"
168
177
  },
169
178
  "keywords": [
@@ -2,7 +2,7 @@
2
2
 
3
3
  English | [中文](README.zh.md)
4
4
 
5
- The Host capability for one Agent Team in a dshHome. `ctx.agentTeam` owns the append-only operation ledger, reconstructs the current collaboration projection, and is the lifecycle owner for member Agents managed by the same capability. Thread Attention and the Member Inbox are durable Host projections. When unread state changes, this package sends one bounded, coalesced context notification through the Agent's public safe-boundary API; it never interrupts an active request or acts as a Session delivery worker. The [Agent Team architecture note](../../../.agents/notes/proposed/architecture/2026-08-15-agent-team-operation-ledger.md) owns the persistence and package-topology decisions.
5
+ The Host capability for one Agent Team in a dshHome. `ctx.agentTeam` owns the append-only operation ledger, reconstructs the current collaboration projection, and is the lifecycle owner for member Agents managed by the same capability. Thread Attention and the Member Inbox are durable Host projections. When unread state changes, this package sends one bounded, coalesced context notification through the Agent's public safe-boundary API; it never interrupts an active request or acts as a Session delivery worker. The [Agent Team architecture note](../../.scratch/archive/2026-08/m1/design/agent-notes/2026-08-15-agent-team-operation-ledger.md) owns the persistence and package-topology decisions.
6
6
 
7
7
  ## Service contract
8
8
 
@@ -65,4 +65,4 @@ The Host ledger and Human status reads do not alter model requests or cache reus
65
65
  - **Single Host writer** — concurrent processes over one dshHome are unsupported; operation serialization is process-local.
66
66
  - **Permanent ledger** — M1 provides neither snapshots nor compaction, so storage grows with committed collaboration facts.
67
67
  - **No remote provider seam** — the package combines the capability definition and its only implementation until a real remote Consumer requires another Provider.
68
- - **Current DSH SQLite only** — SQLite Session persistence uses the current DSH schema. Delete old Session databases and create new Member sessions; this package provides no migration, compatibility read, or fallback.
68
+ - **No Session migration of its own** — Member Sessions ride the Host's Session persistence and its released-format migration chain; this package provides no Team ledger or Member Session migration, compatibility read, or fallback.
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](README.md) | 中文
4
4
 
5
- 一个 dshHome 内唯一 Agent Team 的 Host capability。`ctx.agentTeam` 拥有 append-only operation ledger、重建当前协作 projection,并作为同一 capability 内 Member Agent 的 lifecycle owner。Thread Attention 和 Member Inbox 是 Host 的持久 projection。未读状态变化时,本包通过 Agent 的公开安全边界 API 发送一次有界、合并的 context 通知;不会中断正在执行的请求,也不运行 Session delivery worker。[Agent Team 架构 Agent Note](../../../.agents/notes/proposed/architecture/2026-08-15-agent-team-operation-ledger.md)记录持久化和包拓扑决策。
5
+ 一个 dshHome 内唯一 Agent Team 的 Host capability。`ctx.agentTeam` 拥有 append-only operation ledger、重建当前协作 projection,并作为同一 capability 内 Member Agent 的 lifecycle owner。Thread Attention 和 Member Inbox 是 Host 的持久 projection。未读状态变化时,本包通过 Agent 的公开安全边界 API 发送一次有界、合并的 context 通知;不会中断正在执行的请求,也不运行 Session delivery worker。[Agent Team 架构 Agent Note](../../.scratch/archive/2026-08/m1/design/agent-notes/2026-08-15-agent-team-operation-ledger.md)记录持久化和包拓扑决策。
6
6
 
7
7
  ## Service 约定
8
8
 
@@ -65,3 +65,4 @@ Host ledger 和 Human status read 不改变模型请求或 cache reuse。
65
65
  - **单 Host writer** — 不支持多个进程并发写同一 dshHome;operation serialization 只在进程内生效。
66
66
  - **永久 ledger** — M1 不提供 snapshot 或 compaction,storage 会随已提交协作事实增长。
67
67
  - **没有 remote provider seam** — 在真实 remote Consumer 需要另一 Provider 前,本包合并 capability definition 和唯一实现。
68
+ - **自身不提供 Session 迁移** — Member Sessions 依赖 Host 的 Session persistence 与其已发布格式迁移链;本包不提供 Team ledger 或 Member Session 迁移、兼容读取或回退。
@@ -19,10 +19,8 @@
19
19
  */
20
20
  import { SessionId as SessionIdBrand } from '@deepseek-ai/dsh-session';
21
21
  import { createHash } from 'node:crypto';
22
- import { createCheckpointContinuationMessage, createHandoffMessage } from "./context-source.js";
22
+ import { AGENT_TEAM_PLUGIN_ID, createCheckpointContinuationMessage, createHandoffMessage, isAgentTeamContextSource } from "./context-source.js";
23
23
  import { CONTEXT_CHECKPOINT_TOOL_NAME, CONTEXT_ROLLOVER_TOOL_NAME, continuationDelivered, foldContextProjection, } from "./context-projection.js";
24
- /** Plugin identity of the Agent Team Host, for recognizing own notices. */
25
- const AGENT_TEAM_PLUGIN_ID = '@wowyuarm/dsh-agent-team';
26
24
  /** Stable summary of the one-shot rollover pressure notice (ticket 03 wires delivery). */
27
25
  export const CONTEXT_PRESSURE_NOTICE_SUMMARY = 'Context pressure: prepare a handoff';
28
26
  export class ContextManagementCoordinator {
@@ -133,10 +131,15 @@ export class ContextManagementCoordinator {
133
131
  this.capturedInput.delete(memberId);
134
132
  return captured;
135
133
  }
136
- /** Whether one queued message is a Team-owned notice the rederived Inbox replaces. */
134
+ /**
135
+ * Whether one queued message is a Team-owned notice the rederived Inbox
136
+ * replaces. Handoff and continuation envelopes carry the same plugin
137
+ * attribution but are ordinary delivered context the new generation keeps, so
138
+ * they are excluded rather than dropped.
139
+ */
137
140
  isTeamNotice(message) {
138
141
  const source = message.source;
139
- return source.kind === 'plugin' && source.plugin === AGENT_TEAM_PLUGIN_ID;
142
+ return source.kind === 'plugin' && source.plugin === AGENT_TEAM_PLUGIN_ID && !isAgentTeamContextSource(message);
140
143
  }
141
144
  /** Drop one Member's bookkeeping; the Host calls this on dispose/removal. */
142
145
  stopTracking(memberId) {
@@ -20,9 +20,7 @@
20
20
  */
21
21
  import { createHash } from 'node:crypto';
22
22
  import { z } from 'zod';
23
- import { isCheckpointContinuationMessage } from "./context-source.js";
24
- /** Plugin identity of the Agent Team Host, for recognizing own notices. */
25
- const AGENT_TEAM_PLUGIN_ID = '@wowyuarm/dsh-agent-team';
23
+ import { AGENT_TEAM_PLUGIN_ID, continuationCheckpointRefOf, handoffOf, isAgentTeamContextSource, isCheckpointContinuationMessage } from "./context-source.js";
26
24
  /** Summary marker of the pre-compaction memory hint. */
27
25
  const PRE_COMPACTION_NOTICE_SUMMARY = 'Compaction is imminent; consider persisting key conclusions.';
28
26
  /** Team tool whose successful mutations are semantic timeline candidates. */
@@ -246,7 +244,7 @@ function applyContextEvent(state, event, sessionId) {
246
244
  */
247
245
  function boundaryFromUserMessage(sessionId, seq, message, seenThreads) {
248
246
  const source = message.source;
249
- if (source.kind === 'agent-team-context-handoff') {
247
+ if (handoffOf(message) !== undefined) {
250
248
  return { key: `handoff:${seq}`, source: 'handoff', label: 'context handoff', seq, turn: -1, turnEndSeq: -1 };
251
249
  }
252
250
  if (source.kind !== 'plugin' || source.plugin !== AGENT_TEAM_PLUGIN_ID)
@@ -441,9 +439,10 @@ function applyUserMessage(state, seq, message, sessionId) {
441
439
  }
442
440
  // The quiet continuation notice delivered for one checkpoint completes its
443
441
  // delivery state; replay repair reads this to avoid re-scheduling it.
444
- if (message.source.kind !== 'agent-team-context-continuation')
442
+ const continuation = continuationCheckpointRefOf(message);
443
+ if (continuation === undefined)
445
444
  return next;
446
- const checkpointRef = message.source.checkpointRef;
445
+ const checkpointRef = continuation;
447
446
  const existing = next.continuations.find(entry => entry.checkpointRef === checkpointRef);
448
447
  if (existing !== undefined) {
449
448
  if (existing.deliveredSeq !== -1)
@@ -469,10 +468,15 @@ function applyAssistantMessage(state, event) {
469
468
  export function carriedInputOf(state) {
470
469
  return state.carriedCandidates.filter(candidate => !candidate.consumed).map(candidate => candidate.message);
471
470
  }
472
- /** Whether one queued message is a Team-owned notice the rederived Inbox replaces. */
471
+ /**
472
+ * Whether one queued message is a Team-owned notice the rederived Inbox
473
+ * replaces. Handoff and continuation envelopes carry the same plugin
474
+ * attribution but are ordinary delivered context the new generation keeps, so
475
+ * they are excluded rather than dropped.
476
+ */
473
477
  function isTeamNotice(message) {
474
478
  const source = message.source;
475
- return source.kind === 'plugin' && source.plugin === AGENT_TEAM_PLUGIN_ID;
479
+ return source.kind === 'plugin' && source.plugin === AGENT_TEAM_PLUGIN_ID && !isAgentTeamContextSource(message);
476
480
  }
477
481
  /** Whether a quiet continuation for one checkpoint was already delivered in this Session. */
478
482
  export function continuationDelivered(state, checkpointRef) {
@@ -1,35 +1,52 @@
1
1
  /**
2
- * Merge-extended message sources for Agent Team context management.
2
+ * Plugin-attributed message sources for Agent Team context management.
3
3
  *
4
- * The sources ride ordinary `UserMessage`s and never add a Harness
5
- * `ContextForm`: the handoff snapshot reuses the shipped `snapshot` form, the
6
- * quieter notices reuse `notice`. Predicates here are the single place that
7
- * recognizes these notices, so callers never match on localized body text.
4
+ * Both sources ride ordinary `UserMessage`s under the shipped `plugin` kind
5
+ * with the `snapshot` context form — the same shape the Harness's own
6
+ * system-prompt producer writes. This module declares no `MessageSourceMap`
7
+ * member of its own, and carries no bespoke source members: Session format
8
+ * migration validates a `plugin` source against a closed member list
9
+ * (`kind`, `plugin`, `form`, `sections`, `summary`), and refuses every logged
10
+ * Session that carries anything else. A plugin-declared kind is type-legal yet
11
+ * refused the same way. See `docs/dsh-release-compatibility.md`
12
+ * § "Session message sources".
13
+ *
14
+ * Everything the Host needs to read back therefore rides the admitted payload
15
+ * slots: the handoff envelope and the checkpoint correlation both travel as
16
+ * named {@link ContextSnapshotSection} contributions, distinguished by their
17
+ * stable section names. Sections are the format's designed slot for structured
18
+ * producer payload, and they render as named contributions on any
19
+ * snapshot-aware surface.
20
+ *
21
+ * The validators below are the single place that recognizes these messages, so
22
+ * callers never match on localized body text.
8
23
  * @module @wowyuarm/dsh-agent-team/context-source
9
24
  */
10
25
  import { createUserMessage } from '@deepseek-ai/dsh-llm';
11
- /** Stable one-line account for checkpoint continuation notices. */
12
- export const CONTEXT_CONTINUATION_NOTICE_SUMMARY = 'Context checkpoint recorded; work continues in the next turn';
26
+ /** Plugin identity attributing every Agent Team message source. */
27
+ export const AGENT_TEAM_PLUGIN_ID = '@wowyuarm/dsh-agent-team';
13
28
  /** Handoff snapshot section name carrying the model-authored prose. */
14
29
  export const HANDOFF_SECTION_NAME = 'HANDOFF';
30
+ /** Stable section name marking a checkpoint continuation and carrying its ref. */
31
+ export const CHECKPOINT_SECTION_NAME = 'Checkpoint';
32
+ /** Fixed text of the quiet checkpoint continuation delivered on the next turn. */
33
+ export const CHECKPOINT_CONTINUATION_TEXT = 'A context checkpoint was recorded at the end of the previous turn. Continue the work you were doing.';
15
34
  /**
16
35
  * Build the first model-facing context of one rollover generation. The Host
17
36
  * owns only the verifiable envelope; the prose is the Member's own handoff.
37
+ *
38
+ * The envelope sections are additive structure, not the only carrier of these
39
+ * facts: {@link handoffBody} already states them in the model-facing text, so a
40
+ * surface that renders only the body loses nothing.
18
41
  */
19
42
  export function createHandoffMessage(input) {
20
43
  return createUserMessage({
21
44
  content: [{ type: 'text', text: handoffBody(input) }],
22
45
  source: {
23
- kind: 'agent-team-context-handoff',
46
+ kind: 'plugin',
47
+ plugin: AGENT_TEAM_PLUGIN_ID,
24
48
  form: 'snapshot',
25
- version: 1,
26
- previousSessionId: input.previousSessionId,
27
- newSessionId: input.newSessionId,
28
- trigger: input.trigger,
29
- handoffEventSeq: input.handoffEventSeq,
30
- ...(input.checkpointRef === undefined ? {} : { checkpointRef: input.checkpointRef }),
31
- ...(input.relatedFiles === undefined || input.relatedFiles.length === 0 ? {} : { relatedFiles: input.relatedFiles.map(file => file.path) }),
32
- sections: [{ name: HANDOFF_SECTION_NAME, text: input.handoff }],
49
+ sections: handoffSections(input),
33
50
  },
34
51
  });
35
52
  }
@@ -38,22 +55,145 @@ export function createHandoffMessage(input) {
38
55
  * concluded its turn. Delivery is scheduled only after the checkpoint's
39
56
  * successful tool result is durable, so a result-render failure can never
40
57
  * leave a ghost continuation behind.
58
+ *
59
+ * This is a `snapshot` rather than a `notice` for one reason: the projection
60
+ * must read the checkpoint ref back out of the durable log to record delivery,
61
+ * and `sections` is the only admitted payload slot that carries structure. A
62
+ * notice would have forced the ref into its human-readable one-line summary.
41
63
  */
42
64
  export function createCheckpointContinuationMessage(checkpointRef) {
43
65
  return createUserMessage({
44
- content: [{ type: 'text', text: 'A context checkpoint was recorded at the end of the previous turn. Continue the work you were doing.' }],
45
- source: { kind: 'agent-team-context-continuation', form: 'notice', summary: CONTEXT_CONTINUATION_NOTICE_SUMMARY, checkpointRef, version: 1 },
66
+ content: [{ type: 'text', text: CHECKPOINT_CONTINUATION_TEXT }],
67
+ source: {
68
+ kind: 'plugin',
69
+ plugin: AGENT_TEAM_PLUGIN_ID,
70
+ form: 'snapshot',
71
+ sections: [{ name: CHECKPOINT_SECTION_NAME, text: checkpointRef }],
72
+ },
46
73
  });
47
74
  }
75
+ /**
76
+ * Read one message's snapshot sections when it is this plugin's own snapshot.
77
+ * @param message - candidate user message.
78
+ * @returns the sections, or `undefined` when another producer owns the message.
79
+ */
80
+ function ownSections(message) {
81
+ const source = message.source;
82
+ if (source.kind !== 'plugin' || source.plugin !== AGENT_TEAM_PLUGIN_ID)
83
+ return undefined;
84
+ if (source.form !== 'snapshot')
85
+ return undefined;
86
+ return source.sections;
87
+ }
88
+ /** The text of one named section, or undefined when it is absent. */
89
+ function sectionText(sections, name) {
90
+ return sections.find(section => section.name === name)?.text;
91
+ }
92
+ /**
93
+ * Decode the `Related files` section. The Host writes the exact path array as
94
+ * JSON, which round-trips every path a file system admits — including one
95
+ * containing a comma, which the `', '`-joined form this replaced could not.
96
+ * Sections written before that encoding are still read; the legacy split is a
97
+ * read-side accommodation for old generations, never a write path.
98
+ */
99
+ function parseRelatedFiles(text) {
100
+ try {
101
+ const parsed = JSON.parse(text);
102
+ if (Array.isArray(parsed) && parsed.every(path => typeof path === 'string' && path.length > 0)) {
103
+ return parsed;
104
+ }
105
+ }
106
+ catch {
107
+ // Not JSON: the section predates the JSON encoding.
108
+ }
109
+ return text.split(', ').filter(path => path.length > 0);
110
+ }
111
+ /**
112
+ * The rollover handoff one message carries, when it is one.
113
+ * @param message - candidate user message.
114
+ * @returns the envelope, or `undefined` when the message is not a handoff.
115
+ */
116
+ export function handoffOf(message) {
117
+ const sections = ownSections(message);
118
+ if (sections === undefined)
119
+ return undefined;
120
+ const handoff = sectionText(sections, HANDOFF_SECTION_NAME);
121
+ if (handoff === undefined)
122
+ return undefined;
123
+ const previousSessionId = sectionText(sections, HANDOFF_PREVIOUS_SESSION);
124
+ const newSessionId = sectionText(sections, HANDOFF_NEW_SESSION);
125
+ const trigger = sectionText(sections, HANDOFF_TRIGGER);
126
+ const handoffEventSeq = sectionText(sections, HANDOFF_EVENT_SEQ);
127
+ if (previousSessionId === undefined || newSessionId === undefined)
128
+ return undefined;
129
+ if (trigger !== 'model' && trigger !== 'pressure')
130
+ return undefined;
131
+ const seq = Number(handoffEventSeq);
132
+ if (handoffEventSeq === undefined || !Number.isSafeInteger(seq))
133
+ return undefined;
134
+ const checkpointRef = sectionText(sections, HANDOFF_CHECKPOINT);
135
+ const relatedFiles = sectionText(sections, HANDOFF_RELATED_FILES);
136
+ return {
137
+ previousSessionId,
138
+ newSessionId,
139
+ trigger,
140
+ handoffEventSeq: seq,
141
+ ...(checkpointRef === undefined ? {} : { checkpointRef }),
142
+ ...(relatedFiles === undefined ? {} : { relatedFiles: parseRelatedFiles(relatedFiles) }),
143
+ sections,
144
+ };
145
+ }
146
+ /**
147
+ * The checkpoint ref one continuation notice carries, when the message is one.
148
+ * @param message - candidate user message.
149
+ * @returns the checkpoint ref, or `undefined` when the message is not a continuation.
150
+ */
151
+ export function continuationCheckpointRefOf(message) {
152
+ const sections = ownSections(message);
153
+ if (sections === undefined || sections.length !== 1)
154
+ return undefined;
155
+ const ref = sectionText(sections, CHECKPOINT_SECTION_NAME);
156
+ return ref === undefined || ref.length === 0 ? undefined : ref;
157
+ }
48
158
  /** Whether one user message is a rollover handoff snapshot. */
49
159
  export function isHandoffMessage(message) {
50
- return message.source.kind === 'agent-team-context-handoff';
160
+ return handoffOf(message) !== undefined;
51
161
  }
52
- /** Whether one user message is a checkpoint continuation notice, optionally for one checkpoint. */
162
+ /** Whether one user message is a checkpoint continuation, optionally for one checkpoint. */
53
163
  export function isCheckpointContinuationMessage(message, checkpointRef) {
54
- const source = message.source;
55
- return source.kind === 'agent-team-context-continuation'
56
- && (checkpointRef === undefined || source.checkpointRef === checkpointRef);
164
+ const ref = continuationCheckpointRefOf(message);
165
+ return ref !== undefined && (checkpointRef === undefined || ref === checkpointRef);
166
+ }
167
+ /**
168
+ * Whether one message carries a rollover-handoff or checkpoint-continuation
169
+ * envelope. Ordinary Team notices share this plugin's attribution, so callers
170
+ * that replace rederived notices must exclude these two families explicitly.
171
+ */
172
+ export function isAgentTeamContextSource(message) {
173
+ return isHandoffMessage(message) || isCheckpointContinuationMessage(message);
174
+ }
175
+ /** Envelope section names; stable, because they are read back from the log.
176
+ * Exported for the legacy-artifact remediation, which rewrites pre-0.1.10
177
+ * envelopes into exactly these names — one shared vocabulary, no drift. */
178
+ export const HANDOFF_PREVIOUS_SESSION = 'Previous session';
179
+ export const HANDOFF_NEW_SESSION = 'New session';
180
+ export const HANDOFF_TRIGGER = 'Trigger';
181
+ export const HANDOFF_EVENT_SEQ = 'Handoff event seq';
182
+ export const HANDOFF_CHECKPOINT = 'Continued from checkpoint';
183
+ export const HANDOFF_RELATED_FILES = 'Related files';
184
+ /** The envelope contributions of one handoff, prose first. */
185
+ function handoffSections(input) {
186
+ return [
187
+ { name: HANDOFF_SECTION_NAME, text: input.handoff },
188
+ { name: HANDOFF_PREVIOUS_SESSION, text: input.previousSessionId },
189
+ { name: HANDOFF_NEW_SESSION, text: input.newSessionId },
190
+ { name: HANDOFF_TRIGGER, text: input.trigger },
191
+ { name: HANDOFF_EVENT_SEQ, text: String(input.handoffEventSeq) },
192
+ ...(input.checkpointRef === undefined ? [] : [{ name: HANDOFF_CHECKPOINT, text: input.checkpointRef }]),
193
+ ...(input.relatedFiles === undefined || input.relatedFiles.length === 0
194
+ ? []
195
+ : [{ name: HANDOFF_RELATED_FILES, text: JSON.stringify(input.relatedFiles.map(file => file.path)) }]),
196
+ ];
57
197
  }
58
198
  function handoffBody(input) {
59
199
  const lines = [