@deepseek-ai/dsh-experimental-agent-team 0.1.6-alpha.2 → 0.1.7-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/experimental/agent-team/README.md
5
- README.md: 1dba55aaef5c2f061de85de3b38cf5c644fa90b7
6
- README.zh.md: 749f5b9d27ed863639a6dabad80254c9a19727dd
5
+ README.md: 6ae7d82214ca9e041324e353057ba6850675c043
6
+ README.zh.md: 89176c285a8bd9d5a1875618dffb0b2eb4890a3e
package/README.md CHANGED
@@ -60,7 +60,7 @@ The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-a
60
60
 
61
61
  Ask the Lead to create a teammate: give it a unique lowercase name such as `reviewer` and describe its job. A teammate starts fresh with no memory of the Lead's conversation, or as a fork that inherits the Lead's completed turns; the creation request chooses which. Teammate names are permanent — even a teammate whose creation failed keeps its name, and no name is ever reused.
62
62
 
63
- The roster shows every member with its role (`lead` or `teammate`) and current status: `running`, `idle`, `inactive` (a member that exists but is not loaded), `provisioning`, or `failed`. A member that is not loaded receives its messages when it wakes.
63
+ The roster shows every member with its role (`lead` or `teammate`) and current status: `running`, `inactive` (no turn is executing, whether loaded or stored), `provisioning`, or `failed`. A member that is not loaded receives its messages when it wakes.
64
64
 
65
65
  Only the Lead can create teammates or interrupt them.
66
66
 
@@ -68,7 +68,7 @@ Only the Lead can create teammates or interrupt them.
68
68
 
69
69
  Any member can send a message to any other member or to the Lead. A live member receives it immediately; an offline member's messages queue and arrive when it resumes. Messages are never lost and never delivered twice.
70
70
 
71
- Every message uses Steer: a running target receives it at the nearest step boundary, an idle target starts a turn, and an inactive teammate cold-resumes. The sender always sees the outcome — accepted by the target inbox, or retained as queued when delivery is temporarily unavailable. A queued message is already safely stored, so it must not be resent.
71
+ Every message uses Steer: a running target receives it at the nearest step boundary; an inactive target starts a turn if loaded or cold-resumes otherwise. The sender always sees the outcome — accepted by the target inbox, or retained as queued when delivery is temporarily unavailable. A queued message is already safely stored, so it must not be resent.
72
72
 
73
73
  ### Shared task board
74
74
 
@@ -145,6 +145,10 @@ Tasks are complete versioned snapshots; every mutation carries `expectedRevision
145
145
 
146
146
  Team events are appended to the exact live Lead Session and flushed before the operation reports success or wakes waiters. `team/member`, `team/task`, `team/message/queued`, and `team/message/delivered` are log-only: they never enter the conversation surface, so derived model history is untouched by coordination records. Session event `seq` and `time` own ordering and timing; snapshots do not duplicate them. The `./invariant` companion replays each candidate Team event against its committed prefix and rejects invalid transitions before append.
147
147
 
148
+ Native V4 Team event and checkpoint admission reject retired `tool-result` content before it can enter mailbox state. Historical conversion belongs to the Session-format migration; the Team projection does not convert old wrappers.
149
+
150
+ Mailbox projection and checkpoint admission preserve every decoded JSON field of accepted content outside the locally declared validators, including an own `__proto__` key. Local field checks cover `text`, `reasoning`, `image`, and `tool-call`; accepted unknown tags remain opaque. Team projection cache version 4 rebuilds checkpoints from earlier cache versions from the Session log; the Session format version is unchanged.
151
+
148
152
  ### Disposal
149
153
 
150
154
  Disposal closes admission, aborts and awaits admitted creation and mailbox-dispatch transactions, then asks the continuation owner to release the roster's exact live direct children and their descendants; non-Team continuable children of the Lead remain untouched. Cleanup failures make disposal fail visibly, bounded by `disposalTimeoutMs`.
@@ -169,7 +173,7 @@ Read these pages when the package-level contract is not enough. They move from t
169
173
 
170
174
  ### Browser Remote
171
175
 
172
- `TeamService` owns the generated `agentTeams/view`, `agentTeams/createTask`, and `agentTeams/updateTask` Remote methods beside the roster, mailbox, task, and lifecycle operations. The `./remote` export supplies the Client contribution mounted by the Web UI, while `./client` re-exports the request, view, and task-mutation result types that are safe in a browser compilation face. Typert retains transport failures in its outer `RemoteResult`; create and update rejections remain explicit domain results inside a successful transport response, with stale update revisions distinguished as task conflicts.
176
+ `TeamService` exposes the read-only `agentTeams/view` Remote method for browser clients. Task creation and updates belong to Team agents through the service and model tools. The `./remote` export supplies the Client contribution mounted by the Web UI, while `./client` exports browser-safe roster and task views.
173
177
 
174
178
  ## Model Experience
175
179
 
@@ -198,7 +202,7 @@ These limits describe what a team cannot do yet or what needs special operationa
198
202
  - **One process and one shared checkout** — members share cwd and observe edits immediately; this package provides no worktree, remote member, merge, or filesystem lock.
199
203
  - **Advisory write scopes** — Bash, formatters, code generators, and direct external writers can bypass filesystem version checks; Leads must coordinate ownership and review the final diff.
200
204
  - **Flat immutable roster** — only the Lead creates direct teammates; there is no nested Team, rename, deletion, or name reuse.
201
- - **No automatic ownership release** — idle, interruption, process exit, and failed work do not release a task owner.
205
+ - **No automatic ownership release** — inactivity, interruption, process exit, and failed work do not release a task owner.
202
206
  - **Mailbox is not cross-process exactly-once** — concurrent harness processes over one Team are unsupported.
203
207
 
204
208
  <a id="dev-note"></a>
package/README.zh.md CHANGED
@@ -60,7 +60,7 @@ kind: "package-reference"
60
60
 
61
61
  请 Lead 创建 teammate:给它一个唯一的小写名字(例如 `reviewer`)并描述其职责。teammate 可以 fresh 启动(不携带 Lead 对话的任何记忆),也可以作为 fork 启动(继承 Lead 已完成的轮次);创建请求决定用哪种。teammate 名字是永久的——即使创建失败的 teammate 也保留其名字,任何名字都不会被复用。
62
62
 
63
- roster 显示每个成员的职责(`lead` 或 `teammate`)与当前状态:`running`、`idle`、`inactive`(存在但未加载的成员)、`provisioning` 或 `failed`。未加载的成员会在唤醒后收到其消息。
63
+ roster 显示每个成员的职责(`lead` 或 `teammate`)与当前状态:`running`、`inactive`(当前没有执行轮次,包括已加载和仅存储的成员)、`provisioning` 或 `failed`。未加载的成员会在唤醒后收到其消息。
64
64
 
65
65
  只有 Lead 可以创建 teammate 或中断它们。
66
66
 
@@ -68,7 +68,7 @@ roster 显示每个成员的职责(`lead` 或 `teammate`)与当前状态:`
68
68
 
69
69
  任何成员都可以向任何其他成员或 Lead 发送消息。live 成员会立即收到;离线成员的消息会排队,并在其恢复后到达。消息不会丢失,也不会重复投递。
70
70
 
71
- 每条消息都使用 Steer:running target 在最近的步骤边界收到消息,idle target 启动一个轮次,inactive teammate 则冷恢复。发送方始终能看到结果——target inbox 已接受,或在投递暂时不可用时保留为 queued。排队的消息已经安全存储,因此绝不能重发。
71
+ 每条消息都使用 Steer:running target 在最近的步骤边界收到消息,inactive target 在已加载时启动一个轮次,否则冷恢复。发送方始终能看到结果——target inbox 已接受,或在投递暂时不可用时保留为 queued。排队的消息已经安全存储,因此绝不能重发。
72
72
 
73
73
  ### 共享任务板
74
74
 
@@ -145,6 +145,10 @@ Lead 可以停止 teammate 的当前轮次,而不会删除其排队的消息
145
145
 
146
146
  Team 事件追加到精确的 live Lead 会话,并在操作报告成功或唤醒等待者之前 flush。`team/member`、`team/task`、`team/message/queued` 与 `team/message/delivered` 仅存在于日志:它们从不进入会话表面,因此派生模型历史不受协作记录影响。顺序与时间由会话事件的 `seq` 与 `time` 负责,快照不重复保存。`./invariant` 伴生插件把每条候选 Team 事件对照已提交前缀回放,并在 append 前拒绝非法转换。
147
147
 
148
+ 原生 V4 的 Team 事件及检查点准入会拒绝退役的 `tool-result` 内容,防止它进入邮箱状态。历史转换由 Session 格式迁移负责,Team 投影不转换旧包装。
149
+
150
+ Mailbox 投影与 checkpoint 准入保留本地声明的校验器之外获准内容中全部已解码 JSON 字段,包括自有 `__proto__` 键。本地字段检查覆盖 `text`、`reasoning`、`image` 和 `tool-call`;获准的未知标签保持不透明。Team 投影缓存版本 4 从 Session 日志重建较早缓存版本的 checkpoint;Session 格式版本保持不变。
151
+
148
152
  ### Dispose
149
153
 
150
154
  dispose 会关闭准入、中止并等待已获准的创建与 mailbox dispatch 事务,再让 continuation owner 释放 roster 中确切的 live direct child 及其后代;Lead 的非 Team continuable child 不受影响。cleanup 失败会让 dispose 明确失败,并以 `disposalTimeoutMs` 为上限。
@@ -169,7 +173,7 @@ dispose 会关闭准入、中止并等待已获准的创建与 mailbox dispatch
169
173
 
170
174
  ### 浏览器 Remote
171
175
 
172
- `TeamService` 除了 roster、mailbox、task 与 lifecycle operation,还拥有生成的 `agentTeams/view`、`agentTeams/createTask` 与 `agentTeams/updateTask` Remote method。`./remote` 导出由 Web UI 挂载的 Client contribution,`./client` 则重新导出可在浏览器 compilation face 中安全使用的 request、view 与 task mutation result type。Typert 在外层 `RemoteResult` 中保留 transport failure;create 与 update rejection 则作为 transport 成功响应中的显式 domain result,其中过期的 update revision 会区分为 task conflict。
176
+ `TeamService` 向浏览器客户端公开只读的 `agentTeams/view` Remote method。任务创建与更新由 Team agent 通过服务和模型工具执行。`./remote` 导出由 Web UI 挂载的 Client contribution,`./client` 导出可供浏览器使用的成员与任务视图。
173
177
 
174
178
  ## 模型体验
175
179
 
@@ -198,7 +202,7 @@ Peer 消息追加在 target 可复用历史前缀之后。冷恢复会先复用
198
202
  - **单进程、共享 checkout**——成员共享 cwd,修改立即可见;本包不提供 worktree、远端成员、merge 或文件锁。
199
203
  - **write scope 仅作提示**——Bash、formatter、代码生成器与直接外部写入可以绕过文件版本检查;Lead 必须协调 owner 并检查最终 diff。
200
204
  - **扁平且不可变的 roster**——只有 Lead 可以创建直接 teammate;不支持嵌套 Team、重命名、删除或名字复用。
201
- - **不会自动释放 owner**——idle、interrupt、进程退出与工作失败都不会释放任务 owner。
205
+ - **不会自动释放 owner**——成员不活动、interrupt、进程退出与工作失败都不会释放任务 owner。
202
206
  - **mailbox 不保证跨进程 exactly-once**——不支持多个 harness 进程并发操作同一 Team。
203
207
 
204
208
  <a id="dev-note"></a>
package/lib/index.js CHANGED
@@ -440,7 +440,7 @@ var TeamRoster = class {
440
440
  id: root.id,
441
441
  name: "lead",
442
442
  role: "lead",
443
- status: root.status,
443
+ status: availability(root),
444
444
  ...root.options.model === void 0 ? {} : { model: root.options.model },
445
445
  diagnostics: []
446
446
  }];
@@ -451,7 +451,7 @@ var TeamRoster = class {
451
451
  id: member.id,
452
452
  name: member.name,
453
453
  role: "teammate",
454
- status: member.phase === "failed" ? "failed" : member.phase === "provisioning" ? "provisioning" : live?.status ?? "inactive",
454
+ status: member.phase === "failed" ? "failed" : member.phase === "provisioning" ? "provisioning" : availability(live),
455
455
  description: member.description,
456
456
  provider: member.provider,
457
457
  context: member.context,
@@ -508,7 +508,7 @@ var TeamRoster = class {
508
508
  if (target.id === membership.root.id) throw new TeamError("the Team Lead cannot interrupt itself", "TEAM_INVALID_TARGET");
509
509
  const live = this.ctx.agents.get(target.id);
510
510
  if (live === void 0) return { previousStatus: "inactive" };
511
- const previousStatus = live.status;
511
+ const previousStatus = availability(live);
512
512
  this.ctx.subagents.interrupt(target.id, {
513
513
  kind: "ancestor",
514
514
  agent: caller
@@ -691,7 +691,7 @@ var TeamRoster = class {
691
691
  id: member.id,
692
692
  name: member.name,
693
693
  role: "teammate",
694
- status: live?.status ?? "inactive",
694
+ status: availability(live),
695
695
  description: member.description,
696
696
  provider: member.provider,
697
697
  context: member.context,
@@ -724,6 +724,10 @@ var TeamRoster = class {
724
724
  return foldSubagentDescriptor(agent.session.snapshotEvents(agent.session.inheritedEventCount)) !== void 0;
725
725
  }
726
726
  };
727
+ /** Turn availability is independent of whether the Agent is loaded. */
728
+ function availability(agent) {
729
+ return agent?.status === "running" ? "running" : "inactive";
730
+ }
727
731
  //#endregion
728
732
  //#region lib/types/mailbox.js
729
733
  /** Durable Team mailbox admission, target-local dispatch, acknowledgement, and recovery. */
@@ -1083,13 +1087,11 @@ const contentBlockSchema = z$1.lazy(() => z$1.union([
1083
1087
  name: z$1.string(),
1084
1088
  arguments: z$1.string()
1085
1089
  }).strict(),
1086
- z$1.object({
1087
- type: z$1.literal("tool-result"),
1088
- toolCallId: z$1.string().min(1),
1089
- content: z$1.array(contentBlockSchema),
1090
- isError: z$1.boolean().optional()
1091
- }).strict(),
1092
- z$1.object({ type: z$1.string().min(1) }).loose().refine((block) => !coreContentBlockTypes.has(block.type), { message: "known content block types must match their declared fields" })
1090
+ z$1.custom((value) => {
1091
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
1092
+ const type = value.type;
1093
+ return typeof type === "string" && type.length > 0 && !coreContentBlockTypes.has(type);
1094
+ })
1093
1095
  ]));
1094
1096
  const teamMemberSnapshotSchema = z$1.object({
1095
1097
  id: sessionIdSchema,
@@ -1282,7 +1284,7 @@ function applyCurrentTeamEvent(state, event) {
1282
1284
  /** Host-only Team projection selected by the projected Session identity. */
1283
1285
  const teamProjectionDefinition = {
1284
1286
  key: "agentTeam",
1285
- stateVersion: 3,
1287
+ stateVersion: 4,
1286
1288
  stateSchema: teamProjectionEntrySchema,
1287
1289
  init: (header) => emptyTeamState(header.id),
1288
1290
  apply: (state, event) => {
@@ -1606,14 +1608,10 @@ let TeamService = (() => {
1606
1608
  let _classSuper = TypertRemoteService;
1607
1609
  let _instanceExtraInitializers = [];
1608
1610
  let _remoteView_decorators;
1609
- let _remoteCreateTask_decorators;
1610
- let _remoteUpdateTask_decorators;
1611
1611
  return class TeamService extends _classSuper {
1612
1612
  static {
1613
1613
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
1614
1614
  _remoteView_decorators = [Remote("view")];
1615
- _remoteCreateTask_decorators = [Remote("createTask")];
1616
- _remoteUpdateTask_decorators = [Remote("updateTask")];
1617
1615
  __esDecorate(this, null, _remoteView_decorators, {
1618
1616
  kind: "method",
1619
1617
  name: "remoteView",
@@ -1625,28 +1623,6 @@ let TeamService = (() => {
1625
1623
  },
1626
1624
  metadata: _metadata
1627
1625
  }, null, _instanceExtraInitializers);
1628
- __esDecorate(this, null, _remoteCreateTask_decorators, {
1629
- kind: "method",
1630
- name: "remoteCreateTask",
1631
- static: false,
1632
- private: false,
1633
- access: {
1634
- has: (obj) => "remoteCreateTask" in obj,
1635
- get: (obj) => obj.remoteCreateTask
1636
- },
1637
- metadata: _metadata
1638
- }, null, _instanceExtraInitializers);
1639
- __esDecorate(this, null, _remoteUpdateTask_decorators, {
1640
- kind: "method",
1641
- name: "remoteUpdateTask",
1642
- static: false,
1643
- private: false,
1644
- access: {
1645
- has: (obj) => "remoteUpdateTask" in obj,
1646
- get: (obj) => obj.remoteUpdateTask
1647
- },
1648
- metadata: _metadata
1649
- }, null, _instanceExtraInitializers);
1650
1626
  if (_metadata) Object.defineProperty(this, Symbol.metadata, {
1651
1627
  enumerable: true,
1652
1628
  configurable: true,
@@ -1823,42 +1799,6 @@ let TeamService = (() => {
1823
1799
  tasks: this.listTasks(agent)
1824
1800
  };
1825
1801
  }
1826
- /**
1827
- * Create one shared task through the generated Remote API.
1828
- * @param agent - exact live Team member creating the task.
1829
- * @param request - task text, blockers, and advisory write scopes.
1830
- * @returns the revision-one task or a typed Team rejection.
1831
- */
1832
- remoteCreateTask(agent, request) {
1833
- return this.taskMutationResult(this.createTask(agent, request));
1834
- }
1835
- /**
1836
- * Apply one task mutation and preserve Team rejections as business results.
1837
- * @param agent - exact live Team member authorizing the mutation.
1838
- * @param request - task identity, expected revision, action, and action fields.
1839
- * @returns the committed task or a typed Team rejection.
1840
- */
1841
- remoteUpdateTask(agent, request) {
1842
- return this.taskMutationResult(this.updateTask(agent, request));
1843
- }
1844
- /** Preserve Team task rejections while allowing unexpected failures to reject the Remote call. */
1845
- async taskMutationResult(operation) {
1846
- try {
1847
- return {
1848
- ok: true,
1849
- value: await operation
1850
- };
1851
- } catch (error) {
1852
- if (!(error instanceof TeamError)) throw error;
1853
- return {
1854
- ok: false,
1855
- error: {
1856
- code: error.code === "TEAM_TASK_STALE_REVISION" ? "team-task-conflict" : "team-rejected",
1857
- message: error.message
1858
- }
1859
- };
1860
- }
1861
- }
1862
1802
  /** Queue one contained recovery pass after publication has unwound. */
1863
1803
  scheduleRecovery(agent) {
1864
1804
  queueMicrotask(() => {
package/lib/invariant.js CHANGED
@@ -128,13 +128,11 @@ const contentBlockSchema = z.lazy(() => z.union([
128
128
  name: z.string(),
129
129
  arguments: z.string()
130
130
  }).strict(),
131
- z.object({
132
- type: z.literal("tool-result"),
133
- toolCallId: z.string().min(1),
134
- content: z.array(contentBlockSchema),
135
- isError: z.boolean().optional()
136
- }).strict(),
137
- z.object({ type: z.string().min(1) }).loose().refine((block) => !coreContentBlockTypes.has(block.type), { message: "known content block types must match their declared fields" })
131
+ z.custom((value) => {
132
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
133
+ const type = value.type;
134
+ return typeof type === "string" && type.length > 0 && !coreContentBlockTypes.has(type);
135
+ })
138
136
  ]));
139
137
  const teamMemberSnapshotSchema = z.object({
140
138
  id: sessionIdSchema,
@@ -327,7 +325,7 @@ function applyCurrentTeamEvent(state, event) {
327
325
  /** Host-only Team projection selected by the projected Session identity. */
328
326
  const teamProjectionDefinition = {
329
327
  key: "agentTeam",
330
- stateVersion: 3,
328
+ stateVersion: 4,
331
329
  stateSchema: teamProjectionEntrySchema,
332
330
  init: (header) => emptyTeamState(header.id),
333
331
  apply: (state, event) => {