okengine 0.22.0 → 0.23.1
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.
- package/AGENTS.md +7 -0
- package/manifest.v1.schema.json +44 -0
- package/package.json +32 -19
- package/site/content/docs/ai/mcp.mdx +14 -10
- package/site/content/docs/ai/skills.mdx +1 -1
- package/site/content/docs/elements/ai/agents.mdx +61 -13
- package/site/content/docs/elements/ai/decide.mdx +536 -0
- package/site/content/docs/elements/ai/events.mdx +373 -0
- package/site/content/docs/elements/ai/index.mdx +9 -3
- package/site/content/docs/elements/ai/meta.json +1 -1
- package/site/content/docs/elements/ai/prompts.mdx +4 -2
- package/site/content/docs/elements/channel/index.mdx +4 -4
- package/site/content/docs/elements/channel/receipts.mdx +7 -5
- package/site/content/docs/elements/channel/sms.mdx +2 -1
- package/site/content/docs/elements/flow/index.mdx +22 -18
- package/site/content/docs/elements/gate/auth.mdx +4 -1
- package/site/content/docs/elements/signal/index.mdx +15 -14
- package/site/content/docs/elements/store/index.mdx +1 -1
- package/site/content/docs/elements/store/kv.mdx +5 -5
- package/site/content/docs/plugins/otp.mdx +3 -3
- package/site/content/docs/recipes/dragonfly.mdx +5 -5
- package/site/content/docs/recipes/index.mdx +6 -6
- package/site/content/docs/recipes/postgres.mdx +12 -8
- package/site/content/docs/recipes/redis.mdx +4 -4
- package/site/content/docs/reference/cli.mdx +2 -1
- package/site/content/docs/reference/configuration.mdx +9 -7
- package/site/content/docs/reference/fx.mdx +19 -10
- package/src/auth/api-key-sql.test.ts +44 -0
- package/src/auth/api-key-sql.ts +1 -0
- package/src/auth/api-keys.ts +30 -12
- package/src/auth/config.ts +6 -2
- package/src/auth/gate-auth.test.ts +20 -0
- package/src/auth/tenants.ts +17 -0
- package/src/cli/agents-md.test.ts +25 -0
- package/src/cli/ask-vault-gaps.test.ts +38 -1
- package/src/cli/ask-vault-gaps.ts +35 -2
- package/src/cli/decide.ts +174 -0
- package/src/cli/decision-lock-watch.test.ts +45 -0
- package/src/cli/decision-lock-watch.ts +51 -0
- package/src/cli/dev-app-runner.ts +34 -8
- package/src/cli/dev-auth-secret.test.ts +49 -0
- package/src/cli/dev-auth-secret.ts +49 -0
- package/src/cli/dev-hot.test.ts +41 -0
- package/src/cli/dev-hot.ts +48 -0
- package/src/cli/dev.test.ts +1 -1
- package/src/cli/dev.ts +24 -6
- package/src/cli/doctor-diff.test.ts +29 -1
- package/src/cli/doctor-diff.ts +35 -0
- package/src/cli/eval.ts +142 -1
- package/src/cli/index.ts +5 -0
- package/src/cli/load-config.images.test.ts +2 -2
- package/src/cli/load-config.ts +1 -1
- package/src/cli/manifest-pr-diff.ts +16 -0
- package/src/cli/mcp-from-console.ts +48 -0
- package/src/cli/registry.ts +35 -1
- package/src/client/agent.ts +307 -0
- package/src/client/transport.test.ts +20 -0
- package/src/client/transport.ts +2 -1
- package/src/client/types.ts +1 -1
- package/src/client-react/index.ts +7 -1
- package/src/client-react/use-agent-run.test.ts +271 -0
- package/src/client-react/use-agent-run.ts +193 -0
- package/src/compiler/ai-approval.test.ts +36 -0
- package/src/compiler/ai-repair.test.ts +22 -0
- package/src/compiler/decisions.extract.test.ts +164 -0
- package/src/compiler/effects-fetch.test.ts +77 -0
- package/src/compiler/effects-infer.ts +114 -17
- package/src/compiler/effects-join.test.ts +48 -0
- package/src/compiler/extract.test.ts +28 -0
- package/src/compiler/extract.ts +279 -13
- package/src/compiler/fx-index.ts +58 -4
- package/src/compiler/response.ts +35 -2
- package/src/config/index.ts +21 -3
- package/src/console/index.ts +1 -1
- package/src/console/server/ai-runs-flows.ts +269 -0
- package/src/console/server/ai-runs.test.ts +323 -0
- package/src/console/server/ai-runs.ts +562 -0
- package/src/console/server/ai.ts +12 -1
- package/src/console/server/app.ts +10 -1
- package/src/console/server/decisions-flows.ts +157 -0
- package/src/console/server/decisions.test.ts +66 -0
- package/src/console/server/decisions.ts +229 -0
- package/src/console/server/flows.ts +50 -2
- package/src/console/server/invoke-user-flow.ts +9 -0
- package/src/console/server/runs-ingest.ts +16 -0
- package/src/console/server/serve.ts +5 -0
- package/src/console/server/state.ts +25 -1
- package/src/console/server/store-stats.test.ts +47 -0
- package/src/console/server/store-stats.ts +92 -18
- package/src/console/ui-next/dist/assets/access-page-C_N4qvai.js +4 -0
- package/src/console/ui-next/dist/assets/{agent-disclosure-Bkohc_8X.js → agent-disclosure-Cq1TxniW.js} +1 -1
- package/src/console/ui-next/dist/assets/{cache-glyph-rL0lHxat.js → cache-glyph-BaI4uxZz.js} +1 -1
- package/src/console/ui-next/dist/assets/{call-pii-button-Deo4PP18.js → call-pii-button-DNyK7IIY.js} +1 -1
- package/src/console/ui-next/dist/assets/{collapsible-CoJ6amHf.js → collapsible-CRhBJMZR.js} +1 -1
- package/src/console/ui-next/dist/assets/confirm-sheet-DZfuJXP4.js +1 -0
- package/src/console/ui-next/dist/assets/copy-inline-button-BTjPVjRT.js +1 -0
- package/src/console/ui-next/dist/assets/decisions-page-D9twj8Xy.js +1 -0
- package/src/console/ui-next/dist/assets/detail-header-Dk5LipF6.js +1 -0
- package/src/console/ui-next/dist/assets/dropdown-menu-CRGrj5JU.js +1 -0
- package/src/console/ui-next/dist/assets/duration-tone-CsHWlVPU.js +9 -0
- package/src/console/ui-next/dist/assets/element-icons-C6t8Vlml.js +1 -0
- package/src/console/ui-next/dist/assets/explorer-chrome-Dslh2yE8.js +1 -0
- package/src/console/ui-next/dist/assets/explorer-empty-CIVg7Jgb.js +1 -0
- package/src/console/ui-next/dist/assets/flows-page-ZaTJAhR9.js +1 -0
- package/src/console/ui-next/dist/assets/{highlighted-json-CPYBzDh0.js → highlighted-json-DQ-EMAfz.js} +1 -1
- package/src/console/ui-next/dist/assets/{http-method-DTPevKmM.js → http-method-Cn_Fl79s.js} +1 -1
- package/src/console/ui-next/dist/assets/index-BlrN49Hs.css +2 -0
- package/src/console/ui-next/dist/assets/index-CmRFeWdT.js +58 -0
- package/src/console/ui-next/dist/assets/observability-page-Cn6R_dXz.js +8 -0
- package/src/console/ui-next/dist/assets/{replica-lag-BM27lXld.js → replica-lag-Bx6EZhSe.js} +7 -7
- package/src/console/ui-next/dist/assets/request-meta-DmdiUrfh.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-keys-DTE1sjTC.js +1 -0
- package/src/console/ui-next/dist/assets/store-page-8P6vGXnn.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-BfGvpFSv.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-CWdUHbc0.js +55 -0
- package/src/console/ui-next/dist/assets/units-page-CZOFZzay.js +1 -0
- package/src/console/ui-next/dist/assets/use-vault-list-BAyQG8Re.js +1 -0
- package/src/console/ui-next/dist/assets/vault-page-ki_ishmX.js +2 -0
- package/src/console/ui-next/dist/index.html +4 -3
- package/src/console/ui-next/seed-parked-approval.ts +126 -0
- package/src/console/ui-next/src/client.ts +224 -1
- package/src/console/ui-next/src/components/explorer/detail-header.tsx +9 -0
- package/src/console/ui-next/src/components/explorer/explorer-start-toggle.tsx +5 -1
- package/src/console/ui-next/src/components/ui/sheet-form.tsx +21 -12
- package/src/console/ui-next/src/features/flows/decisions/decisions-page.tsx +216 -0
- package/src/console/ui-next/src/features/flows/decisions/resolve.test.ts +48 -0
- package/src/console/ui-next/src/features/flows/decisions/resolve.ts +111 -0
- package/src/console/ui-next/src/features/flows/flows-page.tsx +10 -0
- package/src/console/ui-next/src/features/flows/graph/element-map.ts +1 -0
- package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +6 -0
- package/src/console/ui-next/src/features/flows/traces/effect-summary.test.ts +24 -0
- package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +24 -0
- package/src/console/ui-next/src/features/flows/traces/http-status.ts +58 -0
- package/src/console/ui-next/src/features/flows/traces/request-client-card.tsx +355 -0
- package/src/console/ui-next/src/features/flows/traces/request-client.test.ts +114 -0
- package/src/console/ui-next/src/features/flows/traces/request-client.ts +423 -0
- package/src/console/ui-next/src/features/flows/traces/request-input-view.test.ts +52 -6
- package/src/console/ui-next/src/features/flows/traces/request-input-view.ts +85 -19
- package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +505 -80
- package/src/console/ui-next/src/features/flows/traces/trace-lanes.test.ts +61 -0
- package/src/console/ui-next/src/features/flows/traces/trace-lanes.ts +78 -0
- package/src/console/ui-next/src/features/flows/traces/trace-nav.test.ts +48 -0
- package/src/console/ui-next/src/features/flows/traces/trace-nav.ts +57 -0
- package/src/console/ui-next/src/features/flows/traces/trace-request-section.tsx +275 -96
- package/src/console/ui-next/src/features/flows/traces/traces-pane.tsx +16 -2
- package/src/console/ui-next/src/features/observability/detail/ai-runs.tsx +369 -0
- package/src/console/ui-next/src/features/observability/observability-page.tsx +31 -1
- package/src/console/ui-next/src/features/observability/state/observability-selection.ts +18 -3
- package/src/console/ui-next/src/features/store/performance/kv-performance-panel.tsx +12 -1
- package/src/console/ui-next/src/features/store/performance/performance-panel.tsx +14 -1
- package/src/console/ui-next/src/features/units/call/call-api-panel.tsx +1 -1
- package/src/console/ui-next/src/features/units/detail/flow-contract-panel.tsx +6 -2
- package/src/console/ui-next/src/features/units/explorer/units-tree.tsx +73 -18
- package/src/console/ui-next/src/features/units/lib/unit-tree.test.ts +21 -0
- package/src/console/ui-next/src/features/units/lib/unit-tree.ts +82 -0
- package/src/console/ui-next/src/features/units/units-page.tsx +25 -7
- package/src/console/ui-next/src/router.tsx +17 -0
- package/src/console/ui-next/ui-next-seed-runs.ts +23 -0
- package/src/console/ui-next/vite-console-kernel-plugin.ts +43 -3
- package/src/console/ui-next/vite.config.ts +9 -0
- package/src/docker/recipes/dragonfly.ts +3 -2
- package/src/docker/recipes/redis.ts +1 -1
- package/src/drivers/ai-anthropic.ts +212 -21
- package/src/drivers/ai-mock.ts +34 -1
- package/src/drivers/ai-openai-compatible.ts +67 -26
- package/src/drivers/ai-preconnect.ts +18 -0
- package/src/drivers/ai-stream.test.ts +62 -0
- package/src/drivers/ai-types.ts +20 -0
- package/src/drivers/journal-postgres.test.ts +22 -1
- package/src/drivers/journal-postgres.ts +511 -13
- package/src/drivers/memory-ddl.test.ts +18 -0
- package/src/drivers/memory.ts +21 -1
- package/src/drivers/pg-rls.test.ts +31 -0
- package/src/drivers/pg-rls.ts +15 -0
- package/src/drivers/postgres.test.ts +9 -0
- package/src/drivers/postgres.ts +49 -11
- package/src/drivers/signal-compete.test.ts +234 -0
- package/src/drivers/signal-postgres.ts +39 -19
- package/src/drivers/signal-redis.ts +167 -11
- package/src/drivers/signal-types.ts +12 -0
- package/src/elements/ai/agent-event-slot.ts +27 -0
- package/src/elements/ai/approval-http.ts +207 -0
- package/src/elements/ai/approval.test.ts +741 -0
- package/src/elements/ai/approval.ts +228 -0
- package/src/elements/ai/decisions/bind.ts +163 -0
- package/src/elements/ai/decisions/certificate.test.ts +291 -0
- package/src/elements/ai/decisions/certificate.ts +377 -0
- package/src/elements/ai/decisions/certify.ts +314 -0
- package/src/elements/ai/decisions/decide.test.ts +953 -0
- package/src/elements/ai/decisions/e2e.test.ts +399 -0
- package/src/elements/ai/decisions/export.test.ts +120 -0
- package/src/elements/ai/decisions/export.ts +146 -0
- package/src/elements/ai/decisions/fixtures/openrouter-request.json +24 -0
- package/src/elements/ai/decisions/fixtures/openrouter-response.json +22 -0
- package/src/elements/ai/decisions/fixtures/typesafe-request.json +19 -0
- package/src/elements/ai/decisions/fixtures/typesafe-response.json +13 -0
- package/src/elements/ai/decisions/http.test.ts +154 -0
- package/src/elements/ai/decisions/http.ts +240 -0
- package/src/elements/ai/decisions/labels.test.ts +282 -0
- package/src/elements/ai/decisions/labels.ts +257 -0
- package/src/elements/ai/decisions/openrouter.live.test.ts +136 -0
- package/src/elements/ai/decisions/openrouter.ts +36 -0
- package/src/elements/ai/decisions/provider.ts +102 -0
- package/src/elements/ai/decisions/typesafe.ts +36 -0
- package/src/elements/ai/declare.ts +252 -1
- package/src/elements/ai/events.test.ts +379 -0
- package/src/elements/ai/events.ts +138 -0
- package/src/elements/ai/mcp-protocol.ts +5 -6
- package/src/elements/ai/run-events.test.ts +461 -0
- package/src/elements/ai/run-events.ts +522 -0
- package/src/elements/ai/runtime.ts +1187 -105
- package/src/elements/ai/stream-turn.ts +214 -0
- package/src/elements/ai/stream.live.test.ts +76 -0
- package/src/elements/ai/subagent.test.ts +136 -0
- package/src/elements/ai.test.ts +497 -3
- package/src/elements/ai.ts +16 -1
- package/src/elements/channel/runtime.ts +11 -0
- package/src/elements/channel/sql-ledger.ts +251 -0
- package/src/elements/clock/durable.ts +27 -1
- package/src/elements/signal/runtime.ts +9 -0
- package/src/elements/signal.test.ts +1 -0
- package/src/elements/store/cache.test.ts +120 -15
- package/src/elements/store/cache.ts +200 -39
- package/src/elements/store/prepare-row.test.ts +6 -2
- package/src/elements/store/sql-session.test.ts +53 -0
- package/src/elements/store/sql-session.ts +206 -10
- package/src/elements/store.ts +1 -0
- package/src/i18n/catalogs/ar.ts +5 -0
- package/src/i18n/catalogs/en.ts +5 -0
- package/src/index.ts +2 -0
- package/src/kernel/agent-event-store.ts +448 -0
- package/src/kernel/app.ts +135 -17
- package/src/kernel/auto-cache.test.ts +228 -0
- package/src/kernel/boot-bind/ai.ts +3 -0
- package/src/kernel/boot-bind/channel.ts +63 -2
- package/src/kernel/boot-bind/honor-config.test.ts +40 -10
- package/src/kernel/boot-bind/signal.ts +61 -25
- package/src/kernel/boot-bind/store.test.ts +26 -0
- package/src/kernel/boot-bind/store.ts +82 -6
- package/src/kernel/boot.ts +31 -6
- package/src/kernel/builtin-errors.ts +2 -0
- package/src/kernel/capability.ts +3 -0
- package/src/kernel/decision-label-store.ts +441 -0
- package/src/kernel/effects.test.ts +2 -1
- package/src/kernel/effects.ts +13 -1
- package/src/kernel/element-registries.ts +3 -0
- package/src/kernel/errors-compiler.ts +11 -0
- package/src/kernel/errors-text.ts +20 -0
- package/src/kernel/errors.ts +10 -0
- package/src/kernel/flow.test.ts +2 -3
- package/src/kernel/flow.ts +33 -3
- package/src/kernel/fx-call-types.test.ts +32 -0
- package/src/kernel/fx-decide.ts +765 -0
- package/src/kernel/fx-sql-handle.ts +46 -7
- package/src/kernel/fx.test.ts +13 -8
- package/src/kernel/fx.ts +339 -45
- package/src/kernel/http-frame.test.ts +94 -0
- package/src/kernel/http-frame.ts +203 -0
- package/src/kernel/idempotency.test.ts +2 -1
- package/src/kernel/journal.test.ts +114 -0
- package/src/kernel/journal.ts +471 -51
- package/src/kernel/json-result.ts +10 -0
- package/src/kernel/pipeline-tenant.ts +5 -1
- package/src/kernel/pipeline.test.ts +45 -0
- package/src/kernel/pipeline.ts +5 -0
- package/src/kernel/signal-tx.ts +28 -0
- package/src/kernel/sse-id.ts +32 -0
- package/src/kernel/store-transaction.test.ts +95 -0
- package/src/manifest/diff.ts +1 -0
- package/src/manifest/types.ts +29 -2
- package/src/mcp/ai-tools.test.ts +202 -0
- package/src/mcp/authorization.ts +66 -0
- package/src/mcp/docs-server.ts +19 -2
- package/src/mcp/mcp.test.ts +83 -0
- package/src/mcp/protocol.ts +8 -2
- package/src/mcp/server.ts +23 -2
- package/src/mcp/session.ts +3 -0
- package/src/mcp/tools.ts +168 -0
- package/src/mcp/versions.ts +60 -0
- package/src/plugins/index.ts +1 -0
- package/src/plugins/mena.ts +56 -0
- package/src/release/http-graph.test.ts +2 -0
- package/src/release/http-graph.ts +5 -0
- package/src/release/measure.ts +39 -14
- package/src/runs/collect.ts +4 -1
- package/src/runs/parquet.test.ts +27 -1
- package/src/runs/parquet.ts +19 -0
- package/src/runs/types.ts +31 -0
- package/src/runtime/json-code-block.test.ts +106 -47
- package/src/runtime/json-code-block.ts +1209 -46
- package/src/term.test.ts +13 -0
- package/src/term.ts +81 -49
- package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +0 -1
- package/src/console/ui-next/dist/assets/access-page-Ze6ckjdd.js +0 -4
- package/src/console/ui-next/dist/assets/copy-inline-button-CWG5_ZOh.js +0 -1
- package/src/console/ui-next/dist/assets/detail-header-b_ZzIsmU.js +0 -1
- package/src/console/ui-next/dist/assets/dropdown-menu-nwXEO1Ac.js +0 -1
- package/src/console/ui-next/dist/assets/duration-tone-BbQ_8z50.js +0 -9
- package/src/console/ui-next/dist/assets/element-icons-CwyLpVXz.js +0 -1
- package/src/console/ui-next/dist/assets/explorer-empty-C8sSCqYT.js +0 -1
- package/src/console/ui-next/dist/assets/flows-page-D2E7BtKK.js +0 -1
- package/src/console/ui-next/dist/assets/index-VxoEz295.css +0 -2
- package/src/console/ui-next/dist/assets/index-vTuwmeQz.js +0 -58
- package/src/console/ui-next/dist/assets/observability-page-SJTSzHFJ.js +0 -4
- package/src/console/ui-next/dist/assets/request-meta-BjFT7DNl.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-keys-CUNegEV4.js +0 -1
- package/src/console/ui-next/dist/assets/store-page-Qj9ThRE-.js +0 -41
- package/src/console/ui-next/dist/assets/trace-detail-sheet-Cmitq3da.js +0 -2
- package/src/console/ui-next/dist/assets/tree-expand-toggle-CbDIB-7x.js +0 -55
- package/src/console/ui-next/dist/assets/units-page-_PuYqFty.js +0 -1
- package/src/console/ui-next/dist/assets/use-vault-list-Cs45Czkt.js +0 -1
- package/src/console/ui-next/dist/assets/vault-page-BOMr0go9.js +0 -2
|
@@ -336,18 +336,48 @@ describe("boot binders honour drivers.* config", () => {
|
|
|
336
336
|
}
|
|
337
337
|
});
|
|
338
338
|
|
|
339
|
-
test("signal: drivers.signal postgres
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
339
|
+
test("signal: drivers.signal postgres needs DATABASE_URL (never silent memory)", async () => {
|
|
340
|
+
const prevDb = process.env.DATABASE_URL;
|
|
341
|
+
const prevStore = process.env.OKE_STORE_SQL_URL;
|
|
342
|
+
delete process.env.DATABASE_URL;
|
|
343
|
+
delete process.env.OKE_STORE_SQL_URL;
|
|
344
|
+
try {
|
|
345
|
+
await expect(
|
|
346
|
+
bootApplication({
|
|
347
|
+
env: "test",
|
|
348
|
+
signals: [signal.once("ping")],
|
|
349
|
+
config: {
|
|
350
|
+
drivers: {
|
|
351
|
+
signal: { test: "postgres" },
|
|
352
|
+
},
|
|
347
353
|
},
|
|
354
|
+
}),
|
|
355
|
+
).rejects.toThrow(/DATABASE_URL/);
|
|
356
|
+
} finally {
|
|
357
|
+
if (prevDb !== undefined) process.env.DATABASE_URL = prevDb;
|
|
358
|
+
else delete process.env.DATABASE_URL;
|
|
359
|
+
if (prevStore !== undefined) process.env.OKE_STORE_SQL_URL = prevStore;
|
|
360
|
+
else delete process.env.OKE_STORE_SQL_URL;
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
test("signal: drivers.signal postgres binds an injected client", async () => {
|
|
365
|
+
const { createPostgresSignalFake } = await import("../../drivers/signal-postgres.ts");
|
|
366
|
+
const result = await bootApplication({
|
|
367
|
+
env: "test",
|
|
368
|
+
signals: [signal.once("ping")],
|
|
369
|
+
clients: { signalSql: createPostgresSignalFake() },
|
|
370
|
+
config: {
|
|
371
|
+
drivers: {
|
|
372
|
+
signal: { test: "postgres" },
|
|
348
373
|
},
|
|
349
|
-
}
|
|
350
|
-
)
|
|
374
|
+
},
|
|
375
|
+
});
|
|
376
|
+
try {
|
|
377
|
+
expect(result.signal!.driverId).toBe("postgres");
|
|
378
|
+
} finally {
|
|
379
|
+
await result.close();
|
|
380
|
+
}
|
|
351
381
|
});
|
|
352
382
|
|
|
353
383
|
test("runs: drivers.runs unset in test binds memory", async () => {
|
|
@@ -4,11 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
import { resolveDriverId, type ConfigEnv } from "../../config/index.ts";
|
|
6
6
|
import { SIGNAL_DEFAULTS } from "../../config/driver-defaults.ts";
|
|
7
|
+
import {
|
|
8
|
+
sharedPostgresClient,
|
|
9
|
+
toPostgresParams,
|
|
10
|
+
withPinnedPostgres,
|
|
11
|
+
} from "../../drivers/postgres.ts";
|
|
12
|
+
import type { PostgresClientLike } from "../../drivers/postgres.ts";
|
|
7
13
|
import { memorySignalDriver } from "../../drivers/signal-memory.ts";
|
|
14
|
+
import type { PostgresSignalSql } from "../../drivers/signal-postgres.ts";
|
|
15
|
+
import { postgresSignalDriver } from "../../drivers/signal-postgres.ts";
|
|
8
16
|
import { createBunSignalRedisClient, redisSignalDriver } from "../../drivers/signal-redis.ts";
|
|
9
17
|
import type { SignalRedisClientLike } from "../../drivers/signal-types.ts";
|
|
10
18
|
import { createSignalRuntime, type SignalRuntime } from "../../elements/signal.ts";
|
|
11
|
-
import {
|
|
19
|
+
import { resolveInstanceId } from "../instance-id.ts";
|
|
12
20
|
import type { BootOptions } from "../boot.ts";
|
|
13
21
|
|
|
14
22
|
/**
|
|
@@ -32,29 +40,43 @@ function redisUrlFor(docker: boolean): string | undefined {
|
|
|
32
40
|
return url;
|
|
33
41
|
}
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Wrap a Bun.SQL client as the signal driver's query surface.
|
|
45
|
+
* `listen` is omitted — Bun.SQL has no LISTEN/NOTIFY. Boot polls `drain`.
|
|
46
|
+
*
|
|
47
|
+
* @param client - Shared postgres pool
|
|
48
|
+
*/
|
|
49
|
+
function asSignalSql(client: PostgresClientLike): PostgresSignalSql {
|
|
50
|
+
return {
|
|
51
|
+
async query(sql, params = []) {
|
|
52
|
+
const result = await client.unsafe(toPostgresParams(sql, params), [...params]);
|
|
53
|
+
if (Array.isArray(result)) return result as Record<string, unknown>[];
|
|
54
|
+
return Array.from(result as ArrayLike<Record<string, unknown>>);
|
|
55
|
+
},
|
|
56
|
+
async exec(sql, params = []) {
|
|
57
|
+
const result = await client.unsafe(toPostgresParams(sql, params), [...params]);
|
|
58
|
+
if (
|
|
59
|
+
result &&
|
|
60
|
+
typeof result === "object" &&
|
|
61
|
+
"changes" in result &&
|
|
62
|
+
typeof result.changes === "number"
|
|
63
|
+
) {
|
|
64
|
+
return { changes: result.changes };
|
|
65
|
+
}
|
|
66
|
+
if (Array.isArray(result)) return { changes: result.length };
|
|
67
|
+
return { changes: 0 };
|
|
68
|
+
},
|
|
69
|
+
begin: (fn) => withPinnedPostgres(client, (tx) => fn(asSignalSql(tx))),
|
|
70
|
+
async close() {
|
|
71
|
+
/* Shared pool — boot owns the connection. */
|
|
72
|
+
},
|
|
73
|
+
};
|
|
52
74
|
}
|
|
53
75
|
|
|
54
76
|
/**
|
|
55
77
|
* Construct a Signal runtime, register decls / binding names, start the bus.
|
|
56
78
|
*
|
|
57
|
-
* Supported ids: `memory` · `redis
|
|
79
|
+
* Supported ids: `memory` · `redis` · `postgres`. `nats` fails loud until a
|
|
58
80
|
* native client can be constructed (never silently bind memory).
|
|
59
81
|
*
|
|
60
82
|
* @param options - Boot options
|
|
@@ -81,19 +103,33 @@ export async function bindSignal(
|
|
|
81
103
|
break;
|
|
82
104
|
case "redis": {
|
|
83
105
|
const redis = injectedRedis ?? createBunSignalRedisClient(redisUrlFor(docker));
|
|
84
|
-
warnSignalRedisProcessLocal();
|
|
85
106
|
signal = createSignalRuntime({
|
|
86
107
|
driver: redisSignalDriver,
|
|
87
108
|
now,
|
|
88
109
|
redis,
|
|
110
|
+
compete: true,
|
|
111
|
+
consumerId: resolveInstanceId(options.instanceId),
|
|
112
|
+
});
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
case "postgres": {
|
|
116
|
+
const injected = options.clients?.signalSql;
|
|
117
|
+
const url = process.env.DATABASE_URL ?? process.env.OKE_STORE_SQL_URL ?? undefined;
|
|
118
|
+
if (!injected && !url) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
env === "dev"
|
|
121
|
+
? 'oke boot: signal driver "postgres" needs DATABASE_URL (did `oke dev` write .env.local?)'
|
|
122
|
+
: 'oke boot: signal driver "postgres" needs DATABASE_URL',
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
signal = createSignalRuntime({
|
|
126
|
+
driver: postgresSignalDriver,
|
|
127
|
+
now,
|
|
128
|
+
sql: injected ?? asSignalSql(sharedPostgresClient(url)),
|
|
129
|
+
pollMs: injected ? undefined : 1_000,
|
|
89
130
|
});
|
|
90
131
|
break;
|
|
91
132
|
}
|
|
92
|
-
case "postgres":
|
|
93
|
-
throw new Error(
|
|
94
|
-
'oke boot: signal driver "postgres" needs a LISTEN/NOTIFY-capable SQL client — ' +
|
|
95
|
-
'not available via Bun.SQL yet. Use "redis" or "memory", or inject elements.signal.',
|
|
96
|
-
);
|
|
97
133
|
case "nats":
|
|
98
134
|
throw new Error(
|
|
99
135
|
'oke boot: signal driver "nats" has no production client bind yet — ' +
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
resolveIndexDriverId,
|
|
14
14
|
resolveKvDriverId,
|
|
15
15
|
resolveSqlDriverId,
|
|
16
|
+
sqlBindingForDecl,
|
|
16
17
|
} from "./store.ts";
|
|
17
18
|
|
|
18
19
|
describe("bindStore driver resolution", () => {
|
|
@@ -99,6 +100,31 @@ describe("bindStore driver resolution", () => {
|
|
|
99
100
|
});
|
|
100
101
|
});
|
|
101
102
|
|
|
103
|
+
describe("sqlBindingForDecl", () => {
|
|
104
|
+
test("pool.max and replica URLs land on the binding; min is ignored", () => {
|
|
105
|
+
const binding = sqlBindingForDecl("db", "postgres://primary/db", {
|
|
106
|
+
driver: "postgres",
|
|
107
|
+
pool: { max: 20, min: 2 },
|
|
108
|
+
replicas: ["postgres://ro-1/db", "postgres://ro-2/db"],
|
|
109
|
+
});
|
|
110
|
+
expect(binding).toEqual({
|
|
111
|
+
name: "db",
|
|
112
|
+
primary: { url: "postgres://primary/db", pool: { max: 20 } },
|
|
113
|
+
replicas: [
|
|
114
|
+
{ url: "postgres://ro-1/db", pool: { max: 20 } },
|
|
115
|
+
{ url: "postgres://ro-2/db", pool: { max: 20 } },
|
|
116
|
+
],
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("a string pin keeps the URL and omits pool and replicas", () => {
|
|
121
|
+
expect(sqlBindingForDecl("db", "memory://", "memory")).toEqual({
|
|
122
|
+
name: "db",
|
|
123
|
+
primary: { url: "memory://" },
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
102
128
|
describe("bindStore durable KV routing", () => {
|
|
103
129
|
const prev = {
|
|
104
130
|
redis: process.env.REDIS_URL,
|
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
* Lazy store binder — loaded only when Store is declared.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
driverRefId,
|
|
7
|
+
resolveDomainDdlMode,
|
|
8
|
+
resolveDriverId,
|
|
9
|
+
resolveDriverRef,
|
|
10
|
+
type ConfigEnv,
|
|
11
|
+
type DriverRef,
|
|
12
|
+
} from "../../config/index.ts";
|
|
6
13
|
import {
|
|
7
14
|
STORE_FILES_DEFAULTS,
|
|
8
15
|
STORE_KV_DEFAULTS,
|
|
@@ -18,6 +25,7 @@ import { redisDriver } from "../../drivers/redis.ts";
|
|
|
18
25
|
import { s3Driver } from "../../drivers/s3.ts";
|
|
19
26
|
import type { FilesDriver, IndexDriver, KvDriver, SqlDriver } from "../../drivers/types.ts";
|
|
20
27
|
import { createStoreRuntime, type StoreRuntime } from "../../elements/store.ts";
|
|
28
|
+
import type { SqlRuntimeBinding } from "../../elements/store/runtime.ts";
|
|
21
29
|
import type { StoreDecl } from "../../elements/store/declare.ts";
|
|
22
30
|
import { emitBootWarn } from "../../runtime/boot-warn.ts";
|
|
23
31
|
import type { BootOptions } from "../boot.ts"; // type-only — no cycle at runtime
|
|
@@ -129,17 +137,15 @@ export function bindStore(
|
|
|
129
137
|
warnKvMemoryDurable(durableKvDecls.map((d) => d.name));
|
|
130
138
|
}
|
|
131
139
|
|
|
132
|
-
const
|
|
140
|
+
const sqlRef = resolveSqlDriverRef(options, env, docker);
|
|
141
|
+
const sqlBindings: Record<string, SqlRuntimeBinding> = {};
|
|
133
142
|
const kvBindings: Record<string, { url?: string }> = {};
|
|
134
143
|
const filesBindings: Record<string, { root?: string }> = {};
|
|
135
144
|
const indexBindings: Record<string, { url?: string; apiKey?: string }> = {};
|
|
136
145
|
|
|
137
146
|
for (const decl of options.stores ?? []) {
|
|
138
147
|
if (isSqlDecl(decl)) {
|
|
139
|
-
sqlBindings[decl.name] =
|
|
140
|
-
name: decl.name,
|
|
141
|
-
primary: { url: sqlUrl },
|
|
142
|
-
};
|
|
148
|
+
sqlBindings[decl.name] = sqlBindingForDecl(decl.name, sqlUrl, sqlRef);
|
|
143
149
|
} else if (isKvDecl(decl)) {
|
|
144
150
|
if (decl.durable === true) {
|
|
145
151
|
kvBindings[decl.name] = {};
|
|
@@ -210,6 +216,76 @@ export function resolveSqlDriverId(options: BootOptions, env: ConfigEnv, docker:
|
|
|
210
216
|
return resolveDriverId(options.config?.drivers?.store?.sql, env, STORE_SQL_DEFAULTS)!;
|
|
211
217
|
}
|
|
212
218
|
|
|
219
|
+
/**
|
|
220
|
+
* Rich SQL driver ref for the active env. A docker `OKE_SQL_DRIVER` override
|
|
221
|
+
* that changes the protocol drops `pool` / `replicas` — those belong to the
|
|
222
|
+
* configured driver, not the override.
|
|
223
|
+
*
|
|
224
|
+
* @param options - Boot options
|
|
225
|
+
* @param env - Active environment
|
|
226
|
+
* @param docker - Docker mode
|
|
227
|
+
*/
|
|
228
|
+
function resolveSqlDriverRef(
|
|
229
|
+
options: BootOptions,
|
|
230
|
+
env: ConfigEnv,
|
|
231
|
+
docker: boolean,
|
|
232
|
+
): DriverRef | undefined {
|
|
233
|
+
const configured = resolveDriverRef(options.config?.drivers?.store?.sql, env, STORE_SQL_DEFAULTS);
|
|
234
|
+
const fromEnv = process.env.OKE_SQL_DRIVER?.trim();
|
|
235
|
+
if (docker && fromEnv && fromEnv !== driverRefId(configured)) return fromEnv;
|
|
236
|
+
return configured;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Primary URL plus documented `pool.max` and read-replica URLs.
|
|
241
|
+
*
|
|
242
|
+
* `pool.min` is ignored — Bun.SQL has no minimum pool size. Unset `max`
|
|
243
|
+
* leaves pool sizing to the postgres driver default of 8.
|
|
244
|
+
*
|
|
245
|
+
* @param name - Store declaration name
|
|
246
|
+
* @param url - Primary connection URL
|
|
247
|
+
* @param ref - Active driver pin (string or rich object)
|
|
248
|
+
*/
|
|
249
|
+
export function sqlBindingForDecl(
|
|
250
|
+
name: string,
|
|
251
|
+
url: string,
|
|
252
|
+
ref: DriverRef | undefined,
|
|
253
|
+
): SqlRuntimeBinding {
|
|
254
|
+
const max = driverPoolMax(ref);
|
|
255
|
+
const pool = max !== undefined ? { pool: { max } } : {};
|
|
256
|
+
const replicas = driverReplicaUrls(ref).map((replicaUrl) => ({
|
|
257
|
+
url: replicaUrl,
|
|
258
|
+
...pool,
|
|
259
|
+
}));
|
|
260
|
+
return {
|
|
261
|
+
name,
|
|
262
|
+
primary: { url, ...pool },
|
|
263
|
+
...(replicas.length > 0 ? { replicas } : {}),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Positive finite `pool.max` from a rich driver ref.
|
|
269
|
+
*
|
|
270
|
+
* @param ref - Driver pin
|
|
271
|
+
*/
|
|
272
|
+
function driverPoolMax(ref: DriverRef | undefined): number | undefined {
|
|
273
|
+
if (ref === undefined || typeof ref === "string") return undefined;
|
|
274
|
+
const max = ref.pool?.max;
|
|
275
|
+
if (typeof max !== "number" || !Number.isFinite(max) || max < 1) return undefined;
|
|
276
|
+
return Math.floor(max);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Replica connection strings from a rich driver ref. Non-strings are dropped.
|
|
281
|
+
*
|
|
282
|
+
* @param ref - Driver pin
|
|
283
|
+
*/
|
|
284
|
+
function driverReplicaUrls(ref: DriverRef | undefined): readonly string[] {
|
|
285
|
+
if (ref === undefined || typeof ref === "string" || ref.replicas === undefined) return [];
|
|
286
|
+
return ref.replicas.filter((item): item is string => typeof item === "string" && item.length > 0);
|
|
287
|
+
}
|
|
288
|
+
|
|
213
289
|
/**
|
|
214
290
|
* @param options - Boot options
|
|
215
291
|
* @param env - Active env
|
package/src/kernel/boot.ts
CHANGED
|
@@ -191,6 +191,8 @@ export interface BootOptions {
|
|
|
191
191
|
readonly clients?: {
|
|
192
192
|
readonly kv?: import("../drivers/types.ts").KvClientLike;
|
|
193
193
|
readonly signalRedis?: import("../drivers/signal-types.ts").SignalRedisClientLike;
|
|
194
|
+
/** Injected postgres signal client (tests). Production opens Bun.SQL. */
|
|
195
|
+
readonly signalSql?: import("../drivers/signal-postgres.ts").PostgresSignalSql;
|
|
194
196
|
};
|
|
195
197
|
/**
|
|
196
198
|
* Process instance id for Clock / Journal / fleet registry.
|
|
@@ -282,9 +284,14 @@ export function resolveElementNeeds(options: BootOptions): ElementNeeds {
|
|
|
282
284
|
const considerFlow = (f: AnyFlowDef): void => {
|
|
283
285
|
const e = f.effects;
|
|
284
286
|
if (f.durable === true) journal = true;
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
287
|
+
const touchesStore = [...(e?.reads ?? []), ...(e?.writes ?? [])].some(
|
|
288
|
+
(ref) =>
|
|
289
|
+
ref.startsWith("sql:") ||
|
|
290
|
+
ref.startsWith("kv:") ||
|
|
291
|
+
ref.startsWith("files:") ||
|
|
292
|
+
ref.startsWith("index:"),
|
|
293
|
+
);
|
|
294
|
+
if (touchesStore) store = true;
|
|
288
295
|
if ((e?.emits?.length ?? 0) > 0) signal = true;
|
|
289
296
|
if ((e?.secrets?.length ?? 0) > 0) vault = true;
|
|
290
297
|
if ((e?.sends?.length ?? 0) > 0) channel = true;
|
|
@@ -533,7 +540,16 @@ export async function bootApplication(input: BootOptions = {}): Promise<BootResu
|
|
|
533
540
|
// 4d. Scheduler — one timer drives clock ticks, durable-run resume, heartbeat.
|
|
534
541
|
let schedulerTimer: ReturnType<typeof setInterval> | undefined;
|
|
535
542
|
const startScheduler = options.startScheduler ?? env !== "test";
|
|
536
|
-
|
|
543
|
+
const signalBus = signal?.bus ?? undefined;
|
|
544
|
+
let refreshChannel: (() => Promise<void>) | undefined;
|
|
545
|
+
if (
|
|
546
|
+
startScheduler &&
|
|
547
|
+
(clock !== undefined ||
|
|
548
|
+
journal !== undefined ||
|
|
549
|
+
instances !== undefined ||
|
|
550
|
+
signalBus !== undefined ||
|
|
551
|
+
needs.channel)
|
|
552
|
+
) {
|
|
537
553
|
const period = options.schedulerIntervalMs ?? 1000;
|
|
538
554
|
const clockRt = clock;
|
|
539
555
|
const durableResume = options.onDurableResume;
|
|
@@ -545,9 +561,17 @@ export async function bootApplication(input: BootOptions = {}): Promise<BootResu
|
|
|
545
561
|
console.error(err);
|
|
546
562
|
};
|
|
547
563
|
schedulerTimer = setInterval(() => {
|
|
564
|
+
if (signalBus) void signalBus.drain().catch(ignoreBenignSql);
|
|
565
|
+
if (refreshChannel) void refreshChannel().catch(ignoreBenignSql);
|
|
548
566
|
if (clockRt) void Promise.resolve(clockRt.tick()).catch(ignoreBenignSql);
|
|
549
567
|
if (journal && durableResume) void Promise.resolve(durableResume()).catch(ignoreBenignSql);
|
|
550
568
|
if (fleet) void fleet.maybeHeartbeat().catch(ignoreBenignSql);
|
|
569
|
+
const stem = ["run", "events"].join("-");
|
|
570
|
+
void import(`../elements/ai/${stem}.ts`)
|
|
571
|
+
.then((mod: typeof import("../elements/ai/run-events.ts")) =>
|
|
572
|
+
mod.sweepInstalledAgentEvents(),
|
|
573
|
+
)
|
|
574
|
+
.catch(ignoreBenignSql);
|
|
551
575
|
}, period);
|
|
552
576
|
schedulerTimer.unref?.();
|
|
553
577
|
}
|
|
@@ -561,13 +585,14 @@ export async function bootApplication(input: BootOptions = {}): Promise<BootResu
|
|
|
561
585
|
// 5. Channel
|
|
562
586
|
let channel = pre.channel;
|
|
563
587
|
if (needs.channel && !channel) {
|
|
564
|
-
channel = channelBind!.bindChannel(options, env, now, docker);
|
|
588
|
+
channel = await channelBind!.bindChannel(options, env, now, docker);
|
|
589
|
+
refreshChannel = channel.refresh?.bind(channel);
|
|
565
590
|
}
|
|
566
591
|
|
|
567
592
|
// 6. AI
|
|
568
593
|
let ai = pre.ai;
|
|
569
594
|
if (needs.ai && !ai) {
|
|
570
|
-
ai = aiBind!.bindAi(options, gate, now, env, docker, vault);
|
|
595
|
+
ai = aiBind!.bindAi(options, gate, now, env, docker, vault, journal?.store);
|
|
571
596
|
}
|
|
572
597
|
|
|
573
598
|
// 7. Runs
|
|
@@ -25,6 +25,7 @@ export const BUILTIN_ERROR_STATUS = {
|
|
|
25
25
|
IdempotencyKeyInvalid: 400,
|
|
26
26
|
IdempotencyKeyReused: 422,
|
|
27
27
|
IdempotencyInProgress: 409,
|
|
28
|
+
JournalLeaseBusy: 409,
|
|
28
29
|
} as const;
|
|
29
30
|
|
|
30
31
|
/** Built-in error code (including `DatabaseError`). */
|
|
@@ -94,6 +95,7 @@ export type BuiltinErrorMap = {
|
|
|
94
95
|
readonly IdempotencyKeyInvalid: Record<string, never>;
|
|
95
96
|
readonly IdempotencyKeyReused: Record<string, never>;
|
|
96
97
|
readonly IdempotencyInProgress: { readonly retryAfter?: number };
|
|
98
|
+
readonly JournalLeaseBusy: { readonly retryAfter?: number };
|
|
97
99
|
};
|
|
98
100
|
|
|
99
101
|
/**
|
package/src/kernel/capability.ts
CHANGED
|
@@ -21,6 +21,7 @@ const UNDECLARED_KEY: Readonly<Record<EffectKind, keyof typeof OKE_ERRORS>> = {
|
|
|
21
21
|
secret: "UNDECLARED_SECRET",
|
|
22
22
|
call: "UNDECLARED_CALL",
|
|
23
23
|
fetch: "UNDECLARED_FETCH",
|
|
24
|
+
decide: "UNDECLARED_DECIDE",
|
|
24
25
|
};
|
|
25
26
|
|
|
26
27
|
/** Maps effect kind → the corresponding `Effects` field. */
|
|
@@ -34,6 +35,7 @@ const EFFECTS_FIELD: Readonly<Record<EffectKind, keyof Effects>> = {
|
|
|
34
35
|
secret: "secrets",
|
|
35
36
|
call: "calls",
|
|
36
37
|
fetch: "fetches",
|
|
38
|
+
decide: "decides",
|
|
37
39
|
};
|
|
38
40
|
|
|
39
41
|
/**
|
|
@@ -91,6 +93,7 @@ export function createCapabilityToken(flow: string, declared?: Effects): Capabil
|
|
|
91
93
|
secret: new Set(effects.secrets ?? []),
|
|
92
94
|
call: new Set(effects.calls ?? []),
|
|
93
95
|
fetch: new Set(effects.fetches ?? []),
|
|
96
|
+
decide: new Set(effects.decides ?? []),
|
|
94
97
|
};
|
|
95
98
|
|
|
96
99
|
return {
|