@zhushanwen/zcode-subagent-cli 0.4.3 → 0.5.0

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.
@@ -11,16 +11,6 @@ var ZCODE_APPSERVER_STDERR_TAIL_CHARS = 400;
11
11
  var ZCODE_APPSERVER_STDERR_TAIL_BUFFER_CHARS = 2048;
12
12
  var ZCODE_APPSERVER_TURN_READ_TIMEOUT_MS = 5e3;
13
13
  var ZCODE_APPSERVER_TURN_CLOSE_TIMEOUT_MS = 1500;
14
- var ZCODE_TURN_IDLE_TIMEOUT_ENV = "TAIJI_ZCODE_TURN_IDLE_TIMEOUT_MS";
15
- var ZCODE_TURN_MAX_TIMEOUT_ENV = "TAIJI_ZCODE_TURN_MAX_TIMEOUT_MS";
16
- var ZCODE_TURN_IDLE_TIMEOUT_MS = 18e5;
17
- var ZCODE_TURN_MAX_TIMEOUT_MS = 36e5;
18
- function parseZcodeTurnTimeoutEnv(raw) {
19
- if (raw === void 0 || raw === "") return { state: "unset" };
20
- const parsed = Number(raw);
21
- if (!Number.isFinite(parsed)) return { state: "invalid" };
22
- return { state: "valid", ms: parsed };
23
- }
24
14
  var ZCODE_APPSERVER_STOP_TIMEOUT_MS = 3e3;
25
15
  var ZCODE_APPSERVER_ABORT_GRACE_MS = 3e3;
26
16
  var ZCODE_APPSERVER_STOP_TIMEOUT_ENV = "TAIJI_ZCODE_TEST_STOP_TIMEOUT_MS";
@@ -328,9 +318,9 @@ import {
328
318
  sessionIdFromHandle
329
319
  } from "@zhushanwen/subagent-engine-sdk";
330
320
  function replayJournalToSessionView(handle, engineId) {
331
- const journalPath = handle.data.journalPath;
332
- if (journalPath === void 0) return void 0;
333
- const events = replayJournal2(journalPath);
321
+ const eventsPath = handle.data.eventsPath;
322
+ if (eventsPath === void 0) return void 0;
323
+ const events = replayJournal2(eventsPath);
334
324
  if (events.length === 0) return void 0;
335
325
  return eventsToSessionView(events, engineId, sessionIdFromHandle(handle.data));
336
326
  }
@@ -1378,66 +1368,6 @@ function extractResumeTotalTokens(resumeResult) {
1378
1368
  }
1379
1369
  return acc.has ? acc.total : void 0;
1380
1370
  }
1381
- var TurnTimeoutError = class extends Error {
1382
- kind;
1383
- elapsed;
1384
- lastEventAt;
1385
- thresholdMs;
1386
- constructor(kind, parts) {
1387
- super(
1388
- kind === "idle" ? `zcode turn idle \u5224\u6B7B\uFF1A\u8FDE\u7EED ${parts.thresholdMs}ms \u672A\u89C2\u5BDF\u5230\u672C turn \u4EFB\u4F55\u4E8B\u4EF6\uFF08session/event \u4E0E v4/telemetry/event \u5747\u9759\u9ED8\uFF09\uFF0C\u7EC8\u6001\u672A\u5230\u8FBE\u3002` + (parts.lastEventAt === void 0 ? "\u672C\u8F6E\u81EA send \u8D77\u672A\u89C2\u5BDF\u5230\u4EFB\u4F55\u4E8B\u4EF6\uFF08\u8FDB\u7A0B\u5047\u6B7B/\u534F\u8BAE\u9759\u9ED8\u5F62\u6001\uFF09\u3002" : `\u6700\u540E\u4E8B\u4EF6\u65F6\u523B ${new Date(parts.lastEventAt).toISOString()}\u3002`) + "\u6062\u590D\u6307\u5F15\uFF1A\u76F4\u63A5\u91CD\u8DD1\u672C\u4EFB\u52A1\uFF1B\u82E5\u6301\u7EED\u51FA\u73B0\uFF0C\u68C0\u67E5 ZCode \u684C\u9762\u7AEF\u6A21\u578B\u8FDE\u901A\u6027\u6216\u6539\u7528 engine: pi\u3002" : `zcode turn \u603B\u4E0A\u754C\u5224\u6B7B\uFF1A${parts.thresholdMs}ms \u5185\u672A\u89C2\u5BDF\u5230\u7EC8\u6001\uFF08turn.terminal \u4E0E\u6536\u5C3E\u5E27\u5747\u672A\u5230\u8FBE\uFF1Bidle \u5224\u5B9A\u672A\u89E6\u53D1\u2014\u2014\u4E8B\u4EF6\u6D41\u4ECD\u6D3B\u8DC3\uFF0Cchatty-wedge \u5F62\u6001\uFF09\u3002` + (parts.lastEventAt === void 0 ? "" : `\u6700\u540E\u4E8B\u4EF6\u65F6\u523B ${new Date(parts.lastEventAt).toISOString()}\u3002`) + `\u6062\u590D\u6307\u5F15\uFF1A\u76F4\u63A5\u91CD\u8DD1\u672C\u4EFB\u52A1\uFF1B\u82E5\u672C\u4EFB\u52A1\u5C5E\u5408\u6CD5\u8D85\u957F\u4EFB\u52A1\uFF08\u9884\u671F\u8D85\u8FC7 ${parts.thresholdMs}ms \u603B\u4E0A\u754C\uFF09\uFF0C\u91CD\u8DD1\u524D\u8BBE ${ZCODE_TURN_MAX_TIMEOUT_ENV} \u4E3A\u66F4\u5927\u6BEB\u79D2\u503C\uFF0C\u6216\u8BBE 0 \u5173\u95ED\u603B\u4E0A\u754C\uFF08\u5173\u95ED\u540E chatty \u5F62\u6001\u4E0D\u518D\u81EA\u52A8\u56DE\u6536\uFF0C\u9759\u9ED8 wedged \u4ECD\u7531 idle \u5C42\u515C\u5E95\u2014\u2014\u81EA\u884C\u6743\u8861\uFF09\u3002`
1389
- );
1390
- this.name = "TurnTimeoutError";
1391
- this.kind = kind;
1392
- this.elapsed = parts.elapsed;
1393
- this.lastEventAt = parts.lastEventAt;
1394
- this.thresholdMs = parts.thresholdMs;
1395
- }
1396
- };
1397
- function clearTurnTimers(turn) {
1398
- if (turn.idleTimer !== void 0) {
1399
- clearTimeout(turn.idleTimer);
1400
- turn.idleTimer = void 0;
1401
- }
1402
- if (turn.ceilingTimer !== void 0) {
1403
- clearTimeout(turn.ceilingTimer);
1404
- turn.ceilingTimer = void 0;
1405
- }
1406
- }
1407
- function armTurnTimer(onFire, ms) {
1408
- const timer = setTimeout(onFire, ms);
1409
- if (typeof timer.unref === "function") timer.unref();
1410
- return timer;
1411
- }
1412
- function resolveTurnTimerMs(parts) {
1413
- let value;
1414
- let source;
1415
- if (parts.explicit !== void 0) {
1416
- value = parts.explicit;
1417
- source = "\u663E\u5F0F\u4F20\u53C2";
1418
- } else {
1419
- const raw = process.env[parts.envName];
1420
- const parsed = parseZcodeTurnTimeoutEnv(raw);
1421
- if (parsed.state === "valid") {
1422
- value = parsed.ms;
1423
- source = `env ${parts.envName}=${raw}`;
1424
- } else {
1425
- if (parsed.state === "invalid") {
1426
- logger2.warn(
1427
- `[session-channel] ${parts.envName}="${raw}" \u975E\u6CD5\uFF08\u5E94\u4E3A\u6BEB\u79D2\u6570\u5B57\uFF09\u2014\u2014\u56DE\u843D\u9ED8\u8BA4 ${parts.fallbackMs}ms\uFF08${parts.label}\uFF09\u3002\u8BBE\u7F6E\u6B63\u6BEB\u79D2\u503C\u8986\u76D6\uFF0C\u6216 0 \u663E\u5F0F\u5173\u95ED`
1428
- );
1429
- }
1430
- value = parts.fallbackMs;
1431
- source = "\u9ED8\u8BA4\u503C";
1432
- }
1433
- }
1434
- if (value <= 0) {
1435
- logger2.warn(
1436
- `[session-channel] zcode turn ${parts.label}\u5DF2\u5173\u95ED\uFF08${source}\uFF09\u2014\u2014${parts.offConsequence}\u3002\u8BBE\u6B63\u6BEB\u79D2\u503C\uFF08env ${parts.envName} \u6216\u663E\u5F0F\u4F20\u53C2\uFF09\u6062\u590D\u56DE\u6536\u5C42`
1437
- );
1438
- }
1439
- return value;
1440
- }
1441
1371
  var SessionChannel = class {
1442
1372
  conn;
1443
1373
  /** sessionId → 在途 turn(终态落定后仍保留到 runTurn 收尾——收尾帧数据吸收窗口)。 */
@@ -1463,23 +1393,23 @@ var SessionChannel = class {
1463
1393
  ),
1464
1394
  // 连接崩溃收割:进程死亡(崩溃/我方杀链)立即 fail 全部在途 turn——
1465
1395
  // onClose 契约保证触发时连接层在途 request 已全部 reject,此处补齐
1466
- // 「无在途 request 的 turn」的收割(否则挂到 turn idle/总上界预算耗尽)
1396
+ // 「无在途 request 的 turn」的收割(否则在途 turn 失去全部终局来源)
1467
1397
  conn.onClose((reason) => this.failAllTurns(`app-server ${reason}`))
1468
1398
  ];
1469
1399
  }
1470
1400
  /**
1471
1401
  * [P0-1 U5/D7] dispose 前收割 + 退订:「退订 = 不会再有事件」在结构上蕴含「在途
1472
1402
  * turn 不应再等」——close 缺失/被吞没形态(onClose 收割主路径未触发)下,在途
1473
- * turn 于退订前经既有 failAllTurns 收敛为明确失败,不再挂满自身 idle/总上界预算
1474
- * (设计 §3.4 退化路径闭合;race 窗口在引擎 shutdownRuntimeAndDisposeChannel,
1475
- * 量级与 awaitConnFinalized 同源 ZCODE_APPSERVER_HARVEST_GRACE_MS)。幂等:与
1476
- * onClose 收割先到者赢(turn.fail 的 settled 守卫——已落定 turn 不改写),
1477
- * activeTurns 已空则 no-op。不影响连接自身生命周期(R4)。
1403
+ * turn 于退订前经既有 failAllTurns 收敛为明确失败(设计 §3.4 退化路径闭合;
1404
+ * race 窗口在引擎 shutdownRuntimeAndDisposeChannel,量级与 awaitConnFinalized
1405
+ * 同源 ZCODE_APPSERVER_HARVEST_GRACE_MS)。幂等:与 onClose 收割先到者赢
1406
+ * (turn.fail 的 settled 守卫——已落定 turn 不改写),activeTurns 已空则 no-op。
1407
+ * 不影响连接自身生命周期(R4)。
1478
1408
  */
1479
1409
  dispose() {
1480
1410
  this.disposed = true;
1481
1411
  this.failAllTurns(
1482
- "zcode session channel \u5DF2 dispose\uFF1A\u8FDE\u63A5 close \u4E8B\u4EF6\u672A\u5728\u6536\u5272\u7A97\u53E3\u5185\u5230\u8FBE\uFF0C\u5728\u9014 turn \u4E8E\u9000\u8BA2\u524D\u6536\u5272\u4E3A\u660E\u786E\u5931\u8D25\uFF08\u4E0D\u518D\u6302\u5230 turn \u81EA\u8EAB\u9884\u7B97\u8017\u5C3D\uFF09\u3002\u6062\u590D\u6307\u5F15\uFF1A\u76F4\u63A5\u91CD\u8DD1\u672C\u4EFB\u52A1\u3002"
1412
+ "zcode session channel \u5DF2 dispose\uFF1A\u8FDE\u63A5 close \u4E8B\u4EF6\u672A\u5728\u6536\u5272\u7A97\u53E3\u5185\u5230\u8FBE\uFF0C\u5728\u9014 turn \u4E8E\u9000\u8BA2\u524D\u6536\u5272\u4E3A\u660E\u786E\u5931\u8D25\u3002\u6062\u590D\u6307\u5F15\uFF1A\u76F4\u63A5\u91CD\u8DD1\u672C\u4EFB\u52A1\u3002"
1483
1413
  );
1484
1414
  for (const off of this.offHandlers.splice(0)) off();
1485
1415
  }
@@ -1609,7 +1539,6 @@ var SessionChannel = class {
1609
1539
  ...opened.turn.lastTerminalError !== void 0 ? { lastTerminalError: opened.turn.lastTerminalError } : {}
1610
1540
  };
1611
1541
  } finally {
1612
- opened.stopTimers();
1613
1542
  this.activeTurns.delete(sessionId);
1614
1543
  await this.closeSession(sessionId);
1615
1544
  }
@@ -1630,7 +1559,7 @@ var SessionChannel = class {
1630
1559
  onTextDelta: opts.onTextDelta,
1631
1560
  onThinkingDelta: opts.onThinkingDelta,
1632
1561
  // [PR3] tool 执行期活性回调(SessionTurnCallbacks.onActivity 注释——宿主侧
1633
- // 无进展守护的工具执行期刷新面)
1562
+ // 的工具执行期刷新面)
1634
1563
  onActivity: opts.onActivity
1635
1564
  },
1636
1565
  settled: false,
@@ -1640,83 +1569,25 @@ var SessionChannel = class {
1640
1569
  terminal: void 0,
1641
1570
  lastTerminalStatus: void 0,
1642
1571
  lastTerminalError: void 0,
1643
- idleMs: 0,
1644
- ceilingMs: 0,
1645
- startedAt: Date.now(),
1646
- lastEventAt: void 0,
1647
- idleTimer: void 0,
1648
- ceilingTimer: void 0,
1649
- // 装配占位:下方 timer 创建后重绑(settle/fail 需要清理两 timer)
1572
+ // 装配占位:下方重绑(settle/fail 闭包引用 turn 本体)
1650
1573
  settle: () => {
1651
1574
  },
1652
1575
  fail: () => {
1653
- },
1654
- fireTimeout: () => {
1655
1576
  }
1656
1577
  };
1657
- turn.idleMs = resolveTurnTimerMs({
1658
- explicit: opts.idleTimeoutMs,
1659
- envName: ZCODE_TURN_IDLE_TIMEOUT_ENV,
1660
- fallbackMs: ZCODE_TURN_IDLE_TIMEOUT_MS,
1661
- label: "idle \u4E3B\u5224\u5B9A",
1662
- offConsequence: "\u9759\u9ED8 wedged\uFF08\u65E0\u4E8B\u4EF6\uFF09\u5F62\u6001\u5C06\u65E0\u81EA\u52A8\u56DE\u6536\uFF0C\u4EFB\u52A1\u53EF\u80FD\u6302\u5230\u5BBF\u4E3B\u8FDB\u7A0B\u9000\u51FA"
1663
- });
1664
- turn.ceilingMs = resolveTurnTimerMs({
1665
- explicit: opts.turnTimeoutMs,
1666
- envName: ZCODE_TURN_MAX_TIMEOUT_ENV,
1667
- fallbackMs: ZCODE_TURN_MAX_TIMEOUT_MS,
1668
- label: "\u603B\u4E0A\u754C",
1669
- offConsequence: "chatty-wedge\uFF08\u6709\u4E8B\u4EF6\u65E0\u7EC8\u6001\uFF09\u5F62\u6001\u5C06\u65E0\u81EA\u52A8\u56DE\u6536\uFF0C\u4EC5\u5269 idle \u9759\u9ED8\u5224\u5B9A\u515C\u5E95"
1670
- });
1671
- const fireTimeout = (kind) => {
1672
- if (turn.settled) return;
1673
- turn.settled = true;
1674
- clearTurnTimers(turn);
1675
- this.activeTurns.delete(sessionId);
1676
- rejectDone(
1677
- new TurnTimeoutError(kind, {
1678
- thresholdMs: kind === "idle" ? turn.idleMs : turn.ceilingMs,
1679
- elapsed: Date.now() - turn.startedAt,
1680
- lastEventAt: turn.lastEventAt
1681
- })
1682
- );
1683
- };
1684
- turn.fireTimeout = fireTimeout;
1685
- if (turn.idleMs > 0) {
1686
- turn.idleTimer = armTurnTimer(() => turn.fireTimeout("idle"), turn.idleMs);
1687
- }
1688
- if (turn.ceilingMs > 0) {
1689
- turn.ceilingTimer = armTurnTimer(
1690
- () => turn.fireTimeout("ceiling"),
1691
- turn.ceilingMs
1692
- );
1693
- }
1694
1578
  turn.settle = (t) => {
1695
1579
  if (turn.settled) return;
1696
1580
  turn.settled = true;
1697
1581
  turn.terminal = t;
1698
- clearTurnTimers(turn);
1699
1582
  resolveDone(t);
1700
1583
  };
1701
1584
  turn.fail = (err) => {
1702
1585
  if (turn.settled) return;
1703
1586
  turn.settled = true;
1704
- clearTurnTimers(turn);
1705
1587
  rejectDone(err);
1706
1588
  };
1707
1589
  this.activeTurns.set(sessionId, turn);
1708
- return { turn, done, stopTimers: () => clearTurnTimers(turn) };
1709
- }
1710
- /**
1711
- * idle 主判定的事件刷新(P0-1 D1):本 turn 任何事件到达即重置 idle 倒数——
1712
- * 活跃事件流零误杀的结构保证。总上界不受影响(固定倒数)。已落定 turn 无
1713
- * timer 可刷新(只剩 lastEventAt 记账)。
1714
- */
1715
- refreshIdle(turn) {
1716
- turn.lastEventAt = Date.now();
1717
- if (turn.settled || turn.idleTimer === void 0) return;
1718
- clearTimeout(turn.idleTimer);
1719
- turn.idleTimer = armTurnTimer(() => turn.fireTimeout("idle"), turn.idleMs);
1590
+ return { turn, done };
1720
1591
  }
1721
1592
  lookupTurn(sessionId) {
1722
1593
  if (sessionId !== void 0) return this.activeTurns.get(sessionId);
@@ -1746,7 +1617,6 @@ var SessionChannel = class {
1746
1617
  if (turn === void 0) return;
1747
1618
  const payload = isRecord3(params) && isRecord3(params.payload) ? params.payload : void 0;
1748
1619
  if (payload === void 0) return;
1749
- this.refreshIdle(turn);
1750
1620
  if (this.applyFinalFrame(turn, payload)) return;
1751
1621
  if (!this.applyStreamDelta(turn, payload) && !turn.settled) {
1752
1622
  turn.callbacks.onActivity?.();
@@ -1863,7 +1733,6 @@ var SessionChannel = class {
1863
1733
  applyStreamChunkTelemetry(params) {
1864
1734
  const turn = this.lookupTurn(extractPushSessionId(params));
1865
1735
  if (turn === void 0 || turn.settled) return;
1866
- this.refreshIdle(turn);
1867
1736
  for (const key of ["chunk", "text", "content"]) {
1868
1737
  const v = params[key];
1869
1738
  if (typeof v === "string" && v !== "") {
@@ -2219,30 +2088,19 @@ var ZcodeEngine = class {
2219
2088
  * 只在最终轮终态后合成(不变量 2/5)。
2220
2089
  */
2221
2090
  async runAppServerAttemptsWithRetry(task, ctx, modelRef, cwd, basePrompt, schema, usageAcc) {
2222
- const attemptStartedAt = Date.now();
2223
2091
  let final = await this.attemptAppServerTurn(task, ctx, modelRef, cwd, basePrompt);
2224
2092
  accumulateUsage(usageAcc, final);
2225
2093
  if (final.kind === "run-failed" && final.transient !== void 0 && ctx.signal?.aborted !== true && !this.disposed) {
2226
- const budget = resolveTransientRetryBudget(explicitTurnBudgetMs(), Date.now() - attemptStartedAt);
2227
- if (budget.state === "depleted") {
2228
- logger4.warn(
2229
- `[zcode-engine] \u672B\u6B21 attempt \u77AC\u65F6\u5931\u8D25\uFF08${final.transient}\uFF09\u2014\u2014\u663E\u5F0F\u603B\u4E0A\u754C\u9884\u7B97\u5269\u4F59\u4E0D\u8DB3 ${ZCODE_TURN_RETRY_MIN_BUDGET_MS}ms\uFF0C\u4E0D\u91CD\u8BD5\u76F4\u63A5\u7EC8\u6001\u5316\uFF08\u9884\u7B97\u7EE7\u627F\uFF1A\u91CD\u8BD5\u4E0D\u91CD\u7F6E\u603B\u9884\u7B97\uFF09`
2230
- );
2231
- } else {
2232
- logger4.warn(
2233
- `[zcode-engine] \u672B\u6B21 attempt \u77AC\u65F6\u5931\u8D25\uFF08${final.transient}\uFF09\u2014\u2014\u6B62\u635F\u94FE\u5DF2\u7EC8\u5C40\uFF0C\u65B0\u4F1A\u8BDD\u81EA\u52A8\u91CD\u8BD5\u4E00\u6B21` + (budget.state === "inherit" ? `\uFF08\u9884\u7B97\u7EE7\u627F\uFF1A\u91CD\u8BD5\u8F6E\u603B\u4E0A\u754C=\u5269\u4F59 ${budget.remainingMs}ms\uFF0C\u4E0D\u91CD\u7F6E\u603B\u9884\u7B97\uFF09` : "\uFF08\u65E0\u663E\u5F0F\u603B\u4E0A\u754C\u9884\u7B97\uFF0C\u91CD\u8BD5\u8F6E\u8D70 env/\u9ED8\u8BA4\u4E0A\u754C\uFF09")
2234
- );
2235
- const retry = await this.attemptAppServerTurn(task, ctx, modelRef, cwd, basePrompt, {
2236
- // 预算继承传递点(D2 内部传参面):显式预算 → 剩余值;无显式预算 → 缺省
2237
- // (channel 侧走同一 env/默认,与首轮行为一致)
2238
- ...budget.state === "inherit" ? { turnTimeoutMs: budget.remainingMs } : {},
2239
- // 重试事实进文案:「已自动重试一次」句仅对真实发生的重试生效(未重试形态
2240
- // 不含——与行为一致,§5.2 F-1/F-4)
2241
- retried: true
2242
- });
2243
- accumulateUsage(usageAcc, retry);
2244
- final = retry;
2245
- }
2094
+ logger4.warn(
2095
+ `[zcode-engine] \u672B\u6B21 attempt \u77AC\u65F6\u5931\u8D25\uFF08${final.transient}\uFF09\u2014\u2014\u6B62\u635F\u94FE\u5DF2\u7EC8\u5C40\uFF0C\u65B0\u4F1A\u8BDD\u81EA\u52A8\u91CD\u8BD5\u4E00\u6B21`
2096
+ );
2097
+ const retry = await this.attemptAppServerTurn(task, ctx, modelRef, cwd, basePrompt, {
2098
+ // 重试事实进文案:「已自动重试一次」句仅对真实发生的重试生效(未重试形态
2099
+ // 不含——与行为一致,§5.2 F-1/F-4)
2100
+ retried: true
2101
+ });
2102
+ accumulateUsage(usageAcc, retry);
2103
+ final = retry;
2246
2104
  }
2247
2105
  if (final.kind === "parsed" && final.schemaResult !== void 0 && !final.schemaResult.ok && schema !== void 0) {
2248
2106
  const retryPrompt = appendSchemaRetryDirective(basePrompt, final.schemaResult.error);
@@ -2274,9 +2132,7 @@ var ZcodeEngine = class {
2274
2132
  * 单轮常驻执行:runTurn 组合面 + D3 abort 链 + 事件前移(text_delta 实时流出;
2275
2133
  * 终态数据经 read 兜底收口后才 resolve——不变量 1/2)。
2276
2134
  *
2277
- * @param opts turnTimeoutMs:显式总上界传参面(D2 内部传参点)——U4/D6 预算继承
2278
- * 向重试轮传剩余值;缺省不传(channel 走 env→默认,首轮行为)。
2279
- * retried:瞬时重试轮标记——失败文案补「已自动重试一次」句(F-1/F-4)。
2135
+ * @param opts retried:瞬时重试轮标记——失败文案补「已自动重试一次」句(F-1/F-4)。
2280
2136
  */
2281
2137
  async attemptAppServerTurn(task, ctx, modelRef, cwd, prompt, opts = {}) {
2282
2138
  const rt = this.ensureAppServerRuntime();
@@ -2306,12 +2162,10 @@ var ZcodeEngine = class {
2306
2162
  ctx.onHandleReady?.({
2307
2163
  sessionRef: { dbPath: zcodeSessionDbPath(this.deps.engineDataDir()), sessionId }
2308
2164
  });
2309
- },
2310
- // 显式总上界传参(D2/U4:缺省缺席——channel 侧 resolveTurnTimerMs 走 env→默认)
2311
- ...opts.turnTimeoutMs !== void 0 ? { turnTimeoutMs: opts.turnTimeoutMs } : {}
2165
+ }
2312
2166
  });
2313
2167
  const onAbort = () => {
2314
- void this.appServerAbortChain(rt, turn, () => currentSessionId, sessionCreated, { escalateOn: "turn-settled" });
2168
+ void this.appServerAbortChain(rt, turn, () => currentSessionId, sessionCreated);
2315
2169
  };
2316
2170
  if (ctx.signal !== void 0) {
2317
2171
  if (ctx.signal.aborted) onAbort();
@@ -2337,32 +2191,16 @@ var ZcodeEngine = class {
2337
2191
  /**
2338
2192
  * [attemptAppServerTurn 拆分] 失败分流(catch 半边):
2339
2193
  * - signal 已 aborted → 中止终态收口;
2340
- * - [P0-1 U2] 超时入口(D3 v1.1):turn 已被 channel 判死 reject——升级判据不能
2341
- * 再挂在 turn 落定上(race 恒真,killChain 结构性不可达的 v1 击穿点),改以
2342
- * stop 应答三态裁决。await 链终局(非 fire-and-forget):outcome 止损文案与
2343
- * 重试时序(D6,u-z4)都依赖链终局信号——止损完成前不合成终态。timeout 类
2344
- * (idle/ceiling 都算)是 D6 明文的可重试形态——结构化标记
2345
- * (TurnTimeoutError 类型化判据,不经字符串匹配,D4 同精神);
2346
- * - [P0-1 U4] 连接崩溃收割形态(failAllTurns 的错误,D6 第二可重试形态)判据:
2194
+ * - [P0-1 U4] 连接崩溃收割形态(failAllTurns 的错误,D6 可重试形态)判据:
2347
2195
  * 非 RPC error(服务端无明确应答——有应答即精确错误归类,非瞬时崩溃面)且
2348
2196
  * conn 不存活。时序可靠性:catch 时刻紧随 onClose 收割,连接重建仅由
2349
2197
  * conn.request 惰性触发——本链路中 runTurn finally 的 closeSession 对死连接
2350
- * 短路(channel 侧 !alive 守卫)、stop 只属 abort/超时入口(前者已被
2351
- * signal.aborted 短路、后者走上一分支)——此刻无 request 可重建,判据可靠;
2198
+ * 短路(channel 侧 !alive 守卫)、stop 只属 abort 链(已被 signal.aborted
2199
+ * 短路)——此刻无 request 可重建,判据可靠;
2352
2200
  * - 其余 → 精确错误终态。
2353
2201
  */
2354
2202
  async classifyAppServerTurnFailure(err, ctx, args) {
2355
2203
  if (ctx.signal?.aborted === true) return abortedAppServerAttempt(ctx);
2356
- if (err instanceof TurnTimeoutError) {
2357
- const stopPath = await this.appServerAbortChain(
2358
- args.rt,
2359
- args.turn,
2360
- args.currentSessionId,
2361
- args.sessionCreated,
2362
- { escalateOn: "stop-outcome" }
2363
- );
2364
- return timeoutAppServerAttempt(err, args.currentSessionId(), stopPath, { retried: args.retried });
2365
- }
2366
2204
  if (!isAppServerRpcError(err) && !args.rt.conn.alive) {
2367
2205
  return failedAppServerAttempt(err, args.currentSessionId(), { retried: args.retried, transient: "conn-closed" });
2368
2206
  }
@@ -2373,8 +2211,8 @@ var ZcodeEngine = class {
2373
2211
  * shutdown resolve 于 `exit` 事件,而 finalize 挂 `close`(stdio 排空)——两者之间的
2374
2212
  * 事件窗口内 conn.child 仍非 null,紧接的下一任务 request 会复用垂死进程(写入成功
2375
2213
  * 但必败,走崩溃路径)而非触发重建。与 shutdownRuntimeAndDisposeChannel 的
2376
- * HARVEST_GRACE 同款 race 形态(close 永不到达不挂死)。超时入口的 await 链终局
2377
- * 语义(D6 重试时序依据)因此是「进程收割确认完成」而非「SIGTERM 已发出」。
2214
+ * HARVEST_GRACE 同款 race 形态(close 永不到达不挂死)。abort 链的 await 终局
2215
+ * 语义因此是「进程收割确认完成」而非「SIGTERM 已发出」。
2378
2216
  */
2379
2217
  async awaitConnFinalized(rt) {
2380
2218
  if (!rt.conn.alive) return;
@@ -2390,25 +2228,14 @@ var ZcodeEngine = class {
2390
2228
  });
2391
2229
  }
2392
2230
  /**
2393
- * D3 abort 链执行体(双入口分岔,P0-1 U2 参数化,D3 v1.1):
2394
- * - **用户取消入口**(`escalateOn: "turn-settled"`,现状语义零改动;fire-and-forget——
2395
- * 与 turn promise 并行推进):stop 帧(超时 ZCODE_APPSERVER_STOP_TIMEOUT_MS)→
2396
- * grace 窗口内 turn 落定即止(不杀共享进程)→ 超窗 killChain(conn.shutdown 全序:
2397
- * SIGTERM→grace→SIGKILL)。turn 的最终落定由 attempt 主路径 await 收口,本链不
2398
- * 直接产出终态。abort 与 create 竞态(signal 先到、session 未建立):等会话建立
2399
- * (带上限)再发 stop——否则 stop 永远发不出,直接连坐杀共享进程。
2400
- * - **超时入口**(`escalateOn: "stop-outcome"`,channel 判死后由 catch await 链终局):
2401
- * turn 已 reject,对它 race 恒真不可用(v1 击穿点)——升级判据改挂在 **stop 应答
2402
- * 三态**:①成功应答 → 服务端接受停 turn,止损确认,链终止;②协议性 error 应答
2403
- * (有 error 帧即控制面活的证据,多因 runTurn finally 的 closeSession 先行关会话,
2404
- * 健康形态竞态)→ 链终止**不升级**(止损由 close 回收 + 服务端自治承担;把
2405
- * 「stop 报错」一律升级会误杀健康共享进程并连坐并发任务);③超时/写入失败/进程
2406
- * 死等连接级失败(控制面死)→ killChain 升级。判据实现依据:error 应答帧 reject
2407
- * 携带 number code(isAppServerRpcError);连接级失败是无 code 的新 Error
2408
- * (connection.request 三态 reject 形态)。返回值即止损路径(超时入口的 outcome
2409
- * 文案素材——D3 强制可观测面)。
2231
+ * D3 abort 链执行体(用户取消入口,fire-and-forget——与 turn promise 并行推进):
2232
+ * stop 帧(超时 ZCODE_APPSERVER_STOP_TIMEOUT_MS)→ grace 窗口内 turn 落定即止
2233
+ * (不杀共享进程)→ 超窗 killChain(conn.shutdown 全序:SIGTERM→grace→SIGKILL)。
2234
+ * turn 的最终落定由 attempt 主路径 await 收口,本链不直接产出终态。abort 与 create
2235
+ * 竞态(signal 先到、session 未建立):等会话建立(带上限)再发 stop——否则 stop
2236
+ * 永远发不出,直接连坐杀共享进程。
2410
2237
  */
2411
- async appServerAbortChain(rt, turn, getSessionId, sessionCreated, entry) {
2238
+ async appServerAbortChain(rt, turn, getSessionId, sessionCreated) {
2412
2239
  const graceRaceThenKill = async () => {
2413
2240
  const settled = await Promise.race([
2414
2241
  turn.then(
@@ -2417,49 +2244,31 @@ var ZcodeEngine = class {
2417
2244
  ),
2418
2245
  delayResolved(abortGraceMs(), false)
2419
2246
  ]);
2420
- if (settled) return "settled-in-grace";
2247
+ if (settled) return;
2421
2248
  logger4.warn(
2422
2249
  `[zcode-engine] abort grace \u7A97\u53E3\u5185\u672A\u89C1\u7EC8\u6001\u2014\u2014killChain \u6536\u5272\u5171\u4EAB\u8FDB\u7A0B\uFF08\u63A5\u53D7\u8FDE\u5750\uFF0C\u5728\u9014\u4EFB\u52A1\u8D70\u5D29\u6E83\u8DEF\u5F84\uFF09`
2423
2250
  );
2424
2251
  await rt.conn.shutdown({ graceMs: ZCODE_KILL_GRACE_MS });
2425
2252
  await this.awaitConnFinalized(rt);
2426
- return "escalated-kill";
2427
2253
  };
2428
2254
  let sessionId = getSessionId();
2429
2255
  if (sessionId === void 0) {
2430
2256
  await Promise.race([sessionCreated, delayResolved(stopTimeoutMs(), void 0)]);
2431
2257
  sessionId = getSessionId();
2432
2258
  if (sessionId === void 0) {
2433
- if (entry.escalateOn === "stop-outcome") return "no-session";
2434
2259
  return graceRaceThenKill();
2435
2260
  }
2436
2261
  }
2437
2262
  if (!rt.conn.alive) {
2438
- if (entry.escalateOn === "stop-outcome") return "stop-unreachable-killed";
2439
2263
  return graceRaceThenKill();
2440
2264
  }
2441
2265
  try {
2442
2266
  await rt.conn.request("session/stop", { sessionId }, { timeoutMs: stopTimeoutMs() });
2443
2267
  } catch (err) {
2444
- if (entry.escalateOn === "stop-outcome") {
2445
- if (isAppServerRpcError(err)) {
2446
- logger4.debug(
2447
- `[zcode-engine] session/stop \u62A5\u534F\u8BAE\u6027 error\uFF08${errMessage3(err)}\uFF09\u2014\u2014\u4F1A\u8BDD\u5DF2\u56DE\u6536\uFF0C\u63A7\u5236\u9762\u5B58\u6D3B\uFF0C\u4E0D\u5347\u7EA7\u6740\u94FE`
2448
- );
2449
- return "stop-rejected";
2450
- }
2451
- logger4.warn(
2452
- `[zcode-engine] session/stop \u65E0\u5E94\u7B54\uFF08${errMessage3(err)}\uFF09\u2014\u2014\u5347\u7EA7 killChain \u6536\u5272\u5171\u4EAB\u8FDB\u7A0B\uFF08\u8D85\u65F6\u5165\u53E3\uFF0C\u63A5\u53D7\u8FDE\u5750\uFF09`
2453
- );
2454
- await rt.conn.shutdown({ graceMs: ZCODE_KILL_GRACE_MS });
2455
- await this.awaitConnFinalized(rt);
2456
- return "stop-unreachable-killed";
2457
- }
2458
2268
  logger4.debug(
2459
2269
  `[zcode-engine] session/stop \u5931\u8D25\uFF08${errMessage3(err)}\uFF09\u2014\u2014grace \u540E\u8D70 killChain \u515C\u5E95`
2460
2270
  );
2461
2271
  }
2462
- if (entry.escalateOn === "stop-outcome") return "stop-acked";
2463
2272
  return graceRaceThenKill();
2464
2273
  }
2465
2274
  // ── 常驻运行时管理(D1/D6)──────────────────────
@@ -2513,15 +2322,15 @@ var ZcodeEngine = class {
2513
2322
  * [R5 修复 R4 既有竞态] shutdown → 等崩溃收割实际发生 → channel 退订。killChain 在
2514
2323
  * `exit` 事件 resolve,而连接 finalize(onClose → channel 的 failAllTurns)挂
2515
2324
  * `close` 事件——两者之间有一个事件循环窗口:shutdown resolve 后立即退订,在途
2516
- * turn 会错过收割、挂到 turnTimeoutMs(300s)。退订前等 onClose 触发(本方法先于
2325
+ * turn 会错过收割而失去终局来源。退订前等 onClose 触发(本方法先于
2517
2326
  * shutdown 订阅;channel 的订阅在构造期更早——其 failAllTurns 先于本 promise
2518
2327
  * resolve 执行);ZCODE_APPSERVER_HARVEST_GRACE_MS 兜底防 `close` 永不到达时挂死。
2519
2328
  * [P0-1 U5/D7] grace race 输掉(close 迟到/永不到达——stdio 被孙进程持有排空不
2520
2329
  * 尽等病态形态)时,channel.dispose() 内置的 dispose 收割(failAllTurns 先于退订,
2521
- * SessionChannel.dispose)兜底在途 turn——退化终点从「挂满 turn 自身 idle/总上界
2522
- * 预算」收敛为「grace 窗口内明确失败」(设计 §3.4 退化路径闭合);race 窗口与
2523
- * awaitConnFinalized 同源同量级(ZCODE_APPSERVER_HARVEST_GRACE_MS)。正常 close
2524
- * 先到时 onClose 收割先行,dispose 收割幂等 no-op(零回归)。
2330
+ * SessionChannel.dispose)兜底在途 turn——在途 turn 收敛为「grace 窗口内明确失败」
2331
+ * (设计 §3.4 退化路径闭合);race 窗口与 awaitConnFinalized 同源同量级
2332
+ * (ZCODE_APPSERVER_HARVEST_GRACE_MS)。正常 close 先到时 onClose 收割先行,
2333
+ * dispose 收割幂等 no-op(零回归)。
2525
2334
  */
2526
2335
  async shutdownRuntimeAndDisposeChannel(rt) {
2527
2336
  const harvested = new Promise((resolve2) => {
@@ -2560,10 +2369,7 @@ var ZcodeEngine = class {
2560
2369
  engineId: ZCODE_ENGINE_ID,
2561
2370
  content: "",
2562
2371
  durationMs: Date.now() - startedAt,
2563
- ...typeof task.worktree === "object" && task.worktree !== null ? { worktreePath: task.worktree.path } : {},
2564
- // D9① fallback 留痕:路由层经 RunContext 投影(zcode 无 record 通路,outcome 是
2565
- // 唯一留痕面;pi 引擎另有 record 投影)
2566
- ...ctx.engineFallback !== void 0 ? { engineFallback: ctx.engineFallback } : {}
2372
+ ...typeof task.worktree === "object" && task.worktree !== null ? { worktreePath: task.worktree.path } : {}
2567
2373
  };
2568
2374
  const emit = (event) => {
2569
2375
  ctx.onEvent?.(event);
@@ -2658,7 +2464,7 @@ var ZcodeEngine = class {
2658
2464
  * D6 read 三级降级:①sqlite 原生读取 → ②宿主 event journal 重放(对齐点①接线:
2659
2465
  * replayJournalToSessionView 复用 live reducer,重放等价性见 §3.3.6)→ ③outcome-only。
2660
2466
  * sessionId 缺失(解析失败的 run 无法定位 session)跳过①级;②级依赖
2661
- * handle.journalPath(宿主 run 后回填)。dbPath:新 handle 恒为隔离库绝对路径
2467
+ * handle.eventsPath(宿主 run 后回填)。dbPath:新 handle 恒为隔离库绝对路径
2662
2468
  * (zcodeSessionDbPath(engineDataDir),tier1 白名单集合见方法体——宿主路径仅
2663
2469
  * 「共享 HOME 时代」存量兼容);旧 records(池时代)的相对路径仍按池目录锚定
2664
2470
  * 解析(read 兼容旧数据,池目录不存在时自然落②级 journal 降级)。[池抽象降级
@@ -2829,16 +2635,6 @@ ${omissionNote}${kept.join("\n")}
2829
2635
  function buildResumeUnavailableNoticeSegment() {
2830
2636
  return "[\u4F1A\u8BDD\u5EF6\u7EED\u63D0\u793A] \u672C\u6D88\u606F\u662F\u540C\u4E00\u4EFB\u52A1\u7684\u5EF6\u7EED\u4F1A\u8BDD\uFF0C\u4F46\u4E0A\u4E00\u4F1A\u8BDD\u7684\u5386\u53F2\u8BB0\u5F55\u4E0D\u53EF\u6062\u590D\uFF08\u539F\u59CB\u4F1A\u8BDD\u5DF2\u5931\u6548\uFF09\u3002\n\u6CA1\u6709\u66F4\u65E9\u7684\u5BF9\u8BDD\u4E0A\u4E0B\u6587\u53EF\u5F15\u7528\u2014\u2014\u8BF7\u57FA\u4E8E\u4E0B\u65B9\u6700\u65B0\u6D88\u606F\u72EC\u7ACB\u5224\u65AD\u5E76\u7EE7\u7EED\u63A8\u8FDB\u4EFB\u52A1\u3002\n\n";
2831
2637
  }
2832
- var ZCODE_TURN_RETRY_MIN_BUDGET_MS = 3e5;
2833
- function resolveTransientRetryBudget(totalBudgetMs, consumedMs) {
2834
- if (totalBudgetMs === void 0) return { state: "unbounded" };
2835
- const remainingMs = totalBudgetMs - consumedMs;
2836
- return remainingMs >= ZCODE_TURN_RETRY_MIN_BUDGET_MS ? { state: "inherit", remainingMs } : { state: "depleted" };
2837
- }
2838
- function explicitTurnBudgetMs() {
2839
- const parsed = parseZcodeTurnTimeoutEnv(process.env[ZCODE_TURN_MAX_TIMEOUT_ENV]);
2840
- return parsed.state === "valid" && parsed.ms > 0 ? parsed.ms : void 0;
2841
- }
2842
2638
  function isPlainObject(v) {
2843
2639
  return typeof v === "object" && v !== null && !Array.isArray(v);
2844
2640
  }
@@ -2984,8 +2780,7 @@ function failedAppServerAttempt(err, currentSessionId, opts = {}) {
2984
2780
  kind: "run-failed",
2985
2781
  output: syntheticAppServerOutput(null),
2986
2782
  message: buildAppServerRunFailedMessage(err, currentSessionId, opts.retried === true),
2987
- // [P0-1 U4/D6] 瞬时失败形态标记(重试判定判据——timeout 形态走
2988
- // timeoutAppServerAttempt,此处只承载 conn-closed)
2783
+ // [P0-1 U4/D6] 瞬时失败形态标记(重试判定判据,conn-closed 连接崩溃形态)
2989
2784
  ...opts.transient !== void 0 ? { transient: opts.transient } : {},
2990
2785
  // 错误规格表 -32004 行「按任务失败上报(含会话 id)」:create 成功后运行中失败
2991
2786
  // (-32004/-32010 等)时留痕会话 id——经 applyRunFailedOutcome 落 outcome.sessionId
@@ -2993,38 +2788,6 @@ function failedAppServerAttempt(err, currentSessionId, opts = {}) {
2993
2788
  ...currentSessionId !== void 0 ? { sessionId: currentSessionId } : {}
2994
2789
  };
2995
2790
  }
2996
- function timeoutAppServerAttempt(err, currentSessionId, stopPath, opts = {}) {
2997
- return {
2998
- kind: "run-failed",
2999
- output: syntheticAppServerOutput(null),
3000
- message: buildAppServerTimeoutMessage(err, currentSessionId, stopPath, opts.retried === true),
3001
- transient: "timeout",
3002
- ...currentSessionId !== void 0 ? { sessionId: currentSessionId } : {}
3003
- };
3004
- }
3005
- function stopPathText(stopPath) {
3006
- switch (stopPath) {
3007
- case "stop-acked":
3008
- return "session/stop \u5DF2\u9001\u8FBE\uFF08\u670D\u52A1\u7AEF\u63A5\u53D7\u505C turn\uFF09";
3009
- case "stop-rejected":
3010
- return "session/stop \u62A5\u534F\u8BAE\u6027 error\uFF08\u4F1A\u8BDD\u5DF2\u88AB\u56DE\u6536\uFF0C\u63A7\u5236\u9762\u5B58\u6D3B\u2014\u2014\u6B62\u635F\u7531\u4F1A\u8BDD\u56DE\u6536\u627F\u62C5\uFF0C\u4E0D\u5347\u7EA7\u6740\u94FE\uFF09";
3011
- case "stop-unreachable-killed":
3012
- return `session/stop \u65E0\u5E94\u7B54\u5DF2\u5347\u7EA7\u6740\u94FE\uFF08SIGTERM\u2192${ZCODE_KILL_GRACE_MS}ms\u2192SIGKILL \u6536\u5272\u5171\u4EAB\u8FDB\u7A0B\uFF09`;
3013
- case "no-session":
3014
- return "\u4F1A\u8BDD\u672A\u5EFA\u7ACB\uFF08\u4EFB\u52A1\u672A\u5F00\u59CB\u6267\u884C\uFF0C\u65E0\u5728\u9014\u6D88\u8017\uFF09";
3015
- default:
3016
- return "grace \u7A97\u53E3\u5185\u7EC8\u6001\u843D\u5B9A\u6216\u5DF2\u8D70\u6740\u94FE";
3017
- }
3018
- }
3019
- function buildAppServerTimeoutMessage(err, sessionId, stopPath, retried = false) {
3020
- const sid = sessionId !== void 0 ? `\uFF08\u4F1A\u8BDD ${sessionId}\uFF09` : "";
3021
- const lastEventText = err.lastEventAt !== void 0 ? `\uFF0C\u6700\u540E\u4E8B\u4EF6 ${new Date(err.lastEventAt).toISOString()}` : "\uFF0C\u6574\u8F6E\u672A\u89C2\u5BDF\u5230\u4EFB\u4F55\u4E8B\u4EF6\uFF08\u8FDB\u7A0B\u5047\u6B7B/\u534F\u8BAE\u9759\u9ED8\u5F62\u6001\uFF09";
3022
- const head = err.kind === "idle" ? `engine_timeout: zcode turn \u8FDE\u7EED\u9759\u9ED8 ${err.thresholdMs}ms\uFF08idle \u5224\u5B9A${lastEventText}\uFF0C\u603B\u8017\u65F6 ${err.elapsed}ms\uFF09${sid}\u3002` : `engine_timeout: zcode turn \u603B\u4E0A\u754C ${err.thresholdMs}ms \u5185\u672A\u89C2\u5BDF\u5230\u7EC8\u6001\uFF08chatty-wedge \u5224\u5B9A\u2014\u2014\u4E8B\u4EF6\u6D41\u4ECD\u6D3B\u8DC3\u800C\u7EC8\u6001\u672A\u5230\u8FBE\uFF0C\u603B\u8017\u65F6 ${err.elapsed}ms\uFF09${sid}\u3002`;
3023
- const selfHelp = err.kind === "ceiling" ? `\u82E5\u672C\u4EFB\u52A1\u5C5E\u5408\u6CD5\u8D85\u957F\u4EFB\u52A1\uFF08\u9884\u671F\u8D85\u8FC7 ${err.thresholdMs}ms\uFF09\uFF0C\u91CD\u8DD1\u524D\u8BBE ${ZCODE_TURN_MAX_TIMEOUT_ENV} \u4E3A\u66F4\u5927\u6BEB\u79D2\u503C\u6216 0 \u5173\u95ED\u603B\u4E0A\u754C\uFF08\u5173\u95ED\u540E chatty \u5F62\u6001\u4E0D\u518D\u81EA\u52A8\u56DE\u6536\uFF0C\u9759\u9ED8 wedged \u4ECD\u7531 idle \u5C42\u515C\u5E95\u2014\u2014\u81EA\u884C\u6743\u8861\uFF09\u3002` : "";
3024
- const rerunGuide = retried ? `\u{1F449} \u6062\u590D\u6307\u5F15\uFF1A\u76F4\u63A5\u91CD\u8DD1\u672C\u4EFB\u52A1\uFF08\u77AC\u65F6\u6545\u969C\u5DF2\u81EA\u52A8\u91CD\u8BD5\u4E00\u6B21\u4ECD\u8D85\u65F6\uFF1B\u91CD\u8BD5\u5728\u6B62\u635F\u94FE\u7EC8\u5C40\u540E\u542F\u52A8\uFF0C\u65E0\u65B0\u65E7\u4EFB\u52A1\u53CC\u8DD1\u7A97\uFF09\uFF1B` : `\u{1F449} \u6062\u590D\u6307\u5F15\uFF1A\u76F4\u63A5\u91CD\u8DD1\u672C\u4EFB\u52A1\uFF1B`;
3025
- return `${head}\u6B62\u635F\u8DEF\u5F84\uFF1A${stopPathText(stopPath)}\u3002
3026
- ${rerunGuide}${selfHelp}\u82E5\u6301\u7EED\u51FA\u73B0\uFF0C\u68C0\u67E5 ZCode \u684C\u9762\u7AEF\u6A21\u578B\u8FDE\u901A\u6027\u6216\u6539\u7528 engine: pi\u3002`;
3027
- }
3028
2791
  function isHostTimeoutAbort(ctx) {
3029
2792
  return ctx.signal?.aborted === true && ctx.signal.reason === HOST_TIMEOUT_ABORT_REASON;
3030
2793
  }
@@ -3076,24 +2839,32 @@ function createDefaultZcodeEngine() {
3076
2839
 
3077
2840
  // src/server.ts
3078
2841
  import {
3079
- ENGINE_PROTOCOL_VERSION,
3080
2842
  EngineSdkError,
3081
- getLogger as getLogger5,
3082
- isResponseFrame,
3083
- isReverseRequestFrame
2843
+ getLogger as getLogger5
3084
2844
  } from "@zhushanwen/subagent-engine-sdk";
3085
2845
 
3086
2846
  // src/port-types.ts
3087
2847
  import { parseCtxModel } from "@zhushanwen/subagent-engine-sdk";
3088
2848
 
3089
2849
  // src/server.ts
2850
+ import {
2851
+ assembleFullTask,
2852
+ handleInboundFrame,
2853
+ initializeEngine,
2854
+ notInitializedError,
2855
+ sendReverseRequest,
2856
+ unknownMethodError,
2857
+ writeRunEvent,
2858
+ REVERSE_TIMEOUT_DEFAULT_MS,
2859
+ toProtocolError as toProtocolErrorShared
2860
+ } from "@zhushanwen/subagent-engine-sdk/server";
3090
2861
  var logger5 = getLogger5("subagents");
3091
- var REVERSE_TIMEOUT_DEFAULT_MS = 6e4;
3092
2862
  var EngineProtocolServer = class {
3093
2863
  write;
3094
2864
  engine;
3095
2865
  reverseClock;
3096
2866
  reverseTimeoutMs;
2867
+ frameLoop;
3097
2868
  activeRuns = /* @__PURE__ */ new Map();
3098
2869
  reversePending = /* @__PURE__ */ new Map();
3099
2870
  revSeq = 0;
@@ -3103,45 +2874,21 @@ var EngineProtocolServer = class {
3103
2874
  this.engine = opts.engine ?? createDefaultZcodeEngine();
3104
2875
  this.reverseClock = opts.reverseClock;
3105
2876
  this.reverseTimeoutMs = opts.reverseTimeoutMs ?? REVERSE_TIMEOUT_DEFAULT_MS;
2877
+ this.frameLoop = {
2878
+ write: this.write,
2879
+ settleReverse: (id, frame) => this.settleReverse(id, frame),
2880
+ dispatch: (id, method, params) => this.dispatch(id, method, params),
2881
+ toError: (err) => toProtocolError(err)
2882
+ };
3106
2883
  }
3107
2884
  /** 入站帧消费(请求帧 + 反向请求应答帧;main.ts 的行解析器拆行后喂入)。 */
3108
2885
  handleFrame(frame) {
3109
- if (isResponseFrame(frame)) {
3110
- this.settleReverse(frame.id, frame);
3111
- return;
3112
- }
3113
- if (isReverseRequestFrame(frame)) {
3114
- this.write({
3115
- id: 0,
3116
- error: {
3117
- code: "engine_protocol_bad_frame",
3118
- message: `unexpected reverse request frame from host: ${String(frame.method)}`,
3119
- recovery: "The engine protocol v1 only carries host/* requests engine\u2192host."
3120
- }
3121
- });
3122
- return;
3123
- }
3124
- if (typeof frame === "object" && frame !== null && "id" in frame && "method" in frame && typeof frame.method === "string") {
3125
- const { id, method, params } = frame;
3126
- if (typeof id === "number") {
3127
- void this.dispatch(id, method, params).then(
3128
- (result) => this.write({ id, result }),
3129
- (err) => this.write({ id, error: toProtocolError(err) })
3130
- );
3131
- return;
3132
- }
3133
- }
2886
+ handleInboundFrame(frame, this.frameLoop);
3134
2887
  }
3135
2888
  /** 9 正向方法分发(表驱动:method → 处理器;未知方法 → engine_protocol_unknown_method)。 */
3136
2889
  async dispatch(id, method, params) {
3137
2890
  const handler = this.methodHandlers[method];
3138
- if (handler === void 0) {
3139
- throw new EngineSdkError(
3140
- "engine_protocol_unknown_method",
3141
- `unknown protocol method: ${method} (request id ${id})`,
3142
- "The engine speaks protocol v1; check the installed engine package version vs the host."
3143
- );
3144
- }
2891
+ if (handler === void 0) throw unknownMethodError(id, method);
3145
2892
  return handler(params);
3146
2893
  }
3147
2894
  /**
@@ -3164,42 +2911,23 @@ var EngineProtocolServer = class {
3164
2911
  };
3165
2912
  // ── initialize:版本协商(越界 → engine_protocol_mismatch)+ 能力应答 ──
3166
2913
  initialize(params) {
3167
- if (params?.protocolVersion !== ENGINE_PROTOCOL_VERSION) {
3168
- throw new EngineSdkError(
3169
- "engine_protocol_mismatch",
3170
- `host protocol version ${String(params?.protocolVersion)} is not compatible with engine protocol v${ENGINE_PROTOCOL_VERSION}`,
3171
- `Upgrade the engine package or the host so both speak protocol v${ENGINE_PROTOCOL_VERSION}.`
3172
- );
3173
- }
3174
- this.initialized = true;
3175
- const models = this.engine.listModels?.() ?? null;
3176
- return {
3177
- protocolVersion: ENGINE_PROTOCOL_VERSION,
2914
+ const result = initializeEngine(params, {
3178
2915
  engineId: this.engine.id,
3179
- engineVersion: ZCODE_ADAPTER_VERSION,
3180
2916
  adapterVersion: ZCODE_ADAPTER_VERSION,
3181
2917
  capabilities: this.engine.capabilities(),
3182
- ...models !== null ? { models: models.map((m) => ({ id: m.id })) } : {}
3183
- };
2918
+ listModels: () => this.engine.listModels?.() ?? null
2919
+ });
2920
+ this.initialized = true;
2921
+ return result;
3184
2922
  }
3185
2923
  // ── run:协议载荷 → 本地 AgentCallOpts/RunContext;事件 → 通知/host 通道 ──
3186
2924
  async run(params) {
3187
- if (!this.initialized) {
3188
- throw new EngineSdkError(
3189
- "engine_protocol_not_initialized",
3190
- "run before initialize is a protocol violation",
3191
- "The host must complete the initialize handshake before dispatching runs."
3192
- );
3193
- }
2925
+ if (!this.initialized) throw notInitializedError();
3194
2926
  const { runId, task, ctx } = params;
3195
2927
  const controller = new AbortController();
3196
2928
  const active = { controller, seq: 0 };
3197
2929
  this.activeRuns.set(runId, active);
3198
- const fullTask = {
3199
- ...task,
3200
- ...ctx.model !== void 0 ? { model: ctx.model } : {},
3201
- ...ctx.cwd !== void 0 ? { cwd: ctx.cwd } : {}
3202
- };
2930
+ const fullTask = assembleFullTask(task, ctx);
3203
2931
  const ctxModel = parseCtxModel(ctx.ctxModel);
3204
2932
  const stream = ctx.streamMode === "stream" ? {
3205
2933
  onDelta: (delta) => {
@@ -3212,7 +2940,6 @@ var EngineProtocolServer = class {
3212
2940
  onEvent: (event) => this.emitEvent(runId, event),
3213
2941
  ...ctxModel !== void 0 ? { ctxModel } : {},
3214
2942
  ...stream !== void 0 ? { stream } : {},
3215
- ...ctx.engineFallback !== void 0 ? { engineFallback: ctx.engineFallback } : {},
3216
2943
  // [U6 / §3.2.6 要点 3] resume 锚点透传(宿主 → 引擎的续聊通道:zcode 锚
3217
2944
  // sessionRef {sessionId, dbPath},引擎侧 resume 读 + 新 session 注入消费)。
3218
2945
  ...params.resume !== void 0 ? { resume: params.resume } : {},
@@ -3247,9 +2974,7 @@ var EngineProtocolServer = class {
3247
2974
  }
3248
2975
  // ── 出站:事件通知 + 反向请求客户端 ──
3249
2976
  emitEvent(runId, event) {
3250
- const active = this.activeRuns.get(runId);
3251
- const seq = active !== void 0 ? ++active.seq : 0;
3252
- this.write({ method: "event", params: { runId, seq, event } });
2977
+ writeRunEvent(this.write, this.activeRuns, runId, event);
3253
2978
  }
3254
2979
  /**
3255
2980
  * 发反向请求并等应答(帧④必须应答)。计时面登记:发出 → clock.started;收到
@@ -3275,25 +3000,17 @@ var EngineProtocolServer = class {
3275
3000
  return this.reverseRequestInternal(method, params);
3276
3001
  }
3277
3002
  reverseRequestInternal(method, params) {
3278
- const id = `rev-${++this.revSeq}`;
3279
- return new Promise((resolve2, reject) => {
3280
- const timer = setTimeout(() => {
3281
- this.reversePending.delete(id);
3282
- this.reverseClock?.settled(id);
3283
- reject(new Error(`reverse request ${method} (${id}) timed out after ${this.reverseTimeoutMs}ms`));
3284
- }, this.reverseTimeoutMs);
3285
- if (typeof timer.unref === "function") timer.unref();
3286
- this.reversePending.set(id, { resolve: resolve2, reject, timer });
3287
- this.reverseClock?.started(id);
3288
- try {
3289
- this.write({ id, method, params });
3290
- } catch (err) {
3291
- this.reversePending.delete(id);
3292
- clearTimeout(timer);
3293
- this.reverseClock?.settled(id);
3294
- reject(new Error(`reverse request ${method} (${id}) could not be written: ${toErrorMessage(err)}`));
3295
- }
3296
- });
3003
+ return sendReverseRequest(
3004
+ {
3005
+ write: this.write,
3006
+ pending: this.reversePending,
3007
+ timeoutMs: this.reverseTimeoutMs,
3008
+ ...this.reverseClock !== void 0 ? { clock: this.reverseClock } : {},
3009
+ nextId: () => `rev-${++this.revSeq}`
3010
+ },
3011
+ method,
3012
+ params
3013
+ );
3297
3014
  }
3298
3015
  /** 反向请求应答落位(ack 两阶段:{ack:true}/{unsupported:true}/{ok:true} 均先 ack 计时面)。 */
3299
3016
  settleReverse(id, frame) {
@@ -3310,13 +3027,9 @@ var EngineProtocolServer = class {
3310
3027
  function probeParamsOf(params) {
3311
3028
  return typeof params === "object" && params !== null ? params : void 0;
3312
3029
  }
3030
+ var RUN_FAILURE_RECOVERY = "Check the engine process logs (engineDataDir/logs/) and rerun; if persistent, reinstall or upgrade the engine package.";
3313
3031
  function toProtocolError(err) {
3314
- if (err instanceof EngineSdkError) return err.toStructured();
3315
- return {
3316
- code: "engine_run_failed",
3317
- message: toErrorMessage(err),
3318
- recovery: "Check the engine process logs (engineDataDir/logs/) and rerun; if persistent, reinstall or upgrade the engine package."
3319
- };
3032
+ return toProtocolErrorShared(err, RUN_FAILURE_RECOVERY);
3320
3033
  }
3321
3034
 
3322
3035
  export {