@stigmer/runner 3.10.0 → 3.11.1-dev.20260812192248

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 (355) hide show
  1. package/README.md +14 -1
  2. package/dist/.build-fingerprint +1 -1
  3. package/dist/activities/call-llm.js +9 -10
  4. package/dist/activities/call-llm.js.map +1 -1
  5. package/dist/activities/classify-tool-approvals.d.ts +2 -1
  6. package/dist/activities/classify-tool-approvals.js +28 -2
  7. package/dist/activities/classify-tool-approvals.js.map +1 -1
  8. package/dist/activities/discover-mcp-server.d.ts +39 -0
  9. package/dist/activities/discover-mcp-server.js +165 -28
  10. package/dist/activities/discover-mcp-server.js.map +1 -1
  11. package/dist/activities/emit-event.d.ts +14 -2
  12. package/dist/activities/emit-event.js +52 -17
  13. package/dist/activities/emit-event.js.map +1 -1
  14. package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.d.ts +8 -0
  15. package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js +1 -1
  16. package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js.map +1 -1
  17. package/dist/activities/execute-cursor/approval-state.d.ts +28 -2
  18. package/dist/activities/execute-cursor/approval-state.js +7 -1
  19. package/dist/activities/execute-cursor/approval-state.js.map +1 -1
  20. package/dist/activities/execute-cursor/attachment-resolver.d.ts +14 -0
  21. package/dist/activities/execute-cursor/attachment-resolver.js +18 -4
  22. package/dist/activities/execute-cursor/attachment-resolver.js.map +1 -1
  23. package/dist/activities/execute-cursor/blueprint-resolver.d.ts +1 -9
  24. package/dist/activities/execute-cursor/blueprint-resolver.js +6 -22
  25. package/dist/activities/execute-cursor/blueprint-resolver.js.map +1 -1
  26. package/dist/activities/execute-cursor/env-resolver.js +3 -1
  27. package/dist/activities/execute-cursor/env-resolver.js.map +1 -1
  28. package/dist/activities/execute-cursor/error-classifier.d.ts +59 -3
  29. package/dist/activities/execute-cursor/error-classifier.js +115 -6
  30. package/dist/activities/execute-cursor/error-classifier.js.map +1 -1
  31. package/dist/activities/execute-cursor/extract-structured-output.d.ts +29 -0
  32. package/dist/activities/execute-cursor/extract-structured-output.js +58 -0
  33. package/dist/activities/execute-cursor/extract-structured-output.js.map +1 -0
  34. package/dist/activities/execute-cursor/hook-script.d.ts +14 -3
  35. package/dist/activities/execute-cursor/hook-script.js +72 -10
  36. package/dist/activities/execute-cursor/hook-script.js.map +1 -1
  37. package/dist/activities/execute-cursor/index.d.ts +51 -11
  38. package/dist/activities/execute-cursor/index.js +177 -93
  39. package/dist/activities/execute-cursor/index.js.map +1 -1
  40. package/dist/activities/execute-cursor/mcp-resolver.d.ts +24 -1
  41. package/dist/activities/execute-cursor/mcp-resolver.js +5 -2
  42. package/dist/activities/execute-cursor/mcp-resolver.js.map +1 -1
  43. package/dist/activities/execute-cursor/model-pricing-data.d.ts +2 -0
  44. package/dist/activities/execute-cursor/model-pricing-data.js +13 -3
  45. package/dist/activities/execute-cursor/model-pricing-data.js.map +1 -1
  46. package/dist/activities/execute-cursor/prompt-builder.d.ts +51 -4
  47. package/dist/activities/execute-cursor/prompt-builder.js +49 -7
  48. package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
  49. package/dist/activities/execute-cursor/turn-recovery.d.ts +52 -0
  50. package/dist/activities/execute-cursor/turn-recovery.js +193 -0
  51. package/dist/activities/execute-cursor/turn-recovery.js.map +1 -0
  52. package/dist/activities/execute-cursor/turn-stream.js +4 -1
  53. package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
  54. package/dist/activities/execute-deep-agent/attachment-injector.d.ts +18 -1
  55. package/dist/activities/execute-deep-agent/attachment-injector.js +68 -23
  56. package/dist/activities/execute-deep-agent/attachment-injector.js.map +1 -1
  57. package/dist/activities/execute-deep-agent/environment.js +3 -1
  58. package/dist/activities/execute-deep-agent/environment.js.map +1 -1
  59. package/dist/activities/execute-deep-agent/index.js +15 -0
  60. package/dist/activities/execute-deep-agent/index.js.map +1 -1
  61. package/dist/activities/execute-deep-agent/prompt-builder.d.ts +7 -7
  62. package/dist/activities/execute-deep-agent/prompt-builder.js +8 -2
  63. package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
  64. package/dist/activities/execute-deep-agent/setup.d.ts +10 -0
  65. package/dist/activities/execute-deep-agent/setup.js +65 -24
  66. package/dist/activities/execute-deep-agent/setup.js.map +1 -1
  67. package/dist/activities/execute-deep-agent/shell-env.d.ts +5 -1
  68. package/dist/activities/execute-deep-agent/shell-env.js +7 -9
  69. package/dist/activities/execute-deep-agent/shell-env.js.map +1 -1
  70. package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +18 -1
  71. package/dist/activities/execute-deep-agent/subagent-transformer.js +15 -2
  72. package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
  73. package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +27 -6
  74. package/dist/activities/execute-deep-agent/subagent-wiring.js +26 -5
  75. package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
  76. package/dist/activities/hydrate-workflow-execution.js +8 -3
  77. package/dist/activities/hydrate-workflow-execution.js.map +1 -1
  78. package/dist/activities/run-command.d.ts +5 -2
  79. package/dist/activities/run-command.js +22 -10
  80. package/dist/activities/run-command.js.map +1 -1
  81. package/dist/activities/run-env.d.ts +40 -0
  82. package/dist/activities/run-env.js +66 -0
  83. package/dist/activities/run-env.js.map +1 -0
  84. package/dist/activities/workflow-event-activities.d.ts +28 -10
  85. package/dist/activities/workflow-event-activities.js +87 -58
  86. package/dist/activities/workflow-event-activities.js.map +1 -1
  87. package/dist/bootstrap.js +5 -0
  88. package/dist/bootstrap.js.map +1 -1
  89. package/dist/claimcheck/payload-codec.js +21 -1
  90. package/dist/claimcheck/payload-codec.js.map +1 -1
  91. package/dist/client/stigmer-client.d.ts +83 -21
  92. package/dist/client/stigmer-client.js +106 -29
  93. package/dist/client/stigmer-client.js.map +1 -1
  94. package/dist/encryption/config.d.ts +64 -0
  95. package/dist/encryption/config.js +108 -0
  96. package/dist/encryption/config.js.map +1 -0
  97. package/dist/encryption/index.d.ts +3 -0
  98. package/dist/encryption/index.js +3 -0
  99. package/dist/encryption/index.js.map +1 -0
  100. package/dist/encryption/payload-codec.d.ts +41 -0
  101. package/dist/encryption/payload-codec.js +130 -0
  102. package/dist/encryption/payload-codec.js.map +1 -0
  103. package/dist/middleware/index.d.ts +3 -0
  104. package/dist/middleware/index.js +7 -0
  105. package/dist/middleware/index.js.map +1 -1
  106. package/dist/middleware/path-normalization.d.ts +57 -0
  107. package/dist/middleware/path-normalization.js +109 -0
  108. package/dist/middleware/path-normalization.js.map +1 -0
  109. package/dist/middleware/types.d.ts +8 -0
  110. package/dist/payload-codecs.d.ts +17 -0
  111. package/dist/payload-codecs.js +42 -0
  112. package/dist/payload-codecs.js.map +1 -0
  113. package/dist/preflight.d.ts +31 -0
  114. package/dist/preflight.js +43 -0
  115. package/dist/preflight.js.map +1 -1
  116. package/dist/runner-manager.js +21 -17
  117. package/dist/runner-manager.js.map +1 -1
  118. package/dist/runner.js +11 -18
  119. package/dist/runner.js.map +1 -1
  120. package/dist/shared/approval-policy.d.ts +9 -3
  121. package/dist/shared/approval-policy.js +15 -6
  122. package/dist/shared/approval-policy.js.map +1 -1
  123. package/dist/shared/artifact-storage.d.ts +23 -1
  124. package/dist/shared/artifact-storage.js +51 -10
  125. package/dist/shared/artifact-storage.js.map +1 -1
  126. package/dist/shared/attachment-naming.d.ts +53 -0
  127. package/dist/shared/attachment-naming.js +59 -0
  128. package/dist/shared/attachment-naming.js.map +1 -0
  129. package/dist/shared/caller-identity.d.ts +23 -2
  130. package/dist/shared/caller-identity.js +36 -5
  131. package/dist/shared/caller-identity.js.map +1 -1
  132. package/dist/shared/channel-attachment.js +1 -0
  133. package/dist/shared/channel-attachment.js.map +1 -1
  134. package/dist/shared/checkpointer/http-saver.d.ts +26 -1
  135. package/dist/shared/checkpointer/http-saver.js +54 -8
  136. package/dist/shared/checkpointer/http-saver.js.map +1 -1
  137. package/dist/shared/conversation-attachment.js +1 -0
  138. package/dist/shared/conversation-attachment.js.map +1 -1
  139. package/dist/shared/conversation-catchup.d.ts +9 -2
  140. package/dist/shared/conversation-catchup.js +39 -6
  141. package/dist/shared/conversation-catchup.js.map +1 -1
  142. package/dist/shared/datastore-attachment.d.ts +50 -7
  143. package/dist/shared/datastore-attachment.js +93 -11
  144. package/dist/shared/datastore-attachment.js.map +1 -1
  145. package/dist/shared/grpc-retry.d.ts +5 -1
  146. package/dist/shared/grpc-retry.js +5 -1
  147. package/dist/shared/grpc-retry.js.map +1 -1
  148. package/dist/shared/http-retry.d.ts +85 -0
  149. package/dist/shared/http-retry.js +101 -0
  150. package/dist/shared/http-retry.js.map +1 -0
  151. package/dist/shared/llm-backend.d.ts +275 -0
  152. package/dist/shared/llm-backend.js +425 -0
  153. package/dist/shared/llm-backend.js.map +1 -0
  154. package/dist/shared/llm-proxy.d.ts +8 -0
  155. package/dist/shared/llm-proxy.js +15 -0
  156. package/dist/shared/llm-proxy.js.map +1 -1
  157. package/dist/shared/mcp-enabled-tools.d.ts +57 -0
  158. package/dist/shared/mcp-enabled-tools.js +86 -0
  159. package/dist/shared/mcp-enabled-tools.js.map +1 -0
  160. package/dist/shared/mcp-manager.d.ts +11 -5
  161. package/dist/shared/mcp-manager.js +47 -8
  162. package/dist/shared/mcp-manager.js.map +1 -1
  163. package/dist/shared/mcp-resolver.d.ts +39 -2
  164. package/dist/shared/mcp-resolver.js +38 -2
  165. package/dist/shared/mcp-resolver.js.map +1 -1
  166. package/dist/shared/mcp-schema-sanitizer.d.ts +70 -0
  167. package/dist/shared/mcp-schema-sanitizer.js +197 -0
  168. package/dist/shared/mcp-schema-sanitizer.js.map +1 -0
  169. package/dist/shared/model-client.d.ts +18 -5
  170. package/dist/shared/model-client.js +149 -18
  171. package/dist/shared/model-client.js.map +1 -1
  172. package/dist/shared/model-error.js +198 -5
  173. package/dist/shared/model-error.js.map +1 -1
  174. package/dist/shared/model-pricing-data.d.ts +2 -0
  175. package/dist/shared/model-pricing-data.js +13 -3
  176. package/dist/shared/model-pricing-data.js.map +1 -1
  177. package/dist/shared/model-registry.js +3 -2
  178. package/dist/shared/model-registry.js.map +1 -1
  179. package/dist/shared/plan-mode-permissions.d.ts +33 -0
  180. package/dist/shared/plan-mode-permissions.js +35 -0
  181. package/dist/shared/plan-mode-permissions.js.map +1 -0
  182. package/dist/shared/registry-endpoint.d.ts +20 -0
  183. package/dist/shared/registry-endpoint.js +25 -0
  184. package/dist/shared/registry-endpoint.js.map +1 -1
  185. package/dist/shared/runner-credential-keys.d.ts +24 -0
  186. package/dist/shared/runner-credential-keys.js +47 -0
  187. package/dist/shared/runner-credential-keys.js.map +1 -0
  188. package/dist/worker.d.ts +2 -1
  189. package/dist/worker.js +2 -4
  190. package/dist/worker.js.map +1 -1
  191. package/dist/workflow-engine/resolve.d.ts +19 -1
  192. package/dist/workflow-engine/resolve.js +37 -2
  193. package/dist/workflow-engine/resolve.js.map +1 -1
  194. package/dist/workflow-engine/types.d.ts +18 -0
  195. package/dist/workflow-engine/types.js.map +1 -1
  196. package/dist/workflows/call-agent-orchestrator.d.ts +9 -0
  197. package/dist/workflows/call-agent-orchestrator.js +1 -0
  198. package/dist/workflows/call-agent-orchestrator.js.map +1 -1
  199. package/dist/workflows/connect-mcp-server.js +9 -0
  200. package/dist/workflows/connect-mcp-server.js.map +1 -1
  201. package/dist/workflows/engine-core.js +23 -2
  202. package/dist/workflows/engine-core.js.map +1 -1
  203. package/dist/workflows/execute-from-execution.d.ts +1 -1
  204. package/dist/workflows/execute-from-execution.js +11 -1
  205. package/dist/workflows/execute-from-execution.js.map +1 -1
  206. package/dist/workflows/types.d.ts +8 -0
  207. package/package.json +9 -4
  208. package/src/__test-utils__/__tests__/vitest-global-setup.test.ts +37 -0
  209. package/src/__test-utils__/vitest-global-setup.ts +50 -0
  210. package/src/__tests__/bootstrap.test.ts +43 -0
  211. package/src/__tests__/claimcheck-codec.test.ts +36 -0
  212. package/src/__tests__/encryption-codec.test.ts +287 -0
  213. package/src/__tests__/fixtures/encrypted-payload-fixture.json +15 -0
  214. package/src/__tests__/history-encryption-e2e.test.ts +243 -0
  215. package/src/__tests__/preflight.test.ts +65 -2
  216. package/src/activities/__tests__/call-llm.test.ts +75 -0
  217. package/src/activities/__tests__/classify-tool-approvals.test.ts +117 -1
  218. package/src/activities/__tests__/discover-mcp-server.hang.test.ts +103 -0
  219. package/src/activities/__tests__/discover-mcp-server.test.ts +242 -1
  220. package/src/activities/__tests__/error-classifier.test.ts +94 -2
  221. package/src/activities/__tests__/run-command.test.ts +221 -0
  222. package/src/activities/__tests__/workflow-event-activities.test.ts +107 -8
  223. package/src/activities/call-llm.ts +9 -16
  224. package/src/activities/classify-tool-approvals.ts +34 -4
  225. package/src/activities/discover-mcp-server.ts +203 -31
  226. package/src/activities/emit-event.ts +65 -21
  227. package/src/activities/execute-cursor/__test-utils__/cursor-hook-harness.ts +9 -0
  228. package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +14 -0
  229. package/src/activities/execute-cursor/__tests__/attachment-resolver.test.ts +53 -0
  230. package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +151 -16
  231. package/src/activities/execute-cursor/__tests__/error-classifier-extraction.test.ts +208 -0
  232. package/src/activities/execute-cursor/__tests__/extract-structured-output.test.ts +120 -0
  233. package/src/activities/execute-cursor/__tests__/hook-script.test.ts +93 -0
  234. package/src/activities/execute-cursor/__tests__/mcp-resolver.test.ts +125 -0
  235. package/src/activities/execute-cursor/__tests__/model-pricing.test.ts +62 -1
  236. package/src/activities/execute-cursor/__tests__/prompt-builder-delegation.test.ts +1 -1
  237. package/src/activities/execute-cursor/__tests__/turn-recovery.test.ts +160 -0
  238. package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +13 -0
  239. package/src/activities/execute-cursor/approval-state.ts +30 -1
  240. package/src/activities/execute-cursor/attachment-resolver.ts +31 -3
  241. package/src/activities/execute-cursor/blueprint-resolver.ts +7 -27
  242. package/src/activities/execute-cursor/env-resolver.ts +3 -1
  243. package/src/activities/execute-cursor/error-classifier.ts +131 -7
  244. package/src/activities/execute-cursor/extract-structured-output.ts +72 -0
  245. package/src/activities/execute-cursor/hook-script.ts +74 -10
  246. package/src/activities/execute-cursor/index.ts +209 -109
  247. package/src/activities/execute-cursor/mcp-resolver.ts +36 -2
  248. package/src/activities/execute-cursor/model-pricing-data.ts +22 -3
  249. package/src/activities/execute-cursor/prompt-builder.ts +85 -9
  250. package/src/activities/execute-cursor/turn-recovery.ts +208 -0
  251. package/src/activities/execute-cursor/turn-stream.ts +5 -2
  252. package/src/activities/execute-deep-agent/__tests__/attachment-injector.test.ts +110 -8
  253. package/src/activities/execute-deep-agent/__tests__/datastore-degradation.test.ts +104 -0
  254. package/src/activities/execute-deep-agent/__tests__/hitl-reject.test.ts +2 -0
  255. package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +1 -0
  256. package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +1 -0
  257. package/src/activities/execute-deep-agent/__tests__/plan-mode-path-normalization.test.ts +174 -0
  258. package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +34 -5
  259. package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +1 -0
  260. package/src/activities/execute-deep-agent/__tests__/shell-env.test.ts +24 -10
  261. package/src/activities/execute-deep-agent/__tests__/subagent-plan-mode-permissions.test.ts +178 -0
  262. package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +12 -7
  263. package/src/activities/execute-deep-agent/attachment-injector.ts +94 -30
  264. package/src/activities/execute-deep-agent/environment.ts +3 -1
  265. package/src/activities/execute-deep-agent/index.ts +20 -0
  266. package/src/activities/execute-deep-agent/prompt-builder.ts +20 -10
  267. package/src/activities/execute-deep-agent/setup.ts +85 -29
  268. package/src/activities/execute-deep-agent/shell-env.ts +8 -9
  269. package/src/activities/execute-deep-agent/subagent-transformer.ts +30 -2
  270. package/src/activities/execute-deep-agent/subagent-wiring.ts +39 -6
  271. package/src/activities/hydrate-workflow-execution.ts +8 -3
  272. package/src/activities/run-command.ts +25 -12
  273. package/src/activities/run-env.ts +79 -0
  274. package/src/activities/workflow-event-activities.ts +96 -69
  275. package/src/bootstrap.ts +5 -0
  276. package/src/claimcheck/payload-codec.ts +33 -1
  277. package/src/client/__tests__/stigmer-client.test.ts +110 -11
  278. package/src/client/stigmer-client.ts +150 -37
  279. package/src/encryption/config.ts +155 -0
  280. package/src/encryption/index.ts +3 -0
  281. package/src/encryption/payload-codec.ts +152 -0
  282. package/src/middleware/__tests__/path-normalization.test.ts +140 -0
  283. package/src/middleware/index.ts +8 -0
  284. package/src/middleware/path-normalization.ts +125 -0
  285. package/src/middleware/types.ts +11 -0
  286. package/src/payload-codecs.ts +62 -0
  287. package/src/preflight.ts +45 -0
  288. package/src/runner-manager.ts +26 -26
  289. package/src/runner.ts +12 -27
  290. package/src/shared/__tests__/approval-policy.test.ts +82 -39
  291. package/src/shared/__tests__/artifact-storage.test.ts +145 -2
  292. package/src/shared/__tests__/attachment-naming.test.ts +159 -0
  293. package/src/shared/__tests__/bedrock-adapter.test.ts +213 -0
  294. package/src/shared/__tests__/bedrock-seam.test.ts +390 -0
  295. package/src/shared/__tests__/caller-identity.test.ts +25 -0
  296. package/src/shared/__tests__/channel-attachment.test.ts +1 -1
  297. package/src/shared/__tests__/connect-backfill.test.ts +1 -0
  298. package/src/shared/__tests__/conversation-attachment.test.ts +1 -1
  299. package/src/shared/__tests__/conversation-catchup.test.ts +24 -0
  300. package/src/shared/__tests__/datastore-attachment.test.ts +129 -1
  301. package/src/shared/__tests__/foundry-adapter.test.ts +276 -0
  302. package/src/shared/__tests__/foundry-seam.test.ts +482 -0
  303. package/src/shared/__tests__/http-retry.test.ts +68 -0
  304. package/src/shared/__tests__/llm-backend.test.ts +616 -0
  305. package/src/shared/__tests__/mcp-enabled-tools.test.ts +86 -0
  306. package/src/shared/__tests__/mcp-manager.test.ts +137 -15
  307. package/src/shared/__tests__/mcp-resolver.test.ts +146 -3
  308. package/src/shared/__tests__/mcp-schema-sanitizer.test.ts +267 -0
  309. package/src/shared/__tests__/model-client.test.ts +200 -0
  310. package/src/shared/__tests__/model-error.test.ts +289 -1
  311. package/src/shared/__tests__/model-pricing.test.ts +62 -2
  312. package/src/shared/__tests__/model-registry.test.ts +22 -0
  313. package/src/shared/__tests__/synthesized-attachment.test.ts +1 -0
  314. package/src/shared/__tests__/vertex-adapter.test.ts +169 -0
  315. package/src/shared/__tests__/vertex-seam.test.ts +295 -0
  316. package/src/shared/approval-policy.ts +14 -7
  317. package/src/shared/artifact-storage.ts +75 -10
  318. package/src/shared/attachment-naming.ts +78 -0
  319. package/src/shared/caller-identity.ts +40 -5
  320. package/src/shared/channel-attachment.ts +1 -0
  321. package/src/shared/checkpointer/__tests__/http-saver.test.ts +196 -1
  322. package/src/shared/checkpointer/http-saver.ts +71 -8
  323. package/src/shared/conversation-attachment.ts +1 -0
  324. package/src/shared/conversation-catchup.ts +39 -6
  325. package/src/shared/datastore-attachment.ts +106 -11
  326. package/src/shared/grpc-retry.ts +5 -1
  327. package/src/shared/http-retry.ts +139 -0
  328. package/src/shared/llm-backend.ts +544 -0
  329. package/src/shared/llm-proxy.ts +15 -0
  330. package/src/shared/mcp-enabled-tools.ts +105 -0
  331. package/src/shared/mcp-manager.ts +56 -8
  332. package/src/shared/mcp-resolver.ts +73 -2
  333. package/src/shared/mcp-schema-sanitizer.ts +224 -0
  334. package/src/shared/model-client.ts +179 -19
  335. package/src/shared/model-error.ts +222 -4
  336. package/src/shared/model-pricing-data.ts +22 -3
  337. package/src/shared/model-registry.ts +7 -2
  338. package/src/shared/plan-mode-permissions.ts +37 -0
  339. package/src/shared/registry-endpoint.ts +27 -0
  340. package/src/shared/runner-credential-keys.ts +46 -0
  341. package/src/worker.ts +4 -5
  342. package/src/workflow-engine/__tests__/tasks/emit-event.test.ts +127 -49
  343. package/src/workflow-engine/resolve.ts +48 -2
  344. package/src/workflow-engine/types.ts +18 -0
  345. package/src/workflows/__tests__/connect-mcp-server.test.ts +25 -0
  346. package/src/workflows/__tests__/execute-serverless-workflow.test.ts +68 -2
  347. package/src/workflows/call-agent-orchestrator.ts +10 -0
  348. package/src/workflows/connect-mcp-server.ts +9 -0
  349. package/src/workflows/engine-core.ts +23 -2
  350. package/src/workflows/execute-from-execution.ts +12 -2
  351. package/src/workflows/types.ts +8 -0
  352. package/dist/activities/execute-cursor/mcp-config.d.ts +0 -30
  353. package/dist/activities/execute-cursor/mcp-config.js +0 -39
  354. package/dist/activities/execute-cursor/mcp-config.js.map +0 -1
  355. package/src/activities/execute-cursor/mcp-config.ts +0 -66
@@ -9,12 +9,24 @@
9
9
  *
10
10
  * Supported delivery targets:
11
11
  * - webhook: HTTP POST with Content-Type: application/cloudevents+json
12
- * - signal: Temporal signal to another running workflow's listen task
12
+ * - signal: signal to another workflow execution's listen task, routed
13
+ * through the server's SendSignal lane
14
+ *
15
+ * Signal delivery is deliberately server-mediated (oss#517): a direct
16
+ * Temporal client here would bypass the authorization boundary (any
17
+ * workflow could signal any workflow id in the namespace) and is
18
+ * structurally incompatible with payload encryption — emit→listen is
19
+ * the platform's only runner-to-runner channel, and under per-identity
20
+ * runner keys a sender-encrypted signal fails closed at a receiver
21
+ * holding a different key. The server re-produces the payload, so each
22
+ * side's codec passes it through.
13
23
  *
14
24
  * CloudEvents spec: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md
15
25
  */
16
26
 
17
27
  import { randomUUID } from "node:crypto";
28
+ import type { JsonObject } from "@bufbuild/protobuf";
29
+ import { StigmerClient } from "../client/stigmer-client.js";
18
30
  import { loadConfig } from "../config.js";
19
31
  import { resolveRuntimePlaceholders } from "../workflow-engine/resolve.js";
20
32
 
@@ -24,7 +36,9 @@ export interface WebhookDeliveryTarget {
24
36
  }
25
37
 
26
38
  export interface SignalDeliveryTarget {
27
- readonly workflow_id: string;
39
+ /** Target workflow execution id ("wfx_..."), as returned by run/create. */
40
+ readonly execution_id: string;
41
+ /** Signal name matching the target's listen task event id (verbatim). */
28
42
  readonly signal_name: string;
29
43
  }
30
44
 
@@ -54,6 +68,11 @@ export interface EmitEventResult {
54
68
 
55
69
  const WEBHOOK_TIMEOUT_MS = 30_000;
56
70
 
71
+ // Delivery is best-effort by contract, so no single target may consume the
72
+ // CallFunction activity's whole 5m startToClose budget. Same bound as the
73
+ // webhook arm.
74
+ const SIGNAL_TIMEOUT_MS = 30_000;
75
+
57
76
  function buildEnvelope(
58
77
  config: EmitEventConfig,
59
78
  executionId: string,
@@ -120,33 +139,53 @@ async function deliverWebhook(
120
139
  }
121
140
  }
122
141
 
142
+ function buildClient(): StigmerClient {
143
+ const config = loadConfig();
144
+ return new StigmerClient({
145
+ endpoint: config.stigmerBackendEndpoint,
146
+ token: config.stigmerToken,
147
+ });
148
+ }
149
+
123
150
  async function deliverSignal(
124
151
  envelope: Record<string, unknown>,
125
152
  target: SignalDeliveryTarget,
153
+ client: StigmerClient,
126
154
  ): Promise<DeliveryError | null> {
127
- try {
128
- const { Connection, Client } = await import("@temporalio/client");
129
-
130
- // Resolve Temporal coordinates through the central config layer rather than
131
- // reading env directly: signal delivery must dial the SAME cluster/namespace
132
- // the worker connected with. config.ts is the single source of truth for the
133
- // canonical TEMPORAL_SERVICE_ADDRESS / TEMPORAL_NAMESPACE resolution — reading
134
- // env here would diverge (and previously dialed localhost via a stale name).
135
- const config = loadConfig();
136
- const temporalAddress = config.temporalAddress;
137
- const temporalNamespace = config.temporalNamespace;
138
-
139
- const connection = await Connection.connect({ address: temporalAddress });
140
- const client = new Client({ connection, namespace: temporalNamespace });
141
-
142
- const handle = client.workflow.getHandle(target.workflow_id);
143
- await handle.signal(target.signal_name, envelope);
155
+ // Refuse the pre-oss#517 field by name: direct-addressing raw Temporal
156
+ // workflow ids is exactly the capability server mediation removes.
157
+ const legacyWorkflowId = (target as { workflow_id?: unknown }).workflow_id;
158
+ if (!target.execution_id) {
159
+ const reason = legacyWorkflowId
160
+ ? "signal delivery addresses workflow executions by 'execution_id' (\"wfx_...\"); 'workflow_id' is not supported"
161
+ : "signal delivery requires 'execution_id'";
162
+ return {
163
+ target: `signal:${String(legacyWorkflowId ?? "")}/${target.signal_name ?? ""}`,
164
+ error: reason,
165
+ };
166
+ }
167
+ if (!target.signal_name) {
168
+ return {
169
+ target: `signal:${target.execution_id}/`,
170
+ error: "signal delivery requires 'signal_name'",
171
+ };
172
+ }
144
173
 
174
+ try {
175
+ await client.sendWorkflowSignal(
176
+ target.execution_id,
177
+ target.signal_name,
178
+ envelope as JsonObject,
179
+ { timeoutMs: SIGNAL_TIMEOUT_MS },
180
+ );
145
181
  return null;
146
182
  } catch (err) {
183
+ // Server refusals arrive as ConnectErrors whose messages carry the code
184
+ // (e.g. [not_found], [failed_precondition] for terminal executions) —
185
+ // surfaced verbatim in delivery_errors, same contract as the webhook arm.
147
186
  const message = err instanceof Error ? err.message : String(err);
148
187
  return {
149
- target: `signal:${target.workflow_id}/${target.signal_name}`,
188
+ target: `signal:${target.execution_id}/${target.signal_name}`,
150
189
  error: message,
151
190
  };
152
191
  }
@@ -173,13 +212,18 @@ export async function emitEventAction(
173
212
  const errors: DeliveryError[] = [];
174
213
  const env = runtimeEnv ?? {};
175
214
 
215
+ // One client serves all signal targets of this emit; constructed lazily so
216
+ // webhook-only emits never pay for a gRPC transport.
217
+ let client: StigmerClient | undefined;
218
+
176
219
  for (const target of config.delivery) {
177
220
  let err: DeliveryError | null = null;
178
221
 
179
222
  if ("webhook" in target) {
180
223
  err = await deliverWebhook(envelope, target.webhook, env);
181
224
  } else if ("signal" in target) {
182
- err = await deliverSignal(envelope, target.signal);
225
+ client ??= buildClient();
226
+ err = await deliverSignal(envelope, target.signal, client);
183
227
  }
184
228
 
185
229
  if (err) {
@@ -108,6 +108,14 @@ export interface CursorHookHarnessOptions {
108
108
  * non-pausing "unattended" kind and the adapt-and-explain agent message.
109
109
  */
110
110
  unattendedSkip?: boolean;
111
+ /**
112
+ * Per-server enabled_tools allow-lists (issue #350) — restricted servers
113
+ * only. The hook's manifest arm denies a beforeMCPExecution call whose
114
+ * mcp_server_name is listed here with a tool name outside its list (kind
115
+ * "disabled", ahead of every approval bypass). hookMcp payloads carry
116
+ * mcp_server_name "srv".
117
+ */
118
+ mcpServerEnabledTools?: Record<string, string[]>;
111
119
  }
112
120
 
113
121
  /**
@@ -170,6 +178,7 @@ export function setupCursorHookHarness(opts: CursorHookHarnessOptions = {}): Cur
170
178
  opts.captureIgnored ?? false,
171
179
  opts.gitWorkspace ?? true,
172
180
  opts.unattendedSkip ?? false,
181
+ opts.mcpServerEnabledTools ?? {},
173
182
  );
174
183
  writeFileSync(statePath, JSON.stringify(state), "utf-8");
175
184
  }
@@ -219,6 +219,20 @@ describe("buildApprovalState", () => {
219
219
  expect((state as unknown as Record<string, unknown>).builtInGatedList).toBeUndefined();
220
220
  });
221
221
 
222
+ it("carries the enabled_tools manifest (issue #350) and defaults it to empty", () => {
223
+ // Default: no restriction — the hook's disabled arm is inert.
224
+ const bare = buildApprovalState(mcpPolicies, false, new Set());
225
+ expect(bare.mcpServerEnabledTools).toEqual({});
226
+
227
+ // Restricted servers ride through verbatim for the hook's server-scoped
228
+ // membership check.
229
+ const restricted = buildApprovalState(
230
+ mcpPolicies, false, new Set(), undefined, false, false, true, false,
231
+ { planton: ["get_cloud_resource"] },
232
+ );
233
+ expect(restricted.mcpServerEnabledTools).toEqual({ planton: ["get_cloud_resource"] });
234
+ });
235
+
222
236
  it("emits the CONTENT token when a grant carries a content digest", () => {
223
237
  const grants = [{ toolName: "edit", mcpServerSlug: "", key: "write", salient: "/x/gated.txt", contentDigest: "deadbeef", sourceToolCallId: "consent-1" }];
224
238
  const state = buildApprovalState(mcpPolicies, false, new Set(), grants);
@@ -114,6 +114,59 @@ describe("resolveAttachments", () => {
114
114
  expect(readFileSync(join(platformDir, "inputs", "data.csv"), "utf-8")).toBe("a,b,c");
115
115
  });
116
116
 
117
+ it("uniquifies duplicate filenames on the storage branch — neither file's bytes are lost (issue #364)", async () => {
118
+ // Before the fix this branch had no collision check and the second write
119
+ // silently overwrote the first.
120
+ const { storage } = makeInMemoryArtifactStorage();
121
+ await storage.upload("attachments/01AAA/report.pdf", Buffer.from("first bytes"), "application/pdf");
122
+ await storage.upload("attachments/01BBB/report.pdf", Buffer.from("second bytes"), "application/pdf");
123
+
124
+ const result = await resolveAttachments(
125
+ [
126
+ makeAttachment({ filename: "report.pdf", storageKey: "attachments/01AAA/report.pdf" }),
127
+ makeAttachment({ filename: "report.pdf", storageKey: "attachments/01BBB/report.pdf" }),
128
+ ],
129
+ options({ storage }),
130
+ );
131
+
132
+ expect(result).toEqual([
133
+ { filename: "report.pdf", relativePath: ".stigmer/inputs/report.pdf" },
134
+ {
135
+ filename: "report-2.pdf",
136
+ relativePath: ".stigmer/inputs/report-2.pdf",
137
+ renamedFrom: "report.pdf",
138
+ },
139
+ ]);
140
+ expect(readFileSync(join(platformDir, "inputs", "report.pdf"), "utf-8")).toBe("first bytes");
141
+ expect(readFileSync(join(platformDir, "inputs", "report-2.pdf"), "utf-8")).toBe("second bytes");
142
+ });
143
+
144
+ it("uniquifies duplicate filenames across the local and storage branches (one shared taken-set)", async () => {
145
+ const srcPath = join(workspaceDir, "notes.md");
146
+ writeFileSync(srcPath, "local copy");
147
+ const { storage } = makeInMemoryArtifactStorage();
148
+ await storage.upload("attachments/01ABC/notes.md", Buffer.from("uploaded copy"), "text/markdown");
149
+
150
+ const result = await resolveAttachments(
151
+ [
152
+ makeAttachment({ filename: "notes.md", storageKey: "", localPath: srcPath }),
153
+ makeAttachment({ filename: "notes.md", storageKey: "attachments/01ABC/notes.md" }),
154
+ ],
155
+ options({ storage }),
156
+ );
157
+
158
+ expect(result).toEqual([
159
+ { filename: "notes.md", relativePath: ".stigmer/inputs/notes.md" },
160
+ {
161
+ filename: "notes-2.md",
162
+ relativePath: ".stigmer/inputs/notes-2.md",
163
+ renamedFrom: "notes.md",
164
+ },
165
+ ]);
166
+ expect(readFileSync(join(platformDir, "inputs", "notes.md"), "utf-8")).toBe("local copy");
167
+ expect(readFileSync(join(platformDir, "inputs", "notes-2.md"), "utf-8")).toBe("uploaded copy");
168
+ });
169
+
117
170
  it("ignores localPath in cloud mode and downloads by storage key", async () => {
118
171
  const { storage } = makeInMemoryArtifactStorage();
119
172
  await storage.upload("attachments/01ABC/plan.md", Buffer.from("from storage"), "text/markdown");
@@ -14,7 +14,7 @@ import { ApprovalAction, InteractionMode } from "@stigmer/protos/ai/stigmer/agen
14
14
  import { PendingApprovalSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/approval_pb";
15
15
  import { ApiResourceReferenceSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
16
16
 
17
- import { buildPrompt, isHitlReinvocation } from "../index.js";
17
+ import { appendStructuredOutputDirective, buildPrompt, isHitlReinvocation, primarySendCarriesImages } from "../index.js";
18
18
  import type { BuildPromptInput } from "../index.js";
19
19
  import { buildReinvocationPrompt, formatInteractionModePrefix, formatImplementPlanSection, formatToolApprovalProtocol, buildToolApprovalRuleFile } from "../prompt-builder.js";
20
20
  import { PLAN_MODE_DIRECTIVE } from "../../../shared/plan-mode-prompt.js";
@@ -47,7 +47,7 @@ function input(overrides: Partial<BuildPromptInput>): BuildPromptInput {
47
47
  subAgents: [],
48
48
  workspaceDirs: ["/tmp/ws"],
49
49
  workspaceFileRefs: [],
50
- attachmentPaths: [],
50
+ attachments: [],
51
51
  pendingApprovals: [],
52
52
  ...overrides,
53
53
  };
@@ -103,6 +103,10 @@ describe("buildPrompt", () => {
103
103
  expect(prompt).toContain("<available_datastores>");
104
104
  expect(prompt).toContain("- clinic");
105
105
  expect(prompt).toContain("describe_datastore");
106
+ // The standing failure-disclosure instruction (issue #325) is this
107
+ // harness's ONLY outage coverage: the Cursor SDK connects MCP itself,
108
+ // so the runner can never reconcile the live roster here.
109
+ expect(prompt).toContain("do not answer from memory");
106
110
  });
107
111
 
108
112
  it("omits the datastores section when the agent uses no datastores", () => {
@@ -251,7 +255,7 @@ describe("buildPrompt", () => {
251
255
  });
252
256
  });
253
257
 
254
- describe("isHitlReinvocation (the single discriminator for message-accompanied payloads)", () => {
258
+ describe("isHitlReinvocation (paired with resolution.reason at every consumer — issue #366)", () => {
255
259
  it("is false with no decisions and false with an empty map", () => {
256
260
  expect(isHitlReinvocation(undefined)).toBe(false);
257
261
  expect(isHitlReinvocation(new Map())).toBe(false);
@@ -264,12 +268,121 @@ describe("isHitlReinvocation (the single discriminator for message-accompanied p
264
268
  });
265
269
  });
266
270
 
271
+ describe("HITL recovery — fresh agent mid-HITL (issue #366)", () => {
272
+ const decisions = () =>
273
+ new Map<string, ApprovalAction>([["tool-call-1", ApprovalAction.APPROVE]]);
274
+ const approvals = () => [
275
+ create(PendingApprovalSchema, {
276
+ toolCallId: "tool-call-1",
277
+ toolName: "Write",
278
+ message: "Write file: gated.txt",
279
+ }),
280
+ ];
281
+ const recoveryInput = (overrides: Partial<BuildPromptInput> = {}) =>
282
+ input({
283
+ resolution: resolution("local", "created_after_resume_failure"),
284
+ approvalDecisions: decisions(),
285
+ pendingApprovals: approvals(),
286
+ ...overrides,
287
+ });
288
+
289
+ it("keeps the resumed-HITL prompt byte-identical to the bare reinvocation prompt (regression guard)", () => {
290
+ const prompt = buildPrompt(
291
+ input({
292
+ resolution: resolution("local", "resumed_successfully"),
293
+ approvalDecisions: decisions(),
294
+ pendingApprovals: approvals(),
295
+ }),
296
+ );
297
+ expect(prompt).toBe(buildReinvocationPrompt(approvals(), decisions()));
298
+ });
299
+
300
+ it("rebuilds full context for a fresh agent mid-HITL: instructions, the ORIGINAL user message, the state-loss disclosure, and the decisions", () => {
301
+ const prompt = buildPrompt(recoveryInput());
302
+ // The pre-fix failure mode: bare decisions with no story.
303
+ expect(prompt).not.toBe(buildReinvocationPrompt(approvals(), decisions()));
304
+ expect(prompt).toContain("<agent_instructions>");
305
+ expect(prompt).toContain("<tool_approval_protocol>");
306
+ expect(prompt).toContain(`<user_request>\n${USER_MESSAGE}\n</user_request>`);
307
+ expect(prompt).toContain("<turn_recovery>");
308
+ expect(prompt).toContain("Write file: gated.txt");
309
+ expect(prompt).toContain("APPROVED");
310
+ // The opaque tool-call id must not leak into the prompt (reinvocation rule).
311
+ expect(prompt).not.toContain("tool-call-1");
312
+ });
313
+
314
+ it("orders the recovery chronologically: request, then the recovered work, then the decisions, ending on the continue directive", () => {
315
+ const prompt = buildPrompt(
316
+ recoveryInput({ turnRecoveryDigest: "Tool: Write file: draft.txt — completed" }),
317
+ );
318
+ const requestIdx = prompt.indexOf("<user_request>");
319
+ const recoveryIdx = prompt.indexOf("<turn_recovery>");
320
+ const decisionsIdx = prompt.indexOf("APPROVED");
321
+ expect(requestIdx).toBeGreaterThan(-1);
322
+ expect(recoveryIdx).toBeGreaterThan(requestIdx);
323
+ expect(decisionsIdx).toBeGreaterThan(recoveryIdx);
324
+ expect(prompt).toContain("Tool: Write file: draft.txt — completed");
325
+ expect(prompt.trimEnd().endsWith("do not ask the user for permission in prose.")).toBe(true);
326
+ });
327
+
328
+ it("discloses the state loss even with NO transcript — otherwise the decisions read as reactions to proposals this agent never made", () => {
329
+ const prompt = buildPrompt(recoveryInput({ turnRecoveryDigest: undefined }));
330
+ expect(prompt).toContain("<turn_recovery>");
331
+ expect(prompt).toContain("no transcript of your progress is available");
332
+ });
333
+
334
+ it("preserves already-applied semantics: exact-applied writes are described as done, not as work to redo", () => {
335
+ const prompt = buildPrompt(
336
+ recoveryInput({ appliedToolCallIds: new Set(["tool-call-1"]) }),
337
+ );
338
+ expect(prompt).toContain("ALREADY applied");
339
+ expect(prompt).not.toContain("Carry them out now");
340
+ });
341
+
342
+ it("carries the session-standing context a first turn would get — the replacement agent inherits the whole story, not just the blueprint", () => {
343
+ const prompt = buildPrompt(
344
+ recoveryInput({ contextBridge: "User: earlier thread\nAssistant: earlier reply" }),
345
+ );
346
+ expect(prompt).toContain("<previous_conversation_context>");
347
+ expect(prompt).toContain("User: earlier thread");
348
+ });
349
+
350
+ it("selects the recovery shape for ANY non-resumed reason — decisions-only is safe only when native context is guaranteed", () => {
351
+ // created_first_execution + decisions cannot happen in practice
352
+ // (decisions are reconstructed from a persisted transcript, which
353
+ // implies a prior invocation) — but if it ever did, the bare decisions
354
+ // prompt would reproduce the #366 amnesia. Fail safe.
355
+ const prompt = buildPrompt(
356
+ recoveryInput({ resolution: resolution("local", "created_first_execution") }),
357
+ );
358
+ expect(prompt).toContain("<turn_recovery>");
359
+ expect(prompt).toContain("<agent_instructions>");
360
+ });
361
+
362
+ it("primary send skips images ONLY for a resumed HITL agent (which holds them natively); every fresh agent gets the re-delivery", () => {
363
+ expect(primarySendCarriesImages(decisions(), "resumed_successfully")).toBe(false);
364
+ // The #366 vision corollary: fresh agent mid-HITL after a
365
+ // resolution-time resume failure.
366
+ expect(primarySendCarriesImages(decisions(), "created_after_resume_failure")).toBe(true);
367
+ // Non-HITL turns always carry the message's images, resumed or not.
368
+ expect(primarySendCarriesImages(undefined, "resumed_successfully")).toBe(true);
369
+ expect(primarySendCarriesImages(undefined, "created_first_execution")).toBe(true);
370
+ });
371
+
372
+ it("the structured-output directive is a pure append shared by the primary and recovery sends — absent schema, absent suffix", () => {
373
+ expect(appendStructuredOutputDirective("base", undefined)).toBe("base");
374
+ const withSchema = appendStructuredOutputDirective("base", { type: "object" });
375
+ expect(withSchema.startsWith("base\n\n---\nCRITICAL OUTPUT REQUIREMENT:")).toBe(true);
376
+ expect(withSchema).toContain('"type": "object"');
377
+ });
378
+ });
379
+
267
380
  describe("attachments on a resumed turn (T04 — the mid-session WhatsApp case)", () => {
268
381
  const RESUMED = { resolution: resolution("local", "resumed_successfully") };
269
382
 
270
383
  it("announces this turn's attachments to a resumed agent (per-execution value, never inherited)", () => {
271
384
  const prompt = buildPrompt(
272
- input({ ...RESUMED, attachmentPaths: [".stigmer/inputs/lease.pdf"] }),
385
+ input({ ...RESUMED, attachments: [{ path: ".stigmer/inputs/lease.pdf" }] }),
273
386
  );
274
387
  expect(prompt).toContain("<input_files>");
275
388
  expect(prompt).toContain("`.stigmer/inputs/lease.pdf`");
@@ -277,6 +390,23 @@ describe("attachments on a resumed turn (T04 — the mid-session WhatsApp case)"
277
390
  expect(prompt.endsWith(USER_MESSAGE)).toBe(true);
278
391
  });
279
392
 
393
+ it("discloses a duplicate-renamed attachment's original name (issue #364)", () => {
394
+ const prompt = buildPrompt(
395
+ input({
396
+ ...RESUMED,
397
+ attachments: [
398
+ { path: ".stigmer/inputs/report.pdf" },
399
+ { path: ".stigmer/inputs/report-2.pdf", renamedFrom: "report.pdf" },
400
+ ],
401
+ }),
402
+ );
403
+ expect(prompt).toContain(
404
+ "- `.stigmer/inputs/report-2.pdf` (renamed from duplicate 'report.pdf')",
405
+ );
406
+ // The first file keeps a clean entry — no disclosure noise.
407
+ expect(prompt).toContain("- `.stigmer/inputs/report.pdf`\n");
408
+ });
409
+
280
410
  it("keeps a resumed turn WITHOUT attachments byte-identical to the raw message (regression guard)", () => {
281
411
  const prompt = buildPrompt(input({ ...RESUMED }));
282
412
  expect(prompt).toBe(USER_MESSAGE);
@@ -286,7 +416,7 @@ describe("attachments on a resumed turn (T04 — the mid-session WhatsApp case)"
286
416
  const prompt = buildPrompt(
287
417
  input({
288
418
  ...RESUMED,
289
- attachmentPaths: [".stigmer/inputs/photo.jpg"],
419
+ attachments: [{ path: ".stigmer/inputs/photo.jpg" }],
290
420
  conversationCatchup: "User also said hello on the channel.",
291
421
  }),
292
422
  );
@@ -300,7 +430,7 @@ describe("attachments on a resumed turn (T04 — the mid-session WhatsApp case)"
300
430
  const prompt = buildPrompt(
301
431
  input({
302
432
  ...RESUMED,
303
- attachmentPaths: [".stigmer/inputs/a.jpg", ".stigmer/inputs/big.png"],
433
+ attachments: [{ path: ".stigmer/inputs/a.jpg" }, { path: ".stigmer/inputs/big.png" }],
304
434
  vision: {
305
435
  inlineFilenames: ["a.jpg"],
306
436
  notViewable: [{ path: ".stigmer/inputs/big.png", reason: "too_large" }],
@@ -316,7 +446,7 @@ describe("attachments on a resumed turn (T04 — the mid-session WhatsApp case)"
316
446
  const prompt = buildPrompt(
317
447
  input({
318
448
  resolution: resolution("local", "created_first_execution"),
319
- attachmentPaths: [".stigmer/inputs/a.jpg"],
449
+ attachments: [{ path: ".stigmer/inputs/a.jpg" }],
320
450
  vision: { inlineFilenames: ["a.jpg"], notViewable: [] },
321
451
  }),
322
452
  );
@@ -336,7 +466,7 @@ describe("attachments on a resumed turn (T04 — the mid-session WhatsApp case)"
336
466
  message: "Write file: gated.txt",
337
467
  }),
338
468
  ],
339
- attachmentPaths: [".stigmer/inputs/photo.jpg"],
469
+ attachments: [{ path: ".stigmer/inputs/photo.jpg" }],
340
470
  vision: { inlineFilenames: ["photo.jpg"], notViewable: [] },
341
471
  }),
342
472
  );
@@ -543,7 +673,10 @@ describe("formatImplementPlanSection", () => {
543
673
  const PLAN_PATH = ".stigmer/inputs/plan.md";
544
674
 
545
675
  it("wraps the attached-plan directive when the plan is among the attachments", () => {
546
- const section = formatImplementPlanSection(true, [PLAN_PATH, ".stigmer/inputs/data.csv"]);
676
+ const section = formatImplementPlanSection(true, [
677
+ { path: PLAN_PATH },
678
+ { path: ".stigmer/inputs/data.csv" },
679
+ ]);
547
680
 
548
681
  expect(section).toBeDefined();
549
682
  expect(section!.startsWith("<implement_plan>")).toBe(true);
@@ -553,7 +686,9 @@ describe("formatImplementPlanSection", () => {
553
686
  });
554
687
 
555
688
  it("falls back to the conversation-plan directive when no plan attachment resolved", () => {
556
- const section = formatImplementPlanSection(true, [".stigmer/inputs/data.csv"]);
689
+ const section = formatImplementPlanSection(true, [
690
+ { path: ".stigmer/inputs/data.csv" },
691
+ ]);
557
692
 
558
693
  expect(section).toBeDefined();
559
694
  expect(section).not.toContain("plan.md");
@@ -561,12 +696,12 @@ describe("formatImplementPlanSection", () => {
561
696
  });
562
697
 
563
698
  it("returns undefined for an ordinary (non-build) execution", () => {
564
- expect(formatImplementPlanSection(false, [PLAN_PATH])).toBeUndefined();
565
- expect(formatImplementPlanSection(undefined, [PLAN_PATH])).toBeUndefined();
699
+ expect(formatImplementPlanSection(false, [{ path: PLAN_PATH }])).toBeUndefined();
700
+ expect(formatImplementPlanSection(undefined, [{ path: PLAN_PATH }])).toBeUndefined();
566
701
  });
567
702
 
568
703
  it("carries the plan-derived progress-tracking instruction (Tier 3)", () => {
569
- const section = formatImplementPlanSection(true, [PLAN_PATH]);
704
+ const section = formatImplementPlanSection(true, [{ path: PLAN_PATH }]);
570
705
 
571
706
  expect(section).toContain("to-do list");
572
707
  expect(section).toContain("break the plan into");
@@ -577,7 +712,7 @@ describe("formatImplementPlanSection", () => {
577
712
  input({
578
713
  resolution: resolution("local", "created_first_execution"),
579
714
  buildFromPlan: true,
580
- attachmentPaths: [PLAN_PATH],
715
+ attachments: [{ path: PLAN_PATH }],
581
716
  }),
582
717
  );
583
718
 
@@ -593,7 +728,7 @@ describe("formatImplementPlanSection", () => {
593
728
  input({
594
729
  resolution: resolution("local", "resumed_successfully"),
595
730
  buildFromPlan: true,
596
- attachmentPaths: [PLAN_PATH],
731
+ attachments: [{ path: PLAN_PATH }],
597
732
  }),
598
733
  );
599
734
 
@@ -611,7 +746,7 @@ describe("formatImplementPlanSection", () => {
611
746
  input({
612
747
  resolution: resolution("local", "resumed_successfully"),
613
748
  buildFromPlan: false,
614
- attachmentPaths: [PLAN_PATH],
749
+ attachments: [{ path: PLAN_PATH }],
615
750
  }),
616
751
  );
617
752