@stigmer/runner 3.15.1 → 3.15.2-dev.20260913223433

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 (518) hide show
  1. package/README.md +2 -4
  2. package/dist/.build-fingerprint +1 -1
  3. package/dist/__test-utils__/execution-record-fixture.d.ts +14 -0
  4. package/dist/__test-utils__/execution-record-fixture.js +4 -0
  5. package/dist/__test-utils__/execution-record-fixture.js.map +1 -1
  6. package/dist/__test-utils__/file-review-projection.d.ts +40 -0
  7. package/dist/__test-utils__/file-review-projection.js +136 -0
  8. package/dist/__test-utils__/file-review-projection.js.map +1 -0
  9. package/dist/__test-utils__/harness-contract/types.d.ts +23 -1
  10. package/dist/__test-utils__/harness-contract/types.js +15 -0
  11. package/dist/__test-utils__/harness-contract/types.js.map +1 -1
  12. package/dist/__test-utils__/hermetic-activity.d.ts +44 -12
  13. package/dist/__test-utils__/hermetic-activity.js +52 -14
  14. package/dist/__test-utils__/hermetic-activity.js.map +1 -1
  15. package/dist/__test-utils__/model-registry-fixture.d.ts +32 -7
  16. package/dist/__test-utils__/model-registry-fixture.js +31 -7
  17. package/dist/__test-utils__/model-registry-fixture.js.map +1 -1
  18. package/dist/__test-utils__/turn-input-fixture.d.ts +1 -1
  19. package/dist/__test-utils__/turn-input-fixture.js +1 -1
  20. package/dist/__test-utils__/turn-input-fixture.js.map +1 -1
  21. package/dist/activities/call-agent.js +2 -2
  22. package/dist/activities/call-agent.js.map +1 -1
  23. package/dist/activities/execute-cursor/__test-utils__/contract-subject.js +45 -3
  24. package/dist/activities/execute-cursor/__test-utils__/contract-subject.js.map +1 -1
  25. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.d.ts +0 -9
  26. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +2 -41
  27. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -1
  28. package/dist/activities/execute-cursor/adapter.d.ts +7 -5
  29. package/dist/activities/execute-cursor/adapter.js +7 -5
  30. package/dist/activities/execute-cursor/adapter.js.map +1 -1
  31. package/dist/activities/execute-cursor/approval-policy.d.ts +5 -2
  32. package/dist/activities/execute-cursor/approval-policy.js +5 -2
  33. package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
  34. package/dist/activities/execute-cursor/approval-state.d.ts +16 -8
  35. package/dist/activities/execute-cursor/approval-state.js +6 -3
  36. package/dist/activities/execute-cursor/approval-state.js.map +1 -1
  37. package/dist/activities/execute-cursor/cas-observations.d.ts +14 -2
  38. package/dist/activities/execute-cursor/cas-observations.js +19 -2
  39. package/dist/activities/execute-cursor/cas-observations.js.map +1 -1
  40. package/dist/activities/execute-cursor/cursor-capabilities.d.ts +8 -4
  41. package/dist/activities/execute-cursor/cursor-capabilities.js +21 -4
  42. package/dist/activities/execute-cursor/cursor-capabilities.js.map +1 -1
  43. package/dist/activities/execute-cursor/message-translator.d.ts +12 -20
  44. package/dist/activities/execute-cursor/message-translator.js +19 -32
  45. package/dist/activities/execute-cursor/message-translator.js.map +1 -1
  46. package/dist/activities/execute-cursor/prompt-builder.d.ts +48 -58
  47. package/dist/activities/execute-cursor/prompt-builder.js +76 -95
  48. package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
  49. package/dist/activities/execute-cursor/turn-boundary.d.ts +27 -55
  50. package/dist/activities/execute-cursor/turn-boundary.js +30 -90
  51. package/dist/activities/execute-cursor/turn-boundary.js.map +1 -1
  52. package/dist/activities/execute-cursor/turn-settle.d.ts +11 -7
  53. package/dist/activities/execute-cursor/turn-settle.js +37 -49
  54. package/dist/activities/execute-cursor/turn-settle.js.map +1 -1
  55. package/dist/activities/execute-cursor/turn-setup.d.ts +16 -24
  56. package/dist/activities/execute-cursor/turn-setup.js +29 -59
  57. package/dist/activities/execute-cursor/turn-setup.js.map +1 -1
  58. package/dist/activities/execute-cursor/turn-stream.d.ts +3 -8
  59. package/dist/activities/execute-cursor/turn-stream.js +9 -22
  60. package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
  61. package/dist/activities/execute-cursor/turn.js +5 -1
  62. package/dist/activities/execute-cursor/turn.js.map +1 -1
  63. package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.d.ts +93 -0
  64. package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.js +348 -0
  65. package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.js.map +1 -0
  66. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.d.ts +170 -0
  67. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js +225 -0
  68. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js.map +1 -0
  69. package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.d.ts +24 -0
  70. package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js +41 -0
  71. package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js.map +1 -0
  72. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.d.ts +54 -0
  73. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js +72 -0
  74. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js.map +1 -0
  75. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.d.ts +187 -23
  76. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js +238 -27
  77. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js.map +1 -1
  78. package/dist/activities/execute-deep-agent/adapter.d.ts +41 -0
  79. package/dist/activities/execute-deep-agent/adapter.js +78 -0
  80. package/dist/activities/execute-deep-agent/adapter.js.map +1 -0
  81. package/dist/activities/execute-deep-agent/cas-capture-observer.d.ts +8 -0
  82. package/dist/activities/execute-deep-agent/cas-capture-observer.js +9 -0
  83. package/dist/activities/execute-deep-agent/cas-capture-observer.js.map +1 -1
  84. package/dist/activities/execute-deep-agent/deep-agent-capabilities.d.ts +31 -0
  85. package/dist/activities/execute-deep-agent/deep-agent-capabilities.js +40 -0
  86. package/dist/activities/execute-deep-agent/deep-agent-capabilities.js.map +1 -0
  87. package/dist/activities/execute-deep-agent/execution-state.d.ts +4 -11
  88. package/dist/activities/execute-deep-agent/execution-state.js +4 -12
  89. package/dist/activities/execute-deep-agent/execution-state.js.map +1 -1
  90. package/dist/activities/execute-deep-agent/hitl.d.ts +49 -47
  91. package/dist/activities/execute-deep-agent/hitl.js +77 -103
  92. package/dist/activities/execute-deep-agent/hitl.js.map +1 -1
  93. package/dist/activities/execute-deep-agent/prompt-builder.d.ts +51 -23
  94. package/dist/activities/execute-deep-agent/prompt-builder.js +115 -76
  95. package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
  96. package/dist/activities/execute-deep-agent/status-builder-shared.d.ts +18 -32
  97. package/dist/activities/execute-deep-agent/status-builder-shared.js +10 -65
  98. package/dist/activities/execute-deep-agent/status-builder-shared.js.map +1 -1
  99. package/dist/activities/execute-deep-agent/streaming-side-effects.d.ts +10 -8
  100. package/dist/activities/execute-deep-agent/streaming-side-effects.js +12 -14
  101. package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
  102. package/dist/activities/execute-deep-agent/subagent-tracker.d.ts +36 -13
  103. package/dist/activities/execute-deep-agent/subagent-tracker.js +76 -39
  104. package/dist/activities/execute-deep-agent/subagent-tracker.js.map +1 -1
  105. package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +25 -28
  106. package/dist/activities/execute-deep-agent/subagent-transformer.js +29 -82
  107. package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
  108. package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +3 -2
  109. package/dist/activities/execute-deep-agent/subagent-wiring.js +2 -2
  110. package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
  111. package/dist/activities/execute-deep-agent/turn-settle.d.ts +42 -0
  112. package/dist/activities/execute-deep-agent/turn-settle.js +146 -0
  113. package/dist/activities/execute-deep-agent/turn-settle.js.map +1 -0
  114. package/dist/activities/execute-deep-agent/turn-setup.d.ts +212 -0
  115. package/dist/activities/execute-deep-agent/turn-setup.js +430 -0
  116. package/dist/activities/execute-deep-agent/turn-setup.js.map +1 -0
  117. package/dist/activities/execute-deep-agent/turn-stream.d.ts +97 -0
  118. package/dist/activities/execute-deep-agent/turn-stream.js +242 -0
  119. package/dist/activities/execute-deep-agent/turn-stream.js.map +1 -0
  120. package/dist/activities/execute-deep-agent/turn.d.ts +42 -0
  121. package/dist/activities/execute-deep-agent/turn.js +116 -0
  122. package/dist/activities/execute-deep-agent/turn.js.map +1 -0
  123. package/dist/activities/execute-deep-agent/v3-event-recorder.d.ts +6 -5
  124. package/dist/activities/execute-deep-agent/v3-event-recorder.js +6 -5
  125. package/dist/activities/execute-deep-agent/v3-event-recorder.js.map +1 -1
  126. package/dist/activities/execute-deep-agent/v3-events.d.ts +1 -1
  127. package/dist/activities/execute-deep-agent/v3-events.js +1 -1
  128. package/dist/activities/execute-deep-agent/v3-status-builder.d.ts +60 -17
  129. package/dist/activities/execute-deep-agent/v3-status-builder.js +72 -57
  130. package/dist/activities/execute-deep-agent/v3-status-builder.js.map +1 -1
  131. package/dist/harness/approval-decisions.d.ts +61 -0
  132. package/dist/harness/approval-decisions.js +102 -0
  133. package/dist/harness/approval-decisions.js.map +1 -0
  134. package/dist/harness/capabilities.d.ts +16 -5
  135. package/dist/harness/capabilities.js +9 -0
  136. package/dist/harness/capabilities.js.map +1 -1
  137. package/dist/harness/capture.d.ts +144 -0
  138. package/dist/harness/capture.js +219 -0
  139. package/dist/harness/capture.js.map +1 -0
  140. package/dist/harness/persist-chokepoint.d.ts +19 -4
  141. package/dist/harness/persist-chokepoint.js +17 -4
  142. package/dist/harness/persist-chokepoint.js.map +1 -1
  143. package/dist/harness/run-turn.js +115 -29
  144. package/dist/harness/run-turn.js.map +1 -1
  145. package/dist/harness/terminal-table.d.ts +40 -11
  146. package/dist/harness/terminal-table.js +58 -19
  147. package/dist/harness/terminal-table.js.map +1 -1
  148. package/dist/harness/turn-context.d.ts +141 -76
  149. package/dist/harness/turn-context.js +194 -99
  150. package/dist/harness/turn-context.js.map +1 -1
  151. package/dist/harness/types.d.ts +65 -12
  152. package/dist/harness/types.js +8 -2
  153. package/dist/harness/types.js.map +1 -1
  154. package/dist/harness-adapters.d.ts +12 -8
  155. package/dist/harness-adapters.js +17 -9
  156. package/dist/harness-adapters.js.map +1 -1
  157. package/dist/middleware/approval-gate.d.ts +1 -1
  158. package/dist/middleware/approval-gate.js +1 -1
  159. package/dist/middleware/approval-gate.js.map +1 -1
  160. package/dist/middleware/cost-advisory.d.ts +39 -0
  161. package/dist/middleware/{cost-cap.js → cost-advisory.js} +37 -55
  162. package/dist/middleware/cost-advisory.js.map +1 -0
  163. package/dist/middleware/index.d.ts +23 -12
  164. package/dist/middleware/index.js +26 -18
  165. package/dist/middleware/index.js.map +1 -1
  166. package/dist/middleware/types.d.ts +3 -2
  167. package/dist/runner-manager.js +1 -5
  168. package/dist/runner-manager.js.map +1 -1
  169. package/dist/runner.js +1 -5
  170. package/dist/runner.js.map +1 -1
  171. package/dist/shared/attachment-resolver.d.ts +41 -18
  172. package/dist/shared/attachment-resolver.js +178 -46
  173. package/dist/shared/attachment-resolver.js.map +1 -1
  174. package/dist/shared/attachment-zip.d.ts +85 -0
  175. package/dist/shared/attachment-zip.js +185 -0
  176. package/dist/shared/attachment-zip.js.map +1 -0
  177. package/dist/shared/connect-backfill.js +5 -4
  178. package/dist/shared/connect-backfill.js.map +1 -1
  179. package/dist/shared/cost-guard.d.ts +3 -3
  180. package/dist/shared/cost-guard.js +3 -3
  181. package/dist/shared/execution-status-writer.d.ts +5 -5
  182. package/dist/shared/execution-status-writer.js +5 -5
  183. package/dist/shared/extract-structured-output.d.ts +8 -7
  184. package/dist/shared/extract-structured-output.js +8 -7
  185. package/dist/shared/extract-structured-output.js.map +1 -1
  186. package/dist/shared/filereview/cas-progress.d.ts +1 -18
  187. package/dist/shared/filereview/cas-progress.js +4 -0
  188. package/dist/shared/filereview/cas-progress.js.map +1 -1
  189. package/dist/shared/filereview/cas-touched.d.ts +56 -0
  190. package/dist/shared/filereview/cas-touched.js +63 -0
  191. package/dist/shared/filereview/cas-touched.js.map +1 -0
  192. package/dist/shared/filereview/index.d.ts +2 -1
  193. package/dist/shared/filereview/index.js +2 -1
  194. package/dist/shared/filereview/index.js.map +1 -1
  195. package/dist/shared/mcp-resolver.d.ts +4 -5
  196. package/dist/shared/mcp-resolver.js +4 -5
  197. package/dist/shared/mcp-resolver.js.map +1 -1
  198. package/dist/shared/persist-decision.d.ts +3 -4
  199. package/dist/shared/persist-decision.js +3 -4
  200. package/dist/shared/persist-decision.js.map +1 -1
  201. package/dist/shared/plan-mode-permissions.d.ts +2 -2
  202. package/dist/shared/plan-mode-permissions.js +2 -2
  203. package/dist/shared/plan-mode-prompt.d.ts +1 -1
  204. package/dist/shared/plan-mode-prompt.js +1 -1
  205. package/dist/shared/prompt-sections.d.ts +163 -0
  206. package/dist/shared/prompt-sections.js +164 -0
  207. package/dist/shared/prompt-sections.js.map +1 -0
  208. package/dist/shared/skill-resolver.d.ts +4 -2
  209. package/dist/shared/skill-resolver.js +4 -2
  210. package/dist/shared/skill-resolver.js.map +1 -1
  211. package/dist/shared/tool-rounds.d.ts +32 -0
  212. package/dist/shared/tool-rounds.js +43 -0
  213. package/dist/shared/tool-rounds.js.map +1 -1
  214. package/dist/shared/tool-row.d.ts +34 -0
  215. package/dist/shared/tool-row.js +52 -0
  216. package/dist/shared/tool-row.js.map +1 -1
  217. package/dist/shared/workspace/session-provision.d.ts +3 -3
  218. package/dist/shared/workspace/session-provision.js +3 -3
  219. package/dist/shared/workspace/stigmer-link.d.ts +3 -3
  220. package/dist/shared/workspace/stigmer-link.js +3 -3
  221. package/dist/shared/workspace/writeback-coordinator.d.ts +15 -22
  222. package/dist/shared/workspace/writeback-coordinator.js +16 -53
  223. package/dist/shared/workspace/writeback-coordinator.js.map +1 -1
  224. package/dist/tools/index.d.ts +1 -1
  225. package/dist/tools/index.js +1 -1
  226. package/package.json +4 -4
  227. package/src/__test-utils__/__tests__/harness-contract-self-check.test.ts +1 -0
  228. package/src/__test-utils__/execution-record-fixture.ts +18 -2
  229. package/src/__test-utils__/file-review-projection.ts +156 -0
  230. package/src/__test-utils__/git-workspace-fixture.ts +58 -0
  231. package/src/__test-utils__/harness-boot-order-child.ts +7 -2
  232. package/src/__test-utils__/harness-contract/contract.ts +33 -20
  233. package/src/__test-utils__/harness-contract/recording-sink.ts +11 -1
  234. package/src/__test-utils__/harness-contract/runtime-contract.ts +328 -10
  235. package/src/__test-utils__/harness-contract/scripted-adapter.ts +38 -6
  236. package/src/__test-utils__/harness-contract/types.ts +27 -3
  237. package/src/__test-utils__/hermetic-activity.ts +52 -14
  238. package/src/__test-utils__/model-registry-fixture.ts +32 -7
  239. package/src/__test-utils__/module-specifiers.ts +24 -0
  240. package/src/__test-utils__/turn-input-fixture.ts +2 -2
  241. package/src/__tests__/harness-boot-order.test.ts +9 -2
  242. package/src/activities/call-agent.ts +2 -2
  243. package/src/activities/execute-cursor/__test-utils__/contract-subject.ts +47 -3
  244. package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +2 -48
  245. package/src/activities/execute-cursor/__tests__/approval-decisions-agree.test.ts +1 -1
  246. package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +6 -1
  247. package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +52 -27
  248. package/src/activities/execute-cursor/__tests__/cas-observations.test.ts +25 -0
  249. package/src/activities/execute-cursor/__tests__/goldens/prompt.enhanced.everything.prompt.md +166 -0
  250. package/src/activities/execute-cursor/__tests__/goldens/prompt.enhanced.plan-mode.prompt.md +186 -0
  251. package/src/activities/execute-cursor/__tests__/goldens/prompt.hitl.recovery.prompt.md +191 -0
  252. package/src/activities/execute-cursor/__tests__/goldens/prompt.hitl.reinvocation.prompt.md +13 -0
  253. package/src/activities/execute-cursor/__tests__/goldens/prompt.resumed.prefixed.prompt.md +32 -0
  254. package/src/activities/execute-cursor/__tests__/goldens/prompt.resumed.raw.prompt.md +1 -0
  255. package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +1 -2
  256. package/src/activities/execute-cursor/__tests__/hermetic/goldens/sdk-error-at-create.status.json +4 -0
  257. package/src/activities/execute-cursor/__tests__/hermetic/harness-contract.test.ts +24 -4
  258. package/src/activities/execute-cursor/__tests__/hermetic/stream-self-stop-arms.test.ts +6 -5
  259. package/src/activities/execute-cursor/__tests__/hermetic/thrown-error-arms.test.ts +9 -0
  260. package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +11 -8
  261. package/src/activities/execute-cursor/__tests__/prompt-goldens.test.ts +229 -0
  262. package/src/activities/execute-cursor/__tests__/same-identity-reproposal.test.ts +3 -3
  263. package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +2 -2
  264. package/src/activities/execute-cursor/__tests__/turn-boundary.test.ts +78 -234
  265. package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +1 -26
  266. package/src/activities/execute-cursor/adapter.ts +7 -5
  267. package/src/activities/execute-cursor/approval-policy.ts +5 -2
  268. package/src/activities/execute-cursor/approval-state.ts +16 -8
  269. package/src/activities/execute-cursor/cas-observations.ts +20 -2
  270. package/src/activities/execute-cursor/cursor-capabilities.ts +24 -5
  271. package/src/activities/execute-cursor/message-translator.ts +29 -41
  272. package/src/activities/execute-cursor/prompt-builder.ts +99 -157
  273. package/src/activities/execute-cursor/turn-boundary.ts +34 -140
  274. package/src/activities/execute-cursor/turn-settle.ts +37 -51
  275. package/src/activities/execute-cursor/turn-setup.ts +30 -70
  276. package/src/activities/execute-cursor/turn-stream.ts +8 -31
  277. package/src/activities/execute-cursor/turn.ts +5 -1
  278. package/src/activities/execute-deep-agent/__test-utils__/contract-subject.ts +391 -0
  279. package/src/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.ts +313 -0
  280. package/src/activities/execute-deep-agent/__test-utils__/hitl-script.ts +48 -0
  281. package/src/activities/execute-deep-agent/__test-utils__/scripted-model-module.ts +85 -0
  282. package/src/activities/execute-deep-agent/__test-utils__/scripted-model.ts +383 -36
  283. package/src/activities/execute-deep-agent/__tests__/adapter-graph-is-sdk-free.test.ts +76 -0
  284. package/src/activities/execute-deep-agent/__tests__/adapter-is-temporal-free.test.ts +47 -0
  285. package/src/activities/execute-deep-agent/__tests__/adapter.test.ts +80 -0
  286. package/src/activities/execute-deep-agent/__tests__/cas-capture-backend.test.ts +1 -1
  287. package/src/activities/execute-deep-agent/__tests__/execution-state-extended.test.ts +0 -2
  288. package/src/activities/execute-deep-agent/__tests__/execution-state.test.ts +0 -3
  289. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.build-from-plan.prompt.md +172 -0
  290. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.everything.prompt.md +167 -0
  291. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.minimal.prompt.md +36 -0
  292. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.plan-mode.prompt.md +178 -0
  293. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.skills-below-threshold.prompt.md +158 -0
  294. package/src/activities/execute-deep-agent/__tests__/hermetic/file-review-capture.test.ts +210 -0
  295. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn1.status.json +74 -0
  296. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn2.status.json +104 -0
  297. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve.turn2.status.json +78 -0
  298. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/file-review-capture.status.json +138 -0
  299. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/gate.turn1.status.json +68 -0
  300. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn1.status.json +154 -0
  301. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn2.status.json +116 -0
  302. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.after-tool.status.json +69 -0
  303. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.mid-tool.status.json +67 -0
  304. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/plain-turn.status.json +47 -0
  305. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/platform-stop.status.json +68 -0
  306. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/recursion-limit.persisted.status.json +280 -0
  307. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/reject.turn2.status.json +77 -0
  308. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/resolution-error.status.json +18 -0
  309. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn2.status.json +99 -0
  310. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn3.status.json +109 -0
  311. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/skip.turn2.status.json +76 -0
  312. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/structured-output.text-fallback.status.json +51 -0
  313. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/structured-output.tier2.status.json +51 -0
  314. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/structured-output.tool-strategy.status.json +56 -0
  315. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sub-agent-delegation.status.json +90 -0
  316. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/tool-call.status.json +70 -0
  317. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/worker-shutdown.status.json +71 -0
  318. package/src/activities/execute-deep-agent/__tests__/hermetic/harness-contract.test.ts +144 -0
  319. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-approve-all-lease.test.ts +184 -0
  320. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-round-trips.test.ts +203 -0
  321. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-sequential-gates.test.ts +170 -0
  322. package/src/activities/execute-deep-agent/__tests__/hermetic/inline-artifact-across-gate.test.ts +175 -0
  323. package/src/activities/execute-deep-agent/__tests__/hermetic/pause-vs-shutdown.test.ts +206 -0
  324. package/src/activities/execute-deep-agent/__tests__/hermetic/plain-turn.test.ts +142 -0
  325. package/src/activities/execute-deep-agent/__tests__/hermetic/platform-stop.test.ts +126 -0
  326. package/src/activities/execute-deep-agent/__tests__/hermetic/recursion-limit.test.ts +139 -0
  327. package/src/activities/execute-deep-agent/__tests__/hermetic/resolution-error.test.ts +122 -0
  328. package/src/activities/execute-deep-agent/__tests__/hermetic/structured-output.test.ts +236 -0
  329. package/src/activities/execute-deep-agent/__tests__/hermetic/sub-agent-delegation.test.ts +161 -0
  330. package/src/activities/execute-deep-agent/__tests__/hermetic/tool-call.test.ts +157 -0
  331. package/src/activities/execute-deep-agent/__tests__/hermetic/workspace-lock-timeout.test.ts +152 -0
  332. package/src/activities/execute-deep-agent/__tests__/hitl.test.ts +50 -122
  333. package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +8 -6
  334. package/src/activities/execute-deep-agent/__tests__/plan-mode-path-normalization.test.ts +2 -2
  335. package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +35 -35
  336. package/src/activities/execute-deep-agent/__tests__/prompt-goldens.test.ts +247 -0
  337. package/src/activities/execute-deep-agent/__tests__/scripted-model.test.ts +329 -0
  338. package/src/activities/execute-deep-agent/__tests__/status-builder-shared.test.ts +1 -20
  339. package/src/activities/execute-deep-agent/__tests__/streaming-side-effects.test.ts +78 -0
  340. package/src/activities/execute-deep-agent/__tests__/subagent-gitignored-capture.test.ts +1 -1
  341. package/src/activities/execute-deep-agent/__tests__/subagent-plan-mode-permissions.test.ts +2 -2
  342. package/src/activities/execute-deep-agent/__tests__/subagent-tracker.test.ts +57 -31
  343. package/src/activities/execute-deep-agent/__tests__/subagent-transformer.test.ts +19 -79
  344. package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +15 -15
  345. package/src/activities/execute-deep-agent/__tests__/summarization-verification.test.ts +1 -1
  346. package/src/activities/execute-deep-agent/__tests__/turn-stream.test.ts +391 -0
  347. package/src/activities/execute-deep-agent/__tests__/v3-status-builder.test.ts +343 -38
  348. package/src/activities/execute-deep-agent/__tests__/vision-input.test.ts +5 -5
  349. package/src/activities/execute-deep-agent/adapter.ts +94 -0
  350. package/src/activities/execute-deep-agent/cas-capture-observer.ts +11 -0
  351. package/src/activities/execute-deep-agent/deep-agent-capabilities.ts +43 -0
  352. package/src/activities/execute-deep-agent/execution-state.ts +4 -13
  353. package/src/activities/execute-deep-agent/hitl.ts +100 -123
  354. package/src/activities/execute-deep-agent/prompt-builder.ts +141 -115
  355. package/src/activities/execute-deep-agent/status-builder-shared.ts +21 -84
  356. package/src/activities/execute-deep-agent/streaming-side-effects.ts +13 -19
  357. package/src/activities/execute-deep-agent/subagent-tracker.ts +77 -42
  358. package/src/activities/execute-deep-agent/subagent-transformer.ts +46 -105
  359. package/src/activities/execute-deep-agent/subagent-wiring.ts +5 -4
  360. package/src/activities/execute-deep-agent/turn-settle.ts +164 -0
  361. package/src/activities/execute-deep-agent/turn-setup.ts +541 -0
  362. package/src/activities/execute-deep-agent/turn-stream.ts +313 -0
  363. package/src/activities/execute-deep-agent/turn.ts +131 -0
  364. package/src/activities/execute-deep-agent/v3-event-recorder.ts +6 -5
  365. package/src/activities/execute-deep-agent/v3-events.ts +1 -1
  366. package/src/activities/execute-deep-agent/v3-status-builder.ts +84 -63
  367. package/src/harness/__tests__/approval-decisions.test.ts +116 -0
  368. package/src/harness/__tests__/capture.test.ts +268 -0
  369. package/src/harness/__tests__/mount-skills.test.ts +169 -0
  370. package/src/harness/__tests__/persist-chokepoint.test.ts +38 -2
  371. package/src/harness/__tests__/run-turn-deterministic.test.ts +94 -0
  372. package/src/harness/__tests__/run-turn.test.ts +39 -23
  373. package/src/harness/__tests__/turn-attachments-vision.test.ts +80 -0
  374. package/src/harness/__tests__/turn-context.test.ts +73 -26
  375. package/src/harness/approval-decisions.ts +104 -0
  376. package/src/harness/capabilities.ts +16 -5
  377. package/src/harness/capture.ts +301 -0
  378. package/src/harness/persist-chokepoint.ts +24 -4
  379. package/src/harness/run-turn.ts +120 -29
  380. package/src/harness/terminal-table.ts +61 -20
  381. package/src/harness/turn-context.ts +208 -111
  382. package/src/harness/types.ts +66 -12
  383. package/src/harness-adapters.ts +17 -9
  384. package/src/middleware/__tests__/approval-gate.test.ts +1 -1
  385. package/src/middleware/__tests__/cost-advisory.test.ts +113 -0
  386. package/src/middleware/approval-gate.ts +2 -2
  387. package/src/middleware/{cost-cap.ts → cost-advisory.ts} +40 -70
  388. package/src/middleware/index.ts +29 -29
  389. package/src/middleware/types.ts +3 -2
  390. package/src/runner-manager.ts +0 -8
  391. package/src/runner.ts +0 -8
  392. package/src/shared/__tests__/attachment-naming.test.ts +6 -19
  393. package/src/shared/__tests__/attachment-resolver.test.ts +158 -8
  394. package/src/shared/__tests__/attachment-zip.test.ts +236 -0
  395. package/src/shared/__tests__/bedrock-adapter.test.ts +1 -1
  396. package/src/shared/__tests__/blueprint-resolver.test.ts +43 -0
  397. package/src/shared/__tests__/cost-guard.test.ts +3 -3
  398. package/src/shared/__tests__/prompt-sections.test.ts +167 -0
  399. package/src/shared/__tests__/secret-leak-scan.test.ts +2 -2
  400. package/src/{activities/execute-deep-agent → shared}/__tests__/stamp-flowed-rows.test.ts +33 -10
  401. package/src/shared/__tests__/vertex-adapter.test.ts +1 -1
  402. package/src/shared/attachment-resolver.ts +218 -55
  403. package/src/shared/attachment-zip.ts +281 -0
  404. package/src/shared/connect-backfill.ts +9 -11
  405. package/src/shared/cost-guard.ts +3 -3
  406. package/src/shared/execution-status-writer.ts +5 -5
  407. package/src/shared/extract-structured-output.ts +8 -7
  408. package/src/shared/filereview/__tests__/capture.test.ts +86 -4
  409. package/src/shared/filereview/__tests__/cas-progress.test.ts +2 -1
  410. package/src/shared/filereview/cas-progress.ts +5 -19
  411. package/src/shared/filereview/cas-touched.ts +89 -0
  412. package/src/shared/filereview/index.ts +4 -2
  413. package/src/shared/mcp-resolver.ts +4 -5
  414. package/src/shared/persist-decision.ts +3 -4
  415. package/src/shared/plan-mode-permissions.ts +2 -2
  416. package/src/shared/plan-mode-prompt.ts +1 -1
  417. package/src/shared/prompt-sections.ts +256 -0
  418. package/src/shared/skill-resolver.ts +4 -2
  419. package/src/shared/tool-rounds.ts +48 -0
  420. package/src/shared/tool-row.ts +61 -0
  421. package/src/shared/workspace/__tests__/stigmer-link.test.ts +7 -22
  422. package/src/shared/workspace/__tests__/writeback-coordinator.test.ts +20 -61
  423. package/src/shared/workspace/session-provision.ts +3 -3
  424. package/src/shared/workspace/stigmer-link.ts +3 -3
  425. package/src/shared/workspace/writeback-coordinator.ts +16 -64
  426. package/src/tools/index.ts +1 -1
  427. package/dist/activities/execute-cursor/capture-flow.d.ts +0 -142
  428. package/dist/activities/execute-cursor/capture-flow.js +0 -285
  429. package/dist/activities/execute-cursor/capture-flow.js.map +0 -1
  430. package/dist/activities/execute-cursor/command-provenance.d.ts +0 -48
  431. package/dist/activities/execute-cursor/command-provenance.js +0 -38
  432. package/dist/activities/execute-cursor/command-provenance.js.map +0 -1
  433. package/dist/activities/execute-deep-agent/attachment-injector.d.ts +0 -134
  434. package/dist/activities/execute-deep-agent/attachment-injector.js +0 -386
  435. package/dist/activities/execute-deep-agent/attachment-injector.js.map +0 -1
  436. package/dist/activities/execute-deep-agent/command-provenance.d.ts +0 -61
  437. package/dist/activities/execute-deep-agent/command-provenance.js +0 -72
  438. package/dist/activities/execute-deep-agent/command-provenance.js.map +0 -1
  439. package/dist/activities/execute-deep-agent/environment.d.ts +0 -24
  440. package/dist/activities/execute-deep-agent/environment.js +0 -58
  441. package/dist/activities/execute-deep-agent/environment.js.map +0 -1
  442. package/dist/activities/execute-deep-agent/event-recorder.d.ts +0 -21
  443. package/dist/activities/execute-deep-agent/event-recorder.js +0 -67
  444. package/dist/activities/execute-deep-agent/event-recorder.js.map +0 -1
  445. package/dist/activities/execute-deep-agent/index.d.ts +0 -15
  446. package/dist/activities/execute-deep-agent/index.js +0 -918
  447. package/dist/activities/execute-deep-agent/index.js.map +0 -1
  448. package/dist/activities/execute-deep-agent/mcp-gate.d.ts +0 -28
  449. package/dist/activities/execute-deep-agent/mcp-gate.js +0 -22
  450. package/dist/activities/execute-deep-agent/mcp-gate.js.map +0 -1
  451. package/dist/activities/execute-deep-agent/post-stream.d.ts +0 -23
  452. package/dist/activities/execute-deep-agent/post-stream.js +0 -71
  453. package/dist/activities/execute-deep-agent/post-stream.js.map +0 -1
  454. package/dist/activities/execute-deep-agent/setup.d.ts +0 -136
  455. package/dist/activities/execute-deep-agent/setup.js +0 -767
  456. package/dist/activities/execute-deep-agent/setup.js.map +0 -1
  457. package/dist/activities/execute-deep-agent/stamp-flowed-rows.d.ts +0 -36
  458. package/dist/activities/execute-deep-agent/stamp-flowed-rows.js +0 -56
  459. package/dist/activities/execute-deep-agent/stamp-flowed-rows.js.map +0 -1
  460. package/dist/activities/execute-deep-agent/status-builder.d.ts +0 -93
  461. package/dist/activities/execute-deep-agent/status-builder.js +0 -365
  462. package/dist/activities/execute-deep-agent/status-builder.js.map +0 -1
  463. package/dist/activities/execute-deep-agent/streaming-terminal.d.ts +0 -21
  464. package/dist/activities/execute-deep-agent/streaming-terminal.js +0 -80
  465. package/dist/activities/execute-deep-agent/streaming-terminal.js.map +0 -1
  466. package/dist/activities/execute-deep-agent/streaming-v3.d.ts +0 -13
  467. package/dist/activities/execute-deep-agent/streaming-v3.js +0 -174
  468. package/dist/activities/execute-deep-agent/streaming-v3.js.map +0 -1
  469. package/dist/activities/execute-deep-agent/streaming.d.ts +0 -81
  470. package/dist/activities/execute-deep-agent/streaming.js +0 -160
  471. package/dist/activities/execute-deep-agent/streaming.js.map +0 -1
  472. package/dist/middleware/cost-cap.d.ts +0 -22
  473. package/dist/middleware/cost-cap.js.map +0 -1
  474. package/dist/middleware/graceful-stop.d.ts +0 -17
  475. package/dist/middleware/graceful-stop.js +0 -63
  476. package/dist/middleware/graceful-stop.js.map +0 -1
  477. package/dist/shared/skill-writer.d.ts +0 -75
  478. package/dist/shared/skill-writer.js +0 -207
  479. package/dist/shared/skill-writer.js.map +0 -1
  480. package/src/activities/execute-cursor/__tests__/capture-flow.test.ts +0 -1032
  481. package/src/activities/execute-cursor/__tests__/command-provenance.test.ts +0 -240
  482. package/src/activities/execute-cursor/__tests__/progress-substrate.test.ts +0 -169
  483. package/src/activities/execute-cursor/capture-flow.ts +0 -379
  484. package/src/activities/execute-cursor/command-provenance.ts +0 -73
  485. package/src/activities/execute-deep-agent/__tests__/attachment-injector.test.ts +0 -1161
  486. package/src/activities/execute-deep-agent/__tests__/command-provenance.test.ts +0 -252
  487. package/src/activities/execute-deep-agent/__tests__/environment.test.ts +0 -108
  488. package/src/activities/execute-deep-agent/__tests__/event-recorder.test.ts +0 -150
  489. package/src/activities/execute-deep-agent/__tests__/hitl-integration.test.ts +0 -226
  490. package/src/activities/execute-deep-agent/__tests__/hitl-reject.test.ts +0 -318
  491. package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +0 -351
  492. package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +0 -293
  493. package/src/activities/execute-deep-agent/__tests__/index.test.ts +0 -99
  494. package/src/activities/execute-deep-agent/__tests__/mcp-gate.test.ts +0 -44
  495. package/src/activities/execute-deep-agent/__tests__/post-stream.test.ts +0 -112
  496. package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +0 -358
  497. package/src/activities/execute-deep-agent/__tests__/status-builder.test.ts +0 -1933
  498. package/src/activities/execute-deep-agent/__tests__/streaming-terminal.test.ts +0 -40
  499. package/src/activities/execute-deep-agent/__tests__/streaming-v3.test.ts +0 -597
  500. package/src/activities/execute-deep-agent/__tests__/streaming.test.ts +0 -508
  501. package/src/activities/execute-deep-agent/attachment-injector.ts +0 -627
  502. package/src/activities/execute-deep-agent/command-provenance.ts +0 -102
  503. package/src/activities/execute-deep-agent/environment.ts +0 -76
  504. package/src/activities/execute-deep-agent/event-recorder.ts +0 -95
  505. package/src/activities/execute-deep-agent/index.ts +0 -1072
  506. package/src/activities/execute-deep-agent/mcp-gate.ts +0 -37
  507. package/src/activities/execute-deep-agent/post-stream.ts +0 -109
  508. package/src/activities/execute-deep-agent/setup.ts +0 -1081
  509. package/src/activities/execute-deep-agent/stamp-flowed-rows.ts +0 -64
  510. package/src/activities/execute-deep-agent/status-builder.ts +0 -481
  511. package/src/activities/execute-deep-agent/streaming-terminal.ts +0 -106
  512. package/src/activities/execute-deep-agent/streaming-v3.ts +0 -277
  513. package/src/activities/execute-deep-agent/streaming.ts +0 -313
  514. package/src/middleware/__tests__/cost-cap.test.ts +0 -192
  515. package/src/middleware/__tests__/graceful-stop.test.ts +0 -105
  516. package/src/middleware/graceful-stop.ts +0 -86
  517. package/src/shared/__tests__/skill-writer.test.ts +0 -372
  518. package/src/shared/skill-writer.ts +0 -266
@@ -226,11 +226,6 @@ export async function createStigmerRunnerManager(
226
226
  // in-process embedders like the desktop never execute main.ts.
227
227
  captureRunnerSecrets();
228
228
 
229
- const { registerStigmerDeepagentsProfiles } = await import(
230
- "./activities/execute-deep-agent/deepagents-profiles.js"
231
- );
232
- registerStigmerDeepagentsProfiles();
233
-
234
229
  // `tokenRef` holds the control-plane token (the host's durable credential,
235
230
  // e.g. the desktop's Auth0 token) and is shared with activity clients via
236
231
  // config.stigmerTokenRef. The proxy credential (x-stigmer-auth) is managed
@@ -745,7 +740,6 @@ async function createAllActivities(config: Config): Promise<WorkerActivities> {
745
740
  const [
746
741
  { HARNESS_ADAPTERS },
747
742
  { createHarnessActivities },
748
- { createDeepAgentActivities },
749
743
  { createEnsureThreadActivities },
750
744
  { createGenerateSessionSubjectActivities },
751
745
  { createClassifyToolApprovalsActivities },
@@ -765,7 +759,6 @@ async function createAllActivities(config: Config): Promise<WorkerActivities> {
765
759
  ] = await Promise.all([
766
760
  import("./harness-adapters.js"),
767
761
  import("./harness/registry.js"),
768
- import("./activities/execute-deep-agent/index.js"),
769
762
  import("./activities/ensure-thread.js"),
770
763
  import("./activities/generate-session-subject.js"),
771
764
  import("./activities/classify-tool-approvals.js"),
@@ -786,7 +779,6 @@ async function createAllActivities(config: Config): Promise<WorkerActivities> {
786
779
 
787
780
  return {
788
781
  ...(await createHarnessActivities(HARNESS_ADAPTERS, config)),
789
- ...createDeepAgentActivities(config),
790
782
  ...createEnsureThreadActivities(),
791
783
  ...createGenerateSessionSubjectActivities(config),
792
784
  ...createClassifyToolApprovalsActivities(config),
package/src/runner.ts CHANGED
@@ -201,11 +201,6 @@ export async function createStigmerRunner(
201
201
  // for in-process embedders.
202
202
  captureRunnerSecrets();
203
203
 
204
- const { registerStigmerDeepagentsProfiles } = await import(
205
- "./activities/execute-deep-agent/deepagents-profiles.js"
206
- );
207
- registerStigmerDeepagentsProfiles();
208
-
209
204
  const baseConfig = mapOptionsToConfig(options);
210
205
 
211
206
  assertLlmBackendsPreflight(baseConfig.proxyEndpoint);
@@ -462,7 +457,6 @@ async function createAllActivities(config: Config): Promise<WorkerActivities> {
462
457
  const [
463
458
  { HARNESS_ADAPTERS },
464
459
  { createHarnessActivities },
465
- { createDeepAgentActivities },
466
460
  { createEnsureThreadActivities },
467
461
  { createGenerateSessionSubjectActivities },
468
462
  { createClassifyToolApprovalsActivities },
@@ -482,7 +476,6 @@ async function createAllActivities(config: Config): Promise<WorkerActivities> {
482
476
  ] = await Promise.all([
483
477
  import("./harness-adapters.js"),
484
478
  import("./harness/registry.js"),
485
- import("./activities/execute-deep-agent/index.js"),
486
479
  import("./activities/ensure-thread.js"),
487
480
  import("./activities/generate-session-subject.js"),
488
481
  import("./activities/classify-tool-approvals.js"),
@@ -503,7 +496,6 @@ async function createAllActivities(config: Config): Promise<WorkerActivities> {
503
496
 
504
497
  return {
505
498
  ...(await createHarnessActivities(HARNESS_ADAPTERS, config)),
506
- ...createDeepAgentActivities(config),
507
499
  ...createEnsureThreadActivities(),
508
500
  ...createGenerateSessionSubjectActivities(config),
509
501
  ...createClassifyToolApprovalsActivities(config),
@@ -1,13 +1,11 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
2
  import { mkdtempSync, readFileSync, rmSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { tmpdir } from "node:os";
5
5
  import { uniquifyFilename, allocateUniqueName } from "../attachment-naming.js";
6
- import { injectAttachments } from "../../activities/execute-deep-agent/attachment-injector.js";
7
6
  import { resolveAttachments } from "../attachment-resolver.js";
8
7
  import { getPlatformDir } from "../workspace/platform-dir.js";
9
8
  import { makeInMemoryArtifactStorage } from "../../__test-utils__/fake-artifact-storage.js";
10
- import type { WorkspaceBackend } from "../workspace/types.js";
11
9
 
12
10
  describe("uniquifyFilename", () => {
13
11
  // This table is the byte-for-byte twin of the React SDK suite
@@ -118,7 +116,7 @@ describe("cross-harness naming parity", () => {
118
116
  } as any;
119
117
  }
120
118
 
121
- it("both harnesses produce identical final basenames for the same duplicate-heavy list", async () => {
119
+ it("the resolver produces the React SDK's basenames for the same duplicate-heavy list", async () => {
122
120
  const { storage } = makeInMemoryArtifactStorage();
123
121
  await storage.upload("attachments/01A/data.csv", Buffer.from("a"), "text/plain");
124
122
  await storage.upload("attachments/01B/data.csv", Buffer.from("b"), "text/plain");
@@ -131,14 +129,8 @@ describe("cross-harness naming parity", () => {
131
129
  makeAttachment("Makefile", "attachments/01D/Makefile"),
132
130
  ];
133
131
 
134
- const backend = { writeFileBuffer: vi.fn() } as unknown as WorkspaceBackend;
135
- const injected = await injectAttachments({
136
- backend,
137
- attachments,
138
- storage,
139
- isLocalMode: false,
140
- });
141
-
132
+ // One attachment pipeline serves both harnesses since S3 M2b (Q-S3-14);
133
+ // the names it lands are the ones the React SDK's table above predicts.
142
134
  const resolved = await resolveAttachments(attachments, {
143
135
  sessionId,
144
136
  primaryWorkspaceDir: workspaceDir,
@@ -146,13 +138,8 @@ describe("cross-harness naming parity", () => {
146
138
  storage,
147
139
  });
148
140
 
149
- const injectedNames = injected.map((f) => f.filename);
150
- const resolvedNames = resolved.map((r) => r.filename);
151
- expect(injectedNames).toEqual(["data.csv", "data-2.csv", "Makefile", "Makefile-2"]);
152
- expect(resolvedNames).toEqual(injectedNames);
153
- expect(injected.map((f) => f.renamedFrom)).toEqual(
154
- resolved.map((r) => r.renamedFrom),
155
- );
141
+ expect(resolved.map((r) => r.filename)).toEqual(["data.csv", "data-2.csv", "Makefile", "Makefile-2"]);
142
+ expect(resolved.map((r) => r.renamedFrom)).toEqual([undefined, "data.csv", undefined, "Makefile"]);
156
143
  // The resolver's files really land under the renamed paths.
157
144
  expect(readFileSync(join(platformDir, "inputs", "data-2.csv"), "utf-8")).toBe("b");
158
145
  });
@@ -1,14 +1,19 @@
1
1
  /**
2
- * Tests for the attachment resolver, which the Cursor activity calls today
3
- * and the turn runtime takes over in S2 M3 (S3 retires the native twin,
4
- * execute-deep-agent/attachment-injector.ts).
2
+ * Tests for the attachment resolver the turn runtime's attachment phase,
3
+ * the one pipeline for both harnesses since S3 M2b (the native twin,
4
+ * `attachment-injector.ts`, retired then; its ZIP guards moved to
5
+ * `attachment-zip.ts` at S3 M1).
5
6
  *
6
7
  * The load-bearing behaviors: storage-key attachments materialize under the
7
8
  * platform inputs dir (the universal path — every server-created attachment
8
9
  * carries a storage key), the workspace `.stigmer` symlink exists even when
9
10
  * the agent has no skills, and any attachment that cannot be materialized
10
11
  * fails the resolution loudly (the silent-skip regression behind "plan file
11
- * wasn't found").
12
+ * wasn't found"). Since S3 M1 (Q-S3-14, Q-M1-2) the resolver is the one
13
+ * attachment pipeline for every harness, so it also pins the two behaviors
14
+ * lifted from the native injector: `extract` archives land under their mount
15
+ * directory through the shared #567 guards, and an explicit `mountPath` is
16
+ * honoured inside the `inputs/` namespace and refused outside it.
12
17
  */
13
18
 
14
19
  import { describe, it, expect, beforeEach, afterEach } from "vitest";
@@ -16,6 +21,8 @@ import { mkdtempSync, writeFileSync, readFileSync, rmSync, lstatSync, readlinkSy
16
21
  import { join } from "node:path";
17
22
  import { tmpdir } from "node:os";
18
23
  import { resolveAttachments, AttachmentResolutionError } from "../attachment-resolver.js";
24
+ import { AttachmentValidationError } from "../attachment-zip.js";
25
+ import { buildZip } from "@stigmer/zip-structure/testing";
19
26
  import { getPlatformDir } from "../workspace/platform-dir.js";
20
27
  import { makeInMemoryArtifactStorage } from "../../__test-utils__/fake-artifact-storage.js";
21
28
  import { CURSOR_VISION_PROFILE, VisionBudget } from "../attachment-vision.js";
@@ -83,6 +90,7 @@ describe("resolveAttachments", () => {
83
90
  {
84
91
  filename: "plan.md",
85
92
  relativePath: ".stigmer/inputs/plan.md",
93
+ sizeBytes: 10,
86
94
  downloadUrl: "mem://attachments/01ABC/plan.md",
87
95
  },
88
96
  ]);
@@ -115,7 +123,7 @@ describe("resolveAttachments", () => {
115
123
  );
116
124
 
117
125
  expect(result).toEqual([
118
- { filename: "data.csv", relativePath: ".stigmer/inputs/data.csv" },
126
+ { filename: "data.csv", relativePath: ".stigmer/inputs/data.csv", sizeBytes: 5 },
119
127
  ]);
120
128
  expect(readFileSync(join(platformDir, "inputs", "data.csv"), "utf-8")).toBe("a,b,c");
121
129
  });
@@ -139,11 +147,13 @@ describe("resolveAttachments", () => {
139
147
  {
140
148
  filename: "report.pdf",
141
149
  relativePath: ".stigmer/inputs/report.pdf",
150
+ sizeBytes: 11,
142
151
  downloadUrl: "mem://attachments/01AAA/report.pdf",
143
152
  },
144
153
  {
145
154
  filename: "report-2.pdf",
146
155
  relativePath: ".stigmer/inputs/report-2.pdf",
156
+ sizeBytes: 12,
147
157
  renamedFrom: "report.pdf",
148
158
  downloadUrl: "mem://attachments/01BBB/report.pdf",
149
159
  },
@@ -169,10 +179,11 @@ describe("resolveAttachments", () => {
169
179
  // The key-less local file lists no URL; the storage twin does — the
170
180
  // per-file split the prompt renders.
171
181
  expect(result).toEqual([
172
- { filename: "notes.md", relativePath: ".stigmer/inputs/notes.md" },
182
+ { filename: "notes.md", relativePath: ".stigmer/inputs/notes.md", sizeBytes: 10 },
173
183
  {
174
184
  filename: "notes-2.md",
175
185
  relativePath: ".stigmer/inputs/notes-2.md",
186
+ sizeBytes: 13,
176
187
  renamedFrom: "notes.md",
177
188
  downloadUrl: "mem://attachments/01ABC/notes.md",
178
189
  },
@@ -242,6 +253,7 @@ describe("resolveAttachments", () => {
242
253
  {
243
254
  filename: "evil.md",
244
255
  relativePath: ".stigmer/inputs/evil.md",
256
+ sizeBytes: 9,
245
257
  downloadUrl: "mem://attachments/01ABC/x",
246
258
  },
247
259
  ]);
@@ -260,11 +272,148 @@ describe("resolveAttachments", () => {
260
272
  );
261
273
 
262
274
  expect(result).toEqual([
263
- { filename: "evil.txt", relativePath: ".stigmer/inputs/evil.txt" },
275
+ { filename: "evil.txt", relativePath: ".stigmer/inputs/evil.txt", sizeBytes: 15 },
264
276
  ]);
265
277
  expect(readFileSync(join(platformDir, "inputs", "evil.txt"), "utf-8")).toBe("local-contained");
266
278
  });
267
279
 
280
+ // ── Explicit mountPath: honoured inside the inputs namespace, refused outside ──
281
+
282
+ describe("mountPath (S3 M1, Q-M1-2)", () => {
283
+ it("places a file at its explicit `inputs/…` path, in every spelling the producers use", async () => {
284
+ const { storage } = makeInMemoryArtifactStorage();
285
+ await storage.upload("k/a", Buffer.from("A"), "text/plain");
286
+ await storage.upload("k/b", Buffer.from("B"), "text/plain");
287
+ await storage.upload("k/c", Buffer.from("C"), "text/plain");
288
+
289
+ const result = await resolveAttachments(
290
+ [
291
+ makeAttachment({ filename: "a.txt", storageKey: "k/a", mountPath: "inputs/data/a.txt" }),
292
+ makeAttachment({ filename: "b.txt", storageKey: "k/b", mountPath: ".stigmer/inputs/b-renamed.txt" }),
293
+ makeAttachment({ filename: "c.txt", storageKey: "k/c", mountPath: "/inputs/c.txt" }),
294
+ ],
295
+ options({ storage }),
296
+ );
297
+
298
+ expect(result.map((r) => r.relativePath)).toEqual([
299
+ ".stigmer/inputs/data/a.txt",
300
+ ".stigmer/inputs/b-renamed.txt",
301
+ ".stigmer/inputs/c.txt",
302
+ ]);
303
+ expect(result.map((r) => r.filename), "the on-disk basename, not the attachment's").toEqual(["a.txt", "b-renamed.txt", "c.txt"]);
304
+ expect(readFileSync(join(platformDir, "inputs", "data", "a.txt"), "utf-8")).toBe("A");
305
+ expect(readFileSync(join(platformDir, "inputs", "b-renamed.txt"), "utf-8")).toBe("B");
306
+ });
307
+
308
+ it("a default-named attachment uniquifies around a name an explicit path claims directly under inputs/", async () => {
309
+ const { storage } = makeInMemoryArtifactStorage();
310
+ await storage.upload("k/explicit", Buffer.from("explicit"), "text/plain");
311
+ await storage.upload("k/default", Buffer.from("default"), "text/plain");
312
+
313
+ const result = await resolveAttachments(
314
+ [
315
+ makeAttachment({ filename: "other.txt", storageKey: "k/explicit", mountPath: "inputs/plan.md" }),
316
+ makeAttachment({ filename: "plan.md", storageKey: "k/default" }),
317
+ ],
318
+ options({ storage }),
319
+ );
320
+
321
+ expect(result.map(({ filename, relativePath, renamedFrom }) => ({ filename, relativePath, renamedFrom }))).toEqual([
322
+ { filename: "plan.md", relativePath: ".stigmer/inputs/plan.md", renamedFrom: undefined },
323
+ { filename: "plan-2.md", relativePath: ".stigmer/inputs/plan-2.md", renamedFrom: "plan.md" },
324
+ ]);
325
+ expect(readFileSync(join(platformDir, "inputs", "plan.md"), "utf-8")).toBe("explicit");
326
+ expect(readFileSync(join(platformDir, "inputs", "plan-2.md"), "utf-8")).toBe("default");
327
+ });
328
+
329
+ it("refuses two attachments explicitly pinning the same path (a contradiction no rename resolves), before any bytes move", async () => {
330
+ const { storage } = makeInMemoryArtifactStorage();
331
+ await expect(
332
+ resolveAttachments(
333
+ [
334
+ makeAttachment({ filename: "a.csv", storageKey: "k/a", mountPath: "inputs/data.csv" }),
335
+ makeAttachment({ filename: "b.csv", storageKey: "k/b", mountPath: "inputs/data.csv" }),
336
+ ],
337
+ options({ storage }),
338
+ ),
339
+ ).rejects.toThrow(/mount path 'inputs\/data\.csv' collides with attachment 'a\.csv'/);
340
+ expect(storage.download).not.toHaveBeenCalled();
341
+ });
342
+
343
+ it("refuses a mountPath outside the inputs namespace with the namespace named (a file in the user's tree would ride the write-back commit)", async () => {
344
+ const { storage } = makeInMemoryArtifactStorage();
345
+ const attempt = resolveAttachments(
346
+ [makeAttachment({ filename: "data.yaml", storageKey: "k/d", mountPath: "/workspace/data/data.yaml" })],
347
+ options({ storage }),
348
+ );
349
+ await expect(attempt).rejects.toBeInstanceOf(AttachmentResolutionError);
350
+ await expect(attempt).rejects.toThrow(/outside the attachment namespace.*set mountPath to 'inputs\/<name>'/);
351
+ expect(storage.download).not.toHaveBeenCalled();
352
+ });
353
+
354
+ it("refuses a mountPath that climbs out of the namespace, and one that names the inputs directory itself", async () => {
355
+ const { storage } = makeInMemoryArtifactStorage();
356
+ await expect(
357
+ resolveAttachments([makeAttachment({ storageKey: "k/e", mountPath: "inputs/../../evil.md" })], options({ storage })),
358
+ ).rejects.toThrow(/escapes the attachment namespace/);
359
+ await expect(
360
+ resolveAttachments([makeAttachment({ storageKey: "k/e", mountPath: "inputs/" })], options({ storage })),
361
+ ).rejects.toThrow(/names the inputs directory itself/);
362
+ });
363
+ });
364
+
365
+ // ── Archives: `extract` lands the entries under the mount directory ──────
366
+
367
+ describe("extract (S3 M1, Q-S3-14)", () => {
368
+ const archive = () =>
369
+ Buffer.from(
370
+ buildZip([
371
+ { name: "notes/readme.md", content: "# notes" },
372
+ { name: "data.csv", content: "a,b\n1,2" },
373
+ ]),
374
+ );
375
+
376
+ it("extracts a storage-backed archive under `inputs/<archive filename>/`, one resolved attachment per entry, with no vision, rename or URL", async () => {
377
+ const { storage } = makeInMemoryArtifactStorage();
378
+ await storage.upload("k/bundle", archive(), "application/zip");
379
+
380
+ const result = await resolveAttachments(
381
+ [makeAttachment({ filename: "bundle.zip", storageKey: "k/bundle", extract: true, contentType: "application/zip" })],
382
+ options({ storage, visionBudget: new VisionBudget(CURSOR_VISION_PROFILE, { modelVision: true }) }),
383
+ );
384
+
385
+ expect(result).toEqual([
386
+ { filename: "data.csv", relativePath: ".stigmer/inputs/bundle.zip/data.csv", sizeBytes: 7 },
387
+ { filename: "readme.md", relativePath: ".stigmer/inputs/bundle.zip/notes/readme.md", sizeBytes: 7 },
388
+ ]);
389
+ expect(readFileSync(join(platformDir, "inputs", "bundle.zip", "notes", "readme.md"), "utf-8")).toBe("# notes");
390
+ expect(storage.getDownloadUrl, "no URL for entries: the stored object is the ZIP, not any listed file").not.toHaveBeenCalled();
391
+ });
392
+
393
+ it("extracts a local archive at the CLI's `inputs/<dirname>/` mount (the directory-attachment shape)", async () => {
394
+ const srcPath = join(workspaceDir, "docs.zip");
395
+ writeFileSync(srcPath, archive());
396
+
397
+ const result = await resolveAttachments(
398
+ [makeAttachment({ filename: "docs.zip", storageKey: "", localPath: srcPath, extract: true, mountPath: "inputs/docs/" })],
399
+ options(),
400
+ );
401
+
402
+ expect(result.map((r) => r.relativePath)).toEqual([".stigmer/inputs/docs/data.csv", ".stigmer/inputs/docs/notes/readme.md"]);
403
+ expect(readFileSync(join(platformDir, "inputs", "docs", "data.csv"), "utf-8")).toBe("a,b\n1,2");
404
+ });
405
+
406
+ it("an archive that fails the shared guards aborts the resolution with the guard's own error", async () => {
407
+ const srcPath = join(workspaceDir, "evil.zip");
408
+ writeFileSync(srcPath, Buffer.from(buildZip([{ name: "../escape.txt", content: "x" }])));
409
+
410
+ await expect(
411
+ resolveAttachments([makeAttachment({ filename: "evil.zip", storageKey: "", localPath: srcPath, extract: true })], options()),
412
+ ).rejects.toBeInstanceOf(AttachmentValidationError);
413
+ expect(() => readFileSync(join(platformDir, "inputs", "evil.zip"))).toThrow();
414
+ });
415
+ });
416
+
268
417
  // ── Download-URL hand-off (issue #532) ───────────────────────────────────
269
418
  // The URL is strictly additive (shared/attachment-download-urls.ts): every
270
419
  // case below also asserts the file materialized exactly as it would
@@ -279,7 +428,7 @@ describe("resolveAttachments", () => {
279
428
  const result = await resolveAttachments([makeAttachment()], options({ storage }));
280
429
 
281
430
  expect(result).toEqual([
282
- { filename: "plan.md", relativePath: ".stigmer/inputs/plan.md" },
431
+ { filename: "plan.md", relativePath: ".stigmer/inputs/plan.md", sizeBytes: 10 },
283
432
  ]);
284
433
  expect(readFileSync(join(platformDir, "inputs", "plan.md"), "utf-8")).toBe("# The Plan");
285
434
  });
@@ -300,6 +449,7 @@ describe("resolveAttachments", () => {
300
449
  {
301
450
  filename: "data.csv",
302
451
  relativePath: ".stigmer/inputs/data.csv",
452
+ sizeBytes: 5,
303
453
  downloadUrl: "mem://attachments/01ABC/data.csv",
304
454
  },
305
455
  ]);
@@ -0,0 +1,236 @@
1
+ /**
2
+ * The ZIP guards an `extract` attachment passes before a byte is written
3
+ * (`shared/attachment-zip.ts`): the manifest checks (paths, traversal, null
4
+ * bytes, duplicates, methods, emptiness, count and declared-size caps) and
5
+ * the central-directory parsing that issue #567 made authoritative. Moved
6
+ * from the native injector's test file at S3 M1 with the code; every
7
+ * archive comes from the shared real-shape builder
8
+ * (@stigmer/zip-structure/testing). The write of an admitted entry is each
9
+ * caller's and is pinned in that caller's tests (`attachment-resolver.test.ts`).
10
+ */
11
+
12
+ import { describe, it, expect } from "vitest";
13
+ import { buildZip, type ZipFixtureFile } from "@stigmer/zip-structure/testing";
14
+ import {
15
+ AttachmentValidationError,
16
+ MAX_ZIP_EXTRACTED_SIZE,
17
+ MAX_ZIP_FILES,
18
+ validateZipForExtraction,
19
+ } from "../attachment-zip.js";
20
+
21
+ function makeZip(entries: Record<string, string | Buffer>): Buffer {
22
+ return makeZipWith(entries, { method: "deflated" });
23
+ }
24
+
25
+ function makeStoredZip(entries: Record<string, Buffer>): Buffer {
26
+ return makeZipWith(entries, { method: "stored" });
27
+ }
28
+
29
+ function makeZipWith(
30
+ entries: Record<string, string | Buffer>,
31
+ shape: Pick<ZipFixtureFile, "method" | "streaming">,
32
+ ): Buffer {
33
+ const files: ZipFixtureFile[] = Object.entries(entries).map(([name, content]) => ({
34
+ name,
35
+ content: typeof content === "string" ? content : new Uint8Array(content),
36
+ ...shape,
37
+ }));
38
+ return Buffer.from(buildZip(files));
39
+ }
40
+
41
+ function makeDirectoryOnlyZip(): Buffer {
42
+ return Buffer.from(buildZip([{ name: "empty_dir/", content: "" }]));
43
+ }
44
+
45
+ // ═══════════════════════════════════════════════════════════════════════
46
+ // validateZipForExtraction
47
+ // ═══════════════════════════════════════════════════════════════════════
48
+
49
+ describe("validateZipForExtraction", () => {
50
+ it("returns sorted manifest for valid ZIP with multiple files", () => {
51
+ const zip = makeZip({ "b.txt": "beta", "a.txt": "alpha", "c.txt": "charlie" });
52
+ const result = validateZipForExtraction(zip, "test.zip");
53
+
54
+ expect(result).toHaveLength(3);
55
+ expect(result.map(e => e.relativePath)).toEqual(["a.txt", "b.txt", "c.txt"]);
56
+ });
57
+
58
+ it("preserves nested directory paths in entries", () => {
59
+ const zip = makeZip({
60
+ "src/main.py": "print('hi')",
61
+ "README.md": "# Hello",
62
+ "src/lib/util.py": "pass",
63
+ });
64
+ const result = validateZipForExtraction(zip, "project.zip");
65
+ const paths = result.map(e => e.relativePath);
66
+
67
+ expect(paths).toContain("src/main.py");
68
+ expect(paths).toContain("src/lib/util.py");
69
+ expect(paths).toContain("README.md");
70
+ });
71
+
72
+ it("reports correct uncompressed sizes", () => {
73
+ const contentA = "hello world";
74
+ const contentB = "x".repeat(500);
75
+ const zip = makeZip({ "a.txt": contentA, "b.txt": contentB });
76
+
77
+ const result = validateZipForExtraction(zip, "test.zip");
78
+ const sizeMap = new Map(result.map(e => [e.relativePath, e.uncompressedSize]));
79
+
80
+ expect(sizeMap.get("a.txt")).toBe(Buffer.from(contentA).length);
81
+ expect(sizeMap.get("b.txt")).toBe(Buffer.from(contentB).length);
82
+ });
83
+
84
+ it("excludes directory-only entries", () => {
85
+ const zip = makeDirectoryOnlyZip();
86
+ expect(() => validateZipForExtraction(zip, "dirs.zip")).toThrow(
87
+ AttachmentValidationError,
88
+ );
89
+ });
90
+
91
+ it("rejects invalid ZIP format (random bytes)", () => {
92
+ expect(() => validateZipForExtraction(Buffer.from("not-a-zip"), "bad.zip"))
93
+ .toThrow(AttachmentValidationError);
94
+ expect(() => validateZipForExtraction(Buffer.from("not-a-zip"), "bad.zip"))
95
+ .toThrow(/not a valid ZIP archive/);
96
+ });
97
+
98
+ it("rejects file too small to be a ZIP", () => {
99
+ expect(() => validateZipForExtraction(Buffer.from([0x50, 0x4b]), "tiny.zip"))
100
+ .toThrow(/too small/);
101
+ });
102
+
103
+ it("rejects absolute path entries (forward slash)", () => {
104
+ const zip = makeZip({ "/etc/passwd": "root:x:0:0" });
105
+ expect(() => validateZipForExtraction(zip, "evil.zip"))
106
+ .toThrow(/absolute path/);
107
+ });
108
+
109
+ it("rejects absolute path entries (backslash)", () => {
110
+ const zip = makeZip({ "\\windows\\system32\\evil.dll": "payload" });
111
+ expect(() => validateZipForExtraction(zip, "evil.zip"))
112
+ .toThrow(/absolute path/);
113
+ });
114
+
115
+ it("rejects path traversal with leading ..", () => {
116
+ const zip = makeZip({ "../../etc/passwd": "root:x:0:0" });
117
+ expect(() => validateZipForExtraction(zip, "evil.zip"))
118
+ .toThrow(/path traversal/);
119
+ });
120
+
121
+ it("rejects path traversal with embedded ..", () => {
122
+ const zip = makeZip({ "foo/../../etc/passwd": "root:x:0:0" });
123
+ expect(() => validateZipForExtraction(zip, "evil.zip"))
124
+ .toThrow(/path traversal/);
125
+ });
126
+
127
+ it("rejects null bytes in filenames", () => {
128
+ const zip = makeZip({ "file\u0000.txt": "test" });
129
+ expect(() => validateZipForExtraction(zip, "null.zip"))
130
+ .toThrow(/null bytes/);
131
+ });
132
+
133
+ it("rejects empty archive (valid ZIP, zero file entries)", () => {
134
+ const zip = makeDirectoryOnlyZip();
135
+ expect(() => validateZipForExtraction(zip, "empty.zip"))
136
+ .toThrow(/empty ZIP archive/);
137
+ });
138
+
139
+ it("rejects file count exceeding limit", () => {
140
+ const entries: Record<string, Buffer> = {};
141
+ for (let i = 0; i < MAX_ZIP_FILES + 1; i++) {
142
+ entries[`file_${String(i).padStart(4, "0")}.txt`] = Buffer.from("x");
143
+ }
144
+ const zip = makeStoredZip(entries);
145
+ expect(() => validateZipForExtraction(zip, "bomb.zip"))
146
+ .toThrow(new RegExp(`limit: ${MAX_ZIP_FILES}`));
147
+ });
148
+
149
+ it("rejects total uncompressed size exceeding limit", () => {
150
+ const overLimit = MAX_ZIP_EXTRACTED_SIZE + 1;
151
+ const zip = makeStoredZip({ "big.bin": Buffer.alloc(overLimit, 0) });
152
+ expect(() => validateZipForExtraction(zip, "bomb.zip"))
153
+ .toThrow(/limit: 100 MB/);
154
+ });
155
+
156
+ it("accepts ZIP at exactly the file count limit", () => {
157
+ const entries: Record<string, Buffer> = {};
158
+ for (let i = 0; i < MAX_ZIP_FILES; i++) {
159
+ entries[`f${i}.txt`] = Buffer.from("x");
160
+ }
161
+ const zip = makeStoredZip(entries);
162
+ const result = validateZipForExtraction(zip, "ok.zip");
163
+ expect(result).toHaveLength(MAX_ZIP_FILES);
164
+ });
165
+ });
166
+
167
+ // ═══════════════════════════════════════════════════════════════════════
168
+ // Central-directory parsing (issue #567)
169
+ // ═══════════════════════════════════════════════════════════════════════
170
+ // The archive shapes the old local-header walk corrupted or rejected:
171
+ // stored streaming entries (silent manifest truncation) and Go-default
172
+ // deflated streaming entries (rejected outright). Sizes come from the
173
+ // central directory — the format's authoritative index — and structural
174
+ // failures are fail-hard, unlike the skill-artifact reader's non-fatal
175
+ // empty return: nothing upstream vouches for an attachment.
176
+
177
+ describe("validateZipForExtraction — central-directory parsing (issue #567)", () => {
178
+ it("validates every entry of a stored streaming archive (no silent truncation)", () => {
179
+ // Method 0 + flag bit 3 + zeroed local sizes: the old walk admitted the
180
+ // first entry with size 0, landed mid-payload, and quietly dropped the
181
+ // rest of the manifest.
182
+ const zip = Buffer.from(buildZip([
183
+ { name: "first.txt", content: "first file content", streaming: true },
184
+ { name: "second.txt", content: "second file content", streaming: true },
185
+ ]));
186
+
187
+ const result = validateZipForExtraction(zip, "streamed.zip");
188
+ expect(result.map((e) => e.relativePath)).toEqual(["first.txt", "second.txt"]);
189
+ expect(result.map((e) => e.uncompressedSize)).toEqual([
190
+ "first file content".length,
191
+ "second file content".length,
192
+ ]);
193
+ });
194
+
195
+ it("accepts a Go-default archive (deflated streaming entries)", () => {
196
+ const zip = Buffer.from(buildZip([
197
+ { name: "main.go", content: "package main", method: "deflated", streaming: true },
198
+ { name: "go.mod", content: "module example", method: "deflated", streaming: true },
199
+ ]));
200
+
201
+ const result = validateZipForExtraction(zip, "go-built.zip");
202
+ expect(result.map((e) => e.relativePath)).toEqual(["go.mod", "main.go"]);
203
+ });
204
+
205
+ it("rejects an archive with no central directory (fail-hard, unlike skill extraction)", () => {
206
+ const zip = Buffer.from(buildZip(
207
+ [{ name: "a.txt", content: "aaa" }],
208
+ { omitCentralDirectory: true },
209
+ ));
210
+
211
+ expect(() => validateZipForExtraction(zip, "truncated.zip"))
212
+ .toThrow(AttachmentValidationError);
213
+ expect(() => validateZipForExtraction(zip, "truncated.zip"))
214
+ .toThrow(/not a valid ZIP archive/);
215
+ });
216
+
217
+ it("rejects duplicate entry paths (a contradictory manifest)", () => {
218
+ const zip = Buffer.from(buildZip([
219
+ { name: "dup.txt", content: "one" },
220
+ { name: "dup.txt", content: "two" },
221
+ ]));
222
+
223
+ expect(() => validateZipForExtraction(zip, "dup.zip"))
224
+ .toThrow(/duplicate entry/);
225
+ });
226
+
227
+ it("locates the central directory behind a trailing archive comment", () => {
228
+ const zip = Buffer.from(buildZip(
229
+ [{ name: "a.txt", content: "aaa" }],
230
+ { comment: "release archive — built by tooling" },
231
+ ));
232
+
233
+ const result = validateZipForExtraction(zip, "commented.zip");
234
+ expect(result.map((e) => e.relativePath)).toEqual(["a.txt"]);
235
+ });
236
+ });
@@ -152,7 +152,7 @@ describe("buildChatModel bedrock adapter", () => {
152
152
  it("injects the CANONICAL id's default maxTokens when the caller omits it (the 4096 landmine)", async () => {
153
153
  // LangChain's per-model default table prefix-matches the model string.
154
154
  // `anthropic.…` ids match nothing and silently fall back to 4096 —
155
- // setup.ts omits maxTokens, so every deep-agent call would be capped.
155
+ // turn-setup.ts omits maxTokens, so every deep-agent call would be capped.
156
156
  // The adapter must inject what the canonical id would have received on
157
157
  // the public API or vertex, whatever that is at the installed LangChain
158
158
  // version (16384 for 4.5/4.6-generation ids at 1.4.0).
@@ -0,0 +1,43 @@
1
+ /**
2
+ * `shared/blueprint-resolver.ts` `mergeSkillRefs`: the one merge of the
3
+ * agent's and the session's skill refs the turn runtime resolves from
4
+ * (`resolveAgentBlueprint`). Union, deduplicated by slug, the session's ref
5
+ * winning a collision (it may pin a different version).
6
+ *
7
+ * Carried from `skill-writer.test.ts` at S3 M2b, when the native
8
+ * orchestrator's byte-twin of this function was deleted with its module;
9
+ * until then the runtime's copy was tested only through the twin.
10
+ */
11
+
12
+ import { describe, it, expect } from "vitest";
13
+ import type { ApiResourceReference } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
14
+ import { mergeSkillRefs } from "../blueprint-resolver.js";
15
+
16
+ function makeRef(slug: string, org = "test-org"): ApiResourceReference {
17
+ return { slug, org, $typeName: "ai.stigmer.commons.apiresource.ApiResourceReference" } as ApiResourceReference;
18
+ }
19
+
20
+ describe("mergeSkillRefs", () => {
21
+ it("returns empty for no refs", () => {
22
+ expect(mergeSkillRefs([], [])).toEqual([]);
23
+ });
24
+
25
+ it("returns the agent's refs when the session names none", () => {
26
+ const result = mergeSkillRefs([makeRef("skill-a"), makeRef("skill-b")], []);
27
+ expect(result.map((r) => r.slug)).toEqual(["skill-a", "skill-b"]);
28
+ });
29
+
30
+ it("deduplicates by slug with the session's ref winning", () => {
31
+ const result = mergeSkillRefs([makeRef("skill-a", "org1")], [makeRef("skill-a", "org2")]);
32
+ expect(result).toHaveLength(1);
33
+ expect(result[0].org).toBe("org2");
34
+ });
35
+
36
+ it("unions distinct skills from both sources", () => {
37
+ expect(mergeSkillRefs([makeRef("skill-a")], [makeRef("skill-b")])).toHaveLength(2);
38
+ });
39
+
40
+ it("ignores a ref with no slug (nothing to key it by)", () => {
41
+ expect(mergeSkillRefs([makeRef("")], [])).toEqual([]);
42
+ });
43
+ });