@zhushanwen/subagent-core 0.4.0 → 0.6.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 (309) hide show
  1. package/dist/{chunk-4KN4TTG7.js → chunk-43ONBFZX.js} +1 -1
  2. package/dist/{chunk-X7SZ5HLQ.js → chunk-V3VHZ2VX.js} +20 -27
  3. package/dist/execution/engine/engines/zcode/constants.cjs +29 -41
  4. package/dist/execution/engine/engines/zcode/constants.d.cts +65 -53
  5. package/dist/execution/engine/engines/zcode/constants.d.ts +65 -53
  6. package/dist/execution/engine/engines/zcode/constants.js +17 -27
  7. package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
  8. package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
  9. package/dist/execution/engine/engines/zcode/reader.js +2 -2
  10. package/dist/index.cjs +17100 -14082
  11. package/dist/index.d.cts +5336 -4033
  12. package/dist/index.d.ts +5336 -4033
  13. package/dist/index.js +17020 -14055
  14. package/dist/{types-DpUO16pj.d.cts → types-C3fE3R9x.d.cts} +280 -112
  15. package/dist/{types-DpUO16pj.d.ts → types-C3fE3R9x.d.ts} +280 -112
  16. package/dist.bundle/index.cjs +34933 -0
  17. package/package.json +3 -3
  18. package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
  19. package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
  20. package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
  21. package/src/__tests__/m2-append-content-probe.test.ts +0 -1
  22. package/src/__tests__/manifest-store.test.ts +10 -9
  23. package/src/__tests__/record-store-cache.test.ts +0 -2
  24. package/src/__tests__/record-store-index.test.ts +1 -2
  25. package/src/__tests__/review-fix-loop-script.test.ts +4 -2
  26. package/src/__tests__/review-fix-loop-utils.test.ts +10 -10
  27. package/src/__tests__/robustness-low-batch1.test.ts +6 -5
  28. package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
  29. package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
  30. package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
  31. package/src/__tests__/session-runner.test.ts +1 -1
  32. package/src/core/__tests__/host-services.test.ts +2 -2
  33. package/src/core/__tests__/logger.test.ts +1 -1
  34. package/src/core/error-message.ts +9 -0
  35. package/src/core/host-services.ts +21 -5
  36. package/src/core/notify-ports.ts +18 -5
  37. package/src/execution/__tests__/agent-profile.test.ts +4 -4
  38. package/src/execution/__tests__/agent-registry.test.ts +370 -0
  39. package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
  40. package/src/execution/__tests__/agents-assembly.test.ts +2 -2
  41. package/src/execution/__tests__/alive-store.test.ts +1 -1
  42. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
  43. package/src/execution/__tests__/chat-engine-routing.test.ts +68 -33
  44. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +4 -4
  45. package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
  46. package/src/execution/__tests__/collect-budget.test.ts +291 -0
  47. package/src/execution/__tests__/collect-coordinator-service.test.ts +327 -0
  48. package/src/execution/__tests__/collect-coordinator.test.ts +371 -0
  49. package/src/execution/__tests__/collect-mixed-dispatch.test.ts +262 -0
  50. package/src/execution/__tests__/config-collect-sync.test.ts +120 -0
  51. package/src/execution/__tests__/config.test.ts +1 -1
  52. package/src/execution/__tests__/delivery-methods.test.ts +75 -62
  53. package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
  54. package/src/execution/__tests__/descendant-sweep.test.ts +6 -6
  55. package/src/execution/__tests__/dialog-queue.test.ts +2 -2
  56. package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
  57. package/src/execution/__tests__/epipe-fallback.test.ts +29 -24
  58. package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
  59. package/src/execution/__tests__/execute-nesting.test.ts +34 -188
  60. package/src/execution/__tests__/execution-record.test.ts +217 -60
  61. package/src/execution/__tests__/execution-runtime-face.test.ts +5 -5
  62. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +4 -4
  63. package/src/execution/__tests__/finalize-record.test.ts +173 -17
  64. package/src/execution/__tests__/finalized-marker.test.ts +1 -1
  65. package/src/execution/__tests__/gc-timer.test.ts +56 -3
  66. package/src/execution/__tests__/get-record-for-action-restart.test.ts +12 -12
  67. package/src/execution/__tests__/get-state-handshake.test.ts +1 -1
  68. package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
  69. package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
  70. package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
  71. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
  72. package/src/execution/__tests__/keep-alive-no-progress.test.ts +50 -148
  73. package/src/execution/__tests__/kill-all-escalation.test.ts +2 -2
  74. package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
  75. package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
  76. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +1 -1
  77. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +1 -1
  78. package/src/execution/__tests__/model-resolver.test.ts +2 -2
  79. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
  80. package/src/execution/__tests__/nested-visibility.test.ts +2 -0
  81. package/src/execution/__tests__/notify-batch.test.ts +500 -0
  82. package/src/execution/__tests__/notify-ledger.test.ts +1000 -18
  83. package/src/execution/__tests__/output-collector.test.ts +117 -6
  84. package/src/execution/__tests__/pi-invocation.test.ts +36 -4
  85. package/src/execution/__tests__/record-entry-collect.test.ts +102 -0
  86. package/src/execution/__tests__/record-store.test.ts +10 -9
  87. package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
  88. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
  89. package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
  90. package/src/execution/__tests__/relay-env.test.ts +12 -1
  91. package/src/execution/__tests__/rpc-mode.test.ts +2 -2
  92. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
  93. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +15 -9
  94. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +4 -4
  95. package/src/execution/__tests__/run-spawn-edges.test.ts +17 -15
  96. package/src/execution/__tests__/run-spawn-integration.test.ts +2 -2
  97. package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
  98. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
  99. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +4 -4
  100. package/src/execution/__tests__/service-kill-escalation.test.ts +7 -5
  101. package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
  102. package/src/execution/__tests__/session-file-gc.test.ts +36 -1
  103. package/src/execution/__tests__/session-pending.test.ts +257 -14
  104. package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
  105. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +1 -1
  106. package/src/execution/__tests__/session-runner-close-prune.test.ts +2 -2
  107. package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
  108. package/src/execution/__tests__/session-runner-epipe.test.ts +3 -3
  109. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +5 -4
  110. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
  111. package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
  112. package/src/execution/__tests__/settled-watchdog.test.ts +282 -47
  113. package/src/execution/__tests__/spawn-args.test.ts +11 -10
  114. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
  115. package/src/execution/__tests__/spawn-event-adapter.test.ts +3 -3
  116. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
  117. package/src/execution/__tests__/spawned-children.test.ts +2 -1
  118. package/src/execution/__tests__/start-collect-guard.test.ts +255 -0
  119. package/src/execution/__tests__/start-sync-model-guard.test.ts +5 -5
  120. package/src/execution/__tests__/stdin-writer.test.ts +6 -4
  121. package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
  122. package/src/execution/__tests__/subagent-actions-core.test.ts +48 -22
  123. package/src/execution/__tests__/subagent-service-message-close.test.ts +29 -26
  124. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +21 -8
  125. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +27 -22
  126. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +30 -29
  127. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +45 -20
  128. package/src/execution/__tests__/subagent-service.test.ts +37 -30
  129. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
  130. package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
  131. package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
  132. package/src/execution/__tests__/sync-collect-recovery.test.ts +1117 -0
  133. package/src/execution/__tests__/temp-prompt.test.ts +1 -1
  134. package/src/execution/__tests__/timeout-integration.test.ts +6 -5
  135. package/src/execution/__tests__/tombstone-store.test.ts +1 -1
  136. package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
  137. package/src/execution/__tests__/turn-limiter.test.ts +1 -1
  138. package/src/execution/__tests__/ui-request-handler-factory.test.ts +12 -6
  139. package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
  140. package/src/execution/__tests__/ui-request-queue.test.ts +142 -2
  141. package/src/execution/__tests__/worktree-git-ops.test.ts +1 -1
  142. package/src/execution/__tests__/worktree-manager.test.ts +14 -5
  143. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
  144. package/src/execution/__tests__/worktree-reconcile-aging.test.ts +2 -2
  145. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +1 -1
  146. package/src/execution/__tests__/worktree-registry.test.ts +1 -1
  147. package/src/execution/agent-registry.ts +74 -30
  148. package/src/execution/agent-result-mapper.ts +3 -0
  149. package/src/execution/cold-resurrect.ts +199 -0
  150. package/src/execution/collect-coordinator.ts +200 -0
  151. package/src/execution/config.ts +77 -5
  152. package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
  153. package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
  154. package/src/execution/engine/__tests__/common/event-journal.test.ts +1 -1
  155. package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
  156. package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
  157. package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
  158. package/src/execution/engine/__tests__/common/pool-manager.test.ts +213 -53
  159. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
  160. package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
  161. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +22 -112
  162. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +15 -28
  163. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +1 -1
  164. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +12 -12
  165. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +2 -2
  166. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +21 -82
  167. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +4 -7
  168. package/src/execution/engine/__tests__/engine-discovery.test.ts +15 -3
  169. package/src/execution/engine/__tests__/model-prompt.test.ts +15 -3
  170. package/src/execution/engine/__tests__/registry.test.ts +4 -2
  171. package/src/execution/engine/__tests__/routing.test.ts +115 -6
  172. package/src/execution/engine/common/capability-gate.ts +92 -0
  173. package/src/execution/engine/common/journal-wiring.ts +97 -0
  174. package/src/execution/engine/common/kill-chain.ts +42 -10
  175. package/src/execution/engine/common/nesting-guard.ts +66 -0
  176. package/src/execution/engine/common/persona-router.ts +18 -8
  177. package/src/execution/engine/common/pool-manager.ts +332 -78
  178. package/src/execution/engine/common/session-view-service.ts +506 -0
  179. package/src/execution/engine/common/session-view-types.ts +125 -0
  180. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
  181. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
  182. package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
  183. package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +1 -1
  184. package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
  185. package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
  186. package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
  187. package/src/execution/engine/engines/pi/reader.ts +11 -0
  188. package/src/execution/engine/engines/pi/registration.ts +4 -2
  189. package/src/execution/{session-runner.ts → engine/engines/pi/session-runner.ts} +617 -412
  190. package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
  191. package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
  192. package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
  193. package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
  194. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +54 -27
  195. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
  196. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +13 -12
  197. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
  198. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
  199. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
  200. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
  201. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +20 -8
  202. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +77 -110
  203. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +26 -320
  204. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
  205. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
  206. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
  207. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
  208. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +48 -148
  209. package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
  210. package/src/execution/engine/engines/zcode/connection.ts +24 -11
  211. package/src/execution/engine/engines/zcode/constants.ts +75 -71
  212. package/src/execution/engine/engines/zcode/golden-sample.ts +8 -46
  213. package/src/execution/engine/engines/zcode/parser.ts +12 -346
  214. package/src/execution/engine/engines/zcode/preparer.ts +12 -143
  215. package/src/execution/engine/engines/zcode/session-channel.ts +351 -39
  216. package/src/execution/engine/engines/zcode/zcode-engine.ts +665 -760
  217. package/src/execution/engine/host-task-spec.ts +32 -36
  218. package/src/execution/engine/model-validation.ts +177 -0
  219. package/src/execution/engine/port.ts +24 -6
  220. package/src/execution/engine/routing.ts +75 -1
  221. package/src/execution/engine/types.ts +30 -103
  222. package/src/execution/execution-record.ts +229 -144
  223. package/src/execution/finalize-record.ts +141 -99
  224. package/src/execution/idle-gc.ts +28 -0
  225. package/src/execution/lifecycle-predicates.ts +1 -1
  226. package/src/execution/notifier.ts +234 -25
  227. package/src/execution/notify-host.ts +232 -0
  228. package/src/execution/record-entry.ts +16 -0
  229. package/src/execution/record-store.ts +280 -103
  230. package/src/execution/round-settlement.ts +93 -0
  231. package/src/execution/session-file-gc.ts +96 -62
  232. package/src/execution/session-reconstructor.ts +256 -143
  233. package/src/execution/sessions-index.ts +117 -47
  234. package/src/execution/settled-watchdog.ts +264 -52
  235. package/src/execution/subagent-actions-core.ts +84 -28
  236. package/src/execution/subagent-service.ts +1225 -964
  237. package/src/execution/subprocess-agent-runner.ts +144 -97
  238. package/src/execution/sync-rebuild.ts +87 -0
  239. package/src/execution/types.ts +72 -0
  240. package/src/execution/ui-request-handler-factory.ts +102 -74
  241. package/src/execution/ui-request-queue.ts +50 -19
  242. package/src/index.ts +442 -316
  243. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +1 -1
  244. package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
  245. package/src/orchestration/__tests__/args-validator.test.ts +14 -0
  246. package/src/orchestration/__tests__/config-loader.test.ts +1 -1
  247. package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +1 -1
  248. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +11 -9
  249. package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
  250. package/src/orchestration/__tests__/file-run-store-prune.test.ts +1 -1
  251. package/src/orchestration/__tests__/file-run-store-throttle.test.ts +1 -1
  252. package/src/orchestration/__tests__/file-run-store.test.ts +7 -6
  253. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +2 -2
  254. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +1 -1
  255. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
  256. package/src/orchestration/__tests__/lifecycle.test.ts +7 -5
  257. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
  258. package/src/orchestration/__tests__/review-fix-loop-scriptpath-failfast.test.ts +1 -1
  259. package/src/orchestration/__tests__/run-snapshot.test.ts +8 -5
  260. package/src/orchestration/__tests__/script-generate.test.ts +5 -5
  261. package/src/orchestration/__tests__/script-lint.test.ts +17 -0
  262. package/src/orchestration/__tests__/skill-discovery.test.ts +2 -2
  263. package/src/orchestration/__tests__/test-mocks.ts +2 -0
  264. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
  265. package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
  266. package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +27 -19
  267. package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
  268. package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
  269. package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
  270. package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +1 -1
  271. package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
  272. package/src/orchestration/__tests__/workflow-files.test.ts +10 -10
  273. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
  274. package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +1 -1
  275. package/src/orchestration/agent-opts-resolver.ts +1 -1
  276. package/src/orchestration/args-validator.ts +53 -31
  277. package/src/orchestration/execute-agent-call.ts +19 -73
  278. package/src/orchestration/launcher.ts +128 -81
  279. package/src/orchestration/lifecycle.ts +43 -45
  280. package/src/orchestration/models/ports.ts +4 -4
  281. package/src/orchestration/models/run-runtime.ts +2 -2
  282. package/src/orchestration/models/trace.ts +1 -1
  283. package/src/orchestration/models/types.ts +111 -22
  284. package/src/orchestration/models/workflow-run.ts +1 -1
  285. package/src/orchestration/script-lint.ts +91 -58
  286. package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +157 -150
  287. package/src/shared/__tests__/atomic-write.test.ts +1 -1
  288. package/src/shared/__tests__/meta-parser.test.ts +151 -0
  289. package/src/shared/__tests__/resource-discovery-host-roots.test.ts +5 -5
  290. package/src/shared/__tests__/resource-discovery.test.ts +258 -215
  291. package/src/shared/atomic-write.ts +12 -9
  292. package/src/shared/meta-parser.ts +158 -84
  293. package/src/shared/model-ref.ts +15 -2
  294. package/src/shared/resource-discovery.ts +9 -183
  295. package/src/shared/schema-jsonify.ts +1 -1
  296. package/workflows/review-fix-loop-utils.cjs +151 -89
  297. package/workflows/review-fix-loop-utils.d.cts +287 -0
  298. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  299. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  300. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  301. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  302. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  303. package/src/execution/engine/engines/zcode/__tests__/preparer-appserver.test.ts +0 -387
  304. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -710
  305. package/src/execution/engine/engines/zcode/appserver-home.ts +0 -442
  306. package/src/execution/engine/engines/zcode/appserver-probe.ts +0 -141
  307. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  308. package/src/execution/execute-options-mapper.ts +0 -115
  309. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
@@ -1,161 +0,0 @@
1
- // src/execution/engine/engines/zcode/launcher.ts
2
- //
3
- // ZcodeEngine launcher(P3):spawn 命令组装 + 进程启动——四件套里唯一持有 spawn 权
4
- // 的模块(设计 §3.3.7)。TS 重写自 zsub driver.js 的 runHeadless 命令组装段,机制
5
- // 保留(真机验证过):
6
- // - argv 形态:`node <zcode.cjs> --json --cwd <dir> --mode yolo [--disallowed-tools a,b]
7
- // [--resume <sid>] --prompt <text>`;flag 拒收清单(--model/--skill/--max-turns 等
8
- // help 漂移项)一个不用;
9
- // - stdin: 'ignore'(CLI 不读 stdin,置管道会挂起——zsub 实测);
10
- // - env:HOME=隔离池目录(provider 配置与 db 全部落池内)+ 嵌套防护标记;
11
- // - 杀链:SIGTERM → grace → SIGKILL,exited 用 close 事件维护的标志判断
12
- // (child.killed 在 kill() 调用后恒 true,zsub 修过的原版死代码 bug)。
13
- //
14
- // 与 zsub 的差异:超时不在本模块(宿主公共杀链职责,设计 D4/附录 A「CLI 超时 6/6
15
- // 全缺全补」);abort 由 AbortSignal 驱动(EnginePort 契约),zsub 的 cancel() 变为
16
- // abort(graceMs)。
17
-
18
- import { spawn, type ChildProcess } from "node:child_process";
19
- import { Readable } from "node:stream";
20
-
21
- import { killChain } from "../../common/kill-chain.ts";
22
- import { buildNestedSpawnEnv } from "../../common/nesting-guard.ts";
23
- import { assertArgvBudget } from "../../common/persona-router.ts";
24
- import { ZCODE_KILL_GRACE_MS } from "./constants.ts";
25
-
26
- // ============================================================
27
- // argv 组装与字节预算(对齐点②收口:估算/断言单一权威 = common/persona-router,
28
- // 本模块只组装;引擎侧在 launch 前调 assertArgvBudget)
29
- // ============================================================
30
-
31
- export interface ZcodeSpawnSpec {
32
- /** 任务运行目录(--cwd flag 的值)。 */
33
- cwd: string;
34
- /** 完整 prompt(persona 拼接后的产物)。 */
35
- prompt: string;
36
- /** 工具 denylist(逗号连接进 --disallowed-tools;空数组不加 flag)。 */
37
- denyTools?: string[];
38
- /** 冷续聊目标 session(--resume;spawn 单轮模式内不做 resume 仿真,留给宿主)。 */
39
- resumeSessionId?: string;
40
- }
41
-
42
- /** 组装 zcode.cjs 的 argv(不含 `node` 与 cli 路径两个前导元素)。 */
43
- export function buildZcodeArgv(spec: ZcodeSpawnSpec): string[] {
44
- const args = ["--json", "--cwd", spec.cwd, "--mode", "yolo"];
45
- // denylist 硬约束:非空数组才落 flag(CLI 对空值行为未定义,不赌——zsub 同判)
46
- const disallowed = (spec.denyTools ?? []).filter((t) => typeof t === "string" && t.trim() !== "");
47
- if (disallowed.length > 0) args.push("--disallowed-tools", disallowed.join(","));
48
- if (spec.resumeSessionId) args.push("--resume", String(spec.resumeSessionId));
49
- args.push("--prompt", String(spec.prompt));
50
- return args;
51
- }
52
-
53
- /**
54
- * argv 预算断言(launch 前调用;对齐点②:公共 persona-router 的单一权威实现,
55
- * DEFAULT_ARGV_BUDGET_BYTES 128KB 阈值同源——超限抛 EngineError(prompt_too_large),
56
- * 进程创建前拦截,禁止 spawn 后撞 E2BIG)。
57
- */
58
- export function assertZcodeArgvBudget(nodeBin: string, cliPath: string, args: string[]): void {
59
- assertArgvBudget([nodeBin, cliPath, ...args]);
60
- }
61
-
62
- // ============================================================
63
- // env 构造
64
- // ============================================================
65
-
66
- /**
67
- * 构造子进程 env:嵌套防护经公共 nesting-guard(D8:注入统一 XYZ_AGENT_SUBAGENT=1 +
68
- * 剥离引擎原生嵌套标记 ZSW_NESTED/PI_SUBAGENT_*——旧标记继承会让孙代理误判嵌套层),
69
- * HOME 最后落(隔离 HOME 是 provider 配置与 db 的定位锚,基 env 同名键不许覆盖)。
70
- */
71
- export function buildZcodeEnv(homeDir: string, baseEnv: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {
72
- return { ...buildNestedSpawnEnv(baseEnv), HOME: homeDir };
73
- }
74
-
75
- // ============================================================
76
- // 进程启动(EngineProcess 形态,设计 §3.3.7)
77
- // ============================================================
78
-
79
- /** launcher 产出的进程句柄(parser 消费 stdout/stderr/exited;abort 是杀链执行体)。 */
80
- export interface ZcodeLaunchedProcess {
81
- /** spawn 出的原始子进程句柄([U0 D10] 终止链记账——引擎经 RunContext.onChildSpawned 注册进宿主 spawnedChildren)。 */
82
- readonly child: ChildProcess;
83
- readonly pid: number;
84
- readonly stdout: Readable;
85
- readonly stderr: Readable;
86
- /** 杀链:SIGTERM → graceMs 后未退出则 SIGKILL;resolve 于进程退出。幂等。 */
87
- readonly abort: (graceMs?: number) => Promise<void>;
88
- /** 进程退出(code=null 表示被信号杀死)。 */
89
- readonly exited: Promise<{ code: number | null; signal: string | undefined }>;
90
- /** 本方杀链是否介入过(合成终态的判据:介入后 code 语义不再是引擎自身失败)。 */
91
- readonly killedByUs: () => boolean;
92
- }
93
-
94
- export interface ZcodeLaunchOptions {
95
- /** zcode CLI 路径(node 脚本)。 */
96
- cliPath: string;
97
- /** cliPath 之后的 argv(buildZcodeArgv 产物)。 */
98
- args: string[];
99
- /** 完整 env(buildZcodeEnv 产物)。 */
100
- env: NodeJS.ProcessEnv;
101
- /** node 二进制(缺省 'node',走 PATH)。 */
102
- nodeBin?: string;
103
- }
104
-
105
- /**
106
- * 启动 zcode 子进程。spawn 同步失败(如 node 不存在)直接 throw——调用方(引擎 run)
107
- * 按进程创建前错误处理;启动成功后的一切失败归 parser 终态路径(不 throw)。
108
- *
109
- * 杀链(对齐点②收口):SIGTERM → grace → SIGKILL 的实现单一权威 =
110
- * common/kill-chain.killChain(ChildProcess 结构满足 KillableChild)——本模块只保留
111
- * killedByUs 标志(合成终态判据:「我方介入过」与「引擎自身失败」的区分)与幂等守卫。
112
- */
113
- export function launchZcodeProcess(opts: ZcodeLaunchOptions): ZcodeLaunchedProcess {
114
- const nodeBin = opts.nodeBin ?? "node";
115
- const child = spawn(nodeBin, [opts.cliPath, ...opts.args], {
116
- env: opts.env,
117
- stdio: ["ignore", "pipe", "pipe"],
118
- });
119
-
120
- let exited = false;
121
- let killTriggered = false;
122
-
123
- // ENOENT(node 缺失)等 spawn 失败经 'error' 事件异步到达且 stdout/stderr 可能为
124
- // null——空流兜底,保证消费方(parser)始终拿到可读流
125
- const emptyStream = (): Readable => Readable.from([]);
126
- const stdoutStream = child.stdout ?? emptyStream();
127
- const stderrStream = child.stderr ?? emptyStream();
128
-
129
- const exitedPromise = new Promise<{ code: number | null; signal: string | undefined }>((resolve) => {
130
- child.once("close", (code, signal) => {
131
- exited = true;
132
- resolve({ code, signal: signal ?? undefined });
133
- });
134
- child.once("error", () => {
135
- // spawn 失败(ENOENT 等)也会走 error;close 不一定触发,这里兜底 resolve 防
136
- // exited 永挂——code 取 null(无引擎语义可判)
137
- exited = true;
138
- resolve({ code: null, signal: undefined });
139
- });
140
- });
141
-
142
- const abort = (graceMs: number = ZCODE_KILL_GRACE_MS): Promise<void> => {
143
- if (!killTriggered && !exited) {
144
- killTriggered = true;
145
- // 公共杀链(不 await:abort 以 exitedPromise 为准;killChain 自身含 grace/
146
- // SIGKILL/收尸全时序,fire-and-forget 不改变 abort 的 resolve 语义)
147
- void killChain(child, { graceMs });
148
- }
149
- return exitedPromise.then(() => undefined);
150
- };
151
-
152
- return {
153
- child,
154
- pid: child.pid ?? -1,
155
- stdout: stdoutStream,
156
- stderr: stderrStream,
157
- abort,
158
- exited: exitedPromise,
159
- killedByUs: () => killTriggered,
160
- };
161
- }
@@ -1,115 +0,0 @@
1
- // src/execution/execute-options-mapper.ts
2
- //
3
- // D-A2: AgentCallOpts → ExecuteOptions 映射(adapter 职责)
4
- // D-A9: per-call timeoutMs 合并进 AbortSignal
5
- //
6
- // 接线层级:[模块内直调] —— SAR.run 内调。
7
-
8
- import type { AgentCallOpts } from "../orchestration/models/types.ts";
9
- import { HOST_TIMEOUT_ABORT_REASON } from "./engine/common/kill-chain.ts";
10
- import type { ModelInfo } from "./model-resolver.ts";
11
- import type { ExecuteOptions } from "./types.ts";
12
-
13
- // SLUG_MAX_LENGTH 权威定义(subagent-core 包抽离 u1-move 内化):core 切面不得反向
14
- // import 壳侧 interface(D1 依赖闭包判据),常量定义回归 execution 侧本文件——
15
- // 与 tool schema 的 maxLength 语义同源(壳侧 subagent-tool-schema.ts 改经本文件 import)。
16
- /**
17
- * slug 最大长度。历史值 20 偏紧——描述性 slug 如 "audit-structured-output"(23)/
18
- * "fix-subagent-wf-tools"(21)会撞上限,放宽到 35 兼顾「短到能塞进 TUI 标题行」
19
- * 与「容纳合理描述性 kebab-case 名」。
20
- */
21
- export const SLUG_MAX_LENGTH = 35;
22
-
23
- /**
24
- * D-A2: AgentCallOpts → ExecuteOptions 映射。
25
- *
26
- * adapter 职责——SubagentService 的 ExecuteOptions 是稳定内部契约,不为 workflow 的
27
- * AgentCallOpts 做适配(映射归调用方 SAR)。
28
- *
29
- * 映射规则:
30
- * prompt → task
31
- * description → slug(≤35 字符,超长截断。缺失时回落 agent 名)
32
- * agent → agent
33
- * schema → schema
34
- * schemaEnv → schemaEnv(D-A6 bridge)
35
- * cwd → cwd
36
- * model → model(显式 override 透传,ctxModel 不再压扁为 id 填底)
37
- * ctxModel → ctxModel(完整 ModelInfo 对象透传,让 resolveModel 走第 3 层透明传递)
38
- * skillPath → skillPath
39
- * thinkingLevel → thinkingLevel(M1: 否则下游 subagent-service 读到 undefined)
40
- * appendSystemPrompt → appendSystemPrompt(内容数组,同名同义透传)
41
- * maxTurns → maxTurns(turn limiter 上限;undefined = 不限,不挂 turns 估算 watchdog)
42
- *
43
- * 忽略字段(委托后由 executeAndAwait 内部机制替代):
44
- * timeoutMs —— mergeTimeoutSignal 单独处理
45
- * scene —— subagents 不消费
46
- *
47
- * description 原先被忽略,现作为 slug 透传(ExecuteOptions.slug 必填)。
48
- */
49
- export function mapToExecuteOptions(
50
- opts: AgentCallOpts,
51
- ctxModel?: ModelInfo,
52
- ): ExecuteOptions {
53
- // slug:优先 description,缺失回落 agent 名(保证非空)。超长截断。
54
- const rawSlug = opts.description ?? opts.agent ?? "workflow-agent";
55
- return {
56
- task: opts.prompt,
57
- slug: rawSlug.length > SLUG_MAX_LENGTH ? rawSlug.slice(0, SLUG_MAX_LENGTH) : rawSlug,
58
- agent: opts.agent,
59
- schema: opts.schema,
60
- schemaEnv: opts.schemaEnv,
61
- cwd: opts.cwd,
62
- fork: opts.fork,
63
- worktree: opts.worktree,
64
- model: opts.model,
65
- ctxModel,
66
- skillPath: opts.skillPath,
67
- thinkingLevel: opts.thinkingLevel,
68
- appendSystemPrompt: opts.appendSystemPrompt,
69
- maxTurns: opts.maxTurns,
70
- };
71
- }
72
-
73
- /**
74
- * D-A9: per-call timeoutMs 合并进 AbortSignal。
75
- *
76
- * 墙钟 timeoutMs(per-call)+ 外部 signal(run 级 abort)都生效。
77
- * 缺此合并则 agent({timeoutMs:5000}) 静默无效(BC-9)。
78
- *
79
- * @param signal 外部 signal(workflow run 级 controller.signal)
80
- * @param timeoutMs per-call 墙钟超时;undefined/<=0 → 不设超时,原样返回 signal
81
- * @returns 合并后的 signal(timeoutMs 或外部 signal 任一 abort 都触发)
82
- */
83
- export function mergeTimeoutSignal(
84
- signal: AbortSignal,
85
- timeoutMs?: number,
86
- ): AbortSignal {
87
- if (!timeoutMs || timeoutMs <= 0) {
88
- return signal;
89
- }
90
-
91
- const controller = new AbortController();
92
- // 超时 abort 带 reason 标记(对齐点④):引擎合成终态时判别「宿主超时」
93
- // (engine_timeout 公共合成)vs「外部 cancel」(中止标记)——pi 链路不读 reason,
94
- // 行为不变。外部 signal abort 不带标记(用户/编排层 cancel 语义)。
95
- const timer = setTimeout(() => controller.abort(HOST_TIMEOUT_ABORT_REASON), timeoutMs);
96
- timer.unref();
97
-
98
- const onExternalAbort = (): void => controller.abort();
99
- if (signal.aborted) {
100
- controller.abort();
101
- } else {
102
- signal.addEventListener("abort", onExternalAbort, { once: true });
103
- }
104
-
105
- controller.signal.addEventListener(
106
- "abort",
107
- () => {
108
- clearTimeout(timer);
109
- if (!signal.aborted) signal.removeEventListener("abort", onExternalAbort);
110
- },
111
- { once: true },
112
- );
113
-
114
- return controller.signal;
115
- }
@@ -1,288 +0,0 @@
1
- // src/shared/__tests__/resource-discovery-manifest-cache.test.ts
2
- //
3
- // manifestCache 专项测试(swf-perf-impl cleanup IF1/DM1/TC1)。
4
- // readPackageManifestSync / async readPackageManifest 共享同一 Map:
5
- // - 命中:同 mtime 二次调用不重读(readFileSync 计数不增)
6
- // - 失效:package.json 重写(mtime 变)→ 重读
7
- // - 坏 JSON:不缓存(mtime 未变时二次调用仍重新 read)、不驱逐已有好条目
8
- // (恢复原内容 + utimesSync 恢复原 mtime → 命中原条目,readFileSync 不增)
9
- // - stat 失败(文件删除)→ undefined 且条目驱逐(重建同内容同 mtime → 必重读)
10
- // - 双读者共享:sync 写入的条目被 async 命中(fs/promises readFile 零调用),反之亦然
11
- //
12
- // ESM 下 node:fs 命名空间不可直接 spyOn(resource-discovery.test.ts TC4a 注记),
13
- // 计数走 vi.mock 工厂包装(透传 actual 实现 + vi.fn 计数,行为零变化)。
14
- // mtime 控制用 utimesSync 定值(整数 ms),消除写入浮点 mtime 的精度/flake 面。
15
-
16
- import * as fs from "node:fs";
17
- import * as os from "node:os";
18
- import * as path from "node:path";
19
- import * as fsp from "node:fs/promises";
20
-
21
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
22
-
23
- // 隔离真实用户全局目录(homedir 推导 user-agents 源,同 resource-discovery.test.ts)
24
- vi.mock("node:os", async (importOriginal) => {
25
- const actual = await importOriginal<typeof import("node:os")>();
26
- return { ...actual, homedir: () => "/nonexistent-home-for-tests" };
27
- });
28
-
29
- vi.mock("node:fs", async (importOriginal) => {
30
- const actual = await importOriginal<typeof import("node:fs")>();
31
- return {
32
- ...actual,
33
- readFileSync: vi.fn(
34
- (...args: Parameters<typeof actual.readFileSync>) => actual.readFileSync(...args),
35
- ),
36
- statSync: vi.fn(
37
- (...args: Parameters<typeof actual.statSync>) => actual.statSync(...args),
38
- ),
39
- };
40
- });
41
-
42
- vi.mock("node:fs/promises", async (importOriginal) => {
43
- const actual = await importOriginal<typeof import("node:fs/promises")>();
44
- return {
45
- ...actual,
46
- readFile: vi.fn(
47
- (...args: Parameters<typeof actual.readFile>) => actual.readFile(...args),
48
- ),
49
- };
50
- });
51
-
52
- import { clearFileCache, discoverResources, readPackageManifestSync } from "../resource-discovery.ts";
53
-
54
- // ── helpers ──
55
-
56
- function tmpPkgDir(): string {
57
- return fs.mkdtempSync(path.join(os.tmpdir(), "manifest-cache-test-"));
58
- }
59
-
60
- function writePackageJson(pkgDir: string, pi: Record<string, unknown> | undefined): string {
61
- fs.mkdirSync(pkgDir, { recursive: true });
62
- const filePath = path.join(pkgDir, "package.json");
63
- fs.writeFileSync(filePath, JSON.stringify({ name: "test-pkg", pi }), "utf-8");
64
- return filePath;
65
- }
66
-
67
- /** 写原始(非 JSON)package.json 内容——坏 JSON 用例。 */
68
- function writeRawPackageJson(pkgDir: string, content: string): string {
69
- fs.mkdirSync(pkgDir, { recursive: true });
70
- const filePath = path.join(pkgDir, "package.json");
71
- fs.writeFileSync(filePath, content, "utf-8");
72
- return filePath;
73
- }
74
-
75
- /** 定值 mtime(整数 ms,消除浮点 mtime 与 utimesSync 恢复的精度差)。 */
76
- const T1 = new Date("2026-01-01T00:00:00Z");
77
- const T2 = new Date("2026-01-01T00:00:10Z");
78
-
79
- function setMtime(filePath: string, t: Date): void {
80
- fs.utimesSync(filePath, t, t);
81
- }
82
-
83
- function readCount(): number {
84
- return vi.mocked(fs.readFileSync).mock.calls.length;
85
- }
86
-
87
- /** node:fs/promises readFile 包装计数(异步读者)。 */
88
- function asyncReadCount(): number {
89
- return vi.mocked(fsp.readFile).mock.calls.length;
90
- }
91
-
92
- // ============================================================
93
- // manifestCache(IF1)
94
- // ============================================================
95
-
96
- describe("manifestCache(readPackageManifestSync)", () => {
97
- let pkgDir: string;
98
- let pkgJson: string;
99
-
100
- beforeEach(() => {
101
- clearFileCache();
102
- vi.mocked(fs.readFileSync).mockClear();
103
- pkgDir = tmpPkgDir();
104
- });
105
- afterEach(() => {
106
- fs.rmSync(pkgDir, { recursive: true, force: true });
107
- });
108
-
109
- it("命中:同 mtime 二次调用不重读(readFileSync 只调 1 次)", () => {
110
- pkgJson = writePackageJson(pkgDir, { agents: ["./a.md"] });
111
- setMtime(pkgJson, T1);
112
- const r1 = readPackageManifestSync(pkgDir, "agents");
113
- expect(r1).toEqual(["./a.md"]);
114
- expect(readCount()).toBe(1);
115
- const r2 = readPackageManifestSync(pkgDir, "agents");
116
- expect(r2).toEqual(["./a.md"]);
117
- // 命中缓存:statSync 每次(判 mtime)但 readFileSync 不增
118
- expect(readCount()).toBe(1);
119
- });
120
-
121
- it("kind 共享一次 parse:同 package.json 的 agents/workflows 两 kind 只读一次", () => {
122
- pkgJson = writePackageJson(pkgDir, { agents: ["./a.md"], workflows: ["./w.js"] });
123
- setMtime(pkgJson, T1);
124
- expect(readPackageManifestSync(pkgDir, "agents")).toEqual(["./a.md"]);
125
- expect(readPackageManifestSync(pkgDir, "workflows")).toEqual(["./w.js"]);
126
- expect(readCount()).toBe(1);
127
- });
128
-
129
- it("失效:package.json 重写(mtime 变)→ 重读新内容", () => {
130
- pkgJson = writePackageJson(pkgDir, { agents: ["./a.md"] });
131
- setMtime(pkgJson, T1);
132
- expect(readPackageManifestSync(pkgDir, "agents")).toEqual(["./a.md"]);
133
- writePackageJson(pkgDir, { agents: ["./b.md"] });
134
- // 不需显式设 mtime:新写入的 mtime 必 ≠ 定值 T1
135
- expect(readPackageManifestSync(pkgDir, "agents")).toEqual(["./b.md"]);
136
- expect(readCount()).toBe(2);
137
- });
138
-
139
- it("坏 JSON → undefined 且不缓存:mtime 未变时二次调用仍重新 read(计数递增)", () => {
140
- // 起点即坏 JSON(无缓存条目)——若坏结果被缓存,二次调用不会重新 readFile。
141
- // (good→bad 且 mtime 未变的场景由上一用例的命中路径覆盖:mtime 相等直接命中原好条目)
142
- pkgJson = writeRawPackageJson(pkgDir, "{ broken json");
143
- setMtime(pkgJson, T1);
144
- expect(readPackageManifestSync(pkgDir, "agents")).toBeUndefined();
145
- expect(readCount()).toBe(1);
146
- expect(readPackageManifestSync(pkgDir, "agents")).toBeUndefined();
147
- expect(readCount()).toBe(2); // 二次调用仍重新 read = 坏结果未入缓存(毒条目永不入缓存)
148
- });
149
-
150
- it("坏 JSON 不驱逐已有好条目:恢复原内容 + 恢复原 mtime → 命中原条目(readFileSync 不增)", () => {
151
- pkgJson = writePackageJson(pkgDir, { agents: ["./a.md"] });
152
- setMtime(pkgJson, T1);
153
- expect(readPackageManifestSync(pkgDir, "agents")).toEqual(["./a.md"]);
154
- expect(readCount()).toBe(1);
155
- // 改坏且 mtime 变(T2)→ undefined(read 失败路径,不驱逐 {T1, pi} 好条目)
156
- fs.writeFileSync(pkgJson, "{ broken", "utf-8");
157
- setMtime(pkgJson, T2);
158
- expect(readPackageManifestSync(pkgDir, "agents")).toBeUndefined();
159
- expect(readCount()).toBe(2);
160
- // 恢复原内容 + utimesSync 恢复原 mtime → 若好条目仍在则命中(不重 read)
161
- writePackageJson(pkgDir, { agents: ["./a.md"] });
162
- setMtime(pkgJson, T1);
163
- expect(readPackageManifestSync(pkgDir, "agents")).toEqual(["./a.md"]);
164
- expect(readCount()).toBe(2); // 无第 3 次 read = 命中保留的原条目
165
- });
166
-
167
- it("stat 失败(文件删除)→ undefined 且条目驱逐(重建同内容同 mtime → 必重读)", () => {
168
- pkgJson = writePackageJson(pkgDir, { agents: ["./a.md"] });
169
- setMtime(pkgJson, T1);
170
- expect(readPackageManifestSync(pkgDir, "agents")).toEqual(["./a.md"]);
171
- expect(readCount()).toBe(1);
172
- fs.rmSync(pkgJson);
173
- expect(readPackageManifestSync(pkgDir, "agents")).toBeUndefined();
174
- expect(readCount()).toBe(1); // stat 失败路径不触发 read
175
- // 重建同内容 + 恢复同 mtime:条目已被驱逐 → 必重 read(计数 +1)
176
- writePackageJson(pkgDir, { agents: ["./a.md"] });
177
- setMtime(pkgJson, T1);
178
- expect(readPackageManifestSync(pkgDir, "agents")).toEqual(["./a.md"]);
179
- expect(readCount()).toBe(2);
180
- });
181
-
182
- it("无 pi 字段(合法解析结果)也缓存:二次调用不重读", () => {
183
- pkgJson = writePackageJson(pkgDir, undefined);
184
- setMtime(pkgJson, T1);
185
- expect(readPackageManifestSync(pkgDir, "agents")).toBeUndefined();
186
- expect(readCount()).toBe(1);
187
- expect(readPackageManifestSync(pkgDir, "agents")).toBeUndefined();
188
- expect(readCount()).toBe(1);
189
- });
190
-
191
- // [review 修复] 非对象 JSON 结构守卫:JSON.parse 对 "42" / '"str"' / "null" 等
192
- // 合法 JSON 产出非对象值,旧 `as Record<string, unknown>` 盲断言下 .pi 访问
193
- // 「碰巧不抛」(primitive 装箱返 undefined / null 抛 TypeError 落 catch)——
194
- // 显式守卫后按「无 manifest」(undefined)处理,行为等价但不再依赖巧合。
195
- it("非对象 JSON(42 / null / string / pi 字段非对象或数组)→ undefined 不抛,按合法解析结果缓存", () => {
196
- const cases: Array<{ raw: string; label: string }> = [
197
- { raw: "42", label: "number" },
198
- { raw: "null", label: "null" },
199
- { raw: '"just-a-string"', label: "string" },
200
- { raw: '{"name":"p","pi":42}', label: "pi-field-not-object" },
201
- // [review 修复] 数组能骗过 typeof "object" 守卫(装箱对象)——非合法 pi 容器,
202
- // 显式归 undefined(语义上仍是「无 manifest」,与数组 entries 读出 undefined 一致)
203
- { raw: '{"name":"p","pi":[]}', label: "pi-field-array" },
204
- ];
205
- // 每 case 独立子目录(缓存 key = package.json 绝对路径),互不命中;
206
- // readCount 是跨 case 累积计数,断言用本 case 增量
207
- for (const { raw, label } of cases) {
208
- const before = readCount();
209
- const caseDir = path.join(pkgDir, label);
210
- pkgJson = writeRawPackageJson(caseDir, raw);
211
- setMtime(pkgJson, T1);
212
- expect(readPackageManifestSync(caseDir, "agents"), `case ${label}`).toBeUndefined();
213
- expect(readPackageManifestSync(caseDir, "workflows"), `case ${label}`).toBeUndefined();
214
- // 合法解析 + 无 manifest → 缓存 undefined 条目(同「无 pi 字段」语义,二次调用不重读)
215
- expect(readCount() - before, `case ${label}`).toBe(1);
216
- }
217
- });
218
- });
219
-
220
- // ============================================================
221
- // 双读者共享(async readPackageManifest ↔ sync 共享同一 Map)
222
- // ============================================================
223
-
224
- describe("manifestCache 双读者共享(async ↔ sync)", () => {
225
- let ws: string;
226
- let pkgDir: string;
227
- let savedEnv: string | undefined;
228
-
229
- beforeEach(() => {
230
- clearFileCache();
231
- vi.mocked(fs.readFileSync).mockClear();
232
- vi.mocked(fsp.readFile).mockClear();
233
- ws = tmpPkgDir();
234
- pkgDir = path.join(ws, "my-ext");
235
- savedEnv = process.env.XYZ_EXTENSION_PATHS;
236
- });
237
- afterEach(() => {
238
- if (savedEnv === undefined) delete process.env.XYZ_EXTENSION_PATHS;
239
- else process.env.XYZ_EXTENSION_PATHS = savedEnv;
240
- fs.rmSync(ws, { recursive: true, force: true });
241
- });
242
-
243
- it("sync 写入的条目被 async 命中:discoverResources 零 fs/promises readFile", async () => {
244
- const pkgJson = writePackageJson(pkgDir, { agents: ["./agents"] });
245
- fs.mkdirSync(path.join(pkgDir, "agents"), { recursive: true });
246
- fs.writeFileSync(path.join(pkgDir, "agents", "a.md"), "body", "utf-8");
247
- setMtime(pkgJson, T1);
248
- // sync 读者先填充缓存
249
- expect(readPackageManifestSync(pkgDir, "agents")).toEqual(["./agents"]);
250
- expect(readCount()).toBe(1);
251
- // async 读者(XYZ_EXTENSION_PATHS → processPackage → readPackageManifest)应命中共享条目
252
- process.env.XYZ_EXTENSION_PATHS = pkgDir;
253
- const result = await discoverResources({
254
- kind: "agents",
255
- workspaceRoot: ws,
256
- hostRoots: [
257
- { dir: path.join(ws, ".fake-agent", "agents"), source: "user-pi" },
258
- { dir: path.join(ws, ".fake-agent", "npm", "node_modules"), source: "npm" },
259
- { dir: path.join(ws, ".fake-agent", "extensions"), source: "npm-dev" },
260
- ],
261
- });
262
- const ext = result.filter((r) => r.source === "user-extension-paths");
263
- expect(ext.map((r) => path.basename(r.path))).toEqual(["a.md"]);
264
- expect(asyncReadCount()).toBe(0); // async readFile 未被调用 = 命中共享缓存
265
- expect(readCount()).toBe(1); // sync 侧无新增 read
266
- });
267
-
268
- it("async 写入的条目被 sync 命中:async 读后 sync 不再 readFileSync", async () => {
269
- const pkgJson = writePackageJson(pkgDir, { agents: ["./agents"] });
270
- fs.mkdirSync(path.join(pkgDir, "agents"), { recursive: true });
271
- fs.writeFileSync(path.join(pkgDir, "agents", "a.md"), "body", "utf-8");
272
- setMtime(pkgJson, T1);
273
- process.env.XYZ_EXTENSION_PATHS = pkgDir;
274
- await discoverResources({
275
- kind: "agents",
276
- workspaceRoot: ws,
277
- hostRoots: [
278
- { dir: path.join(ws, ".fake-agent", "agents"), source: "user-pi" },
279
- { dir: path.join(ws, ".fake-agent", "npm", "node_modules"), source: "npm" },
280
- { dir: path.join(ws, ".fake-agent", "extensions"), source: "npm-dev" },
281
- ],
282
- });
283
- expect(asyncReadCount()).toBe(1); // async 读者读了一次
284
- // sync 读者应命中 async 写入的条目(readFileSync 计数不变)
285
- expect(readPackageManifestSync(pkgDir, "agents")).toEqual(["./agents"]);
286
- expect(readCount()).toBe(0);
287
- });
288
- });