okengine 0.21.1 → 0.23.0

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 (342) hide show
  1. package/AGENTS.md +11 -4
  2. package/LICENSE +202 -0
  3. package/NOTICE +2 -0
  4. package/README.md +15 -9
  5. package/manifest.v1.schema.json +54 -0
  6. package/package.json +53 -38
  7. package/site/content/docs/ai/mcp.mdx +13 -9
  8. package/site/content/docs/ai/skills.mdx +2 -2
  9. package/site/content/docs/client/calling.mdx +27 -19
  10. package/site/content/docs/client/index.mdx +1 -1
  11. package/site/content/docs/elements/ai/agents.mdx +61 -13
  12. package/site/content/docs/elements/ai/decide.mdx +536 -0
  13. package/site/content/docs/elements/ai/events.mdx +373 -0
  14. package/site/content/docs/elements/ai/index.mdx +9 -3
  15. package/site/content/docs/elements/ai/meta.json +1 -1
  16. package/site/content/docs/elements/ai/prompts.mdx +4 -2
  17. package/site/content/docs/elements/channel/email.mdx +1 -1
  18. package/site/content/docs/elements/channel/index.mdx +1 -1
  19. package/site/content/docs/elements/flow/http.mdx +4 -4
  20. package/site/content/docs/elements/flow/index.mdx +3 -3
  21. package/site/content/docs/elements/flow/meta.json +1 -1
  22. package/site/content/docs/elements/gate/auth.mdx +4 -1
  23. package/site/content/docs/elements/gate/tenancy.mdx +1 -1
  24. package/site/content/docs/elements/store/index.mdx +1 -1
  25. package/site/content/docs/elements/store/kv.mdx +5 -5
  26. package/site/content/docs/elements/vault/rotation.mdx +15 -0
  27. package/site/content/docs/plugins/otp.mdx +31 -31
  28. package/site/content/docs/recipes/dragonfly.mdx +5 -5
  29. package/site/content/docs/recipes/index.mdx +6 -6
  30. package/site/content/docs/recipes/mailpit.mdx +1 -1
  31. package/site/content/docs/recipes/meilisearch.mdx +1 -1
  32. package/site/content/docs/recipes/pgdog.mdx +1 -1
  33. package/site/content/docs/recipes/postgres.mdx +12 -8
  34. package/site/content/docs/recipes/redis.mdx +4 -4
  35. package/site/content/docs/reference/cli.mdx +2 -1
  36. package/site/content/docs/reference/configuration.mdx +11 -9
  37. package/site/content/docs/reference/errors.mdx +12 -2
  38. package/site/content/docs/reference/fx.mdx +35 -21
  39. package/site/content/docs/reference/idempotency.mdx +192 -0
  40. package/site/content/docs/reference/index.mdx +6 -1
  41. package/site/content/docs/reference/meta.json +2 -1
  42. package/site/content/docs/understand/meta.json +1 -1
  43. package/site/content/docs/{elements/flow → understand}/routing.mdx +11 -10
  44. package/site/content/docs/understand/the-architecture.mdx +14 -12
  45. package/site/content/docs/understand/try-it.mdx +3 -3
  46. package/src/auth/api-key-sql.test.ts +44 -0
  47. package/src/auth/api-key-sql.ts +1 -0
  48. package/src/auth/api-keys.ts +30 -12
  49. package/src/auth/config.ts +6 -2
  50. package/src/auth/gate-auth.test.ts +20 -0
  51. package/src/cli/agents-md.test.ts +25 -0
  52. package/src/cli/ask-vault-gaps.test.ts +38 -1
  53. package/src/cli/ask-vault-gaps.ts +35 -2
  54. package/src/cli/competitor-mention-removal.test.ts +3 -3
  55. package/src/cli/decide.ts +174 -0
  56. package/src/cli/decision-lock-watch.test.ts +45 -0
  57. package/src/cli/decision-lock-watch.ts +51 -0
  58. package/src/cli/dev-app-runner.ts +34 -8
  59. package/src/cli/dev-auth-secret.test.ts +49 -0
  60. package/src/cli/dev-auth-secret.ts +49 -0
  61. package/src/cli/dev-hot.test.ts +41 -0
  62. package/src/cli/dev-hot.ts +48 -0
  63. package/src/cli/dev.test.ts +1 -1
  64. package/src/cli/dev.ts +24 -6
  65. package/src/cli/docker-cli.test.ts +1 -1
  66. package/src/cli/eval.ts +142 -1
  67. package/src/cli/index.ts +14 -1
  68. package/src/cli/load-config.images.test.ts +12 -12
  69. package/src/cli/load-config.ts +2 -2
  70. package/src/cli/mcp-from-console.ts +48 -0
  71. package/src/cli/registry.ts +35 -1
  72. package/src/cli/vault-cmd.test.ts +23 -0
  73. package/src/cli/vault-cmd.ts +7 -0
  74. package/src/client/agent.ts +307 -0
  75. package/src/client/create.ts +18 -1
  76. package/src/client/transport.test.ts +245 -0
  77. package/src/client/transport.ts +133 -19
  78. package/src/client/types.ts +29 -3
  79. package/src/client-react/index.ts +7 -1
  80. package/src/client-react/use-agent-run.test.ts +271 -0
  81. package/src/client-react/use-agent-run.ts +193 -0
  82. package/src/compiler/ai-approval.test.ts +36 -0
  83. package/src/compiler/ai-repair.test.ts +22 -0
  84. package/src/compiler/decisions.extract.test.ts +164 -0
  85. package/src/compiler/effects-infer.ts +753 -17
  86. package/src/compiler/extract.test.ts +37 -0
  87. package/src/compiler/extract.ts +381 -24
  88. package/src/compiler/fixtures/skyport.expected.json +24 -0
  89. package/src/compiler/fx-follow.test.ts +164 -0
  90. package/src/compiler/fx-index.ts +399 -0
  91. package/src/compiler/response.ts +35 -2
  92. package/src/config/index.ts +21 -3
  93. package/src/console/server/ai-runs-flows.ts +269 -0
  94. package/src/console/server/ai-runs.test.ts +323 -0
  95. package/src/console/server/ai-runs.ts +562 -0
  96. package/src/console/server/ai.ts +12 -1
  97. package/src/console/server/app.ts +10 -1
  98. package/src/console/server/decisions-flows.ts +157 -0
  99. package/src/console/server/decisions.test.ts +66 -0
  100. package/src/console/server/decisions.ts +229 -0
  101. package/src/console/server/flows-invoke.test.ts +2 -2
  102. package/src/console/server/flows.ts +50 -2
  103. package/src/console/server/invoke-user-flow.ts +9 -0
  104. package/src/console/server/runs-ingest.ts +16 -0
  105. package/src/console/server/serve.ts +5 -0
  106. package/src/console/server/state.ts +25 -1
  107. package/src/console/server/store-stats.test.ts +47 -0
  108. package/src/console/server/store-stats.ts +92 -18
  109. package/src/console/ui-next/dist/assets/access-page-DDKkhT9v.js +4 -0
  110. package/src/console/ui-next/dist/assets/agent-disclosure-62Xts8Xi.js +1 -0
  111. package/src/console/ui-next/dist/assets/{cache-glyph-92uM5MO7.js → cache-glyph-DyeoKHKA.js} +1 -1
  112. package/src/console/ui-next/dist/assets/{call-pii-button-DtGVPtcs.js → call-pii-button-DvfpLXsU.js} +1 -1
  113. package/src/console/ui-next/dist/assets/{collapsible-DN7l6zmC.js → collapsible-BY03SeCg.js} +1 -1
  114. package/src/console/ui-next/dist/assets/confirm-sheet-DZfuJXP4.js +1 -0
  115. package/src/console/ui-next/dist/assets/copy-inline-button-BTjPVjRT.js +1 -0
  116. package/src/console/ui-next/dist/assets/decisions-page-CgkKlA56.js +1 -0
  117. package/src/console/ui-next/dist/assets/detail-header-Dk5LipF6.js +1 -0
  118. package/src/console/ui-next/dist/assets/dropdown-menu-CRGrj5JU.js +1 -0
  119. package/src/console/ui-next/dist/assets/duration-tone-Y6HLjyJ5.js +9 -0
  120. package/src/console/ui-next/dist/assets/element-icons-C6t8Vlml.js +1 -0
  121. package/src/console/ui-next/dist/assets/explorer-chrome-Dslh2yE8.js +1 -0
  122. package/src/console/ui-next/dist/assets/explorer-empty-CIVg7Jgb.js +1 -0
  123. package/src/console/ui-next/dist/assets/flows-page-DLPfNy-_.js +1 -0
  124. package/src/console/ui-next/dist/assets/{highlighted-json-BlAEVgNW.js → highlighted-json-D7nNzpgB.js} +1 -1
  125. package/src/console/ui-next/dist/assets/{http-method-BDf7OAHv.js → http-method-DdL19zzh.js} +1 -1
  126. package/src/console/ui-next/dist/assets/index-BlrN49Hs.css +2 -0
  127. package/src/console/ui-next/dist/assets/index-C0lc_s9d.js +58 -0
  128. package/src/console/ui-next/dist/assets/observability-page-CRlcyLCC.js +8 -0
  129. package/src/console/ui-next/dist/assets/{replica-lag-B3GLNVfF.js → replica-lag-Bb3FWUu9.js} +7 -7
  130. package/src/console/ui-next/dist/assets/request-meta-DYBMWhX8.js +1 -0
  131. package/src/console/ui-next/dist/assets/shortcut-keys-DTE1sjTC.js +1 -0
  132. package/src/console/ui-next/dist/assets/store-page-CcE-SXC8.js +41 -0
  133. package/src/console/ui-next/dist/assets/trace-detail-sheet-CKMSEQ4s.js +2 -0
  134. package/src/console/ui-next/dist/assets/tree-expand-toggle-QOfk0M0H.js +55 -0
  135. package/src/console/ui-next/dist/assets/units-page-DKC1CDDd.js +1 -0
  136. package/src/console/ui-next/dist/assets/use-vault-list-BAyQG8Re.js +1 -0
  137. package/src/console/ui-next/dist/assets/vault-page-B54v4Yit.js +2 -0
  138. package/src/console/ui-next/dist/index.html +4 -3
  139. package/src/console/ui-next/seed-parked-approval.ts +126 -0
  140. package/src/console/ui-next/src/client.ts +224 -1
  141. package/src/console/ui-next/src/components/explorer/detail-header.tsx +9 -0
  142. package/src/console/ui-next/src/components/explorer/explorer-start-toggle.tsx +5 -1
  143. package/src/console/ui-next/src/components/ui/sheet-form.tsx +21 -12
  144. package/src/console/ui-next/src/features/flows/decisions/decisions-page.tsx +216 -0
  145. package/src/console/ui-next/src/features/flows/decisions/resolve.test.ts +48 -0
  146. package/src/console/ui-next/src/features/flows/decisions/resolve.ts +111 -0
  147. package/src/console/ui-next/src/features/flows/flows-page.tsx +10 -0
  148. package/src/console/ui-next/src/features/flows/graph/element-map.ts +1 -0
  149. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +6 -0
  150. package/src/console/ui-next/src/features/flows/traces/effect-summary.test.ts +24 -0
  151. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +24 -0
  152. package/src/console/ui-next/src/features/flows/traces/http-status.ts +58 -0
  153. package/src/console/ui-next/src/features/flows/traces/request-client-card.tsx +355 -0
  154. package/src/console/ui-next/src/features/flows/traces/request-client.test.ts +114 -0
  155. package/src/console/ui-next/src/features/flows/traces/request-client.ts +423 -0
  156. package/src/console/ui-next/src/features/flows/traces/request-input-view.test.ts +52 -6
  157. package/src/console/ui-next/src/features/flows/traces/request-input-view.ts +85 -19
  158. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +505 -80
  159. package/src/console/ui-next/src/features/flows/traces/trace-lanes.test.ts +61 -0
  160. package/src/console/ui-next/src/features/flows/traces/trace-lanes.ts +78 -0
  161. package/src/console/ui-next/src/features/flows/traces/trace-nav.test.ts +48 -0
  162. package/src/console/ui-next/src/features/flows/traces/trace-nav.ts +57 -0
  163. package/src/console/ui-next/src/features/flows/traces/trace-request-section.tsx +275 -96
  164. package/src/console/ui-next/src/features/flows/traces/traces-pane.tsx +16 -2
  165. package/src/console/ui-next/src/features/observability/detail/ai-runs.tsx +369 -0
  166. package/src/console/ui-next/src/features/observability/observability-page.tsx +31 -1
  167. package/src/console/ui-next/src/features/observability/state/observability-selection.ts +18 -3
  168. package/src/console/ui-next/src/features/store/performance/kv-performance-panel.tsx +12 -1
  169. package/src/console/ui-next/src/features/store/performance/performance-panel.tsx +14 -1
  170. package/src/console/ui-next/src/features/units/call/call-api-panel.tsx +1 -1
  171. package/src/console/ui-next/src/features/units/detail/flow-contract-panel.tsx +15 -2
  172. package/src/console/ui-next/src/features/units/explorer/units-tree.tsx +73 -18
  173. package/src/console/ui-next/src/features/units/lib/unit-tree.test.ts +21 -0
  174. package/src/console/ui-next/src/features/units/lib/unit-tree.ts +82 -0
  175. package/src/console/ui-next/src/features/units/units-page.tsx +25 -7
  176. package/src/console/ui-next/src/router.tsx +17 -0
  177. package/src/console/ui-next/ui-next-seed-runs.ts +23 -0
  178. package/src/console/ui-next/vite-console-kernel-plugin.ts +43 -3
  179. package/src/console/ui-next/vite.config.ts +9 -0
  180. package/src/docker/docker.test.ts +13 -13
  181. package/src/docker/images-config.test.ts +12 -12
  182. package/src/docker/recipes/dragonfly.ts +3 -2
  183. package/src/docker/recipes/redis.ts +1 -1
  184. package/src/docker/stack-id.test.ts +1 -1
  185. package/src/drivers/ai-anthropic.ts +212 -21
  186. package/src/drivers/ai-mock.ts +34 -1
  187. package/src/drivers/ai-openai-compatible.ts +67 -26
  188. package/src/drivers/ai-preconnect.ts +18 -0
  189. package/src/drivers/ai-stream.test.ts +62 -0
  190. package/src/drivers/ai-types.ts +20 -0
  191. package/src/drivers/journal-postgres.ts +455 -3
  192. package/src/drivers/meilisearch.ts +2 -0
  193. package/src/drivers/memory-ddl.test.ts +18 -0
  194. package/src/drivers/memory.ts +21 -1
  195. package/src/drivers/pg-rls.test.ts +31 -0
  196. package/src/drivers/pg-rls.ts +15 -0
  197. package/src/drivers/postgres.test.ts +9 -0
  198. package/src/drivers/postgres.ts +49 -11
  199. package/src/drivers/vault-builtin.test.ts +10 -0
  200. package/src/drivers/vault-builtin.ts +13 -1
  201. package/src/drivers/vault-remote-bag.ts +5 -1
  202. package/src/drivers/vault-types.ts +6 -1
  203. package/src/elements/ai/agent-event-slot.ts +27 -0
  204. package/src/elements/ai/approval-http.ts +207 -0
  205. package/src/elements/ai/approval.test.ts +741 -0
  206. package/src/elements/ai/approval.ts +228 -0
  207. package/src/elements/ai/decisions/bind.ts +163 -0
  208. package/src/elements/ai/decisions/certificate.test.ts +291 -0
  209. package/src/elements/ai/decisions/certificate.ts +377 -0
  210. package/src/elements/ai/decisions/certify.ts +314 -0
  211. package/src/elements/ai/decisions/decide.test.ts +953 -0
  212. package/src/elements/ai/decisions/e2e.test.ts +399 -0
  213. package/src/elements/ai/decisions/export.test.ts +120 -0
  214. package/src/elements/ai/decisions/export.ts +146 -0
  215. package/src/elements/ai/decisions/fixtures/openrouter-request.json +24 -0
  216. package/src/elements/ai/decisions/fixtures/openrouter-response.json +22 -0
  217. package/src/elements/ai/decisions/fixtures/typesafe-request.json +19 -0
  218. package/src/elements/ai/decisions/fixtures/typesafe-response.json +13 -0
  219. package/src/elements/ai/decisions/http.test.ts +154 -0
  220. package/src/elements/ai/decisions/http.ts +240 -0
  221. package/src/elements/ai/decisions/labels.test.ts +282 -0
  222. package/src/elements/ai/decisions/labels.ts +257 -0
  223. package/src/elements/ai/decisions/openrouter.live.test.ts +136 -0
  224. package/src/elements/ai/decisions/openrouter.ts +36 -0
  225. package/src/elements/ai/decisions/provider.ts +102 -0
  226. package/src/elements/ai/decisions/typesafe.ts +36 -0
  227. package/src/elements/ai/declare.ts +252 -1
  228. package/src/elements/ai/events.test.ts +379 -0
  229. package/src/elements/ai/events.ts +138 -0
  230. package/src/elements/ai/run-events.test.ts +461 -0
  231. package/src/elements/ai/run-events.ts +522 -0
  232. package/src/elements/ai/runtime.ts +1186 -105
  233. package/src/elements/ai/stream-turn.ts +214 -0
  234. package/src/elements/ai/stream.live.test.ts +76 -0
  235. package/src/elements/ai/subagent.test.ts +136 -0
  236. package/src/elements/ai.test.ts +497 -3
  237. package/src/elements/ai.ts +16 -1
  238. package/src/elements/clock/durable.ts +27 -1
  239. package/src/elements/store/emit-drizzle.ts +2 -2
  240. package/src/elements/store/live-default.test.ts +2 -0
  241. package/src/elements/store/live-http.test.ts +1 -0
  242. package/src/elements/store/resource-list-docs.test.ts +2 -2
  243. package/src/elements/store/resource.test.ts +3 -3
  244. package/src/elements/store/schema-decl.test.ts +1 -0
  245. package/src/elements/store/sql-condition.ts +24 -7
  246. package/src/elements/store/sql-session.test.ts +51 -0
  247. package/src/elements/store/sql-session.ts +111 -1
  248. package/src/elements/store/upsert-app.test.ts +1 -1
  249. package/src/elements/vault/audit.test.ts +137 -0
  250. package/src/elements/vault/audit.ts +106 -13
  251. package/src/elements/vault/boot-chain.ts +9 -1
  252. package/src/elements/vault/builtin-adapter.ts +33 -3
  253. package/src/i18n/catalogs/ar.ts +9 -0
  254. package/src/i18n/catalogs/en.ts +9 -0
  255. package/src/index.ts +2 -0
  256. package/src/kernel/abort-scope.ts +33 -2
  257. package/src/kernel/agent-event-store.ts +448 -0
  258. package/src/kernel/app.ts +294 -19
  259. package/src/kernel/auto-cache.test.ts +6 -6
  260. package/src/kernel/boot-bind/ai.ts +3 -0
  261. package/src/kernel/boot-bind/store.test.ts +26 -0
  262. package/src/kernel/boot-bind/store.ts +82 -6
  263. package/src/kernel/boot-bind/vault.ts +1 -0
  264. package/src/kernel/boot.ts +34 -5
  265. package/src/kernel/builtin-errors.ts +10 -0
  266. package/src/kernel/capability.ts +3 -0
  267. package/src/kernel/client-descriptor.ts +1 -0
  268. package/src/kernel/concurrency.test.ts +33 -0
  269. package/src/kernel/decision-label-store.ts +441 -0
  270. package/src/kernel/effects-stamping.test.ts +2 -2
  271. package/src/kernel/effects.test.ts +2 -1
  272. package/src/kernel/effects.ts +13 -1
  273. package/src/kernel/element-registries.ts +3 -0
  274. package/src/kernel/errors-compiler.ts +20 -0
  275. package/src/kernel/errors-text.ts +4 -0
  276. package/src/kernel/errors.ts +2 -0
  277. package/src/kernel/external-effects.test.ts +36 -0
  278. package/src/kernel/flow.test.ts +2 -3
  279. package/src/kernel/flow.ts +24 -0
  280. package/src/kernel/fx-decide.ts +761 -0
  281. package/src/kernel/fx-fetch.ts +5 -1
  282. package/src/kernel/fx.test.ts +12 -5
  283. package/src/kernel/fx.ts +316 -29
  284. package/src/kernel/http-frame.test.ts +94 -0
  285. package/src/kernel/http-frame.ts +203 -0
  286. package/src/kernel/idempotency-store.ts +578 -0
  287. package/src/kernel/idempotency.test.ts +556 -0
  288. package/src/kernel/idempotency.ts +304 -0
  289. package/src/kernel/index.ts +1 -0
  290. package/src/kernel/journal.ts +160 -0
  291. package/src/kernel/json-result.ts +10 -0
  292. package/src/kernel/pipeline.test.ts +45 -0
  293. package/src/kernel/sse-id.ts +32 -0
  294. package/src/manifest/diff.ts +1 -0
  295. package/src/manifest/types.ts +37 -2
  296. package/src/mcp/ai-tools.test.ts +202 -0
  297. package/src/mcp/authorization.ts +66 -0
  298. package/src/mcp/session.ts +3 -0
  299. package/src/mcp/tools.ts +168 -0
  300. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  301. package/src/plugins/otp.test.ts +65 -1
  302. package/src/plugins/otp.ts +59 -7
  303. package/src/release/absolute-regression.test.ts +63 -0
  304. package/src/release/http-graph.test.ts +27 -0
  305. package/src/release/http-graph.ts +95 -0
  306. package/src/release/index.ts +3 -0
  307. package/src/release/limits.ts +17 -2
  308. package/src/release/measure.ts +144 -28
  309. package/src/runs/collect.ts +4 -1
  310. package/src/runs/parquet.test.ts +27 -1
  311. package/src/runs/parquet.ts +19 -0
  312. package/src/runs/types.ts +31 -0
  313. package/src/runtime/json-code-block.test.ts +106 -47
  314. package/src/runtime/json-code-block.ts +1209 -46
  315. package/src/term.test.ts +13 -0
  316. package/src/term.ts +81 -49
  317. package/src/test/create-test-app.test.ts +3 -1
  318. package/src/test/create-test-app.ts +40 -0
  319. package/src/test/live-signals.test.ts +1 -0
  320. package/src/test/provisions.integration.test.ts +1 -0
  321. package/src/test/tenant-isolation.test.ts +1 -0
  322. package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +0 -1
  323. package/src/console/ui-next/dist/assets/access-page-Bgt9bq2r.js +0 -4
  324. package/src/console/ui-next/dist/assets/agent-disclosure-B43CXZZR.js +0 -1
  325. package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +0 -1
  326. package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +0 -1
  327. package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +0 -1
  328. package/src/console/ui-next/dist/assets/duration-tone-BmIR9FV8.js +0 -9
  329. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +0 -1
  330. package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +0 -1
  331. package/src/console/ui-next/dist/assets/flows-page-BMHs-IzK.js +0 -1
  332. package/src/console/ui-next/dist/assets/index-BKpaes3n.js +0 -63
  333. package/src/console/ui-next/dist/assets/index-VxoEz295.css +0 -2
  334. package/src/console/ui-next/dist/assets/observability-page-WnVLI-0j.js +0 -4
  335. package/src/console/ui-next/dist/assets/request-meta-DMbnAe3f.js +0 -1
  336. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +0 -1
  337. package/src/console/ui-next/dist/assets/store-page-KvFDingJ.js +0 -41
  338. package/src/console/ui-next/dist/assets/trace-detail-sheet-Htk8Cm9t.js +0 -2
  339. package/src/console/ui-next/dist/assets/tree-expand-toggle-CFMPWX4f.js +0 -55
  340. package/src/console/ui-next/dist/assets/units-page-C4NdNuxP.js +0 -1
  341. package/src/console/ui-next/dist/assets/use-vault-list-CT4-gajj.js +0 -1
  342. package/src/console/ui-next/dist/assets/vault-page-CBYl0LW_.js +0 -2
@@ -0,0 +1,36 @@
1
+ /**
2
+ * OpenRouter Decisions driver. `POST https://openrouter.ai/api/alpha/decisions`.
3
+ */
4
+
5
+ import { decisionHttp } from "./http.ts";
6
+ import type { DecisionProvider, DecisionRequest, DecisionResponse } from "./provider.ts";
7
+
8
+ /** Default OpenRouter decisions URL. */
9
+ export const OPENROUTER_DECISION_URL = "https://openrouter.ai/api/alpha/decisions";
10
+
11
+ /** Default Jev model id on OpenRouter. */
12
+ export const OPENROUTER_JEV_MODEL = "typesafe/jev-1.13";
13
+
14
+ /**
15
+ * OpenRouter decision provider. This is the default when a decision omits `model`.
16
+ *
17
+ * @param apiKey - `OPENROUTER_API_KEY`
18
+ * @param timeoutMs - Provider deadline
19
+ */
20
+ export function createOpenRouterDecisionProvider(
21
+ apiKey: string,
22
+ timeoutMs = 30_000,
23
+ ): DecisionProvider {
24
+ return {
25
+ id: "openrouter",
26
+ evaluate(request: DecisionRequest): Promise<DecisionResponse> {
27
+ return decisionHttp({
28
+ url: OPENROUTER_DECISION_URL,
29
+ apiKey,
30
+ request: { ...request, model: request.model || OPENROUTER_JEV_MODEL },
31
+ timeoutMs,
32
+ breakerKey: "openrouter",
33
+ });
34
+ },
35
+ };
36
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Decision provider contract. Chat drivers stay on {@link AiDriverId}.
3
+ * Boolean questions are `noul` on the wire.
4
+ */
5
+
6
+ /** Wire question sent in one batched request. */
7
+ export type WireQuestion =
8
+ | {
9
+ readonly type: "choice";
10
+ readonly instructions: string;
11
+ readonly criteria: Readonly<Record<string, string | null>>;
12
+ }
13
+ | {
14
+ readonly type: "score";
15
+ readonly instructions: string;
16
+ readonly criteria: readonly string[];
17
+ }
18
+ | {
19
+ readonly type: "noul";
20
+ readonly instructions: string;
21
+ readonly criteria?: { readonly true?: string; readonly false?: string };
22
+ };
23
+
24
+ /** One provider call. */
25
+ export interface DecisionRequest {
26
+ readonly model: string;
27
+ readonly state: unknown;
28
+ readonly questions: Readonly<Record<string, WireQuestion>>;
29
+ readonly signal?: AbortSignal;
30
+ }
31
+
32
+ /** Token usage reported by the provider. */
33
+ export interface DecisionUsage {
34
+ readonly inputTokens?: number;
35
+ readonly outputTokens?: number;
36
+ readonly cost?: number;
37
+ }
38
+
39
+ /** Parsed provider body. `model` is the resolved version. */
40
+ export interface DecisionResponse {
41
+ readonly model: string;
42
+ readonly provider?: string;
43
+ readonly answers: Readonly<Record<string, unknown>>;
44
+ readonly usage: DecisionUsage;
45
+ }
46
+
47
+ /** Driver id for a decision provider. */
48
+ export type DecisionDriverId = "typesafe" | "openrouter";
49
+
50
+ /**
51
+ * One System One decision call.
52
+ */
53
+ export interface DecisionProvider {
54
+ readonly id: DecisionDriverId;
55
+ /**
56
+ * Evaluate every question against `state` in one request.
57
+ *
58
+ * @param request - Model, state, and questions
59
+ */
60
+ evaluate(request: DecisionRequest): Promise<DecisionResponse>;
61
+ }
62
+
63
+ /** Statuses that are the caller's problem. They do not open the breaker. */
64
+ export type DecisionRequestStatus = 400 | 401 | 402 | 403 | 404 | 422;
65
+
66
+ /** Auth, billing, or a body the provider rejected. Does not open the breaker. */
67
+ export class DecisionRequestError extends Error {
68
+ readonly status: DecisionRequestStatus;
69
+ /**
70
+ * @param status - HTTP status
71
+ * @param message - Provider body, truncated by the caller
72
+ */
73
+ constructor(status: DecisionRequestStatus, message: string) {
74
+ super(message);
75
+ this.name = "DecisionRequestError";
76
+ this.status = status;
77
+ }
78
+ }
79
+
80
+ /** The decision is missing a configured secret. Not an outage. */
81
+ export class DecisionConfigError extends Error {
82
+ readonly secret: string;
83
+ /**
84
+ * @param secret - Secret contract name
85
+ */
86
+ constructor(secret: string) {
87
+ super(`fx.decide: secret "${secret}" is not configured`);
88
+ this.name = "DecisionConfigError";
89
+ this.secret = secret;
90
+ }
91
+ }
92
+
93
+ /** The breaker is open. Callers take the non-auto path. */
94
+ export class DecisionOutageError extends Error {
95
+ /**
96
+ * @param message - Why the call was not sent
97
+ */
98
+ constructor(message: string) {
99
+ super(message);
100
+ this.name = "DecisionOutageError";
101
+ }
102
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * TypeSafe System One driver. `POST https://api.typesafe.ai/v1/systemone`.
3
+ */
4
+
5
+ import { decisionHttp } from "./http.ts";
6
+ import type { DecisionProvider, DecisionRequest, DecisionResponse } from "./provider.ts";
7
+
8
+ /** Default TypeSafe evaluation URL. */
9
+ export const TYPESAFE_DECISION_URL = "https://api.typesafe.ai/v1/systemone";
10
+
11
+ /** Pinned Jev version when the author selects the TypeSafe driver. */
12
+ export const TYPESAFE_JEV_MODEL = "jev-1.13.0";
13
+
14
+ /**
15
+ * TypeSafe decision provider.
16
+ *
17
+ * @param apiKey - `TYPESAFE_API_KEY`
18
+ * @param timeoutMs - Provider deadline
19
+ */
20
+ export function createTypesafeDecisionProvider(
21
+ apiKey: string,
22
+ timeoutMs = 30_000,
23
+ ): DecisionProvider {
24
+ return {
25
+ id: "typesafe",
26
+ evaluate(request: DecisionRequest): Promise<DecisionResponse> {
27
+ return decisionHttp({
28
+ url: TYPESAFE_DECISION_URL,
29
+ apiKey,
30
+ request: { ...request, model: request.model || TYPESAFE_JEV_MODEL },
31
+ timeoutMs,
32
+ breakerKey: "typesafe",
33
+ });
34
+ },
35
+ };
36
+ }
@@ -6,6 +6,7 @@
6
6
 
7
7
  import {
8
8
  aiAgentRegistry,
9
+ aiDecisionRegistry,
9
10
  aiEmbedRegistry,
10
11
  aiMcpServerRegistry,
11
12
  aiModelRegistry,
@@ -53,6 +54,8 @@ export interface AiPromptOptions {
53
54
  readonly version?: number;
54
55
  readonly evals?: string;
55
56
  readonly budget?: AiBudgetDecl;
57
+ /** One schema-mismatch retry. Default `0` throws on the first mismatch. */
58
+ readonly repair?: 0 | 1;
56
59
  /**
57
60
  * Ordered recovery chain of logical model names for this command.
58
61
  * Resolved as `ask.via ?? prompt.via ?? [prompt.model]`.
@@ -70,11 +73,103 @@ export interface AiEmbedOptions {
70
73
  readonly into?: { readonly name: string; readonly facet?: string } | string;
71
74
  }
72
75
 
76
+ /** Who may have to approve one agent tool before it runs. */
77
+ export type AiToolApproval =
78
+ | boolean
79
+ | ((input: unknown, ctx: { readonly auth?: unknown; readonly tenant?: unknown }) => boolean);
80
+
81
+ /** One agent tool, optionally held for approval. */
82
+ export interface AiAgentToolOptions {
83
+ readonly name: string;
84
+ /** `true` always waits. A function waits only when it returns true. */
85
+ readonly approval?: AiToolApproval;
86
+ /** Gate that may approve or deny. Required when {@link approval} is set. */
87
+ readonly gate?: { readonly name: string } | string;
88
+ /** How long to wait before a deny. Default `24h`. */
89
+ readonly timeout?: string;
90
+ }
91
+
92
+ /**
93
+ * Choice answer a Flow can receive. Author keys, plus `none_of_these`.
94
+ *
95
+ * @typeParam Options - Author option map
96
+ */
97
+ export type DecisionChoiceValue<Options extends Readonly<Record<string, string | null>>> =
98
+ | keyof Options
99
+ | "none_of_these";
100
+
101
+ /** One choice question. `none_of_these` is injected on the wire. */
102
+ export interface AiChoiceQuestion {
103
+ readonly kind: "choice";
104
+ readonly instructions: string;
105
+ readonly options: Readonly<Record<string, string | null>>;
106
+ }
107
+
108
+ /** One ordered score question. */
109
+ export interface AiScoreQuestion {
110
+ readonly kind: "score";
111
+ readonly instructions: string;
112
+ readonly levels: readonly string[];
113
+ }
114
+
115
+ /** One yes/no question. The wire type is `noul`. */
116
+ export interface AiBooleanQuestion {
117
+ readonly kind: "boolean";
118
+ readonly instructions: string;
119
+ readonly criteria?: { readonly true?: string; readonly false?: string };
120
+ }
121
+
122
+ /** A question inside `ai.decision({ ask })`. */
123
+ export type AiDecisionQuestion = AiChoiceQuestion | AiScoreQuestion | AiBooleanQuestion;
124
+
125
+ /** Autonomy grant. Present only in the lockfile's certificate. */
126
+ export interface AiDecisionAutonomy {
127
+ readonly maxError: number;
128
+ /** Audit sample rate in `[0, 1]`. Required whenever autonomy is set. */
129
+ readonly audit: number;
130
+ /** Learn-then-Test family-wise level. Default `0.1`. */
131
+ readonly risk?: number;
132
+ }
133
+
134
+ /** Options for {@link ai.decision}. Exactly one of `review` and `onUncertain` is required. */
135
+ export interface AiDecisionOptions {
136
+ readonly model?: AiModelDecl | string;
137
+ readonly in?: unknown;
138
+ readonly ask: Readonly<Record<string, AiDecisionQuestion>>;
139
+ readonly autonomy?: AiDecisionAutonomy;
140
+ readonly review?: { readonly name: string } | string;
141
+ readonly onUncertain?: "abstain";
142
+ readonly locale?: (input: unknown) => string | undefined;
143
+ readonly evals?: string;
144
+ /** `openrouter` (default) or `typesafe`. */
145
+ readonly driverId?: "openrouter" | "typesafe";
146
+ readonly timeout?: AiTimeout;
147
+ }
148
+
149
+ /** Declared decision handle. */
150
+ export interface AiDecisionDecl {
151
+ readonly kind: "decision";
152
+ readonly name: string;
153
+ readonly ask: Readonly<Record<string, AiDecisionQuestion>>;
154
+ readonly mode: "review" | "abstain";
155
+ readonly review?: string;
156
+ readonly model?: string;
157
+ readonly driverId: "openrouter" | "typesafe";
158
+ readonly autonomy?: AiDecisionAutonomy;
159
+ readonly evals?: string;
160
+ readonly timeout?: AiTimeout;
161
+ readonly locale?: (input: unknown) => string | undefined;
162
+ /** Declared `in` schema. Label export keeps only these fields. */
163
+ readonly inputSchema?: unknown;
164
+ }
165
+
73
166
  /** Options for {@link ai.agent}. */
74
167
  export interface AiAgentOptions {
75
168
  readonly model?: AiModelDecl | string;
76
- readonly tools?: readonly ({ readonly name: string } | string)[];
169
+ readonly tools?: readonly (AiAgentToolOptions | { readonly name: string } | string)[];
77
170
  readonly maxSteps?: number;
171
+ /** Nested agent depth. Default 3. */
172
+ readonly maxDepth?: number;
78
173
  readonly budget?: AiBudgetDecl;
79
174
  }
80
175
 
@@ -104,6 +199,8 @@ export interface AiPromptDecl {
104
199
  readonly version?: number;
105
200
  readonly evals?: string;
106
201
  readonly budget?: AiBudgetDecl;
202
+ /** One schema-mismatch retry. Default `0`. */
203
+ readonly repair?: 0 | 1;
107
204
  readonly via?: readonly string[];
108
205
  readonly timeout?: AiTimeout;
109
206
  readonly model?: string;
@@ -119,12 +216,24 @@ export interface AiEmbedDecl {
119
216
  readonly into?: string;
120
217
  }
121
218
 
219
+ /** Approval held on one tool. The predicate stays in code. */
220
+ export interface AiAgentApprovalDecl {
221
+ readonly gate: string;
222
+ readonly timeout: string;
223
+ readonly when?: (
224
+ input: unknown,
225
+ ctx: { readonly auth?: unknown; readonly tenant?: unknown },
226
+ ) => boolean;
227
+ }
228
+
122
229
  /** Declared agent whose tools are flows. */
123
230
  export interface AiAgentDecl {
124
231
  readonly kind: "agent";
125
232
  readonly name: string;
126
233
  readonly tools: readonly string[];
234
+ readonly approvals?: Readonly<Record<string, AiAgentApprovalDecl>>;
127
235
  readonly maxSteps?: number;
236
+ readonly maxDepth?: number;
128
237
  readonly model?: string;
129
238
  readonly budget?: AiBudgetDecl;
130
239
  }
@@ -186,6 +295,10 @@ function toolName(tool: { readonly name: string } | string): string {
186
295
  return typeof tool === "string" ? tool : tool.name;
187
296
  }
188
297
 
298
+ function gateName(gate: { readonly name: string } | string): string {
299
+ return typeof gate === "string" ? gate : gate.name;
300
+ }
301
+
189
302
  /**
190
303
  * Shape of the {@link ai} element namespace.
191
304
  */
@@ -211,6 +324,37 @@ export interface AiNamespace {
211
324
  * @param options - Tools / maxSteps / model / budget
212
325
  */
213
326
  agent(name: string, options?: AiAgentOptions): AiAgentDecl;
327
+ /**
328
+ * Declare a decision. Exactly one of `review` and `onUncertain: "abstain"`.
329
+ *
330
+ * @param name - Decision id
331
+ * @param options - Questions, mode, and optional autonomy
332
+ */
333
+ decision(name: string, options: AiDecisionOptions): AiDecisionDecl;
334
+ /**
335
+ * A choice question. The answer union is {@link DecisionChoiceValue}: author keys or `none_of_these`.
336
+ *
337
+ * @param instructions - What to decide
338
+ * @param options - At most 254 author options
339
+ */
340
+ choice(instructions: string, options: Readonly<Record<string, string | null>>): AiChoiceQuestion;
341
+ /**
342
+ * An ordered score question.
343
+ *
344
+ * @param instructions - What to rate
345
+ * @param levels - 2–10 level descriptions, low to high
346
+ */
347
+ score(instructions: string, levels: readonly string[]): AiScoreQuestion;
348
+ /**
349
+ * A yes/no question. Sent as `noul`.
350
+ *
351
+ * @param instructions - The yes/no question
352
+ * @param criteria - Optional true/false descriptions
353
+ */
354
+ boolean(
355
+ instructions: string,
356
+ criteria?: { readonly true?: string; readonly false?: string },
357
+ ): AiBooleanQuestion;
214
358
  /**
215
359
  * Declare an external MCP server whose allowlisted tools join `fx.call`.
216
360
  *
@@ -228,6 +372,7 @@ export function listAiDecls(): {
228
372
  readonly prompts: readonly AiPromptDecl[];
229
373
  readonly embeds: readonly AiEmbedDecl[];
230
374
  readonly agents: readonly AiAgentDecl[];
375
+ readonly decisions: readonly AiDecisionDecl[];
231
376
  readonly mcpServers: readonly AiMcpServerDecl[];
232
377
  } {
233
378
  return {
@@ -235,6 +380,7 @@ export function listAiDecls(): {
235
380
  prompts: aiPromptRegistry.slice(),
236
381
  embeds: aiEmbedRegistry.slice(),
237
382
  agents: aiAgentRegistry.slice(),
383
+ decisions: aiDecisionRegistry.slice(),
238
384
  mcpServers: aiMcpServerRegistry.slice(),
239
385
  };
240
386
  }
@@ -247,6 +393,7 @@ export function resetAiDecls(): void {
247
393
  aiPromptRegistry.length = 0;
248
394
  aiEmbedRegistry.length = 0;
249
395
  aiAgentRegistry.length = 0;
396
+ aiDecisionRegistry.length = 0;
250
397
  aiMcpServerRegistry.length = 0;
251
398
  }
252
399
 
@@ -290,6 +437,7 @@ export const ai: AiNamespace = {
290
437
  ...(promptOpts.version !== undefined ? { version: promptOpts.version } : {}),
291
438
  ...(promptOpts.evals !== undefined ? { evals: promptOpts.evals } : {}),
292
439
  ...(promptOpts.budget !== undefined ? { budget: promptOpts.budget } : {}),
440
+ ...(promptOpts.repair !== undefined ? { repair: promptOpts.repair } : {}),
293
441
  ...(promptOpts.via !== undefined ? { via: promptOpts.via } : {}),
294
442
  ...(promptOpts.timeout !== undefined ? { timeout: promptOpts.timeout } : {}),
295
443
  ...(promptOpts.in !== undefined ? { in: promptOpts.in } : {}),
@@ -329,11 +477,28 @@ export const ai: AiNamespace = {
329
477
  * @param options - Tools / maxSteps / model / budget
330
478
  */
331
479
  agent(name: string, options: AiAgentOptions = {}): AiAgentDecl {
480
+ const approvals: Record<string, AiAgentApprovalDecl> = {};
481
+ for (const tool of options.tools ?? []) {
482
+ if (typeof tool === "string" || !("approval" in tool) || tool.approval === undefined) {
483
+ continue;
484
+ }
485
+ if (tool.approval === false) continue;
486
+ if (tool.gate === undefined) {
487
+ throw new TypeError(`ai.agent("${name}"): tool "${toolName(tool)}" approval requires gate`);
488
+ }
489
+ approvals[toolName(tool)] = {
490
+ gate: gateName(tool.gate),
491
+ timeout: tool.timeout ?? "24h",
492
+ ...(typeof tool.approval === "function" ? { when: tool.approval } : {}),
493
+ };
494
+ }
332
495
  const decl: AiAgentDecl = {
333
496
  kind: "agent",
334
497
  name,
335
498
  tools: (options.tools ?? []).map(toolName),
499
+ ...(Object.keys(approvals).length > 0 ? { approvals } : {}),
336
500
  ...(options.maxSteps !== undefined ? { maxSteps: options.maxSteps } : {}),
501
+ ...(options.maxDepth !== undefined ? { maxDepth: options.maxDepth } : {}),
337
502
  ...(options.budget !== undefined ? { budget: options.budget } : {}),
338
503
  ...(typeof options.model === "string"
339
504
  ? { model: options.model }
@@ -345,6 +510,27 @@ export const ai: AiNamespace = {
345
510
  return decl;
346
511
  },
347
512
 
513
+ decision(name: string, options: AiDecisionOptions): AiDecisionDecl {
514
+ const decl = buildDecisionDecl(name, options);
515
+ aiDecisionRegistry.push(decl);
516
+ return decl;
517
+ },
518
+
519
+ choice(instructions: string, options: Readonly<Record<string, string | null>>): AiChoiceQuestion {
520
+ return { kind: "choice", instructions, options };
521
+ },
522
+
523
+ score(instructions: string, levels: readonly string[]): AiScoreQuestion {
524
+ return { kind: "score", instructions, levels };
525
+ },
526
+
527
+ boolean(
528
+ instructions: string,
529
+ criteria?: { readonly true?: string; readonly false?: string },
530
+ ): AiBooleanQuestion {
531
+ return { kind: "boolean", instructions, ...(criteria !== undefined ? { criteria } : {}) };
532
+ },
533
+
348
534
  /**
349
535
  * Declare an external MCP server whose allowlisted tools join `fx.call`.
350
536
  *
@@ -386,3 +572,68 @@ export const ai: AiNamespace = {
386
572
  return decl;
387
573
  },
388
574
  };
575
+
576
+ /**
577
+ * Validate a decision and return the registered shape.
578
+ *
579
+ * @param name - Decision id
580
+ * @param options - Author options
581
+ */
582
+ export function buildDecisionDecl(name: string, options: AiDecisionOptions): AiDecisionDecl {
583
+ if (!name) throw new TypeError("ai.decision: name is required");
584
+ const keys = Object.keys(options.ask ?? {});
585
+ if (keys.length === 0) throw new TypeError(`ai.decision("${name}"): ask is empty`);
586
+ const hasReview = options.review !== undefined;
587
+ const abstain = options.onUncertain === "abstain";
588
+ if (hasReview === abstain) {
589
+ throw new TypeError(
590
+ `ai.decision("${name}"): declare exactly one of review or onUncertain: "abstain"`,
591
+ );
592
+ }
593
+ if (options.autonomy !== undefined && options.autonomy.audit === undefined) {
594
+ throw new TypeError(`ai.decision("${name}"): autonomy requires audit`);
595
+ }
596
+ for (const key of keys) {
597
+ if (key === "meta" || key === "$") {
598
+ throw new TypeError(`ai.decision("${name}"): question id "${key}" is reserved`);
599
+ }
600
+ const question = options.ask[key];
601
+ if (!question) continue;
602
+ if (question.kind === "choice") {
603
+ const count = Object.keys(question.options).length;
604
+ if (count > 254) {
605
+ throw new TypeError(
606
+ `ai.decision("${name}"): choice "${key}" has ${count} options; max 254`,
607
+ );
608
+ }
609
+ if (Object.prototype.hasOwnProperty.call(question.options, "none_of_these")) {
610
+ throw new TypeError(`ai.decision("${name}"): choice "${key}" must not set none_of_these`);
611
+ }
612
+ }
613
+ if (question.kind === "score") {
614
+ const count = question.levels.length;
615
+ if (count < 2 || count > 10) {
616
+ throw new TypeError(`ai.decision("${name}"): score "${key}" needs 2–10 levels`);
617
+ }
618
+ }
619
+ }
620
+ const review = typeof options.review === "string" ? options.review : options.review?.name;
621
+ const model =
622
+ typeof options.model === "string"
623
+ ? options.model
624
+ : (options.model?.model ?? options.model?.name);
625
+ return {
626
+ kind: "decision",
627
+ name,
628
+ ask: options.ask,
629
+ mode: abstain ? "abstain" : "review",
630
+ ...(review !== undefined ? { review } : {}),
631
+ ...(model !== undefined ? { model } : {}),
632
+ driverId: options.driverId ?? "openrouter",
633
+ ...(options.autonomy !== undefined ? { autonomy: options.autonomy } : {}),
634
+ ...(options.evals !== undefined ? { evals: options.evals } : {}),
635
+ ...(options.timeout !== undefined ? { timeout: options.timeout } : {}),
636
+ ...(options.locale !== undefined ? { locale: options.locale } : {}),
637
+ ...(options.in !== undefined ? { inputSchema: options.in } : {}),
638
+ };
639
+ }