okengine 0.1.4
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 +91 -0
- package/README.md +327 -0
- package/docs/spec/complete-example.md +1188 -0
- package/docs/spec/console.md +739 -0
- package/docs/spec/four-applications.md +1188 -0
- package/docs/spec/unified-theory.md +489 -0
- package/package.json +152 -0
- package/spec/manifest.v1.schema.json +624 -0
- package/src/auth/api-keys.ts +168 -0
- package/src/auth/attenuation.ts +116 -0
- package/src/auth/auth.test.ts +273 -0
- package/src/auth/cross-plane.ts +98 -0
- package/src/auth/index.ts +112 -0
- package/src/auth/invites.ts +66 -0
- package/src/auth/invoke-as.ts +49 -0
- package/src/auth/operator.ts +178 -0
- package/src/auth/planes.ts +102 -0
- package/src/auth/plugin.ts +44 -0
- package/src/auth/roles.ts +84 -0
- package/src/auth/sessions.ts +418 -0
- package/src/auth/tables.ts +131 -0
- package/src/cli/args.ts +50 -0
- package/src/cli/branch.ts +75 -0
- package/src/cli/build.ts +86 -0
- package/src/cli/client-add.test.ts +140 -0
- package/src/cli/client-add.ts +164 -0
- package/src/cli/completion.test.ts +58 -0
- package/src/cli/completion.ts +296 -0
- package/src/cli/dev-app-runner.ts +77 -0
- package/src/cli/dev.test.ts +435 -0
- package/src/cli/dev.ts +594 -0
- package/src/cli/doc-drift.test.ts +146 -0
- package/src/cli/doc-drift.ts +397 -0
- package/src/cli/docker-cli.test.ts +221 -0
- package/src/cli/docker.ts +127 -0
- package/src/cli/doctor-diff-examples.ts +92 -0
- package/src/cli/doctor-diff.test.ts +91 -0
- package/src/cli/doctor-diff.ts +274 -0
- package/src/cli/doctor-pii.test.ts +98 -0
- package/src/cli/doctor-pii.ts +151 -0
- package/src/cli/doctor.test.ts +150 -0
- package/src/cli/doctor.ts +223 -0
- package/src/cli/eval.ts +122 -0
- package/src/cli/exit.ts +19 -0
- package/src/cli/gates-list.test.ts +47 -0
- package/src/cli/gates-list.ts +84 -0
- package/src/cli/images.ts +277 -0
- package/src/cli/index.ts +99 -0
- package/src/cli/json-out.test.ts +129 -0
- package/src/cli/load-config.ts +71 -0
- package/src/cli/mcp-from-console.ts +50 -0
- package/src/cli/privacy-erase.ts +73 -0
- package/src/cli/registry.ts +455 -0
- package/src/cli/safe-defaults.test.ts +92 -0
- package/src/cli/schema.ts +196 -0
- package/src/cli/stack.ts +82 -0
- package/src/cli/start.ts +106 -0
- package/src/cli/upgrade.ts +105 -0
- package/src/cli/vault-cmd.ts +140 -0
- package/src/client/budget-entry.ts +34 -0
- package/src/client/budget.test.ts +17 -0
- package/src/client/create.test.ts +159 -0
- package/src/client/create.ts +152 -0
- package/src/client/errors.ts +60 -0
- package/src/client/index.ts +35 -0
- package/src/client/notes-contract.test.ts +289 -0
- package/src/client/transport.test.ts +123 -0
- package/src/client/transport.ts +254 -0
- package/src/client/types.ts +282 -0
- package/src/compiler/aot.test.ts +248 -0
- package/src/compiler/aot.ts +182 -0
- package/src/compiler/differential.test.ts +241 -0
- package/src/compiler/dynamic.ts +54 -0
- package/src/compiler/effects-infer.ts +541 -0
- package/src/compiler/emit.ts +40 -0
- package/src/compiler/extract.test.ts +177 -0
- package/src/compiler/extract.ts +1368 -0
- package/src/compiler/fixtures/raw/raw-unannotated.ts +28 -0
- package/src/compiler/fixtures/skyport/oke.config.ts +24 -0
- package/src/compiler/fixtures/skyport/src/ai.ts +15 -0
- package/src/compiler/fixtures/skyport/src/app.ts +10 -0
- package/src/compiler/fixtures/skyport/src/channels.ts +6 -0
- package/src/compiler/fixtures/skyport/src/core.ts +3 -0
- package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +57 -0
- package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +11 -0
- package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +14 -0
- package/src/compiler/fixtures/skyport/src/flows/support/index.ts +20 -0
- package/src/compiler/fixtures/skyport/src/gates.ts +14 -0
- package/src/compiler/fixtures/skyport/src/journeys.ts +9 -0
- package/src/compiler/fixtures/skyport/src/schema.ts +3 -0
- package/src/compiler/fixtures/skyport/src/vault.ts +6 -0
- package/src/compiler/fixtures/skyport.expected.json +241 -0
- package/src/compiler/fixtures/triggers/five-triggers.ts +69 -0
- package/src/compiler/http-parse.ts +214 -0
- package/src/compiler/index.ts +67 -0
- package/src/compiler/response.ts +89 -0
- package/src/compiler/sucrose.ts +238 -0
- package/src/config/index.ts +167 -0
- package/src/console/budget.test.ts +73 -0
- package/src/console/index.ts +30 -0
- package/src/console/server/access.test.ts +370 -0
- package/src/console/server/access.ts +777 -0
- package/src/console/server/ai.test.ts +172 -0
- package/src/console/server/ai.ts +632 -0
- package/src/console/server/app.ts +294 -0
- package/src/console/server/auth-rate.test.ts +81 -0
- package/src/console/server/auth-rate.ts +72 -0
- package/src/console/server/bind.ts +25 -0
- package/src/console/server/channels.test.ts +179 -0
- package/src/console/server/channels.ts +469 -0
- package/src/console/server/claim.test.ts +66 -0
- package/src/console/server/claim.ts +134 -0
- package/src/console/server/clock.test.ts +239 -0
- package/src/console/server/clock.ts +390 -0
- package/src/console/server/console.test.ts +338 -0
- package/src/console/server/diff.test.ts +299 -0
- package/src/console/server/diff.ts +352 -0
- package/src/console/server/dry-run.audit.test.ts +118 -0
- package/src/console/server/flows-invoke.test.ts +148 -0
- package/src/console/server/flows.ts +3633 -0
- package/src/console/server/gates.test.ts +360 -0
- package/src/console/server/gates.ts +869 -0
- package/src/console/server/index.ts +141 -0
- package/src/console/server/live.test.ts +30 -0
- package/src/console/server/live.ts +109 -0
- package/src/console/server/plugin.ts +50 -0
- package/src/console/server/plugins.test.ts +131 -0
- package/src/console/server/plugins.ts +429 -0
- package/src/console/server/runs-pii.ts +98 -0
- package/src/console/server/security-headers.ts +97 -0
- package/src/console/server/security.gate.test.ts +286 -0
- package/src/console/server/serve.ts +319 -0
- package/src/console/server/signals.test.ts +88 -0
- package/src/console/server/signals.ts +282 -0
- package/src/console/server/state.ts +982 -0
- package/src/console/server/store.test.ts +233 -0
- package/src/console/server/store.ts +861 -0
- package/src/console/server/structural.ts +95 -0
- package/src/console/server/vault.test.ts +251 -0
- package/src/console/server/vault.ts +430 -0
- package/src/console/ui/access/AccessA11yView.tsx +237 -0
- package/src/console/ui/access/a11y.test.tsx +122 -0
- package/src/console/ui/access/acknowledgement.test.ts +13 -0
- package/src/console/ui/access/acknowledgement.ts +35 -0
- package/src/console/ui/access/blast-radius.test.ts +26 -0
- package/src/console/ui/access/blast-radius.ts +40 -0
- package/src/console/ui/access/confirmation.test.ts +38 -0
- package/src/console/ui/access/confirmation.ts +36 -0
- package/src/console/ui/access/fixture.ts +203 -0
- package/src/console/ui/access/grantable.test.ts +17 -0
- package/src/console/ui/access/grantable.ts +36 -0
- package/src/console/ui/access/hygiene.test.ts +12 -0
- package/src/console/ui/access/hygiene.ts +39 -0
- package/src/console/ui/access/index.ts +54 -0
- package/src/console/ui/access/provenance.test.ts +12 -0
- package/src/console/ui/access/provenance.ts +29 -0
- package/src/console/ui/access/search.test.ts +17 -0
- package/src/console/ui/access/search.ts +72 -0
- package/src/console/ui/access/types.ts +111 -0
- package/src/console/ui/ai/AiA11yView.tsx +265 -0
- package/src/console/ui/ai/a11y.test.tsx +99 -0
- package/src/console/ui/ai/findings.ts +41 -0
- package/src/console/ui/ai/fixture.ts +182 -0
- package/src/console/ui/ai/format.ts +90 -0
- package/src/console/ui/ai/group.test.ts +37 -0
- package/src/console/ui/ai/group.ts +88 -0
- package/src/console/ui/ai/index.ts +66 -0
- package/src/console/ui/ai/promotion.test.ts +77 -0
- package/src/console/ui/ai/promotion.ts +150 -0
- package/src/console/ui/ai/search.test.ts +40 -0
- package/src/console/ui/ai/search.ts +89 -0
- package/src/console/ui/ai/types.ts +139 -0
- package/src/console/ui/architecture/ArchitectureA11yView.tsx +163 -0
- package/src/console/ui/architecture/a11y.test.tsx +97 -0
- package/src/console/ui/architecture/boundary.ts +42 -0
- package/src/console/ui/architecture/declared.ts +165 -0
- package/src/console/ui/architecture/fixture.ts +74 -0
- package/src/console/ui/architecture/index.ts +84 -0
- package/src/console/ui/architecture/layers.ts +64 -0
- package/src/console/ui/architecture/layout.ts +68 -0
- package/src/console/ui/architecture/pathologies.ts +260 -0
- package/src/console/ui/architecture/search.ts +145 -0
- package/src/console/ui/architecture/traffic.ts +102 -0
- package/src/console/ui/architecture/types.ts +144 -0
- package/src/console/ui/architecture/view.test.ts +165 -0
- package/src/console/ui/architecture/view.ts +516 -0
- package/src/console/ui/channels/ChannelsA11yView.tsx +180 -0
- package/src/console/ui/channels/a11y.test.tsx +96 -0
- package/src/console/ui/channels/confirmation.test.ts +37 -0
- package/src/console/ui/channels/confirmation.ts +27 -0
- package/src/console/ui/channels/fallback.test.ts +15 -0
- package/src/console/ui/channels/fallback.ts +20 -0
- package/src/console/ui/channels/findings.ts +38 -0
- package/src/console/ui/channels/fixture.ts +134 -0
- package/src/console/ui/channels/group.ts +25 -0
- package/src/console/ui/channels/index.ts +57 -0
- package/src/console/ui/channels/locale.test.ts +17 -0
- package/src/console/ui/channels/locale.ts +31 -0
- package/src/console/ui/channels/mask.test.ts +14 -0
- package/src/console/ui/channels/mask.ts +12 -0
- package/src/console/ui/channels/search.ts +58 -0
- package/src/console/ui/channels/taxonomy.test.ts +17 -0
- package/src/console/ui/channels/taxonomy.ts +49 -0
- package/src/console/ui/channels/types.ts +117 -0
- package/src/console/ui/clock/ClockA11yView.tsx +182 -0
- package/src/console/ui/clock/a11y.test.tsx +99 -0
- package/src/console/ui/clock/confirmation.test.ts +32 -0
- package/src/console/ui/clock/confirmation.ts +34 -0
- package/src/console/ui/clock/findings.ts +40 -0
- package/src/console/ui/clock/fixture.ts +111 -0
- package/src/console/ui/clock/health.test.ts +25 -0
- package/src/console/ui/clock/health.ts +61 -0
- package/src/console/ui/clock/index.ts +49 -0
- package/src/console/ui/clock/search.test.ts +39 -0
- package/src/console/ui/clock/search.ts +76 -0
- package/src/console/ui/clock/timeline.test.ts +44 -0
- package/src/console/ui/clock/timeline.ts +41 -0
- package/src/console/ui/clock/types.ts +78 -0
- package/src/console/ui/clock/waiting-on.test.ts +34 -0
- package/src/console/ui/clock/waiting-on.ts +83 -0
- package/src/console/ui/confirmation.unify.test.ts +92 -0
- package/src/console/ui/diff/DiffA11yView.tsx +85 -0
- package/src/console/ui/diff/a11y.test.tsx +99 -0
- package/src/console/ui/diff/fixture.ts +85 -0
- package/src/console/ui/diff/group.test.ts +58 -0
- package/src/console/ui/diff/group.ts +97 -0
- package/src/console/ui/diff/index.ts +38 -0
- package/src/console/ui/diff/search.test.ts +32 -0
- package/src/console/ui/diff/search.ts +75 -0
- package/src/console/ui/diff/summary.ts +74 -0
- package/src/console/ui/diff/types.ts +48 -0
- package/src/console/ui/dist/assets/index-B71Yl_SS.js +10 -0
- package/src/console/ui/dist/assets/panel-access-Dd37LU2c.js +64 -0
- package/src/console/ui/dist/assets/panel-ai-CC7LR6-J.js +1 -0
- package/src/console/ui/dist/assets/panel-architecture-B5b3iKCz.js +1 -0
- package/src/console/ui/dist/assets/panel-channels-CeNjTKXp.js +1 -0
- package/src/console/ui/dist/assets/panel-clock-DgFTLoHV.js +1 -0
- package/src/console/ui/dist/assets/panel-diff-DxehccqB.js +1 -0
- package/src/console/ui/dist/assets/panel-flows-BtrVn-Eg.js +45 -0
- package/src/console/ui/dist/assets/panel-gates-Z9MKRGdH.js +1 -0
- package/src/console/ui/dist/assets/panel-overview-Bd48d9km.js +1 -0
- package/src/console/ui/dist/assets/panel-plugins-DWd0TowH.js +1 -0
- package/src/console/ui/dist/assets/panel-runs-BwsWqKeB.js +1 -0
- package/src/console/ui/dist/assets/panel-signals-9najbZY2.js +1 -0
- package/src/console/ui/dist/assets/panel-store-OHkP2pDp.js +1 -0
- package/src/console/ui/dist/assets/panel-traces-tn2JoY8U.js +1 -0
- package/src/console/ui/dist/assets/panel-vault-BbfWdox0.js +1 -0
- package/src/console/ui/dist/assets/rolldown-runtime-CNC7AqOf.js +1 -0
- package/src/console/ui/dist/assets/style-Cnl7WLya.css +3 -0
- package/src/console/ui/dist/index.html +14 -0
- package/src/console/ui/flows/FlowsA11yView.tsx +155 -0
- package/src/console/ui/flows/a11y.test.tsx +105 -0
- package/src/console/ui/flows/buffer.test.ts +91 -0
- package/src/console/ui/flows/buffer.ts +118 -0
- package/src/console/ui/flows/confirmation.ts +131 -0
- package/src/console/ui/flows/contract.test.ts +89 -0
- package/src/console/ui/flows/contract.ts +353 -0
- package/src/console/ui/flows/fixture.ts +81 -0
- package/src/console/ui/flows/graph.test.ts +101 -0
- package/src/console/ui/flows/graph.ts +549 -0
- package/src/console/ui/flows/index.ts +87 -0
- package/src/console/ui/flows/save-as-test.test.ts +47 -0
- package/src/console/ui/flows/save-as-test.ts +99 -0
- package/src/console/ui/flows/search.ts +255 -0
- package/src/console/ui/flows/tiers.ts +140 -0
- package/src/console/ui/gates/GatesA11yView.tsx +192 -0
- package/src/console/ui/gates/a11y.test.tsx +116 -0
- package/src/console/ui/gates/audit.test.ts +40 -0
- package/src/console/ui/gates/audit.ts +63 -0
- package/src/console/ui/gates/denial.test.ts +36 -0
- package/src/console/ui/gates/denial.ts +51 -0
- package/src/console/ui/gates/findings.ts +19 -0
- package/src/console/ui/gates/fixture.ts +189 -0
- package/src/console/ui/gates/group.test.ts +30 -0
- package/src/console/ui/gates/group.ts +102 -0
- package/src/console/ui/gates/index.ts +47 -0
- package/src/console/ui/gates/search.test.ts +49 -0
- package/src/console/ui/gates/search.ts +115 -0
- package/src/console/ui/gates/types.ts +121 -0
- package/src/console/ui/overview/OverviewA11yView.tsx +152 -0
- package/src/console/ui/overview/a11y.test.tsx +113 -0
- package/src/console/ui/overview/busiest.ts +26 -0
- package/src/console/ui/overview/compose.ts +133 -0
- package/src/console/ui/overview/cost.test.ts +39 -0
- package/src/console/ui/overview/cost.ts +138 -0
- package/src/console/ui/overview/fixture.ts +161 -0
- package/src/console/ui/overview/golden.ts +57 -0
- package/src/console/ui/overview/index.ts +59 -0
- package/src/console/ui/overview/rank.test.ts +136 -0
- package/src/console/ui/overview/rank.ts +210 -0
- package/src/console/ui/overview/slo.test.ts +73 -0
- package/src/console/ui/overview/slo.ts +229 -0
- package/src/console/ui/overview/types.ts +121 -0
- package/src/console/ui/overview/verdict.ts +131 -0
- package/src/console/ui/plugins/PluginsA11yView.tsx +227 -0
- package/src/console/ui/plugins/a11y.test.tsx +97 -0
- package/src/console/ui/plugins/command.test.ts +22 -0
- package/src/console/ui/plugins/command.ts +27 -0
- package/src/console/ui/plugins/findings.ts +39 -0
- package/src/console/ui/plugins/fixture.ts +233 -0
- package/src/console/ui/plugins/group.test.ts +38 -0
- package/src/console/ui/plugins/group.ts +67 -0
- package/src/console/ui/plugins/index.ts +40 -0
- package/src/console/ui/plugins/search.ts +67 -0
- package/src/console/ui/plugins/types.ts +115 -0
- package/src/console/ui/runs/RunsA11yView.tsx +267 -0
- package/src/console/ui/runs/a11y.test.tsx +99 -0
- package/src/console/ui/runs/explain.test.ts +23 -0
- package/src/console/ui/runs/explain.ts +32 -0
- package/src/console/ui/runs/fixture.ts +189 -0
- package/src/console/ui/runs/group.test.ts +24 -0
- package/src/console/ui/runs/group.ts +85 -0
- package/src/console/ui/runs/histogram.test.ts +31 -0
- package/src/console/ui/runs/histogram.ts +85 -0
- package/src/console/ui/runs/index.ts +75 -0
- package/src/console/ui/runs/project.ts +122 -0
- package/src/console/ui/runs/query.test.ts +50 -0
- package/src/console/ui/runs/query.ts +283 -0
- package/src/console/ui/runs/search.test.ts +45 -0
- package/src/console/ui/runs/search.ts +141 -0
- package/src/console/ui/runs/trace-link.test.ts +30 -0
- package/src/console/ui/runs/trace-link.ts +99 -0
- package/src/console/ui/runs/types.ts +137 -0
- package/src/console/ui/shell/App.tsx +56 -0
- package/src/console/ui/shell/client.ts +1715 -0
- package/src/console/ui/shell/components/ui.tsx +110 -0
- package/src/console/ui/shell/index.html +12 -0
- package/src/console/ui/shell/layout/Shell.tsx +95 -0
- package/src/console/ui/shell/main.tsx +281 -0
- package/src/console/ui/shell/panels/Access.tsx +5 -0
- package/src/console/ui/shell/panels/Ai.tsx +5 -0
- package/src/console/ui/shell/panels/Architecture.tsx +5 -0
- package/src/console/ui/shell/panels/Channels.tsx +5 -0
- package/src/console/ui/shell/panels/Clock.tsx +5 -0
- package/src/console/ui/shell/panels/Diff.tsx +5 -0
- package/src/console/ui/shell/panels/Flows.tsx +5 -0
- package/src/console/ui/shell/panels/Gates.tsx +5 -0
- package/src/console/ui/shell/panels/Overview.tsx +6 -0
- package/src/console/ui/shell/panels/Plugins.tsx +5 -0
- package/src/console/ui/shell/panels/Runs.tsx +5 -0
- package/src/console/ui/shell/panels/Signals.tsx +5 -0
- package/src/console/ui/shell/panels/Store.tsx +5 -0
- package/src/console/ui/shell/panels/Traces.tsx +5 -0
- package/src/console/ui/shell/panels/Vault.tsx +5 -0
- package/src/console/ui/shell/panels/access/AccessPanel.tsx +813 -0
- package/src/console/ui/shell/panels/ai/AiPanel.tsx +563 -0
- package/src/console/ui/shell/panels/architecture/ArchitecturePanel.tsx +377 -0
- package/src/console/ui/shell/panels/channels/ChannelsPanel.tsx +454 -0
- package/src/console/ui/shell/panels/clock/ClockPanel.tsx +479 -0
- package/src/console/ui/shell/panels/diff/DiffPanel.tsx +275 -0
- package/src/console/ui/shell/panels/flows/ContractEditor.tsx +301 -0
- package/src/console/ui/shell/panels/flows/FlowDrawer.tsx +350 -0
- package/src/console/ui/shell/panels/flows/FlowsPanel.tsx +502 -0
- package/src/console/ui/shell/panels/gates/GatesPanel.tsx +482 -0
- package/src/console/ui/shell/panels/overview/OverviewPanel.tsx +428 -0
- package/src/console/ui/shell/panels/plugins/PluginsPanel.tsx +413 -0
- package/src/console/ui/shell/panels/runs/RunsPanel.tsx +649 -0
- package/src/console/ui/shell/panels/signals/SignalsPanel.tsx +840 -0
- package/src/console/ui/shell/panels/store/StorePanel.tsx +787 -0
- package/src/console/ui/shell/panels/traces/TracesPanel.tsx +539 -0
- package/src/console/ui/shell/panels/vault/VaultPanel.tsx +449 -0
- package/src/console/ui/shell/plugin/Sandbox.tsx +29 -0
- package/src/console/ui/shell/setup/Wizard.tsx +189 -0
- package/src/console/ui/shell/styles.css +75 -0
- package/src/console/ui/signals/SignalsA11yView.tsx +276 -0
- package/src/console/ui/signals/a11y.test.tsx +99 -0
- package/src/console/ui/signals/confirmation.test.ts +39 -0
- package/src/console/ui/signals/confirmation.ts +63 -0
- package/src/console/ui/signals/dry-run.test.ts +18 -0
- package/src/console/ui/signals/dry-run.ts +36 -0
- package/src/console/ui/signals/durable.test.ts +22 -0
- package/src/console/ui/signals/durable.ts +41 -0
- package/src/console/ui/signals/findings.ts +33 -0
- package/src/console/ui/signals/fixture.ts +242 -0
- package/src/console/ui/signals/group.test.ts +32 -0
- package/src/console/ui/signals/group.ts +49 -0
- package/src/console/ui/signals/index.ts +52 -0
- package/src/console/ui/signals/monitor.test.ts +30 -0
- package/src/console/ui/signals/monitor.ts +79 -0
- package/src/console/ui/signals/schema-form.test.ts +34 -0
- package/src/console/ui/signals/schema-form.ts +112 -0
- package/src/console/ui/signals/search.ts +102 -0
- package/src/console/ui/signals/types.ts +71 -0
- package/src/console/ui/store/StoreA11yView.tsx +199 -0
- package/src/console/ui/store/a11y.test.tsx +99 -0
- package/src/console/ui/store/cache-view.test.ts +13 -0
- package/src/console/ui/store/cache-view.ts +36 -0
- package/src/console/ui/store/confirmation.test.ts +38 -0
- package/src/console/ui/store/confirmation.ts +55 -0
- package/src/console/ui/store/dry-run.test.ts +15 -0
- package/src/console/ui/store/dry-run.ts +29 -0
- package/src/console/ui/store/fixture.ts +163 -0
- package/src/console/ui/store/group.test.ts +21 -0
- package/src/console/ui/store/group.ts +41 -0
- package/src/console/ui/store/index.ts +40 -0
- package/src/console/ui/store/search.ts +78 -0
- package/src/console/ui/store/types.ts +65 -0
- package/src/console/ui/store/will-not-fire.test.ts +19 -0
- package/src/console/ui/store/will-not-fire.ts +40 -0
- package/src/console/ui/traces/TracesA11yView.tsx +200 -0
- package/src/console/ui/traces/a11y.test.tsx +97 -0
- package/src/console/ui/traces/chain.test.ts +61 -0
- package/src/console/ui/traces/chain.ts +144 -0
- package/src/console/ui/traces/critical-path.test.ts +24 -0
- package/src/console/ui/traces/critical-path.ts +59 -0
- package/src/console/ui/traces/filter.test.ts +78 -0
- package/src/console/ui/traces/filter.ts +112 -0
- package/src/console/ui/traces/fixture.ts +126 -0
- package/src/console/ui/traces/fold.test.ts +67 -0
- package/src/console/ui/traces/fold.ts +170 -0
- package/src/console/ui/traces/index.ts +64 -0
- package/src/console/ui/traces/mini.ts +65 -0
- package/src/console/ui/traces/replay.test.ts +24 -0
- package/src/console/ui/traces/replay.ts +35 -0
- package/src/console/ui/traces/sampling.test.ts +26 -0
- package/src/console/ui/traces/sampling.ts +66 -0
- package/src/console/ui/traces/search.ts +148 -0
- package/src/console/ui/traces/tier.ts +72 -0
- package/src/console/ui/traces/types.ts +125 -0
- package/src/console/ui/tsconfig.json +20 -0
- package/src/console/ui/vault/VaultA11yView.tsx +177 -0
- package/src/console/ui/vault/a11y.test.tsx +97 -0
- package/src/console/ui/vault/blast-radius.test.ts +35 -0
- package/src/console/ui/vault/blast-radius.ts +55 -0
- package/src/console/ui/vault/confirmation.test.ts +41 -0
- package/src/console/ui/vault/confirmation.ts +39 -0
- package/src/console/ui/vault/dormant.test.ts +37 -0
- package/src/console/ui/vault/dormant.ts +28 -0
- package/src/console/ui/vault/export-safe.test.ts +30 -0
- package/src/console/ui/vault/export-safe.ts +65 -0
- package/src/console/ui/vault/fixture.ts +75 -0
- package/src/console/ui/vault/group.test.ts +25 -0
- package/src/console/ui/vault/group.ts +50 -0
- package/src/console/ui/vault/index.ts +45 -0
- package/src/console/ui/vault/search.ts +62 -0
- package/src/console/ui/vault/types.ts +57 -0
- package/src/console/ui/vite.config.ts +84 -0
- package/src/console/xss.gate.test.ts +118 -0
- package/src/docker/compose.ts +226 -0
- package/src/docker/credentials.ts +25 -0
- package/src/docker/derive.ts +83 -0
- package/src/docker/docker.test.ts +185 -0
- package/src/docker/dockerfile.integration.test.ts +170 -0
- package/src/docker/dockerfile.ts +39 -0
- package/src/docker/helpers.ts +103 -0
- package/src/docker/index.ts +52 -0
- package/src/docker/pin.ts +90 -0
- package/src/docker/recipes/index.ts +33 -0
- package/src/docker/recipes/postgres.ts +18 -0
- package/src/docker/recipes/redis.ts +18 -0
- package/src/docker/stack.integration.test.ts +140 -0
- package/src/docker/stack.ts +62 -0
- package/src/docker/types.ts +134 -0
- package/src/drivers/ai-anthropic.ts +128 -0
- package/src/drivers/ai-mock.ts +82 -0
- package/src/drivers/ai-openai-compatible.ts +139 -0
- package/src/drivers/ai-providers.test.ts +163 -0
- package/src/drivers/ai-types.ts +83 -0
- package/src/drivers/bun-native-completeness.test.ts +155 -0
- package/src/drivers/channel-console.ts +118 -0
- package/src/drivers/channel-fcm.ts +96 -0
- package/src/drivers/channel-resend.ts +27 -0
- package/src/drivers/channel-smtp.ts +33 -0
- package/src/drivers/channel-types.ts +160 -0
- package/src/drivers/channel-unifonic.ts +79 -0
- package/src/drivers/channel-wa-cloud.ts +84 -0
- package/src/drivers/channel-webpush.ts +317 -0
- package/src/drivers/conformance.test.ts +89 -0
- package/src/drivers/conformance.ts +162 -0
- package/src/drivers/fs.ts +73 -0
- package/src/drivers/index.ts +194 -0
- package/src/drivers/kv-lua.ts +222 -0
- package/src/drivers/memory.ts +472 -0
- package/src/drivers/pgvector.ts +130 -0
- package/src/drivers/postgres.ts +221 -0
- package/src/drivers/redis.ts +254 -0
- package/src/drivers/s3.ts +129 -0
- package/src/drivers/signal-engine.ts +690 -0
- package/src/drivers/signal-memory.ts +27 -0
- package/src/drivers/signal-nats.ts +131 -0
- package/src/drivers/signal-postgres.ts +1089 -0
- package/src/drivers/signal-redis.ts +275 -0
- package/src/drivers/signal-types.ts +328 -0
- package/src/drivers/sqlite.ts +53 -0
- package/src/drivers/types.ts +334 -0
- package/src/drivers/vault-dotenv-parse.ts +56 -0
- package/src/drivers/vault-env.ts +101 -0
- package/src/drivers/vault-infisical.ts +61 -0
- package/src/drivers/vault-managed.ts +42 -0
- package/src/drivers/vault-memory.ts +38 -0
- package/src/drivers/vault-openbao.ts +80 -0
- package/src/drivers/vault-sops.ts +252 -0
- package/src/drivers/vault-types.ts +84 -0
- package/src/elements/ai/declare.ts +206 -0
- package/src/elements/ai/eval.ts +113 -0
- package/src/elements/ai/pii.ts +108 -0
- package/src/elements/ai/runtime.ts +540 -0
- package/src/elements/ai/schema.ts +194 -0
- package/src/elements/ai.test.ts +393 -0
- package/src/elements/ai.ts +56 -0
- package/src/elements/channel/consent.ts +81 -0
- package/src/elements/channel/costs.test.ts +62 -0
- package/src/elements/channel/costs.ts +130 -0
- package/src/elements/channel/declare.ts +141 -0
- package/src/elements/channel/email-auth.test.ts +47 -0
- package/src/elements/channel/email-auth.ts +109 -0
- package/src/elements/channel/locale.ts +97 -0
- package/src/elements/channel/mask.test.ts +19 -0
- package/src/elements/channel/mask.ts +60 -0
- package/src/elements/channel/mime.ts +23 -0
- package/src/elements/channel/outcomes.test.ts +40 -0
- package/src/elements/channel/outcomes.ts +126 -0
- package/src/elements/channel/receipts.ts +121 -0
- package/src/elements/channel/runtime.ts +500 -0
- package/src/elements/channel/suppression.ts +136 -0
- package/src/elements/channel.test.ts +232 -0
- package/src/elements/channel.ts +109 -0
- package/src/elements/clock/actions.ts +170 -0
- package/src/elements/clock/declare.ts +58 -0
- package/src/elements/clock/dst.ts +197 -0
- package/src/elements/clock/durable.ts +140 -0
- package/src/elements/clock/duration.ts +29 -0
- package/src/elements/clock/health.test.ts +103 -0
- package/src/elements/clock/health.ts +112 -0
- package/src/elements/clock/leader.ts +127 -0
- package/src/elements/clock/reconcile.ts +241 -0
- package/src/elements/clock/runtime.ts +243 -0
- package/src/elements/clock/schedule.ts +262 -0
- package/src/elements/clock/time-travel.ts +53 -0
- package/src/elements/clock.test.ts +365 -0
- package/src/elements/clock.ts +80 -0
- package/src/elements/gate/declare.ts +134 -0
- package/src/elements/gate/permissions.ts +104 -0
- package/src/elements/gate/runtime.ts +177 -0
- package/src/elements/gate/strategies.ts +388 -0
- package/src/elements/gate.test.ts +145 -0
- package/src/elements/gate.ts +41 -0
- package/src/elements/index.ts +169 -0
- package/src/elements/signal/chaos-child.ts +47 -0
- package/src/elements/signal/declare.ts +77 -0
- package/src/elements/signal/dry-run-replay.test.ts +95 -0
- package/src/elements/signal/dry-run-write-isolation.test.ts +94 -0
- package/src/elements/signal/reconcile.test.ts +23 -0
- package/src/elements/signal/reconcile.ts +122 -0
- package/src/elements/signal/runtime.ts +94 -0
- package/src/elements/signal.test.ts +457 -0
- package/src/elements/signal.ts +26 -0
- package/src/elements/store/cache.ts +197 -0
- package/src/elements/store/classify.ts +125 -0
- package/src/elements/store/declare.ts +200 -0
- package/src/elements/store/files-policy.test.ts +31 -0
- package/src/elements/store/files-policy.ts +70 -0
- package/src/elements/store/replica.ts +68 -0
- package/src/elements/store/runtime.ts +393 -0
- package/src/elements/store/sql-condition.ts +205 -0
- package/src/elements/store/sql-session.ts +579 -0
- package/src/elements/store/table.ts +282 -0
- package/src/elements/store.test.ts +370 -0
- package/src/elements/store.ts +99 -0
- package/src/elements/vault/declare.ts +143 -0
- package/src/elements/vault/fingerprint.ts +30 -0
- package/src/elements/vault/redact.ts +52 -0
- package/src/elements/vault/runtime.ts +461 -0
- package/src/elements/vault.test.ts +296 -0
- package/src/elements/vault.ts +44 -0
- package/src/index.ts +183 -0
- package/src/kernel/adopt-routes.ts +194 -0
- package/src/kernel/app.ts +1068 -0
- package/src/kernel/auth-resolve.ts +93 -0
- package/src/kernel/boot.test.ts +173 -0
- package/src/kernel/boot.ts +439 -0
- package/src/kernel/budget-entry.ts +55 -0
- package/src/kernel/budget.test.ts +19 -0
- package/src/kernel/call.test.ts +83 -0
- package/src/kernel/capability.ts +113 -0
- package/src/kernel/dry-run.test.ts +64 -0
- package/src/kernel/dry-run.ts +171 -0
- package/src/kernel/effects.test.ts +89 -0
- package/src/kernel/effects.ts +140 -0
- package/src/kernel/errors.registry.test.ts +39 -0
- package/src/kernel/errors.ts +225 -0
- package/src/kernel/flow.test.ts +154 -0
- package/src/kernel/flow.ts +290 -0
- package/src/kernel/fx.test.ts +245 -0
- package/src/kernel/fx.ts +996 -0
- package/src/kernel/hook-timing.test.ts +72 -0
- package/src/kernel/hook-timing.ts +190 -0
- package/src/kernel/hooks.test.ts +181 -0
- package/src/kernel/hooks.ts +275 -0
- package/src/kernel/index.ts +278 -0
- package/src/kernel/journal.ts +405 -0
- package/src/kernel/journey.ts +75 -0
- package/src/kernel/on.ts +74 -0
- package/src/kernel/pipeline.test.ts +349 -0
- package/src/kernel/pipeline.ts +245 -0
- package/src/kernel/plug.ts +82 -0
- package/src/kernel/plugin/capabilities.test.ts +84 -0
- package/src/kernel/plugin/conflicts.test.ts +106 -0
- package/src/kernel/plugin/decorate.test.ts +111 -0
- package/src/kernel/plugin/scoping.test.ts +138 -0
- package/src/kernel/plugin.ts +485 -0
- package/src/kernel/rate-limit.ts +25 -0
- package/src/kernel/registry.ts +398 -0
- package/src/kernel/router.test.ts +114 -0
- package/src/kernel/router.ts +556 -0
- package/src/kernel/routing-budget.test.ts +19 -0
- package/src/kernel/run-telemetry.ts +70 -0
- package/src/kernel/triggers.ts +324 -0
- package/src/kernel/unit.ts +88 -0
- package/src/manifest/diff.test.ts +866 -0
- package/src/manifest/diff.ts +1670 -0
- package/src/manifest/fixtures/base.manifest.json +112 -0
- package/src/manifest/fixtures/skyport.excerpt.json +51 -0
- package/src/manifest/fixtures/skyport.manifest.json +153 -0
- package/src/manifest/index.ts +27 -0
- package/src/manifest/types.ts +358 -0
- package/src/manifest/undeclared.test.ts +82 -0
- package/src/manifest/undeclared.ts +65 -0
- package/src/manifest/validate.test.ts +105 -0
- package/src/manifest/validate.ts +174 -0
- package/src/mcp/authorization.ts +340 -0
- package/src/mcp/confirmation.ts +174 -0
- package/src/mcp/data.ts +90 -0
- package/src/mcp/index.ts +80 -0
- package/src/mcp/injection.gate.test.ts +176 -0
- package/src/mcp/mcp.test.ts +348 -0
- package/src/mcp/protocol.ts +172 -0
- package/src/mcp/server.ts +283 -0
- package/src/mcp/session.ts +139 -0
- package/src/mcp/tools.ts +391 -0
- package/src/plugins/catalogue.test.ts +48 -0
- package/src/plugins/catalogue.ts +113 -0
- package/src/plugins/node-import-scan.test.ts +28 -0
- package/src/plugins/node-import-scan.ts +98 -0
- package/src/plugins/supply-chain.test.ts +48 -0
- package/src/plugins/supply-chain.ts +460 -0
- package/src/release/index.ts +30 -0
- package/src/release/limits.ts +20 -0
- package/src/release/measure.ts +443 -0
- package/src/release/publish.ts +56 -0
- package/src/runs/bench.test.ts +93 -0
- package/src/runs/collect.ts +144 -0
- package/src/runs/drivers/clickhouse.ts +108 -0
- package/src/runs/drivers/files.ts +201 -0
- package/src/runs/drivers/memory.ts +94 -0
- package/src/runs/drivers/postgres.ts +110 -0
- package/src/runs/duckdb.ts +99 -0
- package/src/runs/index.ts +82 -0
- package/src/runs/outlier.ts +180 -0
- package/src/runs/parquet.ts +210 -0
- package/src/runs/privacy.ts +49 -0
- package/src/runs/runs.test.ts +324 -0
- package/src/runs/runtime.ts +229 -0
- package/src/runs/shred.ts +243 -0
- package/src/runs/telemetry.ts +10 -0
- package/src/runs/types.ts +179 -0
- package/src/runtime/bun.ts +140 -0
- package/src/runtime/cold-start.bench.ts +20 -0
- package/src/runtime/index.ts +46 -0
- package/src/runtime/primitives.ts +242 -0
- package/src/runtime/security.test.ts +170 -0
- package/src/runtime/security.ts +168 -0
- package/src/runtime/serve.test.ts +223 -0
- package/src/runtime/types.ts +217 -0
- package/src/runtime/web-standard.ts +77 -0
- package/src/test/create-test-app.test.ts +161 -0
- package/src/test/create-test-app.ts +533 -0
- package/src/test/index.ts +21 -0
- package/src/test/provisions.integration.test.ts +174 -0
- package/src/upgrade/codemods.test.ts +58 -0
- package/src/upgrade/codemods.ts +83 -0
- package/src/validation/index.ts +23 -0
- package/src/validation/standard-schema.test.ts +96 -0
- package/src/validation/standard-schema.ts +316 -0
package/src/kernel/fx.ts
ADDED
|
@@ -0,0 +1,996 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fx` — the single door to the outside world.
|
|
3
|
+
*
|
|
4
|
+
* All world access goes through this object. Nothing in `src/elements` or
|
|
5
|
+
* user flows may import `node:` / `bun:` modules directly. Every call that
|
|
6
|
+
* touches a declared effect kind is capability-checked and ledgered.
|
|
7
|
+
*
|
|
8
|
+
* Drivers are stubs in v1 (in-memory). The surface is swappable wholesale
|
|
9
|
+
* for deterministic tests (§7.6).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { Effects, ResourceRef } from "../manifest/types.ts";
|
|
13
|
+
import type {
|
|
14
|
+
StoreDecl,
|
|
15
|
+
StoreHandle,
|
|
16
|
+
StoreRuntime,
|
|
17
|
+
SqlStoreHandle,
|
|
18
|
+
} from "../elements/store.ts";
|
|
19
|
+
import type { SignalRuntime } from "../elements/signal.ts";
|
|
20
|
+
import type { VaultRuntime } from "../elements/vault.ts";
|
|
21
|
+
import type { ChannelRuntime } from "../elements/channel.ts";
|
|
22
|
+
import type { AiRuntime } from "../elements/ai.ts";
|
|
23
|
+
import { parseDurationMs } from "../elements/clock/duration.ts";
|
|
24
|
+
import {
|
|
25
|
+
createCapabilityToken,
|
|
26
|
+
type CapabilityToken,
|
|
27
|
+
} from "./capability.ts";
|
|
28
|
+
import {
|
|
29
|
+
createEffectLedger,
|
|
30
|
+
recordEffect,
|
|
31
|
+
reversibilityOf,
|
|
32
|
+
type EffectLedger,
|
|
33
|
+
} from "./effects.ts";
|
|
34
|
+
import {
|
|
35
|
+
DryRunWriteIsolationError,
|
|
36
|
+
isDryRun,
|
|
37
|
+
recordWouldHaveFired,
|
|
38
|
+
touchDryRunStore,
|
|
39
|
+
} from "./dry-run.ts";
|
|
40
|
+
import { fail, type FailOptions, type FlowFailure } from "./errors.ts";
|
|
41
|
+
import type { JournalSession } from "./journal.ts";
|
|
42
|
+
import type { RunTelemetry } from "./run-telemetry.ts";
|
|
43
|
+
|
|
44
|
+
/** Named ref: plain string or `{ name }` element handle. */
|
|
45
|
+
export type NamedRef = string | { readonly name: string };
|
|
46
|
+
|
|
47
|
+
/** Resolve a {@link NamedRef} to its string id. */
|
|
48
|
+
export function resolveName(ref: NamedRef): string {
|
|
49
|
+
return typeof ref === "string" ? ref : ref.name;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Resolve a store argument to a `facet:name` resource ref. */
|
|
53
|
+
export function resolveStoreRef(ref: NamedRef | { readonly ref: ResourceRef }): ResourceRef {
|
|
54
|
+
if (typeof ref === "string") return ref as ResourceRef;
|
|
55
|
+
if ("ref" in ref) return ref.ref;
|
|
56
|
+
return ref.name as ResourceRef;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Auth principal on the user plane. */
|
|
60
|
+
export interface FxAuth {
|
|
61
|
+
readonly userId: string | null;
|
|
62
|
+
readonly scopes: ReadonlySet<string>;
|
|
63
|
+
/** Whether the identity has completed verification (email / MFA). */
|
|
64
|
+
readonly verified?: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Operator principal on the Console plane. */
|
|
68
|
+
export interface FxOperator {
|
|
69
|
+
readonly id: string | null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Active tenant (multi-tenancy as a dimension of `fx`). */
|
|
73
|
+
export interface FxTenant {
|
|
74
|
+
readonly id: string | null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Clock surface on `fx`. */
|
|
78
|
+
export interface FxClock {
|
|
79
|
+
/** Current epoch-ms (injectable via {@link CreateFxOptions.now}). */
|
|
80
|
+
now(): number;
|
|
81
|
+
/**
|
|
82
|
+
* Durable sleep — when the flow is `durable`, journals the wake time and
|
|
83
|
+
* survives restart / deploy. Without a journal, resolves immediately
|
|
84
|
+
* (non-durable flows).
|
|
85
|
+
*
|
|
86
|
+
* @param label - Step label for the journal
|
|
87
|
+
* @param duration - Duration string (e.g. `"7d"`, `"2m"`)
|
|
88
|
+
*/
|
|
89
|
+
sleep(label: string, duration: string): Promise<void>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** In-memory cache surface. */
|
|
93
|
+
export interface FxCache {
|
|
94
|
+
/**
|
|
95
|
+
* Get a cached value.
|
|
96
|
+
*
|
|
97
|
+
* @param key - Cache key
|
|
98
|
+
*/
|
|
99
|
+
get<T = unknown>(key: string): Promise<T | undefined>;
|
|
100
|
+
/**
|
|
101
|
+
* Set a cached value.
|
|
102
|
+
*
|
|
103
|
+
* @param key - Cache key
|
|
104
|
+
* @param value - Value to store
|
|
105
|
+
* @param ttl - Optional TTL string (ignored by the stub)
|
|
106
|
+
*/
|
|
107
|
+
set(key: string, value: unknown, ttl?: string): Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* Get-or-set helper.
|
|
110
|
+
*
|
|
111
|
+
* @param key - Cache key
|
|
112
|
+
* @param ttl - TTL string (ignored by the stub)
|
|
113
|
+
* @param produce - Producer when missing
|
|
114
|
+
*/
|
|
115
|
+
getOrSet<T>(key: string, ttl: string, produce: () => T | Promise<T>): Promise<T>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Structured logger surface. */
|
|
119
|
+
export interface FxLog {
|
|
120
|
+
/** Debug line. */
|
|
121
|
+
debug(message: string, data?: Record<string, unknown>): void;
|
|
122
|
+
/** Info line. */
|
|
123
|
+
info(message: string, data?: Record<string, unknown>): void;
|
|
124
|
+
/** Warning line. */
|
|
125
|
+
warn(message: string, data?: Record<string, unknown>): void;
|
|
126
|
+
/** Error line. */
|
|
127
|
+
error(message: string, data?: Record<string, unknown>): void;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* In-memory / stub store handle when no {@link StoreRuntime} is bound.
|
|
132
|
+
* Read ops record `read`; write ops record `write`.
|
|
133
|
+
*/
|
|
134
|
+
export interface FxStubStoreHandle {
|
|
135
|
+
/** Resource ref this handle is bound to. */
|
|
136
|
+
readonly ref: ResourceRef;
|
|
137
|
+
/**
|
|
138
|
+
* Read a value by key (records a `read` effect).
|
|
139
|
+
*
|
|
140
|
+
* @param key - Row / entry key
|
|
141
|
+
*/
|
|
142
|
+
get(key: string): Promise<unknown>;
|
|
143
|
+
/**
|
|
144
|
+
* Write a value by key (records a `write` effect).
|
|
145
|
+
*
|
|
146
|
+
* @param key - Row / entry key
|
|
147
|
+
* @param value - Value to store
|
|
148
|
+
*/
|
|
149
|
+
set(key: string, value: unknown): Promise<void>;
|
|
150
|
+
/**
|
|
151
|
+
* Stub select — records a `read` and returns all rows.
|
|
152
|
+
*/
|
|
153
|
+
select(): Promise<unknown[]>;
|
|
154
|
+
/**
|
|
155
|
+
* Stub insert — records a `write`.
|
|
156
|
+
*
|
|
157
|
+
* @param row - Row to insert
|
|
158
|
+
*/
|
|
159
|
+
insert(row: Record<string, unknown>): Promise<{ id: string }>;
|
|
160
|
+
/**
|
|
161
|
+
* Stub delete — records a `write`.
|
|
162
|
+
*
|
|
163
|
+
* @param key - Row key
|
|
164
|
+
*/
|
|
165
|
+
delete(key: string): Promise<boolean>;
|
|
166
|
+
/**
|
|
167
|
+
* Audit-plugin convenience (no-op on the stub).
|
|
168
|
+
*
|
|
169
|
+
* @param _ctx - Unused context
|
|
170
|
+
*/
|
|
171
|
+
log(_ctx?: unknown): Promise<void>;
|
|
172
|
+
/** Audit-plugin CLI convenience. */
|
|
173
|
+
exportCsv(): Promise<string>;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Handle returned by {@link Fx.store} — driver-backed SQL/KV/files/index
|
|
178
|
+
* when a runtime is bound, otherwise the in-memory stub.
|
|
179
|
+
*/
|
|
180
|
+
export type FxStoreHandle = StoreHandle | FxStubStoreHandle;
|
|
181
|
+
|
|
182
|
+
/** Options for {@link Fx.send}. */
|
|
183
|
+
export interface FxSendOptions {
|
|
184
|
+
readonly to?: string;
|
|
185
|
+
readonly via?: readonly NamedRef[];
|
|
186
|
+
readonly data?: Record<string, unknown>;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Options for {@link Fx.ask}. */
|
|
190
|
+
export interface FxAskOptions {
|
|
191
|
+
readonly via?: readonly NamedRef[];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Options for {@link Fx.search}. */
|
|
195
|
+
export interface FxSearchOptions {
|
|
196
|
+
readonly topK?: number;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* The `fx` context object — v1 surface.
|
|
201
|
+
*
|
|
202
|
+
* Implementations must be plain objects so tests can replace `fx` wholesale.
|
|
203
|
+
*/
|
|
204
|
+
export interface Fx {
|
|
205
|
+
/**
|
|
206
|
+
* Open a store handle for `ref` (capability checked on each op).
|
|
207
|
+
*
|
|
208
|
+
* When a {@link CreateFxOptions.storeRuntime} is bound and `ref` is a
|
|
209
|
+
* registered store declaration, returns the driver-backed handle.
|
|
210
|
+
*
|
|
211
|
+
* @param ref - Store resource ref, named handle, or store declaration
|
|
212
|
+
*/
|
|
213
|
+
store(
|
|
214
|
+
ref: NamedRef | { readonly ref: ResourceRef } | StoreDecl,
|
|
215
|
+
): FxStoreHandle;
|
|
216
|
+
/**
|
|
217
|
+
* Emit a signal (records `emit`).
|
|
218
|
+
*
|
|
219
|
+
* @param signal - Signal name or handle
|
|
220
|
+
* @param payload - Payload
|
|
221
|
+
*/
|
|
222
|
+
emit(signal: NamedRef, payload?: unknown): Promise<void>;
|
|
223
|
+
/**
|
|
224
|
+
* Call another flow (records `call`). Stub returns `undefined`.
|
|
225
|
+
*
|
|
226
|
+
* @param flow - Flow name or handle
|
|
227
|
+
* @param input - Input payload
|
|
228
|
+
*/
|
|
229
|
+
call(flow: NamedRef, input?: unknown): Promise<unknown>;
|
|
230
|
+
/** Clock surface. */
|
|
231
|
+
readonly clock: FxClock;
|
|
232
|
+
/**
|
|
233
|
+
* Read a vault secret (records `secret`).
|
|
234
|
+
*
|
|
235
|
+
* @param secret - Secret name or handle
|
|
236
|
+
*/
|
|
237
|
+
vault(secret: NamedRef): string;
|
|
238
|
+
/** Cache surface. */
|
|
239
|
+
readonly cache: FxCache;
|
|
240
|
+
/**
|
|
241
|
+
* Send a channel template (records `send`).
|
|
242
|
+
*
|
|
243
|
+
* @param template - Template name or handle
|
|
244
|
+
* @param opts - Recipient / data
|
|
245
|
+
*/
|
|
246
|
+
send(template: NamedRef, opts?: FxSendOptions): Promise<{ ok: true }>;
|
|
247
|
+
/**
|
|
248
|
+
* Ask an AI prompt (records `ask`). Stub returns `{}`.
|
|
249
|
+
*
|
|
250
|
+
* @param prompt - Prompt name or handle
|
|
251
|
+
* @param input - Prompt input
|
|
252
|
+
* @param opts - Model routing opts
|
|
253
|
+
*/
|
|
254
|
+
ask(
|
|
255
|
+
prompt: NamedRef,
|
|
256
|
+
input?: unknown,
|
|
257
|
+
opts?: FxAskOptions,
|
|
258
|
+
): Promise<Record<string, unknown>>;
|
|
259
|
+
/**
|
|
260
|
+
* Similarity search over an index/embed (records `read` on the embed ref).
|
|
261
|
+
*
|
|
262
|
+
* @param embed - Index / embed name or handle
|
|
263
|
+
* @param query - Query text or vector
|
|
264
|
+
* @param opts - Search options
|
|
265
|
+
*/
|
|
266
|
+
search(
|
|
267
|
+
embed: NamedRef,
|
|
268
|
+
query: unknown,
|
|
269
|
+
opts?: FxSearchOptions,
|
|
270
|
+
): Promise<unknown[]>;
|
|
271
|
+
/**
|
|
272
|
+
* Run a bounded AI agent (records `ask`).
|
|
273
|
+
*
|
|
274
|
+
* @param agent - Agent name or handle
|
|
275
|
+
* @param input - Agent input (`{ message }` or string)
|
|
276
|
+
*/
|
|
277
|
+
run(
|
|
278
|
+
agent: NamedRef,
|
|
279
|
+
input?: unknown,
|
|
280
|
+
): Promise<unknown>;
|
|
281
|
+
/**
|
|
282
|
+
* Stream model tokens (records `ask`). Returns an async iterable of chunks.
|
|
283
|
+
*
|
|
284
|
+
* @param model - Model name or handle
|
|
285
|
+
* @param opts - Prompt / data
|
|
286
|
+
*/
|
|
287
|
+
stream(
|
|
288
|
+
model: NamedRef,
|
|
289
|
+
opts?: { readonly prompt?: string; readonly data?: unknown },
|
|
290
|
+
): AsyncIterable<string>;
|
|
291
|
+
/** Logger. */
|
|
292
|
+
readonly log: FxLog;
|
|
293
|
+
/**
|
|
294
|
+
* i18n stub — returns the key, optionally with JSON params suffix.
|
|
295
|
+
*
|
|
296
|
+
* @param key - Message key
|
|
297
|
+
* @param params - Interpolation params
|
|
298
|
+
*/
|
|
299
|
+
t(key: string, params?: Record<string, unknown>): string;
|
|
300
|
+
/** Generate a unique id (UUID). */
|
|
301
|
+
id(): string;
|
|
302
|
+
/** User-plane auth principal. */
|
|
303
|
+
readonly auth: FxAuth;
|
|
304
|
+
/** Operator-plane principal. */
|
|
305
|
+
readonly operator: FxOperator;
|
|
306
|
+
/** Active tenant. */
|
|
307
|
+
readonly tenant: FxTenant;
|
|
308
|
+
/**
|
|
309
|
+
* Flow-boundary failure value (does not throw).
|
|
310
|
+
*
|
|
311
|
+
* @param code - Declared error code (narrowed by clients via `error.code`)
|
|
312
|
+
* @param data - Error payload
|
|
313
|
+
* @param opts - Optional message
|
|
314
|
+
*/
|
|
315
|
+
fail<E>(code: string, data: E, opts?: FailOptions): FlowFailure<E>;
|
|
316
|
+
/**
|
|
317
|
+
* Named durable step — never re-runs on journal replay.
|
|
318
|
+
*
|
|
319
|
+
* @param name - Step name
|
|
320
|
+
* @param fn - Step body
|
|
321
|
+
*/
|
|
322
|
+
step<T>(name: string, fn: () => T | Promise<T>): Promise<T>;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Dispatch target for {@link Fx.call}. Wired by the app so untriggered
|
|
327
|
+
* flows execute through the same pipeline as triggered ones.
|
|
328
|
+
*/
|
|
329
|
+
export type FxCallHandler = (
|
|
330
|
+
name: string,
|
|
331
|
+
input: unknown,
|
|
332
|
+
) => Promise<unknown>;
|
|
333
|
+
|
|
334
|
+
/** Options for {@link createFx}. */
|
|
335
|
+
export interface CreateFxOptions {
|
|
336
|
+
/** Flow id — used in capability error messages. */
|
|
337
|
+
readonly flow: string;
|
|
338
|
+
/** Declared effects that mint the capability token. */
|
|
339
|
+
readonly effects?: Effects;
|
|
340
|
+
/** Ledger to append to (created if omitted). */
|
|
341
|
+
readonly ledger?: EffectLedger;
|
|
342
|
+
/** Pre-built token (defaults from `flow` + `effects`). */
|
|
343
|
+
readonly capability?: CapabilityToken;
|
|
344
|
+
/** Injectable clock for timestamps / `fx.clock.now`. */
|
|
345
|
+
readonly now?: () => number;
|
|
346
|
+
/** Auth principal. */
|
|
347
|
+
readonly auth?: FxAuth;
|
|
348
|
+
/** Operator principal. */
|
|
349
|
+
readonly operator?: FxOperator;
|
|
350
|
+
/** Tenant. */
|
|
351
|
+
readonly tenant?: FxTenant;
|
|
352
|
+
/** Secret name → value map for `fx.vault`. */
|
|
353
|
+
readonly secrets?: Readonly<Record<string, string>>;
|
|
354
|
+
/** Seed data for in-memory stores: ref → key → value. */
|
|
355
|
+
readonly storeData?: Readonly<
|
|
356
|
+
Record<string, Readonly<Record<string, unknown>>>
|
|
357
|
+
>;
|
|
358
|
+
/** Optional log sink (defaults to no-op). */
|
|
359
|
+
readonly onLog?: (
|
|
360
|
+
level: "debug" | "info" | "warn" | "error",
|
|
361
|
+
message: string,
|
|
362
|
+
data?: Record<string, unknown>,
|
|
363
|
+
) => void;
|
|
364
|
+
/**
|
|
365
|
+
* Real `fx.call` dispatch. When omitted, calls are ledgered and return
|
|
366
|
+
* `undefined` (v1 stub).
|
|
367
|
+
*/
|
|
368
|
+
readonly callHandler?: FxCallHandler;
|
|
369
|
+
/**
|
|
370
|
+
* Optional store runtime (protocol drivers). When set, `fx.store(decl)`
|
|
371
|
+
* opens driver-backed handles; string refs still use the in-memory stub
|
|
372
|
+
* unless registered on the runtime.
|
|
373
|
+
*/
|
|
374
|
+
readonly storeRuntime?: StoreRuntime;
|
|
375
|
+
/**
|
|
376
|
+
* Optional signal runtime. When set, `fx.emit` enrols through the
|
|
377
|
+
* configured driver (postgres = same transaction as store writes).
|
|
378
|
+
*/
|
|
379
|
+
readonly signalRuntime?: SignalRuntime;
|
|
380
|
+
/**
|
|
381
|
+
* Optional vault runtime. When set, `fx.vault` reads through it and
|
|
382
|
+
* `fx.log` redacts loaded secret values automatically.
|
|
383
|
+
*/
|
|
384
|
+
readonly vaultRuntime?: VaultRuntime;
|
|
385
|
+
/**
|
|
386
|
+
* Optional channel runtime. When set, `fx.send` delivers through it
|
|
387
|
+
* (templates, consent, fallback chains, receipts).
|
|
388
|
+
*/
|
|
389
|
+
readonly channelRuntime?: ChannelRuntime;
|
|
390
|
+
/**
|
|
391
|
+
* Optional AI runtime. When set, `fx.ask` routes through it.
|
|
392
|
+
* Nondeterministic ⇒ journaling forced; auto-cache disabled.
|
|
393
|
+
*/
|
|
394
|
+
readonly aiRuntime?: AiRuntime;
|
|
395
|
+
/**
|
|
396
|
+
* Optional run telemetry collector. When set, `fx.cache` and `fx.log`
|
|
397
|
+
* accumulate dimensions / log lines for the wide-event runs store
|
|
398
|
+
* with zero flow instrumentation.
|
|
399
|
+
*/
|
|
400
|
+
readonly runTelemetry?: RunTelemetry;
|
|
401
|
+
/** Reveal PII through the store runtime (requires `pii:reveal` upstream). */
|
|
402
|
+
readonly revealPii?: boolean;
|
|
403
|
+
/**
|
|
404
|
+
* Active journal session when the flow is durable. Every `fx` call is
|
|
405
|
+
* recorded; `step` / `sleep` replay from the journal on resume.
|
|
406
|
+
*/
|
|
407
|
+
readonly journal?: JournalSession;
|
|
408
|
+
/** When true (or when `journal` is set), journal every fx call. */
|
|
409
|
+
readonly durable?: boolean;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** Bundle returned by {@link createFxContext}. */
|
|
413
|
+
export interface FxContext {
|
|
414
|
+
/** The context object passed into flows. */
|
|
415
|
+
readonly fx: Fx;
|
|
416
|
+
/** Effect ledger for this invocation. */
|
|
417
|
+
readonly ledger: EffectLedger;
|
|
418
|
+
/** Capability token enforcing declared effects. */
|
|
419
|
+
readonly capability: CapabilityToken;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Create an in-memory `fx` context (v1 stubs, full surface).
|
|
424
|
+
*
|
|
425
|
+
* @param options - Flow identity, declared effects, ledger, principals
|
|
426
|
+
*/
|
|
427
|
+
export function createFx(options: CreateFxOptions): Fx {
|
|
428
|
+
return createFxContext(options).fx;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Create `fx` plus its ledger and capability token.
|
|
433
|
+
*
|
|
434
|
+
* @param options - Same as {@link createFx}
|
|
435
|
+
*/
|
|
436
|
+
export function createFxContext(options: CreateFxOptions): FxContext {
|
|
437
|
+
const ledger = options.ledger ?? createEffectLedger();
|
|
438
|
+
const capability =
|
|
439
|
+
options.capability ??
|
|
440
|
+
createCapabilityToken(options.flow, options.effects);
|
|
441
|
+
const now = options.now ?? (() => Date.now());
|
|
442
|
+
const secrets = options.secrets ?? {};
|
|
443
|
+
const onLog = options.onLog;
|
|
444
|
+
|
|
445
|
+
const stores = new Map<string, Map<string, unknown>>();
|
|
446
|
+
for (const [ref, rows] of Object.entries(options.storeData ?? {})) {
|
|
447
|
+
stores.set(ref, new Map(Object.entries(rows)));
|
|
448
|
+
}
|
|
449
|
+
const cacheStore = new Map<string, unknown>();
|
|
450
|
+
|
|
451
|
+
const auth: FxAuth = options.auth ?? {
|
|
452
|
+
userId: null,
|
|
453
|
+
scopes: new Set(),
|
|
454
|
+
};
|
|
455
|
+
const operator: FxOperator = options.operator ?? { id: null };
|
|
456
|
+
const tenant: FxTenant = options.tenant ?? { id: null };
|
|
457
|
+
const journal = options.journal;
|
|
458
|
+
|
|
459
|
+
async function gated<T>(
|
|
460
|
+
kind: Parameters<CapabilityToken["assert"]>[0],
|
|
461
|
+
resource: string,
|
|
462
|
+
body: () => T | Promise<T>,
|
|
463
|
+
): Promise<T> {
|
|
464
|
+
capability.assert(kind, resource);
|
|
465
|
+
const execute = () => recordEffect(ledger, kind, resource, now, body);
|
|
466
|
+
if (journal) {
|
|
467
|
+
return journal.effect(kind, resource, execute);
|
|
468
|
+
}
|
|
469
|
+
return execute();
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function stubStoreHandle(ref: ResourceRef): FxStubStoreHandle {
|
|
473
|
+
const table = (): Map<string, unknown> => {
|
|
474
|
+
let m = stores.get(ref);
|
|
475
|
+
if (!m) {
|
|
476
|
+
m = new Map();
|
|
477
|
+
stores.set(ref, m);
|
|
478
|
+
}
|
|
479
|
+
// Snapshot before any dry-run touch so writes can be rolled back.
|
|
480
|
+
touchDryRunStore(ref, m);
|
|
481
|
+
return m;
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
return {
|
|
485
|
+
ref,
|
|
486
|
+
get(key: string): Promise<unknown> {
|
|
487
|
+
return gated("read", ref, () => {
|
|
488
|
+
const v = table().get(key);
|
|
489
|
+
if (v === undefined || v === null) return null;
|
|
490
|
+
// Clone during dry-run so in-place mutation cannot leak past rollback.
|
|
491
|
+
return isDryRun() ? structuredClone(v) : v;
|
|
492
|
+
});
|
|
493
|
+
},
|
|
494
|
+
set(key: string, value: unknown): Promise<void> {
|
|
495
|
+
return gated("write", ref, () => {
|
|
496
|
+
table().set(key, value);
|
|
497
|
+
});
|
|
498
|
+
},
|
|
499
|
+
select(): Promise<unknown[]> {
|
|
500
|
+
return gated("read", ref, () => {
|
|
501
|
+
const values = [...table().values()];
|
|
502
|
+
return isDryRun()
|
|
503
|
+
? values.map((v) => structuredClone(v))
|
|
504
|
+
: values;
|
|
505
|
+
});
|
|
506
|
+
},
|
|
507
|
+
insert(row: Record<string, unknown>): Promise<{ id: string }> {
|
|
508
|
+
return gated("write", ref, () => {
|
|
509
|
+
const id =
|
|
510
|
+
typeof row.id === "string" ? row.id : crypto.randomUUID();
|
|
511
|
+
table().set(id, { ...row, id });
|
|
512
|
+
return { id };
|
|
513
|
+
});
|
|
514
|
+
},
|
|
515
|
+
delete(key: string): Promise<boolean> {
|
|
516
|
+
return gated("write", ref, () => table().delete(key));
|
|
517
|
+
},
|
|
518
|
+
/** Audit-plugin convenience (no-op on the stub). */
|
|
519
|
+
async log(_ctx?: unknown): Promise<void> {
|
|
520
|
+
/* stub */
|
|
521
|
+
},
|
|
522
|
+
/** Audit-plugin CLI convenience. */
|
|
523
|
+
async exportCsv(): Promise<string> {
|
|
524
|
+
return "";
|
|
525
|
+
},
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Lazy, capability-gated proxy over a driver-backed {@link SqlStoreHandle}.
|
|
531
|
+
*
|
|
532
|
+
* @param decl - Store declaration
|
|
533
|
+
* @param open - Opens (and caches) the runtime handle
|
|
534
|
+
*/
|
|
535
|
+
function gatedSqlHandle(
|
|
536
|
+
decl: StoreDecl,
|
|
537
|
+
open: () => Promise<SqlStoreHandle>,
|
|
538
|
+
): SqlStoreHandle {
|
|
539
|
+
const ref = decl.ref as `sql:${string}`;
|
|
540
|
+
let cached: SqlStoreHandle | undefined;
|
|
541
|
+
const ensure = async (): Promise<SqlStoreHandle> => {
|
|
542
|
+
if (!cached) cached = await open();
|
|
543
|
+
return cached;
|
|
544
|
+
};
|
|
545
|
+
/** Driver-backed SQL has no dry-run transaction — refuse writes. */
|
|
546
|
+
const refuseDryRunWrite = (): void => {
|
|
547
|
+
if (isDryRun()) {
|
|
548
|
+
throw new DryRunWriteIsolationError(
|
|
549
|
+
`Driver-backed store "${ref}" cannot isolate writes during dry-run; dry-run refused rather than risk a double-write.`,
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
return {
|
|
555
|
+
ref,
|
|
556
|
+
get routedRole() {
|
|
557
|
+
return cached?.routedRole ?? "primary";
|
|
558
|
+
},
|
|
559
|
+
get driverId() {
|
|
560
|
+
return cached?.driverId ?? "memory";
|
|
561
|
+
},
|
|
562
|
+
select(columns?) {
|
|
563
|
+
return {
|
|
564
|
+
from(table) {
|
|
565
|
+
const all = gated("read", ref, async () => {
|
|
566
|
+
const h = await ensure();
|
|
567
|
+
return h.select(columns).from(table);
|
|
568
|
+
});
|
|
569
|
+
return {
|
|
570
|
+
where(where) {
|
|
571
|
+
const filtered = gated("read", ref, async () => {
|
|
572
|
+
const h = await ensure();
|
|
573
|
+
return h.select(columns).from(table).where(where);
|
|
574
|
+
});
|
|
575
|
+
return {
|
|
576
|
+
limit(n) {
|
|
577
|
+
return gated("read", ref, async () => {
|
|
578
|
+
const h = await ensure();
|
|
579
|
+
return h.select(columns).from(table).where(where).limit(n);
|
|
580
|
+
});
|
|
581
|
+
},
|
|
582
|
+
then(onfulfilled, onrejected) {
|
|
583
|
+
return filtered.then(onfulfilled, onrejected);
|
|
584
|
+
},
|
|
585
|
+
};
|
|
586
|
+
},
|
|
587
|
+
then(onfulfilled, onrejected) {
|
|
588
|
+
return all.then(onfulfilled, onrejected);
|
|
589
|
+
},
|
|
590
|
+
};
|
|
591
|
+
},
|
|
592
|
+
};
|
|
593
|
+
},
|
|
594
|
+
insert(table) {
|
|
595
|
+
return {
|
|
596
|
+
values(row) {
|
|
597
|
+
const runExecute = () =>
|
|
598
|
+
gated("write", ref, async () => {
|
|
599
|
+
refuseDryRunWrite();
|
|
600
|
+
const h = await ensure();
|
|
601
|
+
await h.insert(table).values(row).execute();
|
|
602
|
+
});
|
|
603
|
+
return {
|
|
604
|
+
returning() {
|
|
605
|
+
return gated("write", ref, async () => {
|
|
606
|
+
refuseDryRunWrite();
|
|
607
|
+
const h = await ensure();
|
|
608
|
+
return h.insert(table).values(row).returning();
|
|
609
|
+
});
|
|
610
|
+
},
|
|
611
|
+
execute: runExecute,
|
|
612
|
+
then(onfulfilled, onrejected) {
|
|
613
|
+
return runExecute().then(onfulfilled, onrejected);
|
|
614
|
+
},
|
|
615
|
+
};
|
|
616
|
+
},
|
|
617
|
+
};
|
|
618
|
+
},
|
|
619
|
+
update(table) {
|
|
620
|
+
return {
|
|
621
|
+
set(row) {
|
|
622
|
+
return {
|
|
623
|
+
where(where) {
|
|
624
|
+
return gated("write", ref, async () => {
|
|
625
|
+
refuseDryRunWrite();
|
|
626
|
+
const h = await ensure();
|
|
627
|
+
return h.update(table).set(row).where(where);
|
|
628
|
+
});
|
|
629
|
+
},
|
|
630
|
+
};
|
|
631
|
+
},
|
|
632
|
+
};
|
|
633
|
+
},
|
|
634
|
+
findById(table, id) {
|
|
635
|
+
return gated("read", ref, async () => {
|
|
636
|
+
const h = await ensure();
|
|
637
|
+
return h.findById(table, id);
|
|
638
|
+
});
|
|
639
|
+
},
|
|
640
|
+
delete(table: Parameters<SqlStoreHandle["delete"]>[0], id?: string) {
|
|
641
|
+
if (id !== undefined) {
|
|
642
|
+
return gated("write", ref, async () => {
|
|
643
|
+
refuseDryRunWrite();
|
|
644
|
+
const h = await ensure();
|
|
645
|
+
return h.delete(table, id);
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
return {
|
|
649
|
+
where(where: unknown) {
|
|
650
|
+
return gated("write", ref, async () => {
|
|
651
|
+
refuseDryRunWrite();
|
|
652
|
+
const h = await ensure();
|
|
653
|
+
return h.delete(table).where(where);
|
|
654
|
+
});
|
|
655
|
+
},
|
|
656
|
+
};
|
|
657
|
+
},
|
|
658
|
+
exists(table, idOrWhere) {
|
|
659
|
+
return gated("read", ref, async () => {
|
|
660
|
+
const h = await ensure();
|
|
661
|
+
return h.exists(table, idOrWhere);
|
|
662
|
+
});
|
|
663
|
+
},
|
|
664
|
+
increment(table, id, column, by) {
|
|
665
|
+
return gated("write", ref, async () => {
|
|
666
|
+
refuseDryRunWrite();
|
|
667
|
+
const h = await ensure();
|
|
668
|
+
return h.increment(table, id, column, by);
|
|
669
|
+
});
|
|
670
|
+
},
|
|
671
|
+
raw(sql, params) {
|
|
672
|
+
return gated("read", ref, async () => {
|
|
673
|
+
const h = await ensure();
|
|
674
|
+
return h.raw(sql, params);
|
|
675
|
+
});
|
|
676
|
+
},
|
|
677
|
+
ensureTable(table) {
|
|
678
|
+
return gated("write", ref, async () => {
|
|
679
|
+
refuseDryRunWrite();
|
|
680
|
+
const h = await ensure();
|
|
681
|
+
return h.ensureTable(table);
|
|
682
|
+
});
|
|
683
|
+
},
|
|
684
|
+
} as SqlStoreHandle;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function storeHandle(
|
|
688
|
+
ref: NamedRef | { readonly ref: ResourceRef } | StoreDecl,
|
|
689
|
+
): FxStoreHandle {
|
|
690
|
+
const runtime = options.storeRuntime;
|
|
691
|
+
if (runtime && typeof ref === "object" && ref !== null && "facet" in ref) {
|
|
692
|
+
const decl = ref;
|
|
693
|
+
const cache: { handle?: StoreHandle } = {};
|
|
694
|
+
const open = async () => {
|
|
695
|
+
if (!cache.handle) {
|
|
696
|
+
cache.handle = await runtime.open(decl, {
|
|
697
|
+
effects: options.effects ?? {},
|
|
698
|
+
revealPii: options.revealPii,
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
return cache.handle;
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
if (decl.facet === "sql") {
|
|
705
|
+
return gatedSqlHandle(decl, async () => {
|
|
706
|
+
const h = await open();
|
|
707
|
+
return h as SqlStoreHandle;
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// KV / files / index — thin gated wrappers preserving driver methods.
|
|
712
|
+
const baseRef = decl.ref;
|
|
713
|
+
return new Proxy({} as StoreHandle, {
|
|
714
|
+
get(_t, prop) {
|
|
715
|
+
if (prop === "ref") return baseRef;
|
|
716
|
+
if (prop === "then") return undefined;
|
|
717
|
+
const isRead =
|
|
718
|
+
prop === "get" || prop === "search" || prop === "list";
|
|
719
|
+
return (...args: unknown[]) =>
|
|
720
|
+
gated(isRead ? "read" : "write", baseRef, async () => {
|
|
721
|
+
if (!isRead && isDryRun()) {
|
|
722
|
+
throw new DryRunWriteIsolationError(
|
|
723
|
+
`Driver-backed store "${baseRef}" cannot isolate writes during dry-run; dry-run refused rather than risk a double-write.`,
|
|
724
|
+
);
|
|
725
|
+
}
|
|
726
|
+
const h = await open();
|
|
727
|
+
const fn = (h as unknown as Record<string | symbol, unknown>)[
|
|
728
|
+
prop
|
|
729
|
+
];
|
|
730
|
+
if (typeof fn !== "function") return undefined;
|
|
731
|
+
return (fn as (...a: unknown[]) => unknown).apply(h, args);
|
|
732
|
+
});
|
|
733
|
+
},
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
return stubStoreHandle(
|
|
737
|
+
resolveStoreRef(ref as NamedRef | { readonly ref: ResourceRef }),
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
const clock: FxClock = {
|
|
742
|
+
now,
|
|
743
|
+
async sleep(label: string, duration: string): Promise<void> {
|
|
744
|
+
if (journal) {
|
|
745
|
+
await journal.sleep(label, duration, parseDurationMs);
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
/* non-durable: resolve immediately (tests / sync flows) */
|
|
749
|
+
void label;
|
|
750
|
+
void duration;
|
|
751
|
+
},
|
|
752
|
+
};
|
|
753
|
+
|
|
754
|
+
const aiDisablesCache = options.aiRuntime?.autoCacheDisabled === true;
|
|
755
|
+
const telemetry = options.runTelemetry;
|
|
756
|
+
|
|
757
|
+
const cache: FxCache = {
|
|
758
|
+
async get<T = unknown>(key: string): Promise<T | undefined> {
|
|
759
|
+
if (aiDisablesCache) return undefined;
|
|
760
|
+
if (cacheStore.has(key)) {
|
|
761
|
+
if (telemetry) telemetry.cacheHits += 1;
|
|
762
|
+
return cacheStore.get(key) as T | undefined;
|
|
763
|
+
}
|
|
764
|
+
if (telemetry) telemetry.cacheMisses += 1;
|
|
765
|
+
return undefined;
|
|
766
|
+
},
|
|
767
|
+
async set(key: string, value: unknown, _ttl?: string): Promise<void> {
|
|
768
|
+
if (aiDisablesCache) return;
|
|
769
|
+
cacheStore.set(key, value);
|
|
770
|
+
},
|
|
771
|
+
async getOrSet<T>(
|
|
772
|
+
key: string,
|
|
773
|
+
_ttl: string,
|
|
774
|
+
produce: () => T | Promise<T>,
|
|
775
|
+
): Promise<T> {
|
|
776
|
+
if (aiDisablesCache) return await produce();
|
|
777
|
+
if (cacheStore.has(key)) {
|
|
778
|
+
if (telemetry) telemetry.cacheHits += 1;
|
|
779
|
+
return cacheStore.get(key) as T;
|
|
780
|
+
}
|
|
781
|
+
if (telemetry) telemetry.cacheMisses += 1;
|
|
782
|
+
const value = await produce();
|
|
783
|
+
cacheStore.set(key, value);
|
|
784
|
+
return value;
|
|
785
|
+
},
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
function redactLog(
|
|
789
|
+
message: string,
|
|
790
|
+
data?: Record<string, unknown>,
|
|
791
|
+
): { message: string; data?: Record<string, unknown> } {
|
|
792
|
+
const vault = options.vaultRuntime;
|
|
793
|
+
if (!vault) return { message, data };
|
|
794
|
+
const safeMessage = vault.redactString(message);
|
|
795
|
+
const safeData = data
|
|
796
|
+
? (vault.redact(data) as Record<string, unknown>)
|
|
797
|
+
: undefined;
|
|
798
|
+
return { message: safeMessage, data: safeData };
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
function emitLog(
|
|
802
|
+
level: "debug" | "info" | "warn" | "error",
|
|
803
|
+
message: string,
|
|
804
|
+
data?: Record<string, unknown>,
|
|
805
|
+
): void {
|
|
806
|
+
const r = redactLog(message, data);
|
|
807
|
+
if (telemetry) {
|
|
808
|
+
telemetry.logs.push({
|
|
809
|
+
level,
|
|
810
|
+
message: r.message,
|
|
811
|
+
...(r.data !== undefined ? { data: r.data } : {}),
|
|
812
|
+
at: now(),
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
onLog?.(level, r.message, r.data);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
const log: FxLog = {
|
|
819
|
+
debug(message, data) {
|
|
820
|
+
emitLog("debug", message, data);
|
|
821
|
+
},
|
|
822
|
+
info(message, data) {
|
|
823
|
+
emitLog("info", message, data);
|
|
824
|
+
},
|
|
825
|
+
warn(message, data) {
|
|
826
|
+
emitLog("warn", message, data);
|
|
827
|
+
},
|
|
828
|
+
error(message, data) {
|
|
829
|
+
emitLog("error", message, data);
|
|
830
|
+
},
|
|
831
|
+
};
|
|
832
|
+
|
|
833
|
+
const fx: Fx = {
|
|
834
|
+
store(ref) {
|
|
835
|
+
return storeHandle(ref);
|
|
836
|
+
},
|
|
837
|
+
emit(signal, payload) {
|
|
838
|
+
const name = resolveName(signal);
|
|
839
|
+
return gated("emit", name, async () => {
|
|
840
|
+
if (options.signalRuntime) {
|
|
841
|
+
await options.signalRuntime.emit(name, payload);
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
},
|
|
845
|
+
call(flow, input) {
|
|
846
|
+
const name = resolveName(flow);
|
|
847
|
+
return gated("call", name, async () => {
|
|
848
|
+
if (options.callHandler) {
|
|
849
|
+
return options.callHandler(name, input);
|
|
850
|
+
}
|
|
851
|
+
return undefined;
|
|
852
|
+
});
|
|
853
|
+
},
|
|
854
|
+
clock,
|
|
855
|
+
vault(secret) {
|
|
856
|
+
const name = resolveName(secret);
|
|
857
|
+
// vault is sync in the public examples; check + record synchronously
|
|
858
|
+
capability.assert("secret", name);
|
|
859
|
+
const timestamp = now();
|
|
860
|
+
const value = options.vaultRuntime
|
|
861
|
+
? options.vaultRuntime.read(name)
|
|
862
|
+
: (secrets[name] ?? `[secret:${name}]`);
|
|
863
|
+
ledger.record({
|
|
864
|
+
kind: "secret",
|
|
865
|
+
resource: name,
|
|
866
|
+
timestamp,
|
|
867
|
+
duration: Math.max(0, now() - timestamp),
|
|
868
|
+
reversibility: reversibilityOf("secret"),
|
|
869
|
+
});
|
|
870
|
+
return value;
|
|
871
|
+
},
|
|
872
|
+
cache,
|
|
873
|
+
send(template, opts) {
|
|
874
|
+
const name = resolveName(template);
|
|
875
|
+
return gated("send", name, async () => {
|
|
876
|
+
// Dry-run: record "would have fired" — never contact a real channel
|
|
877
|
+
// (console §9.1 · §9.3 · §9.4).
|
|
878
|
+
if (isDryRun()) {
|
|
879
|
+
recordWouldHaveFired("send", name);
|
|
880
|
+
return { ok: true as const };
|
|
881
|
+
}
|
|
882
|
+
if (options.channelRuntime) {
|
|
883
|
+
const result = await options.channelRuntime.send(name, {
|
|
884
|
+
to: opts?.to ?? "",
|
|
885
|
+
data: opts?.data,
|
|
886
|
+
via: opts?.via?.map(resolveName),
|
|
887
|
+
});
|
|
888
|
+
return { ok: result.ok as true };
|
|
889
|
+
}
|
|
890
|
+
return { ok: true as const };
|
|
891
|
+
});
|
|
892
|
+
},
|
|
893
|
+
ask(prompt, input, opts) {
|
|
894
|
+
const name = resolveName(prompt);
|
|
895
|
+
return gated("ask", name, async () => {
|
|
896
|
+
// Dry-run: stub the model call the same way as send.
|
|
897
|
+
if (isDryRun()) {
|
|
898
|
+
recordWouldHaveFired("ask", name);
|
|
899
|
+
return {};
|
|
900
|
+
}
|
|
901
|
+
if (options.aiRuntime) {
|
|
902
|
+
return options.aiRuntime.ask(name, input, {
|
|
903
|
+
via: opts?.via?.map(resolveName),
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
return {};
|
|
907
|
+
});
|
|
908
|
+
},
|
|
909
|
+
search(embed, query, opts) {
|
|
910
|
+
const name = resolveName(embed);
|
|
911
|
+
return gated("read", name, async () => {
|
|
912
|
+
if (options.aiRuntime && "search" in options.aiRuntime) {
|
|
913
|
+
const searchFn = (
|
|
914
|
+
options.aiRuntime as {
|
|
915
|
+
search?: (
|
|
916
|
+
embed: string,
|
|
917
|
+
query: unknown,
|
|
918
|
+
opts?: FxSearchOptions,
|
|
919
|
+
) => Promise<unknown[]>;
|
|
920
|
+
}
|
|
921
|
+
).search;
|
|
922
|
+
if (typeof searchFn === "function") {
|
|
923
|
+
return searchFn(name, query, opts);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
// Deterministic stub for tests without an index driver.
|
|
927
|
+
if (typeof query === "string" && query.length > 0) {
|
|
928
|
+
return [{ id: "stub", score: 1, text: query, topK: opts?.topK ?? 5 }];
|
|
929
|
+
}
|
|
930
|
+
return [];
|
|
931
|
+
});
|
|
932
|
+
},
|
|
933
|
+
run(agent, input) {
|
|
934
|
+
const name = resolveName(agent);
|
|
935
|
+
return gated("ask", name, async () => {
|
|
936
|
+
if (options.aiRuntime) {
|
|
937
|
+
const message =
|
|
938
|
+
typeof input === "string"
|
|
939
|
+
? input
|
|
940
|
+
: input && typeof input === "object" && "message" in input
|
|
941
|
+
? String((input as { message: unknown }).message)
|
|
942
|
+
: JSON.stringify(input ?? {});
|
|
943
|
+
return options.aiRuntime.runAgent(name, {
|
|
944
|
+
message,
|
|
945
|
+
auth: {
|
|
946
|
+
userId: auth.userId,
|
|
947
|
+
scopes: auth.scopes,
|
|
948
|
+
verified: auth.verified,
|
|
949
|
+
},
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
return { ok: true, steps: 0, denials: [], output: input };
|
|
953
|
+
});
|
|
954
|
+
},
|
|
955
|
+
stream(model, opts) {
|
|
956
|
+
const name = resolveName(model);
|
|
957
|
+
const chunks = (async function* () {
|
|
958
|
+
await gated("ask", name, async () => undefined);
|
|
959
|
+
const text =
|
|
960
|
+
typeof opts?.data === "object" && opts?.data !== null
|
|
961
|
+
? JSON.stringify(opts.data)
|
|
962
|
+
: String(opts?.prompt ?? "");
|
|
963
|
+
if (text.length === 0) {
|
|
964
|
+
yield "";
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
// Chunk for clients that consume streaming tokens.
|
|
968
|
+
const size = Math.max(1, Math.ceil(text.length / 3));
|
|
969
|
+
for (let i = 0; i < text.length; i += size) {
|
|
970
|
+
yield text.slice(i, i + size);
|
|
971
|
+
}
|
|
972
|
+
})();
|
|
973
|
+
return chunks;
|
|
974
|
+
},
|
|
975
|
+
log,
|
|
976
|
+
t(key, params) {
|
|
977
|
+
if (params === undefined) return key;
|
|
978
|
+
return `${key}:${JSON.stringify(params)}`;
|
|
979
|
+
},
|
|
980
|
+
id() {
|
|
981
|
+
return crypto.randomUUID();
|
|
982
|
+
},
|
|
983
|
+
auth,
|
|
984
|
+
operator,
|
|
985
|
+
tenant,
|
|
986
|
+
fail,
|
|
987
|
+
async step<T>(name: string, fn: () => T | Promise<T>): Promise<T> {
|
|
988
|
+
if (journal) {
|
|
989
|
+
return journal.step(name, fn);
|
|
990
|
+
}
|
|
991
|
+
return await fn();
|
|
992
|
+
},
|
|
993
|
+
};
|
|
994
|
+
|
|
995
|
+
return { fx, ledger, capability };
|
|
996
|
+
}
|