@khorsheed/dsh-local-agent 0.1.0-rc.8 → 0.2.0-rc.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # 变更记录
2
2
 
3
+ ## 0.2.0-rc.1(2026-09-30)
4
+
5
+ - **家族折叠:委派工具工厂收进本包**。新增 `./tool-factory` 导出(`mountSubagentDelegationTool` / `subagentToolConfigSchema` / `SubagentToolConfig` / `RESUME_HINT_PREFIX`)——原共享包 `@khorsheed/dsh-local-agent-tool-subagent` 的实现整体迁入,该包随之退役。各 provider 的委派工具改为各自包内的 `./tool` 组合入口(`@khorsheed/dsh-local-agent-kimi/tool` 等),由 agent 预设点名按会话授予,不再挂 profile 根;工具来源标签的 owner 由调用入口传自己包名,工厂不再硬编码。随工厂迁入新增 peer 依赖 `@deepseek-ai/dsh-tools`。
6
+
3
7
  ## 0.1.0-rc.8(2026-09-30)
4
8
 
5
9
  家族同版联动:加宽 `@deepseek-ai/dsh-*` peer 区间以覆盖宿主 0.2.0(0.2.0 的兼容闸会禁用 peer 区间不覆盖宿主版本的已装插件)。无功能变更。
package/README.en.md CHANGED
@@ -111,7 +111,7 @@ export function registerCodex(ctx: Context): void {
111
111
 
112
112
  ### Delegation registry (resume carrier)
113
113
 
114
- The registry also owns the family's **delegation registry**: a per-child-session record of which provider and CLI session a delegation used, plus a per-(parent, provider) FIFO of delegation intents. The family tool (`@khorsheed/dsh-local-agent-tool-subagent`, mounted by each harness bundle's patch) stages exactly one intent per call before `ctx.subagents.start()`, and the owning provider consumes exactly one per start — so fresh and resume rounds stay paired even under parallel delegation. A resume handle (the dsh child session id) resolves through the registry, which rejects a handle naming an unknown child, another parent's session, or the wrong provider; the subagent request descriptor cannot carry the target, so this service is the family-internal carrier. Mappings persist per harness in an append-only `delegations.jsonl` under the harness's scoped home (last line per child session wins), so a resume handle survives a host restart.
114
+ The registry also owns the family's **delegation registry**: a per-child-session record of which provider and CLI session a delegation used, plus a per-(parent, provider) FIFO of delegation intents. The family tool (each provider package's `./tool` composition entry, sharing this package's `./tool-factory` export — the former `@khorsheed/dsh-local-agent-tool-subagent` package retired at the family fold) stages exactly one intent per call before `ctx.subagents.start()`, and the owning provider consumes exactly one per start — so fresh and resume rounds stay paired even under parallel delegation. A resume handle (the dsh child session id) resolves through the registry, which rejects a handle naming an unknown child, another parent's session, or the wrong provider; the subagent request descriptor cannot carry the target, so this service is the family-internal carrier. Mappings persist per harness in an append-only `delegations.jsonl` under the harness's scoped home (last line per child session wins), so a resume handle survives a host restart.
115
115
 
116
116
  ### Delegation call options (facade)
117
117
 
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # last confirmed-consistent state. Both languages carry equal authority; after
3
3
  # editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/local-agent/README.en.md
5
- packages/local-agent/README.en.md: 131ab524efcb081b0cbd4f27f6bd2b221d58003f
6
- packages/local-agent/README.md: 16d63a8046557a8f6d7582ec338f41a0f1774110
5
+ packages/local-agent/README.en.md: 94e1222b1d0da755b9d1bdd1cbf3d3561dafa033
6
+ packages/local-agent/README.md: d63cf0d8cceadf5c2b6d159c3c638341358e2b3a
package/README.md CHANGED
@@ -111,7 +111,7 @@ export function registerCodex(ctx: Context): void {
111
111
 
112
112
  ### 委派 registry(resume 载体)
113
113
 
114
- registry 还持有家族的**委派 registry**:每个子会话一条记录,记下该委派用的 provider 与 CLI 会话,以及按 (parent, provider) 分组的委派 intent FIFO。家族工具(`@khorsheed/dsh-local-agent-tool-subagent`,由各 harness bundle 的 patch 挂载)在每次调用 `ctx.subagents.start()` 前恰好 stage 一个 intent,归属 provider 每次 start 恰好消费一个——因此即使并行委派,fresh 轮与 resume 轮也能正确配对。resume 轮的句柄(dsh 子会话 id)经 registry 解析,凡是未知子会话、他人 parent 的会话、或错误 provider 的句柄都会被拒绝;subagent 请求 descriptor 无法携带该目标,因此本服务就是家族内部的载体。映射按 harness 持久化在其作用域目录下的 append-only `delegations.jsonl`(同一子会话最后一行生效),resume 句柄因此能跨宿主重启存活。
114
+ registry 还持有家族的**委派 registry**:每个子会话一条记录,记下该委派用的 provider 与 CLI 会话,以及按 (parent, provider) 分组的委派 intent FIFO。家族工具(各 provider 包的 `./tool` 组合入口,实现共享自本包的 `./tool-factory` 导出——原 `@khorsheed/dsh-local-agent-tool-subagent` 已于家族折叠退役)在每次调用 `ctx.subagents.start()` 前恰好 stage 一个 intent,归属 provider 每次 start 恰好消费一个——因此即使并行委派,fresh 轮与 resume 轮也能正确配对。resume 轮的句柄(dsh 子会话 id)经 registry 解析,凡是未知子会话、他人 parent 的会话、或错误 provider 的句柄都会被拒绝;subagent 请求 descriptor 无法携带该目标,因此本服务就是家族内部的载体。映射按 harness 持久化在其作用域目录下的 append-only `delegations.jsonl`(同一子会话最后一行生效),resume 句柄因此能跨宿主重启存活。
115
115
 
116
116
  ### 委派调用选项(facade)
117
117
 
package/lib/client.js CHANGED
@@ -165,17 +165,17 @@ window.__ModuleLoader__.load({
165
165
  document.head.appendChild(tag);
166
166
  }
167
167
  var MemberConfiguration_module_css_default = {
168
- "back": "pkplGa_back",
169
- "triggerLabel": "pkplGa_triggerLabel",
170
- "optionLabel": "pkplGa_optionLabel",
168
+ "actions": "pkplGa_actions",
171
169
  "candidates": "pkplGa_candidates",
172
- "fields": "pkplGa_fields",
170
+ "root": "pkplGa_root",
171
+ "optionLabel": "pkplGa_optionLabel",
172
+ "triggerLabel": "pkplGa_triggerLabel",
173
+ "panel": "pkplGa_panel",
174
+ "settings": "pkplGa_settings",
173
175
  "menuRow": "pkplGa_menuRow",
176
+ "back": "pkplGa_back",
174
177
  "check": "pkplGa_check",
175
- "settings": "pkplGa_settings",
176
- "panel": "pkplGa_panel",
177
- "actions": "pkplGa_actions",
178
- "root": "pkplGa_root"
178
+ "fields": "pkplGa_fields"
179
179
  };
180
180
  //#endregion
181
181
  //#region src/client/ModelConfigurationFields.tsx
@@ -6046,12 +6046,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6046
6046
  "requestId": string(),
6047
6047
  "revision": number(),
6048
6048
  "status": union([
6049
- literal("cancelled"),
6050
6049
  literal("pending"),
6051
6050
  literal("applying"),
6052
6051
  literal("failed"),
6053
6052
  literal("unsupported"),
6054
6053
  literal("applied"),
6054
+ literal("cancelled"),
6055
6055
  literal("conflict"),
6056
6056
  literal("locked")
6057
6057
  ]),
@@ -6071,8 +6071,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6071
6071
  let _khorsheed_dsh_local_agent_localAgentGateway_controlMemberInbox_parameter_3$schema$value;
6072
6072
  const _khorsheed_dsh_local_agent_localAgentGateway_controlMemberInbox_parameter_3$schema = () => _khorsheed_dsh_local_agent_localAgentGateway_controlMemberInbox_parameter_3$schema$value ??= union([
6073
6073
  _undefined(),
6074
- literal("done"),
6075
- literal("cancelled")
6074
+ literal("cancelled"),
6075
+ literal("done")
6076
6076
  ]);
6077
6077
  let _khorsheed_dsh_local_agent_localAgentGateway_controlMemberInbox_parameter_4$schema$value;
6078
6078
  const _khorsheed_dsh_local_agent_localAgentGateway_controlMemberInbox_parameter_4$schema = () => _khorsheed_dsh_local_agent_localAgentGateway_controlMemberInbox_parameter_4$schema$value ??= union([_undefined(), string()]);
@@ -6205,10 +6205,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6205
6205
  let _khorsheed_dsh_local_agent_localAgentGateway_followMemberDirectory_result$schema$value;
6206
6206
  const _khorsheed_dsh_local_agent_localAgentGateway_followMemberDirectory_result$schema = () => _khorsheed_dsh_local_agent_localAgentGateway_followMemberDirectory_result$schema$value ??= object({
6207
6207
  "status": union([
6208
- literal("stale"),
6209
- literal("error"),
6210
6208
  literal("loading"),
6211
6209
  literal("ready"),
6210
+ literal("stale"),
6211
+ literal("error"),
6212
6212
  literal("unsupported")
6213
6213
  ]),
6214
6214
  "refreshing": boolean(),
@@ -6249,7 +6249,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6249
6249
  "id": string(),
6250
6250
  "turn": number(),
6251
6251
  "step": number(),
6252
- "kind": union([literal("text"), literal("think")]),
6252
+ "kind": union([literal("think"), literal("text")]),
6253
6253
  "text": string(),
6254
6254
  "revision": number(),
6255
6255
  "receivedAt": number()
@@ -6260,8 +6260,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6260
6260
  const _khorsheed_dsh_local_agent_localAgentGateway_followMembers_parameter_0$schema = () => _khorsheed_dsh_local_agent_localAgentGateway_followMembers_parameter_0$schema$value ??= array(object({
6261
6261
  "memberId": string(),
6262
6262
  "channel": union([
6263
- literal("directory"),
6264
6263
  literal("configuration"),
6264
+ literal("directory"),
6265
6265
  literal("output")
6266
6266
  ])
6267
6267
  }));
@@ -6389,10 +6389,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6389
6389
  "channel": literal("directory"),
6390
6390
  "value": object({
6391
6391
  "status": union([
6392
- literal("stale"),
6393
- literal("error"),
6394
6392
  literal("loading"),
6395
6393
  literal("ready"),
6394
+ literal("stale"),
6395
+ literal("error"),
6396
6396
  literal("unsupported")
6397
6397
  ]),
6398
6398
  "refreshing": boolean(),
@@ -6434,7 +6434,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6434
6434
  "id": string(),
6435
6435
  "turn": number(),
6436
6436
  "step": number(),
6437
- "kind": union([literal("text"), literal("think")]),
6437
+ "kind": union([literal("think"), literal("text")]),
6438
6438
  "text": string(),
6439
6439
  "revision": number(),
6440
6440
  "receivedAt": number()
@@ -6446,8 +6446,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6446
6446
  "memberId": string(),
6447
6447
  "channel": literal("error"),
6448
6448
  "source": union([
6449
- literal("directory"),
6450
6449
  literal("configuration"),
6450
+ literal("directory"),
6451
6451
  literal("output")
6452
6452
  ]),
6453
6453
  "message": string()
@@ -6460,10 +6460,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6460
6460
  let _khorsheed_dsh_local_agent_localAgentGateway_followModelDirectory_result$schema$value;
6461
6461
  const _khorsheed_dsh_local_agent_localAgentGateway_followModelDirectory_result$schema = () => _khorsheed_dsh_local_agent_localAgentGateway_followModelDirectory_result$schema$value ??= object({
6462
6462
  "status": union([
6463
- literal("stale"),
6464
- literal("error"),
6465
6463
  literal("loading"),
6466
6464
  literal("ready"),
6465
+ literal("stale"),
6466
+ literal("error"),
6467
6467
  literal("unsupported")
6468
6468
  ]),
6469
6469
  "refreshing": boolean(),
@@ -6628,10 +6628,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6628
6628
  "choices": array(string()),
6629
6629
  "directory": object({
6630
6630
  "status": union([
6631
- literal("stale"),
6632
- literal("error"),
6633
6631
  literal("loading"),
6634
6632
  literal("ready"),
6633
+ literal("stale"),
6634
+ literal("error"),
6635
6635
  literal("unsupported")
6636
6636
  ]),
6637
6637
  "refreshing": boolean(),
@@ -6790,10 +6790,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6790
6790
  let _khorsheed_dsh_local_agent_localAgentGateway_memberDirectory_result$schema$value;
6791
6791
  const _khorsheed_dsh_local_agent_localAgentGateway_memberDirectory_result$schema = () => _khorsheed_dsh_local_agent_localAgentGateway_memberDirectory_result$schema$value ??= union([literal(null), object({
6792
6792
  "status": union([
6793
- literal("stale"),
6794
- literal("error"),
6795
6793
  literal("loading"),
6796
6794
  literal("ready"),
6795
+ literal("stale"),
6796
+ literal("error"),
6797
6797
  literal("unsupported")
6798
6798
  ]),
6799
6799
  "refreshing": boolean(),
@@ -6836,11 +6836,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6836
6836
  "id": string(),
6837
6837
  "text": string(),
6838
6838
  "status": union([
6839
- literal("running"),
6840
- literal("done"),
6841
- literal("cancelled"),
6842
6839
  literal("failed"),
6840
+ literal("cancelled"),
6843
6841
  literal("queued"),
6842
+ literal("running"),
6843
+ literal("done"),
6844
6844
  literal("uncertain")
6845
6845
  ]),
6846
6846
  "createdAt": number(),
@@ -6981,10 +6981,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6981
6981
  "choices": array(string()),
6982
6982
  "directory": object({
6983
6983
  "status": union([
6984
- literal("stale"),
6985
- literal("error"),
6986
6984
  literal("loading"),
6987
6985
  literal("ready"),
6986
+ literal("stale"),
6987
+ literal("error"),
6988
6988
  literal("unsupported")
6989
6989
  ]),
6990
6990
  "refreshing": boolean(),
@@ -7038,10 +7038,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7038
7038
  let _khorsheed_dsh_local_agent_localAgentGateway_modelDirectory_result$schema$value;
7039
7039
  const _khorsheed_dsh_local_agent_localAgentGateway_modelDirectory_result$schema = () => _khorsheed_dsh_local_agent_localAgentGateway_modelDirectory_result$schema$value ??= union([literal(null), object({
7040
7040
  "status": union([
7041
- literal("stale"),
7042
- literal("error"),
7043
7041
  literal("loading"),
7044
7042
  literal("ready"),
7043
+ literal("stale"),
7044
+ literal("error"),
7045
7045
  literal("unsupported")
7046
7046
  ]),
7047
7047
  "refreshing": boolean(),
@@ -7120,12 +7120,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7120
7120
  "requestId": string(),
7121
7121
  "revision": number(),
7122
7122
  "status": union([
7123
- literal("cancelled"),
7124
7123
  literal("pending"),
7125
7124
  literal("applying"),
7126
7125
  literal("failed"),
7127
7126
  literal("unsupported"),
7128
7127
  literal("applied"),
7128
+ literal("cancelled"),
7129
7129
  literal("conflict"),
7130
7130
  literal("locked")
7131
7131
  ]),
@@ -8277,29 +8277,29 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8277
8277
  document.head.appendChild(tag);
8278
8278
  }
8279
8279
  var MemberComposer_module_css_default = {
8280
- "modelChipLabel": "_7o4e8W_modelChipLabel",
8281
- "modelPicker": "_7o4e8W_modelPicker",
8282
- "modelItem": "_7o4e8W_modelItem",
8283
- "input": "_7o4e8W_input",
8284
- "frame": "_7o4e8W_frame",
8285
8280
  "root": "_7o4e8W_root",
8281
+ "error": "_7o4e8W_error",
8282
+ "dock": "_7o4e8W_dock",
8283
+ "modelMenu": "_7o4e8W_modelMenu",
8284
+ "modelItemReset": "_7o4e8W_modelItemReset",
8285
+ "modelItemHint": "_7o4e8W_modelItemHint",
8286
+ "card": "_7o4e8W_card",
8287
+ "frame": "_7o4e8W_frame",
8288
+ "modelPicker": "_7o4e8W_modelPicker",
8289
+ "modelChipLabel": "_7o4e8W_modelChipLabel",
8290
+ "chevron": "_7o4e8W_chevron",
8286
8291
  "row": "_7o4e8W_row",
8287
8292
  "header": "_7o4e8W_header",
8288
- "chevron": "_7o4e8W_chevron",
8289
- "primary": "_7o4e8W_primary",
8290
- "modelItemCheck": "_7o4e8W_modelItemCheck",
8291
- "modelItemReset": "_7o4e8W_modelItemReset",
8292
8293
  "modelChip": "_7o4e8W_modelChip",
8293
- "modelMenu": "_7o4e8W_modelMenu",
8294
- "dockRow": "_7o4e8W_dockRow",
8295
- "error": "_7o4e8W_error",
8296
- "modelItemHint": "_7o4e8W_modelItemHint",
8297
- "dock": "_7o4e8W_dock",
8298
- "running": "_7o4e8W_running",
8299
- "chevronOpen": "_7o4e8W_chevronOpen",
8300
8294
  "modelItemCurrent": "_7o4e8W_modelItemCurrent",
8301
8295
  "modelItemLabel": "_7o4e8W_modelItemLabel",
8302
- "card": "_7o4e8W_card"
8296
+ "running": "_7o4e8W_running",
8297
+ "modelItem": "_7o4e8W_modelItem",
8298
+ "modelItemCheck": "_7o4e8W_modelItemCheck",
8299
+ "chevronOpen": "_7o4e8W_chevronOpen",
8300
+ "dockRow": "_7o4e8W_dockRow",
8301
+ "input": "_7o4e8W_input",
8302
+ "primary": "_7o4e8W_primary"
8303
8303
  };
8304
8304
  //#endregion
8305
8305
  //#region src/client/MemberComposer.tsx
@@ -9360,30 +9360,30 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9360
9360
  document.head.appendChild(tag);
9361
9361
  }
9362
9362
  var ProviderAuthBlock_module_css_default = {
9363
- "credentialDot": "On5p7W_credentialDot",
9364
- "rowName": "On5p7W_rowName",
9363
+ "status": "On5p7W_status",
9365
9364
  "retryButton": "On5p7W_retryButton",
9366
- "rowCard": "On5p7W_rowCard",
9367
- "title": "On5p7W_title",
9368
- "codeInput": "On5p7W_codeInput",
9369
- "dotOn": "On5p7W_dotOn",
9370
- "intro": "On5p7W_intro",
9365
+ "credentialDot": "On5p7W_credentialDot",
9366
+ "promptText": "On5p7W_promptText",
9371
9367
  "rowTag": "On5p7W_rowTag",
9372
- "rowActions": "On5p7W_rowActions",
9373
- "dotPending": "On5p7W_dotPending",
9374
- "status": "On5p7W_status",
9375
- "rows": "On5p7W_rows",
9376
- "logoutButton": "On5p7W_logoutButton",
9368
+ "rowCard": "On5p7W_rowCard",
9369
+ "rowName": "On5p7W_rowName",
9377
9370
  "openPage": "On5p7W_openPage",
9371
+ "logoutButton": "On5p7W_logoutButton",
9372
+ "rowPending": "On5p7W_rowPending",
9373
+ "dotOn": "On5p7W_dotOn",
9378
9374
  "section": "On5p7W_section",
9379
- "loginPrompt": "On5p7W_loginPrompt",
9380
- "promptText": "On5p7W_promptText",
9381
9375
  "dotOff": "On5p7W_dotOff",
9382
- "loginButton": "On5p7W_loginButton",
9383
- "rowIdentity": "On5p7W_rowIdentity",
9384
9376
  "rowHead": "On5p7W_rowHead",
9377
+ "rowActions": "On5p7W_rowActions",
9378
+ "title": "On5p7W_title",
9379
+ "loginButton": "On5p7W_loginButton",
9380
+ "loginPrompt": "On5p7W_loginPrompt",
9381
+ "codeInput": "On5p7W_codeInput",
9382
+ "rows": "On5p7W_rows",
9383
+ "intro": "On5p7W_intro",
9384
+ "dotPending": "On5p7W_dotPending",
9385
9385
  "rosterError": "On5p7W_rosterError",
9386
- "rowPending": "On5p7W_rowPending"
9386
+ "rowIdentity": "On5p7W_rowIdentity"
9387
9387
  };
9388
9388
  //#endregion
9389
9389
  //#region src/client/ProviderAuthBlock.tsx
@@ -9656,10 +9656,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9656
9656
  document.head.appendChild(tag);
9657
9657
  }
9658
9658
  var AuthStatusDot_module_css_default = {
9659
- "credentialDot": "OFjNUG_credentialDot",
9660
9659
  "dotOn": "OFjNUG_dotOn",
9661
- "dotPending": "OFjNUG_dotPending",
9662
- "dotOff": "OFjNUG_dotOff"
9660
+ "dotOff": "OFjNUG_dotOff",
9661
+ "credentialDot": "OFjNUG_credentialDot",
9662
+ "dotPending": "OFjNUG_dotPending"
9663
9663
  };
9664
9664
  //#endregion
9665
9665
  //#region src/client/AuthStatusDot.tsx
@@ -9685,18 +9685,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9685
9685
  document.head.appendChild(tag);
9686
9686
  }
9687
9687
  var MemberInboxView_module_css_default = {
9688
- "list": "Qhm0ea_list",
9689
- "iconAction": "Qhm0ea_iconAction",
9690
- "notice": "Qhm0ea_notice",
9691
- "recovery": "Qhm0ea_recovery",
9688
+ "lead": "Qhm0ea_lead",
9689
+ "row": "Qhm0ea_row",
9692
9690
  "action": "Qhm0ea_action",
9693
- "root": "Qhm0ea_root",
9694
- "panel": "Qhm0ea_panel",
9695
9691
  "reconcile": "Qhm0ea_reconcile",
9696
9692
  "header": "Qhm0ea_header",
9697
- "row": "Qhm0ea_row",
9698
- "lead": "Qhm0ea_lead",
9693
+ "notice": "Qhm0ea_notice",
9694
+ "list": "Qhm0ea_list",
9695
+ "iconAction": "Qhm0ea_iconAction",
9696
+ "root": "Qhm0ea_root",
9697
+ "recovery": "Qhm0ea_recovery",
9699
9698
  "preview": "Qhm0ea_preview",
9699
+ "panel": "Qhm0ea_panel",
9700
9700
  "toggle": "Qhm0ea_toggle"
9701
9701
  };
9702
9702
  //#endregion
@@ -0,0 +1,256 @@
1
+ import z from "@deepseek-ai/schemastery";
2
+ import { defineTool } from "@deepseek-ai/dsh-tools";
3
+ //#region lib/types/tool-factory.js
4
+ /**
5
+ * The family-owned delegation-tool factory: the model-facing subagent tool
6
+ * over one configured `ctx.subagents` provider, replacing the official
7
+ * tool-subagent rows in the harness bundles. The schema is the official subset
8
+ * (`description`/`prompt`) plus an optional `resume` handle: the dsh child
9
+ * session id returned by the first delegation's result text. A resumed call
10
+ * continues the SAME CLI conversation instead of starting a fresh one — the
11
+ * family providers spawn their CLI's resume command (kimi `-S`, claude
12
+ * `--resume`, codex `exec resume`) under the harness scoped home, appending
13
+ * the new round into the same dsh child session.
14
+ *
15
+ * The resume target never travels inside the prompt: task text is untrusted,
16
+ * and a forged handle embedded there would hijack another session's context.
17
+ * The tool instead carries the handle as a first-class `resume` parameter and
18
+ * resolves it through the localAgent service, which rejects any handle that
19
+ * does not name a delegation recorded for THIS parent session and provider.
20
+ * Continuation rounds still go through `ctx.subagents.start()`, so lifecycle
21
+ * events (`subagent/start`/`subagent/end`) and the standard 子代理 surface are
22
+ * unchanged.
23
+ *
24
+ * This module used to ship as the standalone companion package
25
+ * `@khorsheed/dsh-local-agent-tool-subagent` (0.1.x); the family fold moved it
26
+ * into the core package as the `./tool-factory` export (the worktrees 0.3.0 /
27
+ * room 0.2.0 pattern, one level down: the mountable row is each PROVIDER's own
28
+ * `./tool` composition entry, which binds its provider/toolName defaults and
29
+ * passes its package name as the tool-origin owner). `local-agent-dsh`
30
+ * consumes the same factory for its settings-driven dynamic `subagent_dsh`
31
+ * mount. The factory itself is never a loader row.
32
+ *
33
+ * @module @khorsheed/dsh-local-agent/tool-factory
34
+ */
35
+ /**
36
+ * Tag the model-visible tool with its origin (AGENTS.md § Tool origin tagging;
37
+ * seam S12): the capability catalog reads this `Symbol.for`-keyed tag back
38
+ * through `ctx.tools.get()`. This factory registers on behalf of every family
39
+ * provider, so the owner arrives as a PARAMETER — the calling entry's own
40
+ * package identity — never a hardcoded package name. The tag is host-side
41
+ * only and never travels on the model wire.
42
+ */
43
+ const definePluginTool = (def, owner) => Object.assign(def, { [Symbol.for("dsh.tool.origin")]: {
44
+ channel: "plugin",
45
+ owner
46
+ } });
47
+ /**
48
+ * Model-visible self-description appended to a FRESH delegation's result, so
49
+ * the model can continue the same conversation in a later round. Pinned
50
+ * verbatim; the resume tool's `resume` parameter carries the handle.
51
+ */
52
+ const RESUME_HINT_PREFIX = "追问请带 resume=\"";
53
+ /**
54
+ * The Config schema of a provider's `./tool` composition entry: every field
55
+ * optional, defaulting to the CALLING provider's own values, so a preset row
56
+ * needs no config (`- id: tool-subagent-kimi / name:
57
+ * '@khorsheed/dsh-local-agent-kimi/tool'` mounts the kimi delegation tool).
58
+ *
59
+ * Bare `z` return annotation: `z<Config>` fails schemastery 3.18.4's variance
60
+ * under exactOptionalPropertyTypes (TS2375) and dropping the annotation trips
61
+ * TS2742; the interface stays the apply signature's contract.
62
+ */
63
+ function subagentToolConfigSchema(provider, toolName) {
64
+ return z.object({
65
+ provider: z.string().default(provider),
66
+ toolName: z.string().default(toolName)
67
+ });
68
+ }
69
+ /** Render text blocks from the canonical JSON block array without trusting arbitrary values. */
70
+ function outputValueText(values) {
71
+ return values.filter((value) => typeof value === "object" && value !== null && !Array.isArray(value) && value.type === "text" && typeof value.text === "string").map((value) => value.text).join("");
72
+ }
73
+ /** A non-`completed` stop reason means the child did not finish cleanly. */
74
+ function stopReasonError(result) {
75
+ switch (result.stopReason) {
76
+ case "completed": return;
77
+ case "aborted": return "subagent run was cancelled";
78
+ case "error": return result.diagnostic === void 0 ? "subagent run failed" : `subagent run failed: ${result.diagnostic}`;
79
+ case "max-tokens": return "subagent run hit its token limit before finishing";
80
+ case "refusal": return "subagent declined the task";
81
+ default: return `subagent run ended abnormally (${String(result.stopReason)})`;
82
+ }
83
+ }
84
+ /**
85
+ * Fuse a caller-owned controller with the tool execution's signal so EITHER
86
+ * abort cancels the run — the same shape the facade's tracked controller
87
+ * uses. The active-delegation registry's stop lever aborts the controller;
88
+ * the tool's own execution abort flows through the execution signal.
89
+ */
90
+ function fusedSignal(controller, caller) {
91
+ return caller === void 0 ? controller.signal : AbortSignal.any([controller.signal, caller]);
92
+ }
93
+ /**
94
+ * Append the child's preserved partial answer to a stop-reason error so a
95
+ * truncated or cancelled child's real text still reaches the parent model.
96
+ * @param error - the stop-reason headline.
97
+ * @param output - the child's selected output (`SubagentResult.output`).
98
+ * @returns the headline, extended with the partial text when any exists.
99
+ */
100
+ function withPartialText(error, output) {
101
+ const text = output.filter((block) => block.type === "text").map((block) => block.text).join("");
102
+ return text.length === 0 ? error : `${error}\nPartial output before the run ended:\n${text}`;
103
+ }
104
+ /**
105
+ * Collect and release one foreground run without letting disposal replace an
106
+ * independent result failure.
107
+ */
108
+ async function settleForegroundRun(run) {
109
+ const [execution] = await Promise.allSettled([run.result.then((result) => {
110
+ const error = stopReasonError(result);
111
+ if (error !== void 0) throw new Error(withPartialText(error, result.output));
112
+ return {
113
+ kind: "foreground",
114
+ runId: run.id,
115
+ output: result.output
116
+ };
117
+ })]);
118
+ const [disposal] = await Promise.allSettled([Promise.resolve().then(() => run.dispose())]);
119
+ if (execution.status === "rejected") {
120
+ if (disposal.status === "rejected") throw new AggregateError([execution.reason, disposal.reason], `subagent run failed: ${String(execution.reason)}; dispose failed: ${String(disposal.reason)}`);
121
+ throw execution.reason;
122
+ }
123
+ if (disposal.status === "rejected") throw disposal.reason;
124
+ return execution.value;
125
+ }
126
+ /**
127
+ * Model-facing wording for the family CLI providers, which never inherit the
128
+ * parent's conversation (they are fresh CLI processes under a scoped home).
129
+ */
130
+ function familyWording() {
131
+ return {
132
+ description: "Delegate a self-contained task to a local coding-agent CLI (separate process, its own scoped home) so it does not consume this conversation's context. The subagent returns its result, not its intermediate steps. Give it a complete, standalone prompt: it does not see this conversation. To continue the same CLI conversation in a later round, pass the child session id returned by the first result as `resume`. This is the one-shot path — it fits a task you will not revisit. When you expect to work with the SAME agent over several rounds (review → fix → re-review style iteration) and the `room_invite` tool is available, prefer inviting it as a named room member instead: name it by its function (e.g. \"design-review\", \"restart-test\") so later rounds can address it by role and reuse the context it has accumulated.",
133
+ promptDescription: "The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs. Never embed a resume handle inside this text.",
134
+ resumeDescription: "Optional dsh child session id returned by the first delegation's result text, to continue that same CLI conversation in a later round. Omit for a fresh delegation. Never embed the handle inside `prompt` — pass it here."
135
+ };
136
+ }
137
+ /**
138
+ * Mount the family delegation tool on one provider: register the
139
+ * model-visible tool while the provider is registered, mirroring provider
140
+ * lifecycle (sibling load order and HMR replacement can change provider
141
+ * availability while the calling fiber stays active).
142
+ * @param ctx - the calling entry's plugin context (a preset-scope mount, or
143
+ * local-agent-dsh's toggle-owned dynamic plugin).
144
+ * @param config - resolved provider/toolName pair.
145
+ * @param owner - the calling package's own name, stamped as the tool-origin
146
+ * owner (never hardcoded here — this factory serves every family provider).
147
+ */
148
+ function mountSubagentDelegationTool(ctx, config, owner) {
149
+ const toolName = config.toolName ?? "subagent";
150
+ const wording = familyWording();
151
+ let disposeTool;
152
+ const mount = () => {
153
+ disposeTool = ctx.tools.register(definePluginTool(defineTool({
154
+ name: toolName,
155
+ description: wording.description,
156
+ parameters: {
157
+ description: {
158
+ type: "string",
159
+ required: true,
160
+ description: "A short (3-5 word) description of the delegated task, for display."
161
+ },
162
+ prompt: {
163
+ type: "string",
164
+ required: true,
165
+ description: wording.promptDescription
166
+ },
167
+ resume: {
168
+ type: "string",
169
+ description: wording.resumeDescription
170
+ }
171
+ },
172
+ output: {
173
+ schema: {
174
+ type: "object",
175
+ additionalProperties: false,
176
+ properties: {
177
+ kind: {
178
+ type: "string",
179
+ required: true,
180
+ const: "foreground"
181
+ },
182
+ runId: {
183
+ type: "string",
184
+ required: true
185
+ },
186
+ output: {
187
+ type: "array",
188
+ required: true,
189
+ items: { type: "json" }
190
+ }
191
+ }
192
+ },
193
+ render: (_args, value) => [{
194
+ type: "text",
195
+ text: outputValueText(value.output)
196
+ }]
197
+ },
198
+ isConcurrencySafe: () => true,
199
+ async execute(args, exec) {
200
+ const parent = exec.agent;
201
+ if (!parent) throw new Error("subagent tool requires a calling agent (exec.agent was undefined)");
202
+ const parentSessionId = parent.session.id;
203
+ const resumeHandle = args.resume;
204
+ const resume = resumeHandle === void 0 ? void 0 : ctx.localAgent.resolveDelegation(resumeHandle, {
205
+ provider: config.provider,
206
+ parentSessionId
207
+ });
208
+ if (resumeHandle === void 0) ctx.localAgent.stageDelegationIntent(parentSessionId, config.provider, { kind: "fresh" });
209
+ else if (resume !== void 0) {
210
+ await ctx.localAgent.ensureChildLive?.(resumeHandle);
211
+ ctx.localAgent.stageDelegationIntent(parentSessionId, config.provider, {
212
+ kind: "resume",
213
+ childSessionId: resumeHandle,
214
+ cliSessionId: resume.cliSessionId
215
+ });
216
+ }
217
+ const request = {
218
+ label: args.description,
219
+ prompt: [{
220
+ type: "text",
221
+ text: args.prompt
222
+ }],
223
+ parent
224
+ };
225
+ const controller = new AbortController();
226
+ const run = await ctx.subagents.start(config.provider, {
227
+ ...request,
228
+ signal: fusedSignal(controller, exec.signal)
229
+ });
230
+ const registry = ctx.localAgent;
231
+ if (typeof registry.trackDelegationRun === "function") registry.trackDelegationRun(String(run.id), run, () => controller.abort());
232
+ const settled = await settleForegroundRun(run);
233
+ if (resume === void 0) return {
234
+ ...settled,
235
+ output: [...settled.output, {
236
+ type: "text",
237
+ text: `${RESUME_HINT_PREFIX}${run.id}"`
238
+ }]
239
+ };
240
+ return settled;
241
+ }
242
+ }), owner));
243
+ };
244
+ ctx.on("subagent/provider-added", (provider) => {
245
+ if (provider.name === config.provider && disposeTool === void 0) mount();
246
+ });
247
+ ctx.on("subagent/provider-removed", (name) => {
248
+ if (name !== config.provider || disposeTool === void 0) return;
249
+ disposeTool();
250
+ disposeTool = void 0;
251
+ });
252
+ if (ctx.subagents.getProvider(config.provider) !== void 0) mount();
253
+ else ctx.logger.info(`subagent provider "${config.provider}" not registered yet; the "${toolName}" tool will register when it appears`);
254
+ }
255
+ //#endregion
256
+ export { RESUME_HINT_PREFIX, mountSubagentDelegationTool, subagentToolConfigSchema };