@cleocode/core 2026.9.22 → 2026.9.23
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/dist/build-provenance.json +6 -0
- package/dist/decide/audit.d.ts +7 -1
- package/dist/decide/audit.d.ts.map +1 -1
- package/dist/decide/audit.js.map +1 -1
- package/dist/decide/client.d.ts +46 -4
- package/dist/decide/client.d.ts.map +1 -1
- package/dist/decide/client.js +328 -33
- package/dist/decide/client.js.map +1 -1
- package/dist/decide/credentials.d.ts +28 -4
- package/dist/decide/credentials.d.ts.map +1 -1
- package/dist/decide/credentials.js +50 -12
- package/dist/decide/credentials.js.map +1 -1
- package/dist/decide/index.d.ts +10 -4
- package/dist/decide/index.d.ts.map +1 -1
- package/dist/decide/index.js +10 -4
- package/dist/decide/index.js.map +1 -1
- package/dist/decide/jev-wire.d.ts +100 -8
- package/dist/decide/jev-wire.d.ts.map +1 -1
- package/dist/decide/jev-wire.js +402 -55
- package/dist/decide/jev-wire.js.map +1 -1
- package/dist/decide/operations.d.ts +97 -20
- package/dist/decide/operations.d.ts.map +1 -1
- package/dist/decide/operations.js +180 -28
- package/dist/decide/operations.js.map +1 -1
- package/dist/decide/provider-state.d.ts +89 -0
- package/dist/decide/provider-state.d.ts.map +1 -0
- package/dist/decide/provider-state.js +134 -0
- package/dist/decide/provider-state.js.map +1 -0
- package/dist/decide/provider.d.ts +26 -5
- package/dist/decide/provider.d.ts.map +1 -1
- package/dist/decide/provider.js.map +1 -1
- package/dist/decide/providers.d.ts +58 -0
- package/dist/decide/providers.d.ts.map +1 -0
- package/dist/decide/providers.js +69 -0
- package/dist/decide/providers.js.map +1 -0
- package/dist/decide/site.d.ts +2 -2
- package/dist/decide/site.d.ts.map +1 -1
- package/dist/decide/site.js +2 -0
- package/dist/decide/site.js.map +1 -1
- package/dist/decide/sites/list.d.ts +46 -0
- package/dist/decide/sites/list.d.ts.map +1 -0
- package/dist/decide/sites/list.js +206 -0
- package/dist/decide/sites/list.js.map +1 -0
- package/dist/decide/sites/registry.d.ts +130 -0
- package/dist/decide/sites/registry.d.ts.map +1 -0
- package/dist/decide/sites/registry.js +375 -0
- package/dist/decide/sites/registry.js.map +1 -0
- package/dist/decide/spend.d.ts +196 -0
- package/dist/decide/spend.d.ts.map +1 -0
- package/dist/decide/spend.js +387 -0
- package/dist/decide/spend.js.map +1 -0
- package/dist/decide/wizard.d.ts +53 -0
- package/dist/decide/wizard.d.ts.map +1 -0
- package/dist/decide/wizard.js +144 -0
- package/dist/decide/wizard.js.map +1 -0
- package/dist/docs/docs-audit.d.ts.map +1 -1
- package/dist/docs/docs-audit.js +1 -2
- package/dist/docs/docs-audit.js.map +1 -1
- package/dist/docs/export-document.js +775 -415
- package/dist/docs/export-document.js.map +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/init/scaffold-workflows.d.ts.map +1 -1
- package/dist/init/scaffold-workflows.js +6 -3
- package/dist/init/scaffold-workflows.js.map +1 -1
- package/dist/init/upgrade-workflows.d.ts.map +1 -1
- package/dist/init/upgrade-workflows.js +6 -3
- package/dist/init/upgrade-workflows.js.map +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +1 -1
- package/dist/internal.js.map +1 -1
- package/dist/llm/plugin-facade.js +886 -526
- package/dist/llm/plugin-facade.js.map +3 -3
- package/dist/memory/decision-contradiction.d.ts +3 -3
- package/dist/memory/decision-contradiction.d.ts.map +1 -1
- package/dist/memory/decision-contradiction.js +4 -3
- package/dist/memory/decision-contradiction.js.map +1 -1
- package/dist/memory/graph-memory-bridge.d.ts.map +1 -1
- package/dist/memory/graph-memory-bridge.js +2 -0
- package/dist/memory/graph-memory-bridge.js.map +1 -1
- package/dist/memory/observation-type-decision.d.ts +2 -2
- package/dist/memory/observation-type-decision.d.ts.map +1 -1
- package/dist/memory/observation-type-decision.js +3 -2
- package/dist/memory/observation-type-decision.js.map +1 -1
- package/dist/memory/pipeline-manifest-sqlite.d.ts +111 -0
- package/dist/memory/pipeline-manifest-sqlite.d.ts.map +1 -1
- package/dist/memory/pipeline-manifest-sqlite.js +230 -2
- package/dist/memory/pipeline-manifest-sqlite.js.map +1 -1
- package/dist/nexus/migrations/M-split-graph.d.ts.map +1 -1
- package/dist/nexus/migrations/M-split-graph.js +4 -0
- package/dist/nexus/migrations/M-split-graph.js.map +1 -1
- package/dist/nexus/store.d.ts.map +1 -1
- package/dist/nexus/store.js +2 -0
- package/dist/nexus/store.js.map +1 -1
- package/dist/orchestrate/pivot.d.ts.map +1 -1
- package/dist/orchestrate/pivot.js +13 -8
- package/dist/orchestrate/pivot.js.map +1 -1
- package/dist/orchestrate/plan.d.ts.map +1 -1
- package/dist/orchestrate/plan.js +3 -0
- package/dist/orchestrate/plan.js.map +1 -1
- package/dist/orchestration/lead-rollup.js +14 -2
- package/dist/orchestration/lead-rollup.js.map +1 -1
- package/dist/orchestration/owner-decision-readiness.d.ts +2 -2
- package/dist/orchestration/owner-decision-readiness.d.ts.map +1 -1
- package/dist/orchestration/owner-decision-readiness.js +3 -2
- package/dist/orchestration/owner-decision-readiness.js.map +1 -1
- package/dist/orchestration/validate-spawn.d.ts.map +1 -1
- package/dist/orchestration/validate-spawn.js +2 -0
- package/dist/orchestration/validate-spawn.js.map +1 -1
- package/dist/project-scope.d.ts +16 -0
- package/dist/project-scope.d.ts.map +1 -1
- package/dist/project-scope.js +38 -2
- package/dist/project-scope.js.map +1 -1
- package/dist/release/github-pr.d.ts.map +1 -1
- package/dist/release/github-pr.js +2 -1
- package/dist/release/github-pr.js.map +1 -1
- package/dist/routing/capability-matrix.d.ts.map +1 -1
- package/dist/routing/capability-matrix.js +7 -0
- package/dist/routing/capability-matrix.js.map +1 -1
- package/dist/selfimprove/fix-gen.js +895 -535
- package/dist/selfimprove/fix-gen.js.map +3 -3
- package/dist/sessions/briefing.d.ts.map +1 -1
- package/dist/sessions/briefing.js +23 -62
- package/dist/sessions/briefing.js.map +1 -1
- package/dist/sessions/focus-state-store.d.ts +6 -1
- package/dist/sessions/focus-state-store.d.ts.map +1 -1
- package/dist/sessions/focus-state-store.js +5 -2
- package/dist/sessions/focus-state-store.js.map +1 -1
- package/dist/setup/config-json-merge.d.ts +28 -0
- package/dist/setup/config-json-merge.d.ts.map +1 -1
- package/dist/setup/config-json-merge.js +51 -0
- package/dist/setup/config-json-merge.js.map +1 -1
- package/dist/setup/index.d.ts +13 -10
- package/dist/setup/index.d.ts.map +1 -1
- package/dist/setup/index.js +15 -10
- package/dist/setup/index.js.map +1 -1
- package/dist/setup/sections/llm.js +1 -1
- package/dist/setup/sections/llm.js.map +1 -1
- package/dist/setup/sections/system-one.d.ts +28 -0
- package/dist/setup/sections/system-one.d.ts.map +1 -0
- package/dist/setup/sections/system-one.js +53 -0
- package/dist/setup/sections/system-one.js.map +1 -0
- package/dist/setup/wizard.d.ts +15 -1
- package/dist/setup/wizard.d.ts.map +1 -1
- package/dist/setup/wizard.js +15 -0
- package/dist/setup/wizard.js.map +1 -1
- package/dist/skills/prune-bundled.d.ts +40 -1
- package/dist/skills/prune-bundled.d.ts.map +1 -1
- package/dist/skills/prune-bundled.js +117 -1
- package/dist/skills/prune-bundled.js.map +1 -1
- package/dist/store/agent-registry-accessor.d.ts.map +1 -1
- package/dist/store/agent-registry-accessor.js +2 -0
- package/dist/store/agent-registry-accessor.js.map +1 -1
- package/dist/store/backup-pack.js +1 -1
- package/dist/store/backup-pack.js.map +1 -1
- package/dist/store/backup.d.ts +15 -1
- package/dist/store/backup.d.ts.map +1 -1
- package/dist/store/backup.js +18 -2
- package/dist/store/backup.js.map +1 -1
- package/dist/store/conduit-sqlite.d.ts.map +1 -1
- package/dist/store/conduit-sqlite.js +3 -0
- package/dist/store/conduit-sqlite.js.map +1 -1
- package/dist/store/credential-transfer.d.ts.map +1 -1
- package/dist/store/credential-transfer.js +2 -0
- package/dist/store/credential-transfer.js.map +1 -1
- package/dist/store/cross-db-cleanup.d.ts.map +1 -1
- package/dist/store/cross-db-cleanup.js +2 -0
- package/dist/store/cross-db-cleanup.js.map +1 -1
- package/dist/store/dual-scope-db.d.ts.map +1 -1
- package/dist/store/dual-scope-db.js +13 -2
- package/dist/store/dual-scope-db.js.map +1 -1
- package/dist/store/migrate-signaldock-to-conduit.d.ts.map +1 -1
- package/dist/store/migrate-signaldock-to-conduit.js +3 -0
- package/dist/store/migrate-signaldock-to-conduit.js.map +1 -1
- package/dist/store/migration-manager.d.ts.map +1 -1
- package/dist/store/migration-manager.js +37 -3
- package/dist/store/migration-manager.js.map +1 -1
- package/dist/store/migration-sqlite.d.ts.map +1 -1
- package/dist/store/migration-sqlite.js +2 -0
- package/dist/store/migration-sqlite.js.map +1 -1
- package/dist/store/nexus-sqlite.d.ts.map +1 -1
- package/dist/store/nexus-sqlite.js +3 -0
- package/dist/store/nexus-sqlite.js.map +1 -1
- package/dist/store/open-cleo-db.d.ts.map +1 -1
- package/dist/store/open-cleo-db.js +3 -0
- package/dist/store/open-cleo-db.js.map +1 -1
- package/dist/store/portable-bundle-import.d.ts.map +1 -1
- package/dist/store/portable-bundle-import.js +2 -0
- package/dist/store/portable-bundle-import.js.map +1 -1
- package/dist/store/portable-bundle-relocate.d.ts.map +1 -1
- package/dist/store/portable-bundle-relocate.js +2 -0
- package/dist/store/portable-bundle-relocate.js.map +1 -1
- package/dist/store/portable-bundle-scan.js +1 -1
- package/dist/store/portable-bundle-scan.js.map +1 -1
- package/dist/store/ports/domain-binding.d.ts.map +1 -1
- package/dist/store/ports/domain-binding.js +5 -0
- package/dist/store/ports/domain-binding.js.map +1 -1
- package/dist/store/split-brain-import.d.ts.map +1 -1
- package/dist/store/split-brain-import.js +2 -0
- package/dist/store/split-brain-import.js.map +1 -1
- package/dist/store/sqlite-data-accessor.js +7 -0
- package/dist/store/sqlite-data-accessor.js.map +1 -1
- package/dist/store/sqlite-native.d.ts.map +1 -1
- package/dist/store/sqlite-native.js +1 -0
- package/dist/store/sqlite-native.js.map +1 -1
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/sqlite.js +5 -0
- package/dist/store/sqlite.js.map +1 -1
- package/dist/store/worktree-build-guard.d.ts +191 -0
- package/dist/store/worktree-build-guard.d.ts.map +1 -0
- package/dist/store/worktree-build-guard.js +486 -0
- package/dist/store/worktree-build-guard.js.map +1 -0
- package/dist/store/worktree-isolation-guard.d.ts +27 -0
- package/dist/store/worktree-isolation-guard.d.ts.map +1 -1
- package/dist/store/worktree-isolation-guard.js +67 -1
- package/dist/store/worktree-isolation-guard.js.map +1 -1
- package/dist/store/writer-lease.d.ts.map +1 -1
- package/dist/store/writer-lease.js +2 -0
- package/dist/store/writer-lease.js.map +1 -1
- package/dist/system/backup.d.ts +6 -2
- package/dist/system/backup.d.ts.map +1 -1
- package/dist/system/backup.js +6 -3
- package/dist/system/backup.js.map +1 -1
- package/dist/task-tools/index.d.ts +4 -2
- package/dist/task-tools/index.d.ts.map +1 -1
- package/dist/task-tools/index.js +3 -2
- package/dist/task-tools/index.js.map +1 -1
- package/dist/task-tools/score-task-priority.d.ts +93 -18
- package/dist/task-tools/score-task-priority.d.ts.map +1 -1
- package/dist/task-tools/score-task-priority.js +193 -81
- package/dist/task-tools/score-task-priority.js.map +1 -1
- package/dist/tasks/affected-packages.d.ts +5 -1
- package/dist/tasks/affected-packages.d.ts.map +1 -1
- package/dist/tasks/affected-packages.js +21 -3
- package/dist/tasks/affected-packages.js.map +1 -1
- package/dist/tasks/analyze.d.ts +29 -1
- package/dist/tasks/analyze.d.ts.map +1 -1
- package/dist/tasks/analyze.js +45 -34
- package/dist/tasks/analyze.js.map +1 -1
- package/dist/tasks/backfill-child-projections.d.ts.map +1 -1
- package/dist/tasks/backfill-child-projections.js +4 -1
- package/dist/tasks/backfill-child-projections.js.map +1 -1
- package/dist/tasks/change-set.d.ts.map +1 -1
- package/dist/tasks/change-set.js +16 -3
- package/dist/tasks/change-set.js.map +1 -1
- package/dist/tasks/command-line.d.ts +54 -0
- package/dist/tasks/command-line.d.ts.map +1 -0
- package/dist/tasks/command-line.js +151 -0
- package/dist/tasks/command-line.js.map +1 -0
- package/dist/tasks/component-pr.d.ts.map +1 -1
- package/dist/tasks/component-pr.js +97 -12
- package/dist/tasks/component-pr.js.map +1 -1
- package/dist/tasks/delete.d.ts.map +1 -1
- package/dist/tasks/delete.js +20 -0
- package/dist/tasks/delete.js.map +1 -1
- package/dist/tasks/duplicate-detector.d.ts +3 -3
- package/dist/tasks/duplicate-detector.d.ts.map +1 -1
- package/dist/tasks/duplicate-detector.js +4 -3
- package/dist/tasks/duplicate-detector.js.map +1 -1
- package/dist/tasks/engine-wrap-ops.d.ts +29 -0
- package/dist/tasks/engine-wrap-ops.d.ts.map +1 -1
- package/dist/tasks/engine-wrap-ops.js +40 -0
- package/dist/tasks/engine-wrap-ops.js.map +1 -1
- package/dist/tasks/engine-wrap.d.ts +1 -1
- package/dist/tasks/engine-wrap.d.ts.map +1 -1
- package/dist/tasks/engine-wrap.js +1 -1
- package/dist/tasks/engine-wrap.js.map +1 -1
- package/dist/tasks/evidence.d.ts +2 -0
- package/dist/tasks/evidence.d.ts.map +1 -1
- package/dist/tasks/evidence.js +30 -4
- package/dist/tasks/evidence.js.map +1 -1
- package/dist/tasks/gate-runner.d.ts.map +1 -1
- package/dist/tasks/gate-runner.js +48 -12
- package/dist/tasks/gate-runner.js.map +1 -1
- package/dist/tasks/index.d.ts +1 -1
- package/dist/tasks/index.d.ts.map +1 -1
- package/dist/tasks/index.js +1 -1
- package/dist/tasks/index.js.map +1 -1
- package/dist/tasks/ops.d.ts +1 -0
- package/dist/tasks/ops.d.ts.map +1 -1
- package/dist/tasks/ranking-audit.d.ts +122 -0
- package/dist/tasks/ranking-audit.d.ts.map +1 -0
- package/dist/tasks/ranking-audit.js +193 -0
- package/dist/tasks/ranking-audit.js.map +1 -0
- package/dist/tasks/task-analyze.d.ts.map +1 -1
- package/dist/tasks/task-analyze.js +19 -32
- package/dist/tasks/task-analyze.js.map +1 -1
- package/dist/tasks/task-next.d.ts +44 -2
- package/dist/tasks/task-next.d.ts.map +1 -1
- package/dist/tasks/task-next.js +80 -102
- package/dist/tasks/task-next.js.map +1 -1
- package/dist/tasks/task-ops.d.ts +1 -1
- package/dist/tasks/task-ops.d.ts.map +1 -1
- package/dist/tasks/task-ops.js +1 -1
- package/dist/tasks/task-ops.js.map +1 -1
- package/dist/tasks/tool-resolver.d.ts +7 -1
- package/dist/tasks/tool-resolver.d.ts.map +1 -1
- package/dist/tasks/tool-resolver.js +24 -10
- package/dist/tasks/tool-resolver.js.map +1 -1
- package/dist/tasks/update.d.ts +8 -2
- package/dist/tasks/update.d.ts.map +1 -1
- package/dist/tasks/update.js +28 -1
- package/dist/tasks/update.js.map +1 -1
- package/dist/telemetry/sqlite.d.ts.map +1 -1
- package/dist/telemetry/sqlite.js +2 -0
- package/dist/telemetry/sqlite.js.map +1 -1
- package/dist/validation/doctor/checks.d.ts.map +1 -1
- package/dist/validation/doctor/checks.js +2 -2
- package/dist/validation/doctor/checks.js.map +1 -1
- package/package.json +14 -14
- package/schemas/config.schema.json +13 -0
- package/templates/CLEO-INJECTION.md +1 -1
- package/templates/CLEO-REFERENCE.md +5 -1
package/dist/decide/audit.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ import type { DecisionAnswer, DecisionOutcome, DecisionOutcomeSource } from '@cl
|
|
|
18
18
|
/** Project-relative path of the decision audit log. */
|
|
19
19
|
export declare const DECISION_AUDIT_FILE = ".cleo/audit/decisions.jsonl";
|
|
20
20
|
/** Why a fallback was used (absent for provider/cache outcomes). */
|
|
21
|
-
export type DecisionFallbackReason = 'unconfigured' | 'invalid_request' | 'budget_exhausted' | 'budget_cooling_down' | 'budget_unavailable' | 'timeout' | 'unauthorized' | 'insufficient_credits' | 'rate_limited' | 'server_error' | 'network' | 'invalid_response' | 'provider_error';
|
|
21
|
+
export type DecisionFallbackReason = 'unconfigured' | 'invalid_request' | 'budget_exhausted' | 'budget_cooling_down' | 'budget_unavailable' | 'timeout' | 'unauthorized' | 'insufficient_credits' | 'key_limit_exceeded' | 'budget' | 'rate_limited' | 'overloaded' | 'server_error' | 'network' | 'invalid_response' | 'provider_error';
|
|
22
22
|
/** One audited answer: the decided value plus its distribution. */
|
|
23
23
|
export interface DecisionAuditAnswer {
|
|
24
24
|
/** Question type. */
|
|
@@ -89,6 +89,12 @@ export interface DecisionAuditEntry {
|
|
|
89
89
|
readonly latencyMs: number;
|
|
90
90
|
/** Provider-reported cost in USD, when known. */
|
|
91
91
|
readonly costUsd?: number;
|
|
92
|
+
/** Provider-reported cost in integer micro-dollars, when known (T12664). */
|
|
93
|
+
readonly costMicros?: number;
|
|
94
|
+
/** Account balance in integer micro-dollars reported with the answer, when known. */
|
|
95
|
+
readonly balanceMicros?: number;
|
|
96
|
+
/** Provider model checkpoint that answered, when reported. */
|
|
97
|
+
readonly checkpoint?: string;
|
|
92
98
|
/** Model the request named, when it named one. */
|
|
93
99
|
readonly model?: string;
|
|
94
100
|
/** Shadow-mode comparison, when the call site recorded one. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/decide/audit.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/decide/audit.ts"],"names":[],"mappings":"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"}
|
package/dist/decide/audit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/decide/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAClF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,uDAAuD;AACvD,MAAM,CAAC,MAAM,mBAAmB,GAAG,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/decide/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAClF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,uDAAuD;AACvD,MAAM,CAAC,MAAM,mBAAmB,GAAG,6BAA6B,CAAC;AA+GjE;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,OAAwB;IACnD,MAAM,GAAG,GAAwC,EAAE,CAAC;IACpD,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACxD,GAAG,CAAC,IAAI,CAAC;YACP,CAAC,CAAC,IAAI,KAAK,MAAM;gBACf,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE;gBACxF,CAAC,CAAC;oBACE,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,KAAK,EAAE,CAAC,CAAC,KAAK;oBACd,aAAa,EAAE,CAAC,CAAC,aAAa;oBAC9B,UAAU,EAAE,CAAC,CAAC,UAAU;iBACzB,CAAC;IACV,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,4DAA4D;AAC5D,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAEhE,kFAAkF;AAClF,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC;AAU7C,sEAAsE;AACtE,SAAS,MAAM,CAAC,IAAY,EAAE,IAAY;IACxC,MAAM,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,IAAI,CAAC;YACH,UAAU,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACjD,CAAC;QAAC,MAAM,CAAC;YACP,wCAAwC;QAC1C,CAAC;IACH,CAAC;IACD,IAAI,IAAI,IAAI,CAAC;QAAE,UAAU,CAAC,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC;;QACxC,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACrC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CACtC,WAAmB,EACnB,WAAkC,EAAE;IAEpC,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,QAAQ,IAAI,gCAAgC,CAAC,CAAC;IACpF,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,IAAI,2BAA2B,CAAC,CAAC,CAAC;IACnF,OAAO;QACL,KAAK,CAAC,KAAK;YACT,IAAI,CAAC;gBACH,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBACpC,IAAI,IAAI,GAAG,CAAC,CAAC;gBACb,IAAI,CAAC;oBACH,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;gBAC7B,CAAC;gBAAC,MAAM,CAAC;oBACP,oBAAoB;gBACtB,CAAC;gBACD,IAAI,IAAI,IAAI,QAAQ;oBAAE,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBACzC,cAAc,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC9D,CAAC;YAAC,MAAM,CAAC;gBACP,wCAAwC;YAC1C,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/decide/client.d.ts
CHANGED
|
@@ -11,10 +11,16 @@
|
|
|
11
11
|
* request with the contract schema (invalid → fallback).
|
|
12
12
|
* 3. In-memory LRU cache hit → `source: 'cache'`.
|
|
13
13
|
* 4. Within ONE per-site deadline (default {@link DEFAULT_DECISION_TIMEOUT_MS}):
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* check the monthly spend cap (`./spend.ts`, D11159), take a token from the
|
|
15
|
+
* machine-wide request budget, then call the provider. Spend cap reached
|
|
16
|
+
* (`budget`), key monthly limit (`key_limit_exceeded`), budget denial,
|
|
17
|
+
* timeout, 401/402/403/429/5xx, network failure or a malformed answer →
|
|
18
|
+
* fallback. A 429 empties the shared budget for its `retry-after`; a
|
|
19
|
+
* 503/529 trips it as a short circuit breaker; a 403 key limit stops
|
|
20
|
+
* decisions until the UTC month ends. Reported cost is added to the spend
|
|
21
|
+
* ledger. A call aborted AFTER it was sent (deadline or caller) may still be
|
|
22
|
+
* billed, so its reserved estimate is committed; only a failure before send
|
|
23
|
+
* or an error response releases the reservation.
|
|
18
24
|
* 5. Append one audit line (never containing the key or raw state).
|
|
19
25
|
*
|
|
20
26
|
* Provider-specific knowledge (endpoints, wire JSON) lives only in
|
|
@@ -28,8 +34,11 @@ import { type DecisionAuditSink } from './audit.js';
|
|
|
28
34
|
import { type DecisionBudget } from './budget.js';
|
|
29
35
|
import { type DecisionCache } from './cache.js';
|
|
30
36
|
import { type DecisionProvider, type DecisionProviderConnection } from './provider.js';
|
|
37
|
+
import { type SpendLedger } from './spend.js';
|
|
31
38
|
/** Default per-site deadline for the budget + provider round trip, in ms. */
|
|
32
39
|
export declare const DEFAULT_DECISION_TIMEOUT_MS = 300;
|
|
40
|
+
/** Circuit-breaker trip after a 503/529 that carried no `retry-after`, ms. */
|
|
41
|
+
export declare const OVERLOADED_COOLDOWN_MS = 30000;
|
|
33
42
|
/**
|
|
34
43
|
* A call site's local heuristic. It must be total and fast: it answers every
|
|
35
44
|
* question in the request whenever no provider answer is available.
|
|
@@ -52,6 +61,10 @@ export interface DecideOptions {
|
|
|
52
61
|
readonly cache?: DecisionCache | null;
|
|
53
62
|
/** Request budget; `null` disables. Default: the machine-wide file bucket. */
|
|
54
63
|
readonly budget?: DecisionBudget | null;
|
|
64
|
+
/** Monthly spend ledger; `null` disables the cap. Default: the machine-wide file ledger. */
|
|
65
|
+
readonly spend?: SpendLedger | null;
|
|
66
|
+
/** Monthly cap in micro-dollars. Default: `decide.budget.monthlyMicros`, else $1. */
|
|
67
|
+
readonly spendCapMicros?: number;
|
|
55
68
|
/** Audit sink; `null` disables. Default: `<projectRoot>/.cleo/audit/decisions.jsonl`. */
|
|
56
69
|
readonly audit?: DecisionAuditSink | null;
|
|
57
70
|
/** Project root for the default audit sink. Default: the resolved CLEO project root. */
|
|
@@ -76,6 +89,12 @@ export declare function _resetDecideDefaultsForTest(): void;
|
|
|
76
89
|
* @returns Redacted state of the same shape.
|
|
77
90
|
*/
|
|
78
91
|
export declare function redactDecisionState(state: DecisionState): DecisionState;
|
|
92
|
+
/**
|
|
93
|
+
* Cost reserved per question before a call, micro-dollars. Only a
|
|
94
|
+
* reservation: the provider-reported cost replaces it on commit, so this is
|
|
95
|
+
* never what is recorded (layahost bills 5–15 µ$ per decision, 2026-09).
|
|
96
|
+
*/
|
|
97
|
+
export declare const DECISION_COST_ESTIMATE_MICROS_PER_QUESTION = 15;
|
|
79
98
|
/**
|
|
80
99
|
* Ask a typed decision at call site `siteId`.
|
|
81
100
|
*
|
|
@@ -100,4 +119,27 @@ export declare function redactDecisionState(state: DecisionState): DecisionState
|
|
|
100
119
|
* ```
|
|
101
120
|
*/
|
|
102
121
|
export declare function decide(siteId: string, req: DecisionRequest, fallback: DecisionHeuristic, opts?: DecideOptions): Promise<DecisionOutcome>;
|
|
122
|
+
/** One request of a {@link decideBatch} call, with its own heuristic. */
|
|
123
|
+
export interface DecisionBatchEntry {
|
|
124
|
+
/** Questions and state. */
|
|
125
|
+
readonly req: DecisionRequest;
|
|
126
|
+
/** The heuristic for this request. */
|
|
127
|
+
readonly fallback: DecisionHeuristic;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Ask several typed decisions at call site `siteId` under ONE deadline.
|
|
131
|
+
*
|
|
132
|
+
* When the provider reports a batch capability (`capabilities().batch`) and
|
|
133
|
+
* the entries fit its limits, the uncached ones go out as a single batch call
|
|
134
|
+
* (one spend check, one budget token). Otherwise it degrades to sequential
|
|
135
|
+
* {@link decide} calls that share the remaining deadline; entries the
|
|
136
|
+
* deadline cannot reach fall back with reason `timeout`. Never rejects; each
|
|
137
|
+
* entry is audited like a single decision.
|
|
138
|
+
*
|
|
139
|
+
* @param siteId - Stable call-site identifier.
|
|
140
|
+
* @param entries - Requests with their heuristics.
|
|
141
|
+
* @param opts - Same wiring as {@link decide}; `timeoutMs` covers the whole batch.
|
|
142
|
+
* @returns One outcome per entry, in order.
|
|
143
|
+
*/
|
|
144
|
+
export declare function decideBatch(siteId: string, entries: readonly DecisionBatchEntry[], opts?: DecideOptions): Promise<DecisionOutcome[]>;
|
|
103
145
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/decide/client.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/decide/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EACL,KAAK,cAAc,EAGnB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,aAAa,EAInB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAGL,KAAK,iBAAiB,EAEvB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAEL,KAAK,aAAa,EAGnB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,0BAA0B,EAGhC,MAAM,eAAe,CAAC;AAEvB,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,YAAY,CAAC;AAEpB,6EAA6E;AAC7E,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,GAAG,EAAE,eAAe,KAAK,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;AAEnG,kCAAkC;AAClC,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,0BAA0B,GAAG,IAAI,CAAC;IACxD,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IACrC,uFAAuF;IACvF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,oEAAoE;IACpE,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACtC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACxC,4FAA4F;IAC5F,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACpC,qFAAqF;IACrF,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,yFAAyF;IACzF,QAAQ,CAAC,KAAK,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC1C,wFAAwF;IACxF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,6DAA6D;IAC7D,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,wEAAwE;IACxE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAwCD;;;;GAIG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAKlD;AAWD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa,CAMvE;AAsED;;;;GAIG;AACH,eAAO,MAAM,0CAA0C,KAAK,CAAC;AA2K7D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,MAAM,CAC1B,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,eAAe,CAAC,CAyB1B;AAmED,yEAAyE;AACzE,MAAM,WAAW,kBAAkB;IACjC,2BAA2B;IAC3B,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAC;IAC9B,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;CACtC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,SAAS,kBAAkB,EAAE,EACtC,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,eAAe,EAAE,CAAC,CA8F5B"}
|
package/dist/decide/client.js
CHANGED
|
@@ -11,10 +11,16 @@
|
|
|
11
11
|
* request with the contract schema (invalid → fallback).
|
|
12
12
|
* 3. In-memory LRU cache hit → `source: 'cache'`.
|
|
13
13
|
* 4. Within ONE per-site deadline (default {@link DEFAULT_DECISION_TIMEOUT_MS}):
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* check the monthly spend cap (`./spend.ts`, D11159), take a token from the
|
|
15
|
+
* machine-wide request budget, then call the provider. Spend cap reached
|
|
16
|
+
* (`budget`), key monthly limit (`key_limit_exceeded`), budget denial,
|
|
17
|
+
* timeout, 401/402/403/429/5xx, network failure or a malformed answer →
|
|
18
|
+
* fallback. A 429 empties the shared budget for its `retry-after`; a
|
|
19
|
+
* 503/529 trips it as a short circuit breaker; a 403 key limit stops
|
|
20
|
+
* decisions until the UTC month ends. Reported cost is added to the spend
|
|
21
|
+
* ledger. A call aborted AFTER it was sent (deadline or caller) may still be
|
|
22
|
+
* billed, so its reserved estimate is committed; only a failure before send
|
|
23
|
+
* or an error response releases the reservation.
|
|
18
24
|
* 5. Append one audit line (never containing the key or raw state).
|
|
19
25
|
*
|
|
20
26
|
* Provider-specific knowledge (endpoints, wire JSON) lives only in
|
|
@@ -32,10 +38,16 @@ import { createDecisionCache, decisionCacheKey, hashCanonical, } from './cache.j
|
|
|
32
38
|
import { loadDecideConnection } from './credentials.js';
|
|
33
39
|
import { createJevProvider, JEV_ADAPTER_VERSION } from './jev-wire.js';
|
|
34
40
|
import { DecisionProviderError, } from './provider.js';
|
|
41
|
+
import { cachedCapabilities } from './provider-state.js';
|
|
42
|
+
import { createFileSpendLedger, DEFAULT_MONTHLY_SPEND_CAP_MICROS, MONTHLY_SPEND_CAP_KEY, } from './spend.js';
|
|
35
43
|
/** Default per-site deadline for the budget + provider round trip, in ms. */
|
|
36
44
|
export const DEFAULT_DECISION_TIMEOUT_MS = 300;
|
|
45
|
+
/** Circuit-breaker trip after a 503/529 that carried no `retry-after`, ms. */
|
|
46
|
+
export const OVERLOADED_COOLDOWN_MS = 30_000;
|
|
37
47
|
let defaultCache = null;
|
|
38
48
|
let defaultBudget = null;
|
|
49
|
+
let defaultSpend = null;
|
|
50
|
+
let defaultSpendCap = null;
|
|
39
51
|
function processCache() {
|
|
40
52
|
defaultCache ??= createDecisionCache();
|
|
41
53
|
return defaultCache;
|
|
@@ -44,6 +56,28 @@ function processBudget() {
|
|
|
44
56
|
defaultBudget ??= createFileTokenBucket();
|
|
45
57
|
return defaultBudget;
|
|
46
58
|
}
|
|
59
|
+
function processSpend() {
|
|
60
|
+
defaultSpend ??= createFileSpendLedger();
|
|
61
|
+
return defaultSpend;
|
|
62
|
+
}
|
|
63
|
+
/** The configured monthly cap, read once per process; the $1 default when unset or unreadable. */
|
|
64
|
+
function processSpendCap(projectRoot) {
|
|
65
|
+
defaultSpendCap ??= (async () => {
|
|
66
|
+
try {
|
|
67
|
+
const { getConfigValue } = await import('../config/registry.js');
|
|
68
|
+
const value = await getConfigValue(MONTHLY_SPEND_CAP_KEY, {
|
|
69
|
+
projectRoot: projectRoot ?? getProjectRoot(),
|
|
70
|
+
});
|
|
71
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0
|
|
72
|
+
? value
|
|
73
|
+
: DEFAULT_MONTHLY_SPEND_CAP_MICROS;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return DEFAULT_MONTHLY_SPEND_CAP_MICROS;
|
|
77
|
+
}
|
|
78
|
+
})();
|
|
79
|
+
return defaultSpendCap;
|
|
80
|
+
}
|
|
47
81
|
/**
|
|
48
82
|
* Reset the process-default cache and budget. Tests only.
|
|
49
83
|
*
|
|
@@ -52,6 +86,8 @@ function processBudget() {
|
|
|
52
86
|
export function _resetDecideDefaultsForTest() {
|
|
53
87
|
defaultCache = null;
|
|
54
88
|
defaultBudget = null;
|
|
89
|
+
defaultSpend = null;
|
|
90
|
+
defaultSpendCap = null;
|
|
55
91
|
}
|
|
56
92
|
function redactJson(value) {
|
|
57
93
|
if (typeof value === 'string')
|
|
@@ -98,7 +134,8 @@ function resolveProvider(opts, connection) {
|
|
|
98
134
|
return null;
|
|
99
135
|
if (!decisionProviderConfigSchema.safeParse({ baseUrl: connection.baseUrl }).success)
|
|
100
136
|
return null;
|
|
101
|
-
|
|
137
|
+
const capabilities = cachedCapabilities(connection);
|
|
138
|
+
return createJevProvider(connection, capabilities ? { capabilities } : {});
|
|
102
139
|
}
|
|
103
140
|
function resolveAudit(opts) {
|
|
104
141
|
if (opts.audit !== undefined)
|
|
@@ -120,17 +157,54 @@ function reasonForError(err) {
|
|
|
120
157
|
return err.kind;
|
|
121
158
|
}
|
|
122
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* The gates for a call. The configured cap is read BEFORE the deadline
|
|
162
|
+
* starts (a cold config read is not provider wait), once per process.
|
|
163
|
+
*/
|
|
164
|
+
async function resolveGates(opts) {
|
|
165
|
+
const budget = opts.budget === undefined ? processBudget() : opts.budget;
|
|
166
|
+
const spend = opts.spend === undefined ? processSpend() : opts.spend;
|
|
167
|
+
const capMicros = opts.spendCapMicros ??
|
|
168
|
+
(spend ? await processSpendCap(opts.projectRoot) : Number.POSITIVE_INFINITY);
|
|
169
|
+
return { budget, spend, capMicros };
|
|
170
|
+
}
|
|
123
171
|
/** True when `outcome` is contract-valid and answers every question with the right type. */
|
|
124
172
|
function coversRequest(outcome, req) {
|
|
125
173
|
if (!decisionOutcomeSchema.safeParse(outcome).success)
|
|
126
174
|
return false;
|
|
127
175
|
return Object.entries(req.questions).every(([name, q]) => outcome.answers[name]?.type === q.type);
|
|
128
176
|
}
|
|
129
|
-
/**
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
177
|
+
/**
|
|
178
|
+
* Cost reserved per question before a call, micro-dollars. Only a
|
|
179
|
+
* reservation: the provider-reported cost replaces it on commit, so this is
|
|
180
|
+
* never what is recorded (layahost bills 5–15 µ$ per decision, 2026-09).
|
|
181
|
+
*/
|
|
182
|
+
export const DECISION_COST_ESTIMATE_MICROS_PER_QUESTION = 15;
|
|
183
|
+
/** Estimated cost of asking `reqs`. */
|
|
184
|
+
function estimateMicros(reqs) {
|
|
185
|
+
return reqs.reduce((n, r) => n + Object.keys(r.questions).length * DECISION_COST_ESTIMATE_MICROS_PER_QUESTION, 0);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Spend cap (check and reserve, atomically) then request budget. A
|
|
189
|
+
* reservation is released again when the budget refuses.
|
|
190
|
+
*/
|
|
191
|
+
async function passGates(gates, estimate) {
|
|
192
|
+
let reservation;
|
|
193
|
+
if (gates.spend) {
|
|
194
|
+
const r = await gates.spend.reserve(gates.capMicros, estimate);
|
|
195
|
+
if (r.verdict === 'over_budget')
|
|
196
|
+
return { ok: false, reason: 'budget' };
|
|
197
|
+
if (r.verdict === 'key_limited')
|
|
198
|
+
return { ok: false, reason: 'key_limit_exceeded' };
|
|
199
|
+
if (r.verdict === 'unavailable')
|
|
200
|
+
return { ok: false, reason: 'budget_unavailable' };
|
|
201
|
+
reservation = r.id;
|
|
202
|
+
}
|
|
203
|
+
if (gates.budget) {
|
|
204
|
+
const grant = await gates.budget.tryAcquire();
|
|
133
205
|
if (!grant.granted) {
|
|
206
|
+
if (reservation !== undefined)
|
|
207
|
+
await gates.spend?.release(reservation);
|
|
134
208
|
return {
|
|
135
209
|
ok: false,
|
|
136
210
|
reason: grant.reason === 'exhausted'
|
|
@@ -141,19 +215,82 @@ async function attempt(provider, budget, req, signal) {
|
|
|
141
215
|
};
|
|
142
216
|
}
|
|
143
217
|
}
|
|
144
|
-
|
|
218
|
+
return { ok: true, ...(reservation !== undefined ? { reservation } : {}) };
|
|
219
|
+
}
|
|
220
|
+
/** Commit the reported cost against a reservation (or record it when there is none). */
|
|
221
|
+
async function settle(gates, reservation, micros) {
|
|
222
|
+
if (!gates.spend)
|
|
223
|
+
return;
|
|
224
|
+
if (reservation !== undefined)
|
|
225
|
+
await gates.spend.commit(reservation, micros);
|
|
226
|
+
else
|
|
227
|
+
await gates.spend.record(micros);
|
|
228
|
+
}
|
|
229
|
+
/** Side effects of a provider error on the shared gates; the fallback reason. */
|
|
230
|
+
async function onProviderError(err, gates) {
|
|
231
|
+
if (err.kind === 'rate_limited' && gates.budget)
|
|
232
|
+
await gates.budget.penalize(err.retryAfterMs);
|
|
233
|
+
if (err.kind === 'overloaded' && gates.budget) {
|
|
234
|
+
await gates.budget.penalize(err.retryAfterMs ?? OVERLOADED_COOLDOWN_MS);
|
|
235
|
+
}
|
|
236
|
+
if (err.kind === 'key_limit_exceeded' && gates.spend)
|
|
237
|
+
await gates.spend.markKeyLimited();
|
|
238
|
+
return reasonForError(err);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Whether a failed call may still have been billed: it was aborted (deadline
|
|
242
|
+
* or caller) AFTER the request was handed to the provider. The server may
|
|
243
|
+
* have answered and billed it, so its reservation is committed at the
|
|
244
|
+
* estimate rather than released. Every other failure is an error response or
|
|
245
|
+
* a transport failure, which the provider does not bill.
|
|
246
|
+
*/
|
|
247
|
+
function abortedAfterSend(err, signal) {
|
|
248
|
+
return signal.aborted || (err instanceof DecisionProviderError && err.kind === 'aborted');
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Settle a reservation after a failed call: commit the estimate when the
|
|
252
|
+
* call was aborted after send ({@link abortedAfterSend}), else release it.
|
|
253
|
+
*/
|
|
254
|
+
async function settleFailure(gates, reservation, estimate, err, signal) {
|
|
255
|
+
if (reservation === undefined || !gates.spend)
|
|
256
|
+
return;
|
|
257
|
+
if (abortedAfterSend(err, signal))
|
|
258
|
+
await gates.spend.commit(reservation, estimate);
|
|
259
|
+
else
|
|
260
|
+
await gates.spend.release(reservation);
|
|
261
|
+
}
|
|
262
|
+
/** Micro-dollars an outcome reported, preferring the exact integer. */
|
|
263
|
+
function reportedMicros(outcome) {
|
|
264
|
+
if (outcome.costMicros !== undefined)
|
|
265
|
+
return outcome.costMicros;
|
|
266
|
+
return outcome.costUsd !== undefined ? Math.round(outcome.costUsd * 1e6) : 0;
|
|
267
|
+
}
|
|
268
|
+
/** Spend gate + budget + provider call; never rejects. */
|
|
269
|
+
async function attempt(provider, gates, req, signal) {
|
|
270
|
+
const estimate = estimateMicros([req]);
|
|
271
|
+
const gate = await passGates(gates, estimate);
|
|
272
|
+
if (!gate.ok)
|
|
273
|
+
return { ok: false, reason: gate.reason };
|
|
274
|
+
if (signal.aborted) {
|
|
275
|
+
// Pre-send: nothing left the machine, so nothing can be billed.
|
|
276
|
+
if (gate.reservation !== undefined)
|
|
277
|
+
await gates.spend?.release(gate.reservation);
|
|
145
278
|
return { ok: false, reason: 'timeout' };
|
|
279
|
+
}
|
|
146
280
|
try {
|
|
147
281
|
const outcome = await provider.decide(req, signal);
|
|
282
|
+
// A 2xx is billed even when its body is unusable.
|
|
283
|
+
await settle(gates, gate.reservation, reportedMicros(outcome));
|
|
148
284
|
if (!coversRequest(outcome, req))
|
|
149
285
|
return { ok: false, reason: 'invalid_response' };
|
|
150
286
|
return { ok: true, outcome };
|
|
151
287
|
}
|
|
152
288
|
catch (err) {
|
|
289
|
+
// Failed requests are not billed (provider docs), but one aborted after
|
|
290
|
+
// send may have been answered and billed: charge its estimate.
|
|
291
|
+
await settleFailure(gates, gate.reservation, estimate, err, signal);
|
|
153
292
|
if (err instanceof DecisionProviderError) {
|
|
154
|
-
|
|
155
|
-
await budget.penalize(err.retryAfterMs);
|
|
156
|
-
return { ok: false, reason: reasonForError(err) };
|
|
293
|
+
return { ok: false, reason: await onProviderError(err, gates) };
|
|
157
294
|
}
|
|
158
295
|
return { ok: false, reason: signal.aborted ? 'timeout' : 'provider_error' };
|
|
159
296
|
}
|
|
@@ -163,7 +300,7 @@ async function attempt(provider, budget, req, signal) {
|
|
|
163
300
|
* deadline or the caller's signal fires, even if the provider ignores its
|
|
164
301
|
* abort signal.
|
|
165
302
|
*/
|
|
166
|
-
function withDeadline(run, timeoutMs, callerSignal) {
|
|
303
|
+
function withDeadline(run, timeoutMs, callerSignal, timedOut, crashed) {
|
|
167
304
|
const controller = new AbortController();
|
|
168
305
|
return new Promise((resolve) => {
|
|
169
306
|
let settled = false;
|
|
@@ -177,7 +314,7 @@ function withDeadline(run, timeoutMs, callerSignal) {
|
|
|
177
314
|
};
|
|
178
315
|
const onTimeout = () => {
|
|
179
316
|
controller.abort();
|
|
180
|
-
finish(
|
|
317
|
+
finish(timedOut);
|
|
181
318
|
};
|
|
182
319
|
const onCallerAbort = () => onTimeout();
|
|
183
320
|
const timer = setTimeout(onTimeout, Math.max(0, timeoutMs));
|
|
@@ -186,7 +323,7 @@ function withDeadline(run, timeoutMs, callerSignal) {
|
|
|
186
323
|
return;
|
|
187
324
|
}
|
|
188
325
|
callerSignal?.addEventListener('abort', onCallerAbort, { once: true });
|
|
189
|
-
run(controller.signal).then(finish, () => finish(
|
|
326
|
+
run(controller.signal).then(finish, () => finish(crashed));
|
|
190
327
|
});
|
|
191
328
|
}
|
|
192
329
|
/**
|
|
@@ -213,10 +350,29 @@ function withDeadline(run, timeoutMs, callerSignal) {
|
|
|
213
350
|
* ```
|
|
214
351
|
*/
|
|
215
352
|
export async function decide(siteId, req, fallback, opts = {}) {
|
|
216
|
-
const started = performance.now();
|
|
217
|
-
const elapsed = () => Math.max(0, performance.now() - started);
|
|
218
353
|
const audit = resolveAudit(opts);
|
|
219
354
|
const connection = resolveConnection(opts);
|
|
355
|
+
const item = prepare(siteId, req, fallback, opts, audit, connection);
|
|
356
|
+
const provider = resolveProvider(opts, connection);
|
|
357
|
+
if (!provider)
|
|
358
|
+
return item.useFallback('unconfigured');
|
|
359
|
+
if (!item.valid)
|
|
360
|
+
return item.useFallback('invalid_request');
|
|
361
|
+
const cache = opts.cache === undefined ? processCache() : opts.cache;
|
|
362
|
+
const hit = cache?.get(item.key);
|
|
363
|
+
if (hit)
|
|
364
|
+
return item.finish({ ...hit, source: 'cache', latencyMs: item.elapsed() });
|
|
365
|
+
const gates = await resolveGates(opts);
|
|
366
|
+
const result = await withDeadline((signal) => attempt(provider, gates, item.sent, signal), opts.timeoutMs ?? DEFAULT_DECISION_TIMEOUT_MS, opts.signal, { ok: false, reason: 'timeout' }, { ok: false, reason: 'provider_error' });
|
|
367
|
+
if (!result.ok)
|
|
368
|
+
return item.useFallback(result.reason);
|
|
369
|
+
cache?.set(item.key, result.outcome);
|
|
370
|
+
return item.finish({ ...result.outcome, source: 'provider', latencyMs: item.elapsed() });
|
|
371
|
+
}
|
|
372
|
+
/** Redact, validate, key and wire the audit for one request. */
|
|
373
|
+
function prepare(siteId, req, fallback, opts, audit, connection) {
|
|
374
|
+
const started = performance.now();
|
|
375
|
+
const elapsed = () => Math.max(0, performance.now() - started);
|
|
220
376
|
const defaultModel = req.model === undefined ? connection?.model : undefined;
|
|
221
377
|
const sent = {
|
|
222
378
|
...req,
|
|
@@ -237,26 +393,165 @@ export async function decide(siteId, req, fallback, opts = {}) {
|
|
|
237
393
|
...(reason ? { fallbackReason: reason } : {}),
|
|
238
394
|
latencyMs: outcome.latencyMs,
|
|
239
395
|
...(outcome.costUsd !== undefined ? { costUsd: outcome.costUsd } : {}),
|
|
396
|
+
...(outcome.costMicros !== undefined ? { costMicros: outcome.costMicros } : {}),
|
|
397
|
+
...(outcome.balanceMicros !== undefined ? { balanceMicros: outcome.balanceMicros } : {}),
|
|
398
|
+
...(outcome.checkpoint !== undefined ? { checkpoint: outcome.checkpoint } : {}),
|
|
240
399
|
...(sent.model !== undefined ? { model: sent.model } : {}),
|
|
241
400
|
});
|
|
242
401
|
return outcome;
|
|
243
402
|
};
|
|
244
|
-
|
|
403
|
+
return {
|
|
404
|
+
sent,
|
|
405
|
+
valid: decisionRequestSchema.safeParse(sent).success,
|
|
406
|
+
key: decisionCacheKey(opts.adapterVersion ?? JEV_ADAPTER_VERSION, sent),
|
|
407
|
+
elapsed,
|
|
408
|
+
finish,
|
|
409
|
+
useFallback: (reason) => finish({ answers: fallback(req), source: 'fallback', latencyMs: elapsed() }, reason),
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Ask several typed decisions at call site `siteId` under ONE deadline.
|
|
414
|
+
*
|
|
415
|
+
* When the provider reports a batch capability (`capabilities().batch`) and
|
|
416
|
+
* the entries fit its limits, the uncached ones go out as a single batch call
|
|
417
|
+
* (one spend check, one budget token). Otherwise it degrades to sequential
|
|
418
|
+
* {@link decide} calls that share the remaining deadline; entries the
|
|
419
|
+
* deadline cannot reach fall back with reason `timeout`. Never rejects; each
|
|
420
|
+
* entry is audited like a single decision.
|
|
421
|
+
*
|
|
422
|
+
* @param siteId - Stable call-site identifier.
|
|
423
|
+
* @param entries - Requests with their heuristics.
|
|
424
|
+
* @param opts - Same wiring as {@link decide}; `timeoutMs` covers the whole batch.
|
|
425
|
+
* @returns One outcome per entry, in order.
|
|
426
|
+
*/
|
|
427
|
+
export async function decideBatch(siteId, entries, opts = {}) {
|
|
428
|
+
if (entries.length === 0)
|
|
429
|
+
return [];
|
|
430
|
+
const started = performance.now();
|
|
431
|
+
const deadlineMs = opts.timeoutMs ?? DEFAULT_DECISION_TIMEOUT_MS;
|
|
432
|
+
const remaining = () => Math.max(0, deadlineMs - (performance.now() - started));
|
|
433
|
+
const connection = resolveConnection(opts);
|
|
245
434
|
const provider = resolveProvider(opts, connection);
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
435
|
+
const limits = provider?.capabilities?.().batch;
|
|
436
|
+
const questions = entries.reduce((n, e) => n + Object.keys(e.req.questions).length, 0);
|
|
437
|
+
const batchable = provider !== null &&
|
|
438
|
+
provider.decideBatch !== undefined &&
|
|
439
|
+
limits !== undefined &&
|
|
440
|
+
entries.length > 1 &&
|
|
441
|
+
entries.length <= limits.maxRequests &&
|
|
442
|
+
questions <= limits.maxQuestions;
|
|
443
|
+
if (!batchable || !provider?.decideBatch) {
|
|
444
|
+
const out = [];
|
|
445
|
+
for (const entry of entries) {
|
|
446
|
+
const timeoutMs = remaining();
|
|
447
|
+
// model-site-allowed: plumbing — the caller's siteId is checked at its decideBatch call (gate 35)
|
|
448
|
+
const outcome = await decide(siteId, entry.req, entry.fallback, { ...opts, timeoutMs });
|
|
449
|
+
out.push(outcome);
|
|
450
|
+
}
|
|
451
|
+
return out;
|
|
452
|
+
}
|
|
453
|
+
const audit = resolveAudit(opts);
|
|
250
454
|
const cache = opts.cache === undefined ? processCache() : opts.cache;
|
|
251
|
-
const
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
455
|
+
const items = entries.map((e) => prepare(siteId, e.req, e.fallback, opts, audit, connection));
|
|
456
|
+
const out = items.map((item) => {
|
|
457
|
+
if (!item.valid)
|
|
458
|
+
return item.useFallback('invalid_request');
|
|
459
|
+
const hit = cache?.get(item.key);
|
|
460
|
+
return hit ? item.finish({ ...hit, source: 'cache', latencyMs: item.elapsed() }) : undefined;
|
|
461
|
+
});
|
|
462
|
+
const pending = items.flatMap((item, i) => (out[i] === undefined ? [{ item, i }] : []));
|
|
463
|
+
if (pending.length > 0) {
|
|
464
|
+
const gates = await resolveGates(opts);
|
|
465
|
+
const batch = provider.decideBatch.bind(provider);
|
|
466
|
+
const result = await withDeadline(async (signal) => {
|
|
467
|
+
const sent = pending.map((p) => p.item.sent);
|
|
468
|
+
const estimate = estimateMicros(sent);
|
|
469
|
+
const gate = await passGates(gates, estimate);
|
|
470
|
+
if (!gate.ok)
|
|
471
|
+
return { ok: false, reason: gate.reason };
|
|
472
|
+
if (signal.aborted) {
|
|
473
|
+
if (gate.reservation !== undefined)
|
|
474
|
+
await gates.spend?.release(gate.reservation);
|
|
475
|
+
return { ok: false, reason: 'timeout' };
|
|
476
|
+
}
|
|
477
|
+
try {
|
|
478
|
+
const answered = await batch(sent, signal);
|
|
479
|
+
const billed = answered.reduce((n, a) => n + (a.ok ? reportedMicros(a.outcome) : 0), 0);
|
|
480
|
+
await settle(gates, gate.reservation, billed);
|
|
481
|
+
return { ok: true, items: answered };
|
|
482
|
+
}
|
|
483
|
+
catch (err) {
|
|
484
|
+
await settleFailure(gates, gate.reservation, estimate, err, signal);
|
|
485
|
+
if (err instanceof DecisionProviderError) {
|
|
486
|
+
return { ok: false, reason: await onProviderError(err, gates) };
|
|
487
|
+
}
|
|
488
|
+
return { ok: false, reason: signal.aborted ? 'timeout' : 'provider_error' };
|
|
489
|
+
}
|
|
490
|
+
}, remaining(), opts.signal, { ok: false, reason: 'timeout' }, { ok: false, reason: 'provider_error' });
|
|
491
|
+
if (result.ok)
|
|
492
|
+
await applyBatchItemErrors(result.items, gates);
|
|
493
|
+
for (const [k, { item, i }] of pending.entries()) {
|
|
494
|
+
if (!result.ok) {
|
|
495
|
+
out[i] = item.useFallback(result.reason);
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
const answered = result.items[k];
|
|
499
|
+
if (answered?.ok && coversRequest(answered.outcome, item.sent)) {
|
|
500
|
+
cache?.set(item.key, answered.outcome);
|
|
501
|
+
out[i] = item.finish({
|
|
502
|
+
...answered.outcome,
|
|
503
|
+
source: 'provider',
|
|
504
|
+
latencyMs: item.elapsed(),
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
out[i] = item.useFallback(answered && !answered.ok ? batchItemReason(answered.errorKind) : 'invalid_response');
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return items.map((item, i) => out[i] ?? item.useFallback('provider_error'));
|
|
513
|
+
}
|
|
514
|
+
/** Fallback reason for a failed batch item. */
|
|
515
|
+
function batchItemReason(kind) {
|
|
516
|
+
switch (kind) {
|
|
517
|
+
case 'unauthorized':
|
|
518
|
+
case 'insufficient_credits':
|
|
519
|
+
case 'key_limit_exceeded':
|
|
520
|
+
case 'rate_limited':
|
|
521
|
+
case 'overloaded':
|
|
522
|
+
case 'server_error':
|
|
523
|
+
case 'invalid_request':
|
|
524
|
+
case 'invalid_response':
|
|
525
|
+
return kind;
|
|
526
|
+
default:
|
|
527
|
+
return 'provider_error';
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
/** Batch item error kinds with a side effect on the shared gates. */
|
|
531
|
+
const GATE_ERROR_KINDS = new Set(['key_limit_exceeded', 'rate_limited', 'overloaded']);
|
|
532
|
+
/** The gate-affecting provider error kind named by a batch item, if any. */
|
|
533
|
+
function gateErrorKind(kind) {
|
|
534
|
+
for (const k of GATE_ERROR_KINDS)
|
|
535
|
+
if (k === kind)
|
|
536
|
+
return k;
|
|
537
|
+
return null;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Give failed batch items the same gate side effects as a failed single
|
|
541
|
+
* call (key-limit stop, rate-limit and overload back-off), once per kind.
|
|
542
|
+
*/
|
|
543
|
+
async function applyBatchItemErrors(items, gates) {
|
|
544
|
+
const seen = new Set();
|
|
545
|
+
for (const item of items) {
|
|
546
|
+
if (item.ok)
|
|
547
|
+
continue;
|
|
548
|
+
const kind = gateErrorKind(item.errorKind);
|
|
549
|
+
if (kind === null || seen.has(kind))
|
|
550
|
+
continue;
|
|
551
|
+
seen.add(kind);
|
|
552
|
+
await onProviderError(new DecisionProviderError(kind, `batch item failed: ${kind}`, {
|
|
553
|
+
...(item.status !== undefined ? { status: item.status } : {}),
|
|
554
|
+
}), gates);
|
|
555
|
+
}
|
|
261
556
|
}
|
|
262
557
|
//# sourceMappingURL=client.js.map
|