@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,167 +0,0 @@
1
- // src/__tests__/spawn-event-adapter.test.ts
2
- import * as fs from "node:fs";
3
- import * as os from "node:os";
4
- import * as path from "node:path";
5
-
6
- import { describe, expect, it } from "vitest";
7
-
8
- import {
9
- deriveSessionFilePath,
10
- findSessionFileByHeaderId,
11
- parseSpawnLine,
12
- } from "../engine/engines/pi/spawn-event-adapter.ts";
13
-
14
- describe("parseSpawnLine", () => {
15
- describe("空白行", () => {
16
- it("空字符串返回 null", () => {
17
- expect(parseSpawnLine("")).toBeNull();
18
- });
19
-
20
- it("纯空白返回 null", () => {
21
- expect(parseSpawnLine(" ")).toBeNull();
22
- expect(parseSpawnLine("\t\t")).toBeNull();
23
- });
24
- });
25
-
26
- describe("header 行", () => {
27
- it("type=session + id 识别为 header", () => {
28
- const line = JSON.stringify({
29
- type: "session",
30
- id: "abc-123",
31
- timestamp: "2026-07-03T12:00:00.000Z",
32
- cwd: "/home/user/project",
33
- });
34
- const result = parseSpawnLine(line);
35
- expect(result?.kind).toBe("header");
36
- if (result?.kind === "header") {
37
- expect(result.header.id).toBe("abc-123");
38
- expect(result.header.cwd).toBe("/home/user/project");
39
- }
40
- });
41
-
42
- it("header 含 parentSession + version", () => {
43
- const line = JSON.stringify({
44
- type: "session",
45
- id: "child-456",
46
- timestamp: "2026-07-03T12:00:00.000Z",
47
- cwd: "/home/user/project",
48
- parentSession: "parent-789",
49
- version: 2,
50
- });
51
- const result = parseSpawnLine(line);
52
- expect(result?.kind).toBe("header");
53
- if (result?.kind === "header") {
54
- expect(result.header.parentSession).toBe("parent-789");
55
- expect(result.header.version).toBe(2);
56
- }
57
- });
58
- });
59
-
60
- describe("事件行", () => {
61
- it("tool_execution_start 识别为 event", () => {
62
- const line = JSON.stringify({
63
- type: "tool_execution_start",
64
- toolName: "bash",
65
- toolCallId: "call-1",
66
- args: { command: "ls" },
67
- });
68
- const result = parseSpawnLine(line);
69
- expect(result?.kind).toBe("event");
70
- if (result?.kind === "event") {
71
- expect(result.event.type).toBe("tool_execution_start");
72
- expect(result.event.toolName).toBe("bash");
73
- }
74
- });
75
-
76
- it("message_end 识别为 event", () => {
77
- const line = JSON.stringify({
78
- type: "message_end",
79
- message: {
80
- role: "assistant",
81
- usage: { input: 100, output: 50 },
82
- },
83
- });
84
- const result = parseSpawnLine(line);
85
- expect(result?.kind).toBe("event");
86
- if (result?.kind === "event") {
87
- expect(result.event.type).toBe("message_end");
88
- }
89
- });
90
-
91
- it("turn_end 识别为 event", () => {
92
- const result = parseSpawnLine(JSON.stringify({ type: "turn_end" }));
93
- expect(result?.kind).toBe("event");
94
- });
95
-
96
- it("未知 type 仍识别为 event(schema 校验由调用方)", () => {
97
- const result = parseSpawnLine(JSON.stringify({ type: "some_future_event" }));
98
- expect(result?.kind).toBe("event");
99
- });
100
- });
101
-
102
- describe("invalid 行", () => {
103
- it("非 JSON 返回 invalid", () => {
104
- const result = parseSpawnLine("not json at all");
105
- expect(result?.kind).toBe("invalid");
106
- if (result?.kind === "invalid") {
107
- expect(result.raw).toBe("not json at all");
108
- expect(result.error).toBeTruthy();
109
- }
110
- });
111
-
112
- it("JSON 但无 type 字段返回 invalid", () => {
113
- const result = parseSpawnLine(JSON.stringify({ foo: "bar" }));
114
- expect(result?.kind).toBe("invalid");
115
- if (result?.kind === "invalid") {
116
- expect(result.error).toContain("type");
117
- }
118
- });
119
-
120
- it("JSON 但 type 非 string 返回 invalid", () => {
121
- const result = parseSpawnLine(JSON.stringify({ type: 123 }));
122
- expect(result?.kind).toBe("invalid");
123
- });
124
-
125
- it("JSON null 返回 invalid", () => {
126
- const result = parseSpawnLine("null");
127
- expect(result?.kind).toBe("invalid");
128
- });
129
- });
130
- });
131
-
132
- describe("deriveSessionFilePath", () => {
133
- it("拼接 sessionDir + fileTimestamp(冒号点转连字符) + id", () => {
134
- const header = {
135
- type: "session" as const,
136
- id: "abc-123",
137
- timestamp: "2026-07-03T12:00:00.000Z",
138
- cwd: "/proj",
139
- };
140
- const path = deriveSessionFilePath(header, "/sessions/dir");
141
- // fileTimestamp = timestamp.replace(/[:.]/g, "-") = "2026-07-03T12-00-00-000Z"
142
- expect(path).toBe("/sessions/dir/2026-07-03T12-00-00-000Z_abc-123.jsonl");
143
- });
144
- });
145
-
146
- describe("findSessionFileByHeaderId", () => {
147
- it("sessionId 后缀匹配返回实际文件路径", () => {
148
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "spawn-adapter-test-"));
149
- const expectedFile = path.join(tmpDir, "2026-07-03T12-00-00-000Z_sid-456.jsonl");
150
- fs.writeFileSync(expectedFile, "{}");
151
- const result = findSessionFileByHeaderId(tmpDir, "sid-456");
152
- expect(result).toBe(expectedFile);
153
- fs.rmSync(tmpDir, { recursive: true, maxRetries: 5, retryDelay: 20 });
154
- });
155
-
156
- it("无匹配返回 undefined", () => {
157
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "spawn-adapter-test-"));
158
- const result = findSessionFileByHeaderId(tmpDir, "nonexistent");
159
- expect(result).toBeUndefined();
160
- fs.rmSync(tmpDir, { recursive: true, maxRetries: 5, retryDelay: 20 });
161
- });
162
-
163
- it("目录不存在返回 undefined(不抛错)", () => {
164
- const result = findSessionFileByHeaderId("/nonexistent/path/xyz", "sid");
165
- expect(result).toBeUndefined();
166
- });
167
- });
@@ -1,208 +0,0 @@
1
- // src/execution/__tests__/spawn-worktree-guidance.test.ts
2
- //
3
- // worktree 模式认知纠正提示注入测试。
4
- //
5
- // 背景:worktree checkout 放 os.tmpdir()(路径形似临时沙箱),子 agent 无 worktree 语义
6
- // 提示时误判 cwd 为"空隔离目录",主动 cd 别处放弃隔离(wave-agent 事故 session 019ff64c)。
7
- // 修复在 runSpawn 的 appendParts 注入 WORKTREE_GUIDANCE_PROMPT。
8
- //
9
- // 验证:
10
- // - worktree 模式(opts.worktree 传入)→ appendSystemPrompt content 含 WORKTREE_GUIDANCE_PROMPT
11
- // - 非 worktree 模式 → content 不含 worktree 提示
12
- //
13
- // 测试策略:mock writePromptToTempFile 捕获拼接后的 appendParts content(vi.hoisted 防
14
- // vi.mock hoisting 引用未初始化变量),断言含/不含 worktree 提示标识。
15
-
16
- import type { PassThrough } from "node:stream";
17
-
18
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
19
-
20
- // 捕获 writePromptToTempFile 的 content 参数(appendParts.join("\n\n"))。
21
- // vi.hoisted 保证变量在 vi.mock factory hoisting 前已声明可用。
22
- const captured = vi.hoisted(() => ({ content: "" }));
23
-
24
- // ── mock modules(与 session-runner-schema-env.test.ts 同模式)──
25
-
26
- vi.mock("node:child_process", async () => {
27
- const { EventEmitter } = await import("node:events");
28
- const { PassThrough } = await import("node:stream");
29
-
30
- class FakeChild extends EventEmitter {
31
- pid = 12345;
32
- stdout = new PassThrough();
33
- stdin = new PassThrough(); // runSpawn 注册 stdin EPIPE handler(session-runner),FakeChild 必须提供
34
- stderr = new PassThrough();
35
- killed = false;
36
- killSignal: string | undefined;
37
- kill(sig?: string): boolean {
38
- this.killed = true;
39
- this.killSignal = sig;
40
- return true;
41
- }
42
- }
43
-
44
- return {
45
- spawn: vi.fn(() => new FakeChild()),
46
- execFileSync: vi.fn(() => ""),
47
- };
48
- });
49
-
50
- vi.mock("node:fs", async () => {
51
- const actual = await import("node:fs");
52
- return {
53
- default: {
54
- ...actual,
55
- mkdirSync: vi.fn(),
56
- existsSync: vi.fn(() => false),
57
- appendFileSync: vi.fn(),
58
- writeFileSync: vi.fn(),
59
- readdirSync: vi.fn(() => []),
60
- },
61
- mkdirSync: vi.fn(),
62
- existsSync: vi.fn(() => false),
63
- appendFileSync: vi.fn(),
64
- writeFileSync: vi.fn(),
65
- readdirSync: vi.fn(() => []),
66
- promises: actual.promises,
67
- };
68
- });
69
-
70
- vi.mock("../alive-store.ts", () => ({
71
- writeAliveMarker: vi.fn(),
72
- }));
73
-
74
- vi.mock("../engine/engines/pi/temp-prompt.ts", () => ({
75
- writePromptToTempFile: vi.fn(async (agent: string, content: string) => {
76
- captured.content = content;
77
- const safeName = agent.replace(/[^\w.-]+/g, "_");
78
- return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt.md` };
79
- }),
80
- cleanupTempPrompt: vi.fn(async () => {}),
81
- }));
82
-
83
- import { spawn } from "node:child_process";
84
-
85
- import { createRecord } from "../execution-record.ts";
86
- import { runSpawn, type RunOptions, type SessionRunnerContext } from "../engine/engines/pi/session-runner.ts";
87
-
88
- const mockSpawn = vi.mocked(spawn);
89
-
90
- interface FakeChild {
91
- pid: number;
92
- stdout: PassThrough;
93
- stderr: PassThrough;
94
- killed: boolean;
95
- killSignal: string | undefined;
96
- kill(sig?: string): boolean;
97
- emit(event: string, ...args: unknown[]): boolean;
98
- }
99
-
100
- function getLastSpawnedChild(): FakeChild {
101
- const result = mockSpawn.mock.results.at(-1);
102
- if (!result) throw new Error("spawn was not called yet");
103
- return result.value as FakeChild;
104
- }
105
-
106
- async function waitForSpawn(timeoutMs = 1000): Promise<void> {
107
- const start = Date.now();
108
- while (mockSpawn.mock.results.length === 0) {
109
- if (Date.now() - start > timeoutMs) {
110
- throw new Error(`spawn was not called within ${timeoutMs}ms`);
111
- }
112
- await new Promise((r) => setTimeout(r, 5));
113
- }
114
- }
115
-
116
- function makeRecord() {
117
- return createRecord("wt-guidance-1", {
118
- agent: "general-purpose",
119
- model: "test/model",
120
- mode: "background",
121
- task: "test task",
122
- slug: "test",
123
- startedAt: Date.now(),
124
- rootSessionId: "s1",
125
- parentRecordId: undefined,
126
- depth: 0,
127
- });
128
- }
129
-
130
- function makeRunOpts(overrides: Partial<RunOptions> = {}): RunOptions {
131
- return {
132
- resolved: { model: { provider: "test", id: "model", name: "Model", reasoning: false }, thinkingLevel: undefined },
133
- agentConfig: undefined,
134
- appendSystemPrompt: undefined,
135
- skillPath: undefined,
136
- schema: undefined,
137
- maxTurns: undefined,
138
- graceTurns: undefined,
139
- signal: undefined,
140
- onEvent: undefined,
141
- ...overrides,
142
- };
143
- }
144
-
145
- function makeCtx(overrides: Partial<SessionRunnerContext> = {}): SessionRunnerContext {
146
- return {
147
- cwd: "/fake/cwd",
148
- agentDir: "/fake/agent",
149
- skillDirs: [],
150
- mainCwd: "/fake/cwd",
151
- sessionRootId: "root-session-test",
152
- rootCwd: "/fake/cwd",
153
- ...overrides,
154
- };
155
- }
156
-
157
- describe("worktree guidance prompt injection", () => {
158
- beforeEach(() => {
159
- vi.clearAllMocks();
160
- captured.content = "";
161
- });
162
-
163
- afterEach(() => {
164
- vi.restoreAllMocks();
165
- });
166
-
167
- it("worktree 模式 → appendSystemPrompt content 含 worktree 认知纠正提示", async () => {
168
- const record = makeRecord();
169
- const opts = makeRunOpts({
170
- worktree: {
171
- path: "/tmp/pi-subagents/--fake--/pi-sub-sa-wt-guidance-1",
172
- branch: "pi-sub-sa-wt-guidance-1",
173
- baseCommit: "abc123",
174
- mainCwd: "/fake/cwd",
175
- },
176
- });
177
- const ctx = makeCtx();
178
-
179
- const resultPromise = runSpawn(record, "test task", opts, ctx);
180
- await waitForSpawn();
181
-
182
- // content 含 WORKTREE_GUIDANCE_PROMPT 的标题
183
- expect(captured.content).toContain("Git Worktree");
184
- // 含关键纠正信息:cwd 含完整项目代码(非临时沙箱)
185
- expect(captured.content).toContain("complete project source code");
186
- // 含防 cd 别处的行为指引
187
- expect(captured.content).toContain("Do NOT");
188
-
189
- const child = getLastSpawnedChild();
190
- child.emit("close", 0);
191
- await resultPromise;
192
- });
193
-
194
- it("非 worktree 模式 → content 不含 worktree 认知提示", async () => {
195
- const record = makeRecord();
196
- const opts = makeRunOpts(); // 不传 worktree
197
- const ctx = makeCtx();
198
-
199
- const resultPromise = runSpawn(record, "test task", opts, ctx);
200
- await waitForSpawn();
201
-
202
- expect(captured.content).not.toContain("Git Worktree");
203
-
204
- const child = getLastSpawnedChild();
205
- child.emit("close", 0);
206
- await resultPromise;
207
- });
208
- });
@@ -1,236 +0,0 @@
1
- // src/execution/__tests__/spawned-children.test.ts
2
- //
3
- // spawnedChildren Map + getChildByRecord + killAllSpawnedChildren 单元测试(M2-B1)。
4
- //
5
- // M2-B1 把 spawnedChildren 从 Set<ChildProcess> 改为 Map<recordId, ChildProcess>,
6
- // 建立 record→child 映射(busy 投递定位活进程用,设计决策 6)。本文件测 Map API 核心行为:
7
- // - getChildByRecord:set 后能查到;delete 后查不到;未注册返回 undefined
8
- // - killAllSpawnedChildren:遍历 .values() kill + clear;单 child kill 抛错不阻断
9
- // - [R1 D6③] killAllSpawnedChildren 编排扩容:先触发 engine registry dispose(触发不等待),
10
- // 后杀 per-record children——dispose 全部先于任何 kill(D6① 时序)
11
- //
12
- // [T2-⑤ / LC-2] 跳过条件变更(原「跳过 child.killed=true」契约已废止):killed=发过
13
- // kill 请求 ≠ 已死,只有 exitCode/signalCode 非 null(已确认死亡)才跳过;killed 但
14
- // 未确认死亡的 child 补 SIGKILL 升级(SIGTERM 可能被无视)。新契约的完整行为矩阵见
15
- // kill-all-escalation.test.ts(runSpawn 集成面),本文件保留纯 Map 遍历面的正/反例。
16
- //
17
- // 直接操作模块级 spawnedChildren(afterEach clear 防泄漏),不依赖 runSpawn/spawn/fs。
18
-
19
- import type { ChildProcess } from "node:child_process";
20
-
21
- import { afterEach, describe, expect, it, vi } from "vitest";
22
-
23
- const { loggerMock } = vi.hoisted(() => ({
24
- loggerMock: { debug: vi.fn(), warn: vi.fn(), error: vi.fn() },
25
- }));
26
- // mock core logger:session-runner 与 engine/registry 共用同一 facade 模块(两处
27
- // `../../core/logger.ts` 解析到同一文件),本 mock 对二者同时生效。
28
- vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
29
-
30
- import type { EnginePort } from "../engine/port.ts";
31
- import { clearEngines, getEngine, registerEngine } from "../engine/registry.ts";
32
- import { getChildByRecord, killAllSpawnedChildren, spawnedChildren } from "../engine/engines/pi/session-runner.ts";
33
-
34
- /**
35
- * 假 child:只关心 killed 标记 + kill 调用(killAllSpawnedChildren 遍历用)。
36
- * extra 可注入 exitCode/signalCode(模拟已确认死亡的形态)。
37
- */
38
- function makeFakeChild(
39
- killed = false,
40
- extra: Partial<Pick<ChildProcess, "exitCode" | "signalCode">> = {},
41
- ): ChildProcess & { kill: ReturnType<typeof vi.fn> } {
42
- return { killed, kill: vi.fn(), ...extra } as unknown as ChildProcess & {
43
- kill: ReturnType<typeof vi.fn>;
44
- };
45
- }
46
-
47
- /** 带 dispose 的假引擎(编排测试用——dispose 触发顺序记进 events,与 child kill 事件共序)。 */
48
- function makeDisposingEngine(
49
- id: string,
50
- events: string[],
51
- disposeImpl?: () => Promise<void>,
52
- ): EnginePort {
53
- return {
54
- id,
55
- capabilities: () => ({
56
- schemaEnforcement: "native",
57
- steer: "unsupported",
58
- conversation: "unsupported",
59
- personaInjection: "prompt",
60
- eventGranularity: "coarse",
61
- sandbox: "none",
62
- sessionRead: "outcome-only",
63
- resume: "unsupported",
64
- interrupt: "kill-only",
65
- permissionMode: "fixed",
66
- maxTurns: false,
67
- }),
68
- probe: () =>
69
- Promise.resolve({ ok: true, engineVersion: "0.0.0-test", checks: [{ name: "stub", ok: true }] }),
70
- run: () => Promise.reject(new Error("fake engine: run not implemented")),
71
- interact: () => Promise.resolve({ ok: false, code: "engine_capability_unsupported", message: "stub" }),
72
- read: () => Promise.resolve({ engineId: id, turns: [], source: "outcome-only" }),
73
- dispose: disposeImpl ?? (() => {
74
- events.push(`dispose:${id}`);
75
- return Promise.resolve();
76
- }),
77
- };
78
- }
79
-
80
- describe("spawnedChildren Map + getChildByRecord (M2-B1)", () => {
81
- afterEach(() => {
82
- spawnedChildren.clear();
83
- });
84
-
85
- it("set(recordId, child) 后 getChildByRecord 能查到;未注册返回 undefined", () => {
86
- const child = makeFakeChild();
87
- spawnedChildren.set("rec-1", child);
88
- expect(getChildByRecord("rec-1")).toBe(child);
89
- expect(getChildByRecord("rec-2")).toBeUndefined();
90
- });
91
-
92
- it("delete(recordId) 后 getChildByRecord 返回 undefined(close/error 回调语义)", () => {
93
- const child = makeFakeChild();
94
- spawnedChildren.set("rec-1", child);
95
- spawnedChildren.delete("rec-1");
96
- expect(getChildByRecord("rec-1")).toBeUndefined();
97
- });
98
-
99
- it("killAllSpawnedChildren 遍历 .values() 对每个未 killed child kill + clear", () => {
100
- const c1 = makeFakeChild();
101
- const c2 = makeFakeChild();
102
- spawnedChildren.set("rec-a", c1);
103
- spawnedChildren.set("rec-b", c2);
104
-
105
- const n = killAllSpawnedChildren();
106
-
107
- expect(n).toBe(2);
108
- expect(c1.kill).toHaveBeenCalledTimes(1);
109
- expect(c2.kill).toHaveBeenCalledTimes(1);
110
- expect(spawnedChildren.size).toBe(0);
111
- });
112
-
113
- it("[T2-⑤] killed 但未确认死亡 → 不跳过,补 SIGKILL 升级(killed=发过请求≠已死)", () => {
114
- const alive = makeFakeChild(false);
115
- const killedNotClosed = makeFakeChild(true); // SIGTERM 已发、exit/signal 仍 null
116
- spawnedChildren.set("rec-1", alive);
117
- spawnedChildren.set("rec-2", killedNotClosed);
118
-
119
- const n = killAllSpawnedChildren();
120
-
121
- expect(n).toBe(2);
122
- expect(alive.kill).toHaveBeenCalledWith("SIGTERM");
123
- // dispose 是最后兜底,对 killed-not-closed 直接升级 SIGKILL(不再赌 SIGTERM 生效)
124
- expect(killedNotClosed.kill).toHaveBeenCalledWith("SIGKILL");
125
- expect(spawnedChildren.size).toBe(0);
126
- });
127
-
128
- it("[T2-⑤] killed 且已确认死亡(signalCode 非 null)→ 跳过,不重复发信号", () => {
129
- const dead = makeFakeChild(true, { signalCode: "SIGTERM" });
130
- spawnedChildren.set("rec-1", dead);
131
-
132
- const n = killAllSpawnedChildren();
133
-
134
- expect(n).toBe(0);
135
- expect(dead.kill).not.toHaveBeenCalled();
136
- expect(spawnedChildren.size).toBe(0);
137
- });
138
-
139
- it("killAllSpawnedChildren 对单个 child kill 抛错不阻断其他(best-effort catch)", () => {
140
- const ok = makeFakeChild();
141
- const throwy = makeFakeChild();
142
- throwy.kill.mockImplementation(() => {
143
- throw new Error("ESRCH");
144
- });
145
- spawnedChildren.set("rec-1", ok);
146
- spawnedChildren.set("rec-2", throwy);
147
-
148
- const n = killAllSpawnedChildren();
149
-
150
- // throwy 抛错被 catch,不计入 n;ok 正常 kill。两者都调过 kill,Map 已 clear。
151
- expect(n).toBe(1);
152
- expect(ok.kill).toHaveBeenCalledTimes(1);
153
- expect(throwy.kill).toHaveBeenCalledTimes(1);
154
- expect(spawnedChildren.size).toBe(0);
155
- });
156
- });
157
-
158
- describe("killAllSpawnedChildren 编排扩容(R1 D6③:先引擎 dispose 后 per-record children)", () => {
159
- afterEach(() => {
160
- spawnedChildren.clear();
161
- // registry 是进程级全局状态(Symbol.for 槽),防用例间 fake 引擎泄漏串扰
162
- clearEngines();
163
- });
164
-
165
- it("先全部触发已实例化引擎 dispose,后遍历杀 children(D6① 时序:dispose 先于 SIGTERM)", () => {
166
- const events: string[] = [];
167
- registerEngine("e-alpha", () => makeDisposingEngine("e-alpha", events));
168
- registerEngine("e-beta", () => makeDisposingEngine("e-beta", events));
169
- getEngine("e-alpha"); // disposeEngines 只遍历已实例化单例,须先实例化
170
- getEngine("e-beta");
171
-
172
- const c1 = makeFakeChild();
173
- const c2 = makeFakeChild();
174
- c1.kill.mockImplementation(() => {
175
- events.push("kill:rec-a");
176
- });
177
- c2.kill.mockImplementation(() => {
178
- events.push("kill:rec-b");
179
- });
180
- spawnedChildren.set("rec-a", c1);
181
- spawnedChildren.set("rec-b", c2);
182
-
183
- const n = killAllSpawnedChildren();
184
-
185
- expect(n).toBe(2);
186
- // 时序断言(不依赖穿插序):最后一个 dispose 的索引 < 第一个 kill 的索引
187
- const disposeIdx = events
188
- .map((e, i) => (e.startsWith("dispose:") ? i : -1))
189
- .filter((i) => i >= 0);
190
- const firstKill = events.findIndex((e) => e.startsWith("kill:"));
191
- expect(disposeIdx.length).toBe(2);
192
- expect(firstKill).toBeGreaterThan(Math.max(...disposeIdx));
193
- expect(spawnedChildren.size).toBe(0);
194
- });
195
-
196
- it("引擎 dispose 同步 throw 不阻断 children 清理(best-effort 编排,catch 记日志继续)", () => {
197
- const events: string[] = [];
198
- registerEngine(
199
- "throwy",
200
- () =>
201
- makeDisposingEngine("throwy", events, () => {
202
- throw new Error("dispose boom");
203
- }),
204
- );
205
- getEngine("throwy");
206
- const child = makeFakeChild();
207
- child.kill.mockImplementation(() => {
208
- events.push("kill:rec-1");
209
- });
210
- spawnedChildren.set("rec-1", child);
211
-
212
- expect(() => killAllSpawnedChildren()).not.toThrow();
213
- expect(child.kill).toHaveBeenCalledTimes(1);
214
- expect(spawnedChildren.size).toBe(0);
215
- expect(loggerMock.warn).toHaveBeenCalledWith(
216
- expect.stringContaining("[engine-registry] engine 'throwy' dispose threw synchronously"),
217
- );
218
- });
219
-
220
- it("引擎仅注册工厂未实例化 → 不触发 dispose 也不实例化(停机路径不反向创建资源)", () => {
221
- const events: string[] = [];
222
- const engine = makeDisposingEngine("unused", events);
223
- registerEngine("unused", () => engine);
224
- const child = makeFakeChild();
225
- child.kill.mockImplementation(() => {
226
- events.push("kill:rec-1");
227
- });
228
- spawnedChildren.set("rec-1", child);
229
-
230
- const n = killAllSpawnedChildren();
231
-
232
- expect(n).toBe(1);
233
- expect(events).toEqual(["kill:rec-1"]); // 无 dispose 事件混入
234
- expect(child.kill).toHaveBeenCalledTimes(1);
235
- });
236
- });