@zhushanwen/zcode-subagent-cli 0.2.0 → 0.2.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.
@@ -28,6 +28,9 @@ var ZCODE_APPSERVER_ABORT_GRACE_MS = 3e3;
28
28
  var ZCODE_APPSERVER_ERR_MODEL_CONFIG_MISSING = -32603;
29
29
  var ZCODE_APPSERVER_ERR_BUSY_SESSION = -32010;
30
30
  var ZCODE_APPSERVER_HARVEST_GRACE_MS = 1e3;
31
+ function isFailedTerminalStatus(status) {
32
+ return status === "failed" || status === "error";
33
+ }
31
34
 
32
35
  // src/db-path.ts
33
36
  import * as os from "os";
@@ -1371,7 +1374,13 @@ var SessionChannel = class {
1371
1374
  });
1372
1375
  const turn = {
1373
1376
  sessionId,
1374
- callbacks: { onTextDelta: opts.onTextDelta, onThinkingDelta: opts.onThinkingDelta },
1377
+ callbacks: {
1378
+ onTextDelta: opts.onTextDelta,
1379
+ onThinkingDelta: opts.onThinkingDelta,
1380
+ // [PR3] tool 执行期活性回调(SessionTurnCallbacks.onActivity 注释——宿主侧
1381
+ // 无进展守护的工具执行期刷新面)
1382
+ onActivity: opts.onActivity
1383
+ },
1375
1384
  settled: false,
1376
1385
  deltas: [],
1377
1386
  finalText: void 0,
@@ -1487,7 +1496,9 @@ var SessionChannel = class {
1487
1496
  if (payload === void 0) return;
1488
1497
  this.refreshIdle(turn);
1489
1498
  if (this.applyFinalFrame(turn, payload)) return;
1490
- this.applyStreamDelta(turn, payload);
1499
+ if (!this.applyStreamDelta(turn, payload) && !turn.settled) {
1500
+ turn.callbacks.onActivity?.();
1501
+ }
1491
1502
  }
1492
1503
  /**
1493
1504
  * 收尾帧判定与吸收(A.2 ⑤ 两种形态:payload.response / stopReason+content 变体)。
@@ -1511,9 +1522,12 @@ var SessionChannel = class {
1511
1522
  /** 增量 delta 分发:非空 delta 入账 + 回调;终态后迟到丢弃(不变量 2)。
1512
1523
  * kind 区分两通道(F2):reasoning_delta 只走 thinking 回调(不入 deltas 账本
1513
1524
  * ——deltas 是 response 兜底聚合,推理文本不是答案正文);text_delta / kind
1514
- * 缺席走 text 路径。 */
1525
+ * 缺席走 text 路径。
1526
+ * 返回值([PR3]):帧被 delta 路径消费(thinking 或 text,回调若注册则已触发)
1527
+ * → true;无 delta / 空 delta / 终态后迟到丢弃 → false(handleSessionEvent 据
1528
+ * 此分流 onActivity——delta 帧已是宿主可见进展,不双发活性信号)。 */
1515
1529
  applyStreamDelta(turn, payload) {
1516
- if (typeof payload.delta !== "string" || payload.delta === "") return;
1530
+ if (typeof payload.delta !== "string" || payload.delta === "") return false;
1517
1531
  if (turn.settled) {
1518
1532
  logger2.warn(
1519
1533
  `\u7EC8\u6001\u540E\u8FDF\u5230\u7684 delta \u4E22\u5F03\uFF08\u4F1A\u8BDD ${turn.sessionId}\uFF0C\u4E0D\u53D8\u91CF 2\uFF1Aresolve \u540E\u4E0D\u518D\u53D1\u4E8B\u4EF6\uFF09: ${payload.delta.slice(
@@ -1521,14 +1535,15 @@ var SessionChannel = class {
1521
1535
  DELTA_LOG_CHARS
1522
1536
  )}`
1523
1537
  );
1524
- return;
1538
+ return false;
1525
1539
  }
1526
1540
  if (payload.kind === "reasoning_delta") {
1527
1541
  turn.callbacks.onThinkingDelta?.(payload.delta);
1528
- return;
1542
+ return true;
1529
1543
  }
1530
1544
  turn.deltas.push(payload.delta);
1531
1545
  turn.callbacks.onTextDelta?.(payload.delta);
1546
+ return true;
1532
1547
  }
1533
1548
  handleTelemetry(params) {
1534
1549
  if (!isRecord3(params)) return;
@@ -1548,13 +1563,38 @@ var SessionChannel = class {
1548
1563
  turn.lastTerminalStatus = status;
1549
1564
  this.recordTerminalError(turn, params);
1550
1565
  if (turn.settled) {
1551
- logger2.warn(
1552
- `\u6743\u5A01\u7EC8\u6001\u665A\u4E8E\u843D\u5B9A\u7ED3\u679C\u5230\u8FBE\uFF08\u4F1A\u8BDD ${turn.sessionId}\uFF0C\u5DF2\u843D\u5B9A source=${turn.terminal?.source}\uFF09\uFF1Aturn.terminal status="${status}" \u4EC5\u8BB0\u5F55\u4E0D\u6539\u5199\uFF08P0-1 D5\u2460\uFF09`
1553
- );
1566
+ this.logLateTerminal(turn, status);
1554
1567
  return;
1555
1568
  }
1556
1569
  turn.settle({ status, source: "turn.terminal" });
1557
1570
  }
1571
+ /**
1572
+ * 已落定 turn 的迟到权威终态分级日志(P-Z2 后续:常态迟到降噪)。真机实证该
1573
+ * 形态的唯一可达路径是 success 终态常态迟到(final-frame 先落定 + 权威
1574
+ * turn.terminal 迟到)——即每个成功任务必经;原无条件 warn 经 SDK cli-entry
1575
+ * 的 stderr 兜底 → runtime [rpc:stderr] 全量 console.error 链路,每个成功任务
1576
+ * 产一条零异常区分度的 ERROR 级日志。按权威 status 分级(落定语义不动——
1577
+ * lastTerminalStatus/lastTerminalError 的入账在上游无条件路径完成):
1578
+ * - "success":常态迟到,零日志;
1579
+ * - "interrupted":用户中断形态,debug(stderr 兜底对 debug no-op——对齐
1580
+ * SDK logger CONSOLE_SINK 语义,host/log 反向请求侧仍可观测);
1581
+ * - 失败类(isFailedTerminalStatus,与引擎失败分流同口径)与 unknown/未识别
1582
+ * status:保守 warn(原样 status 已写入文案)——假成功识破的防御面,理论
1583
+ * 不可达但保留,宁可误报不静默。
1584
+ */
1585
+ logLateTerminal(turn, status) {
1586
+ if (status === "success") return;
1587
+ const message = `\u6743\u5A01\u7EC8\u6001\u665A\u4E8E\u843D\u5B9A\u7ED3\u679C\u5230\u8FBE\uFF08\u4F1A\u8BDD ${turn.sessionId}\uFF0C\u5DF2\u843D\u5B9A source=${turn.terminal?.source}\uFF09\uFF1Aturn.terminal status="${status}" \u4EC5\u8BB0\u5F55\u4E0D\u6539\u5199\uFF08P0-1 D5\u2460\uFF09`;
1588
+ if (isFailedTerminalStatus(status)) {
1589
+ logger2.warn(message);
1590
+ return;
1591
+ }
1592
+ if (status === "interrupted") {
1593
+ logger2.debug(message);
1594
+ return;
1595
+ }
1596
+ logger2.warn(message);
1597
+ }
1558
1598
  /** terminal 帧专属错误信息入账(⛔P-Z2 实证:真实 failed 终态的 errorCode/errorMessage
1559
1599
  * 只在 terminal 帧携带,read/delta 拿不到——先到/迟到都记录,消费层合成失败
1560
1600
  * 文案时优先采信)。二者均缺失时不挂 lastTerminalError 字段。 */
@@ -1840,6 +1880,11 @@ var ZcodeEngine = class {
1840
1880
  // 不变量 3a 的拼接比对不含 reasoning)
1841
1881
  onTextDelta: (delta) => ctx.onEvent?.({ type: "text_delta", delta }),
1842
1882
  onThinkingDelta: (delta) => ctx.onEvent?.({ type: "thinking_delta", delta }),
1883
+ // [PR3] 工具执行期活性:非终态非增量的 session/event 帧(tool.updated progress
1884
+ // 等,真机探针实证每 ~1s 一帧)→ activity 事件(零载荷纯活性信号——宿主无进展
1885
+ // 守护对任何事件类型刷新;reducer no-op、不落 journal)。zcode 引擎
1886
+ // conversation:"unsupported",run 域是唯一发射面(无 chat 域接线)。
1887
+ onActivity: () => ctx.onEvent?.({ type: "activity" }),
1843
1888
  onSessionCreated: (sessionId) => {
1844
1889
  currentSessionId = sessionId;
1845
1890
  rt.activeSessions.add(sessionId);
@@ -2145,17 +2190,6 @@ var ZcodeEngine = class {
2145
2190
  }
2146
2191
  for (const ev of synthesizeCoarseEvents(payload.response, payload.usage)) emit(ev);
2147
2192
  }
2148
- /**
2149
- * D1 可选面:zcode 首期不支持 conversation(capabilities 声明)——同步拒绝、
2150
- * 不创建进程,文案给可操作建议(A11)。
2151
- */
2152
- async interact(_handle, _action) {
2153
- return {
2154
- ok: false,
2155
- code: "engine_capability_unsupported",
2156
- message: "zcode \u5F15\u64CE\u4E0D\u652F\u6301 conversation \u4EA4\u4E92\u63A7\u5236\u9762\uFF08capabilities.conversation = 'unsupported'\uFF0C\u6BCF\u4EFB\u52A1\u81EA\u5305\u542B\u4F1A\u8BDD\uFF0C\u65E0\u540C\u8FDB\u7A0B idle \u590D\u7528\uFF09\u3002\u6062\u590D\u6307\u5F15\uFF1A\u6539\u7528\u5355\u6B21 subagent \u8C03\u7528\u91CD\u65B0\u6D3E\u53D1\u4EFB\u52A1\uFF0C\u6216\u4F7F\u7528 engine: 'pi'\uFF08chatMode idle \u590D\u7528\uFF0C\u652F\u6301 message/close/cancel\uFF09\u3002"
2157
- };
2158
- }
2159
2193
  /** [U7] 模型可发现性:v2 桌面登录态聚合(带凭据 provider × models),失败安全返回清单本身可能为空。 */
2160
2194
  listModels() {
2161
2195
  return listZcodeModels(this.deps.sources);
@@ -2355,9 +2389,6 @@ function authoritativeTerminalStatus(r) {
2355
2389
  if (r.lastTerminalStatus !== void 0) return r.lastTerminalStatus;
2356
2390
  return r.terminal.source === "turn.terminal" ? r.terminal.status : void 0;
2357
2391
  }
2358
- function isFailedTerminalStatus(status) {
2359
- return status === "failed" || status === "error";
2360
- }
2361
2392
  function parsedAppServerAttempt(task, r) {
2362
2393
  if (isFailedTerminalStatus(authoritativeTerminalStatus(r))) {
2363
2394
  return failedTerminalAppServerAttempt(r);
@@ -2542,7 +2573,7 @@ var EngineProtocolServer = class {
2542
2573
  }
2543
2574
  }
2544
2575
  }
2545
- /** 10 正向方法分发(表驱动:method → 处理器;未知方法 → engine_protocol_unknown_method)。 */
2576
+ /** 9 正向方法分发(表驱动:method → 处理器;未知方法 → engine_protocol_unknown_method)。 */
2546
2577
  async dispatch(id, method, params) {
2547
2578
  const handler = this.methodHandlers[method];
2548
2579
  if (handler === void 0) {
@@ -2555,7 +2586,7 @@ var EngineProtocolServer = class {
2555
2586
  return handler(params);
2556
2587
  }
2557
2588
  /**
2558
- * 10 正向方法 → 处理器映射(每个处理器消费原始 params 并自行收敛类型——
2589
+ * 9 正向方法 → 处理器映射(每个处理器消费原始 params 并自行收敛类型——
2559
2590
  * 与原 switch case 表达式一一对应)。
2560
2591
  */
2561
2592
  methodHandlers = {
@@ -2563,7 +2594,6 @@ var EngineProtocolServer = class {
2563
2594
  probe: (p) => this.engine.probe(probeParamsOf(p)),
2564
2595
  run: (p) => this.run(p),
2565
2596
  cancel: (p) => this.cancel(p),
2566
- interact: (p) => this.interact(p),
2567
2597
  read: (p) => this.read(p),
2568
2598
  listModels: () => ({ models: this.engine.listModels?.() ?? null }),
2569
2599
  validateModel: (p) => this.validateModel(p),
@@ -2639,9 +2669,6 @@ var EngineProtocolServer = class {
2639
2669
  if (active !== void 0) active.controller.abort(new Error(`cancelled by host: ${params.reason}`));
2640
2670
  return { ok: true };
2641
2671
  }
2642
- async interact(params) {
2643
- return this.engine.interact({ data: params.handle }, params.action);
2644
- }
2645
2672
  read(params) {
2646
2673
  return this.engine.read({ data: params.handle });
2647
2674
  }
package/dist/index.cjs CHANGED
@@ -78,6 +78,9 @@ var ZCODE_APPSERVER_ABORT_GRACE_MS = 3e3;
78
78
  var ZCODE_APPSERVER_ERR_MODEL_CONFIG_MISSING = -32603;
79
79
  var ZCODE_APPSERVER_ERR_BUSY_SESSION = -32010;
80
80
  var ZCODE_APPSERVER_HARVEST_GRACE_MS = 1e3;
81
+ function isFailedTerminalStatus(status) {
82
+ return status === "failed" || status === "error";
83
+ }
81
84
 
82
85
  // src/registration.ts
83
86
  var import_subagent_engine_sdk10 = require("@zhushanwen/subagent-engine-sdk");
@@ -1393,7 +1396,13 @@ var SessionChannel = class {
1393
1396
  });
1394
1397
  const turn = {
1395
1398
  sessionId,
1396
- callbacks: { onTextDelta: opts.onTextDelta, onThinkingDelta: opts.onThinkingDelta },
1399
+ callbacks: {
1400
+ onTextDelta: opts.onTextDelta,
1401
+ onThinkingDelta: opts.onThinkingDelta,
1402
+ // [PR3] tool 执行期活性回调(SessionTurnCallbacks.onActivity 注释——宿主侧
1403
+ // 无进展守护的工具执行期刷新面)
1404
+ onActivity: opts.onActivity
1405
+ },
1397
1406
  settled: false,
1398
1407
  deltas: [],
1399
1408
  finalText: void 0,
@@ -1509,7 +1518,9 @@ var SessionChannel = class {
1509
1518
  if (payload === void 0) return;
1510
1519
  this.refreshIdle(turn);
1511
1520
  if (this.applyFinalFrame(turn, payload)) return;
1512
- this.applyStreamDelta(turn, payload);
1521
+ if (!this.applyStreamDelta(turn, payload) && !turn.settled) {
1522
+ turn.callbacks.onActivity?.();
1523
+ }
1513
1524
  }
1514
1525
  /**
1515
1526
  * 收尾帧判定与吸收(A.2 ⑤ 两种形态:payload.response / stopReason+content 变体)。
@@ -1533,9 +1544,12 @@ var SessionChannel = class {
1533
1544
  /** 增量 delta 分发:非空 delta 入账 + 回调;终态后迟到丢弃(不变量 2)。
1534
1545
  * kind 区分两通道(F2):reasoning_delta 只走 thinking 回调(不入 deltas 账本
1535
1546
  * ——deltas 是 response 兜底聚合,推理文本不是答案正文);text_delta / kind
1536
- * 缺席走 text 路径。 */
1547
+ * 缺席走 text 路径。
1548
+ * 返回值([PR3]):帧被 delta 路径消费(thinking 或 text,回调若注册则已触发)
1549
+ * → true;无 delta / 空 delta / 终态后迟到丢弃 → false(handleSessionEvent 据
1550
+ * 此分流 onActivity——delta 帧已是宿主可见进展,不双发活性信号)。 */
1537
1551
  applyStreamDelta(turn, payload) {
1538
- if (typeof payload.delta !== "string" || payload.delta === "") return;
1552
+ if (typeof payload.delta !== "string" || payload.delta === "") return false;
1539
1553
  if (turn.settled) {
1540
1554
  logger2.warn(
1541
1555
  `\u7EC8\u6001\u540E\u8FDF\u5230\u7684 delta \u4E22\u5F03\uFF08\u4F1A\u8BDD ${turn.sessionId}\uFF0C\u4E0D\u53D8\u91CF 2\uFF1Aresolve \u540E\u4E0D\u518D\u53D1\u4E8B\u4EF6\uFF09: ${payload.delta.slice(
@@ -1543,14 +1557,15 @@ var SessionChannel = class {
1543
1557
  DELTA_LOG_CHARS
1544
1558
  )}`
1545
1559
  );
1546
- return;
1560
+ return false;
1547
1561
  }
1548
1562
  if (payload.kind === "reasoning_delta") {
1549
1563
  turn.callbacks.onThinkingDelta?.(payload.delta);
1550
- return;
1564
+ return true;
1551
1565
  }
1552
1566
  turn.deltas.push(payload.delta);
1553
1567
  turn.callbacks.onTextDelta?.(payload.delta);
1568
+ return true;
1554
1569
  }
1555
1570
  handleTelemetry(params) {
1556
1571
  if (!isRecord3(params)) return;
@@ -1570,13 +1585,38 @@ var SessionChannel = class {
1570
1585
  turn.lastTerminalStatus = status;
1571
1586
  this.recordTerminalError(turn, params);
1572
1587
  if (turn.settled) {
1573
- logger2.warn(
1574
- `\u6743\u5A01\u7EC8\u6001\u665A\u4E8E\u843D\u5B9A\u7ED3\u679C\u5230\u8FBE\uFF08\u4F1A\u8BDD ${turn.sessionId}\uFF0C\u5DF2\u843D\u5B9A source=${turn.terminal?.source}\uFF09\uFF1Aturn.terminal status="${status}" \u4EC5\u8BB0\u5F55\u4E0D\u6539\u5199\uFF08P0-1 D5\u2460\uFF09`
1575
- );
1588
+ this.logLateTerminal(turn, status);
1576
1589
  return;
1577
1590
  }
1578
1591
  turn.settle({ status, source: "turn.terminal" });
1579
1592
  }
1593
+ /**
1594
+ * 已落定 turn 的迟到权威终态分级日志(P-Z2 后续:常态迟到降噪)。真机实证该
1595
+ * 形态的唯一可达路径是 success 终态常态迟到(final-frame 先落定 + 权威
1596
+ * turn.terminal 迟到)——即每个成功任务必经;原无条件 warn 经 SDK cli-entry
1597
+ * 的 stderr 兜底 → runtime [rpc:stderr] 全量 console.error 链路,每个成功任务
1598
+ * 产一条零异常区分度的 ERROR 级日志。按权威 status 分级(落定语义不动——
1599
+ * lastTerminalStatus/lastTerminalError 的入账在上游无条件路径完成):
1600
+ * - "success":常态迟到,零日志;
1601
+ * - "interrupted":用户中断形态,debug(stderr 兜底对 debug no-op——对齐
1602
+ * SDK logger CONSOLE_SINK 语义,host/log 反向请求侧仍可观测);
1603
+ * - 失败类(isFailedTerminalStatus,与引擎失败分流同口径)与 unknown/未识别
1604
+ * status:保守 warn(原样 status 已写入文案)——假成功识破的防御面,理论
1605
+ * 不可达但保留,宁可误报不静默。
1606
+ */
1607
+ logLateTerminal(turn, status) {
1608
+ if (status === "success") return;
1609
+ const message = `\u6743\u5A01\u7EC8\u6001\u665A\u4E8E\u843D\u5B9A\u7ED3\u679C\u5230\u8FBE\uFF08\u4F1A\u8BDD ${turn.sessionId}\uFF0C\u5DF2\u843D\u5B9A source=${turn.terminal?.source}\uFF09\uFF1Aturn.terminal status="${status}" \u4EC5\u8BB0\u5F55\u4E0D\u6539\u5199\uFF08P0-1 D5\u2460\uFF09`;
1610
+ if (isFailedTerminalStatus(status)) {
1611
+ logger2.warn(message);
1612
+ return;
1613
+ }
1614
+ if (status === "interrupted") {
1615
+ logger2.debug(message);
1616
+ return;
1617
+ }
1618
+ logger2.warn(message);
1619
+ }
1580
1620
  /** terminal 帧专属错误信息入账(⛔P-Z2 实证:真实 failed 终态的 errorCode/errorMessage
1581
1621
  * 只在 terminal 帧携带,read/delta 拿不到——先到/迟到都记录,消费层合成失败
1582
1622
  * 文案时优先采信)。二者均缺失时不挂 lastTerminalError 字段。 */
@@ -1862,6 +1902,11 @@ var ZcodeEngine = class {
1862
1902
  // 不变量 3a 的拼接比对不含 reasoning)
1863
1903
  onTextDelta: (delta) => ctx.onEvent?.({ type: "text_delta", delta }),
1864
1904
  onThinkingDelta: (delta) => ctx.onEvent?.({ type: "thinking_delta", delta }),
1905
+ // [PR3] 工具执行期活性:非终态非增量的 session/event 帧(tool.updated progress
1906
+ // 等,真机探针实证每 ~1s 一帧)→ activity 事件(零载荷纯活性信号——宿主无进展
1907
+ // 守护对任何事件类型刷新;reducer no-op、不落 journal)。zcode 引擎
1908
+ // conversation:"unsupported",run 域是唯一发射面(无 chat 域接线)。
1909
+ onActivity: () => ctx.onEvent?.({ type: "activity" }),
1865
1910
  onSessionCreated: (sessionId) => {
1866
1911
  currentSessionId = sessionId;
1867
1912
  rt.activeSessions.add(sessionId);
@@ -2167,17 +2212,6 @@ var ZcodeEngine = class {
2167
2212
  }
2168
2213
  for (const ev of synthesizeCoarseEvents(payload.response, payload.usage)) emit(ev);
2169
2214
  }
2170
- /**
2171
- * D1 可选面:zcode 首期不支持 conversation(capabilities 声明)——同步拒绝、
2172
- * 不创建进程,文案给可操作建议(A11)。
2173
- */
2174
- async interact(_handle, _action) {
2175
- return {
2176
- ok: false,
2177
- code: "engine_capability_unsupported",
2178
- message: "zcode \u5F15\u64CE\u4E0D\u652F\u6301 conversation \u4EA4\u4E92\u63A7\u5236\u9762\uFF08capabilities.conversation = 'unsupported'\uFF0C\u6BCF\u4EFB\u52A1\u81EA\u5305\u542B\u4F1A\u8BDD\uFF0C\u65E0\u540C\u8FDB\u7A0B idle \u590D\u7528\uFF09\u3002\u6062\u590D\u6307\u5F15\uFF1A\u6539\u7528\u5355\u6B21 subagent \u8C03\u7528\u91CD\u65B0\u6D3E\u53D1\u4EFB\u52A1\uFF0C\u6216\u4F7F\u7528 engine: 'pi'\uFF08chatMode idle \u590D\u7528\uFF0C\u652F\u6301 message/close/cancel\uFF09\u3002"
2179
- };
2180
- }
2181
2215
  /** [U7] 模型可发现性:v2 桌面登录态聚合(带凭据 provider × models),失败安全返回清单本身可能为空。 */
2182
2216
  listModels() {
2183
2217
  return listZcodeModels(this.deps.sources);
@@ -2377,9 +2411,6 @@ function authoritativeTerminalStatus(r) {
2377
2411
  if (r.lastTerminalStatus !== void 0) return r.lastTerminalStatus;
2378
2412
  return r.terminal.source === "turn.terminal" ? r.terminal.status : void 0;
2379
2413
  }
2380
- function isFailedTerminalStatus(status) {
2381
- return status === "failed" || status === "error";
2382
- }
2383
2414
  function parsedAppServerAttempt(task, r) {
2384
2415
  if (isFailedTerminalStatus(authoritativeTerminalStatus(r))) {
2385
2416
  return failedTerminalAppServerAttempt(r);
@@ -2555,7 +2586,7 @@ var EngineProtocolServer = class {
2555
2586
  }
2556
2587
  }
2557
2588
  }
2558
- /** 10 正向方法分发(表驱动:method → 处理器;未知方法 → engine_protocol_unknown_method)。 */
2589
+ /** 9 正向方法分发(表驱动:method → 处理器;未知方法 → engine_protocol_unknown_method)。 */
2559
2590
  async dispatch(id, method, params) {
2560
2591
  const handler = this.methodHandlers[method];
2561
2592
  if (handler === void 0) {
@@ -2568,7 +2599,7 @@ var EngineProtocolServer = class {
2568
2599
  return handler(params);
2569
2600
  }
2570
2601
  /**
2571
- * 10 正向方法 → 处理器映射(每个处理器消费原始 params 并自行收敛类型——
2602
+ * 9 正向方法 → 处理器映射(每个处理器消费原始 params 并自行收敛类型——
2572
2603
  * 与原 switch case 表达式一一对应)。
2573
2604
  */
2574
2605
  methodHandlers = {
@@ -2576,7 +2607,6 @@ var EngineProtocolServer = class {
2576
2607
  probe: (p) => this.engine.probe(probeParamsOf(p)),
2577
2608
  run: (p) => this.run(p),
2578
2609
  cancel: (p) => this.cancel(p),
2579
- interact: (p) => this.interact(p),
2580
2610
  read: (p) => this.read(p),
2581
2611
  listModels: () => ({ models: this.engine.listModels?.() ?? null }),
2582
2612
  validateModel: (p) => this.validateModel(p),
@@ -2652,9 +2682,6 @@ var EngineProtocolServer = class {
2652
2682
  if (active !== void 0) active.controller.abort(new Error(`cancelled by host: ${params.reason}`));
2653
2683
  return { ok: true };
2654
2684
  }
2655
- async interact(params) {
2656
- return this.engine.interact({ data: params.handle }, params.action);
2657
- }
2658
2685
  read(params) {
2659
2686
  return this.engine.read({ data: params.handle });
2660
2687
  }
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { EnginePort, ReverseRequestClock, EngineCapabilities, ProbeReport, EngineAgentCallOpts, RunContext, EngineRunResult, EngineHandle, InteractAction, InteractResult, SessionView } from '@zhushanwen/subagent-engine-sdk';
1
+ import { EnginePort, ReverseRequestClock, EngineCapabilities, ProbeReport, EngineAgentCallOpts, RunContext, EngineRunResult, EngineHandle, SessionView } from '@zhushanwen/subagent-engine-sdk';
2
2
 
3
3
  /** 出站帧写入面(main.ts 注入 process.stdout;测试注入内存缓冲)。 */
4
4
  type FrameWriter = (frame: unknown) => void;
@@ -30,17 +30,16 @@ declare class EngineProtocolServer {
30
30
  constructor(opts: EngineProtocolServerOptions);
31
31
  /** 入站帧消费(请求帧 + 反向请求应答帧;main.ts 的行解析器拆行后喂入)。 */
32
32
  handleFrame(frame: unknown): void;
33
- /** 10 正向方法分发(表驱动:method → 处理器;未知方法 → engine_protocol_unknown_method)。 */
33
+ /** 9 正向方法分发(表驱动:method → 处理器;未知方法 → engine_protocol_unknown_method)。 */
34
34
  private dispatch;
35
35
  /**
36
- * 10 正向方法 → 处理器映射(每个处理器消费原始 params 并自行收敛类型——
36
+ * 9 正向方法 → 处理器映射(每个处理器消费原始 params 并自行收敛类型——
37
37
  * 与原 switch case 表达式一一对应)。
38
38
  */
39
39
  private readonly methodHandlers;
40
40
  private initialize;
41
41
  private run;
42
42
  private cancel;
43
- private interact;
44
43
  private read;
45
44
  private validateModel;
46
45
  private emitEvent;
@@ -237,11 +236,6 @@ declare class ZcodeEngine implements EnginePort {
237
236
  private applyRunFailedOutcome;
238
237
  /** parsed 合成终态:content/sessionId/usage 落位 + schema 校验分流 + coarse 事件。 */
239
238
  private applyParsedOutcome;
240
- /**
241
- * D1 可选面:zcode 首期不支持 conversation(capabilities 声明)——同步拒绝、
242
- * 不创建进程,文案给可操作建议(A11)。
243
- */
244
- interact(_handle: EngineHandle, _action: InteractAction): Promise<InteractResult>;
245
239
  /** [U7] 模型可发现性:v2 桌面登录态聚合(带凭据 provider × models),失败安全返回清单本身可能为空。 */
246
240
  listModels(): Array<{
247
241
  id: string;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { EnginePort, ReverseRequestClock, EngineCapabilities, ProbeReport, EngineAgentCallOpts, RunContext, EngineRunResult, EngineHandle, InteractAction, InteractResult, SessionView } from '@zhushanwen/subagent-engine-sdk';
1
+ import { EnginePort, ReverseRequestClock, EngineCapabilities, ProbeReport, EngineAgentCallOpts, RunContext, EngineRunResult, EngineHandle, SessionView } from '@zhushanwen/subagent-engine-sdk';
2
2
 
3
3
  /** 出站帧写入面(main.ts 注入 process.stdout;测试注入内存缓冲)。 */
4
4
  type FrameWriter = (frame: unknown) => void;
@@ -30,17 +30,16 @@ declare class EngineProtocolServer {
30
30
  constructor(opts: EngineProtocolServerOptions);
31
31
  /** 入站帧消费(请求帧 + 反向请求应答帧;main.ts 的行解析器拆行后喂入)。 */
32
32
  handleFrame(frame: unknown): void;
33
- /** 10 正向方法分发(表驱动:method → 处理器;未知方法 → engine_protocol_unknown_method)。 */
33
+ /** 9 正向方法分发(表驱动:method → 处理器;未知方法 → engine_protocol_unknown_method)。 */
34
34
  private dispatch;
35
35
  /**
36
- * 10 正向方法 → 处理器映射(每个处理器消费原始 params 并自行收敛类型——
36
+ * 9 正向方法 → 处理器映射(每个处理器消费原始 params 并自行收敛类型——
37
37
  * 与原 switch case 表达式一一对应)。
38
38
  */
39
39
  private readonly methodHandlers;
40
40
  private initialize;
41
41
  private run;
42
42
  private cancel;
43
- private interact;
44
43
  private read;
45
44
  private validateModel;
46
45
  private emitEvent;
@@ -237,11 +236,6 @@ declare class ZcodeEngine implements EnginePort {
237
236
  private applyRunFailedOutcome;
238
237
  /** parsed 合成终态:content/sessionId/usage 落位 + schema 校验分流 + coarse 事件。 */
239
238
  private applyParsedOutcome;
240
- /**
241
- * D1 可选面:zcode 首期不支持 conversation(capabilities 声明)——同步拒绝、
242
- * 不创建进程,文案给可操作建议(A11)。
243
- */
244
- interact(_handle: EngineHandle, _action: InteractAction): Promise<InteractResult>;
245
239
  /** [U7] 模型可发现性:v2 桌面登录态聚合(带凭据 provider × models),失败安全返回清单本身可能为空。 */
246
240
  listModels(): Array<{
247
241
  id: string;
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  readZcodeSessionView,
11
11
  zcodeDbPathAllowlist,
12
12
  zcodeSessionDbPath
13
- } from "./chunk-36VY2TNC.js";
13
+ } from "./chunk-WQ7NVGKB.js";
14
14
 
15
15
  // src/golden-sample.ts
16
16
  var ZCODE_APPSERVER_GOLDEN = {
package/dist/main.cjs CHANGED
@@ -58,6 +58,9 @@ var ZCODE_APPSERVER_ABORT_GRACE_MS = 3e3;
58
58
  var ZCODE_APPSERVER_ERR_MODEL_CONFIG_MISSING = -32603;
59
59
  var ZCODE_APPSERVER_ERR_BUSY_SESSION = -32010;
60
60
  var ZCODE_APPSERVER_HARVEST_GRACE_MS = 1e3;
61
+ function isFailedTerminalStatus(status) {
62
+ return status === "failed" || status === "error";
63
+ }
61
64
 
62
65
  // src/registration.ts
63
66
  var import_subagent_engine_sdk10 = require("@zhushanwen/subagent-engine-sdk");
@@ -1373,7 +1376,13 @@ var SessionChannel = class {
1373
1376
  });
1374
1377
  const turn = {
1375
1378
  sessionId,
1376
- callbacks: { onTextDelta: opts.onTextDelta, onThinkingDelta: opts.onThinkingDelta },
1379
+ callbacks: {
1380
+ onTextDelta: opts.onTextDelta,
1381
+ onThinkingDelta: opts.onThinkingDelta,
1382
+ // [PR3] tool 执行期活性回调(SessionTurnCallbacks.onActivity 注释——宿主侧
1383
+ // 无进展守护的工具执行期刷新面)
1384
+ onActivity: opts.onActivity
1385
+ },
1377
1386
  settled: false,
1378
1387
  deltas: [],
1379
1388
  finalText: void 0,
@@ -1489,7 +1498,9 @@ var SessionChannel = class {
1489
1498
  if (payload === void 0) return;
1490
1499
  this.refreshIdle(turn);
1491
1500
  if (this.applyFinalFrame(turn, payload)) return;
1492
- this.applyStreamDelta(turn, payload);
1501
+ if (!this.applyStreamDelta(turn, payload) && !turn.settled) {
1502
+ turn.callbacks.onActivity?.();
1503
+ }
1493
1504
  }
1494
1505
  /**
1495
1506
  * 收尾帧判定与吸收(A.2 ⑤ 两种形态:payload.response / stopReason+content 变体)。
@@ -1513,9 +1524,12 @@ var SessionChannel = class {
1513
1524
  /** 增量 delta 分发:非空 delta 入账 + 回调;终态后迟到丢弃(不变量 2)。
1514
1525
  * kind 区分两通道(F2):reasoning_delta 只走 thinking 回调(不入 deltas 账本
1515
1526
  * ——deltas 是 response 兜底聚合,推理文本不是答案正文);text_delta / kind
1516
- * 缺席走 text 路径。 */
1527
+ * 缺席走 text 路径。
1528
+ * 返回值([PR3]):帧被 delta 路径消费(thinking 或 text,回调若注册则已触发)
1529
+ * → true;无 delta / 空 delta / 终态后迟到丢弃 → false(handleSessionEvent 据
1530
+ * 此分流 onActivity——delta 帧已是宿主可见进展,不双发活性信号)。 */
1517
1531
  applyStreamDelta(turn, payload) {
1518
- if (typeof payload.delta !== "string" || payload.delta === "") return;
1532
+ if (typeof payload.delta !== "string" || payload.delta === "") return false;
1519
1533
  if (turn.settled) {
1520
1534
  logger2.warn(
1521
1535
  `\u7EC8\u6001\u540E\u8FDF\u5230\u7684 delta \u4E22\u5F03\uFF08\u4F1A\u8BDD ${turn.sessionId}\uFF0C\u4E0D\u53D8\u91CF 2\uFF1Aresolve \u540E\u4E0D\u518D\u53D1\u4E8B\u4EF6\uFF09: ${payload.delta.slice(
@@ -1523,14 +1537,15 @@ var SessionChannel = class {
1523
1537
  DELTA_LOG_CHARS
1524
1538
  )}`
1525
1539
  );
1526
- return;
1540
+ return false;
1527
1541
  }
1528
1542
  if (payload.kind === "reasoning_delta") {
1529
1543
  turn.callbacks.onThinkingDelta?.(payload.delta);
1530
- return;
1544
+ return true;
1531
1545
  }
1532
1546
  turn.deltas.push(payload.delta);
1533
1547
  turn.callbacks.onTextDelta?.(payload.delta);
1548
+ return true;
1534
1549
  }
1535
1550
  handleTelemetry(params) {
1536
1551
  if (!isRecord3(params)) return;
@@ -1550,13 +1565,38 @@ var SessionChannel = class {
1550
1565
  turn.lastTerminalStatus = status;
1551
1566
  this.recordTerminalError(turn, params);
1552
1567
  if (turn.settled) {
1553
- logger2.warn(
1554
- `\u6743\u5A01\u7EC8\u6001\u665A\u4E8E\u843D\u5B9A\u7ED3\u679C\u5230\u8FBE\uFF08\u4F1A\u8BDD ${turn.sessionId}\uFF0C\u5DF2\u843D\u5B9A source=${turn.terminal?.source}\uFF09\uFF1Aturn.terminal status="${status}" \u4EC5\u8BB0\u5F55\u4E0D\u6539\u5199\uFF08P0-1 D5\u2460\uFF09`
1555
- );
1568
+ this.logLateTerminal(turn, status);
1556
1569
  return;
1557
1570
  }
1558
1571
  turn.settle({ status, source: "turn.terminal" });
1559
1572
  }
1573
+ /**
1574
+ * 已落定 turn 的迟到权威终态分级日志(P-Z2 后续:常态迟到降噪)。真机实证该
1575
+ * 形态的唯一可达路径是 success 终态常态迟到(final-frame 先落定 + 权威
1576
+ * turn.terminal 迟到)——即每个成功任务必经;原无条件 warn 经 SDK cli-entry
1577
+ * 的 stderr 兜底 → runtime [rpc:stderr] 全量 console.error 链路,每个成功任务
1578
+ * 产一条零异常区分度的 ERROR 级日志。按权威 status 分级(落定语义不动——
1579
+ * lastTerminalStatus/lastTerminalError 的入账在上游无条件路径完成):
1580
+ * - "success":常态迟到,零日志;
1581
+ * - "interrupted":用户中断形态,debug(stderr 兜底对 debug no-op——对齐
1582
+ * SDK logger CONSOLE_SINK 语义,host/log 反向请求侧仍可观测);
1583
+ * - 失败类(isFailedTerminalStatus,与引擎失败分流同口径)与 unknown/未识别
1584
+ * status:保守 warn(原样 status 已写入文案)——假成功识破的防御面,理论
1585
+ * 不可达但保留,宁可误报不静默。
1586
+ */
1587
+ logLateTerminal(turn, status) {
1588
+ if (status === "success") return;
1589
+ const message = `\u6743\u5A01\u7EC8\u6001\u665A\u4E8E\u843D\u5B9A\u7ED3\u679C\u5230\u8FBE\uFF08\u4F1A\u8BDD ${turn.sessionId}\uFF0C\u5DF2\u843D\u5B9A source=${turn.terminal?.source}\uFF09\uFF1Aturn.terminal status="${status}" \u4EC5\u8BB0\u5F55\u4E0D\u6539\u5199\uFF08P0-1 D5\u2460\uFF09`;
1590
+ if (isFailedTerminalStatus(status)) {
1591
+ logger2.warn(message);
1592
+ return;
1593
+ }
1594
+ if (status === "interrupted") {
1595
+ logger2.debug(message);
1596
+ return;
1597
+ }
1598
+ logger2.warn(message);
1599
+ }
1560
1600
  /** terminal 帧专属错误信息入账(⛔P-Z2 实证:真实 failed 终态的 errorCode/errorMessage
1561
1601
  * 只在 terminal 帧携带,read/delta 拿不到——先到/迟到都记录,消费层合成失败
1562
1602
  * 文案时优先采信)。二者均缺失时不挂 lastTerminalError 字段。 */
@@ -1842,6 +1882,11 @@ var ZcodeEngine = class {
1842
1882
  // 不变量 3a 的拼接比对不含 reasoning)
1843
1883
  onTextDelta: (delta) => ctx.onEvent?.({ type: "text_delta", delta }),
1844
1884
  onThinkingDelta: (delta) => ctx.onEvent?.({ type: "thinking_delta", delta }),
1885
+ // [PR3] 工具执行期活性:非终态非增量的 session/event 帧(tool.updated progress
1886
+ // 等,真机探针实证每 ~1s 一帧)→ activity 事件(零载荷纯活性信号——宿主无进展
1887
+ // 守护对任何事件类型刷新;reducer no-op、不落 journal)。zcode 引擎
1888
+ // conversation:"unsupported",run 域是唯一发射面(无 chat 域接线)。
1889
+ onActivity: () => ctx.onEvent?.({ type: "activity" }),
1845
1890
  onSessionCreated: (sessionId) => {
1846
1891
  currentSessionId = sessionId;
1847
1892
  rt.activeSessions.add(sessionId);
@@ -2147,17 +2192,6 @@ var ZcodeEngine = class {
2147
2192
  }
2148
2193
  for (const ev of synthesizeCoarseEvents(payload.response, payload.usage)) emit(ev);
2149
2194
  }
2150
- /**
2151
- * D1 可选面:zcode 首期不支持 conversation(capabilities 声明)——同步拒绝、
2152
- * 不创建进程,文案给可操作建议(A11)。
2153
- */
2154
- async interact(_handle, _action) {
2155
- return {
2156
- ok: false,
2157
- code: "engine_capability_unsupported",
2158
- message: "zcode \u5F15\u64CE\u4E0D\u652F\u6301 conversation \u4EA4\u4E92\u63A7\u5236\u9762\uFF08capabilities.conversation = 'unsupported'\uFF0C\u6BCF\u4EFB\u52A1\u81EA\u5305\u542B\u4F1A\u8BDD\uFF0C\u65E0\u540C\u8FDB\u7A0B idle \u590D\u7528\uFF09\u3002\u6062\u590D\u6307\u5F15\uFF1A\u6539\u7528\u5355\u6B21 subagent \u8C03\u7528\u91CD\u65B0\u6D3E\u53D1\u4EFB\u52A1\uFF0C\u6216\u4F7F\u7528 engine: 'pi'\uFF08chatMode idle \u590D\u7528\uFF0C\u652F\u6301 message/close/cancel\uFF09\u3002"
2159
- };
2160
- }
2161
2195
  /** [U7] 模型可发现性:v2 桌面登录态聚合(带凭据 provider × models),失败安全返回清单本身可能为空。 */
2162
2196
  listModels() {
2163
2197
  return listZcodeModels(this.deps.sources);
@@ -2357,9 +2391,6 @@ function authoritativeTerminalStatus(r) {
2357
2391
  if (r.lastTerminalStatus !== void 0) return r.lastTerminalStatus;
2358
2392
  return r.terminal.source === "turn.terminal" ? r.terminal.status : void 0;
2359
2393
  }
2360
- function isFailedTerminalStatus(status) {
2361
- return status === "failed" || status === "error";
2362
- }
2363
2394
  function parsedAppServerAttempt(task, r) {
2364
2395
  if (isFailedTerminalStatus(authoritativeTerminalStatus(r))) {
2365
2396
  return failedTerminalAppServerAttempt(r);
@@ -2535,7 +2566,7 @@ var EngineProtocolServer = class {
2535
2566
  }
2536
2567
  }
2537
2568
  }
2538
- /** 10 正向方法分发(表驱动:method → 处理器;未知方法 → engine_protocol_unknown_method)。 */
2569
+ /** 9 正向方法分发(表驱动:method → 处理器;未知方法 → engine_protocol_unknown_method)。 */
2539
2570
  async dispatch(id, method, params) {
2540
2571
  const handler = this.methodHandlers[method];
2541
2572
  if (handler === void 0) {
@@ -2548,7 +2579,7 @@ var EngineProtocolServer = class {
2548
2579
  return handler(params);
2549
2580
  }
2550
2581
  /**
2551
- * 10 正向方法 → 处理器映射(每个处理器消费原始 params 并自行收敛类型——
2582
+ * 9 正向方法 → 处理器映射(每个处理器消费原始 params 并自行收敛类型——
2552
2583
  * 与原 switch case 表达式一一对应)。
2553
2584
  */
2554
2585
  methodHandlers = {
@@ -2556,7 +2587,6 @@ var EngineProtocolServer = class {
2556
2587
  probe: (p) => this.engine.probe(probeParamsOf(p)),
2557
2588
  run: (p) => this.run(p),
2558
2589
  cancel: (p) => this.cancel(p),
2559
- interact: (p) => this.interact(p),
2560
2590
  read: (p) => this.read(p),
2561
2591
  listModels: () => ({ models: this.engine.listModels?.() ?? null }),
2562
2592
  validateModel: (p) => this.validateModel(p),
@@ -2632,9 +2662,6 @@ var EngineProtocolServer = class {
2632
2662
  if (active !== void 0) active.controller.abort(new Error(`cancelled by host: ${params.reason}`));
2633
2663
  return { ok: true };
2634
2664
  }
2635
- async interact(params) {
2636
- return this.engine.interact({ data: params.handle }, params.action);
2637
- }
2638
2665
  read(params) {
2639
2666
  return this.engine.read({ data: params.handle });
2640
2667
  }
package/dist/main.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  EngineProtocolServer
3
- } from "./chunk-36VY2TNC.js";
3
+ } from "./chunk-WQ7NVGKB.js";
4
4
 
5
5
  // src/main.ts
6
6
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhushanwen/zcode-subagent-cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "zcode subagent engine as an independent CLI package (W5 of subagent-engine-protocolization): speaks engine-protocol v1 over NDJSON stdio, depends only on @zhushanwen/subagent-engine-sdk (never subagent-core)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -61,7 +61,7 @@
61
61
  "dist/"
62
62
  ],
63
63
  "dependencies": {
64
- "@zhushanwen/subagent-engine-sdk": "0.2.1"
64
+ "@zhushanwen/subagent-engine-sdk": "0.3.0"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@types/node": "^24.0.0",