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
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenRouter Decisions driver. `POST https://openrouter.ai/api/alpha/decisions`.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { decisionHttp } from "./http.ts";
|
|
6
|
+
import type { DecisionProvider, DecisionRequest, DecisionResponse } from "./provider.ts";
|
|
7
|
+
|
|
8
|
+
/** Default OpenRouter decisions URL. */
|
|
9
|
+
export const OPENROUTER_DECISION_URL = "https://openrouter.ai/api/alpha/decisions";
|
|
10
|
+
|
|
11
|
+
/** Default Jev model id on OpenRouter. */
|
|
12
|
+
export const OPENROUTER_JEV_MODEL = "typesafe/jev-1.13";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* OpenRouter decision provider. This is the default when a decision omits `model`.
|
|
16
|
+
*
|
|
17
|
+
* @param apiKey - `OPENROUTER_API_KEY`
|
|
18
|
+
* @param timeoutMs - Provider deadline
|
|
19
|
+
*/
|
|
20
|
+
export function createOpenRouterDecisionProvider(
|
|
21
|
+
apiKey: string,
|
|
22
|
+
timeoutMs = 30_000,
|
|
23
|
+
): DecisionProvider {
|
|
24
|
+
return {
|
|
25
|
+
id: "openrouter",
|
|
26
|
+
evaluate(request: DecisionRequest): Promise<DecisionResponse> {
|
|
27
|
+
return decisionHttp({
|
|
28
|
+
url: OPENROUTER_DECISION_URL,
|
|
29
|
+
apiKey,
|
|
30
|
+
request: { ...request, model: request.model || OPENROUTER_JEV_MODEL },
|
|
31
|
+
timeoutMs,
|
|
32
|
+
breakerKey: "openrouter",
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision provider contract. Chat drivers stay on {@link AiDriverId}.
|
|
3
|
+
* Boolean questions are `noul` on the wire.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** Wire question sent in one batched request. */
|
|
7
|
+
export type WireQuestion =
|
|
8
|
+
| {
|
|
9
|
+
readonly type: "choice";
|
|
10
|
+
readonly instructions: string;
|
|
11
|
+
readonly criteria: Readonly<Record<string, string | null>>;
|
|
12
|
+
}
|
|
13
|
+
| {
|
|
14
|
+
readonly type: "score";
|
|
15
|
+
readonly instructions: string;
|
|
16
|
+
readonly criteria: readonly string[];
|
|
17
|
+
}
|
|
18
|
+
| {
|
|
19
|
+
readonly type: "noul";
|
|
20
|
+
readonly instructions: string;
|
|
21
|
+
readonly criteria?: { readonly true?: string; readonly false?: string };
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** One provider call. */
|
|
25
|
+
export interface DecisionRequest {
|
|
26
|
+
readonly model: string;
|
|
27
|
+
readonly state: unknown;
|
|
28
|
+
readonly questions: Readonly<Record<string, WireQuestion>>;
|
|
29
|
+
readonly signal?: AbortSignal;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Token usage reported by the provider. */
|
|
33
|
+
export interface DecisionUsage {
|
|
34
|
+
readonly inputTokens?: number;
|
|
35
|
+
readonly outputTokens?: number;
|
|
36
|
+
readonly cost?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Parsed provider body. `model` is the resolved version. */
|
|
40
|
+
export interface DecisionResponse {
|
|
41
|
+
readonly model: string;
|
|
42
|
+
readonly provider?: string;
|
|
43
|
+
readonly answers: Readonly<Record<string, unknown>>;
|
|
44
|
+
readonly usage: DecisionUsage;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Driver id for a decision provider. */
|
|
48
|
+
export type DecisionDriverId = "typesafe" | "openrouter";
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* One System One decision call.
|
|
52
|
+
*/
|
|
53
|
+
export interface DecisionProvider {
|
|
54
|
+
readonly id: DecisionDriverId;
|
|
55
|
+
/**
|
|
56
|
+
* Evaluate every question against `state` in one request.
|
|
57
|
+
*
|
|
58
|
+
* @param request - Model, state, and questions
|
|
59
|
+
*/
|
|
60
|
+
evaluate(request: DecisionRequest): Promise<DecisionResponse>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Statuses that are the caller's problem. They do not open the breaker. */
|
|
64
|
+
export type DecisionRequestStatus = 400 | 401 | 402 | 403 | 404 | 422;
|
|
65
|
+
|
|
66
|
+
/** Auth, billing, or a body the provider rejected. Does not open the breaker. */
|
|
67
|
+
export class DecisionRequestError extends Error {
|
|
68
|
+
readonly status: DecisionRequestStatus;
|
|
69
|
+
/**
|
|
70
|
+
* @param status - HTTP status
|
|
71
|
+
* @param message - Provider body, truncated by the caller
|
|
72
|
+
*/
|
|
73
|
+
constructor(status: DecisionRequestStatus, message: string) {
|
|
74
|
+
super(message);
|
|
75
|
+
this.name = "DecisionRequestError";
|
|
76
|
+
this.status = status;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** The decision is missing a configured secret. Not an outage. */
|
|
81
|
+
export class DecisionConfigError extends Error {
|
|
82
|
+
readonly secret: string;
|
|
83
|
+
/**
|
|
84
|
+
* @param secret - Secret contract name
|
|
85
|
+
*/
|
|
86
|
+
constructor(secret: string) {
|
|
87
|
+
super(`fx.decide: secret "${secret}" is not configured`);
|
|
88
|
+
this.name = "DecisionConfigError";
|
|
89
|
+
this.secret = secret;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** The breaker is open. Callers take the non-auto path. */
|
|
94
|
+
export class DecisionOutageError extends Error {
|
|
95
|
+
/**
|
|
96
|
+
* @param message - Why the call was not sent
|
|
97
|
+
*/
|
|
98
|
+
constructor(message: string) {
|
|
99
|
+
super(message);
|
|
100
|
+
this.name = "DecisionOutageError";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeSafe System One driver. `POST https://api.typesafe.ai/v1/systemone`.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { decisionHttp } from "./http.ts";
|
|
6
|
+
import type { DecisionProvider, DecisionRequest, DecisionResponse } from "./provider.ts";
|
|
7
|
+
|
|
8
|
+
/** Default TypeSafe evaluation URL. */
|
|
9
|
+
export const TYPESAFE_DECISION_URL = "https://api.typesafe.ai/v1/systemone";
|
|
10
|
+
|
|
11
|
+
/** Pinned Jev version when the author selects the TypeSafe driver. */
|
|
12
|
+
export const TYPESAFE_JEV_MODEL = "jev-1.13.0";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* TypeSafe decision provider.
|
|
16
|
+
*
|
|
17
|
+
* @param apiKey - `TYPESAFE_API_KEY`
|
|
18
|
+
* @param timeoutMs - Provider deadline
|
|
19
|
+
*/
|
|
20
|
+
export function createTypesafeDecisionProvider(
|
|
21
|
+
apiKey: string,
|
|
22
|
+
timeoutMs = 30_000,
|
|
23
|
+
): DecisionProvider {
|
|
24
|
+
return {
|
|
25
|
+
id: "typesafe",
|
|
26
|
+
evaluate(request: DecisionRequest): Promise<DecisionResponse> {
|
|
27
|
+
return decisionHttp({
|
|
28
|
+
url: TYPESAFE_DECISION_URL,
|
|
29
|
+
apiKey,
|
|
30
|
+
request: { ...request, model: request.model || TYPESAFE_JEV_MODEL },
|
|
31
|
+
timeoutMs,
|
|
32
|
+
breakerKey: "typesafe",
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
aiAgentRegistry,
|
|
9
|
+
aiDecisionRegistry,
|
|
9
10
|
aiEmbedRegistry,
|
|
10
11
|
aiMcpServerRegistry,
|
|
11
12
|
aiModelRegistry,
|
|
@@ -53,6 +54,8 @@ export interface AiPromptOptions {
|
|
|
53
54
|
readonly version?: number;
|
|
54
55
|
readonly evals?: string;
|
|
55
56
|
readonly budget?: AiBudgetDecl;
|
|
57
|
+
/** One schema-mismatch retry. Default `0` throws on the first mismatch. */
|
|
58
|
+
readonly repair?: 0 | 1;
|
|
56
59
|
/**
|
|
57
60
|
* Ordered recovery chain of logical model names for this command.
|
|
58
61
|
* Resolved as `ask.via ?? prompt.via ?? [prompt.model]`.
|
|
@@ -70,11 +73,103 @@ export interface AiEmbedOptions {
|
|
|
70
73
|
readonly into?: { readonly name: string; readonly facet?: string } | string;
|
|
71
74
|
}
|
|
72
75
|
|
|
76
|
+
/** Who may have to approve one agent tool before it runs. */
|
|
77
|
+
export type AiToolApproval =
|
|
78
|
+
| boolean
|
|
79
|
+
| ((input: unknown, ctx: { readonly auth?: unknown; readonly tenant?: unknown }) => boolean);
|
|
80
|
+
|
|
81
|
+
/** One agent tool, optionally held for approval. */
|
|
82
|
+
export interface AiAgentToolOptions {
|
|
83
|
+
readonly name: string;
|
|
84
|
+
/** `true` always waits. A function waits only when it returns true. */
|
|
85
|
+
readonly approval?: AiToolApproval;
|
|
86
|
+
/** Gate that may approve or deny. Required when {@link approval} is set. */
|
|
87
|
+
readonly gate?: { readonly name: string } | string;
|
|
88
|
+
/** How long to wait before a deny. Default `24h`. */
|
|
89
|
+
readonly timeout?: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Choice answer a Flow can receive. Author keys, plus `none_of_these`.
|
|
94
|
+
*
|
|
95
|
+
* @typeParam Options - Author option map
|
|
96
|
+
*/
|
|
97
|
+
export type DecisionChoiceValue<Options extends Readonly<Record<string, string | null>>> =
|
|
98
|
+
| keyof Options
|
|
99
|
+
| "none_of_these";
|
|
100
|
+
|
|
101
|
+
/** One choice question. `none_of_these` is injected on the wire. */
|
|
102
|
+
export interface AiChoiceQuestion {
|
|
103
|
+
readonly kind: "choice";
|
|
104
|
+
readonly instructions: string;
|
|
105
|
+
readonly options: Readonly<Record<string, string | null>>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** One ordered score question. */
|
|
109
|
+
export interface AiScoreQuestion {
|
|
110
|
+
readonly kind: "score";
|
|
111
|
+
readonly instructions: string;
|
|
112
|
+
readonly levels: readonly string[];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** One yes/no question. The wire type is `noul`. */
|
|
116
|
+
export interface AiBooleanQuestion {
|
|
117
|
+
readonly kind: "boolean";
|
|
118
|
+
readonly instructions: string;
|
|
119
|
+
readonly criteria?: { readonly true?: string; readonly false?: string };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** A question inside `ai.decision({ ask })`. */
|
|
123
|
+
export type AiDecisionQuestion = AiChoiceQuestion | AiScoreQuestion | AiBooleanQuestion;
|
|
124
|
+
|
|
125
|
+
/** Autonomy grant. Present only in the lockfile's certificate. */
|
|
126
|
+
export interface AiDecisionAutonomy {
|
|
127
|
+
readonly maxError: number;
|
|
128
|
+
/** Audit sample rate in `[0, 1]`. Required whenever autonomy is set. */
|
|
129
|
+
readonly audit: number;
|
|
130
|
+
/** Learn-then-Test family-wise level. Default `0.1`. */
|
|
131
|
+
readonly risk?: number;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Options for {@link ai.decision}. Exactly one of `review` and `onUncertain` is required. */
|
|
135
|
+
export interface AiDecisionOptions {
|
|
136
|
+
readonly model?: AiModelDecl | string;
|
|
137
|
+
readonly in?: unknown;
|
|
138
|
+
readonly ask: Readonly<Record<string, AiDecisionQuestion>>;
|
|
139
|
+
readonly autonomy?: AiDecisionAutonomy;
|
|
140
|
+
readonly review?: { readonly name: string } | string;
|
|
141
|
+
readonly onUncertain?: "abstain";
|
|
142
|
+
readonly locale?: (input: unknown) => string | undefined;
|
|
143
|
+
readonly evals?: string;
|
|
144
|
+
/** `openrouter` (default) or `typesafe`. */
|
|
145
|
+
readonly driverId?: "openrouter" | "typesafe";
|
|
146
|
+
readonly timeout?: AiTimeout;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Declared decision handle. */
|
|
150
|
+
export interface AiDecisionDecl {
|
|
151
|
+
readonly kind: "decision";
|
|
152
|
+
readonly name: string;
|
|
153
|
+
readonly ask: Readonly<Record<string, AiDecisionQuestion>>;
|
|
154
|
+
readonly mode: "review" | "abstain";
|
|
155
|
+
readonly review?: string;
|
|
156
|
+
readonly model?: string;
|
|
157
|
+
readonly driverId: "openrouter" | "typesafe";
|
|
158
|
+
readonly autonomy?: AiDecisionAutonomy;
|
|
159
|
+
readonly evals?: string;
|
|
160
|
+
readonly timeout?: AiTimeout;
|
|
161
|
+
readonly locale?: (input: unknown) => string | undefined;
|
|
162
|
+
/** Declared `in` schema. Label export keeps only these fields. */
|
|
163
|
+
readonly inputSchema?: unknown;
|
|
164
|
+
}
|
|
165
|
+
|
|
73
166
|
/** Options for {@link ai.agent}. */
|
|
74
167
|
export interface AiAgentOptions {
|
|
75
168
|
readonly model?: AiModelDecl | string;
|
|
76
|
-
readonly tools?: readonly ({ readonly name: string } | string)[];
|
|
169
|
+
readonly tools?: readonly (AiAgentToolOptions | { readonly name: string } | string)[];
|
|
77
170
|
readonly maxSteps?: number;
|
|
171
|
+
/** Nested agent depth. Default 3. */
|
|
172
|
+
readonly maxDepth?: number;
|
|
78
173
|
readonly budget?: AiBudgetDecl;
|
|
79
174
|
}
|
|
80
175
|
|
|
@@ -104,6 +199,8 @@ export interface AiPromptDecl {
|
|
|
104
199
|
readonly version?: number;
|
|
105
200
|
readonly evals?: string;
|
|
106
201
|
readonly budget?: AiBudgetDecl;
|
|
202
|
+
/** One schema-mismatch retry. Default `0`. */
|
|
203
|
+
readonly repair?: 0 | 1;
|
|
107
204
|
readonly via?: readonly string[];
|
|
108
205
|
readonly timeout?: AiTimeout;
|
|
109
206
|
readonly model?: string;
|
|
@@ -119,12 +216,24 @@ export interface AiEmbedDecl {
|
|
|
119
216
|
readonly into?: string;
|
|
120
217
|
}
|
|
121
218
|
|
|
219
|
+
/** Approval held on one tool. The predicate stays in code. */
|
|
220
|
+
export interface AiAgentApprovalDecl {
|
|
221
|
+
readonly gate: string;
|
|
222
|
+
readonly timeout: string;
|
|
223
|
+
readonly when?: (
|
|
224
|
+
input: unknown,
|
|
225
|
+
ctx: { readonly auth?: unknown; readonly tenant?: unknown },
|
|
226
|
+
) => boolean;
|
|
227
|
+
}
|
|
228
|
+
|
|
122
229
|
/** Declared agent whose tools are flows. */
|
|
123
230
|
export interface AiAgentDecl {
|
|
124
231
|
readonly kind: "agent";
|
|
125
232
|
readonly name: string;
|
|
126
233
|
readonly tools: readonly string[];
|
|
234
|
+
readonly approvals?: Readonly<Record<string, AiAgentApprovalDecl>>;
|
|
127
235
|
readonly maxSteps?: number;
|
|
236
|
+
readonly maxDepth?: number;
|
|
128
237
|
readonly model?: string;
|
|
129
238
|
readonly budget?: AiBudgetDecl;
|
|
130
239
|
}
|
|
@@ -186,6 +295,10 @@ function toolName(tool: { readonly name: string } | string): string {
|
|
|
186
295
|
return typeof tool === "string" ? tool : tool.name;
|
|
187
296
|
}
|
|
188
297
|
|
|
298
|
+
function gateName(gate: { readonly name: string } | string): string {
|
|
299
|
+
return typeof gate === "string" ? gate : gate.name;
|
|
300
|
+
}
|
|
301
|
+
|
|
189
302
|
/**
|
|
190
303
|
* Shape of the {@link ai} element namespace.
|
|
191
304
|
*/
|
|
@@ -211,6 +324,37 @@ export interface AiNamespace {
|
|
|
211
324
|
* @param options - Tools / maxSteps / model / budget
|
|
212
325
|
*/
|
|
213
326
|
agent(name: string, options?: AiAgentOptions): AiAgentDecl;
|
|
327
|
+
/**
|
|
328
|
+
* Declare a decision. Exactly one of `review` and `onUncertain: "abstain"`.
|
|
329
|
+
*
|
|
330
|
+
* @param name - Decision id
|
|
331
|
+
* @param options - Questions, mode, and optional autonomy
|
|
332
|
+
*/
|
|
333
|
+
decision(name: string, options: AiDecisionOptions): AiDecisionDecl;
|
|
334
|
+
/**
|
|
335
|
+
* A choice question. The answer union is {@link DecisionChoiceValue}: author keys or `none_of_these`.
|
|
336
|
+
*
|
|
337
|
+
* @param instructions - What to decide
|
|
338
|
+
* @param options - At most 254 author options
|
|
339
|
+
*/
|
|
340
|
+
choice(instructions: string, options: Readonly<Record<string, string | null>>): AiChoiceQuestion;
|
|
341
|
+
/**
|
|
342
|
+
* An ordered score question.
|
|
343
|
+
*
|
|
344
|
+
* @param instructions - What to rate
|
|
345
|
+
* @param levels - 2–10 level descriptions, low to high
|
|
346
|
+
*/
|
|
347
|
+
score(instructions: string, levels: readonly string[]): AiScoreQuestion;
|
|
348
|
+
/**
|
|
349
|
+
* A yes/no question. Sent as `noul`.
|
|
350
|
+
*
|
|
351
|
+
* @param instructions - The yes/no question
|
|
352
|
+
* @param criteria - Optional true/false descriptions
|
|
353
|
+
*/
|
|
354
|
+
boolean(
|
|
355
|
+
instructions: string,
|
|
356
|
+
criteria?: { readonly true?: string; readonly false?: string },
|
|
357
|
+
): AiBooleanQuestion;
|
|
214
358
|
/**
|
|
215
359
|
* Declare an external MCP server whose allowlisted tools join `fx.call`.
|
|
216
360
|
*
|
|
@@ -228,6 +372,7 @@ export function listAiDecls(): {
|
|
|
228
372
|
readonly prompts: readonly AiPromptDecl[];
|
|
229
373
|
readonly embeds: readonly AiEmbedDecl[];
|
|
230
374
|
readonly agents: readonly AiAgentDecl[];
|
|
375
|
+
readonly decisions: readonly AiDecisionDecl[];
|
|
231
376
|
readonly mcpServers: readonly AiMcpServerDecl[];
|
|
232
377
|
} {
|
|
233
378
|
return {
|
|
@@ -235,6 +380,7 @@ export function listAiDecls(): {
|
|
|
235
380
|
prompts: aiPromptRegistry.slice(),
|
|
236
381
|
embeds: aiEmbedRegistry.slice(),
|
|
237
382
|
agents: aiAgentRegistry.slice(),
|
|
383
|
+
decisions: aiDecisionRegistry.slice(),
|
|
238
384
|
mcpServers: aiMcpServerRegistry.slice(),
|
|
239
385
|
};
|
|
240
386
|
}
|
|
@@ -247,6 +393,7 @@ export function resetAiDecls(): void {
|
|
|
247
393
|
aiPromptRegistry.length = 0;
|
|
248
394
|
aiEmbedRegistry.length = 0;
|
|
249
395
|
aiAgentRegistry.length = 0;
|
|
396
|
+
aiDecisionRegistry.length = 0;
|
|
250
397
|
aiMcpServerRegistry.length = 0;
|
|
251
398
|
}
|
|
252
399
|
|
|
@@ -290,6 +437,7 @@ export const ai: AiNamespace = {
|
|
|
290
437
|
...(promptOpts.version !== undefined ? { version: promptOpts.version } : {}),
|
|
291
438
|
...(promptOpts.evals !== undefined ? { evals: promptOpts.evals } : {}),
|
|
292
439
|
...(promptOpts.budget !== undefined ? { budget: promptOpts.budget } : {}),
|
|
440
|
+
...(promptOpts.repair !== undefined ? { repair: promptOpts.repair } : {}),
|
|
293
441
|
...(promptOpts.via !== undefined ? { via: promptOpts.via } : {}),
|
|
294
442
|
...(promptOpts.timeout !== undefined ? { timeout: promptOpts.timeout } : {}),
|
|
295
443
|
...(promptOpts.in !== undefined ? { in: promptOpts.in } : {}),
|
|
@@ -329,11 +477,28 @@ export const ai: AiNamespace = {
|
|
|
329
477
|
* @param options - Tools / maxSteps / model / budget
|
|
330
478
|
*/
|
|
331
479
|
agent(name: string, options: AiAgentOptions = {}): AiAgentDecl {
|
|
480
|
+
const approvals: Record<string, AiAgentApprovalDecl> = {};
|
|
481
|
+
for (const tool of options.tools ?? []) {
|
|
482
|
+
if (typeof tool === "string" || !("approval" in tool) || tool.approval === undefined) {
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
if (tool.approval === false) continue;
|
|
486
|
+
if (tool.gate === undefined) {
|
|
487
|
+
throw new TypeError(`ai.agent("${name}"): tool "${toolName(tool)}" approval requires gate`);
|
|
488
|
+
}
|
|
489
|
+
approvals[toolName(tool)] = {
|
|
490
|
+
gate: gateName(tool.gate),
|
|
491
|
+
timeout: tool.timeout ?? "24h",
|
|
492
|
+
...(typeof tool.approval === "function" ? { when: tool.approval } : {}),
|
|
493
|
+
};
|
|
494
|
+
}
|
|
332
495
|
const decl: AiAgentDecl = {
|
|
333
496
|
kind: "agent",
|
|
334
497
|
name,
|
|
335
498
|
tools: (options.tools ?? []).map(toolName),
|
|
499
|
+
...(Object.keys(approvals).length > 0 ? { approvals } : {}),
|
|
336
500
|
...(options.maxSteps !== undefined ? { maxSteps: options.maxSteps } : {}),
|
|
501
|
+
...(options.maxDepth !== undefined ? { maxDepth: options.maxDepth } : {}),
|
|
337
502
|
...(options.budget !== undefined ? { budget: options.budget } : {}),
|
|
338
503
|
...(typeof options.model === "string"
|
|
339
504
|
? { model: options.model }
|
|
@@ -345,6 +510,27 @@ export const ai: AiNamespace = {
|
|
|
345
510
|
return decl;
|
|
346
511
|
},
|
|
347
512
|
|
|
513
|
+
decision(name: string, options: AiDecisionOptions): AiDecisionDecl {
|
|
514
|
+
const decl = buildDecisionDecl(name, options);
|
|
515
|
+
aiDecisionRegistry.push(decl);
|
|
516
|
+
return decl;
|
|
517
|
+
},
|
|
518
|
+
|
|
519
|
+
choice(instructions: string, options: Readonly<Record<string, string | null>>): AiChoiceQuestion {
|
|
520
|
+
return { kind: "choice", instructions, options };
|
|
521
|
+
},
|
|
522
|
+
|
|
523
|
+
score(instructions: string, levels: readonly string[]): AiScoreQuestion {
|
|
524
|
+
return { kind: "score", instructions, levels };
|
|
525
|
+
},
|
|
526
|
+
|
|
527
|
+
boolean(
|
|
528
|
+
instructions: string,
|
|
529
|
+
criteria?: { readonly true?: string; readonly false?: string },
|
|
530
|
+
): AiBooleanQuestion {
|
|
531
|
+
return { kind: "boolean", instructions, ...(criteria !== undefined ? { criteria } : {}) };
|
|
532
|
+
},
|
|
533
|
+
|
|
348
534
|
/**
|
|
349
535
|
* Declare an external MCP server whose allowlisted tools join `fx.call`.
|
|
350
536
|
*
|
|
@@ -386,3 +572,68 @@ export const ai: AiNamespace = {
|
|
|
386
572
|
return decl;
|
|
387
573
|
},
|
|
388
574
|
};
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Validate a decision and return the registered shape.
|
|
578
|
+
*
|
|
579
|
+
* @param name - Decision id
|
|
580
|
+
* @param options - Author options
|
|
581
|
+
*/
|
|
582
|
+
export function buildDecisionDecl(name: string, options: AiDecisionOptions): AiDecisionDecl {
|
|
583
|
+
if (!name) throw new TypeError("ai.decision: name is required");
|
|
584
|
+
const keys = Object.keys(options.ask ?? {});
|
|
585
|
+
if (keys.length === 0) throw new TypeError(`ai.decision("${name}"): ask is empty`);
|
|
586
|
+
const hasReview = options.review !== undefined;
|
|
587
|
+
const abstain = options.onUncertain === "abstain";
|
|
588
|
+
if (hasReview === abstain) {
|
|
589
|
+
throw new TypeError(
|
|
590
|
+
`ai.decision("${name}"): declare exactly one of review or onUncertain: "abstain"`,
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
if (options.autonomy !== undefined && options.autonomy.audit === undefined) {
|
|
594
|
+
throw new TypeError(`ai.decision("${name}"): autonomy requires audit`);
|
|
595
|
+
}
|
|
596
|
+
for (const key of keys) {
|
|
597
|
+
if (key === "meta" || key === "$") {
|
|
598
|
+
throw new TypeError(`ai.decision("${name}"): question id "${key}" is reserved`);
|
|
599
|
+
}
|
|
600
|
+
const question = options.ask[key];
|
|
601
|
+
if (!question) continue;
|
|
602
|
+
if (question.kind === "choice") {
|
|
603
|
+
const count = Object.keys(question.options).length;
|
|
604
|
+
if (count > 254) {
|
|
605
|
+
throw new TypeError(
|
|
606
|
+
`ai.decision("${name}"): choice "${key}" has ${count} options; max 254`,
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
if (Object.prototype.hasOwnProperty.call(question.options, "none_of_these")) {
|
|
610
|
+
throw new TypeError(`ai.decision("${name}"): choice "${key}" must not set none_of_these`);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
if (question.kind === "score") {
|
|
614
|
+
const count = question.levels.length;
|
|
615
|
+
if (count < 2 || count > 10) {
|
|
616
|
+
throw new TypeError(`ai.decision("${name}"): score "${key}" needs 2–10 levels`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
const review = typeof options.review === "string" ? options.review : options.review?.name;
|
|
621
|
+
const model =
|
|
622
|
+
typeof options.model === "string"
|
|
623
|
+
? options.model
|
|
624
|
+
: (options.model?.model ?? options.model?.name);
|
|
625
|
+
return {
|
|
626
|
+
kind: "decision",
|
|
627
|
+
name,
|
|
628
|
+
ask: options.ask,
|
|
629
|
+
mode: abstain ? "abstain" : "review",
|
|
630
|
+
...(review !== undefined ? { review } : {}),
|
|
631
|
+
...(model !== undefined ? { model } : {}),
|
|
632
|
+
driverId: options.driverId ?? "openrouter",
|
|
633
|
+
...(options.autonomy !== undefined ? { autonomy: options.autonomy } : {}),
|
|
634
|
+
...(options.evals !== undefined ? { evals: options.evals } : {}),
|
|
635
|
+
...(options.timeout !== undefined ? { timeout: options.timeout } : {}),
|
|
636
|
+
...(options.locale !== undefined ? { locale: options.locale } : {}),
|
|
637
|
+
...(options.in !== undefined ? { inputSchema: options.in } : {}),
|
|
638
|
+
};
|
|
639
|
+
}
|