@zhushanwen/subagent-core 0.2.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 (330) hide show
  1. package/README.md +114 -0
  2. package/dist/chunk-3VOERJPJ.js +22 -0
  3. package/dist/chunk-A4IVZWVX.js +31 -0
  4. package/dist/execution/engine/engines/zcode/constants.cjs +54 -0
  5. package/dist/execution/engine/engines/zcode/constants.d.cts +31 -0
  6. package/dist/execution/engine/engines/zcode/constants.d.ts +31 -0
  7. package/dist/execution/engine/engines/zcode/constants.js +22 -0
  8. package/dist/execution/engine/engines/zcode/reader.cjs +276 -0
  9. package/dist/execution/engine/engines/zcode/reader.d.cts +20 -0
  10. package/dist/execution/engine/engines/zcode/reader.d.ts +20 -0
  11. package/dist/execution/engine/engines/zcode/reader.js +239 -0
  12. package/dist/execution/engine/paths.cjs +55 -0
  13. package/dist/execution/engine/paths.d.cts +8 -0
  14. package/dist/execution/engine/paths.d.ts +8 -0
  15. package/dist/execution/engine/paths.js +26 -0
  16. package/dist/execution/relay-env.cjs +62 -0
  17. package/dist/execution/relay-env.d.cts +33 -0
  18. package/dist/execution/relay-env.d.ts +33 -0
  19. package/dist/execution/relay-env.js +20 -0
  20. package/dist/index.cjs +1852 -0
  21. package/dist/index.d.cts +1184 -0
  22. package/dist/index.d.ts +1184 -0
  23. package/dist/index.js +1810 -0
  24. package/dist/types-BxyAidGf.d.cts +875 -0
  25. package/dist/types-BxyAidGf.d.ts +875 -0
  26. package/package.json +99 -0
  27. package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +142 -0
  28. package/src/__tests__/append-system-prompt-assembly.test.ts +242 -0
  29. package/src/__tests__/builtin-workflows-structure.test.ts +180 -0
  30. package/src/__tests__/derive-closed-display-parity.test.ts +93 -0
  31. package/src/__tests__/fr4-get-state-handshake.test.ts +125 -0
  32. package/src/__tests__/fr8-orphan-recovery.test.ts +259 -0
  33. package/src/__tests__/m2-append-content-probe.test.ts +73 -0
  34. package/src/__tests__/manifest-store.test.ts +368 -0
  35. package/src/__tests__/record-store-cache.test.ts +335 -0
  36. package/src/__tests__/record-store-index.test.ts +560 -0
  37. package/src/__tests__/record-store-last-line.test.ts +129 -0
  38. package/src/__tests__/review-fix-loop-script.test.ts +288 -0
  39. package/src/__tests__/review-fix-loop-utils.test.ts +2377 -0
  40. package/src/__tests__/robustness-low-batch1.test.ts +286 -0
  41. package/src/__tests__/robustness-medium-batch1.test.ts +69 -0
  42. package/src/__tests__/robustness-medium-batch2.test.ts +65 -0
  43. package/src/__tests__/robustness-medium-batch3.test.ts +30 -0
  44. package/src/__tests__/robustness-medium-batch4.test.ts +358 -0
  45. package/src/__tests__/session-runner.test.ts +69 -0
  46. package/src/__tests__/smoke.test.ts +9 -0
  47. package/src/__tests__/stream-sink.test.ts +203 -0
  48. package/src/core/__tests__/host-services.test.ts +144 -0
  49. package/src/core/__tests__/logger.test.ts +78 -0
  50. package/src/core/__tests__/notify-ports.test.ts +104 -0
  51. package/src/core/host-services.ts +97 -0
  52. package/src/core/logger.ts +47 -0
  53. package/src/core/notify-ports.ts +126 -0
  54. package/src/execution/__tests__/__fixtures__/notifier-golden-snapshots.json +53 -0
  55. package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +1 -0
  56. package/src/execution/__tests__/agent-result-mapper.test.ts +150 -0
  57. package/src/execution/__tests__/alive-store.test.ts +147 -0
  58. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +490 -0
  59. package/src/execution/__tests__/channel-registry-handshake.test.ts +242 -0
  60. package/src/execution/__tests__/chat-engine-routing.test.ts +610 -0
  61. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +190 -0
  62. package/src/execution/__tests__/concurrency-pool.test.ts +250 -0
  63. package/src/execution/__tests__/config.test.ts +302 -0
  64. package/src/execution/__tests__/delivery-methods.test.ts +424 -0
  65. package/src/execution/__tests__/dialog-queue.test.ts +299 -0
  66. package/src/execution/__tests__/epipe-fallback.test.ts +241 -0
  67. package/src/execution/__tests__/execute-and-await-worktree.test.ts +243 -0
  68. package/src/execution/__tests__/execute-nesting.test.ts +323 -0
  69. package/src/execution/__tests__/execute-options-mapper.test.ts +200 -0
  70. package/src/execution/__tests__/execution-record.test.ts +1394 -0
  71. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +171 -0
  72. package/src/execution/__tests__/finalize-record.test.ts +367 -0
  73. package/src/execution/__tests__/finalized-marker.test.ts +104 -0
  74. package/src/execution/__tests__/format-schema-instruction.test.ts +169 -0
  75. package/src/execution/__tests__/gc-timer.test.ts +184 -0
  76. package/src/execution/__tests__/get-record-for-action-restart.test.ts +254 -0
  77. package/src/execution/__tests__/helpers/mock-extension-api.ts +30 -0
  78. package/src/execution/__tests__/helpers/spawn-mock.ts +242 -0
  79. package/src/execution/__tests__/host-mode.test.ts +87 -0
  80. package/src/execution/__tests__/lifecycle-manager-lock.test.ts +211 -0
  81. package/src/execution/__tests__/lifecycle-manager.test.ts +383 -0
  82. package/src/execution/__tests__/lifecycle-predicates.test.ts +116 -0
  83. package/src/execution/__tests__/manifest-parentid.test.ts +117 -0
  84. package/src/execution/__tests__/model-resolver.test.ts +465 -0
  85. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +287 -0
  86. package/src/execution/__tests__/nested-visibility.test.ts +325 -0
  87. package/src/execution/__tests__/output-collector.test.ts +358 -0
  88. package/src/execution/__tests__/path-encoding.test.ts +104 -0
  89. package/src/execution/__tests__/pi-invocation.test.ts +134 -0
  90. package/src/execution/__tests__/record-store.test.ts +1057 -0
  91. package/src/execution/__tests__/records-cwd-isolation.test.ts +91 -0
  92. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +339 -0
  93. package/src/execution/__tests__/recursive-visibility-env.test.ts +273 -0
  94. package/src/execution/__tests__/relay-env.test.ts +42 -0
  95. package/src/execution/__tests__/resource-policy.test.ts +109 -0
  96. package/src/execution/__tests__/rpc-mode.test.ts +89 -0
  97. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +267 -0
  98. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +253 -0
  99. package/src/execution/__tests__/run-spawn-edges.test.ts +687 -0
  100. package/src/execution/__tests__/run-spawn-integration.test.ts +933 -0
  101. package/src/execution/__tests__/run-spawn-resume.test.ts +322 -0
  102. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +196 -0
  103. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +199 -0
  104. package/src/execution/__tests__/session-context-resolver.test.ts +167 -0
  105. package/src/execution/__tests__/session-file-gc.test.ts +293 -0
  106. package/src/execution/__tests__/session-reconstructor.test.ts +379 -0
  107. package/src/execution/__tests__/session-runner-epipe.test.ts +178 -0
  108. package/src/execution/__tests__/session-runner-schema-env.test.ts +350 -0
  109. package/src/execution/__tests__/spawn-args.test.ts +445 -0
  110. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +189 -0
  111. package/src/execution/__tests__/spawn-event-adapter.test.ts +167 -0
  112. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +207 -0
  113. package/src/execution/__tests__/spawned-children.test.ts +92 -0
  114. package/src/execution/__tests__/start-sync-model-guard.test.ts +150 -0
  115. package/src/execution/__tests__/stdin-writer.test.ts +462 -0
  116. package/src/execution/__tests__/stream-sink-retirement.test.ts +261 -0
  117. package/src/execution/__tests__/subagent-service-abort.test.ts +60 -0
  118. package/src/execution/__tests__/subagent-service-message-close.test.ts +629 -0
  119. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +180 -0
  120. package/src/execution/__tests__/subagent-service.test.ts +788 -0
  121. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +310 -0
  122. package/src/execution/__tests__/subprocess-agent-runner.test.ts +612 -0
  123. package/src/execution/__tests__/temp-prompt.test.ts +53 -0
  124. package/src/execution/__tests__/timeout-integration.test.ts +615 -0
  125. package/src/execution/__tests__/tombstone-store.test.ts +73 -0
  126. package/src/execution/__tests__/turn-limiter-semantics.test.ts +194 -0
  127. package/src/execution/__tests__/turn-limiter.test.ts +65 -0
  128. package/src/execution/__tests__/ui-channels.test.ts +187 -0
  129. package/src/execution/__tests__/ui-interaction-model.test.ts +67 -0
  130. package/src/execution/__tests__/ui-request-handler-factory.test.ts +369 -0
  131. package/src/execution/__tests__/ui-request-handler.test.ts +204 -0
  132. package/src/execution/__tests__/ui-request-observability.test.ts +113 -0
  133. package/src/execution/__tests__/ui-request-queue.test.ts +133 -0
  134. package/src/execution/__tests__/worktree-manager.test.ts +633 -0
  135. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +229 -0
  136. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +181 -0
  137. package/src/execution/__tests__/worktree-registry.test.ts +199 -0
  138. package/src/execution/agent-registry.ts +199 -0
  139. package/src/execution/agent-result-mapper.ts +90 -0
  140. package/src/execution/alive-store.ts +119 -0
  141. package/src/execution/argv-mirror.ts +113 -0
  142. package/src/execution/best-effort.ts +38 -0
  143. package/src/execution/channel-registry-access.ts +144 -0
  144. package/src/execution/concurrency-pool.ts +116 -0
  145. package/src/execution/config.ts +165 -0
  146. package/src/execution/dialog-queue.ts +330 -0
  147. package/src/execution/engine/__tests__/common/data-dir.test.ts +78 -0
  148. package/src/execution/engine/__tests__/common/errors.test.ts +132 -0
  149. package/src/execution/engine/__tests__/common/event-journal.test.ts +177 -0
  150. package/src/execution/engine/__tests__/common/kill-chain.test.ts +192 -0
  151. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +81 -0
  152. package/src/execution/engine/__tests__/common/persona-router.test.ts +123 -0
  153. package/src/execution/engine/__tests__/common/pool-manager.test.ts +154 -0
  154. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +128 -0
  155. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +28 -0
  156. package/src/execution/engine/__tests__/conformance/agent-event-invariants.ts +141 -0
  157. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +109 -0
  158. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +101 -0
  159. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +77 -0
  160. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +104 -0
  161. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +201 -0
  162. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +76 -0
  163. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +79 -0
  164. package/src/execution/engine/__tests__/engine-discovery.test.ts +87 -0
  165. package/src/execution/engine/__tests__/model-prompt.test.ts +197 -0
  166. package/src/execution/engine/__tests__/paths.test.ts +39 -0
  167. package/src/execution/engine/__tests__/registry.test.ts +120 -0
  168. package/src/execution/engine/__tests__/routing.test.ts +231 -0
  169. package/src/execution/engine/common/data-dir.ts +66 -0
  170. package/src/execution/engine/common/errors.ts +183 -0
  171. package/src/execution/engine/common/event-journal.ts +254 -0
  172. package/src/execution/engine/common/journal-replay.ts +62 -0
  173. package/src/execution/engine/common/kill-chain.ts +221 -0
  174. package/src/execution/engine/common/nesting-guard.ts +50 -0
  175. package/src/execution/engine/common/persona-router.ts +108 -0
  176. package/src/execution/engine/common/pool-manager.ts +226 -0
  177. package/src/execution/engine/common/schema-emulation.ts +189 -0
  178. package/src/execution/engine/common/session-view-projection.ts +51 -0
  179. package/src/execution/engine/engine-discovery.ts +65 -0
  180. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +469 -0
  181. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +155 -0
  182. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +164 -0
  183. package/src/execution/engine/engines/pi/pi-engine.ts +415 -0
  184. package/src/execution/engine/engines/pi/reader.ts +48 -0
  185. package/src/execution/engine/engines/pi/registration.ts +35 -0
  186. package/src/execution/engine/engines/pi/task-spec-mapper.ts +100 -0
  187. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +39 -0
  188. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +150 -0
  189. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +246 -0
  190. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +228 -0
  191. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +210 -0
  192. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +71 -0
  193. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +127 -0
  194. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +580 -0
  195. package/src/execution/engine/engines/zcode/constants.ts +43 -0
  196. package/src/execution/engine/engines/zcode/golden-sample.ts +39 -0
  197. package/src/execution/engine/engines/zcode/launcher.ts +161 -0
  198. package/src/execution/engine/engines/zcode/parser.ts +436 -0
  199. package/src/execution/engine/engines/zcode/preparer.ts +363 -0
  200. package/src/execution/engine/engines/zcode/reader.ts +381 -0
  201. package/src/execution/engine/engines/zcode/registration.ts +37 -0
  202. package/src/execution/engine/engines/zcode/zcode-engine.ts +658 -0
  203. package/src/execution/engine/host-task-spec.ts +47 -0
  204. package/src/execution/engine/model-prompt.ts +158 -0
  205. package/src/execution/engine/paths.ts +42 -0
  206. package/src/execution/engine/port.ts +153 -0
  207. package/src/execution/engine/registry.ts +133 -0
  208. package/src/execution/engine/routing.ts +218 -0
  209. package/src/execution/engine/types.ts +309 -0
  210. package/src/execution/execute-options-mapper.ts +115 -0
  211. package/src/execution/execution-record.ts +984 -0
  212. package/src/execution/finalize-record.ts +254 -0
  213. package/src/execution/finalized-marker.ts +70 -0
  214. package/src/execution/get-state-handshake.ts +104 -0
  215. package/src/execution/host-mode.ts +56 -0
  216. package/src/execution/idle-gc.ts +47 -0
  217. package/src/execution/lifecycle-manager.ts +513 -0
  218. package/src/execution/lifecycle-predicates.ts +65 -0
  219. package/src/execution/manifest-store.ts +256 -0
  220. package/src/execution/model-config-service.ts +250 -0
  221. package/src/execution/model-resolver.ts +248 -0
  222. package/src/execution/notifier.ts +372 -0
  223. package/src/execution/notify-ledger.ts +580 -0
  224. package/src/execution/output-collector.ts +228 -0
  225. package/src/execution/path-encoding.ts +60 -0
  226. package/src/execution/pi-invocation.ts +120 -0
  227. package/src/execution/record-entry.ts +132 -0
  228. package/src/execution/record-store.ts +1265 -0
  229. package/src/execution/relay-env.ts +37 -0
  230. package/src/execution/session-context-resolver.ts +64 -0
  231. package/src/execution/session-file-gc.ts +120 -0
  232. package/src/execution/session-pending.ts +170 -0
  233. package/src/execution/session-reconstructor.ts +678 -0
  234. package/src/execution/session-runner.ts +1789 -0
  235. package/src/execution/sessions-index.ts +304 -0
  236. package/src/execution/spawn-event-adapter.ts +363 -0
  237. package/src/execution/stdin-writer.ts +198 -0
  238. package/src/execution/stream-sink.ts +126 -0
  239. package/src/execution/subagent-service.ts +2268 -0
  240. package/src/execution/subprocess-agent-runner.ts +317 -0
  241. package/src/execution/temp-prompt.ts +62 -0
  242. package/src/execution/tombstone-store.ts +72 -0
  243. package/src/execution/turn-limiter.ts +102 -0
  244. package/src/execution/types.ts +1023 -0
  245. package/src/execution/ui-channels.ts +216 -0
  246. package/src/execution/ui-interaction-model.ts +48 -0
  247. package/src/execution/ui-request-handler-factory.ts +243 -0
  248. package/src/execution/ui-request-observability.ts +81 -0
  249. package/src/execution/ui-request-queue.ts +184 -0
  250. package/src/execution/worktree-manager.ts +688 -0
  251. package/src/execution/worktree-registry.ts +279 -0
  252. package/src/index.ts +87 -0
  253. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +349 -0
  254. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +202 -0
  255. package/src/orchestration/__tests__/agent-call-stream.test.ts +152 -0
  256. package/src/orchestration/__tests__/args-validator.test.ts +143 -0
  257. package/src/orchestration/__tests__/config-loader.test.ts +503 -0
  258. package/src/orchestration/__tests__/error-recovery-handlers.test.ts +809 -0
  259. package/src/orchestration/__tests__/error-recovery-postmessage-defense.test.ts +404 -0
  260. package/src/orchestration/__tests__/error-recovery-serialize-failed-result.test.ts +56 -0
  261. package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +166 -0
  262. package/src/orchestration/__tests__/execute-agent-call.test.ts +451 -0
  263. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +600 -0
  264. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +96 -0
  265. package/src/orchestration/__tests__/lifecycle.test.ts +659 -0
  266. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +95 -0
  267. package/src/orchestration/__tests__/review-fix-loop-scriptpath-failfast.test.ts +183 -0
  268. package/src/orchestration/__tests__/script-lint.test.ts +831 -0
  269. package/src/orchestration/__tests__/skill-discovery.test.ts +210 -0
  270. package/src/orchestration/__tests__/test-mocks.ts +197 -0
  271. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +368 -0
  272. package/src/orchestration/__tests__/worker-host.test.ts +120 -0
  273. package/src/orchestration/__tests__/worker-returnmeta-passthrough.test.ts +164 -0
  274. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +563 -0
  275. package/src/orchestration/__tests__/worker-script-builder.test.ts +309 -0
  276. package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +129 -0
  277. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +317 -0
  278. package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +110 -0
  279. package/src/orchestration/agent-opts-resolver.ts +174 -0
  280. package/src/orchestration/args-validator.ts +127 -0
  281. package/src/orchestration/config-loader.ts +316 -0
  282. package/src/orchestration/error-recovery.ts +1018 -0
  283. package/src/orchestration/execute-agent-call.ts +256 -0
  284. package/src/orchestration/launcher.ts +455 -0
  285. package/src/orchestration/lifecycle.ts +399 -0
  286. package/src/orchestration/models/__tests__/budget.test.ts +307 -0
  287. package/src/orchestration/models/agent-call.ts +83 -0
  288. package/src/orchestration/models/budget.ts +118 -0
  289. package/src/orchestration/models/ports.ts +164 -0
  290. package/src/orchestration/models/run-runtime.ts +104 -0
  291. package/src/orchestration/models/run-spec.ts +83 -0
  292. package/src/orchestration/models/run-state.ts +44 -0
  293. package/src/orchestration/models/trace.ts +183 -0
  294. package/src/orchestration/models/types.ts +301 -0
  295. package/src/orchestration/models/workflow-run.ts +254 -0
  296. package/src/orchestration/models/workflow-script-registry.ts +35 -0
  297. package/src/orchestration/models/workflow-script.ts +117 -0
  298. package/src/orchestration/script-lint.ts +766 -0
  299. package/src/orchestration/skill-discovery.ts +135 -0
  300. package/src/orchestration/worker-handle.ts +115 -0
  301. package/src/orchestration/worker-host.ts +98 -0
  302. package/src/orchestration/worker-script-builder.ts +421 -0
  303. package/src/orchestration/workflow-files.ts +85 -0
  304. package/src/orchestration/workflow-script-registry-impl.ts +133 -0
  305. package/src/shared/__tests__/agent-ref.test.ts +34 -0
  306. package/src/shared/__tests__/meta-parser.test.ts +304 -0
  307. package/src/shared/__tests__/model-ref.test.ts +306 -0
  308. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +288 -0
  309. package/src/shared/__tests__/resource-discovery.test.ts +749 -0
  310. package/src/shared/__tests__/resource-meta.test.ts +51 -0
  311. package/src/shared/__tests__/schema-jsonify.test.ts +81 -0
  312. package/src/shared/__tests__/timer-delay.test.ts +61 -0
  313. package/src/shared/agent-event.ts +13 -0
  314. package/src/shared/agent-ref.ts +57 -0
  315. package/src/shared/meta-parser.ts +261 -0
  316. package/src/shared/model-ref.ts +286 -0
  317. package/src/shared/resource-discovery.ts +835 -0
  318. package/src/shared/resource-meta.ts +65 -0
  319. package/src/shared/schema-env.ts +44 -0
  320. package/src/shared/schema-jsonify.ts +58 -0
  321. package/src/shared/timer-delay.ts +54 -0
  322. package/src/shared/xml-injection.ts +35 -0
  323. package/workflows/README.md +81 -0
  324. package/workflows/_shared/agent-refs.cjs +40 -0
  325. package/workflows/chain.js +137 -0
  326. package/workflows/map-reduce.js +180 -0
  327. package/workflows/parallel.js +154 -0
  328. package/workflows/review-fix-loop-utils.cjs +1542 -0
  329. package/workflows/review-fix-loop.js +1605 -0
  330. package/workflows/scatter-gather.js +184 -0
@@ -0,0 +1,335 @@
1
+ // [perf] RecordStore per-file 缓存 + light 列表扫描的行为测试。
2
+ //
3
+ // 覆盖(方案 A + B):
4
+ // A1. stat 命中 → 零文件读取(去掉读权限仍返回记录,证明只 stat 不 read)
5
+ // A2. jsonl 内容变化 → 单文件重建(状态翻转,其余文件不受影响)
6
+ // A3. sidecar 变化(.finalized → .cancelled)→ 状态翻转
7
+ // A4. 删除文件 → 从结果与缓存中移除
8
+ // B1. collectRecords 返回 light(无 eventLog/result/turns)
9
+ // B2. getFullRecord 懒加载全量(result/turns/tokens/eventLog)且缓存
10
+ // B3. identity-only 文件(无 assistant)→ 分支 4 running 呈现(v4 B-1 可续聊语义)
11
+ // B4. getFullRecord 内存源优先(register 后直接投影全量)
12
+ //
13
+ // 「去读权限」验证法:chmod 000 后 statSync 仍可用、readFileSync/openSync 失败——
14
+ // 若实现退化回读文件,记录会消失(readIdentityHeader 捕获异常返回 undefined)。
15
+
16
+ import * as fs from "fs";
17
+ import * as os from "os";
18
+ import * as path from "path";
19
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
20
+
21
+ import { RecordStore } from "../execution/record-store";
22
+ import type { ExecutionRecord } from "../execution/types";
23
+
24
+ describe("RecordStore per-file cache + light scan [perf]", () => {
25
+ let rootDir: string;
26
+ let sessionsDir: string;
27
+ let store: RecordStore;
28
+
29
+ beforeEach(() => {
30
+ // [DS3] 两层布局:sessionsDir 深一层(对齐生产 <enc> 段结构),sessions-index.json
31
+ // 落 dirname(sessionsDir)=rootDir 内随 afterEach 清理,不写 os.tmpdir() 本身
32
+ // (并行 vitest worker 共写同一文件会跨文件污染)。
33
+ rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "subagents-cache-"));
34
+ sessionsDir = path.join(rootDir, "sessions");
35
+ fs.mkdirSync(sessionsDir);
36
+ store = new RecordStore(sessionsDir);
37
+ });
38
+
39
+ afterEach(() => {
40
+ // 恢复权限以防 tmp 清理失败(chmod 000 用例会留下不可读文件)
41
+ for (const f of fs.readdirSync(sessionsDir)) {
42
+ try { fs.chmodSync(path.join(sessionsDir, f), 0o644); } catch { /* best-effort */ }
43
+ }
44
+ fs.rmSync(rootDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
45
+ });
46
+
47
+ /** 构造一个 session.jsonl(header + identity + N 条 assistant message)+ 可选 sidecar。 */
48
+ function writeSession(opts: {
49
+ name: string;
50
+ id: string;
51
+ agent?: string;
52
+ startedAt?: number;
53
+ rootSessionId?: string;
54
+ assistantTexts?: string[];
55
+ finalized?: boolean;
56
+ cancelled?: boolean;
57
+ }): string {
58
+ const file = path.join(sessionsDir, opts.name);
59
+ const lines: unknown[] = [
60
+ { type: "session", id: "sess" },
61
+ {
62
+ type: "custom",
63
+ customType: "subagent-identity",
64
+ data: {
65
+ id: opts.id,
66
+ agent: opts.agent ?? "worker",
67
+ mode: "background",
68
+ task: "test task",
69
+ slug: "test",
70
+ startedAt: opts.startedAt ?? 1000,
71
+ rootSessionId: opts.rootSessionId ?? "root-1",
72
+ depth: 0,
73
+ },
74
+ },
75
+ ];
76
+ for (const text of opts.assistantTexts ?? []) {
77
+ lines.push({
78
+ type: "message",
79
+ timestamp: "2026-08-14T00:00:00.000Z",
80
+ message: {
81
+ role: "assistant",
82
+ content: [{ type: "text", text }],
83
+ usage: { input: 10, output: 5 },
84
+ stopReason: "stop",
85
+ timestamp: 2000,
86
+ },
87
+ });
88
+ }
89
+ fs.writeFileSync(file, lines.map((l) => JSON.stringify(l)).join("\n") + "\n");
90
+ if (opts.finalized) fs.writeFileSync(`${file}.finalized`, "");
91
+ if (opts.cancelled) {
92
+ fs.writeFileSync(
93
+ `${file}.cancelled`,
94
+ JSON.stringify({ id: opts.id, status: "cancelled", agent: "worker", startedAt: 1000, endedAt: 3000 }) + "\n",
95
+ );
96
+ }
97
+ return file;
98
+ }
99
+
100
+ it("A1: 缓存命中零文件读取——chmod 000 后记录仍在", () => {
101
+ const f1 = writeSession({ name: "a.jsonl", id: "sa-1", assistantTexts: ["r1"], finalized: true });
102
+ writeSession({ name: "b.jsonl", id: "sa-2", assistantTexts: ["r2"], finalized: true });
103
+
104
+ const first = store.collectRecords(100, "all", "root-1");
105
+ expect(first).toHaveLength(2);
106
+
107
+ // 去读权限:若二次扫描退化回读文件,readIdentityHeader 会失败 → 记录消失。
108
+ fs.chmodSync(f1, 0o000);
109
+ const second = store.collectRecords(100, "all", "root-1");
110
+ expect(second.map((r) => r.id).sort()).toEqual(["sa-1", "sa-2"]);
111
+ fs.chmodSync(f1, 0o644);
112
+ });
113
+
114
+ it("A2: jsonl 变化触发单文件重建——append + finalize 后状态翻转", () => {
115
+ const fA = writeSession({ name: "a.jsonl", id: "sa-1", assistantTexts: ["r1"] }); // 无 sidecar → running
116
+ writeSession({ name: "b.jsonl", id: "sa-2", assistantTexts: ["r2"] });
117
+
118
+ expect(store.collectRecords(100, "all", "root-1").every((r) => r.status === "running")).toBe(true);
119
+
120
+ // 文件 A 追加一条 assistant + 写 .finalized(模拟真实 finalize)
121
+ fs.appendFileSync(
122
+ fA,
123
+ JSON.stringify({
124
+ type: "message",
125
+ timestamp: "2026-08-14T00:01:00.000Z",
126
+ message: { role: "assistant", content: [{ type: "text", text: "more" }], usage: { input: 1, output: 1 }, stopReason: "stop", timestamp: 2500 },
127
+ }) + "\n",
128
+ );
129
+ fs.writeFileSync(`${fA}.finalized`, ""); // 空 sidecar = 旧格式(死因不可考)
130
+
131
+ const records = store.collectRecords(100, "all", "root-1");
132
+ const a = records.find((r) => r.id === "sa-1");
133
+ const b = records.find((r) => r.id === "sa-2");
134
+ expect(a?.status).toBe("closed");
135
+ // [v8.5 A2] 空 sidecar 兜底 disconnected(旧格式:死因不可考),不再误导为 gc。
136
+ // reason 读回的正向用例在 ended-message-and-fork-from.test.ts。
137
+ expect(a?.closedReason).toBe("disconnected");
138
+ expect(a?.endedAt).toBeGreaterThan(0); // light 分支 2 用 jsonl mtime 近似
139
+ expect(b?.status).toBe("running"); // 未变文件不受影响
140
+ });
141
+
142
+ it("A3: sidecar 变化触发状态翻转——.finalized 换 .cancelled", () => {
143
+ const f = writeSession({ name: "a.jsonl", id: "sa-1", assistantTexts: ["r1"], finalized: true });
144
+ // [v8.5 A2] fixture 写的空 .finalized 属旧格式 → disconnected
145
+ expect(store.collectRecords(100, "all", "root-1")[0].closedReason).toBe("disconnected");
146
+
147
+ fs.rmSync(`${f}.finalized`);
148
+ fs.writeFileSync(
149
+ `${f}.cancelled`,
150
+ JSON.stringify({ id: "sa-1", status: "cancelled", agent: "worker", startedAt: 1000, endedAt: 3000 }) + "\n",
151
+ );
152
+
153
+ const rec = store.collectRecords(100, "all", "root-1")[0];
154
+ expect(rec.status).toBe("closed");
155
+ expect(rec.closedReason).toBe("cancelled");
156
+ expect(rec.error).toBe("cancelled by user");
157
+ expect(rec.endedAt).toBe(3000);
158
+ });
159
+
160
+ it("A4: 删除文件后从结果中移除", () => {
161
+ const f = writeSession({ name: "a.jsonl", id: "sa-1", assistantTexts: ["r1"], finalized: true });
162
+ writeSession({ name: "b.jsonl", id: "sa-2", assistantTexts: ["r2"], finalized: true });
163
+ expect(store.collectRecords(100, "all", "root-1")).toHaveLength(2);
164
+
165
+ fs.rmSync(f);
166
+ fs.rmSync(`${f}.finalized`);
167
+ const records = store.collectRecords(100, "all", "root-1");
168
+ expect(records).toHaveLength(1);
169
+ expect(records[0].id).toBe("sa-2");
170
+ // 缓存索引同步修剪:已删 id 的 full 查询 miss
171
+ expect(store.getFullRecord("sa-1")).toBeUndefined();
172
+ });
173
+
174
+ it("B1: 列表返回 light——不含 result/eventLog/turns", () => {
175
+ writeSession({ name: "a.jsonl", id: "sa-1", assistantTexts: ["SECRET-RESULT"], finalized: true });
176
+
177
+ const light = store.collectRecords(100, "all", "root-1")[0];
178
+ expect(light.id).toBe("sa-1");
179
+ expect(light.status).toBe("closed");
180
+ expect(light.result).toBeUndefined();
181
+ expect(light.eventLog).toEqual([]);
182
+ expect(light.displayItems).toEqual([]);
183
+ expect(light.turns).toBe(0);
184
+ expect(light.totalTokens).toBe(0);
185
+ });
186
+
187
+ it("B2: getFullRecord 懒加载全量并缓存(chmod 000 后仍返回)", () => {
188
+ const f = writeSession({ name: "a.jsonl", id: "sa-1", assistantTexts: ["SECRET-RESULT"], finalized: true });
189
+ store.collectRecords(100, "all", "root-1"); // 建立轻量缓存
190
+
191
+ const full = store.getFullRecord("sa-1");
192
+ expect(full).toBeDefined();
193
+ expect(full?.result).toContain("SECRET-RESULT");
194
+ expect(full?.turns).toBe(1);
195
+ expect(full?.totalTokens).toBe(15); // input 10 + output 5
196
+ expect(full?.eventLog.length).toBeGreaterThan(0); // turn_end 派生事件
197
+ expect(full?.displayItems.length).toBeGreaterThan(0);
198
+ expect(full?.status).toBe("closed"); // 状态矩阵同样套用
199
+
200
+ // 已缓存:去掉读权限再取,仍返回全量(不重读文件)
201
+ fs.chmodSync(f, 0o000);
202
+ const again = store.getFullRecord("sa-1");
203
+ expect(again?.result).toContain("SECRET-RESULT");
204
+
205
+ // 不存在的 id
206
+ expect(store.getFullRecord("sa-missing")).toBeUndefined();
207
+ });
208
+
209
+ it("B3: identity-only 文件(无 assistant)按分支 4 running 呈现", () => {
210
+ // v4 B-1 可续聊语义:pi 延迟写入下这类文件几乎不存在(首个 assistant 才 flush),
211
+ // 若存在(崩溃前 flush)应可见为 running 而非静默消失。getFullRecord 回退 light。
212
+ writeSession({ name: "a.jsonl", id: "sa-1" }); // 无 assistantTexts
213
+ const rec = store.collectRecords(100, "all", "root-1")[0];
214
+ expect(rec.id).toBe("sa-1");
215
+ expect(rec.status).toBe("running");
216
+ expect(rec.endedAt).toBeUndefined();
217
+
218
+ const full = store.getFullRecord("sa-1");
219
+ expect(full).toBeDefined();
220
+ expect(full?.status).toBe("running"); // 哨兵回退 light,不重试全文解析
221
+ });
222
+
223
+ it("B5: 续聊场景 identity 在尾部——尾部定位 + 负缓存回归防护", () => {
224
+ // 续聊(resume)场景:session_start 再次触发,identity append 到文件尾部附近。
225
+ // 真实目录实测 ~65% 文件的 identity 不在头 64KB 而在尾 64KB(本用例复现该形态)。
226
+ const file = path.join(sessionsDir, "resume.jsonl");
227
+ const head = [
228
+ JSON.stringify({ type: "session", id: "sess" }),
229
+ // 首轮内容(无 identity——模拟崩溃后重启续聊的身份补写形态):大体积填充
230
+ // 把 identity 推出头部 64KB 窗口。
231
+ JSON.stringify({ type: "message", message: { role: "user", content: "x".repeat(70000), timestamp: 1000 } }),
232
+ ];
233
+ const tail = [
234
+ {
235
+ type: "custom",
236
+ customType: "subagent-identity",
237
+ data: { id: "sa-tail", agent: "worker", mode: "background", task: "resumed", slug: "tail", startedAt: 1000, rootSessionId: "root-1", depth: 0 },
238
+ },
239
+ {
240
+ type: "message",
241
+ timestamp: "2026-08-14T00:00:00.000Z",
242
+ message: { role: "assistant", content: [{ type: "text", text: "after resume" }], usage: { input: 1, output: 2 }, stopReason: "stop", timestamp: 2000 },
243
+ },
244
+ ];
245
+ fs.writeFileSync(file, [...head, ...tail.map((l) => JSON.stringify(l))].join("\n") + "\n");
246
+
247
+ const rec = store.collectRecords(100, "all", "root-1").find((r) => r.id === "sa-tail");
248
+ expect(rec).toBeDefined();
249
+ expect(rec?.task).toBe("resumed");
250
+ const full = store.getFullRecord("sa-tail");
251
+ expect(full?.result).toBe("after resume");
252
+
253
+ // 负缓存:无 identity 的 jsonl 不应每轮重读(写入后重复扫描结果稳定)
254
+ const junk = path.join(sessionsDir, "junk.jsonl");
255
+ fs.writeFileSync(junk, JSON.stringify({ type: "session", id: "sess2" }) + "\n");
256
+ const ids = store.collectRecords(100, "all", "root-1").map((r) => r.id);
257
+ expect(ids).not.toContain("junk");
258
+ expect(store.collectRecords(100, "all", "root-1").map((r) => r.id)).toEqual(ids); // 负缓存稳定
259
+ });
260
+
261
+ it("B4: getFullRecord 内存源优先——register 后直接投影全量", () => {
262
+ writeSession({ name: "a.jsonl", id: "sa-1", assistantTexts: ["r1"], finalized: true });
263
+ store.collectRecords(100, "all", "root-1");
264
+
265
+ // register 一个同 id 内存 running record(模拟跨重启恢复/续聊)
266
+ const mem = {
267
+ id: "sa-1",
268
+ agent: "worker",
269
+ slug: "test",
270
+ status: "running",
271
+ mode: "background",
272
+ startedAt: 1000,
273
+ rootSessionId: "root-1",
274
+ parentRecordId: undefined,
275
+ depth: 0,
276
+ endedAt: undefined,
277
+ turnCount: 7,
278
+ turns: [],
279
+ totalTokens: 42,
280
+ model: "m/x",
281
+ thinkingLevel: undefined,
282
+ task: "test task",
283
+ eventLog: [],
284
+ displayItems: [],
285
+ result: "live result",
286
+ error: undefined,
287
+ sessionFile: path.join(sessionsDir, "a.jsonl"),
288
+ } as unknown as ExecutionRecord;
289
+ store.register(mem);
290
+
291
+ const full = store.getFullRecord("sa-1");
292
+ expect(full?.status).toBe("running"); // 内存 running 覆盖磁盘 closed
293
+ expect(full?.result).toBe("live result");
294
+ expect(full?.turns).toBe(7);
295
+ });
296
+
297
+ // [perf] 目录 mtime 快路径:/subagents overlay 打开期间高频扫描(250ms 动画 timer
298
+ // + 120ms debounce),目录未变时跳过 readdir + N×4 statSync,直接复用缓存 light。
299
+ // 正确性依据:文件新建/删除/重命名/sidecar 写入都改目录 mtime;jsonl append 不改
300
+ // 目录 mtime 且不影响 light 态(identity/status 由首行与 sidecar 决定)。
301
+ it("D1: jsonl append(目录 mtime 不变)→ 快路径复用 light,记录不丢不变形", () => {
302
+ writeSession({ name: "a.jsonl", id: "sa-1", assistantTexts: ["r1"] });
303
+
304
+ const first = store.collectRecords(100, "all", "root-1");
305
+ expect(first).toHaveLength(1);
306
+
307
+ // running 文件持续 append(子进程写输出)——目录 mtime 不变 → 快路径
308
+ fs.appendFileSync(
309
+ path.join(sessionsDir, "a.jsonl"),
310
+ JSON.stringify({
311
+ type: "message",
312
+ timestamp: "2026-08-14T00:02:00.000Z",
313
+ message: { role: "assistant", content: [{ type: "text", text: "streaming" }], usage: { input: 1, output: 1 }, stopReason: "stop", timestamp: 3000 },
314
+ }) + "\n",
315
+ );
316
+ const second = store.collectRecords(100, "all", "root-1");
317
+ expect(second).toHaveLength(1);
318
+ expect(second[0]?.id).toBe("sa-1");
319
+ expect(second[0]?.status).toBe("running"); // light 态不受 append 影响
320
+
321
+ // 详情走 getFullRecord 独立 stat 校验,仍能看到 append 后的完整数据
322
+ const full = store.getFullRecord("sa-1");
323
+ expect(full?.result).toContain("streaming");
324
+ });
325
+
326
+ it("D2: 新文件出现(目录 mtime 变)→ 快路径失效全扫发现(跨进程可见性保持)", () => {
327
+ writeSession({ name: "a.jsonl", id: "sa-1", assistantTexts: ["r1"] });
328
+ expect(store.collectRecords(100, "all", "root-1")).toHaveLength(1); // 建立快路径基准
329
+
330
+ // 外部进程写入新 session 文件
331
+ writeSession({ name: "ext.jsonl", id: "sa-ext", assistantTexts: ["ext"] });
332
+ const ids = store.collectRecords(100, "all", "root-1").map((r) => r.id).sort();
333
+ expect(ids).toEqual(["sa-1", "sa-ext"]);
334
+ });
335
+ });