@desplega.ai/agent-swarm 2.4.3 → 2.4.5

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 (270) hide show
  1. package/dist/{acp-adapter-1885dpr7.js → acp-adapter-2wdkktv6.js} +1 -2
  2. package/dist/{actions-pn76n32q.js → actions-c40ebcm4.js} +18 -18
  3. package/dist/{amp-adapter-af84rx1d.js → amp-adapter-wb67m95z.js} +2 -2
  4. package/dist/{app-6sv8mhzm.js → app-353vre8g.js} +3 -3
  5. package/dist/{assistant-sc2tf825.js → assistant-sj1njh23.js} +21 -21
  6. package/dist/{boot-reembed-y3m0ydzj.js → boot-reembed-b9c3az3c.js} +1 -1
  7. package/dist/{boot-reembed-gvxjs2py.js → boot-reembed-qdkvxeem.js} +1 -1
  8. package/dist/{boot-scrub-sweep-1j9mk05f.js → boot-scrub-sweep-m3wh8eks.js} +2 -2
  9. package/dist/{boot-seeds-37sp626b.js → boot-seeds-j2273s54.js} +4 -4
  10. package/dist/{claude-adapter-rp0jtnwr.js → claude-adapter-m7q0a085.js} +2 -2
  11. package/dist/{claude-managed-adapter-h2y889ge.js → claude-managed-adapter-5ptb6tzs.js} +2 -2
  12. package/dist/{claude-managed-setup-rdt248k7.js → claude-managed-setup-smrqk5j5.js} +3 -3
  13. package/dist/{claude-sdk-session-53rcw5we.js → claude-sdk-session-38xnz21e.js} +8284 -8267
  14. package/dist/{cli-6eyncg0y.js → cli-018r4jq6.js} +34 -6
  15. package/dist/{cli-sds5zzs8.js → cli-1f3qzw6y.js} +78 -12
  16. package/dist/{cli-f0p0f6ja.js → cli-1sp861s4.js} +7 -7
  17. package/dist/{cli-dm8p2dpf.js → cli-1syn2xy1.js} +10 -10
  18. package/dist/{cli-tkf4x6ha.js → cli-37j04k05.js} +11 -11
  19. package/dist/{cli-v87dr6e3.js → cli-3a45zcgm.js} +1 -1
  20. package/dist/{cli-1n9gdg56.js → cli-43x64hv3.js} +4 -4
  21. package/dist/{cli-p0qerd2r.js → cli-4zfqgjc5.js} +2 -2
  22. package/dist/{cli-aa3tb29r.js → cli-7c9qs530.js} +1 -1
  23. package/dist/{cli-9qtjfd60.js → cli-848hxdwj.js} +1 -1
  24. package/dist/{cli-1xsraagq.js → cli-94gwyg7m.js} +8 -8
  25. package/dist/{cli-0dxxwfkt.js → cli-9hstj09h.js} +17 -14
  26. package/dist/{cli-1012xb7j.js → cli-9yqgm9nd.js} +22 -22
  27. package/dist/{cli-pygdsgh8.js → cli-a92r1127.js} +1 -1
  28. package/dist/{cli-8vverzf7.js → cli-ba8ne1es.js} +5 -0
  29. package/dist/{cli-bxrqvpg5.js → cli-be2161h1.js} +7 -7
  30. package/dist/{cli-npfe9t1s.js → cli-bnes4z6b.js} +2 -2
  31. package/dist/{cli-aypbg9r2.js → cli-cxxe61f6.js} +1 -1
  32. package/dist/{cli-vq9gqarq.js → cli-dk44dxec.js} +1 -1
  33. package/dist/{cli-836ehxmt.js → cli-eww120g8.js} +3 -2
  34. package/dist/{cli-a0kcty51.js → cli-f01h25yt.js} +1 -1
  35. package/dist/{cli-mzkwkbdy.js → cli-f0g3pgvf.js} +1 -1
  36. package/dist/{cli-1ma335fz.js → cli-ftc1g5tj.js} +2 -2
  37. package/dist/{cli-4yef8ghv.js → cli-fv5hepyy.js} +3 -3
  38. package/dist/{cli-58vwn100.js → cli-g35m2xwm.js} +4 -4
  39. package/dist/{cli-qje9k2nh.js → cli-g9ypd5en.js} +12 -2
  40. package/dist/{cli-gx0j2khs.js → cli-j99azg3g.js} +3 -3
  41. package/dist/{cli-er83cjbn.js → cli-k8g16gz3.js} +416 -116
  42. package/dist/{cli-qp2m4btv.js → cli-kpw04va0.js} +2 -2
  43. package/dist/{cli-8y99kfey.js → cli-ms85309d.js} +33 -15
  44. package/dist/{cli-kgnkyabk.js → cli-n72rg53p.js} +1555 -645
  45. package/dist/{cli-e4d78xsv.js → cli-p14m2cnp.js} +1 -1
  46. package/dist/{cli-1eszr343.js → cli-qfxc84gh.js} +1 -1
  47. package/dist/{cli-wk28hzqn.js → cli-qwhy2qma.js} +1 -1
  48. package/dist/{cli-73ezfhha.js → cli-st8aw1m6.js} +228 -11
  49. package/dist/{cli-zvbb4n0e.js → cli-tw4p90jb.js} +92 -192
  50. package/dist/{cli-038fga5k.js → cli-v0yrfmdh.js} +148 -183
  51. package/dist/{cli-6aen3ezb.js → cli-v8fjakwf.js} +1 -1
  52. package/dist/{cli-99jez72r.js → cli-w9etnnxk.js} +6 -1
  53. package/dist/{cli-vcgscaan.js → cli-wt0jf9hm.js} +217 -80
  54. package/dist/{cli-s9rr4gr2.js → cli-xpa64rek.js} +1065 -305
  55. package/dist/{cli-h0mgxann.js → cli-z7d29rw9.js} +2 -2
  56. package/dist/cli.js +45 -20
  57. package/dist/{codex-adapter-2zrmab9b.js → codex-adapter-tn6k5mkg.js} +1 -1
  58. package/dist/{codex-session-runner-kbkz62vc.js → codex-session-runner-7vjt7jbw.js} +1 -1
  59. package/dist/{commands-hd574th6.js → commands-t7rz9146.js} +2 -2
  60. package/dist/{controller-pz5mcfg4.js → controller-m2jasma5.js} +2 -2
  61. package/dist/{controllers-connect-5rgq2hfk.js → controllers-connect-9ftpex99.js} +19 -19
  62. package/dist/{cursor-adapter-mgaprq0j.js → cursor-adapter-mjv79e4z.js} +1 -1
  63. package/dist/{db-6nf1207q.js → db-6sfrn9g3.js} +1 -1
  64. package/dist/{db-h06dxp05.js → db-rn673r06.js} +3 -3
  65. package/dist/{dispatcher-zthpy4m1.js → dispatcher-c155xz0p.js} +17 -17
  66. package/dist/{dsh-adapter-64dhw0j7.js → dsh-adapter-3mmszc1x.js} +1 -1
  67. package/dist/{external-channel-x01t7ke6.js → external-channel-35k6tkv8.js} +2 -2
  68. package/dist/{grok-adapter-h21jv594.js → grok-adapter-tfrq7tg1.js} +2 -3
  69. package/dist/handlers-7gw64dxw.js +39 -0
  70. package/dist/{heartbeat-zzfksckz.js → heartbeat-dyg4npkp.js} +20 -20
  71. package/dist/{hook-z4zhzj1f.js → hook-drspbwpe.js} +1 -1
  72. package/dist/{hook-3xfqhh2s.js → hook-x26b31bg.js} +1 -1
  73. package/dist/{http-q813tmya.js → http-pk972xyw.js} +571 -294
  74. package/dist/{index-va3y63es.js → index-6srwvd5b.js} +1 -2
  75. package/dist/{index-x8xbyn1t.js → index-arytgj5q.js} +1 -1
  76. package/dist/{index-wbbs14c5.js → index-bgdyvcnv.js} +17 -17
  77. package/dist/{index-s98037v0.js → index-cbxkmfc5.js} +1 -2
  78. package/dist/{index-c4ykd0f1.js → index-dr94zbnh.js} +13348 -11611
  79. package/dist/{index-m0a4ydqj.js → index-gabbcdn2.js} +17 -17
  80. package/dist/{index-220tk9bw.js → index-jwxkkex2.js} +139 -76
  81. package/dist/index-tfb26zhy.js +41 -0
  82. package/dist/{keepalive-y5t1my7m.js → keepalive-7yrng7px.js} +3 -3
  83. package/dist/{lead-2xwtq66b.js → lead-7e02z839.js} +29 -30
  84. package/dist/{maintenance-ybfsaedv.js → maintenance-e6cemdqt.js} +1 -1
  85. package/dist/{node-3v3f08f7.js → node-de6akczr.js} +10 -9
  86. package/dist/{oauth-refresh-sweep-909jmn12.js → oauth-refresh-sweep-yw3xdbcj.js} +2 -2
  87. package/dist/{onboard-xn6fkkys.js → onboard-0a9y5yrc.js} +2 -2
  88. package/dist/{otel-impl-e6mn0z13.js → otel-impl-trjpkfb7.js} +1 -1
  89. package/dist/{pricing-refresh-ygd362ep.js → pricing-refresh-d2b5my0y.js} +3 -3
  90. package/dist/{rbac-roles-mb470p4e.js → rbac-roles-hey3yv9d.js} +2 -2
  91. package/dist/{rbac-roles-c9axvfnz.js → rbac-roles-nxh5ahwk.js} +2 -2
  92. package/dist/{registry-rxw10xzg.js → registry-ajhexe44.js} +1 -1
  93. package/dist/{render-v2-g0306nh1.js → render-v2-rr3p2atg.js} +3 -3
  94. package/dist/{resume-v315hnq6.js → resume-2efkf1s1.js} +5 -5
  95. package/dist/{session-access-2zg285a1.js → session-access-s2mttxpw.js} +6 -4
  96. package/dist/{setup-a4mtf02k.js → setup-vypma5yt.js} +1 -1
  97. package/dist/{work-objects-9fr0fgtn.js → work-objects-rvhckdkp.js} +20 -20
  98. package/dist/{worker-qae22jeb.js → worker-5snytpbd.js} +29 -30
  99. package/dist/worker-token-xf11yyds.js +136 -0
  100. package/openapi.json +303 -13
  101. package/package.json +8 -8
  102. package/src/apps/action-runner.ts +4 -0
  103. package/src/apps/catalog.generated.json +209 -4
  104. package/src/apps/page-validator.ts +21 -3
  105. package/src/be/chunking.ts +42 -9
  106. package/src/be/db/tasks/draft-text.ts +15 -0
  107. package/src/be/db/tasks/read.ts +14 -1
  108. package/src/be/db.ts +5 -1
  109. package/src/be/memory/index-content.ts +36 -17
  110. package/src/be/memory/providers/openai-embedding.ts +115 -61
  111. package/src/be/memory/providers/sqlite-store.ts +69 -23
  112. package/src/be/memory/reranker.ts +31 -1
  113. package/src/be/memory/search-candidates.ts +47 -0
  114. package/src/be/memory/types.ts +14 -0
  115. package/src/be/migrations/232_agent_session_tokens.sql +41 -0
  116. package/src/be/scripts/typecheck.ts +2 -0
  117. package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +116 -55
  118. package/src/be/seed-scripts/index.ts +2 -2
  119. package/src/be/swarm-config-guard.ts +40 -1
  120. package/src/be/task-attachment-store.ts +30 -0
  121. package/src/be/users.ts +169 -12
  122. package/src/cli.tsx +24 -0
  123. package/src/commands/worker-token.ts +164 -0
  124. package/src/extensions/catalog.generated.json +4 -4
  125. package/src/extensions/contract-types.generated.ts +1 -1
  126. package/src/extensions/contract.ts +7 -0
  127. package/src/fs/provider.ts +13 -1
  128. package/src/heartbeat/heartbeat.ts +31 -11
  129. package/src/http/apps.ts +43 -5
  130. package/src/http/codex-oauth-keep-warm.ts +27 -9
  131. package/src/http/config.ts +5 -3
  132. package/src/http/core.ts +2 -0
  133. package/src/http/fs.ts +54 -34
  134. package/src/http/mcp-body.ts +37 -0
  135. package/src/http/mcp-user.ts +3 -5
  136. package/src/http/mcp.ts +6 -7
  137. package/src/http/memory.ts +26 -15
  138. package/src/http/page-proxy.ts +61 -5
  139. package/src/http/pages-public.ts +27 -44
  140. package/src/http/pages.ts +4 -4
  141. package/src/http/script-runs.ts +2 -0
  142. package/src/http/session-access.ts +51 -2
  143. package/src/http/sessions.ts +249 -10
  144. package/src/http/tasks.ts +4 -32
  145. package/src/http/users.ts +12 -1
  146. package/src/integrations/chat-sdk/adapters.ts +7 -3
  147. package/src/integrations/chat-sdk/attachments.ts +90 -12
  148. package/src/integrations/chat-sdk/facts.ts +120 -0
  149. package/src/integrations/chat-sdk/inbound.ts +156 -8
  150. package/src/integrations/chat-sdk/limits.ts +24 -0
  151. package/src/integrations/chat-sdk/outbound-files.ts +186 -0
  152. package/src/integrations/chat-sdk/outbound.ts +234 -4
  153. package/src/integrations/chat-sdk/relay.ts +4 -1
  154. package/src/integrations/chat-sdk/router.ts +28 -2
  155. package/src/integrations/chat-sdk/steering.ts +9 -0
  156. package/src/integrations/chat-sdk/templates.ts +14 -0
  157. package/src/integrations/chat-sdk/thread-buffer.ts +4 -0
  158. package/src/mcp-limits.ts +20 -0
  159. package/src/pages/browser-sdk.ts +8 -3
  160. package/src/pages/feedback-overlay.ts +1 -1
  161. package/src/providers/acp-adapter.ts +33 -13
  162. package/src/providers/acp-swarm-events.ts +1 -1
  163. package/src/providers/amp-adapter.ts +1 -1
  164. package/src/providers/codex-adapter.ts +106 -119
  165. package/src/providers/codex-agents-md.ts +35 -229
  166. package/src/providers/codex-oauth/storage.ts +1 -1
  167. package/src/providers/cursor-adapter.ts +1 -1
  168. package/src/rbac/legacy-policy.ts +3 -1
  169. package/src/rbac/permissions.ts +4 -0
  170. package/src/realtime/transport.ts +1 -2
  171. package/src/script-workflows/workflow-ctx.ts +1 -0
  172. package/src/scripts-runtime/types/stdlib.d.ts +2 -0
  173. package/src/scripts-runtime/types/swarm-extension.d.ts +8 -0
  174. package/src/scripts-runtime/types/swarm-sdk.d.ts +2 -0
  175. package/src/server-user.ts +2 -0
  176. package/src/server.ts +2 -0
  177. package/src/slack/assistant.ts +2 -2
  178. package/src/slack/files.ts +2 -0
  179. package/src/slack/handlers.ts +4 -4
  180. package/src/slack/inbound-files.ts +39 -6
  181. package/src/slack/thread-buffer.ts +2 -1
  182. package/src/tasks/task-terminal-effects.ts +21 -17
  183. package/src/tests/acp-adapter.test.ts +10 -23
  184. package/src/tests/acp-fake-agent.ts +36 -0
  185. package/src/tests/acp-swarm-events.test.ts +1 -1
  186. package/src/tests/agent-session-tokens.test.ts +452 -0
  187. package/src/tests/apps-action-parent-task.test.ts +295 -0
  188. package/src/tests/apps-card-grid.test.ts +197 -0
  189. package/src/tests/apps-catalog-additions.test.ts +120 -0
  190. package/src/tests/apps-tree.test.ts +129 -0
  191. package/src/tests/chat-sdk-attachments-reply.test.ts +52 -0
  192. package/src/tests/chat-sdk-config.test.ts +11 -0
  193. package/src/tests/chat-sdk-facts.test.ts +23 -0
  194. package/src/tests/chat-sdk-inbound.test.ts +558 -4
  195. package/src/tests/chat-sdk-outbound-files.test.ts +652 -0
  196. package/src/tests/codex-adapter.test.ts +167 -242
  197. package/src/tests/codex-oauth-keep-warm.test.ts +85 -1
  198. package/src/tests/config-api-only-keys.test.ts +9 -0
  199. package/src/tests/config-session-auth.test.ts +14 -0
  200. package/src/tests/cors-allowlist.test.ts +1 -1
  201. package/src/tests/docs-index-footprint.test.js +2 -2
  202. package/src/tests/extensions-example-slack-reply-gate.test.ts +262 -91
  203. package/src/tests/grok-adapter.test.ts +3 -3
  204. package/src/tests/heartbeat-checklist.test.ts +33 -0
  205. package/src/tests/heartbeat-jev-gate-credentials.test.ts +44 -0
  206. package/src/tests/heartbeat-jev-gate.test.ts +265 -1
  207. package/src/tests/kv-page-proxy.test.ts +91 -5
  208. package/src/tests/launch-password-rejection.test.ts +2 -2
  209. package/src/tests/mcp-input-caps.test.ts +294 -0
  210. package/src/tests/memory-chunked-search-starvation.test.ts +200 -0
  211. package/src/tests/memory-embedding-input-cap.test.ts +114 -0
  212. package/src/tests/memory.test.ts +14 -0
  213. package/src/tests/migration-232-agent-session-tokens.test.ts +67 -0
  214. package/src/tests/page-guest-session.test.ts +19 -8
  215. package/src/tests/page-proxy-authed.test.ts +1 -1
  216. package/src/tests/page-proxy.test.ts +17 -10
  217. package/src/tests/page-session.test.ts +102 -1
  218. package/src/tests/pages-authed-mode.test.ts +82 -12
  219. package/src/tests/pages-password-mode.test.ts +5 -5
  220. package/src/tests/rbac-engine.test.ts +1 -0
  221. package/src/tests/realtime-transport.test.ts +13 -7
  222. package/src/tests/runtime-child-token-cleanup.test.ts +3 -3
  223. package/src/tests/script-connections.test.ts +2 -0
  224. package/src/tests/script-runs-http.test.ts +84 -0
  225. package/src/tests/scripts-http.test.ts +10 -1
  226. package/src/tests/session-route-posture.test.ts +2 -0
  227. package/src/tests/swarm-config-export-to-env.test.ts +22 -3
  228. package/src/tests/task-audio-transcription.test.ts +299 -0
  229. package/src/tests/task-completion-memory-chunking.test.ts +434 -0
  230. package/src/tests/task-terminal-secret-scrub.test.ts +6 -1
  231. package/src/tests/transcription.test.ts +299 -0
  232. package/src/tests/worker-token-cli.test.ts +76 -0
  233. package/src/tests/workflow-executors.test.ts +4 -2
  234. package/src/tests/workflow-system-one-decision-providers.test.ts +98 -1
  235. package/src/tests/workflow-system-one-decision.test.ts +2 -1
  236. package/src/tests/workflow-system-one-extraction.test.ts +805 -0
  237. package/src/tools/chat-access.ts +10 -0
  238. package/src/tools/chat-post.ts +31 -11
  239. package/src/tools/chat-reply.ts +32 -10
  240. package/src/tools/memory-search.ts +3 -1
  241. package/src/tools/system-one-decide.ts +17 -4
  242. package/src/types.ts +8 -1
  243. package/src/utils/config-env-export.ts +3 -1
  244. package/src/utils/page-session.ts +133 -20
  245. package/src/utils/request-auth-context.ts +2 -1
  246. package/src/utils/transcription.ts +246 -0
  247. package/src/workflows/definition.ts +7 -1
  248. package/src/workflows/engine.ts +18 -12
  249. package/src/workflows/executors/index.ts +1 -0
  250. package/src/workflows/executors/registry.ts +2 -0
  251. package/src/workflows/executors/system-one-decision.ts +84 -262
  252. package/src/workflows/executors/system-one-extract-wire.ts +57 -0
  253. package/src/workflows/executors/system-one-extraction.ts +830 -0
  254. package/src/workflows/executors/system-one-providers.ts +316 -114
  255. package/src/workflows/executors/system-one-transport.ts +264 -0
  256. package/templates/extensions/slack-reply-gate/README.md +24 -22
  257. package/templates/extensions/slack-reply-gate/hooks.ts +146 -95
  258. package/templates/extensions/slack-reply-gate/manifest.json +2 -2
  259. package/templates/official/lead/HEARTBEAT.md +1 -1
  260. package/templates/skills/apps/SKILL.md +31 -7
  261. package/templates/skills/apps/content.md +31 -7
  262. package/templates/skills/chat-sdk/content.md +6 -2
  263. package/templates/skills/code-quality/content.md +2 -1
  264. package/templates/skills/heartbeat-runbook/content.md +8 -8
  265. package/templates/skills/pages/content.md +6 -4
  266. package/templates/skills/swarm-scripts/SKILL.md +2 -2
  267. package/templates/skills/swarm-scripts/content.md +2 -2
  268. package/dist/cli-v1sjhn52.js +0 -7
  269. package/dist/handlers-2qjw434w.js +0 -39
  270. package/dist/index-d0g18bax.js +0 -41
@@ -1,6 +1,5 @@
1
1
  import { z } from "zod";
2
2
  import type { ExecutorMeta } from "../../types";
3
- import { getOpenRouterAttributionHeaders } from "../../utils/openrouter-base-url";
4
3
  import { scrubSecrets } from "../../utils/secret-scrubber";
5
4
  import {
6
5
  type ApprovalOutcome,
@@ -17,19 +16,28 @@ import {
17
16
  HumanInTheLoopExecutor,
18
17
  NotificationConfigSchema,
19
18
  } from "./human-in-the-loop";
19
+ import { SYSTEM_ONE_EXTRACTION_NODE_TYPE } from "./system-one-extraction";
20
20
  import {
21
+ resolveSystemOneDefaultProvider,
21
22
  resolveSystemOneTarget,
22
- SYSTEM_ONE_DEFAULT_PROVIDER,
23
23
  SYSTEM_ONE_PROVIDER_IDS,
24
24
  SYSTEM_ONE_PROVIDERS,
25
25
  type SystemOneProvider,
26
26
  type SystemOneProviderId,
27
- systemOneKeyRejectedMessage,
28
27
  systemOneModelProblem,
29
28
  systemOneProviderOf,
30
29
  systemOneQuestionLimitProblem,
31
30
  } from "./system-one-providers";
32
- import { cloudflareErrorCode, SYSTEM_ONE_WIRE, SystemOneWireError } from "./system-one-wire";
31
+ import {
32
+ SYSTEM_ONE_DEFAULT_MAX_RETRIES,
33
+ SYSTEM_ONE_DEFAULT_TIMEOUT_MS,
34
+ SYSTEM_ONE_MAX_RETRIES_LIMIT,
35
+ SYSTEM_ONE_MAX_TIMEOUT_MS,
36
+ SYSTEM_ONE_MIN_TIMEOUT_MS,
37
+ type SystemOneTransportOptions,
38
+ sendSystemOneRequest,
39
+ } from "./system-one-transport";
40
+ import { SYSTEM_ONE_WIRE, SystemOneWireError } from "./system-one-wire";
33
41
 
34
42
  // ─── Constants ──────────────────────────────────────────────
35
43
 
@@ -43,11 +51,13 @@ export const SYSTEM_ONE_DECISION_NODE_TYPE = "system-one-decision";
43
51
  export const SYSTEM_ONE_ENDPOINT = SYSTEM_ONE_PROVIDERS.typesafe.endpoint;
44
52
  export const SYSTEM_ONE_API_KEY_CONFIG_KEY = SYSTEM_ONE_PROVIDERS.typesafe.keyName;
45
53
 
46
- export const SYSTEM_ONE_DEFAULT_TIMEOUT_MS = 30_000;
47
- export const SYSTEM_ONE_MIN_TIMEOUT_MS = 1_000;
48
- export const SYSTEM_ONE_MAX_TIMEOUT_MS = 300_000;
49
- export const SYSTEM_ONE_DEFAULT_MAX_RETRIES = 2;
50
- export const SYSTEM_ONE_MAX_RETRIES_LIMIT = 3;
54
+ export {
55
+ SYSTEM_ONE_DEFAULT_MAX_RETRIES,
56
+ SYSTEM_ONE_DEFAULT_TIMEOUT_MS,
57
+ SYSTEM_ONE_MAX_RETRIES_LIMIT,
58
+ SYSTEM_ONE_MAX_TIMEOUT_MS,
59
+ SYSTEM_ONE_MIN_TIMEOUT_MS,
60
+ };
51
61
 
52
62
  /** Choice: 2-255 options, Score: 2-10 levels (TypeSafe primitive limits; 2 is our authoring floor). */
53
63
  const CHOICE_MIN_OPTIONS = 2;
@@ -55,14 +65,8 @@ const CHOICE_MAX_OPTIONS = 255;
55
65
  const SCORE_MIN_LEVELS = 2;
56
66
  const SCORE_MAX_LEVELS = 10;
57
67
 
58
- /** Room left before the engine watchdog fires so the executor can report its own failure. */
59
- const DEADLINE_MARGIN_MS = 250;
60
- const BACKOFF_BASE_MS = 500;
61
- const BACKOFF_CAP_MS = 8_000;
62
68
  /** A returned distribution must sum to 1 within this tolerance. It is checked, never normalized. */
63
69
  const DISTRIBUTION_TOLERANCE = 0.02;
64
- const MAX_RESPONSE_CHARS = 1_048_576;
65
- const MAX_ERROR_BODY_CHARS = 65_536;
66
70
 
67
71
  /** Output ports of a node that sets `humanReview`, the same names `human-in-the-loop` uses. */
68
72
  const REVIEW_PORTS = ["approved", "rejected", "timeout"] as const;
@@ -160,7 +164,11 @@ export type SystemOneHumanReview = z.infer<typeof HumanReviewSchema>;
160
164
 
161
165
  export const SystemOneDecisionConfigSchema = z
162
166
  .strictObject({
163
- provider: z.enum(SYSTEM_ONE_PROVIDER_IDS).default(SYSTEM_ONE_DEFAULT_PROVIDER),
167
+ /**
168
+ * Decision host. Unset means the `SYSTEM_ONE_DEFAULT_PROVIDER` global config
169
+ * value, read at run time (`typesafe` when that is unset too).
170
+ */
171
+ provider: z.enum(SYSTEM_ONE_PROVIDER_IDS).optional(),
164
172
  /** Provider-specific model id. Unset means the provider's default. */
165
173
  model: z.string().min(1).optional(),
166
174
  state: SystemOneStateSchema,
@@ -231,7 +239,9 @@ export const SystemOneDecisionConfigSchema = z
231
239
  }
232
240
  }
233
241
  // Provider limits. A {{token}} model is skipped at save and checked again at run,
234
- // where the interpolated config is parsed by this same schema.
242
+ // where the interpolated config is parsed by this same schema. With no provider
243
+ // the default is read at run time, so `decide` checks the limits there.
244
+ if (config.provider === undefined) return;
235
245
  const modelProblem = systemOneModelProblem(
236
246
  config.provider,
237
247
  config.model ?? (SYSTEM_ONE_PROVIDERS[config.provider] as SystemOneProvider).defaultModel,
@@ -316,8 +326,18 @@ interface RetryShape {
316
326
  maxRetries?: number;
317
327
  }
318
328
 
329
+ /** Node types that call a System One host: paid, non-idempotent, with their own transient retries. */
330
+ const SYSTEM_ONE_NODE_LABELS: Record<string, string> = {
331
+ [SYSTEM_ONE_DECISION_NODE_TYPE]: "SystemOne Decision",
332
+ [SYSTEM_ONE_EXTRACTION_NODE_TYPE]: "SystemOne Extraction",
333
+ };
334
+
335
+ export function isSystemOneNodeType(type: string): boolean {
336
+ return Object.hasOwn(SYSTEM_ONE_NODE_LABELS, type);
337
+ }
338
+
319
339
  /**
320
- * Why a `system-one-decision` node may not carry an engine retry policy.
340
+ * Why a `system-one-decision` (or `system-one-extraction`) node may not carry an engine retry policy.
321
341
  *
322
342
  * The executor owns its transient retries (`config.maxRetries`). Engine retries
323
343
  * are not status-aware, so a node-level or validation retry would re-send 401 / 422
@@ -329,18 +349,18 @@ export function systemOneRetryViolations(node: {
329
349
  retry?: RetryShape | null;
330
350
  validation?: { retry?: RetryShape | null } | null;
331
351
  }): string[] {
332
- if (node.type !== SYSTEM_ONE_DECISION_NODE_TYPE) return [];
352
+ if (!isSystemOneNodeType(node.type)) return [];
333
353
  const violations: string[] = [];
334
354
  const attempts = (retry: RetryShape | null | undefined) =>
335
355
  retry ? (retry.maxRetries ?? ENGINE_RETRY_DEFAULT) : 0;
336
356
  if (attempts(node.retry) > 0) {
337
357
  violations.push(
338
- `Node "${node.id}" (system-one-decision) must not set retry.maxRetries > 0: the system-one-decision executor retries transient transport errors itself (config.maxRetries), and engine retries would re-send rejected requests`,
358
+ `Node "${node.id}" (${node.type}) must not set retry.maxRetries > 0: the ${node.type} executor retries transient transport errors itself (config.maxRetries), and engine retries would re-send rejected requests`,
339
359
  );
340
360
  }
341
361
  if (attempts(node.validation?.retry) > 0) {
342
362
  violations.push(
343
- `Node "${node.id}" (system-one-decision) must not set validation.retry.maxRetries > 0: a validation-driven retry would send another paid request`,
363
+ `Node "${node.id}" (${node.type}) must not set validation.retry.maxRetries > 0: a validation-driven retry would send another paid request`,
344
364
  );
345
365
  }
346
366
  return violations;
@@ -397,11 +417,17 @@ export function systemOneStaticShapeViolations(node: {
397
417
  return violations;
398
418
  }
399
419
 
400
- /** The engine fails a system-one-decision step before dispatch when any config token is unresolved. */
401
- export function systemOneUnresolvedError(nodeId: string, tokens: string[]): string {
420
+ /** The engine fails a System One step before dispatch when any config token is unresolved. */
421
+ export function systemOneUnresolvedError(
422
+ nodeId: string,
423
+ tokens: string[],
424
+ nodeType: string = SYSTEM_ONE_DECISION_NODE_TYPE,
425
+ ): string {
402
426
  const rendered = [...new Set(tokens)].map((token) => `{{${token}}}`).join(", ");
427
+ const label =
428
+ SYSTEM_ONE_NODE_LABELS[nodeType] ?? SYSTEM_ONE_NODE_LABELS[SYSTEM_ONE_DECISION_NODE_TYPE];
403
429
  return (
404
- `SystemOne Decision node "${nodeId}" has unresolved interpolation token(s): ${rendered}. ` +
430
+ `${label} node "${nodeId}" has unresolved interpolation token(s): ${rendered}. ` +
405
431
  "No request was sent. Declare the value in the node's inputs mapping or fix the path."
406
432
  );
407
433
  }
@@ -795,79 +821,13 @@ export function resolveReviewedDecision(
795
821
 
796
822
  // ─── Transport ──────────────────────────────────────────────
797
823
 
798
- export interface SystemOneDecisionExecutorOptions {
799
- /** Transport. Tests inject a fake; production uses the global `fetch`. */
800
- fetch?: typeof fetch;
824
+ export interface SystemOneDecisionExecutorOptions extends SystemOneTransportOptions {
801
825
  /** Credential lookup for any provider. Defaults to each provider's own source (see `SYSTEM_ONE_PROVIDERS`). */
802
826
  getApiKey?: (provider: SystemOneProviderId) => Promise<string | null | undefined>;
803
827
  /** Base-URL lookup for a provider with no fixed host (`laya`). Defaults to its global config value. */
804
828
  getServerUrl?: (provider: SystemOneProviderId) => Promise<string | null | undefined>;
805
829
  /** Environment the OpenRouter credential is resolved from. Defaults to `process.env`. */
806
830
  env?: NodeJS.ProcessEnv;
807
- sleep?: (ms: number, signal: AbortSignal) => Promise<void>;
808
- now?: () => number;
809
- random?: () => number;
810
- }
811
-
812
- type Attempt =
813
- | { kind: "ok"; text: string; requestId?: string }
814
- | { kind: "timeout" }
815
- | { kind: "fail"; message: string; retryable: boolean; retryAfterMs?: number };
816
-
817
- const REQUEST_ID_RE = /^[A-Za-z0-9._:-]{1,128}$/;
818
- const ERROR_CODE_RE = /^[A-Za-z0-9._:-]{1,64}$/;
819
-
820
- function readRequestId(headers: Headers): string | undefined {
821
- // Cloudflare marks every response with `cf-ray` and sends no request id.
822
- const value = headers.get("x-request-id") ?? headers.get("request-id") ?? headers.get("cf-ray");
823
- return value && REQUEST_ID_RE.test(value) ? value : undefined;
824
- }
825
-
826
- /** Retry-After as delta-seconds or an HTTP date, in ms. Undefined when absent or unparseable. */
827
- function parseRetryAfter(value: string | null, now: number): number | undefined {
828
- if (!value) return undefined;
829
- const trimmed = value.trim();
830
- if (/^\d+(\.\d+)?$/.test(trimmed)) return Math.round(Number(trimmed) * 1000);
831
- const at = Date.parse(trimmed);
832
- return Number.isNaN(at) ? undefined : Math.max(0, at - now);
833
- }
834
-
835
- /** Only a short identifier from the provider's error envelope. Never its message or echoed request. */
836
- function readErrorCode(text: string): string | undefined {
837
- if (text.length === 0 || text.length > MAX_ERROR_BODY_CHARS) return undefined;
838
- let parsed: unknown;
839
- try {
840
- parsed = JSON.parse(text);
841
- } catch {
842
- return undefined;
843
- }
844
- if (!isRecord(parsed)) return undefined;
845
- const nested = isRecord(parsed.error) ? parsed.error : undefined;
846
- const detail = isRecord(parsed.detail) ? parsed.detail : undefined;
847
- for (const candidate of [
848
- nested?.code,
849
- nested?.type,
850
- detail?.error_type,
851
- parsed.code,
852
- parsed.type,
853
- ]) {
854
- if (typeof candidate === "string" && ERROR_CODE_RE.test(candidate)) return candidate;
855
- }
856
- // Cloudflare's envelope: `{ success: false, errors: [{ code: 10000, message }] }`.
857
- return cloudflareErrorCode(parsed);
858
- }
859
-
860
- function defaultSleep(ms: number, signal: AbortSignal): Promise<void> {
861
- return new Promise((resolve) => {
862
- if (signal.aborted) return resolve();
863
- const timer = setTimeout(done, ms);
864
- function done() {
865
- clearTimeout(timer);
866
- signal.removeEventListener("abort", done);
867
- resolve();
868
- }
869
- signal.addEventListener("abort", done, { once: true });
870
- });
871
831
  }
872
832
 
873
833
  // ─── Executor ───────────────────────────────────────────────
@@ -893,6 +853,11 @@ export class SystemOneDecisionExecutor extends BaseExecutor<
893
853
  return { db: this.deps.db, env: this.options.env ?? process.env };
894
854
  }
895
855
 
856
+ /** The provider a config without `provider` uses, read now so a settings change applies at once. */
857
+ private defaultProvider(): Promise<SystemOneProviderId> {
858
+ return resolveSystemOneDefaultProvider(this.providerContext());
859
+ }
860
+
896
861
  private resolveTarget(id: SystemOneProviderId, model?: string) {
897
862
  return resolveSystemOneTarget(id, this.providerContext(), {
898
863
  apiKey: this.options.getApiKey,
@@ -909,9 +874,10 @@ export class SystemOneDecisionExecutor extends BaseExecutor<
909
874
  nodes: readonly ExecutorReadinessNode[],
910
875
  ): Promise<ExecutorReadinessProblem[]> {
911
876
  const nodeIdsByProvider = new Map<SystemOneProviderId, string[]>();
877
+ const defaultId = await this.defaultProvider();
912
878
  for (const node of nodes) {
913
879
  // An unknown provider is a config error the schema reports; it names no credential.
914
- const id = systemOneProviderOf(node.config);
880
+ const id = systemOneProviderOf(node.config, defaultId);
915
881
  if (id) nodeIdsByProvider.set(id, [...(nodeIdsByProvider.get(id) ?? []), node.id]);
916
882
  }
917
883
  const problems: ExecutorReadinessProblem[] = [];
@@ -949,29 +915,44 @@ export class SystemOneDecisionExecutor extends BaseExecutor<
949
915
  */
950
916
  async decide(
951
917
  config: SystemOneDecisionConfig,
952
- ): Promise<{ ok: true; output: SystemOneDecisionOutput } | { ok: false; error: string }> {
953
- const providerId = config.provider;
918
+ ): Promise<
919
+ | { ok: true; output: SystemOneDecisionOutput }
920
+ | { ok: false; error: string; deploymentRefused?: true }
921
+ > {
922
+ const providerId = config.provider ?? (await this.defaultProvider());
954
923
  const provider: SystemOneProvider = SYSTEM_ONE_PROVIDERS[providerId];
955
924
 
956
925
  // A provider with no default sends no `model` when the node sets none.
957
926
  const model = config.model ?? provider.defaultModel;
958
927
  const modelProblem = systemOneModelProblem(providerId, model);
959
928
  if (modelProblem) return { ok: false, error: modelProblem };
929
+ // Checked at save only for a named provider; a defaulted one is checked here.
930
+ const limitProblem = systemOneQuestionLimitProblem(
931
+ providerId,
932
+ Object.keys(config.questions).length,
933
+ );
934
+ if (limitProblem) return { ok: false, error: limitProblem };
960
935
  const target = await this.resolveTarget(providerId, model);
961
- if (!target.ok) return { ok: false, error: target.error };
962
- const { apiKey, endpoint } = target;
936
+ if (!target.ok) return target;
937
+ const { apiKey, endpoint, keyName } = target;
963
938
 
964
939
  const wire = provider.wire ?? SYSTEM_ONE_WIRE;
965
940
  const body = JSON.stringify(wire.buildRequest(config, model));
966
941
 
967
942
  const scrub = (message: string) => message.split(apiKey).join("[REDACTED]");
968
- const sent = await this.send(
969
- providerId,
970
- endpoint,
971
- apiKey,
972
- body,
973
- config.timeoutMs,
974
- config.maxRetries,
943
+ const sent = await sendSystemOneRequest(
944
+ {
945
+ providerId,
946
+ capability: "decide",
947
+ endpoint,
948
+ apiKey,
949
+ keyName,
950
+ body,
951
+ timeoutMs: config.timeoutMs,
952
+ maxRetries: config.maxRetries,
953
+ env: this.providerContext().env,
954
+ },
955
+ this.options,
975
956
  );
976
957
  if (!sent.ok) return { ok: false, error: scrub(sent.error) };
977
958
 
@@ -1101,163 +1082,4 @@ export class SystemOneDecisionExecutor extends BaseExecutor<
1101
1082
  ): ApprovalOutcome | null {
1102
1083
  return resolveReviewedDecision(parkedOutput, approval);
1103
1084
  }
1104
-
1105
- /**
1106
- * POST once, retrying only transient failures inside one time budget.
1107
- * Retries: connection errors, HTTP 408 / 429 / 5xx (incl. 529). Never: 401, 422,
1108
- * other 4xx, redirects, or a bad success body (the caller validates that).
1109
- */
1110
- private async send(
1111
- providerId: SystemOneProviderId,
1112
- endpoint: string,
1113
- apiKey: string,
1114
- body: string,
1115
- timeoutMs: number,
1116
- maxRetries: number,
1117
- ): Promise<{ ok: true; text: string; requestId?: string } | { ok: false; error: string }> {
1118
- const doFetch = this.options.fetch ?? fetch;
1119
- const sleep = this.options.sleep ?? defaultSleep;
1120
- const now = this.options.now ?? Date.now;
1121
- const random = this.options.random ?? Math.random;
1122
-
1123
- const deadlineAt = now() + timeoutMs - DEADLINE_MARGIN_MS;
1124
- const controller = new AbortController();
1125
- const timer = setTimeout(() => controller.abort(), Math.max(0, deadlineAt - now()));
1126
- const timedOut = () =>
1127
- ({
1128
- ok: false,
1129
- error: `SystemOne request exceeded its ${timeoutMs}ms time budget`,
1130
- }) as const;
1131
-
1132
- try {
1133
- for (let attempt = 0; ; attempt++) {
1134
- const result = await this.attemptOnce(
1135
- doFetch,
1136
- providerId,
1137
- endpoint,
1138
- apiKey,
1139
- body,
1140
- controller.signal,
1141
- now,
1142
- );
1143
- if (result.kind === "ok") {
1144
- return { ok: true, text: result.text, requestId: result.requestId };
1145
- }
1146
- if (result.kind === "timeout" || controller.signal.aborted) return timedOut();
1147
-
1148
- const attempts = attempt + 1;
1149
- const suffix = attempts > 1 ? ` after ${attempts} attempts` : "";
1150
- if (!result.retryable || attempt >= maxRetries) {
1151
- return { ok: false, error: `${result.message}${suffix}` };
1152
- }
1153
-
1154
- const backoff = Math.min(BACKOFF_CAP_MS, BACKOFF_BASE_MS * 2 ** attempt);
1155
- const delay = result.retryAfterMs ?? Math.round(backoff * (0.5 + random() * 0.5));
1156
- if (delay >= deadlineAt - now()) {
1157
- return {
1158
- ok: false,
1159
- error: `${result.message}${suffix}; the ${delay}ms retry delay does not fit the ${timeoutMs}ms time budget`,
1160
- };
1161
- }
1162
- await sleep(delay, controller.signal);
1163
- if (controller.signal.aborted) return timedOut();
1164
- }
1165
- } finally {
1166
- clearTimeout(timer);
1167
- }
1168
- }
1169
-
1170
- private async attemptOnce(
1171
- doFetch: typeof fetch,
1172
- providerId: SystemOneProviderId,
1173
- endpoint: string,
1174
- apiKey: string,
1175
- body: string,
1176
- signal: AbortSignal,
1177
- now: () => number,
1178
- ): Promise<Attempt> {
1179
- let res: Response;
1180
- try {
1181
- res = await doFetch(endpoint, {
1182
- method: "POST",
1183
- headers: {
1184
- ...getOpenRouterAttributionHeaders(endpoint, this.providerContext().env),
1185
- "content-type": "application/json",
1186
- accept: "application/json",
1187
- authorization: `Bearer ${apiKey}`,
1188
- },
1189
- body,
1190
- redirect: "manual",
1191
- signal,
1192
- });
1193
- } catch (err) {
1194
- if (signal.aborted) return { kind: "timeout" };
1195
- const code = (err as { code?: unknown })?.code;
1196
- const detail = typeof code === "string" && /^[A-Z0-9_]{3,40}$/.test(code) ? ` (${code})` : "";
1197
- return {
1198
- kind: "fail",
1199
- message: `SystemOne request failed: network error${detail}`,
1200
- retryable: true,
1201
- };
1202
- }
1203
-
1204
- const requestId = readRequestId(res.headers);
1205
- const withId = (message: string) => (requestId ? `${message} [request ${requestId}]` : message);
1206
-
1207
- if (res.status >= 200 && res.status < 300) {
1208
- let text: string;
1209
- try {
1210
- text = await res.text();
1211
- } catch {
1212
- if (signal.aborted) return { kind: "timeout" };
1213
- return {
1214
- kind: "fail",
1215
- message: withId("SystemOne response body could not be read"),
1216
- retryable: true,
1217
- };
1218
- }
1219
- if (text.length > MAX_RESPONSE_CHARS) {
1220
- return {
1221
- kind: "fail",
1222
- message: withId("SystemOne response exceeded the size limit"),
1223
- retryable: false,
1224
- };
1225
- }
1226
- return { kind: "ok", text, requestId };
1227
- }
1228
-
1229
- if (res.status >= 300 && res.status < 400) {
1230
- return {
1231
- kind: "fail",
1232
- message: withId(
1233
- `SystemOne request was redirected (HTTP ${res.status}); redirects are refused`,
1234
- ),
1235
- retryable: false,
1236
- };
1237
- }
1238
-
1239
- let errorText = "";
1240
- try {
1241
- errorText = await res.text();
1242
- } catch {
1243
- if (signal.aborted) return { kind: "timeout" };
1244
- }
1245
- const code = readErrorCode(errorText);
1246
- // The host answered but does not accept the key: name it, never retry.
1247
- if (res.status === 401 || res.status === 403) {
1248
- return {
1249
- kind: "fail",
1250
- message: withId(systemOneKeyRejectedMessage(providerId, res.status, code)),
1251
- retryable: false,
1252
- };
1253
- }
1254
- const detail = code ? ` (${code})` : "";
1255
- const retryable = res.status === 408 || res.status === 429 || res.status >= 500;
1256
- return {
1257
- kind: "fail",
1258
- message: withId(`SystemOne API returned HTTP ${res.status}${detail}`),
1259
- retryable,
1260
- retryAfterMs: retryable ? parseRetryAfter(res.headers.get("retry-after"), now()) : undefined,
1261
- };
1262
- }
1263
1085
  }
@@ -0,0 +1,57 @@
1
+ import type { SystemOneExtractionConfig } from "./system-one-extraction";
2
+ import {
3
+ cloudflareErrorCode,
4
+ isRecord,
5
+ SystemOneWireError,
6
+ wireContractError,
7
+ } from "./system-one-wire";
8
+
9
+ /**
10
+ * How one host's extraction request and response map onto the `system-one-extraction`
11
+ * contract: Clairic's `{ model, state, fields, images?, reference_date? }` request and
12
+ * `{ model, answers, truncated, usage }` body. A host with another shape supplies a
13
+ * wire that builds its body and returns that shape, which
14
+ * `validateSystemOneExtraction` then checks.
15
+ */
16
+ export interface SystemOneExtractWire {
17
+ /** Request body for one call. */
18
+ buildRequest(config: SystemOneExtractionConfig, model: string | undefined): unknown;
19
+ /**
20
+ * Turn a parsed success body into the Clairic `{ model, answers, usage }` shape, or
21
+ * throw a `SystemOneWireError`. The result is still validated afterwards.
22
+ */
23
+ toExtraction(body: unknown): unknown;
24
+ }
25
+
26
+ /** Clairic reads one text; structured state is sent as JSON text, which spans then index into. */
27
+ export function extractionStateText(state: SystemOneExtractionConfig["state"]): string {
28
+ return typeof state === "string" ? state : JSON.stringify(state);
29
+ }
30
+
31
+ /** The Clairic request as is: field specs already use Clairic's option names. */
32
+ export const CLAIRIC_WIRE: SystemOneExtractWire = {
33
+ buildRequest: (config, model) => ({
34
+ ...(model === undefined ? {} : { model }),
35
+ state: extractionStateText(config.state),
36
+ fields: config.fields,
37
+ ...(config.images?.length ? { images: config.images } : {}),
38
+ ...(config.referenceDate ? { reference_date: config.referenceDate } : {}),
39
+ }),
40
+ toExtraction: (body) => body,
41
+ };
42
+
43
+ /** Cloudflare Workers AI: the Clairic request, the response wrapped in `{ result, success, errors }`. */
44
+ export const CLOUDFLARE_EXTRACT_WIRE: SystemOneExtractWire = {
45
+ buildRequest: CLAIRIC_WIRE.buildRequest,
46
+ toExtraction(body) {
47
+ if (!isRecord(body)) wireContractError("response body must be a JSON object");
48
+ if (body.success !== true) {
49
+ const code = cloudflareErrorCode(body);
50
+ throw new SystemOneWireError(
51
+ `Cloudflare Workers AI reported a failed run${code ? ` (${code})` : ""}. Nothing was extracted.`,
52
+ );
53
+ }
54
+ if (!isRecord(body.result)) wireContractError("Cloudflare response is missing its result");
55
+ return body.result;
56
+ },
57
+ };