@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
@@ -6,10 +6,11 @@
6
6
  * (e.g. "claude-haiku-4-5-20251001") before a client is built — so no
7
7
  * activity can bypass the translation and 404 the provider.
8
8
  *
9
- * Layering: `llm-proxy.ts` stays pure routing utilities (no LangChain
10
- * dependency); this module is the LangChain-aware layer above it that owns
11
- * the resolve -> infer-provider -> strip-prefix -> proxy-wire -> construct
12
- * sequence that used to be copy-pasted across every LLM activity.
9
+ * Layering: `llm-proxy.ts` and `llm-backend.ts` stay pure routing utilities
10
+ * (no LangChain dependency); this module is the LangChain-aware layer above
11
+ * them that owns the resolve -> infer-provider -> strip-prefix ->
12
+ * proxy-or-backend-wire -> construct sequence that used to be copy-pasted
13
+ * across every LLM activity.
13
14
  */
14
15
 
15
16
  import { ChatAnthropic } from "@langchain/anthropic";
@@ -23,6 +24,15 @@ import {
23
24
  buildProxyHeaders,
24
25
  type LlmProvider,
25
26
  } from "./llm-proxy.js";
27
+ import {
28
+ resolveAnthropicBackend,
29
+ checkVertexPrerequisites,
30
+ checkBedrockPrerequisites,
31
+ checkFoundryPrerequisites,
32
+ toVertexModelId,
33
+ toBedrockModelId,
34
+ toFoundryDeploymentName,
35
+ } from "./llm-backend.js";
26
36
  import { resolveToApiModelId } from "./model-registry.js";
27
37
 
28
38
  export interface BuildChatModelOptions {
@@ -45,14 +55,37 @@ export interface BuildChatModelOptions {
45
55
  * stays the caller's decision to avoid silently changing behavior.
46
56
  */
47
57
  readonly maxTokens?: number;
58
+ /**
59
+ * Per-request bound. When omitted, defaults to the operator's
60
+ * STIGMER_LLM_REQUEST_TIMEOUT_MS — resolved HERE, not per caller, so no
61
+ * call site can drop the bound by forgetting to plumb it (the sub-agent
62
+ * factory did exactly that; stigmer/stigmer#468). An explicit value wins.
63
+ */
48
64
  readonly timeoutMs?: number;
49
65
  readonly maxRetries?: number;
50
66
  }
51
67
 
68
+ /**
69
+ * The operator's request-timeout bound. Only a positive integer means
70
+ * "bound the request" — unset, non-numeric, zero, and negative all
71
+ * normalize to no bound. Deployment-static env, read here the same way
72
+ * provider API keys are.
73
+ */
74
+ function resolveDefaultTimeoutMs(): number | undefined {
75
+ const parsed = Number.parseInt(process.env.STIGMER_LLM_REQUEST_TIMEOUT_MS ?? "", 10);
76
+ return parsed > 0 ? parsed : undefined;
77
+ }
78
+
52
79
  export interface BuiltChatModel {
53
80
  readonly model: BaseChatModel;
54
81
  readonly provider: LlmProvider;
55
- /** The resolved id actually handed to the provider. */
82
+ /**
83
+ * The canonical provider API id the registry resolved to — what pricing
84
+ * and usage metrics key on. A backend adapter may translate it for the
85
+ * wire (Vertex separates the snapshot date with `@`), but the translated
86
+ * form never leaves the adapter: this field stays canonical (the
87
+ * canonical-id invariant in design decision 001-provider-backends).
88
+ */
56
89
  readonly apiModelId: string;
57
90
  }
58
91
 
@@ -66,6 +99,106 @@ export async function buildChatModel(opts: BuildChatModelOptions): Promise<Built
66
99
  const resolved = await resolveToApiModelId(opts.modelName);
67
100
  const provider = inferProvider(resolved);
68
101
  const apiModelId = stripProviderPrefix(resolved);
102
+ const timeoutMs = opts.timeoutMs ?? resolveDefaultTimeoutMs();
103
+
104
+ // Backend precedence by construction: a proxied call never consults the
105
+ // backend var — the proxy owns provider routing (the factories' preflight
106
+ // warns when both are configured). Selection is deployment-static and read
107
+ // here the same way provider API keys are, not plumbed through Config.
108
+ const anthropicBackend =
109
+ provider === "anthropic" && !opts.proxyEndpoint
110
+ ? resolveAnthropicBackend()
111
+ : "public";
112
+
113
+ // Backend SDKs (and their auth subtrees: google-auth-library, AWS smithy)
114
+ // load lazily so deployments that never configure a backend never
115
+ // evaluate them — cheap cold starts stay cheap, and bundle-slim's CJS
116
+ // output preserves exactly this deferred evaluation (see
117
+ // scripts/bundle-slim.mjs). The factory is invoked lazily by
118
+ // ChatAnthropic once per cached client (batch + streaming), so the class
119
+ // is captured here, in async context.
120
+ //
121
+ // Each factory MUST honor options.maxRetries and options.timeout:
122
+ // LangChain owns retrying and hands the factory maxRetries: 0 — a factory
123
+ // that drops it nests the SDK's default 2 retries inside LangChain's
124
+ // retry loop, multiplying every transient failure — and `timeout` arrives
125
+ // through the same options object (ChatAnthropic spreads clientOptions
126
+ // into it), so a factory that drops it silently unbounds
127
+ // STIGMER_LLM_REQUEST_TIMEOUT_MS on that backend. Pinned by the seam
128
+ // tests. Prerequisites are re-checked here (not only in
129
+ // the factories' preflight) so paths that construct models without a
130
+ // runner factory still fail at dispatch with the catalog message instead
131
+ // of mid-request. Credentials are read natively by each SDK from its
132
+ // standard conventions (GCP: CLOUD_ML_REGION + ADC; AWS: AWS_REGION +
133
+ // the credential chain / AWS_BEARER_TOKEN_BEDROCK; Foundry:
134
+ // ANTHROPIC_FOUNDRY_API_KEY, or the Azure credential chain when no key
135
+ // is set).
136
+ let backendCreateClient:
137
+ | ((options: { maxRetries?: number; timeout?: number }) => unknown)
138
+ | undefined;
139
+ let wireModelId = apiModelId;
140
+ let maxTokens = opts.maxTokens;
141
+ if (anthropicBackend === "vertex") {
142
+ const prereq = checkVertexPrerequisites();
143
+ if (prereq !== null) throw new Error(prereq);
144
+ const { AnthropicVertex } = await import("@anthropic-ai/vertex-sdk");
145
+ backendCreateClient = (options) =>
146
+ new AnthropicVertex({ maxRetries: options.maxRetries, timeout: options.timeout });
147
+ wireModelId = toVertexModelId(apiModelId);
148
+ } else if (anthropicBackend === "bedrock") {
149
+ const prereq = checkBedrockPrerequisites();
150
+ if (prereq !== null) throw new Error(prereq);
151
+ const { AnthropicBedrock } = await import("@anthropic-ai/bedrock-sdk");
152
+ backendCreateClient = (options) =>
153
+ new AnthropicBedrock({ maxRetries: options.maxRetries, timeout: options.timeout });
154
+ wireModelId = toBedrockModelId(apiModelId);
155
+ if (maxTokens === undefined) {
156
+ // LangChain's per-model maxTokens table prefix-matches the model
157
+ // name. Vertex's translated ids still match their canonical prefix;
158
+ // Bedrock's `anthropic.…` ids match nothing and silently fall back
159
+ // to 4096 — a silent output cap. Probe the CANONICAL id with a
160
+ // throwaway construction (pure field assignment, no I/O; the key is
161
+ // never used) so bedrock inherits exactly the default the public API
162
+ // and vertex get for the same model — including models the table
163
+ // doesn't know yet, where all backends agree on the fallback.
164
+ // Pinned by the canonical-parity test in bedrock-adapter.test.ts.
165
+ maxTokens = new ChatAnthropic({ model: apiModelId, apiKey: "max-tokens-probe" }).maxTokens;
166
+ }
167
+ } else if (anthropicBackend === "foundry") {
168
+ const prereq = checkFoundryPrerequisites();
169
+ if (prereq !== null) throw new Error(prereq);
170
+ const { AnthropicFoundry } = await import("@anthropic-ai/foundry-sdk");
171
+ // Auth is an explicit either/or (the Foundry constructor throws when
172
+ // given both a key and a token provider): ANTHROPIC_FOUNDRY_API_KEY if
173
+ // present, otherwise keyless Microsoft Entra ID through the Azure
174
+ // credential chain (env service principal -> workload identity ->
175
+ // managed identity -> az CLI) — the Azure analogue of Vertex ADC and
176
+ // the AWS chain, resolved at request time. The provider is built once
177
+ // here (getBearerTokenProvider caches tokens until near-expiry) and
178
+ // shared by both cached clients; the SDK still consults it per request,
179
+ // so refreshed tokens flow without reconstruction (pinned by
180
+ // foundry-seam.test.ts). Endpoint (resource or base URL) and the key
181
+ // are read natively by the SDK from its own env vars.
182
+ let azureADTokenProvider: (() => Promise<string>) | undefined;
183
+ if (!process.env.ANTHROPIC_FOUNDRY_API_KEY?.trim()) {
184
+ const { DefaultAzureCredential, getBearerTokenProvider } = await import("@azure/identity");
185
+ azureADTokenProvider = getBearerTokenProvider(
186
+ new DefaultAzureCredential(),
187
+ "https://ai.azure.com/.default",
188
+ );
189
+ }
190
+ backendCreateClient = (options) =>
191
+ new AnthropicFoundry({
192
+ maxRetries: options.maxRetries,
193
+ timeout: options.timeout,
194
+ ...(azureADTokenProvider ? { azureADTokenProvider } : {}),
195
+ });
196
+ // Unlike the vertex/bedrock ids, the deployment name needs no maxTokens
197
+ // handling: stripping the snapshot date preserves LangChain's per-model
198
+ // default (probed 2026-08-11 across the full catalog; pinned by the
199
+ // parity test in foundry-adapter.test.ts).
200
+ wireModelId = toFoundryDeploymentName(apiModelId);
201
+ }
69
202
 
70
203
  const baseUrl = opts.proxyEndpoint
71
204
  ? resolveProxyBaseUrl(opts.proxyEndpoint, provider)
@@ -85,10 +218,30 @@ export async function buildChatModel(opts: BuildChatModelOptions): Promise<Built
85
218
  const common = {
86
219
  temperature: opts.temperature ?? 0,
87
220
  apiKey,
88
- ...(opts.maxTokens ? { maxTokens: opts.maxTokens } : {}),
89
- ...(opts.timeoutMs ? { maxRetries: opts.maxRetries ?? 0, timeout: opts.timeoutMs } : {}),
221
+ ...(maxTokens ? { maxTokens } : {}),
222
+ ...(timeoutMs ? { maxRetries: opts.maxRetries ?? 0 } : {}),
90
223
  };
91
224
 
225
+ // The request timeout lives in a different slot per wrapper: ChatOpenAI
226
+ // reads `timeout` as a constructor field, but ChatAnthropic ignores it
227
+ // there — its slot is `clientOptions.timeout`, which the wrapper spreads
228
+ // into the createClient factory options and (on the default factory) the
229
+ // SDK constructor. This split is what makes STIGMER_LLM_REQUEST_TIMEOUT_MS
230
+ // bound every path; putting `timeout` in the shared constructor spread is
231
+ // the exact regression that made it inert for Anthropic (T02 finding 2).
232
+ // The `maxRetries` half above stays constructor-level for both wrappers:
233
+ // that is LangChain's own retry knob, distinct from the SDK-level
234
+ // maxRetries the factories receive.
235
+ const anthropicClientOptions = {
236
+ ...(timeoutMs ? { timeout: timeoutMs } : {}),
237
+ ...(baseUrl ? { baseURL: baseUrl } : {}),
238
+ ...(headers ? { defaultHeaders: headers } : {}),
239
+ };
240
+ const anthropicClientOptionsField =
241
+ Object.keys(anthropicClientOptions).length > 0
242
+ ? { clientOptions: anthropicClientOptions }
243
+ : {};
244
+
92
245
  // The two SDKs name the transport-override block differently (OpenAI:
93
246
  // `configuration`, Anthropic: `clientOptions`) — encapsulating that here is
94
247
  // the whole point, since the shape mismatch is where bugs used to hide.
@@ -96,6 +249,7 @@ export async function buildChatModel(opts: BuildChatModelOptions): Promise<Built
96
249
  ? new ChatOpenAI({
97
250
  model: apiModelId,
98
251
  ...common,
252
+ ...(timeoutMs ? { timeout: timeoutMs } : {}),
99
253
  ...(baseUrl || headers
100
254
  ? {
101
255
  configuration: {
@@ -105,18 +259,24 @@ export async function buildChatModel(opts: BuildChatModelOptions): Promise<Built
105
259
  }
106
260
  : {}),
107
261
  })
108
- : new ChatAnthropic({
109
- model: apiModelId,
110
- ...common,
111
- ...(baseUrl || headers
112
- ? {
113
- clientOptions: {
114
- ...(baseUrl ? { baseURL: baseUrl } : {}),
115
- ...(headers ? { defaultHeaders: headers } : {}),
116
- },
117
- }
118
- : {}),
119
- });
262
+ : backendCreateClient
263
+ ? // Backend adapter: the translated id is wire detail only — each
264
+ // client moves it from the JSON body into its URL path. The waiver
265
+ // in ChatAnthropic (an API key is not required when createClient is
266
+ // provided) is what lets this construct with no ANTHROPIC_API_KEY.
267
+ // (Backend mode never has a proxy — see the precedence rule above —
268
+ // so the clientOptions here carry at most the timeout.)
269
+ new ChatAnthropic({
270
+ model: wireModelId,
271
+ ...common,
272
+ ...anthropicClientOptionsField,
273
+ createClient: backendCreateClient,
274
+ })
275
+ : new ChatAnthropic({
276
+ model: apiModelId,
277
+ ...common,
278
+ ...anthropicClientOptionsField,
279
+ });
120
280
 
121
281
  return { model, provider, apiModelId };
122
282
  }
@@ -26,6 +26,14 @@
26
26
  */
27
27
 
28
28
  import type { LlmProvider } from "./llm-proxy.js";
29
+ import {
30
+ parseAnthropicBackend,
31
+ BACKEND_DOC_URL,
32
+ BEDROCK_INFERENCE_PREFIX_ENV,
33
+ FOUNDRY_DEPLOYMENT_MAP_ENV,
34
+ FOUNDRY_RESOURCE_ENV,
35
+ type AnthropicBackend,
36
+ } from "./llm-backend.js";
29
37
 
30
38
  /**
31
39
  * Machine-readable code the cloud proxy embeds in rewritten platform-fault
@@ -96,6 +104,7 @@ export function classifyModelCallError(
96
104
  ): ClassifiedModelError | undefined {
97
105
  const root = unwrapModelError(err);
98
106
  const message = root instanceof Error ? root.message : String(root);
107
+ const backend = resolveDirectBackend(ctx);
99
108
 
100
109
  // 1. Platform sentinel — before status mapping (see module doc).
101
110
  if (message.includes(PLATFORM_CAPACITY_SENTINEL)) {
@@ -106,6 +115,69 @@ export function classifyModelCallError(
106
115
  };
107
116
  }
108
117
 
118
+ // 1b. Backend credential acquisition — also before status mapping: these
119
+ // failures come from the auth library (thrown while adapting the
120
+ // request, usually with no HTTP status) and won't self-heal on retry.
121
+ // Raw, they read like library internals ("Could not load the default
122
+ // credentials"); the operator needs to hear "fix your cloud
123
+ // credentials".
124
+ if (backend === "vertex" && isGoogleCredentialMessage(message)) {
125
+ return {
126
+ code: "LLM_BACKEND_CREDENTIALS",
127
+ retryable: false,
128
+ message:
129
+ `The vertex backend could not acquire Google credentials for ${modelLabel(ctx)}. ` +
130
+ `Set GOOGLE_APPLICATION_CREDENTIALS to a service-account key, or run on a GCP ` +
131
+ `identity (workload identity / metadata server). ANTHROPIC_VERTEX_PROJECT_ID is ` +
132
+ `only needed when the credentials don't carry a project. See ${BACKEND_DOC_URL}. ` +
133
+ `Underlying error: ${message}`,
134
+ };
135
+ }
136
+ if (backend === "bedrock" && isAwsCredentialMessage(message)) {
137
+ return {
138
+ code: "LLM_BACKEND_CREDENTIALS",
139
+ retryable: false,
140
+ message:
141
+ `The bedrock backend could not acquire AWS credentials for ${modelLabel(ctx)}. ` +
142
+ `Provide credentials through the standard AWS chain (environment keys, an IAM ` +
143
+ `role / IRSA, config files) or set AWS_BEARER_TOKEN_BEDROCK. See ${BACKEND_DOC_URL}. ` +
144
+ `Underlying error: ${message}`,
145
+ };
146
+ }
147
+ if (backend === "foundry" && isFoundryCredentialMessage(message)) {
148
+ // Only the keyless Entra path can land here: API-key failures arrive
149
+ // as HTTP 401s (status arm below), while a failing token provider
150
+ // throws statusless from inside the Foundry SDK's authHeaders.
151
+ return {
152
+ code: "LLM_BACKEND_CREDENTIALS",
153
+ retryable: false,
154
+ message:
155
+ `The foundry backend could not acquire a Microsoft Entra ID token for ${modelLabel(ctx)}. ` +
156
+ `Give the runner an Azure identity the credential chain can resolve (workload ` +
157
+ `identity / managed identity, service-principal env vars, or az login), or set ` +
158
+ `ANTHROPIC_FOUNDRY_API_KEY to use API-key auth instead. See ${BACKEND_DOC_URL}. ` +
159
+ `Underlying error: ${message}`,
160
+ };
161
+ }
162
+
163
+ // 1c. Bedrock's inference-profile rejection — a config condition, not a
164
+ // bad request: newer Claude models cannot be invoked by bare model id
165
+ // (AWS lists their in-region endpoint as N/A). The operator remedy is
166
+ // one env var, so say exactly that instead of relaying AWS prose that
167
+ // talks about ARNs and provisioned throughput.
168
+ if (backend === "bedrock" && isBedrockInferenceProfileMessage(message)) {
169
+ return {
170
+ code: "LLM_BACKEND_MODEL_ROUTING",
171
+ retryable: false,
172
+ message:
173
+ `Bedrock requires an inference profile for ${modelLabel(ctx)} — the bare model ` +
174
+ `id cannot be invoked on-demand. Set ${BEDROCK_INFERENCE_PREFIX_ENV} to your ` +
175
+ `deployment's geography (e.g. "us", "eu", or "global"), or map this model ` +
176
+ `explicitly in STIGMER_BEDROCK_MODEL_MAP. See ${BACKEND_DOC_URL}. ` +
177
+ `Underlying error: ${message}`,
178
+ };
179
+ }
180
+
109
181
  // 2. Provider billing prose. In direct mode this is the user's own
110
182
  // account and the fix is theirs. In proxy mode these patterns should
111
183
  // never appear (the proxy rewrites them), but a version-skewed proxy
@@ -135,7 +207,7 @@ export function classifyModelCallError(
135
207
  ? (root as { status: number }).status
136
208
  : undefined;
137
209
  if (status !== undefined) {
138
- return classifyByStatus(status, message, ctx);
210
+ return classifyByStatus(status, message, ctx, backend);
139
211
  }
140
212
 
141
213
  // 4. Connection/timeout heuristics on the root error's class name. Strict
@@ -175,6 +247,7 @@ function classifyByStatus(
175
247
  status: number,
176
248
  rawMessage: string,
177
249
  ctx: ModelErrorContext,
250
+ backend: AnthropicBackend,
178
251
  ): ClassifiedModelError {
179
252
  const context = modelLabel(ctx);
180
253
 
@@ -186,7 +259,23 @@ function classifyByStatus(
186
259
  message: ctx.proxyMode
187
260
  ? `The Stigmer platform rejected this model call (authentication, HTTP 401) for ${context}. ` +
188
261
  `Your session token may have expired — retry the execution, and contact support if it persists.`
189
- : `Authentication failed for ${context}. Check that your API key is valid and not expired.`,
262
+ // On a cloud backend the credential is a cloud identity "check
263
+ // your API key" would send the operator hunting for a key that
264
+ // isn't in play.
265
+ : backend === "vertex"
266
+ ? `Google rejected this Vertex AI call (authentication, HTTP 401) for ${context}. ` +
267
+ `The credentials are expired or not valid for this project — check ` +
268
+ `GOOGLE_APPLICATION_CREDENTIALS or the runner's GCP identity. See ${BACKEND_DOC_URL}.`
269
+ : backend === "bedrock"
270
+ ? `AWS rejected this Bedrock call (authentication, HTTP 401) for ${context}. ` +
271
+ `The credentials are expired or invalid — check the runner's AWS identity ` +
272
+ `(environment keys, IAM role / IRSA) or AWS_BEARER_TOKEN_BEDROCK. See ${BACKEND_DOC_URL}.`
273
+ : backend === "foundry"
274
+ ? `Azure rejected this Microsoft Foundry call (authentication, HTTP 401) for ${context}. ` +
275
+ `The credential is expired or not valid for this Foundry resource — check ` +
276
+ `ANTHROPIC_FOUNDRY_API_KEY (find it on the deployment's Details tab) or the ` +
277
+ `runner's Azure identity. See ${BACKEND_DOC_URL}.`
278
+ : `Authentication failed for ${context}. Check that your API key is valid and not expired.`,
190
279
  };
191
280
  case 403:
192
281
  return {
@@ -195,13 +284,50 @@ function classifyByStatus(
195
284
  message: ctx.proxyMode
196
285
  ? `The Stigmer platform denied this model call (authorization, HTTP 403) for ${context}. ` +
197
286
  `Verify this execution is permitted to use the model, and contact support if it persists.`
198
- : `Access denied for ${context}. Verify that your API key has permission to use this model.`,
287
+ : backend === "vertex"
288
+ ? `Vertex AI denied this call (HTTP 403) for ${context}. Grant the runner's ` +
289
+ `service account the "Vertex AI User" role (aiplatform.endpoints.predict) ` +
290
+ `in the target project. See ${BACKEND_DOC_URL}.`
291
+ : backend === "bedrock"
292
+ // The most common Bedrock setup mistake: Anthropic models must
293
+ // be enabled per account ("Model access" in the Bedrock console,
294
+ // including the use-case submission), on top of IAM.
295
+ ? `Bedrock denied this call (HTTP 403) for ${context}. Enable this Claude model ` +
296
+ `under "Model access" in the Bedrock console (Anthropic models require a ` +
297
+ `use-case submission), and grant the runner's identity bedrock:InvokeModel ` +
298
+ `for the model and its inference profile. See ${BACKEND_DOC_URL}.`
299
+ : backend === "foundry"
300
+ ? `Microsoft Foundry denied this call (HTTP 403) for ${context}. Grant the ` +
301
+ `runner's Azure identity the "Foundry User" (or "Cognitive Services User") ` +
302
+ `RBAC role on the Foundry resource. See ${BACKEND_DOC_URL}.`
303
+ : `Access denied for ${context}. Verify that your API key has permission to use this model.`,
199
304
  };
200
305
  case 404:
201
306
  return {
202
307
  code: "LLM_MODEL_NOT_FOUND",
203
308
  retryable: false,
204
- message: `Model not found: ${context}. Verify the model name is correct and available in your account.`,
309
+ // The most common Vertex setup mistake: Claude models must be enabled
310
+ // per project in Model Garden, and availability varies by region.
311
+ message: backend === "vertex"
312
+ ? `Model not found on Vertex AI: ${context}. Enable this Claude model for your ` +
313
+ `project in the Vertex AI Model Garden, and confirm it is available in ` +
314
+ `${describeVertexRegion()} — availability varies by region. See ${BACKEND_DOC_URL}.`
315
+ : backend === "bedrock"
316
+ ? `Model not found on Bedrock: ${context}. Confirm the model is available in ` +
317
+ `${describeBedrockRegion()} — availability varies by region — and that the ` +
318
+ `resolved Bedrock id is right for your deployment (STIGMER_BEDROCK_MODEL_MAP ` +
319
+ `overrides, ${BEDROCK_INFERENCE_PREFIX_ENV} for inference profiles). See ${BACKEND_DOC_URL}.`
320
+ : backend === "foundry"
321
+ // The most common Foundry setup mistake: Foundry routes by
322
+ // DEPLOYMENT NAME, and deployments are created one by one in
323
+ // the portal — a model with no deployment (or a custom name)
324
+ // 404s even though the model itself exists on Foundry.
325
+ ? `Model deployment not found on Microsoft Foundry: ${context}. Foundry routes ` +
326
+ `by deployment name — confirm a deployment for this model exists in ` +
327
+ `${describeFoundryResource()} (default deployment names are the dateless ` +
328
+ `model ids), or map it to your custom deployment name in ` +
329
+ `${FOUNDRY_DEPLOYMENT_MAP_ENV}. See ${BACKEND_DOC_URL}.`
330
+ : `Model not found: ${context}. Verify the model name is correct and available in your account.`,
205
331
  };
206
332
  case 400:
207
333
  return {
@@ -260,6 +386,98 @@ export function describeExecutionError(
260
386
  };
261
387
  }
262
388
 
389
+ /**
390
+ * The backend serving this direct-mode Anthropic call — the only condition
391
+ * under which a backend's specific arms may speak. Proxied calls and other
392
+ * providers read as "public" (no backend wording applies). The backend is
393
+ * resolved from env here (deployment-static, like the API keys
394
+ * model-client reads) rather than threaded through every activity's
395
+ * ModelErrorContext; an invalid var value also reads as public, since
396
+ * classification must never throw and invalid values are already fatal at
397
+ * the factories' preflight and at model construction.
398
+ */
399
+ function resolveDirectBackend(ctx: ModelErrorContext): AnthropicBackend {
400
+ if (ctx.proxyMode || ctx.provider !== "anthropic") return "public";
401
+ const parsed = parseAnthropicBackend();
402
+ return parsed.ok ? parsed.backend : "public";
403
+ }
404
+
405
+ /**
406
+ * Google credential-acquisition prose, matched against the raw message.
407
+ * Narrow by design (mirrors isProviderBillingMessage): pinned to
408
+ * google-auth-library's ADC failure, its project-detection failure, the
409
+ * Vertex SDK's own projectId error, and OAuth's invalid_grant (expired or
410
+ * revoked service-account key). A miss falls through to status
411
+ * classification — never worse than the raw error.
412
+ */
413
+ function isGoogleCredentialMessage(message: string): boolean {
414
+ const lower = message.toLowerCase();
415
+ return (
416
+ lower.includes("could not load the default credentials")
417
+ || lower.includes("unable to detect a project id")
418
+ || lower.includes("no projectid was given")
419
+ || lower.includes("invalid_grant")
420
+ );
421
+ }
422
+
423
+ /**
424
+ * AWS credential-acquisition prose, matched against the raw message.
425
+ * Narrow by design (mirrors isGoogleCredentialMessage): pinned to the AWS
426
+ * credential provider chain's terminal failure
427
+ * (@aws-sdk/credential-providers' CredentialsProviderError wordings) and
428
+ * the SigV4 signer's invalid-shape error. A miss falls through to status
429
+ * classification — never worse than the raw error.
430
+ */
431
+ function isAwsCredentialMessage(message: string): boolean {
432
+ const lower = message.toLowerCase();
433
+ return (
434
+ lower.includes("could not load credentials from any providers")
435
+ || lower.includes("credential is missing")
436
+ || lower.includes("resolved credential object is not valid")
437
+ );
438
+ }
439
+
440
+ /**
441
+ * Bedrock's bare-model-id rejection prose (HTTP 400 ValidationException):
442
+ * "Invocation of model ID … with on-demand throughput isn't supported.
443
+ * Retry your request with the ID or ARN of an inference profile …".
444
+ * Matched narrowly on the phrase that only this condition carries.
445
+ */
446
+ function isBedrockInferenceProfileMessage(message: string): boolean {
447
+ return message.toLowerCase().includes("on-demand throughput isn't supported");
448
+ }
449
+
450
+ /**
451
+ * Entra ID token-acquisition prose, matched against the raw message.
452
+ * Narrow by design (mirrors the Google/AWS matchers), and narrower than it
453
+ * looks: the Foundry SDK wraps EVERY token-provider failure — whatever
454
+ * @azure/identity's credential chain threw — in this one prefix before
455
+ * rethrowing (pinned by foundry-seam.test.ts), so a single phrase covers
456
+ * the whole family. A miss falls through to status classification — never
457
+ * worse than the raw error.
458
+ */
459
+ function isFoundryCredentialMessage(message: string): boolean {
460
+ return message.toLowerCase().includes("failed to get token from azureadtokenprovider");
461
+ }
462
+
463
+ /** "region {value}" when CLOUD_ML_REGION is set, else a pointer to the var. */
464
+ function describeVertexRegion(): string {
465
+ const region = process.env.CLOUD_ML_REGION?.trim();
466
+ return region ? `region "${region}"` : "your CLOUD_ML_REGION";
467
+ }
468
+
469
+ /** "region {value}" when AWS_REGION is set, else a pointer to the var. */
470
+ function describeBedrockRegion(): string {
471
+ const region = process.env.AWS_REGION?.trim();
472
+ return region ? `region "${region}"` : "your AWS_REGION";
473
+ }
474
+
475
+ /** `resource "{value}"` when the resource var is set, else a generic label. */
476
+ function describeFoundryResource(): string {
477
+ const resource = process.env[FOUNDRY_RESOURCE_ENV]?.trim();
478
+ return resource ? `resource "${resource}"` : "your Foundry resource";
479
+ }
480
+
263
481
  /**
264
482
  * Provider billing-exhaustion prose, matched against the raw message. Narrow
265
483
  * by design: these phrases are pinned to real provider wordings (Anthropic's
@@ -30,9 +30,18 @@ interface RegistryEntry {
30
30
  };
31
31
  }
32
32
 
33
- import { resolveModelRegistryUrl, buildRegistryHeaders } from "./registry-endpoint.js";
33
+ import {
34
+ resolveModelRegistryUrl,
35
+ buildRegistryHeaders,
36
+ REGISTRY_RETRY_POLICY,
37
+ } from "./registry-endpoint.js";
38
+ import { fetchWithRetry } from "./http-retry.js";
34
39
 
35
40
  const CACHE_TTL_MS = 3_600_000;
41
+ // Failed fetches are cached much shorter than successes (the model-registry.ts
42
+ // policy): a transient failure must not pin DEFAULT_PRICING — wrong rates for
43
+ // cost tracking — for a full hour.
44
+ const FAILURE_CACHE_TTL_MS = 60_000;
36
45
 
37
46
  export const DEFAULT_PRICING: ModelPricing = {
38
47
  model: "unknown",
@@ -66,7 +75,11 @@ function parsePricingTable(json: unknown): ModelPricing[] {
66
75
  }
67
76
 
68
77
  async function fetchFromApi(): Promise<readonly ModelPricing[]> {
69
- const res = await fetch(resolveModelRegistryUrl(), { headers: buildRegistryHeaders() });
78
+ const res = await fetchWithRetry(
79
+ resolveModelRegistryUrl(),
80
+ { headers: buildRegistryHeaders() },
81
+ REGISTRY_RETRY_POLICY,
82
+ );
70
83
  if (!res.ok) throw new Error(`Model registry fetch failed: ${res.status}`);
71
84
  const data: unknown = await res.json();
72
85
  const table = parsePricingTable(data);
@@ -93,7 +106,7 @@ export async function getPricingTable(): Promise<readonly ModelPricing[]> {
93
106
  `Failed to fetch model registry, using default pricing: ${err}`,
94
107
  );
95
108
  const fallback = [DEFAULT_PRICING];
96
- cache = { data: fallback, expiresAt: Date.now() + CACHE_TTL_MS };
109
+ cache = { data: fallback, expiresAt: Date.now() + FAILURE_CACHE_TTL_MS };
97
110
  return fallback;
98
111
  })
99
112
  .finally(() => {
@@ -102,3 +115,9 @@ export async function getPricingTable(): Promise<readonly ModelPricing[]> {
102
115
 
103
116
  return inflightFetch;
104
117
  }
118
+
119
+ /** Exposed for testing — resets the in-memory cache. */
120
+ export function _resetPricingCache(): void {
121
+ cache = null;
122
+ inflightFetch = null;
123
+ }
@@ -10,7 +10,12 @@
10
10
  * (getModelVisionCapability).
11
11
  */
12
12
 
13
- import { resolveModelRegistryUrl, buildRegistryHeaders } from "./registry-endpoint.js";
13
+ import {
14
+ resolveModelRegistryUrl,
15
+ buildRegistryHeaders,
16
+ REGISTRY_RETRY_POLICY,
17
+ } from "./registry-endpoint.js";
18
+ import { fetchWithRetry } from "./http-retry.js";
14
19
 
15
20
  const CACHE_TTL_MS = 3_600_000;
16
21
  // Failed fetches are cached much shorter than successes: a transient failure
@@ -68,7 +73,7 @@ function parseVisionCapability(capabilities: unknown): boolean | undefined {
68
73
 
69
74
  async function fetchRegistry(): Promise<readonly RegistryModel[]> {
70
75
  const url = resolveModelRegistryUrl();
71
- const res = await fetch(url, { headers: buildRegistryHeaders() });
76
+ const res = await fetchWithRetry(url, { headers: buildRegistryHeaders() }, REGISTRY_RETRY_POLICY);
72
77
  if (!res.ok) throw new Error(`Model registry fetch failed: ${res.status}`);
73
78
  const data: unknown = await res.json();
74
79
  return parseRegistry(data);
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The Plan-mode filesystem permission rules — the enforcement twin of
3
+ * `plan-mode-prompt.ts` (which carries the instruction half of the contract).
4
+ *
5
+ * Plan mode is read-only BY CONSTRUCTION on the native harness: these rules
6
+ * deny every filesystem write operation at the tool level so
7
+ * write_file/edit_file cannot mutate the workspace regardless of what the
8
+ * model was told. Rules are first-match-wins with a permissive default, so a
9
+ * single deny-all-writes rule is sufficient. (The Cursor harness has no
10
+ * tool-level lever and enforces plan mode via its prompt prefix instead.)
11
+ *
12
+ * Applied in execute-deep-agent/setup.ts to the parent graph AND threaded
13
+ * into every compiled sub-agent graph: deepagents' parent-permission
14
+ * inheritance covers only spec-style sub-agents, and ours are pre-built
15
+ * CompiledSubAgents, so each sub-agent graph must carry the rules itself
16
+ * (issue #255). Kept as its own side-effect-free module so tests can pin the
17
+ * production rules without dragging in setup.ts's import graph.
18
+ *
19
+ * Rules travel with a companion: every graph that carries them also installs
20
+ * the path-normalization middleware (middleware/path-normalization.ts,
21
+ * issue #429), because deepagents' rule validation refuses workspace-relative
22
+ * paths outright — without the shim, prompt-compliant relative READS die in
23
+ * validation instead of just working. Both are derived from the same
24
+ * expression at each composition site so they cannot drift apart.
25
+ *
26
+ * Invariant: never combine these rules with a shell-capable (sandbox)
27
+ * backend — deepagents rejects that pairing at graph construction (see the
28
+ * cas-capture-backend.ts header). Plan mode guarantees it by construction:
29
+ * it is the mode that clears `shellEnv`, and `shellEnv` is the single switch
30
+ * for shell capability on both the parent and sub-agent backends.
31
+ */
32
+
33
+ import type { FilesystemPermission } from "deepagents";
34
+
35
+ export const PLAN_MODE_PERMISSIONS: FilesystemPermission[] = [
36
+ { operations: ["write"], paths: ["/**"], mode: "deny" },
37
+ ];