@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,210 @@
1
+ /**
2
+ * skill-discovery — resolveSkillPath 结果缓存(IF8/#14)+ 搜索序测试。
3
+ *
4
+ * 现状该文件此前零测试,本文件为首个测试(design IF8 契约):
5
+ * - hit 不再 existsSync(计数 wrapper 观察,spyOn ESM namespace 不可配置的既有教训)
6
+ * - 未命中 undefined 也缓存(不存在的名字二次调用零 stat)
7
+ * - clearSkillPathCache 后重扫
8
+ * - project 优先于 user/npm 的搜索序
9
+ *
10
+ * [merge 注] main 侧同名文件(mock-fs 版)的 5 用例语义均已覆盖:搜索序三用例
11
+ * 同名等价;「npm 候选路径构造含 agentDir」= 本文件各用例断言 join(homeRoot,
12
+ * ".pi/agent/npm/...") 完整路径 + npm 兜底用例的多包枚举;「全 miss undefined 含
13
+ * readdirSync ENOENT 兜底」= 「未命中 undefined 也缓存」用例(homeRoot 无 npm 目录,
14
+ * 真实 fs 下 ENOENT 由实现 catch)。mock 版被本集成版取代。
15
+ *
16
+ * 隔离:node:fs 的 existsSync 用 vi.mock 包一层计数(委托真实实现);
17
+ * skills 发现根经 HostServices.discoveryRoots 端口注入(u0-data-discovery 注入化后
18
+ * 不再消费 pi SDK getAgentDir),每用例 resetCoreForTests + configureCore 假宿主,
19
+ * 假宿主根清单按 pi-host.ts skillRoots 契约形状推导自 hoisted 临时 home;
20
+ * process.cwd 用 spyOn。
21
+ */
22
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
23
+ import { mkdtempSync, mkdirSync, rmSync } from "node:fs";
24
+ import { tmpdir } from "node:os";
25
+ import { join } from "node:path";
26
+
27
+ import { configureCore, resetCoreForTests } from "../../core/host-services.ts";
28
+
29
+ const { existsSyncCalls } = vi.hoisted(() => ({
30
+ existsSyncCalls: { count: 0 },
31
+ }));
32
+
33
+ vi.mock("node:fs", async (importOriginal) => {
34
+ const actual = await importOriginal<typeof import("node:fs")>();
35
+ const realExistsSync = actual.existsSync;
36
+ // 计数 wrapper(委托真实实现)——vi.spyOn(fs, "existsSync") 在 ESM 模块
37
+ // 命名空间不可配置(record-store-index.test.ts:419 同款教训),改用 mock 工厂。
38
+ const countingExistsSync = ((p: string) => {
39
+ existsSyncCalls.count++;
40
+ return realExistsSync(p);
41
+ }) as typeof actual.existsSync;
42
+ return { ...actual, existsSync: countingExistsSync };
43
+ });
44
+
45
+ import { clearSkillPathCache, resolveSkillPath } from "../skill-discovery.ts";
46
+
47
+ // ── 临时目录工具 ──────────────────────────────────────────────
48
+
49
+ let projRoot: string;
50
+ let homeRoot: string;
51
+
52
+ beforeEach(() => {
53
+ clearSkillPathCache();
54
+ existsSyncCalls.count = 0;
55
+ projRoot = mkdtempSync(join(tmpdir(), "skill-proj-"));
56
+ homeRoot = mkdtempSync(join(tmpdir(), "skill-home-"));
57
+ // 假宿主根清单 = pi-host.ts skillRoots() 契约形状(user-pi + npm 两根,
58
+ // 每用例绑定当轮临时 home——discoveryRoots 每次调用现取,用例内改 homeRoot 即生效)
59
+ const agentDir = join(homeRoot, ".pi", "agent");
60
+ resetCoreForTests();
61
+ configureCore({
62
+ dataRoot: () => agentDir,
63
+ log: () => {},
64
+ discoveryRoots: () => ({
65
+ skills: [
66
+ { dir: join(agentDir, "skills"), source: "user-pi" },
67
+ { dir: join(agentDir, "npm", "node_modules"), source: "npm" },
68
+ ],
69
+ }),
70
+ });
71
+ });
72
+
73
+ afterEach(() => {
74
+ vi.restoreAllMocks();
75
+ resetCoreForTests();
76
+ rmSync(projRoot, { recursive: true, force: true });
77
+ rmSync(homeRoot, { recursive: true, force: true });
78
+ });
79
+
80
+ /** mkdir -p(目录存在即 resolveSkillPath 命中——检查的是目录而非 SKILL.md)。 */
81
+ function mkdirp(dir: string): void {
82
+ mkdirSync(dir, { recursive: true });
83
+ }
84
+
85
+ describe("resolveSkillPath — 搜索序(project > user > npm)", () => {
86
+ it("project 优先:同名 skill 同时存在于 project/user/npm 时返回 project 路径", () => {
87
+ const cwdSpy = vi.spyOn(process, "cwd").mockReturnValue(projRoot);
88
+ const name = "dup-skill";
89
+ mkdirp(join(projRoot, ".agents/skills", name));
90
+ mkdirp(join(homeRoot, ".pi/agent/skills", name));
91
+ mkdirp(join(homeRoot, ".pi/agent/npm/node_modules/some-pkg/skills", name));
92
+
93
+ const resolved = resolveSkillPath(name);
94
+
95
+ expect(resolved).toBe(join(projRoot, ".agents/skills", name));
96
+ expect(cwdSpy).toHaveBeenCalled();
97
+ });
98
+
99
+ it("user 次之:仅 user/npm 存在时返回 user 路径", () => {
100
+ vi.spyOn(process, "cwd").mockReturnValue(projRoot);
101
+ const name = "user-skill";
102
+ mkdirp(join(homeRoot, ".pi/agent/skills", name));
103
+ mkdirp(join(homeRoot, ".pi/agent/npm/node_modules/some-pkg/skills", name));
104
+
105
+ expect(resolveSkillPath(name)).toBe(join(homeRoot, ".pi/agent/skills", name));
106
+ });
107
+
108
+ it("npm 兜底:仅 npm 包内存在时返回包内 skills 路径(readdirSync 枚举多包候选)", () => {
109
+ vi.spyOn(process, "cwd").mockReturnValue(projRoot);
110
+ const name = "npm-skill";
111
+ // 两个包并存:断言实现枚举 npm/node_modules 下每个包(S-6 语义,防只查首个包的回退)
112
+ mkdirp(join(homeRoot, ".pi/agent/npm/node_modules/pkg-a/other", "x"));
113
+ mkdirp(join(homeRoot, ".pi/agent/npm/node_modules/some-pkg/skills", name));
114
+
115
+ expect(resolveSkillPath(name)).toBe(
116
+ join(homeRoot, ".pi/agent/npm/node_modules/some-pkg/skills", name),
117
+ );
118
+ });
119
+ });
120
+
121
+ describe("resolveSkillPath — 结果缓存(IF8/DM3)", () => {
122
+ it("hit 不再 existsSync:二次调用零 stat", () => {
123
+ vi.spyOn(process, "cwd").mockReturnValue(projRoot);
124
+ const name = "cache-hit";
125
+ const expected = join(projRoot, ".agents/skills", name);
126
+ mkdirp(expected);
127
+
128
+ const first = resolveSkillPath(name);
129
+ const callsAfterFirst = existsSyncCalls.count;
130
+ expect(first).toBe(expected);
131
+ expect(callsAfterFirst).toBeGreaterThan(0);
132
+
133
+ const second = resolveSkillPath(name);
134
+ expect(second).toBe(expected);
135
+ expect(existsSyncCalls.count).toBe(callsAfterFirst); // 零增量
136
+ });
137
+
138
+ it("未命中 undefined 也缓存:不存在的名字二次调用零 stat", () => {
139
+ vi.spyOn(process, "cwd").mockReturnValue(projRoot);
140
+ const name = "no-such-skill-xyz";
141
+
142
+ const first = resolveSkillPath(name);
143
+ expect(first).toBeUndefined();
144
+ const callsAfterFirst = existsSyncCalls.count;
145
+ expect(callsAfterFirst).toBeGreaterThan(0);
146
+
147
+ const second = resolveSkillPath(name);
148
+ expect(second).toBeUndefined();
149
+ expect(existsSyncCalls.count).toBe(callsAfterFirst); // 零增量(未命中也缓存)
150
+ });
151
+
152
+ it("clearSkillPathCache 后重扫:计数恢复增长且结果一致", () => {
153
+ vi.spyOn(process, "cwd").mockReturnValue(projRoot);
154
+ const name = "cache-clear";
155
+ const expected = join(projRoot, ".agents/skills", name);
156
+ mkdirp(expected);
157
+
158
+ expect(resolveSkillPath(name)).toBe(expected);
159
+ const callsAfterFirst = existsSyncCalls.count;
160
+ expect(callsAfterFirst).toBeGreaterThan(0);
161
+
162
+ clearSkillPathCache();
163
+ expect(resolveSkillPath(name)).toBe(expected);
164
+ expect(existsSyncCalls.count).toBeGreaterThan(callsAfterFirst); // 重扫发生
165
+ });
166
+ });
167
+
168
+ describe("resolveSkillPath — `..` 穿越守卫(三通道对称修复:skill 名是名字不是路径)", () => {
169
+ it("project 源:含 .. 逃逸 skills 根的名字 → not found(即使逃逸目标真实存在)", () => {
170
+ vi.spyOn(process, "cwd").mockReturnValue(projRoot);
171
+ // 逃逸目标真实存在:projRoot/.agents/skills/../../escape-target = projRoot/escape-target。
172
+ // 旧实现 path.resolve 吸收 .. 后 existsSync 命中 → 返回树外目录;守卫后必须 undefined。
173
+ mkdirp(join(projRoot, "escape-target"));
174
+
175
+ expect(resolveSkillPath("../../escape-target")).toBeUndefined();
176
+ });
177
+
178
+ it("user 源:含 .. 逃逸的名字 → not found(同样不受 existsSync 命中误导)", () => {
179
+ vi.spyOn(process, "cwd").mockReturnValue(projRoot);
180
+ // user skills 根 = homeRoot/.pi/agent/skills;../../escape-target → homeRoot/escape-target。
181
+ mkdirp(join(homeRoot, "escape-target"));
182
+
183
+ expect(resolveSkillPath("../../escape-target")).toBeUndefined();
184
+ });
185
+
186
+ it("npm 源:含 .. 逃逸包内 skills 根的名字 → not found", () => {
187
+ vi.spyOn(process, "cwd").mockReturnValue(projRoot);
188
+ // npm skills 根 = homeRoot/.pi/agent/npm/node_modules/pkg/skills;"../steal" 逃逸到 node_modules/<pkg>/steal。
189
+ mkdirp(join(homeRoot, ".pi/agent/npm/node_modules/pkg-a/steal"));
190
+
191
+ expect(resolveSkillPath("../steal")).toBeUndefined();
192
+ });
193
+
194
+ it("根内归一化不被误拒:sub/../real 解析后仍在 skills 根内,正常命中", () => {
195
+ vi.spyOn(process, "cwd").mockReturnValue(projRoot);
196
+ const real = join(projRoot, ".agents/skills", "real");
197
+ mkdirp(real);
198
+
199
+ // 守卫只拒逃逸,不拒归一化:root/sub/../real = root/real(根内)→ 命中。
200
+ expect(resolveSkillPath("sub/../real")).toBe(real);
201
+ });
202
+
203
+ it("绝对路径形式的 skill 名 → not found(名字不具备绝对寻址能力)", () => {
204
+ vi.spyOn(process, "cwd").mockReturnValue(projRoot);
205
+ mkdirp(join(projRoot, "outside-abs"));
206
+
207
+ // resolve(root, "/outside-abs") = /outside-abs —— 树外,拒。
208
+ expect(resolveSkillPath(join(projRoot, "outside-abs"))).toBeUndefined();
209
+ });
210
+ });
@@ -0,0 +1,197 @@
1
+ // src/orchestration/__tests__/test-mocks.ts
2
+ //
3
+ // Pi ExtensionAPI / ExtensionContext 测试 mock 工厂(PR #173 review S8)。
4
+ //
5
+ // 为什么需要工厂:此前测试用 `as never` 注入残缺 mock(如 `{ appendEntry: vi.fn() }`
6
+ // 直接塞进 `JsonlRunStoreOptions.pi`)。`as never` 是 bottom type 断言,mock 缺字段时
7
+ // 编译期零报错,比 `as unknown as T` 更危险。工厂返回**类型完整**的对象——被 mock
8
+ // 接口(真实 SDK 类型,tsc 按根 tsconfig 排除规则不检查 __tests__,但编辑器 LSP 与
9
+ // 显式 tsc 文件级检查会检查)新增成员时,这里补默认值即暴露,用例侧不再有断言后门。
10
+ //
11
+ // 语义约定:
12
+ // - 默认值贴近真实行为:sessionManager.getEntries 返回浅拷贝(真实 SessionManager
13
+ // 契约);appendEntry 捕获变体追加完整 CustomEntry(真实 pi.appendEntry 即向 session
14
+ // 追加 CustomEntry);isIdle → true、hasPendingMessages → false 等按真实语义取值。
15
+ // - 测试真正消费的成员(jsonl-run-store 只读 ctx.sessionManager.getEntries 与
16
+ // pi.appendEntry)行为真实;其余成员为 no-op spy(vi.fn()),调用不炸但返回
17
+ // undefined——本包测试不消费它们。
18
+ // - modelRegistry / ui.theme 是含 private 字段的 nominal class:真实构造需
19
+ // ModelRuntime.create()(async + 读用户级 models.json)/ loadTheme 读包文件。
20
+ // pi-coding-agent 在本包测试中仅剩 import type(转译擦除,无运行时导出可用),
21
+ // 结构构造又被 private 字段阻断。二者经 fail-loud Proxy 提供:误用时立即抛错(而非
22
+ // 静默 undefined),把「缺 mock」变成显式失败。这是本文件仅有的两处断言收敛点。
23
+
24
+ import { vi } from "vitest";
25
+
26
+ import type {
27
+ CustomEntry,
28
+ ExtensionAPI,
29
+ ExtensionContext,
30
+ ExtensionUIContext,
31
+ ModelRegistry,
32
+ Theme,
33
+ } from "@earendil-works/pi-coding-agent";
34
+
35
+ /**
36
+ * 误用即抛错的占位成员:比静默返回 undefined 的 no-op 更早暴露「缺 mock」。
37
+ *
38
+ * T 收窄为 object:调用点(ModelRegistry/Theme)均为对象类型;Proxy 静态类型是 `{}`,
39
+ * 目标是 nominal class(private 字段阻断结构构造),单重 `as T` 断言已足够表达
40
+ * 「结构上无法构造,运行时 fail-loud 兜底」的意图,无需 `as unknown as T` 双重断言。
41
+ */
42
+ function failLoud<T extends object>(memberPath: string): T {
43
+ return new Proxy(
44
+ {},
45
+ {
46
+ get(_target, prop) {
47
+ throw new Error(
48
+ `[test-mocks] ${memberPath}.${String(prop)} 未提供 mock 实现——` +
49
+ `本测试不应消费该成员;确需消费时经 overrides 显式提供`,
50
+ );
51
+ },
52
+ },
53
+ ) as T;
54
+ }
55
+
56
+ /**
57
+ * 构造类型完整的 ExtensionContext。
58
+ *
59
+ * @param entries sessionManager.getEntries 的返回源(浅拷贝返回)。与 mkPi(entries)
60
+ * 共享同一数组时可复现真实 pi 行为:appendEntry 写入 ↔ getEntries 读出。
61
+ * @param overrides 覆写任意 ctx 字段(浅合并,整体替换成员,最后应用)。
62
+ */
63
+ export function mkCtx(
64
+ entries: CustomEntry[] = [],
65
+ overrides: Partial<ExtensionContext> = {},
66
+ ): ExtensionContext {
67
+ const ctx: ExtensionContext = {
68
+ ui: mkUi(),
69
+ // pi 在 xyz-agent 下以 rpc 子进程模式运行;rpc 模式 hasUI 为 true(SDK 契约注释)
70
+ mode: "rpc",
71
+ hasUI: true,
72
+ cwd: "/tmp",
73
+ sessionManager: {
74
+ getCwd: () => "/tmp",
75
+ getSessionDir: () => "/tmp/sessions-test",
76
+ getSessionId: () => "session-test",
77
+ // 真实语义:首条 assistant 消息前 session 文件未落盘,返回 undefined
78
+ getSessionFile: () => undefined,
79
+ getLeafId: () => null,
80
+ getLeafEntry: () => undefined,
81
+ getEntry: () => undefined,
82
+ getLabel: () => undefined,
83
+ getBranch: () => [],
84
+ buildContextEntries: () => [],
85
+ getHeader: () => null,
86
+ // 真实 SessionManager.getEntries 返回浅拷贝
87
+ getEntries: () => [...entries],
88
+ getTree: () => [],
89
+ getSessionName: () => undefined,
90
+ },
91
+ modelRegistry: failLoud<ModelRegistry>("ctx.modelRegistry"),
92
+ // SDK 契约:model / signal 在 agent 未流式输出时合法为 undefined
93
+ model: undefined,
94
+ isIdle: vi.fn(() => true),
95
+ isProjectTrusted: vi.fn(() => true),
96
+ signal: undefined,
97
+ abort: vi.fn(),
98
+ hasPendingMessages: vi.fn(() => false),
99
+ shutdown: vi.fn(),
100
+ getContextUsage: vi.fn(() => undefined),
101
+ compact: vi.fn(),
102
+ getSystemPrompt: vi.fn(() => ""),
103
+ };
104
+ return Object.assign(ctx, overrides);
105
+ }
106
+
107
+ /** ExtensionUIContext 完整 no-op mock(theme 为 fail-loud 占位,见文件头说明)。 */
108
+ function mkUi(): ExtensionUIContext {
109
+ return {
110
+ select: vi.fn(async () => undefined),
111
+ confirm: vi.fn(async () => false),
112
+ input: vi.fn(async () => undefined),
113
+ notify: vi.fn(),
114
+ onTerminalInput: vi.fn(() => () => {}),
115
+ setStatus: vi.fn(),
116
+ setWorkingMessage: vi.fn(),
117
+ setWorkingVisible: vi.fn(),
118
+ setWorkingIndicator: vi.fn(),
119
+ setHiddenThinkingLabel: vi.fn(),
120
+ setWidget: vi.fn(),
121
+ setFooter: vi.fn(),
122
+ setHeader: vi.fn(),
123
+ setTitle: vi.fn(),
124
+ custom: vi.fn(),
125
+ pasteToEditor: vi.fn(),
126
+ setEditorText: vi.fn(),
127
+ getEditorText: vi.fn(() => ""),
128
+ editor: vi.fn(async () => undefined),
129
+ addAutocompleteProvider: vi.fn(),
130
+ setEditorComponent: vi.fn(),
131
+ getEditorComponent: vi.fn(() => undefined),
132
+ theme: failLoud<Theme>("ctx.ui.theme"),
133
+ getAllThemes: vi.fn(() => []),
134
+ getTheme: vi.fn(() => undefined),
135
+ setTheme: vi.fn(() => ({ success: true })),
136
+ getToolsExpanded: vi.fn(() => false),
137
+ setToolsExpanded: vi.fn(),
138
+ };
139
+ }
140
+
141
+ /**
142
+ * 构造类型完整的 ExtensionAPI。
143
+ *
144
+ * @param entries 给定时,appendEntry 追加完整 CustomEntry 到该数组(真实
145
+ * pi.appendEntry 语义:向 session 追加 custom entry);缺省为纯 spy(可断言
146
+ * 调用次数、无副作用)。
147
+ * @param overrides 覆写任意 pi 字段(浅合并,最后应用)。
148
+ */
149
+ export function mkPi(
150
+ entries?: CustomEntry[],
151
+ overrides: Partial<ExtensionAPI> = {},
152
+ ): ExtensionAPI {
153
+ const appendEntry =
154
+ entries === undefined
155
+ ? vi.fn()
156
+ : vi.fn((customType: string, data?: unknown) => {
157
+ entries.push({
158
+ type: "custom",
159
+ customType,
160
+ data,
161
+ id: `entry-${entries.length}`,
162
+ parentId: null,
163
+ timestamp: new Date().toISOString(),
164
+ });
165
+ });
166
+ const pi: ExtensionAPI = {
167
+ on: vi.fn(),
168
+ registerTool: vi.fn(),
169
+ registerCommand: vi.fn(),
170
+ registerShortcut: vi.fn(),
171
+ registerFlag: vi.fn(),
172
+ getFlag: vi.fn(() => undefined),
173
+ registerMessageRenderer: vi.fn(),
174
+ registerEntryRenderer: vi.fn(),
175
+ sendMessage: vi.fn(),
176
+ sendUserMessage: vi.fn(),
177
+ appendEntry,
178
+ setSessionName: vi.fn(),
179
+ getSessionName: vi.fn(() => undefined),
180
+ setLabel: vi.fn(),
181
+ exec: vi.fn(),
182
+ getActiveTools: vi.fn(() => []),
183
+ getAllTools: vi.fn(() => []),
184
+ setActiveTools: vi.fn(),
185
+ getCommands: vi.fn(() => []),
186
+ setModel: vi.fn(async () => true),
187
+ getThinkingLevel: vi.fn(),
188
+ setThinkingLevel: vi.fn(),
189
+ registerProvider: vi.fn(),
190
+ unregisterProvider: vi.fn(),
191
+ events: {
192
+ emit: vi.fn(),
193
+ on: vi.fn(() => () => {}),
194
+ },
195
+ };
196
+ return Object.assign(pi, overrides);
197
+ }