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,193 @@
1
+ /**
2
+ * React hook for one agent run: send, stream, approve, and follow the resume.
3
+ *
4
+ * Import from `okengine/client-react`. This file does not import `okengine/client`.
5
+ */
6
+
7
+ import { useCallback, useEffect, useRef, useState } from "react";
8
+ import { approve, deny, readAgentEvents, type ParsedAgentEvent } from "../client/agent.ts";
9
+
10
+ /** Pending tool approval from a `RUN_FINISHED` interrupt. */
11
+ export interface AgentPendingApproval {
12
+ readonly id: string;
13
+ readonly tool?: string;
14
+ readonly args?: unknown;
15
+ }
16
+
17
+ /** State returned by {@link useAgentRun}. */
18
+ export interface AgentRunState {
19
+ readonly events: readonly ParsedAgentEvent[];
20
+ readonly text: string;
21
+ readonly pending: AgentPendingApproval | null;
22
+ readonly status: "idle" | "streaming" | "approval" | "done" | "error";
23
+ send(message: string): Promise<void>;
24
+ approve(args?: unknown): Promise<void>;
25
+ deny(reason?: string): Promise<void>;
26
+ }
27
+
28
+ /** Routes the hook calls. */
29
+ export interface UseAgentRunOptions {
30
+ /** POST that starts `fx.run(..., { stream: true })`. */
31
+ readonly sendUrl: string;
32
+ /** POST `/agent/approvals/approve`. */
33
+ readonly approveUrl: string;
34
+ /** POST `/agent/approvals/deny`. */
35
+ readonly denyUrl: string;
36
+ /** Follow URL for a run id. */
37
+ followUrl(runId: string): string;
38
+ readonly fetch?: typeof fetch;
39
+ readonly headers?: Record<string, string>;
40
+ }
41
+
42
+ /**
43
+ * Send one message and keep the events, the text so far, and a pending approval.
44
+ *
45
+ * After an interrupt, the hook follows once with `Last-Event-ID`. Approve and
46
+ * deny post the decision and leave that follow in place. Unmount aborts it.
47
+ *
48
+ * @param options - Send, approve, deny, and follow URLs
49
+ */
50
+ export function useAgentRun(options: UseAgentRunOptions): AgentRunState {
51
+ const [events, setEvents] = useState<ParsedAgentEvent[]>([]);
52
+ const [text, setText] = useState("");
53
+ const [pending, setPending] = useState<AgentPendingApproval | null>(null);
54
+ const [status, setStatus] = useState<AgentRunState["status"]>("idle");
55
+ const lastId = useRef<string | undefined>(undefined);
56
+ const runIdRef = useRef<string | undefined>(undefined);
57
+ const seenInterrupt = useRef<string | undefined>(undefined);
58
+ const followedRun = useRef<string | undefined>(undefined);
59
+ const followTask = useRef<Promise<void> | undefined>(undefined);
60
+ const abortRef = useRef<AbortController | undefined>(undefined);
61
+
62
+ useEffect(() => {
63
+ const controller = new AbortController();
64
+ abortRef.current = controller;
65
+ return () => {
66
+ controller.abort();
67
+ abortRef.current = undefined;
68
+ };
69
+ }, []);
70
+
71
+ const consume = useCallback(
72
+ async (source: Response | string, lastEventId?: string): Promise<string | undefined> => {
73
+ let followed: string | undefined;
74
+ for await (const event of readAgentEvents(source, {
75
+ fetch: options.fetch,
76
+ headers: options.headers,
77
+ signal: abortRef.current?.signal,
78
+ ...(lastEventId !== undefined ? { lastEventId } : {}),
79
+ onId(id) {
80
+ lastId.current = id;
81
+ },
82
+ })) {
83
+ if (
84
+ event.type === "RUN_FINISHED" &&
85
+ event.outcome?.type === "interrupt" &&
86
+ seenInterrupt.current === event.outcome.interrupts[0]?.id
87
+ ) {
88
+ continue;
89
+ }
90
+ setEvents((prev) => [...prev, event]);
91
+ if (event.type === "TEXT_MESSAGE_CONTENT") {
92
+ setText((prev) => prev + event.delta);
93
+ }
94
+ if (event.type === "RUN_STARTED" && "runId" in event) {
95
+ runIdRef.current = event.runId;
96
+ followed = event.runId;
97
+ }
98
+ if (event.type === "RUN_FINISHED" && event.outcome?.type === "interrupt") {
99
+ const first = event.outcome.interrupts[0];
100
+ const payload = first?.payload as { tool?: string; args?: unknown } | undefined;
101
+ if (first) {
102
+ seenInterrupt.current = first.id;
103
+ setPending({
104
+ id: first.id,
105
+ ...(payload?.tool !== undefined ? { tool: payload.tool } : {}),
106
+ ...(payload?.args !== undefined ? { args: payload.args } : {}),
107
+ });
108
+ setStatus("approval");
109
+ }
110
+ runIdRef.current = event.runId;
111
+ return event.runId;
112
+ }
113
+ if (event.type === "RUN_FINISHED" || event.type === "RUN_ERROR") {
114
+ setPending(null);
115
+ setStatus(event.type === "RUN_ERROR" ? "error" : "done");
116
+ if (event.type === "RUN_FINISHED") runIdRef.current = event.runId;
117
+ return undefined;
118
+ }
119
+ }
120
+ return followed;
121
+ },
122
+ [options.fetch, options.headers],
123
+ );
124
+
125
+ const followUntilDone = useCallback(
126
+ async (runId: string) => {
127
+ if (followedRun.current === runId) return;
128
+ followedRun.current = runId;
129
+ await consume(options.followUrl(runId), lastId.current);
130
+ },
131
+ [consume, options.followUrl],
132
+ );
133
+
134
+ const send = useCallback(
135
+ async (message: string) => {
136
+ setStatus("streaming");
137
+ setText("");
138
+ setEvents([]);
139
+ setPending(null);
140
+ lastId.current = undefined;
141
+ seenInterrupt.current = undefined;
142
+ followedRun.current = undefined;
143
+ const fetcher = options.fetch ?? fetch;
144
+ const response = await fetcher(options.sendUrl, {
145
+ method: "POST",
146
+ headers: { "content-type": "application/json", ...headersOf(options.headers) },
147
+ body: JSON.stringify({ message }),
148
+ signal: abortRef.current?.signal,
149
+ });
150
+ const runId = await consume(response);
151
+ if (runId) followTask.current = followUntilDone(runId);
152
+ },
153
+ [consume, followUntilDone, options.fetch, options.headers, options.sendUrl],
154
+ );
155
+
156
+ const approvePending = useCallback(
157
+ async (args?: unknown) => {
158
+ if (!pending) return;
159
+ await approve(options.approveUrl, pending.id, args, {
160
+ fetch: options.fetch,
161
+ headers: options.headers,
162
+ });
163
+ setStatus("streaming");
164
+ },
165
+ [options.approveUrl, options.fetch, options.headers, pending],
166
+ );
167
+
168
+ const denyPending = useCallback(
169
+ async (reason?: string) => {
170
+ if (!pending) return;
171
+ await deny(options.denyUrl, pending.id, reason, {
172
+ fetch: options.fetch,
173
+ headers: options.headers,
174
+ });
175
+ setStatus("streaming");
176
+ },
177
+ [options.denyUrl, options.fetch, options.headers, pending],
178
+ );
179
+
180
+ return {
181
+ events,
182
+ text,
183
+ pending,
184
+ status,
185
+ send,
186
+ approve: approvePending,
187
+ deny: denyPending,
188
+ };
189
+ }
190
+
191
+ function headersOf(headers: Record<string, string> | undefined): Record<string, string> {
192
+ return headers ?? {};
193
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Agent tool approval is a Manifest flag. The predicate stays in author code.
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import { extractFromSources } from "./extract.ts";
7
+
8
+ describe("extract agent approval", () => {
9
+ test("approval: true is recorded and a plain tool is not", async () => {
10
+ const manifest = await extractFromSources({
11
+ "src/flows/assist.ts": `
12
+ import { ai } from "okengine";
13
+ export const support = ai.agent("support", {
14
+ tools: [{ name: "billing.refund", approval: true, gate: "ops" }, "billing.lookup"],
15
+ });
16
+ `,
17
+ });
18
+ expect(manifest.ai?.agents?.support?.tools).toEqual(["billing.refund", "billing.lookup"]);
19
+ expect(manifest.ai?.agents?.support?.approvals).toEqual({ "billing.refund": true });
20
+ });
21
+
22
+ test("fx.run asks the agent and an agent tool is a call", async () => {
23
+ const manifest = await extractFromSources({
24
+ "src/flows/assist.ts": `
25
+ import { ai, flow, on, http } from "okengine";
26
+ const lookup = ai.agent("lookup", { tools: [] });
27
+ const support = ai.agent("support", { tools: [lookup] });
28
+ export const assist = on(http.post("/assist"), flow("assist", {
29
+ do: async (_input, fx) => fx.run(support, { message: "hi" }),
30
+ }));
31
+ `,
32
+ });
33
+ expect(manifest.flows?.assist?.effects?.asks).toContain("support");
34
+ expect(manifest.flows?.assist?.effects?.calls).toContain("lookup");
35
+ });
36
+ });
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Prompt `repair` is copied onto the Manifest the same way as `budget`.
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import { extractFromSources } from "./extract.ts";
7
+
8
+ describe("extract prompt repair", () => {
9
+ test("repair: 1 is recorded and repair: 0 is omitted", async () => {
10
+ const manifest = await extractFromSources({
11
+ "src/flows/triage.ts": `
12
+ import { ai } from "okengine";
13
+ export const smart = ai.model("smart", { provider: "mock" });
14
+ smart.prompt("ticket-triage", { repair: 1, budget: { maxCostPerCall: 0.02 } });
15
+ smart.prompt("plain", { repair: 0 });
16
+ `,
17
+ });
18
+ expect(manifest.ai?.prompts?.["ticket-triage"]?.repair).toBe(1);
19
+ expect(manifest.ai?.prompts?.["ticket-triage"]?.budget?.maxCostPerCall).toBe(0.02);
20
+ expect(manifest.ai?.prompts?.plain?.repair).toBeUndefined();
21
+ });
22
+ });
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Decision extraction: exclusive modes, and review cannot sit on HTTP.
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import { extractFromSources } from "./extract.ts";
7
+
8
+ const header = `const triage = ai.decision("triage", {\n`;
9
+
10
+ describe("ai.decision extract", () => {
11
+ test("review and abstain together fail to compile", async () => {
12
+ await expect(
13
+ extractFromSources({
14
+ "src/flows/run.ts": `${header} review: "ops",\n onUncertain: "abstain",\n ask: { team: ai.choice("which", { a: "A" }) },\n});\n`,
15
+ }),
16
+ ).rejects.toThrow(/exactly one of review/);
17
+ });
18
+
19
+ test("an HTTP trigger that reviews fails to compile", async () => {
20
+ await expect(
21
+ extractFromSources({
22
+ "src/flows/run.ts": `
23
+ const triage = ai.decision("triage", {
24
+ review: "ops",
25
+ ask: { team: ai.choice("which", { a: "A" }) },
26
+ });
27
+ on(http.post("/t"), flow("run", {
28
+ durable: true,
29
+ do: async (input, fx) => fx.decide(triage, input),
30
+ }));
31
+ `,
32
+ }),
33
+ ).rejects.toThrow(/fx\.emit/);
34
+ });
35
+
36
+ test("choice, score, and duplicate names fail at compile time", async () => {
37
+ const many = Array.from({ length: 255 }, (_, i) => `o${i}: "x"`).join(", ");
38
+ await expect(
39
+ extractFromSources({
40
+ "src/flows/run.ts": `${header} onUncertain: "abstain",\n ask: { team: ai.choice("which", { ${many} }) },\n});\n`,
41
+ }),
42
+ ).rejects.toThrow(/max 254/);
43
+ await expect(
44
+ extractFromSources({
45
+ "src/flows/run.ts": `${header} onUncertain: "abstain",\n ask: { team: ai.choice("which", { none_of_these: null }) },\n});\n`,
46
+ }),
47
+ ).rejects.toThrow(/none_of_these/);
48
+ await expect(
49
+ extractFromSources({
50
+ "src/flows/run.ts": `${header} onUncertain: "abstain",\n ask: { rank: ai.score("rank", ["only"]) },\n});\n`,
51
+ }),
52
+ ).rejects.toThrow(/2–10 levels/);
53
+ await expect(
54
+ extractFromSources({
55
+ "src/flows/a.ts": `${header} onUncertain: "abstain",\n ask: { team: ai.choice("which", { a: "A", b: "B" }) },\n});\n`,
56
+ "src/flows/b.ts": `${header} onUncertain: "abstain",\n ask: { team: ai.choice("which", { a: "A", b: "B" }) },\n});\n`,
57
+ }),
58
+ ).rejects.toThrow(/duplicate decision name/);
59
+ });
60
+
61
+ test("review records the gate name", async () => {
62
+ const manifest = await extractFromSources({
63
+ "src/flows/run.ts": `
64
+ const ops = gate.policy("ops", () => true);
65
+ const triage = ai.decision("triage", {
66
+ review: ops,
67
+ ask: { team: ai.choice("which", { a: "A", b: "B" }) },
68
+ });
69
+ on(signal.once("job"), flow("run", {
70
+ durable: true,
71
+ do: async (input, fx) => fx.decide(triage, input),
72
+ }));
73
+ `,
74
+ });
75
+ expect(manifest.ai?.decisions?.triage?.review).toBe("ops");
76
+ });
77
+
78
+ test("review resolves a variable to the gate's declared name", async () => {
79
+ const manifest = await extractFromSources({
80
+ "src/flows/run.ts": `
81
+ const someVar = gate.policy("ops", () => true);
82
+ const triage = ai.decision("triage", {
83
+ review: someVar,
84
+ ask: { team: ai.choice("which", { a: "A", b: "B" }) },
85
+ });
86
+ on(signal.once("job"), flow("run", {
87
+ durable: true,
88
+ do: async (input, fx) => fx.decide(triage, input),
89
+ }));
90
+ `,
91
+ });
92
+ expect(manifest.ai?.decisions?.triage?.review).toBe("ops");
93
+ });
94
+
95
+ test("autonomy and evals survive extraction", async () => {
96
+ const manifest = await extractFromSources({
97
+ "src/flows/run.ts": `
98
+ const triage = ai.decision("triage", {
99
+ onUncertain: "abstain",
100
+ evals: "evals/triage.jsonl",
101
+ autonomy: { maxError: 0.05, audit: 0.1, risk: 0.1 },
102
+ ask: { team: ai.choice("which", { a: "A", b: "B" }) },
103
+ });
104
+ `,
105
+ });
106
+ expect(manifest.ai?.decisions?.triage?.evals).toBe("evals/triage.jsonl");
107
+ expect(manifest.ai?.decisions?.triage?.autonomy).toEqual({
108
+ maxError: 0.05,
109
+ audit: 0.1,
110
+ risk: 0.1,
111
+ });
112
+ });
113
+
114
+ test("choice options passed through a variable fail to compile", async () => {
115
+ await expect(
116
+ extractFromSources({
117
+ "src/flows/run.ts": `
118
+ const options = { a: "A", b: "B" };
119
+ ai.decision("triage", {
120
+ onUncertain: "abstain",
121
+ ask: { team: ai.choice("which", options) },
122
+ });
123
+ `,
124
+ }),
125
+ ).rejects.toThrow(/object literal/);
126
+ });
127
+
128
+ test("score levels and a whole question resolve a same-file const", async () => {
129
+ const manifest = await extractFromSources({
130
+ "src/flows/run.ts": `
131
+ const levels = ["low", "high"];
132
+ const team = ai.choice("which", { a: "A", b: "B" });
133
+ ai.decision("triage", {
134
+ onUncertain: "abstain",
135
+ ask: { team, rank: ai.score("rank", levels) },
136
+ });
137
+ `,
138
+ });
139
+ expect(manifest.ai?.decisions?.triage?.questions).toEqual(["team", "rank"]);
140
+ });
141
+
142
+ test("an unresolved score or question fails to compile", async () => {
143
+ await expect(
144
+ extractFromSources({
145
+ "src/flows/run.ts": `
146
+ ai.decision("triage", {
147
+ onUncertain: "abstain",
148
+ ask: { rank: ai.score("rank", missing) },
149
+ });
150
+ `,
151
+ }),
152
+ ).rejects.toThrow(/same-file const/);
153
+ await expect(
154
+ extractFromSources({
155
+ "src/flows/run.ts": `
156
+ ai.decision("triage", {
157
+ onUncertain: "abstain",
158
+ ask: { team: missingQuestion },
159
+ });
160
+ `,
161
+ }),
162
+ ).rejects.toThrow(/not a same-file const/);
163
+ });
164
+ });