@zhushanwen/subagent-core 0.6.0 → 0.7.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.
Files changed (278) hide show
  1. package/README.md +1 -1
  2. package/dist/chunk-KXVBIVOU.js +16 -0
  3. package/dist/chunk-T2SYBW3I.js +24 -0
  4. package/dist/chunk-VURUAGMM.js +2305 -0
  5. package/dist/{types-C3fE3R9x.d.cts → engine-discovery-scan-ocpM8FmI.d.cts} +770 -668
  6. package/dist/{types-C3fE3R9x.d.ts → engine-discovery-scan-ocpM8FmI.d.ts} +770 -668
  7. package/dist/execution/engine/engine-discovery-scan.cjs +2210 -0
  8. package/dist/execution/engine/engine-discovery-scan.d.cts +4 -0
  9. package/dist/execution/engine/engine-discovery-scan.d.ts +4 -0
  10. package/dist/execution/engine/engine-discovery-scan.js +20 -0
  11. package/dist/execution/engine/paths.cjs +6 -23
  12. package/dist/execution/engine/paths.d.cts +1 -8
  13. package/dist/execution/engine/paths.d.ts +1 -8
  14. package/dist/execution/engine/paths.js +1 -1
  15. package/dist/execution/relay-env.cjs +12 -28
  16. package/dist/execution/relay-env.d.cts +1 -33
  17. package/dist/execution/relay-env.d.ts +1 -33
  18. package/dist/execution/relay-env.js +5 -3
  19. package/dist/index.cjs +14045 -16059
  20. package/dist/index.d.cts +1035 -1579
  21. package/dist/index.d.ts +1035 -1579
  22. package/dist/index.js +9838 -13800
  23. package/dist.bundle/index.cjs +28875 -30323
  24. package/package.json +20 -26
  25. package/src/__tests__/record-store-last-line.test.ts +16 -7
  26. package/src/core/error-message.ts +7 -6
  27. package/src/core/host-services.ts +8 -2
  28. package/src/core/notify-ports.ts +17 -2
  29. package/src/execution/__tests__/chat-engine-routing.test.ts +62 -28
  30. package/src/execution/__tests__/chat-round-first-round-watchdog.test.ts +255 -0
  31. package/src/execution/__tests__/collect-coordinator-service.test.ts +36 -47
  32. package/src/execution/__tests__/collect-mixed-dispatch.test.ts +20 -53
  33. package/src/execution/__tests__/delivery-methods.test.ts +179 -237
  34. package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -2
  35. package/src/execution/__tests__/execute-nesting.test.ts +20 -13
  36. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +20 -33
  37. package/src/execution/__tests__/gc-timer.test.ts +7 -22
  38. package/src/execution/__tests__/get-record-for-action-restart.test.ts +20 -10
  39. package/src/execution/__tests__/helpers/fake-engine-port.ts +187 -0
  40. package/src/execution/__tests__/helpers/spawn-mock.ts +36 -9
  41. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +1 -1
  42. package/src/execution/__tests__/idle-gc.test.ts +200 -0
  43. package/src/execution/__tests__/lifecycle-predicates.test.ts +26 -21
  44. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +5 -2
  45. package/src/execution/__tests__/reconcile-sweep.test.ts +200 -0
  46. package/src/execution/__tests__/record-store.test.ts +60 -6
  47. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +20 -13
  48. package/src/execution/__tests__/round-supervisor.test.ts +293 -0
  49. package/src/execution/__tests__/session-pending.test.ts +119 -17
  50. package/src/execution/__tests__/stream-sink-retirement.test.ts +10 -10
  51. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +1 -11
  52. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +33 -30
  53. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +2 -9
  54. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +59 -129
  55. package/src/execution/__tests__/subagent-service.test.ts +2 -2
  56. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +109 -7
  57. package/src/execution/__tests__/subprocess-agent-runner.test.ts +108 -36
  58. package/src/execution/__tests__/sync-collect-recovery.test.ts +21 -36
  59. package/src/execution/__tests__/workflow-state-root.test.ts +232 -0
  60. package/src/execution/agent-registry.ts +6 -2
  61. package/src/execution/dialog-queue.ts +6 -73
  62. package/src/execution/engine/__tests__/common/capability-gate.test.ts +115 -15
  63. package/src/execution/engine/__tests__/common/errors.test.ts +11 -18
  64. package/src/execution/engine/__tests__/common/kill-chain.test.ts +7 -0
  65. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +9 -3
  66. package/src/execution/engine/__tests__/common/session-view-service.test.ts +90 -3
  67. package/src/execution/engine/__tests__/conformance/H9-test-disposition.md +116 -0
  68. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/fake-engine-protocol.mjs +377 -0
  69. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/recorded.fixture.json +331 -0
  70. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/smoke-run.fixture.json +213 -0
  71. package/src/execution/engine/__tests__/conformance/chat-round-protocol.test.ts +230 -0
  72. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +69 -42
  73. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +4 -81
  74. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +67 -44
  75. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +81 -54
  76. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +133 -70
  77. package/src/execution/engine/__tests__/conformance/engine-crash.test.ts +143 -0
  78. package/src/execution/engine/__tests__/conformance/fake-engine-capabilities.ts +19 -0
  79. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +7 -2
  80. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +6 -5
  81. package/src/execution/engine/__tests__/conformance/protocol-blackbox.test.ts +398 -0
  82. package/src/execution/engine/__tests__/conformance/registry-fork-filter.test.ts +221 -0
  83. package/src/execution/engine/__tests__/conformance/round-liveness-supervisor.test.ts +302 -0
  84. package/src/execution/engine/__tests__/conformance/static-spawn-guard.test.ts +58 -0
  85. package/src/execution/engine/__tests__/engine-discovery-scan.test.ts +558 -0
  86. package/src/execution/engine/__tests__/host-task-spec.test.ts +45 -0
  87. package/src/execution/engine/__tests__/model-ref-split.test.ts +74 -0
  88. package/src/execution/engine/__tests__/registry.test.ts +206 -1
  89. package/src/execution/engine/__tests__/routing.test.ts +97 -0
  90. package/src/execution/engine/__tests__/run-failure-worktree-cleanup.test.ts +97 -0
  91. package/src/execution/engine/client/__tests__/__fixtures__/fake-engine.mjs +294 -0
  92. package/src/execution/engine/client/__tests__/engine-client.test.ts +516 -0
  93. package/src/execution/engine/client/__tests__/mirror.test.ts +67 -0
  94. package/src/execution/engine/client/__tests__/pid-file.test.ts +257 -0
  95. package/src/execution/engine/client/__tests__/protocol-closure.test.ts +95 -0
  96. package/src/execution/engine/client/__tests__/remote-engine.test.ts +457 -0
  97. package/src/execution/engine/client/client-options.ts +103 -0
  98. package/src/execution/engine/client/engine-client.ts +687 -0
  99. package/src/execution/engine/client/mirror.ts +167 -0
  100. package/src/execution/engine/client/pid-file.ts +392 -0
  101. package/src/execution/engine/client/reaper.ts +95 -0
  102. package/src/execution/engine/client/remote-engine.ts +441 -0
  103. package/src/execution/engine/client/reverse-router.ts +255 -0
  104. package/src/execution/engine/common/capability-gate.ts +109 -22
  105. package/src/execution/engine/common/errors.ts +9 -25
  106. package/src/execution/engine/common/event-journal.ts +4 -50
  107. package/src/execution/engine/common/journal-replay.ts +3 -1
  108. package/src/execution/engine/common/kill-chain.ts +18 -251
  109. package/src/execution/engine/common/nesting-guard.ts +18 -114
  110. package/src/execution/engine/common/pool-manager.ts +4 -0
  111. package/src/execution/engine/common/session-view-service.ts +56 -84
  112. package/src/execution/engine/config.ts +151 -0
  113. package/src/execution/engine/d8-compat.ts +346 -0
  114. package/src/execution/engine/engine-discovery-roots.ts +107 -0
  115. package/src/execution/engine/engine-discovery-scan.ts +269 -0
  116. package/src/execution/engine/engine-discovery.ts +30 -7
  117. package/src/execution/engine/engine-inspect-package.ts +282 -0
  118. package/src/execution/engine/engine-manifest.ts +244 -0
  119. package/src/execution/engine/host/__tests__/host-askuser-endpoint.test.ts +123 -0
  120. package/src/execution/engine/host/__tests__/host-bridge.test.ts +135 -0
  121. package/src/execution/engine/host/__tests__/spawned-children.test.ts +73 -0
  122. package/src/execution/engine/host/host-bridge.ts +150 -0
  123. package/src/execution/engine/host/host-ui-endpoint.ts +47 -0
  124. package/src/execution/engine/host/pi-host-binding.ts +91 -0
  125. package/src/execution/engine/host/spawned-children.ts +139 -0
  126. package/src/execution/engine/host-task-spec.ts +29 -2
  127. package/src/execution/engine/model-validation.ts +55 -2
  128. package/src/execution/engine/paths.ts +16 -41
  129. package/src/execution/engine/port.ts +48 -2
  130. package/src/execution/engine/registry.ts +153 -34
  131. package/src/execution/engine/routing.ts +225 -52
  132. package/src/execution/engine/types.ts +25 -200
  133. package/src/execution/finalize-record.ts +25 -0
  134. package/src/execution/idle-gc.ts +89 -14
  135. package/src/execution/lifecycle-manager.ts +10 -7
  136. package/src/execution/lifecycle-predicates.ts +10 -8
  137. package/src/execution/record-store.ts +29 -2
  138. package/src/execution/relay-env.ts +19 -36
  139. package/src/execution/round-supervisor/domain.ts +89 -0
  140. package/src/execution/round-supervisor/index.ts +33 -0
  141. package/src/execution/round-supervisor/notify-accounting.ts +87 -0
  142. package/src/execution/round-supervisor/reconcile-sweep.ts +235 -0
  143. package/src/execution/round-supervisor/service-binding.test.ts +661 -0
  144. package/src/execution/round-supervisor/service-binding.ts +273 -0
  145. package/src/execution/round-supervisor/supervisor.ts +426 -0
  146. package/src/execution/session-pending.ts +68 -74
  147. package/src/execution/settled-watchdog.ts +39 -0
  148. package/src/execution/subagent-service.ts +746 -400
  149. package/src/execution/subprocess-agent-runner.ts +16 -8
  150. package/src/execution/types.ts +41 -117
  151. package/src/execution/ui-channels.ts +13 -214
  152. package/src/execution/ui-request-handler-factory.ts +7 -4
  153. package/src/execution/ui-request-observability.ts +2 -2
  154. package/src/execution/workflow-state-root.ts +67 -0
  155. package/src/execution/worktree-git-ops.ts +5 -0
  156. package/src/execution/worktree-manager.ts +11 -1
  157. package/src/index.ts +55 -26
  158. package/src/orchestration/__tests__/execute-agent-call.test.ts +1 -1
  159. package/src/orchestration/execute-agent-call.ts +1 -1
  160. package/src/orchestration/file-run-store.ts +144 -59
  161. package/src/orchestration/models/types.ts +43 -42
  162. package/src/shared/zcode-model-ref.ts +31 -0
  163. package/dist/chunk-43ONBFZX.js +0 -240
  164. package/dist/chunk-A4IVZWVX.js +0 -31
  165. package/dist/chunk-APZY4IME.js +0 -27
  166. package/dist/chunk-V3VHZ2VX.js +0 -59
  167. package/dist/execution/engine/engines/zcode/constants.cjs +0 -107
  168. package/dist/execution/engine/engines/zcode/constants.d.cts +0 -135
  169. package/dist/execution/engine/engines/zcode/constants.d.ts +0 -135
  170. package/dist/execution/engine/engines/zcode/constants.js +0 -54
  171. package/dist/execution/engine/engines/zcode/reader.cjs +0 -276
  172. package/dist/execution/engine/engines/zcode/reader.d.cts +0 -21
  173. package/dist/execution/engine/engines/zcode/reader.d.ts +0 -21
  174. package/dist/execution/engine/engines/zcode/reader.js +0 -9
  175. package/src/__tests__/append-system-prompt-assembly.test.ts +0 -243
  176. package/src/__tests__/fr4-get-state-handshake.test.ts +0 -125
  177. package/src/__tests__/session-runner.test.ts +0 -69
  178. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
  179. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -191
  180. package/src/execution/__tests__/descendant-sweep-guards.test.ts +0 -220
  181. package/src/execution/__tests__/descendant-sweep.test.ts +0 -269
  182. package/src/execution/__tests__/engine-model-validation.test.ts +0 -741
  183. package/src/execution/__tests__/epipe-fallback.test.ts +0 -248
  184. package/src/execution/__tests__/execution-runtime-face.test.ts +0 -272
  185. package/src/execution/__tests__/get-state-handshake.test.ts +0 -127
  186. package/src/execution/__tests__/helpers/session-runner-mocks.ts +0 -113
  187. package/src/execution/__tests__/keep-alive-no-progress.test.ts +0 -280
  188. package/src/execution/__tests__/kill-all-escalation.test.ts +0 -196
  189. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +0 -113
  190. package/src/execution/__tests__/output-collector.test.ts +0 -469
  191. package/src/execution/__tests__/pi-invocation.test.ts +0 -166
  192. package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -149
  193. package/src/execution/__tests__/rpc-mode.test.ts +0 -89
  194. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +0 -387
  195. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -277
  196. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -254
  197. package/src/execution/__tests__/run-spawn-edges.test.ts +0 -1079
  198. package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
  199. package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
  200. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
  201. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +0 -207
  202. package/src/execution/__tests__/service-kill-escalation.test.ts +0 -91
  203. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +0 -110
  204. package/src/execution/__tests__/session-runner-close-prune.test.ts +0 -181
  205. package/src/execution/__tests__/session-runner-dispatch.test.ts +0 -489
  206. package/src/execution/__tests__/session-runner-epipe.test.ts +0 -179
  207. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +0 -227
  208. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +0 -290
  209. package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -352
  210. package/src/execution/__tests__/settled-watchdog.test.ts +0 -524
  211. package/src/execution/__tests__/spawn-args.test.ts +0 -446
  212. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -192
  213. package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
  214. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -208
  215. package/src/execution/__tests__/spawned-children.test.ts +0 -236
  216. package/src/execution/__tests__/start-sync-model-guard.test.ts +0 -150
  217. package/src/execution/__tests__/stdin-writer.test.ts +0 -464
  218. package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -693
  219. package/src/execution/__tests__/temp-prompt.test.ts +0 -53
  220. package/src/execution/__tests__/timeout-integration.test.ts +0 -616
  221. package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
  222. package/src/execution/__tests__/turn-limiter.test.ts +0 -65
  223. package/src/execution/__tests__/ui-request-handler.test.ts +0 -205
  224. package/src/execution/__tests__/ui-request-queue.test.ts +0 -298
  225. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -230
  226. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
  227. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +0 -149
  228. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
  229. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +0 -128
  230. package/src/execution/engine/__tests__/paths.test.ts +0 -39
  231. package/src/execution/engine/common/schema-emulation.ts +0 -189
  232. package/src/execution/engine/common/session-view-projection.ts +0 -51
  233. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -615
  234. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
  235. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +0 -257
  236. package/src/execution/engine/engines/pi/argv-mirror.ts +0 -135
  237. package/src/execution/engine/engines/pi/get-state-handshake.ts +0 -179
  238. package/src/execution/engine/engines/pi/output-collector.ts +0 -317
  239. package/src/execution/engine/engines/pi/pi-engine.ts +0 -811
  240. package/src/execution/engine/engines/pi/pi-invocation.ts +0 -120
  241. package/src/execution/engine/engines/pi/reader.ts +0 -59
  242. package/src/execution/engine/engines/pi/registration.ts +0 -37
  243. package/src/execution/engine/engines/pi/session-runner.ts +0 -2840
  244. package/src/execution/engine/engines/pi/spawn-event-adapter.ts +0 -404
  245. package/src/execution/engine/engines/pi/stdin-writer.ts +0 -199
  246. package/src/execution/engine/engines/pi/temp-prompt.ts +0 -62
  247. package/src/execution/engine/engines/pi/turn-limiter.ts +0 -102
  248. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +0 -301
  249. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +0 -36
  250. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +0 -321
  251. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +0 -473
  252. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -43
  253. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -124
  254. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
  255. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -71
  256. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +0 -247
  257. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +0 -451
  258. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +0 -792
  259. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +0 -782
  260. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +0 -168
  261. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +0 -210
  262. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +0 -353
  263. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +0 -282
  264. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +0 -372
  265. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -145
  266. package/src/execution/engine/engines/zcode/appserver-launcher.ts +0 -167
  267. package/src/execution/engine/engines/zcode/connection.ts +0 -598
  268. package/src/execution/engine/engines/zcode/constants.ts +0 -176
  269. package/src/execution/engine/engines/zcode/golden-sample.ts +0 -47
  270. package/src/execution/engine/engines/zcode/parser.ts +0 -102
  271. package/src/execution/engine/engines/zcode/preparer.ts +0 -235
  272. package/src/execution/engine/engines/zcode/reader.ts +0 -381
  273. package/src/execution/engine/engines/zcode/registration.ts +0 -37
  274. package/src/execution/engine/engines/zcode/session-channel.ts +0 -968
  275. package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -1431
  276. package/src/execution/round-settlement.ts +0 -93
  277. package/src/execution/ui-request-queue.ts +0 -215
  278. package/src/shared/schema-env.ts +0 -44
@@ -1,451 +0,0 @@
1
- // session-channel-turn-timers.test.ts —— P0-1 U1 turn 等待两 timer 状态机单测。
2
- //
3
- // 设计权威源:docs/design/timeout-zcode-turn-and-settled-watchdog.md §6 D1(idle
4
- // 主判定 + 宽上界回收兜底)/ D2(env 配置通道)/ §10 U1(lastTerminalStatus +
5
- // lookupTurn 归因放宽)。验收映射:
6
- // ① 事件流活跃 → idle 持续刷新不触发(活跃任务零误杀)
7
- // ② 静默超 idle 阈值 → TurnTimeoutError(idle 形态)
8
- // ③ 总时长超宽上界 → TurnTimeoutError(ceiling 形态,事件活跃仍回收)
9
- // ④ 事件刷新不影响总上界倒数(fire 时刻 = 阈值精确值)
10
- // ⑤ env 覆盖生效 + 0=关闭语义(env 与显式传参两通道)
11
- // ⑥ 迟到 turn.terminal status 只记录不改写已落定结果(D5①/S5)
12
- //
13
- // 与 session-channel.test.ts(fake-appserver 子进程 + 真实 timer)互补:本文件用
14
- // mock 连接 + fake timers 驱动纯状态机——timer 时序断言(刷新/倒数/精确 fire 点)
15
- // 必须时钟可控才可判定。
16
-
17
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
18
- import type { MockInstance } from "vitest";
19
-
20
- import { getLogger } from "../../../../../core/logger.ts";
21
- import type { AppServerConnection } from "../connection.ts";
22
- import {
23
- ZCODE_TURN_IDLE_TIMEOUT_ENV,
24
- ZCODE_TURN_MAX_TIMEOUT_ENV,
25
- ZCODE_TURN_IDLE_TIMEOUT_MS,
26
- ZCODE_TURN_MAX_TIMEOUT_MS,
27
- } from "../constants.ts";
28
- import {
29
- SessionChannel,
30
- TurnTimeoutError,
31
- type SessionTurnResult,
32
- type SessionTurnOptions,
33
- } from "../session-channel.ts";
34
-
35
- // ============================================================
36
- // mock 连接 + 驱动器(SessionChannel 只触 conn 的 onNotification/onClose/request/alive)
37
- // ============================================================
38
-
39
- interface MockChannel {
40
- ch: SessionChannel;
41
- /** 我方发出的请求帧流水(method 序可断言)。 */
42
- requests: Array<{ method: string; params: unknown }>;
43
- /** 挂起中的 session/read 应答放行器(终态后收尾窗口可控)。 */
44
- readGates: Array<(v: unknown) => void>;
45
- emit: (method: string, params: unknown) => void;
46
- emitDelta: (sessionId: string | undefined, delta: string) => void;
47
- emitFinalFrame: (sessionId: string, response: string) => void;
48
- emitTerminal: (sessionId: string | undefined, status: string) => void;
49
- }
50
-
51
- let sidSeq = 0;
52
-
53
- function makeMockChannel(opts: { gateRead?: boolean } = {}): MockChannel {
54
- const notifications = new Map<string, (params: unknown) => void>();
55
- const requests: Array<{ method: string; params: unknown }> = [];
56
- const readGates: Array<(v: unknown) => void> = [];
57
- const conn = {
58
- alive: true,
59
- onNotification: (method: string, h: (params: unknown) => void) => {
60
- notifications.set(method, h);
61
- return () => notifications.delete(method);
62
- },
63
- onClose: (_h: (reason: string) => void) => () => {},
64
- request: (method: string, params: unknown) => {
65
- requests.push({ method, params });
66
- if (method === "session/create") {
67
- sidSeq += 1;
68
- return Promise.resolve({ session: { sessionId: `sess_t${sidSeq}` } });
69
- }
70
- if (method === "session/send") return Promise.resolve({ accepted: true });
71
- if (method === "session/read") {
72
- if (opts.gateRead === true) {
73
- return new Promise((resolve) => readGates.push(resolve));
74
- }
75
- return Promise.resolve({ messages: [] });
76
- }
77
- return Promise.resolve({});
78
- },
79
- } as unknown as AppServerConnection;
80
- const ch = new SessionChannel(conn);
81
- return {
82
- ch,
83
- requests,
84
- readGates,
85
- emit: (method, params) => notifications.get(method)?.(params),
86
- emitDelta: (sessionId, delta) =>
87
- notifications
88
- .get("session/event")
89
- ?.({ sessionId, payload: { delta } }),
90
- emitFinalFrame: (sessionId, response) =>
91
- notifications
92
- .get("session/event")
93
- ?.({ sessionId, payload: { response } }),
94
- // A.2:telemetry 帧不标会话归属——sessionId 传 undefined 即真实形态
95
- emitTerminal: (sessionId, status) =>
96
- notifications
97
- .get("v4/telemetry/event")
98
- ?.({ kind: "turn.terminal", status, sessionId }),
99
- };
100
- }
101
-
102
- interface TurnTracker {
103
- outcome: "pending" | "fulfilled" | "rejected";
104
- result: SessionTurnResult | undefined;
105
- error: unknown;
106
- promise: Promise<SessionTurnResult>;
107
- sessionId: string;
108
- }
109
-
110
- function startTurn(ch: SessionChannel, opts: SessionTurnOptions = {}): TurnTracker {
111
- let sessionId = "";
112
- const promise = ch.runTurn({ workspacePath: "/tmp/ws-timers", mode: "yolo" }, "任务", {
113
- onSessionCreated: (sid) => {
114
- sessionId = sid;
115
- },
116
- ...opts,
117
- });
118
- const t: TurnTracker = {
119
- outcome: "pending",
120
- result: undefined,
121
- error: undefined,
122
- promise,
123
- sessionId: "",
124
- };
125
- void promise.then(
126
- (r) => {
127
- t.outcome = "fulfilled";
128
- t.result = r;
129
- },
130
- (e) => {
131
- t.outcome = "rejected";
132
- t.error = e;
133
- }
134
- );
135
- // sessionId 经 onSessionCreated 回填——闭包读的是 t.sessionId 快照,改为代理
136
- Object.defineProperty(t, "sessionId", {
137
- get: () => sessionId,
138
- });
139
- return t;
140
- }
141
-
142
- /** fake timers 下把 runTurn 的 setup 微任务链(create→subscribe→send)推到挂起点。 */
143
- async function flushSetup(): Promise<void> {
144
- await vi.advanceTimersByTimeAsync(0);
145
- }
146
-
147
- const warnLogger = getLogger("subagents");
148
- let warnSpy: MockInstance<(msg: string, data?: unknown) => void>;
149
-
150
- beforeEach(() => {
151
- vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout", "Date"] });
152
- warnSpy = vi.spyOn(warnLogger, "warn").mockImplementation(() => {});
153
- });
154
-
155
- afterEach(() => {
156
- vi.useRealTimers();
157
- vi.restoreAllMocks();
158
- vi.unstubAllEnvs();
159
- });
160
-
161
- const MIN = 60_000;
162
-
163
- // ============================================================
164
- // ① 事件刷新——活跃事件流 idle 永不触发
165
- // ============================================================
166
-
167
- describe("① idle 主判定:事件刷新(P0-1 D1)", () => {
168
- it("短阈值:每 900ms 一个 delta(阈值 1s)连续 5 轮 → idle 不触发,终态正常 resolve", async () => {
169
- const m = makeMockChannel();
170
- const t = startTurn(m.ch, { idleTimeoutMs: 1_000, turnTimeoutMs: 0 }); // 关总上界隔离变量
171
- await flushSetup();
172
- expect(t.sessionId).not.toBe("");
173
- for (let i = 0; i < 5; i += 1) {
174
- await vi.advanceTimersByTimeAsync(900); // < 1s 阈值
175
- m.emitDelta(t.sessionId, `片段${i}`); // 事件到达 → idle 重挂
176
- expect(t.outcome).toBe("pending");
177
- }
178
- m.emitTerminal(undefined, "success");
179
- await t.promise;
180
- expect(t.outcome).toBe("fulfilled");
181
- expect(t.result?.terminal).toEqual({ status: "success", source: "turn.terminal" });
182
- });
183
-
184
- it("默认阈值(30min idle / 60min 上界):每 29min 一个事件,59min 处终态 → 全程不判死", async () => {
185
- const m = makeMockChannel();
186
- const t = startTurn(m.ch);
187
- await flushSetup();
188
- await vi.advanceTimersByTimeAsync(29 * MIN);
189
- m.emitDelta(t.sessionId, "a");
190
- await vi.advanceTimersByTimeAsync(29 * MIN);
191
- m.emitDelta(t.sessionId, "b");
192
- await vi.advanceTimersByTimeAsync(1 * MIN); // 累计 59min < 60min 上界
193
- m.emitTerminal(undefined, "success");
194
- await t.promise;
195
- expect(t.outcome).toBe("fulfilled");
196
- // 累计 59min 时若 idle 不刷新早已判死——活到 59min 即刷新语义的直接证据
197
- expect(t.result?.response).toBe("ab");
198
- });
199
- });
200
-
201
- // ============================================================
202
- // ② 静默判死——TurnTimeoutError(idle 形态)
203
- // ============================================================
204
-
205
- describe("② idle 静默判死(P0-1 D1)", () => {
206
- it("整轮无事件:静默达阈值 → TurnTimeoutError kind=idle(lastEventAt=undefined)", async () => {
207
- const m = makeMockChannel();
208
- const t = startTurn(m.ch, { idleTimeoutMs: 1_000, turnTimeoutMs: 0 });
209
- await flushSetup();
210
- await vi.advanceTimersByTimeAsync(1_000);
211
- expect(t.outcome).toBe("rejected");
212
- const err = t.error as TurnTimeoutError;
213
- expect(err).toBeInstanceOf(TurnTimeoutError);
214
- expect(err.kind).toBe("idle");
215
- expect(err.thresholdMs).toBe(1_000);
216
- expect(err.elapsed).toBe(1_000);
217
- expect(err.lastEventAt).toBeUndefined();
218
- expect(err.message).toContain("连续 1000ms");
219
- expect(err.message).toContain("终态"); // 既有消费方 /终态/ 语境兼容
220
- expect(err.message).toContain("engine: pi");
221
- });
222
-
223
- it("有事件后静默:lastEventAt 记最后事件时刻,自此刻起算静默判死", async () => {
224
- const m = makeMockChannel();
225
- const t = startTurn(m.ch, { idleTimeoutMs: 1_000, turnTimeoutMs: 0 });
226
- await flushSetup();
227
- const t0 = Date.now(); // fake 时钟起点 = 挂载后基准(真实 epoch,非 0)
228
- await vi.advanceTimersByTimeAsync(400);
229
- m.emitDelta(t.sessionId, "最后的事件");
230
- await vi.advanceTimersByTimeAsync(1_000); // 自最后事件起 1s(总时长 1.4s)
231
- expect(t.outcome).toBe("rejected");
232
- const err = t.error as TurnTimeoutError;
233
- expect(err.kind).toBe("idle");
234
- expect(err.elapsed).toBe(1_400);
235
- expect(err.lastEventAt).toBe(t0 + 400);
236
- });
237
- });
238
-
239
- // ============================================================
240
- // ③④ 总上界判死——事件活跃仍回收(chatty-wedge);刷新不影响上界倒数
241
- // ============================================================
242
-
243
- describe("③④ 总上界回收兜底(P0-1 D1)", () => {
244
- it("③ 事件流持续活跃:总上界到点仍判死 → TurnTimeoutError kind=ceiling + env 自救指引", async () => {
245
- const m = makeMockChannel();
246
- const t = startTurn(m.ch, { idleTimeoutMs: 1_000, turnTimeoutMs: 5_000 });
247
- await flushSetup();
248
- // 每 800ms 一个事件 ×6( idle 每次刷新,从不触发),末次事件在 4900ms
249
- for (let i = 0; i < 6; i += 1) {
250
- await vi.advanceTimersByTimeAsync(800);
251
- m.emitDelta(t.sessionId, `c${i}`);
252
- }
253
- await vi.advanceTimersByTimeAsync(100); // 4900ms
254
- m.emitDelta(t.sessionId, "c6");
255
- await vi.advanceTimersByTimeAsync(200); // 5100ms > 5000ms 上界
256
- expect(t.outcome).toBe("rejected");
257
- const err = t.error as TurnTimeoutError;
258
- expect(err).toBeInstanceOf(TurnTimeoutError);
259
- expect(err.kind).toBe("ceiling"); // 不是 idle——事件流活跃,idle 从未触发
260
- expect(err.thresholdMs).toBe(5_000);
261
- expect(err.message).toContain("总上界");
262
- expect(err.message).toContain("chatty-wedge");
263
- expect(err.message).toContain("终态");
264
- expect(err.message).toContain(ZCODE_TURN_MAX_TIMEOUT_ENV);
265
- expect(err.message).toContain("0 关闭");
266
- });
267
-
268
- it("④ 事件刷新不影响总上界倒数:5 次刷新后 fire 时刻仍 = 阈值精确值", async () => {
269
- const m = makeMockChannel();
270
- const t = startTurn(m.ch, { idleTimeoutMs: 1_000, turnTimeoutMs: 5_000 });
271
- await flushSetup();
272
- const t0 = Date.now(); // fake 时钟起点 = 挂载后基准(真实 epoch,非 0)
273
- for (let i = 0; i < 4; i += 1) {
274
- await vi.advanceTimersByTimeAsync(800);
275
- m.emitDelta(t.sessionId, `c${i}`);
276
- }
277
- await vi.advanceTimersByTimeAsync(900); // 3200+900=4100ms 处再刷一次(idle 截止 5100 > 上界 5000,避免同刻 tie)
278
- m.emitDelta(t.sessionId, "c4");
279
- await vi.advanceTimersByTimeAsync(1_000); // 5100ms,上界在 5000ms 已 fire
280
- const err = t.error as TurnTimeoutError;
281
- expect(err.kind).toBe("ceiling");
282
- expect(err.elapsed).toBe(5_000); // 刷新只重挂 idle,总上界从挂载起固定倒数
283
- expect(err.lastEventAt).toBe(t0 + 4_100); // 刷新被记账(事件确实到达过)
284
- });
285
- });
286
-
287
- // ============================================================
288
- // ⑤ env 覆盖生效 + 0=关闭语义(env 与显式传参两通道)
289
- // ============================================================
290
-
291
- describe("⑤ 配置通道(P0-1 D2)", () => {
292
- it("env 覆盖 idle:XYZ_ZCODE_TURN_IDLE_TIMEOUT_MS=500 → 静默 500ms 判死(默认 30min 被覆盖)", async () => {
293
- vi.stubEnv(ZCODE_TURN_IDLE_TIMEOUT_ENV, "500");
294
- vi.stubEnv(ZCODE_TURN_MAX_TIMEOUT_ENV, "0"); // 关上界隔离变量
295
- const m = makeMockChannel();
296
- const t = startTurn(m.ch);
297
- await flushSetup();
298
- await vi.advanceTimersByTimeAsync(500);
299
- const err = t.error as TurnTimeoutError;
300
- expect(err).toBeInstanceOf(TurnTimeoutError);
301
- expect(err.kind).toBe("idle");
302
- expect(err.thresholdMs).toBe(500);
303
- });
304
-
305
- it("env 覆盖总上界:XYZ_ZCODE_TURN_MAX_TIMEOUT_MS=800 → 事件活跃 800ms 判死(kind=ceiling)", async () => {
306
- vi.stubEnv(ZCODE_TURN_MAX_TIMEOUT_ENV, "800");
307
- vi.stubEnv(ZCODE_TURN_IDLE_TIMEOUT_ENV, "600000"); // idle 10min 不干扰
308
- const m = makeMockChannel();
309
- const t = startTurn(m.ch);
310
- await flushSetup();
311
- const t0 = Date.now();
312
- await vi.advanceTimersByTimeAsync(300);
313
- m.emitDelta(t.sessionId, "活跃中");
314
- await vi.advanceTimersByTimeAsync(500);
315
- const err = t.error as TurnTimeoutError;
316
- expect(err.kind).toBe("ceiling");
317
- expect(err.thresholdMs).toBe(800);
318
- expect(err.lastEventAt).toBe(t0 + 300);
319
- });
320
-
321
- it("env 0=关闭两 timer:静默 2 小时无任何判死 + 关闭 warn 明示后果(A10①),终态仍可 resolve", async () => {
322
- vi.stubEnv(ZCODE_TURN_IDLE_TIMEOUT_ENV, "0");
323
- vi.stubEnv(ZCODE_TURN_MAX_TIMEOUT_ENV, "0");
324
- const m = makeMockChannel();
325
- const t = startTurn(m.ch);
326
- await flushSetup();
327
- await vi.advanceTimersByTimeAsync(120 * MIN);
328
- expect(t.outcome).toBe("pending"); // 无隐式判死
329
- m.emitTerminal(undefined, "success");
330
- await t.promise;
331
- expect(t.outcome).toBe("fulfilled");
332
- const warns = warnSpy.mock.calls.map((c) => String(c[0]));
333
- expect(warns.some((w) => w.includes("idle 主判定") && w.includes("已关闭"))).toBe(true);
334
- expect(warns.some((w) => w.includes("总上界") && w.includes("已关闭"))).toBe(true);
335
- });
336
-
337
- it("显式传参 0=关闭(优先级高于 env):idleTimeoutMs/turnTimeoutMs=0 压过 env 正值", async () => {
338
- vi.stubEnv(ZCODE_TURN_IDLE_TIMEOUT_ENV, "500");
339
- vi.stubEnv(ZCODE_TURN_MAX_TIMEOUT_ENV, "600");
340
- const m = makeMockChannel();
341
- const t = startTurn(m.ch, { idleTimeoutMs: 0, turnTimeoutMs: 0 });
342
- await flushSetup();
343
- await vi.advanceTimersByTimeAsync(2 * 60 * MIN); // 远超两个 env 值
344
- expect(t.outcome).toBe("pending"); // 显式 0 = 关闭,env 未生效
345
- m.emitTerminal(undefined, "success");
346
- await t.promise;
347
- expect(t.outcome).toBe("fulfilled");
348
- });
349
-
350
- it("显式传参覆盖 env:idleTimeoutMs=60000 压过 env=500,60s 判死", async () => {
351
- vi.stubEnv(ZCODE_TURN_IDLE_TIMEOUT_ENV, "500");
352
- vi.stubEnv(ZCODE_TURN_MAX_TIMEOUT_ENV, "0");
353
- const m = makeMockChannel();
354
- const t = startTurn(m.ch, { idleTimeoutMs: 60_000 });
355
- await flushSetup();
356
- await vi.advanceTimersByTimeAsync(600);
357
- expect(t.outcome).toBe("pending"); // env 500 未生效——显式传参优先
358
- await vi.advanceTimersByTimeAsync(59_400);
359
- const err = t.error as TurnTimeoutError;
360
- expect(err.kind).toBe("idle");
361
- expect(err.thresholdMs).toBe(60_000);
362
- });
363
-
364
- it("env 非法值 warn + 回落默认:'abc' → 30min 默认阈值生效", async () => {
365
- vi.stubEnv(ZCODE_TURN_IDLE_TIMEOUT_ENV, "abc");
366
- vi.stubEnv(ZCODE_TURN_MAX_TIMEOUT_ENV, "0");
367
- const m = makeMockChannel();
368
- const t = startTurn(m.ch);
369
- await flushSetup();
370
- await vi.advanceTimersByTimeAsync(29 * MIN);
371
- expect(t.outcome).toBe("pending");
372
- await vi.advanceTimersByTimeAsync(1 * MIN);
373
- const err = t.error as TurnTimeoutError;
374
- expect(err.kind).toBe("idle");
375
- expect(err.thresholdMs).toBe(ZCODE_TURN_IDLE_TIMEOUT_MS); // 默认 30min
376
- expect(
377
- warnSpy.mock.calls.some((c) => String(c[0]).includes("非法"))
378
- ).toBe(true);
379
- });
380
-
381
- it("默认值锚定:env 未设时 idle=30min、总上界=60min(⛔P-Z0/P-Z1 标定前先验值)", () => {
382
- expect(ZCODE_TURN_IDLE_TIMEOUT_MS).toBe(30 * MIN);
383
- expect(ZCODE_TURN_MAX_TIMEOUT_MS).toBe(60 * MIN);
384
- expect(ZCODE_TURN_IDLE_TIMEOUT_ENV).toBe("XYZ_ZCODE_TURN_IDLE_TIMEOUT_MS");
385
- expect(ZCODE_TURN_MAX_TIMEOUT_ENV).toBe("XYZ_ZCODE_TURN_MAX_TIMEOUT_MS");
386
- });
387
- });
388
-
389
- // ============================================================
390
- // ⑥ 迟到 turn.terminal:只记录不改写已落定结果(D5①/S5 归因放宽)
391
- // ============================================================
392
-
393
- describe("⑥ 迟到权威终态只记录不改写(P0-1 D5①/S5)", () => {
394
- it("final-frame 先落定 → 迟到 turn.terminal(status=error) 记入 lastTerminalStatus,落定结果不被改写", async () => {
395
- const m = makeMockChannel({ gateRead: true }); // 挂起 read——制造 runTurn 收尾窗口
396
- const t = startTurn(m.ch);
397
- await flushSetup();
398
- m.emitFinalFrame(t.sessionId, "全文"); // 宽松终态先落定(final-frame 恒 success)
399
- await flushSetup();
400
- expect(m.requests.some((r) => r.method === "session/read")).toBe(true);
401
- m.emitTerminal(undefined, "error"); // 权威终态迟到(telemetry 无 sid——真实形态)
402
- expect(m.readGates).toHaveLength(1);
403
- m.readGates[0]({ messages: [] }); // 放行 read → runTurn resolve
404
- await t.promise;
405
- expect(t.outcome).toBe("fulfilled");
406
- expect(t.result?.terminal).toEqual({ status: "success", source: "final-frame" }); // 不改写
407
- expect(t.result?.lastTerminalStatus).toBe("error"); // 只记录
408
- });
409
-
410
- it("带 sid 的迟到 turn.terminal 同样精确归因到已落定 turn 并记录", async () => {
411
- const m = makeMockChannel({ gateRead: true });
412
- const t = startTurn(m.ch);
413
- await flushSetup();
414
- m.emitFinalFrame(t.sessionId, "全文");
415
- await flushSetup();
416
- m.emitTerminal(t.sessionId, "error"); // 带 sid 形态(归因放宽的精确分支)
417
- m.readGates[0]({ messages: [] });
418
- await t.promise;
419
- expect(t.result?.terminal).toEqual({ status: "success", source: "final-frame" });
420
- expect(t.result?.lastTerminalStatus).toBe("error");
421
- });
422
-
423
- it("turn.terminal 先到(权威先落定):terminal 与 lastTerminalStatus 同值记录", async () => {
424
- const m = makeMockChannel();
425
- const t = startTurn(m.ch);
426
- await flushSetup();
427
- m.emitTerminal(undefined, "error");
428
- await t.promise;
429
- expect(t.result?.terminal).toEqual({ status: "error", source: "turn.terminal" });
430
- expect(t.result?.lastTerminalStatus).toBe("error");
431
- });
432
-
433
- it("多在途宁丢勿错不回退:一落定一在途时迟到 terminal 归因到在途 turn,落定者不记录", async () => {
434
- const m = makeMockChannel({ gateRead: true });
435
- const t1 = startTurn(m.ch);
436
- await flushSetup();
437
- m.emitFinalFrame(t1.sessionId, "全文一"); // t1 落定,read 挂起(在册窗口)
438
- const t2 = startTurn(m.ch);
439
- await flushSetup();
440
- m.emitTerminal(undefined, "success"); // 无 sid:唯一在途 = t2 → 归因 t2
441
- await flushSetup(); // t2 settle 后的 read 请求在微任务里才发出——先推到位再放行
442
- expect(m.readGates).toHaveLength(2);
443
- m.readGates[0]({ messages: [] }); // 放行 t1 的 read
444
- m.readGates[1]({ messages: [] }); // 放行 t2 的 read(gateRead 对两个 turn 都生效)
445
- await t1.promise;
446
- await t2.promise;
447
- expect(t1.result?.terminal).toEqual({ status: "success", source: "final-frame" });
448
- expect(t1.result?.lastTerminalStatus).toBeUndefined(); // 多在途宁丢勿错
449
- expect(t2.result?.terminal).toEqual({ status: "success", source: "turn.terminal" });
450
- });
451
- });