@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
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Naming policy for execution attachments — the single owner of the rule that
3
+ * resolves duplicate attachment filenames (issue #364).
4
+ *
5
+ * Both harnesses materialize attachments into the platform inputs namespace
6
+ * keyed by filename, so two attachments carrying the same name contend for
7
+ * one on-disk path. Before this module existed each harness had an accidental
8
+ * answer: the deep-agent injector failed the whole execution on the collision
9
+ * and the Cursor resolver silently overwrote the earlier file. The platform
10
+ * answer is neither — a duplicate is mechanically renamed (`report.pdf`,
11
+ * `report-2.pdf`, ...) and the rename is disclosed to the agent through the
12
+ * input-files prompt section, so no execution is ever burned over a
13
+ * resolvable name and no user file silently vanishes.
14
+ *
15
+ * The `stem-2.ext` semantics deliberately mirror the React SDK composer's
16
+ * client-side rename (sdk/react/src/attachment/attachment-utils.ts,
17
+ * `uniquifyFilename` — kept in sync by hand, the packages share no
18
+ * dependency), so a user sees the same rename shape whether the client or the
19
+ * runner performed it.
20
+ *
21
+ * Scope: this module owns NAMES only. What constitutes a collision is the
22
+ * caller's business — the deep-agent injector keys on full mount paths
23
+ * (explicit `mountPath` values participate), the Cursor resolver keys on bare
24
+ * filenames under `inputs/`. Callers that consider a collision a user
25
+ * contradiction (two attachments explicitly pinning the same mount path)
26
+ * keep rejecting; only mechanically derived names are renamed.
27
+ */
28
+
29
+ /** The outcome of allocating a unique name for one attachment. */
30
+ export interface AllocatedName {
31
+ /** The final name — unchanged when it was free, `stem-N.ext` otherwise. */
32
+ readonly name: string;
33
+ /**
34
+ * The original requested name, present only when a rename happened — the
35
+ * disclosure payload the prompt builders render so the agent can still
36
+ * connect "the two report.pdfs" in the user's message to distinct files.
37
+ */
38
+ readonly renamedFrom?: string;
39
+ }
40
+
41
+ /**
42
+ * Returns `name` unchanged when it is not in `taken`, otherwise the first
43
+ * free `stem-2.ext`, `stem-3.ext`, … variant.
44
+ *
45
+ * Byte-for-byte twin of the React SDK's `uniquifyFilename`
46
+ * (sdk/react/src/attachment/attachment-utils.ts) so client-side and
47
+ * runner-side renames are indistinguishable to the user.
48
+ */
49
+ export function uniquifyFilename(
50
+ name: string,
51
+ taken: ReadonlySet<string>,
52
+ ): string {
53
+ if (!taken.has(name)) return name;
54
+
55
+ const dotIndex = name.lastIndexOf(".");
56
+ // A leading dot (".env") is a hidden-file prefix, not an extension.
57
+ const stem = dotIndex > 0 ? name.slice(0, dotIndex) : name;
58
+ const ext = dotIndex > 0 ? name.slice(dotIndex) : "";
59
+
60
+ for (let n = 2; ; n++) {
61
+ const candidate = `${stem}-${n}${ext}`;
62
+ if (!taken.has(candidate)) return candidate;
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Allocate a unique name against `taken`, claiming the result in the set so
68
+ * sequential allocations see each other. Returns the disclosure payload
69
+ * (`renamedFrom`) when the name had to change.
70
+ */
71
+ export function allocateUniqueName(
72
+ name: string,
73
+ taken: Set<string>,
74
+ ): AllocatedName {
75
+ const unique = uniquifyFilename(name, taken);
76
+ taken.add(unique);
77
+ return unique === name ? { name: unique } : { name: unique, renamedFrom: name };
78
+ }
@@ -10,8 +10,10 @@
10
10
  * 2. The session creator (`stigmer_user`) from the Session resource's
11
11
  * audit actor — console/CLI sessions have no channel sender, but the
12
12
  * platform knows exactly who created the session.
13
- * 3. The anonymous sentinel — discovery (no session exists) and sessions
14
- * with no readable creator. Consumers must treat anonymous as a
13
+ * 3. The anonymous sentinel — discovery (no session exists), sessions
14
+ * with no readable creator, and sessions whose creator is the
15
+ * backend's "system" audit placeholder (not a principal — see
16
+ * SYSTEM_CREATOR_SENTINEL). Consumers must treat anonymous as a
15
17
  * first-class caller: answer tools/list, refuse tool calls.
16
18
  *
17
19
  * Injection is opt-in by construction: the values enter the env map used
@@ -54,6 +56,20 @@ export const STIGMER_USER_KIND = "stigmer_user";
54
56
  */
55
57
  export const ANONYMOUS_KIND = "anonymous";
56
58
 
59
+ /**
60
+ * Audit-actor id that backends stamp when NO caller identity exists —
61
+ * the OSS server writes it on every create (no local auth), and the
62
+ * cloud's AuditActorBuilder falls back to it for caller-less internal
63
+ * writes. It names "nobody in particular": unrelated sessions from
64
+ * unrelated people all carry it, so presenting it as a caller identity
65
+ * would make the one string a grantable value that silently covers ALL
66
+ * such traffic in an MCP server's binding sheet. A creator matching this
67
+ * sentinel (and carrying no email) is therefore unresolvable and falls
68
+ * to anonymous — the deny-by-default the docs guide already promises
69
+ * for self-hosted backends.
70
+ */
71
+ export const SYSTEM_CREATOR_SENTINEL = "system";
72
+
57
73
  /** The resolved caller identity. */
58
74
  export interface CallerIdentity {
59
75
  kind: string;
@@ -79,6 +95,12 @@ export function anonymousCallerIdentity(): CallerIdentity {
79
95
  * humans, and the audit actor's `id` field is historically mixed
80
96
  * (identity-account id vs email — see the proto's own @internal note).
81
97
  * Binding matchers should compare emails case-insensitively.
98
+ *
99
+ * A creator with no email whose id is the "system" audit placeholder is
100
+ * NOT a principal (see SYSTEM_CREATOR_SENTINEL) and resolves to
101
+ * anonymous. Email-first is deliberate here too: a real account that
102
+ * merely has "system" somewhere in its id is never demoted, because its
103
+ * email wins before the sentinel check runs.
82
104
  */
83
105
  export function resolveCallerIdentity(
84
106
  sessionMetadata: Record<string, string> | undefined,
@@ -90,10 +112,23 @@ export function resolveCallerIdentity(
90
112
  }
91
113
 
92
114
  const email = creator?.email?.trim();
115
+ if (email) {
116
+ return { kind: STIGMER_USER_KIND, value: email };
117
+ }
118
+
93
119
  const id = creator?.id?.trim();
94
- const value = email || id;
95
- if (value) {
96
- return { kind: STIGMER_USER_KIND, value };
120
+ if (id === SYSTEM_CREATOR_SENTINEL) {
121
+ // Operator tripwire: identity-gated MCP tools will refuse this
122
+ // session; the fix is real caller attribution, never a "system" grant.
123
+ console.info(
124
+ `Session creator is the "${SYSTEM_CREATOR_SENTINEL}" audit placeholder ` +
125
+ `(no email) — not a resolvable principal; presenting the anonymous ` +
126
+ `caller identity to MCP servers`,
127
+ );
128
+ return anonymousCallerIdentity();
129
+ }
130
+ if (id) {
131
+ return { kind: STIGMER_USER_KIND, value: id };
97
132
  }
98
133
 
99
134
  return anonymousCallerIdentity();
@@ -128,6 +128,7 @@ export function synthesizeChannelAttachment(
128
128
  slug: CHANNEL_ATTACHMENT_SLUG,
129
129
  toolApprovals: [],
130
130
  pinnedToolApprovals: [],
131
+ toolApprovalOverrides: [],
131
132
  discoveredCapabilitiesEmpty: false,
132
133
  };
133
134
 
@@ -29,9 +29,17 @@ async function serializeForProxy(obj: unknown): Promise<{ type: string; binary:
29
29
  describe("HttpCheckpointSaver", () => {
30
30
  let saver: HttpCheckpointSaver;
31
31
  let fetchSpy: ReturnType<typeof vi.fn>;
32
+ let recordedDelays: number[];
32
33
 
33
34
  beforeEach(() => {
34
- saver = new HttpCheckpointSaver(PROXY, TOKEN);
35
+ // delayFn injected so retry-path tests record the backoff schedule
36
+ // instead of sleeping through it (~7.75 s of real delays otherwise).
37
+ recordedDelays = [];
38
+ saver = new HttpCheckpointSaver(PROXY, TOKEN, {
39
+ delayFn: async (ms) => {
40
+ recordedDelays.push(ms);
41
+ },
42
+ });
35
43
  fetchSpy = vi.fn();
36
44
  vi.stubGlobal("fetch", fetchSpy);
37
45
  });
@@ -163,6 +171,193 @@ describe("HttpCheckpointSaver", () => {
163
171
  });
164
172
  });
165
173
 
174
+ // The bounded-backoff loop (cloud#188). Classification policy itself is
175
+ // covered by shared/__tests__/http-retry.test.ts; these cases pin the
176
+ // loop's behavior at the saver's call sites: transient failures recover,
177
+ // deterministic failures never retry, budget exhaustion still fails
178
+ // loudly, and 404 semantics survive unchanged.
179
+ describe("retry behavior", () => {
180
+ const ok404 = { status: 404, ok: false, statusText: "Not Found" };
181
+ const err503 = { status: 503, ok: false, statusText: "Service Unavailable" };
182
+ const err400 = { status: 400, ok: false, statusText: "Bad Request" };
183
+ const err401 = { status: 401, ok: false, statusText: "Unauthorized" };
184
+
185
+ function makeCheckpoint(): Checkpoint {
186
+ return {
187
+ v: 4,
188
+ id: "cp-retry",
189
+ ts: new Date().toISOString(),
190
+ channel_values: {},
191
+ channel_versions: {},
192
+ versions_seen: {},
193
+ };
194
+ }
195
+ const md: CheckpointMetadata = { source: "loop", step: 0, parents: {} };
196
+
197
+ it("recovers a put from a transient 503", async () => {
198
+ fetchSpy
199
+ .mockResolvedValueOnce(err503)
200
+ .mockResolvedValueOnce({ ok: true, json: () => Promise.resolve({}) });
201
+
202
+ const result = await saver.put(makeConfig(), makeCheckpoint(), md, {});
203
+ expect(result.configurable?.checkpoint_id).toBe("cp-retry");
204
+ expect(fetchSpy).toHaveBeenCalledTimes(2);
205
+ expect(recordedDelays).toEqual([250]);
206
+ });
207
+
208
+ it("recovers a put from a network-level rejection (undici TypeError)", async () => {
209
+ fetchSpy
210
+ .mockRejectedValueOnce(new TypeError("fetch failed"))
211
+ .mockResolvedValueOnce({ ok: true, json: () => Promise.resolve({}) });
212
+
213
+ await expect(saver.put(makeConfig(), makeCheckpoint(), md, {})).resolves.toBeDefined();
214
+ expect(fetchSpy).toHaveBeenCalledTimes(2);
215
+ });
216
+
217
+ it("backs off exponentially across consecutive transient failures", async () => {
218
+ fetchSpy
219
+ .mockResolvedValueOnce(err503)
220
+ .mockResolvedValueOnce(err503)
221
+ .mockResolvedValueOnce(err503)
222
+ .mockResolvedValueOnce({ ok: true, json: () => Promise.resolve({}) });
223
+
224
+ await saver.put(makeConfig(), makeCheckpoint(), md, {});
225
+ expect(recordedDelays).toEqual([250, 500, 1000]);
226
+ });
227
+
228
+ it("fails loudly when the budget is exhausted on a retryable status", async () => {
229
+ fetchSpy.mockResolvedValue(err503);
230
+
231
+ await expect(saver.put(makeConfig(), makeCheckpoint(), md, {}))
232
+ .rejects.toThrow("Checkpoint PUT failed: 503");
233
+ // 1 initial attempt + 5 retries (the default budget).
234
+ expect(fetchSpy).toHaveBeenCalledTimes(6);
235
+ expect(recordedDelays).toEqual([250, 500, 1000, 2000, 4000]);
236
+ });
237
+
238
+ it("rethrows the original error when the budget is exhausted on network rejections", async () => {
239
+ const netErr = new TypeError("fetch failed");
240
+ fetchSpy.mockRejectedValue(netErr);
241
+
242
+ await expect(saver.putWrites(makeConfig(), [["messages", { a: 1 }]], "t1"))
243
+ .rejects.toBe(netErr);
244
+ expect(fetchSpy).toHaveBeenCalledTimes(6);
245
+ });
246
+
247
+ it("never retries a deterministic 400", async () => {
248
+ fetchSpy.mockResolvedValue(err400);
249
+
250
+ await expect(saver.put(makeConfig(), makeCheckpoint(), md, {}))
251
+ .rejects.toThrow("Checkpoint PUT failed: 400");
252
+ expect(fetchSpy).toHaveBeenCalledTimes(1);
253
+ expect(recordedDelays).toEqual([]);
254
+ });
255
+
256
+ it("never retries a rejected credential (401)", async () => {
257
+ fetchSpy.mockResolvedValue(err401);
258
+
259
+ await expect(saver.getTuple(makeConfig())).rejects.toThrow("Checkpoint GET failed: 401");
260
+ expect(fetchSpy).toHaveBeenCalledTimes(1);
261
+ });
262
+
263
+ it("never retries a manual abort (AbortError)", async () => {
264
+ const abortErr = new DOMException("This operation was aborted", "AbortError");
265
+ fetchSpy.mockRejectedValue(abortErr);
266
+
267
+ await expect(saver.getTuple(makeConfig())).rejects.toBe(abortErr);
268
+ expect(fetchSpy).toHaveBeenCalledTimes(1);
269
+ });
270
+
271
+ it("retries a hung request aborted by the per-request timeout (TimeoutError)", async () => {
272
+ fetchSpy
273
+ .mockRejectedValueOnce(
274
+ new DOMException("The operation was aborted due to timeout", "TimeoutError"),
275
+ )
276
+ .mockResolvedValueOnce(ok404);
277
+
278
+ await expect(saver.getTuple(makeConfig())).resolves.toBeUndefined();
279
+ expect(fetchSpy).toHaveBeenCalledTimes(2);
280
+ });
281
+
282
+ it("preserves getTuple's 404 semantics without retrying", async () => {
283
+ fetchSpy.mockResolvedValue(ok404);
284
+
285
+ await expect(saver.getTuple(makeConfig())).resolves.toBeUndefined();
286
+ expect(fetchSpy).toHaveBeenCalledTimes(1);
287
+ });
288
+
289
+ it("preserves deleteThread's 404 tolerance without retrying", async () => {
290
+ fetchSpy.mockResolvedValue(ok404);
291
+
292
+ await expect(saver.deleteThread("thread-1")).resolves.toBeUndefined();
293
+ expect(fetchSpy).toHaveBeenCalledTimes(1);
294
+ });
295
+
296
+ it("bounds every request with an abort signal", async () => {
297
+ fetchSpy.mockResolvedValue(ok404);
298
+
299
+ await saver.getTuple(makeConfig());
300
+ const [, opts] = fetchSpy.mock.calls[0];
301
+ expect(opts.signal).toBeInstanceOf(AbortSignal);
302
+ });
303
+
304
+ it("surfaces a pending-writes read failure instead of silently degrading", async () => {
305
+ // The checkpoint GET succeeds; the follow-up writes GET fails on every
306
+ // attempt. Pre-#188 code mapped this to "no pending writes" and
307
+ // returned a tuple stripped of resume state.
308
+ const cp = makeCheckpoint();
309
+ const cpSer = await serializeForProxy(cp);
310
+ const mdSer = await serializeForProxy(md);
311
+ const checkpointDoc = {
312
+ thread_id: "thread-1",
313
+ checkpoint_ns: "",
314
+ checkpoint_id: cp.id,
315
+ type: cpSer.type,
316
+ checkpoint: cpSer.binary,
317
+ metadata_type: mdSer.type,
318
+ metadata: mdSer.binary,
319
+ };
320
+ fetchSpy.mockImplementation((url: string) => {
321
+ if (url.includes("/writes")) return Promise.resolve(err503);
322
+ return Promise.resolve({ ok: true, json: () => Promise.resolve(checkpointDoc) });
323
+ });
324
+
325
+ await expect(saver.getTuple(makeConfig())).rejects.toThrow(
326
+ "Checkpoint writes GET failed: 503",
327
+ );
328
+ });
329
+
330
+ it("recovers the pending-writes read from a transient failure", async () => {
331
+ const cp = makeCheckpoint();
332
+ const cpSer = await serializeForProxy(cp);
333
+ const mdSer = await serializeForProxy(md);
334
+ const checkpointDoc = {
335
+ thread_id: "thread-1",
336
+ checkpoint_ns: "",
337
+ checkpoint_id: cp.id,
338
+ type: cpSer.type,
339
+ checkpoint: cpSer.binary,
340
+ metadata_type: mdSer.type,
341
+ metadata: mdSer.binary,
342
+ };
343
+ let writesCalls = 0;
344
+ fetchSpy.mockImplementation((url: string) => {
345
+ if (url.includes("/writes")) {
346
+ writesCalls++;
347
+ return writesCalls === 1
348
+ ? Promise.resolve(err503)
349
+ : Promise.resolve({ ok: true, json: () => Promise.resolve({ writes: [] }) });
350
+ }
351
+ return Promise.resolve({ ok: true, json: () => Promise.resolve(checkpointDoc) });
352
+ });
353
+
354
+ const tuple = await saver.getTuple(makeConfig());
355
+ expect(tuple?.checkpoint.id).toBe(cp.id);
356
+ expect(tuple?.pendingWrites).toEqual([]);
357
+ expect(writesCalls).toBe(2);
358
+ });
359
+ });
360
+
166
361
  describe("URL construction", () => {
167
362
  it("strips trailing slashes from proxy endpoint", () => {
168
363
  const s = new HttpCheckpointSaver("https://proxy.test///", "tok");
@@ -13,6 +13,19 @@
13
13
  *
14
14
  * The Java proxy calls Document.parse(json) which handles $binary
15
15
  * natively, and doc.toJson() emits the same format on reads.
16
+ *
17
+ * Every request runs through the shared bounded-backoff loop
18
+ * (http-retry.ts's fetchWithRetry, with this saver's policy: transient
19
+ * classification + a per-request abort timeout). That loop is the ONLY
20
+ * retry layer between an agent execution and its checkpoints — the deep
21
+ * agent activity is deliberately non-retryable at the Temporal level
22
+ * (maximumAttempts: 1; replaying a whole agent run is not safe), so before
23
+ * this existed a single dropped request killed the execution
24
+ * (stigmer/stigmer-cloud#188). Retrying puts is safe by server contract:
25
+ * CheckpointStore documents putCheckpoint/putWrite as keyed save-or-replace
26
+ * upserts. Budget exhaustion still fails loudly — checkpoints are not
27
+ * lossy-tolerable (unlike status updates, see status.ts), and silently
28
+ * dropping one would corrupt resume state.
16
29
  */
17
30
 
18
31
  import type { RunnableConfig } from "@langchain/core/runnables";
@@ -25,6 +38,8 @@ import {
25
38
  type ChannelVersions,
26
39
  } from "@langchain/langgraph-checkpoint";
27
40
  import type { CheckpointMetadata, PendingWrite } from "@langchain/langgraph-checkpoint";
41
+ import type { RetryOptions } from "../grpc-retry.js";
42
+ import { fetchWithRetry, type FetchRetryPolicy } from "../http-retry.js";
28
43
 
29
44
  function encodeB64(data: Uint8Array): string {
30
45
  if (typeof Buffer !== "undefined") {
@@ -77,17 +92,59 @@ function configOrg(config: RunnableConfig): string | undefined {
77
92
  return config.configurable?.org as string | undefined;
78
93
  }
79
94
 
95
+ /** Retry policy plus the per-request bound; every field is defaulted. */
96
+ export interface HttpCheckpointSaverOptions extends RetryOptions {
97
+ /**
98
+ * Milliseconds before an in-flight request is aborted and the attempt is
99
+ * classified retryable. Without it a hung connection (the realistic
100
+ * unplanned-pod-kill symptom) stalls forever and the retry never engages.
101
+ */
102
+ readonly requestTimeoutMs?: number;
103
+ }
104
+
105
+ /**
106
+ * Default transient-retry policy. Wider than status.ts's DEFAULT_PERSIST_RETRY
107
+ * (3 retries, ~700 ms) on purpose: a dropped status update is lossy-tolerable,
108
+ * a dropped checkpoint kills the execution, so the budget here is sized to
109
+ * span a pod-restart reroute (~7.75 s of delays across 5 retries). Worst case
110
+ * with every attempt hanging — 6 x 30 s + delays, ~3.7 min — stays well inside
111
+ * the deep-agent activity's 1 h startToCloseTimeout (no heartbeatTimeout).
112
+ */
113
+ const DEFAULT_RETRY = {
114
+ baseDelayMs: 250,
115
+ backoffFactor: 2,
116
+ maxRetries: 5,
117
+ requestTimeoutMs: 30_000,
118
+ } as const;
119
+
80
120
  export class HttpCheckpointSaver extends BaseCheckpointSaver {
81
121
  private readonly baseUrl: string;
82
122
  private readonly headers: Record<string, string>;
123
+ private readonly retryPolicy: FetchRetryPolicy;
83
124
 
84
- constructor(proxyEndpoint: string, authToken: string) {
125
+ constructor(
126
+ proxyEndpoint: string,
127
+ authToken: string,
128
+ options: HttpCheckpointSaverOptions = {},
129
+ ) {
85
130
  super();
86
131
  this.baseUrl = `${proxyEndpoint.replace(/\/+$/, "")}/v1/proxy/checkpoints`;
87
132
  this.headers = {
88
133
  Authorization: `Bearer ${authToken}`,
89
134
  "Content-Type": "application/json",
90
135
  };
136
+ this.retryPolicy = {
137
+ label: "HttpCheckpointSaver",
138
+ baseDelayMs: options.baseDelayMs ?? DEFAULT_RETRY.baseDelayMs,
139
+ backoffFactor: options.backoffFactor ?? DEFAULT_RETRY.backoffFactor,
140
+ maxRetries: options.maxRetries ?? DEFAULT_RETRY.maxRetries,
141
+ requestTimeoutMs: options.requestTimeoutMs ?? DEFAULT_RETRY.requestTimeoutMs,
142
+ delayFn: options.delayFn,
143
+ };
144
+ }
145
+
146
+ private fetchWithRetry(url: string, init?: RequestInit): Promise<Response> {
147
+ return fetchWithRetry(url, init, this.retryPolicy);
91
148
  }
92
149
 
93
150
  private async serializeTyped(obj: unknown): Promise<[string, BinaryObj]> {
@@ -113,7 +170,7 @@ export class HttpCheckpointSaver extends BaseCheckpointSaver {
113
170
  params.set("checkpoint_id", checkpointId);
114
171
  }
115
172
 
116
- const resp = await fetch(`${this.baseUrl}/checkpoint?${params}`, {
173
+ const resp = await this.fetchWithRetry(`${this.baseUrl}/checkpoint?${params}`, {
117
174
  headers: this.headers,
118
175
  });
119
176
  if (resp.status === 404) return undefined;
@@ -143,7 +200,7 @@ export class HttpCheckpointSaver extends BaseCheckpointSaver {
143
200
  params.set("before", beforeId);
144
201
  }
145
202
 
146
- const resp = await fetch(`${this.baseUrl}/checkpoints?${params}`, {
203
+ const resp = await this.fetchWithRetry(`${this.baseUrl}/checkpoints?${params}`, {
147
204
  headers: this.headers,
148
205
  });
149
206
  if (!resp.ok) {
@@ -183,7 +240,7 @@ export class HttpCheckpointSaver extends BaseCheckpointSaver {
183
240
  const orgId = configOrg(config);
184
241
  if (orgId) doc.org_id = orgId;
185
242
 
186
- const resp = await fetch(`${this.baseUrl}/checkpoint`, {
243
+ const resp = await this.fetchWithRetry(`${this.baseUrl}/checkpoint`, {
187
244
  method: "PUT",
188
245
  headers: this.headers,
189
246
  body: JSON.stringify(doc),
@@ -203,7 +260,7 @@ export class HttpCheckpointSaver extends BaseCheckpointSaver {
203
260
 
204
261
  async deleteThread(threadId: string): Promise<void> {
205
262
  const params = new URLSearchParams({ thread_id: threadId });
206
- const resp = await fetch(`${this.baseUrl}/thread?${params}`, {
263
+ const resp = await this.fetchWithRetry(`${this.baseUrl}/thread?${params}`, {
207
264
  method: "DELETE",
208
265
  headers: this.headers,
209
266
  });
@@ -238,7 +295,7 @@ export class HttpCheckpointSaver extends BaseCheckpointSaver {
238
295
  return doc;
239
296
  }));
240
297
 
241
- const resp = await fetch(`${this.baseUrl}/writes`, {
298
+ const resp = await this.fetchWithRetry(`${this.baseUrl}/writes`, {
242
299
  method: "PUT",
243
300
  headers: this.headers,
244
301
  body: JSON.stringify({ writes: docs }),
@@ -272,7 +329,7 @@ export class HttpCheckpointSaver extends BaseCheckpointSaver {
272
329
  };
273
330
  }
274
331
 
275
- const writesResp = await fetch(
332
+ const writesResp = await this.fetchWithRetry(
276
333
  `${this.baseUrl}/writes?${new URLSearchParams({
277
334
  thread_id: threadId,
278
335
  checkpoint_ns: checkpointNs,
@@ -280,8 +337,14 @@ export class HttpCheckpointSaver extends BaseCheckpointSaver {
280
337
  })}`,
281
338
  { headers: this.headers },
282
339
  );
340
+ // Fail loudly, never degrade: this used to map ANY non-ok response to "no
341
+ // pending writes", which silently stripped resume state on a transient
342
+ // 500 — a checkpoint would load without the writes recorded against it.
343
+ if (!writesResp.ok) {
344
+ throw new Error(`Checkpoint writes GET failed: ${writesResp.status} ${writesResp.statusText}`);
345
+ }
283
346
  const pendingWrites = await this.parseWrites(
284
- writesResp.ok ? (await writesResp.json()) as Record<string, any> : {},
347
+ (await writesResp.json()) as Record<string, any>,
285
348
  );
286
349
 
287
350
  return {
@@ -105,6 +105,7 @@ export function synthesizeConversationAttachment(
105
105
  slug: CONVERSATION_ATTACHMENT_SLUG,
106
106
  toolApprovals: [],
107
107
  pinnedToolApprovals: [],
108
+ toolApprovalOverrides: [],
108
109
  discoveredCapabilitiesEmpty: false,
109
110
  connectionType: "http",
110
111
  url: options.bridgeEndpoint.replace(/\/+$/, "") + CONVERSATION_ROUTE,
@@ -1,8 +1,15 @@
1
1
  /**
2
2
  * Conversation catchup (cloud channel-conversations DD-006): what happened on
3
3
  * a live channel conversation that the agent has not seen — customer messages
4
- * handled by a human teammate, the teammate's replies, platform notices the
5
- * customer received, notes, and the agent's own earlier escalations.
4
+ * handled by a human teammate, the teammate's replies, platform notices,
5
+ * notes, and the agent's own earlier escalations. Since cloud#347 a line
6
+ * whose send is known-undelivered carries a `(not delivered)` speaker
7
+ * annotation and an in-flight one carries `(sending)` — the digest no longer
8
+ * implies every public-lane line reached the customer. Since cloud#352 the
9
+ * digest also closes the feedback loop on send OUTCOMES: the agent's own
10
+ * dead-lettered sends surface as `You (not delivered):` lines, and a send
11
+ * that failed only after an earlier digest already conveyed it surfaces as a
12
+ * `System:` delivery-failure notice at the moment the failure became known.
6
13
  *
7
14
  * The cloud composes the CONTENT (bare `Customer:` / `Teammate:` / `System:` /
8
15
  * `You escalated:` / `Note:` lines, oldest first) on the execution spec's
@@ -29,16 +36,42 @@ import type { ConversationCatchup } from "@stigmer/protos/ai/stigmer/agentic/age
29
36
  * drift between them. Deliberately takeover-neutral: a digest can exist with
30
37
  * no human handoff at all (a failed turn's re-composed window), so the
31
38
  * preamble asserts only what is always true (the A15/A20 honesty bar).
39
+ *
40
+ * The send-status annotations (cloud#347) get an explicit exception from the
41
+ * don't-re-answer contract: a `(not delivered)` teammate reply is words the
42
+ * customer never got, so treating it as settled history would silently
43
+ * abandon whatever it was meant to convey. The preamble defines the
44
+ * annotations — the DD-013 split puts model-facing meaning here, while the
45
+ * cloud composer owns which lines earn them.
46
+ *
47
+ * The send-outcome lines (cloud#352, cloud triage DD-009) extend the same
48
+ * exception to the agent's OWN sends: `You (not delivered):` lines and
49
+ * `System:` delivery-failure notices mean the customer never got those
50
+ * words. The behavioral contract is owner-ruled (DD-009 Q-4): treat the
51
+ * failure as unfinished conversation business — weigh what still needs
52
+ * saying and re-say it naturally in the next turn — but never resend the
53
+ * failed text verbatim: a multi-chunk send can partially land, and a
54
+ * word-for-word repeat risks the customer reading the same message twice.
32
55
  */
33
56
  const CONVERSATION_CATCHUP_PREAMBLE =
34
57
  "Below is activity from this conversation that you have not seen — " +
35
58
  "oldest first. It may include customer messages that were handled by a " +
36
- "human teammate, the teammate's own replies, notices the customer " +
37
- "received, internal notes, and escalations you raised earlier. Treat it " +
59
+ "human teammate, the teammate's own replies, notices sent to the " +
60
+ "customer, internal notes, and escalations you raised earlier. Treat it " +
38
61
  "as conversation history you already know: do not answer or re-answer " +
39
62
  "these messages, do not repeat or summarize them back, and do not " +
40
- "mention any handoff unless asked. Continue from the customer's newest " +
41
- "message.";
63
+ "mention any handoff unless asked. One exception: lines marked " +
64
+ "(not delivered) never reached the customer, and lines marked (sending) " +
65
+ "were still on their way when this summary was built — the customer may " +
66
+ "not have seen those words, so weigh that when deciding what still needs " +
67
+ "saying. That includes your own words: a line marked " +
68
+ "You (not delivered), or a System line reporting that a message was not " +
69
+ "delivered, means the customer never received it. Treat such a failure " +
70
+ "as unfinished business — if what it said still matters, work it " +
71
+ "naturally into your reply in your own words, but never resend the " +
72
+ "failed text word-for-word (part of it may have reached the customer, " +
73
+ "and an exact repeat reads as a duplicate). " +
74
+ "Continue from the customer's newest message.";
42
75
 
43
76
  /**
44
77
  * Read the catchup digest from an execution spec's `conversation_catchup`.