@namzu/sdk 34.2.0 → 36.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +171 -0
- package/README.md +2 -6
- package/dist/agents/PipelineAgent.d.ts.map +1 -1
- package/dist/agents/PipelineAgent.js +118 -100
- package/dist/agents/PipelineAgent.js.map +1 -1
- package/dist/agents/ReactiveAgent.d.ts.map +1 -1
- package/dist/agents/ReactiveAgent.js +2 -0
- package/dist/agents/ReactiveAgent.js.map +1 -1
- package/dist/agents/RouterAgent.d.ts.map +1 -1
- package/dist/agents/RouterAgent.js +105 -61
- package/dist/agents/RouterAgent.js.map +1 -1
- package/dist/agents/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/SupervisorAgent.js +8 -4
- package/dist/agents/SupervisorAgent.js.map +1 -1
- package/dist/agents/budget.d.ts +6 -0
- package/dist/agents/budget.d.ts.map +1 -0
- package/dist/agents/budget.js +33 -0
- package/dist/agents/budget.js.map +1 -0
- package/dist/agents/explore.d.ts +20 -0
- package/dist/agents/explore.d.ts.map +1 -0
- package/dist/agents/explore.js +26 -0
- package/dist/agents/explore.js.map +1 -0
- package/dist/agents/file-definitions.d.ts +80 -0
- package/dist/agents/file-definitions.d.ts.map +1 -0
- package/dist/agents/file-definitions.js +179 -0
- package/dist/agents/file-definitions.js.map +1 -0
- package/dist/agents/index.d.ts +3 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +2 -0
- package/dist/agents/index.js.map +1 -1
- package/dist/bridge/a2a/mapper.d.ts.map +1 -1
- package/dist/bridge/a2a/mapper.js +6 -0
- package/dist/bridge/a2a/mapper.js.map +1 -1
- package/dist/bridge/sse/mapper.d.ts.map +1 -1
- package/dist/bridge/sse/mapper.js +22 -0
- package/dist/bridge/sse/mapper.js.map +1 -1
- package/dist/bus/lock.d.ts.map +1 -1
- package/dist/bus/lock.js +1 -10
- package/dist/bus/lock.js.map +1 -1
- package/dist/compaction/consolidation.d.ts +31 -0
- package/dist/compaction/consolidation.d.ts.map +1 -0
- package/dist/compaction/consolidation.js +63 -0
- package/dist/compaction/consolidation.js.map +1 -0
- package/dist/compaction/factory.d.ts.map +1 -1
- package/dist/compaction/factory.js +3 -0
- package/dist/compaction/factory.js.map +1 -1
- package/dist/compaction/index.d.ts +6 -1
- package/dist/compaction/index.d.ts.map +1 -1
- package/dist/compaction/index.js +6 -0
- package/dist/compaction/index.js.map +1 -1
- package/dist/compaction/manager.d.ts +7 -0
- package/dist/compaction/manager.d.ts.map +1 -1
- package/dist/compaction/manager.js +31 -0
- package/dist/compaction/manager.js.map +1 -1
- package/dist/compaction/plan.d.ts +23 -0
- package/dist/compaction/plan.d.ts.map +1 -1
- package/dist/compaction/plan.js +41 -24
- package/dist/compaction/plan.js.map +1 -1
- package/dist/compaction/salience/bm25.d.ts +26 -0
- package/dist/compaction/salience/bm25.d.ts.map +1 -0
- package/dist/compaction/salience/bm25.js +56 -0
- package/dist/compaction/salience/bm25.js.map +1 -0
- package/dist/compaction/salience/goal.d.ts +21 -0
- package/dist/compaction/salience/goal.d.ts.map +1 -0
- package/dist/compaction/salience/goal.js +32 -0
- package/dist/compaction/salience/goal.js.map +1 -0
- package/dist/compaction/salience/index.d.ts +4 -0
- package/dist/compaction/salience/index.d.ts.map +1 -0
- package/dist/compaction/salience/index.js +4 -0
- package/dist/compaction/salience/index.js.map +1 -0
- package/dist/compaction/salience/minhash.d.ts +17 -0
- package/dist/compaction/salience/minhash.d.ts.map +1 -0
- package/dist/compaction/salience/minhash.js +54 -0
- package/dist/compaction/salience/minhash.js.map +1 -0
- package/dist/compaction/salience/score.d.ts +64 -0
- package/dist/compaction/salience/score.d.ts.map +1 -0
- package/dist/compaction/salience/score.js +256 -0
- package/dist/compaction/salience/score.js.map +1 -0
- package/dist/compaction/salience/tokenize.d.ts +20 -0
- package/dist/compaction/salience/tokenize.d.ts.map +1 -0
- package/dist/compaction/salience/tokenize.js +92 -0
- package/dist/compaction/salience/tokenize.js.map +1 -0
- package/dist/compaction/salience/working-set.d.ts +59 -0
- package/dist/compaction/salience/working-set.d.ts.map +1 -0
- package/dist/compaction/salience/working-set.js +137 -0
- package/dist/compaction/salience/working-set.js.map +1 -0
- package/dist/compaction/serializer.d.ts +2 -0
- package/dist/compaction/serializer.d.ts.map +1 -1
- package/dist/compaction/serializer.js +15 -0
- package/dist/compaction/serializer.js.map +1 -1
- package/dist/compaction/token-estimate.d.ts +13 -0
- package/dist/compaction/token-estimate.d.ts.map +1 -0
- package/dist/compaction/token-estimate.js +58 -0
- package/dist/compaction/token-estimate.js.map +1 -0
- package/dist/compaction/tool-result-editing.d.ts +10 -2
- package/dist/compaction/tool-result-editing.d.ts.map +1 -1
- package/dist/compaction/tool-result-editing.js +22 -24
- package/dist/compaction/tool-result-editing.js.map +1 -1
- package/dist/compaction/types.d.ts +22 -1
- package/dist/compaction/types.d.ts.map +1 -1
- package/dist/compaction/wire.d.ts +3 -1
- package/dist/compaction/wire.d.ts.map +1 -1
- package/dist/compaction/wire.js +2 -0
- package/dist/compaction/wire.js.map +1 -1
- package/dist/config/runtime.d.ts +58 -28
- package/dist/config/runtime.d.ts.map +1 -1
- package/dist/config/runtime.js +13 -1
- package/dist/config/runtime.js.map +1 -1
- package/dist/connector/builtins/http.d.ts.map +1 -1
- package/dist/connector/builtins/http.js +2 -8
- package/dist/connector/builtins/http.js.map +1 -1
- package/dist/connector/builtins/webhook.js +2 -2
- package/dist/connector/builtins/webhook.js.map +1 -1
- package/dist/connector/mcp/adapter.d.ts +2 -0
- package/dist/connector/mcp/adapter.d.ts.map +1 -1
- package/dist/connector/mcp/adapter.js +30 -0
- package/dist/connector/mcp/adapter.js.map +1 -1
- package/dist/connector/tools/adapter.js +2 -2
- package/dist/connector/tools/adapter.js.map +1 -1
- package/dist/connector/tools/definitions.js +2 -2
- package/dist/connector/tools/definitions.js.map +1 -1
- package/dist/constants/compaction/index.d.ts +1 -0
- package/dist/constants/compaction/index.d.ts.map +1 -1
- package/dist/constants/compaction/index.js +1 -0
- package/dist/constants/compaction/index.js.map +1 -1
- package/dist/constants/telemetry/index.d.ts +0 -1
- package/dist/constants/telemetry/index.d.ts.map +1 -1
- package/dist/constants/telemetry/index.js +0 -1
- package/dist/constants/telemetry/index.js.map +1 -1
- package/dist/contracts/a2a.d.ts +5 -5
- package/dist/contracts/api.d.ts +4 -2
- package/dist/contracts/api.d.ts.map +1 -1
- package/dist/contracts/schemas.d.ts +1 -15
- package/dist/contracts/schemas.d.ts.map +1 -1
- package/dist/contracts/schemas.js +5 -20
- package/dist/contracts/schemas.js.map +1 -1
- package/dist/invariants/index.d.ts +1 -1
- package/dist/manager/agent/lifecycle.d.ts +2 -18
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +373 -340
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/manager/run/persistence.d.ts +3 -0
- package/dist/manager/run/persistence.d.ts.map +1 -1
- package/dist/manager/run/persistence.js +35 -5
- package/dist/manager/run/persistence.js.map +1 -1
- package/dist/plugin/index.d.ts +2 -0
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +1 -0
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/shell-hook.d.ts +128 -0
- package/dist/plugin/shell-hook.d.ts.map +1 -0
- package/dist/plugin/shell-hook.js +299 -0
- package/dist/plugin/shell-hook.js.map +1 -0
- package/dist/prompt/coding-agent-doctrine.d.ts +63 -0
- package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -0
- package/dist/prompt/coding-agent-doctrine.js +100 -0
- package/dist/prompt/coding-agent-doctrine.js.map +1 -0
- package/dist/prompt/index.d.ts +2 -0
- package/dist/prompt/index.d.ts.map +1 -1
- package/dist/prompt/index.js +1 -0
- package/dist/prompt/index.js.map +1 -1
- package/dist/provider/errors.d.ts +1 -1
- package/dist/provider/errors.js +1 -1
- package/dist/provider/fallback.d.ts +2 -0
- package/dist/provider/fallback.d.ts.map +1 -1
- package/dist/provider/fallback.js +3 -1
- package/dist/provider/fallback.js.map +1 -1
- package/dist/provider/retry.d.ts +2 -0
- package/dist/provider/retry.d.ts.map +1 -1
- package/dist/provider/retry.js +3 -1
- package/dist/provider/retry.js.map +1 -1
- package/dist/provider/token-budget.d.ts +13 -0
- package/dist/provider/token-budget.d.ts.map +1 -0
- package/dist/provider/token-budget.js +150 -0
- package/dist/provider/token-budget.js.map +1 -0
- package/dist/public-runtime.d.ts +14 -14
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +15 -10
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-tools.d.ts +2 -0
- package/dist/public-tools.d.ts.map +1 -1
- package/dist/public-tools.js +2 -0
- package/dist/public-tools.js.map +1 -1
- package/dist/public-types.d.ts +12 -2
- package/dist/public-types.d.ts.map +1 -1
- package/dist/registry/command/kernel-commands.d.ts.map +1 -1
- package/dist/registry/command/kernel-commands.js +18 -6
- package/dist/registry/command/kernel-commands.js.map +1 -1
- package/dist/registry/tool/execute.d.ts.map +1 -1
- package/dist/registry/tool/execute.js +4 -1
- package/dist/registry/tool/execute.js.map +1 -1
- package/dist/run/LimitChecker.js +1 -1
- package/dist/run/LimitChecker.js.map +1 -1
- package/dist/run/drain.js +1 -1
- package/dist/run/reporter.d.ts.map +1 -1
- package/dist/run/reporter.js +15 -0
- package/dist/run/reporter.js.map +1 -1
- package/dist/run/token-budget.d.ts +108 -0
- package/dist/run/token-budget.d.ts.map +1 -0
- package/dist/run/token-budget.js +591 -0
- package/dist/run/token-budget.js.map +1 -0
- package/dist/runtime/jobs/registry.d.ts +29 -0
- package/dist/runtime/jobs/registry.d.ts.map +1 -1
- package/dist/runtime/jobs/registry.js +98 -18
- package/dist/runtime/jobs/registry.js.map +1 -1
- package/dist/runtime/query/checkpoint.d.ts.map +1 -1
- package/dist/runtime/query/checkpoint.js +10 -3
- package/dist/runtime/query/checkpoint.js.map +1 -1
- package/dist/runtime/query/context.d.ts +2 -26
- package/dist/runtime/query/context.d.ts.map +1 -1
- package/dist/runtime/query/context.js +1 -29
- package/dist/runtime/query/context.js.map +1 -1
- package/dist/runtime/query/executor.d.ts +13 -9
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +144 -55
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/guard.d.ts.map +1 -1
- package/dist/runtime/query/guard.js +8 -0
- package/dist/runtime/query/guard.js.map +1 -1
- package/dist/runtime/query/index.d.ts +42 -4
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +153 -52
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +3 -3
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +47 -11
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +98 -30
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts +4 -0
- package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/context.js.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.js +29 -7
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/iteration/phases/working-memory.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/working-memory.js +25 -15
- package/dist/runtime/query/iteration/phases/working-memory.js.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.js +10 -5
- package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
- package/dist/runtime/query/plugin-hooks.d.ts +1 -1
- package/dist/runtime/query/plugin-hooks.d.ts.map +1 -1
- package/dist/runtime/query/plugin-hooks.js +20 -1
- package/dist/runtime/query/plugin-hooks.js.map +1 -1
- package/dist/runtime/query/prompt.d.ts.map +1 -1
- package/dist/runtime/query/prompt.js +14 -4
- package/dist/runtime/query/prompt.js.map +1 -1
- package/dist/runtime/query/repeat-call.d.ts +20 -2
- package/dist/runtime/query/repeat-call.d.ts.map +1 -1
- package/dist/runtime/query/repeat-call.js +35 -7
- package/dist/runtime/query/repeat-call.js.map +1 -1
- package/dist/runtime/query/result.d.ts.map +1 -1
- package/dist/runtime/query/result.js +9 -0
- package/dist/runtime/query/result.js.map +1 -1
- package/dist/runtime/query/review-policy.d.ts +142 -0
- package/dist/runtime/query/review-policy.d.ts.map +1 -0
- package/dist/runtime/query/review-policy.js +144 -0
- package/dist/runtime/query/review-policy.js.map +1 -0
- package/dist/runtime/query/run-state.d.ts.map +1 -1
- package/dist/runtime/query/run-state.js +4 -0
- package/dist/runtime/query/run-state.js.map +1 -1
- package/dist/runtime/query/steering.d.ts +4 -0
- package/dist/runtime/query/steering.d.ts.map +1 -1
- package/dist/runtime/query/steering.js +29 -0
- package/dist/runtime/query/steering.js.map +1 -1
- package/dist/runtime/query/token-budget.d.ts +7 -0
- package/dist/runtime/query/token-budget.d.ts.map +1 -0
- package/dist/runtime/query/token-budget.js +101 -0
- package/dist/runtime/query/token-budget.js.map +1 -0
- package/dist/runtime/query/tool-output-budget.d.ts +2 -0
- package/dist/runtime/query/tool-output-budget.d.ts.map +1 -1
- package/dist/runtime/query/tool-output-budget.js +29 -11
- package/dist/runtime/query/tool-output-budget.js.map +1 -1
- package/dist/runtime/query/tool-pause.d.ts.map +1 -1
- package/dist/runtime/query/tool-pause.js +4 -2
- package/dist/runtime/query/tool-pause.js.map +1 -1
- package/dist/runtime/query/tooling.d.ts +4 -0
- package/dist/runtime/query/tooling.d.ts.map +1 -1
- package/dist/runtime/query/tooling.js +4 -0
- package/dist/runtime/query/tooling.js.map +1 -1
- package/dist/sandbox/file-walk-program.d.ts +3 -0
- package/dist/sandbox/file-walk-program.d.ts.map +1 -0
- package/dist/sandbox/file-walk-program.js +94 -0
- package/dist/sandbox/file-walk-program.js.map +1 -0
- package/dist/sandbox/file-walk.d.ts +12 -0
- package/dist/sandbox/file-walk.d.ts.map +1 -0
- package/dist/sandbox/file-walk.js +429 -0
- package/dist/sandbox/file-walk.js.map +1 -0
- package/dist/sandbox/index.d.ts +2 -0
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +1 -0
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/provider/local.d.ts +3 -1
- package/dist/sandbox/provider/local.d.ts.map +1 -1
- package/dist/sandbox/provider/local.js +121 -12
- package/dist/sandbox/provider/local.js.map +1 -1
- package/dist/scheduler/completion-inbox.d.ts +3 -2
- package/dist/scheduler/completion-inbox.d.ts.map +1 -1
- package/dist/scheduler/completion-inbox.js +29 -15
- package/dist/scheduler/completion-inbox.js.map +1 -1
- package/dist/scheduler/delegating.d.ts +1 -0
- package/dist/scheduler/delegating.d.ts.map +1 -1
- package/dist/scheduler/delegating.js +29 -12
- package/dist/scheduler/delegating.js.map +1 -1
- package/dist/scheduler/local.d.ts +1 -0
- package/dist/scheduler/local.d.ts.map +1 -1
- package/dist/scheduler/local.js +4 -7
- package/dist/scheduler/local.js.map +1 -1
- package/dist/session/index.d.ts +0 -1
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/index.js +0 -1
- package/dist/session/index.js.map +1 -1
- package/dist/session/workspace/path-builder.d.ts.map +1 -1
- package/dist/session/workspace/path-builder.js +5 -4
- package/dist/session/workspace/path-builder.js.map +1 -1
- package/dist/store/feedback/disk.d.ts.map +1 -1
- package/dist/store/feedback/disk.js +4 -6
- package/dist/store/feedback/disk.js.map +1 -1
- package/dist/store/index.d.ts +2 -0
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +1 -0
- package/dist/store/index.js.map +1 -1
- package/dist/store/memory/disk.d.ts.map +1 -1
- package/dist/store/memory/disk.js +3 -12
- package/dist/store/memory/disk.js.map +1 -1
- package/dist/store/run/checkpoint-disk.d.ts +3 -3
- package/dist/store/run/checkpoint-disk.d.ts.map +1 -1
- package/dist/store/run/checkpoint-disk.js +14 -26
- package/dist/store/run/checkpoint-disk.js.map +1 -1
- package/dist/store/run/claim-disk.js +1 -1
- package/dist/store/run/conformance.d.ts +1 -1
- package/dist/store/run/conformance.d.ts.map +1 -1
- package/dist/store/run/conformance.js +41 -33
- package/dist/store/run/conformance.js.map +1 -1
- package/dist/store/run/disk.d.ts.map +1 -1
- package/dist/store/run/disk.js +20 -4
- package/dist/store/run/disk.js.map +1 -1
- package/dist/store/run/token-budget-disk.d.ts +37 -0
- package/dist/store/run/token-budget-disk.d.ts.map +1 -0
- package/dist/store/run/token-budget-disk.js +208 -0
- package/dist/store/run/token-budget-disk.js.map +1 -0
- package/dist/store/session/disk.d.ts +4 -13
- package/dist/store/session/disk.d.ts.map +1 -1
- package/dist/store/session/disk.js +34 -31
- package/dist/store/session/disk.js.map +1 -1
- package/dist/store/session/memory.d.ts +2 -0
- package/dist/store/session/memory.d.ts.map +1 -1
- package/dist/store/session/memory.js +41 -2
- package/dist/store/session/memory.js.map +1 -1
- package/dist/store/task/disk.d.ts.map +1 -1
- package/dist/store/task/disk.js +7 -8
- package/dist/store/task/disk.js.map +1 -1
- package/dist/store/topic/memory.d.ts +2 -0
- package/dist/store/topic/memory.d.ts.map +1 -1
- package/dist/store/topic/memory.js +13 -1
- package/dist/store/topic/memory.js.map +1 -1
- package/dist/test-support/ids.d.ts +4 -19
- package/dist/test-support/ids.d.ts.map +1 -1
- package/dist/test-support/ids.js +51 -59
- package/dist/test-support/ids.js.map +1 -1
- package/dist/tools/advisory/index.d.ts.map +1 -1
- package/dist/tools/advisory/index.js +3 -1
- package/dist/tools/advisory/index.js.map +1 -1
- package/dist/tools/builtins/bash.d.ts +2 -0
- package/dist/tools/builtins/bash.d.ts.map +1 -1
- package/dist/tools/builtins/bash.js +224 -70
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/builtins/computer-use.d.ts +2 -2
- package/dist/tools/builtins/computer-use.d.ts.map +1 -1
- package/dist/tools/builtins/computer-use.js +4 -2
- package/dist/tools/builtins/computer-use.js.map +1 -1
- package/dist/tools/builtins/edit.js +2 -2
- package/dist/tools/builtins/edit.js.map +1 -1
- package/dist/tools/builtins/glob.d.ts +2 -1
- package/dist/tools/builtins/glob.d.ts.map +1 -1
- package/dist/tools/builtins/glob.js +95 -103
- package/dist/tools/builtins/glob.js.map +1 -1
- package/dist/tools/builtins/grep.d.ts.map +1 -1
- package/dist/tools/builtins/grep.js +155 -111
- package/dist/tools/builtins/grep.js.map +1 -1
- package/dist/tools/builtins/ls.js +4 -4
- package/dist/tools/builtins/ls.js.map +1 -1
- package/dist/tools/builtins/lsp.js +2 -2
- package/dist/tools/builtins/lsp.js.map +1 -1
- package/dist/tools/builtins/read-file.js +2 -2
- package/dist/tools/builtins/read-file.js.map +1 -1
- package/dist/tools/builtins/write-file.js +2 -2
- package/dist/tools/builtins/write-file.js.map +1 -1
- package/dist/tools/coordinator/ask-user-question.d.ts +33 -0
- package/dist/tools/coordinator/ask-user-question.d.ts.map +1 -0
- package/dist/tools/coordinator/ask-user-question.js +237 -0
- package/dist/tools/coordinator/ask-user-question.js.map +1 -0
- package/dist/tools/coordinator/index.d.ts +1 -2
- package/dist/tools/coordinator/index.d.ts.map +1 -1
- package/dist/tools/coordinator/index.js +4 -220
- package/dist/tools/coordinator/index.js.map +1 -1
- package/dist/tools/memory/read.d.ts.map +1 -1
- package/dist/tools/memory/read.js +2 -4
- package/dist/tools/memory/read.js.map +1 -1
- package/dist/tools/paths.d.ts +13 -0
- package/dist/tools/paths.d.ts.map +1 -1
- package/dist/tools/paths.js +53 -0
- package/dist/tools/paths.js.map +1 -1
- package/dist/tools/roster.d.ts +27 -0
- package/dist/tools/roster.d.ts.map +1 -0
- package/dist/tools/roster.js +42 -0
- package/dist/tools/roster.js.map +1 -0
- package/dist/types/agent/base.d.ts +5 -0
- package/dist/types/agent/base.d.ts.map +1 -1
- package/dist/types/agent/pipeline.d.ts +3 -0
- package/dist/types/agent/pipeline.d.ts.map +1 -1
- package/dist/types/agent/router.d.ts +2 -3
- package/dist/types/agent/router.d.ts.map +1 -1
- package/dist/types/agent/scheduler.d.ts +3 -0
- package/dist/types/agent/scheduler.d.ts.map +1 -1
- package/dist/types/agent/task.d.ts +5 -16
- package/dist/types/agent/task.d.ts.map +1 -1
- package/dist/types/agent/task.js.map +1 -1
- package/dist/types/common/index.d.ts.map +1 -1
- package/dist/types/common/index.js +3 -0
- package/dist/types/common/index.js.map +1 -1
- package/dist/types/computer-use/index.d.ts +8 -0
- package/dist/types/computer-use/index.d.ts.map +1 -1
- package/dist/types/computer-use/index.js.map +1 -1
- package/dist/types/hitl/index.d.ts +5 -0
- package/dist/types/hitl/index.d.ts.map +1 -1
- package/dist/types/hitl/index.js.map +1 -1
- package/dist/types/ids/brand.d.ts +5 -47
- package/dist/types/ids/brand.d.ts.map +1 -1
- package/dist/types/ids/brand.js +2 -39
- package/dist/types/ids/brand.js.map +1 -1
- package/dist/types/ids/index.d.ts +59 -69
- package/dist/types/ids/index.d.ts.map +1 -1
- package/dist/types/ids/index.js +12 -10
- package/dist/types/ids/index.js.map +1 -1
- package/dist/types/permission/index.d.ts +9 -0
- package/dist/types/permission/index.d.ts.map +1 -1
- package/dist/types/permission/index.js +9 -1
- package/dist/types/permission/index.js.map +1 -1
- package/dist/types/plugin/index.d.ts +45 -4
- package/dist/types/plugin/index.d.ts.map +1 -1
- package/dist/types/plugin/index.js +7 -0
- package/dist/types/plugin/index.js.map +1 -1
- package/dist/types/run/config.d.ts +3 -0
- package/dist/types/run/config.d.ts.map +1 -1
- package/dist/types/run/emergency.d.ts +5 -0
- package/dist/types/run/emergency.d.ts.map +1 -1
- package/dist/types/run/emergency.js.map +1 -1
- package/dist/types/run/entity.d.ts +6 -2
- package/dist/types/run/entity.d.ts.map +1 -1
- package/dist/types/run/events.d.ts +35 -0
- package/dist/types/run/events.d.ts.map +1 -1
- package/dist/types/run/events.js.map +1 -1
- package/dist/types/run/index.d.ts +1 -0
- package/dist/types/run/index.d.ts.map +1 -1
- package/dist/types/run/index.js +1 -0
- package/dist/types/run/index.js.map +1 -1
- package/dist/types/run/state.d.ts +16 -15
- package/dist/types/run/state.d.ts.map +1 -1
- package/dist/types/run/state.js +41 -22
- package/dist/types/run/state.js.map +1 -1
- package/dist/types/run/stop-reason.d.ts +3 -14
- package/dist/types/run/stop-reason.d.ts.map +1 -1
- package/dist/types/run/token-budget-store.d.ts +29 -0
- package/dist/types/run/token-budget-store.d.ts.map +1 -0
- package/dist/types/run/token-budget-store.js +25 -0
- package/dist/types/run/token-budget-store.js.map +1 -0
- package/dist/types/sandbox/index.d.ts +47 -2
- package/dist/types/sandbox/index.d.ts.map +1 -1
- package/dist/types/sandbox/index.js.map +1 -1
- package/dist/types/session/store.d.ts +8 -0
- package/dist/types/session/store.d.ts.map +1 -1
- package/dist/types/tool/index.d.ts +15 -0
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/dist/utils/frontmatter.d.ts +1 -1
- package/dist/utils/frontmatter.js +1 -1
- package/dist/utils/id-format.d.ts +3 -0
- package/dist/utils/id-format.d.ts.map +1 -0
- package/dist/utils/id-format.js +9 -0
- package/dist/utils/id-format.js.map +1 -0
- package/dist/utils/id.d.ts +65 -32
- package/dist/utils/id.d.ts.map +1 -1
- package/dist/utils/id.js +168 -148
- package/dist/utils/id.js.map +1 -1
- package/dist/utils/log/exception.js +1 -1
- package/dist/utils/log/exception.js.map +1 -1
- package/dist/utils/log/index.d.ts.map +1 -1
- package/dist/utils/log/index.js +1 -2
- package/dist/utils/log/index.js.map +1 -1
- package/dist/utils/log/templates.d.ts.map +1 -1
- package/dist/utils/log/templates.js +0 -1
- package/dist/utils/log/templates.js.map +1 -1
- package/package.json +6 -1
- package/src/agents/AGENTS.md +1 -1
- package/src/agents/PipelineAgent.ts +129 -109
- package/src/agents/ReactiveAgent.ts +2 -0
- package/src/agents/RouterAgent.ts +120 -73
- package/src/agents/SupervisorAgent.ts +12 -4
- package/src/agents/budget.ts +42 -0
- package/src/agents/explore.ts +29 -0
- package/src/agents/file-definitions.ts +237 -0
- package/src/agents/index.ts +15 -0
- package/src/bridge/a2a/mapper.ts +6 -0
- package/src/bridge/sse/mapper.ts +22 -0
- package/src/bus/lock.ts +1 -10
- package/src/compaction/consolidation.ts +79 -0
- package/src/compaction/factory.ts +3 -0
- package/src/compaction/index.ts +34 -4
- package/src/compaction/manager.ts +39 -1
- package/src/compaction/plan.ts +52 -21
- package/src/compaction/salience/bm25.ts +72 -0
- package/src/compaction/salience/goal.ts +44 -0
- package/src/compaction/salience/index.ts +19 -0
- package/src/compaction/salience/minhash.ts +53 -0
- package/src/compaction/salience/score.ts +305 -0
- package/src/compaction/salience/tokenize.ts +89 -0
- package/src/compaction/salience/working-set.ts +187 -0
- package/src/compaction/serializer.ts +16 -0
- package/src/compaction/token-estimate.ts +57 -0
- package/src/compaction/tool-result-editing.ts +28 -26
- package/src/compaction/types.ts +24 -1
- package/src/compaction/wire.ts +5 -1
- package/src/config/runtime.ts +13 -1
- package/src/connector/builtins/http.ts +2 -8
- package/src/connector/builtins/webhook.ts +2 -2
- package/src/connector/mcp/adapter.ts +32 -0
- package/src/connector/tools/adapter.ts +2 -2
- package/src/connector/tools/definitions.ts +2 -2
- package/src/constants/compaction/index.ts +1 -0
- package/src/constants/telemetry/index.ts +0 -1
- package/src/contracts/api.ts +5 -1
- package/src/contracts/schemas.ts +6 -20
- package/src/invariants/index.ts +1 -1
- package/src/manager/agent/lifecycle.ts +380 -352
- package/src/manager/run/persistence.ts +35 -5
- package/src/plugin/index.ts +19 -0
- package/src/plugin/shell-hook.ts +403 -0
- package/src/prompt/coding-agent-doctrine.ts +118 -0
- package/src/prompt/index.ts +8 -0
- package/src/provider/errors.ts +1 -1
- package/src/provider/fallback.ts +4 -1
- package/src/provider/retry.ts +4 -1
- package/src/provider/token-budget.ts +158 -0
- package/src/public-runtime.ts +56 -28
- package/src/public-tools.ts +5 -0
- package/src/public-types.ts +65 -0
- package/src/registry/command/kernel-commands.ts +16 -6
- package/src/registry/tool/execute.ts +4 -1
- package/src/run/LimitChecker.ts +1 -1
- package/src/run/drain.ts +1 -1
- package/src/run/reporter.ts +15 -0
- package/src/run/token-budget.ts +692 -0
- package/src/runtime/jobs/registry.ts +123 -17
- package/src/runtime/query/checkpoint.ts +10 -3
- package/src/runtime/query/context.ts +3 -57
- package/src/runtime/query/executor.ts +181 -71
- package/src/runtime/query/guard.ts +8 -0
- package/src/runtime/query/index.ts +209 -62
- package/src/runtime/query/iteration/index.ts +53 -15
- package/src/runtime/query/iteration/phases/compaction.ts +130 -31
- package/src/runtime/query/iteration/phases/context.ts +4 -0
- package/src/runtime/query/iteration/phases/tool-review.ts +39 -7
- package/src/runtime/query/iteration/phases/working-memory.ts +23 -14
- package/src/runtime/query/iteration/stream-turn.ts +9 -5
- package/src/runtime/query/plugin-hooks.ts +24 -2
- package/src/runtime/query/prompt.ts +21 -4
- package/src/runtime/query/repeat-call.ts +42 -7
- package/src/runtime/query/result.ts +9 -0
- package/src/runtime/query/review-policy.ts +231 -0
- package/src/runtime/query/run-state.ts +4 -0
- package/src/runtime/query/steering.ts +32 -0
- package/src/runtime/query/token-budget.ts +127 -0
- package/src/runtime/query/tool-output-budget.ts +33 -10
- package/src/runtime/query/tool-pause.ts +4 -2
- package/src/runtime/query/tooling.ts +8 -0
- package/src/sandbox/file-walk-program.ts +93 -0
- package/src/sandbox/file-walk.ts +463 -0
- package/src/sandbox/index.ts +2 -0
- package/src/sandbox/provider/local.ts +136 -13
- package/src/scheduler/completion-inbox.ts +29 -19
- package/src/scheduler/delegating.ts +35 -13
- package/src/scheduler/local.ts +5 -7
- package/src/session/index.ts +0 -1
- package/src/session/workspace/path-builder.ts +5 -4
- package/src/store/feedback/disk.ts +4 -6
- package/src/store/index.ts +2 -0
- package/src/store/memory/disk.ts +3 -11
- package/src/store/run/checkpoint-disk.ts +14 -27
- package/src/store/run/claim-disk.ts +1 -1
- package/src/store/run/conformance.ts +55 -39
- package/src/store/run/disk.ts +19 -4
- package/src/store/run/token-budget-disk.ts +247 -0
- package/src/store/session/disk.ts +36 -29
- package/src/store/session/memory.ts +38 -1
- package/src/store/task/disk.ts +7 -8
- package/src/store/topic/memory.ts +13 -1
- package/src/test-support/ids.ts +62 -59
- package/src/tools/advisory/index.ts +3 -1
- package/src/tools/builtins/bash.ts +241 -70
- package/src/tools/builtins/computer-use.ts +6 -2
- package/src/tools/builtins/edit.ts +2 -2
- package/src/tools/builtins/glob.ts +104 -122
- package/src/tools/builtins/grep.ts +166 -132
- package/src/tools/builtins/ls.ts +4 -4
- package/src/tools/builtins/lsp.ts +2 -2
- package/src/tools/builtins/read-file.ts +2 -2
- package/src/tools/builtins/write-file.ts +2 -2
- package/src/tools/coordinator/ask-user-question.ts +284 -0
- package/src/tools/coordinator/index.ts +5 -248
- package/src/tools/memory/read.ts +2 -4
- package/src/tools/paths.ts +64 -0
- package/src/tools/roster.ts +46 -0
- package/src/types/agent/base.ts +6 -0
- package/src/types/agent/pipeline.ts +3 -0
- package/src/types/agent/router.ts +2 -3
- package/src/types/agent/scheduler.ts +4 -0
- package/src/types/agent/task.ts +5 -18
- package/src/types/common/index.ts +3 -0
- package/src/types/computer-use/index.ts +8 -0
- package/src/types/hitl/index.ts +5 -0
- package/src/types/ids/brand.ts +5 -48
- package/src/types/ids/index.ts +66 -75
- package/src/types/permission/index.ts +11 -0
- package/src/types/plugin/index.ts +49 -1
- package/src/types/run/config.ts +3 -0
- package/src/types/run/emergency.ts +5 -0
- package/src/types/run/entity.ts +6 -2
- package/src/types/run/events.ts +37 -0
- package/src/types/run/index.ts +1 -0
- package/src/types/run/state.ts +58 -30
- package/src/types/run/stop-reason.ts +3 -14
- package/src/types/run/token-budget-store.ts +54 -0
- package/src/types/sandbox/index.ts +53 -0
- package/src/types/session/store.ts +8 -0
- package/src/types/tool/index.ts +15 -0
- package/src/utils/frontmatter.ts +1 -1
- package/src/utils/id-format.ts +9 -0
- package/src/utils/id.ts +222 -181
- package/src/utils/log/exception.ts +1 -1
- package/src/utils/log/index.ts +1 -2
- package/src/utils/log/templates.ts +0 -1
- package/dist/session/migration/errors.d.ts +0 -26
- package/dist/session/migration/errors.d.ts.map +0 -1
- package/dist/session/migration/errors.js +0 -22
- package/dist/session/migration/errors.js.map +0 -1
- package/dist/session/migration/filesystem.d.ts +0 -122
- package/dist/session/migration/filesystem.d.ts.map +0 -1
- package/dist/session/migration/filesystem.js +0 -379
- package/dist/session/migration/filesystem.js.map +0 -1
- package/dist/session/migration/id-prefix.d.ts +0 -113
- package/dist/session/migration/id-prefix.d.ts.map +0 -1
- package/dist/session/migration/id-prefix.js +0 -132
- package/dist/session/migration/id-prefix.js.map +0 -1
- package/dist/session/migration/index.d.ts +0 -12
- package/dist/session/migration/index.d.ts.map +0 -1
- package/dist/session/migration/index.js +0 -12
- package/dist/session/migration/index.js.map +0 -1
- package/dist/session/migration/marker.d.ts +0 -57
- package/dist/session/migration/marker.d.ts.map +0 -1
- package/dist/session/migration/marker.js +0 -123
- package/dist/session/migration/marker.js.map +0 -1
- package/src/session/migration/errors.ts +0 -23
- package/src/session/migration/filesystem.ts +0 -468
- package/src/session/migration/id-prefix.ts +0 -166
- package/src/session/migration/index.ts +0 -41
- package/src/session/migration/marker.ts +0 -142
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto'
|
|
2
|
+
import { EMPTY_TOKEN_USAGE } from '../constants/limits.js'
|
|
3
|
+
import { type TokenUsage, accumulateTokenUsage } from '../types/common/index.js'
|
|
4
|
+
import type { RunId } from '../types/ids/index.js'
|
|
5
|
+
import type { TokenBudgetBinding, TokenBudgetScope } from '../types/run/token-budget-store.js'
|
|
6
|
+
import { isEntityId } from '../utils/id.js'
|
|
7
|
+
|
|
8
|
+
export interface TokenBudgetAccountSnapshot {
|
|
9
|
+
id: string
|
|
10
|
+
parentId?: string
|
|
11
|
+
limit: number
|
|
12
|
+
settled: boolean
|
|
13
|
+
runId?: RunId
|
|
14
|
+
/** Cumulative own usage per run; descendants never appear in this map. */
|
|
15
|
+
usage: Record<string, TokenUsage>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface TokenBudgetRequestSnapshot {
|
|
19
|
+
id: string
|
|
20
|
+
accountId: string
|
|
21
|
+
runId: RunId
|
|
22
|
+
/** Cumulative request usage already charged to the owning run. */
|
|
23
|
+
usage?: TokenUsage
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Durable authority independent of a message checkpoint's cadence. */
|
|
27
|
+
export interface TokenBudgetSnapshot {
|
|
28
|
+
version: 1
|
|
29
|
+
rootAccountId: string
|
|
30
|
+
rootRunId: RunId
|
|
31
|
+
accounts: TokenBudgetAccountSnapshot[]
|
|
32
|
+
requests: TokenBudgetRequestSnapshot[]
|
|
33
|
+
/** Durable receipts make repeated completion delivery a no-op. */
|
|
34
|
+
completedRequests: TokenBudgetRequestSnapshot[]
|
|
35
|
+
poisoned?: boolean
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface TokenBudgetPersistence {
|
|
39
|
+
save(snapshot: TokenBudgetSnapshot): Promise<void>
|
|
40
|
+
readonly scope?: TokenBudgetScope
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface TokenBudgetSummary {
|
|
44
|
+
limit: number
|
|
45
|
+
ownTokens: number
|
|
46
|
+
treeTokens: number
|
|
47
|
+
reservedTokens: number
|
|
48
|
+
/** null means unlimited; unlike Infinity this survives JSON serialization. */
|
|
49
|
+
remainingTokens: number | null
|
|
50
|
+
inFlightRequests: number
|
|
51
|
+
unsettledChildren: number
|
|
52
|
+
/** An unresolved provider call or failed ledger write prevents admission. */
|
|
53
|
+
poisoned: boolean
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface SharedLedger {
|
|
57
|
+
rootAccountId: string
|
|
58
|
+
rootRunId: RunId
|
|
59
|
+
accounts: Map<string, TokenBudgetAccountSnapshot>
|
|
60
|
+
requests: Map<string, TokenBudgetRequestSnapshot>
|
|
61
|
+
finishedRequests: Map<string, TokenBudgetRequestSnapshot>
|
|
62
|
+
poisoned: boolean
|
|
63
|
+
persistence?: TokenBudgetPersistence
|
|
64
|
+
writes: Promise<void>
|
|
65
|
+
persistenceError?: unknown
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface AccountTotals {
|
|
69
|
+
own: number
|
|
70
|
+
tree: number
|
|
71
|
+
reserved: number
|
|
72
|
+
closed: boolean
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function count(value: unknown, name: string): number {
|
|
76
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) {
|
|
77
|
+
throw new Error(`Invalid token budget ${name}: expected a nonnegative safe integer`)
|
|
78
|
+
}
|
|
79
|
+
return value
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function object(value: unknown, name: string): Record<string, unknown> {
|
|
83
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
84
|
+
throw new Error(`Invalid token budget ${name}: expected an object`)
|
|
85
|
+
}
|
|
86
|
+
return value as Record<string, unknown>
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function id(value: unknown, name: string): string {
|
|
90
|
+
if (!isEntityId(value, 'run')) throw new Error(`Invalid token budget ${name}: expected a UUID`)
|
|
91
|
+
return value
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function usageValue(value: unknown): TokenUsage {
|
|
95
|
+
const source = object(value, 'usage')
|
|
96
|
+
const usage: TokenUsage = {
|
|
97
|
+
promptTokens: count(source.promptTokens, 'promptTokens'),
|
|
98
|
+
completionTokens: count(source.completionTokens, 'completionTokens'),
|
|
99
|
+
totalTokens: count(source.totalTokens, 'totalTokens'),
|
|
100
|
+
cachedTokens: count(source.cachedTokens, 'cachedTokens'),
|
|
101
|
+
cacheWriteTokens: count(source.cacheWriteTokens, 'cacheWriteTokens'),
|
|
102
|
+
...(source.reasoningTokens === undefined
|
|
103
|
+
? {}
|
|
104
|
+
: { reasoningTokens: count(source.reasoningTokens, 'reasoningTokens') }),
|
|
105
|
+
}
|
|
106
|
+
// A partial usage frame may understate its total; never lose a component.
|
|
107
|
+
usage.totalTokens = Math.max(
|
|
108
|
+
usage.totalTokens,
|
|
109
|
+
count(usage.promptTokens + usage.completionTokens, 'usage component sum'),
|
|
110
|
+
)
|
|
111
|
+
return usage
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function addUsage(left: TokenUsage, right: TokenUsage): TokenUsage {
|
|
115
|
+
return usageValue(accumulateTokenUsage(left, right))
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function maxUsage(left: TokenUsage, right: TokenUsage): TokenUsage {
|
|
119
|
+
return usageValue({
|
|
120
|
+
promptTokens: Math.max(left.promptTokens, right.promptTokens),
|
|
121
|
+
completionTokens: Math.max(left.completionTokens, right.completionTokens),
|
|
122
|
+
totalTokens: Math.max(left.totalTokens, right.totalTokens),
|
|
123
|
+
cachedTokens: Math.max(left.cachedTokens, right.cachedTokens),
|
|
124
|
+
cacheWriteTokens: Math.max(left.cacheWriteTokens, right.cacheWriteTokens),
|
|
125
|
+
...(left.reasoningTokens === undefined && right.reasoningTokens === undefined
|
|
126
|
+
? {}
|
|
127
|
+
: { reasoningTokens: Math.max(left.reasoningTokens ?? 0, right.reasoningTokens ?? 0) }),
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Validate the whole tree, then return a defensive, JSON-safe snapshot. */
|
|
132
|
+
export function validateTokenBudgetSnapshot(value: unknown): TokenBudgetSnapshot {
|
|
133
|
+
const source = object(value, 'snapshot')
|
|
134
|
+
if (source.version !== 1) throw new Error('Unsupported token budget snapshot version')
|
|
135
|
+
const rootAccountId = id(source.rootAccountId, 'rootAccountId')
|
|
136
|
+
if (!isEntityId(source.rootRunId, 'run')) throw new Error('Invalid token budget rootRunId')
|
|
137
|
+
if (
|
|
138
|
+
!Array.isArray(source.accounts) ||
|
|
139
|
+
!Array.isArray(source.requests) ||
|
|
140
|
+
!Array.isArray(source.completedRequests)
|
|
141
|
+
) {
|
|
142
|
+
throw new Error('Invalid token budget accounts or requests')
|
|
143
|
+
}
|
|
144
|
+
if (source.poisoned !== undefined && typeof source.poisoned !== 'boolean') {
|
|
145
|
+
throw new Error('Invalid token budget poisoned flag')
|
|
146
|
+
}
|
|
147
|
+
const runOwners = new Map<string, string>()
|
|
148
|
+
const accounts = source.accounts.map((raw): TokenBudgetAccountSnapshot => {
|
|
149
|
+
const account = object(raw, 'account')
|
|
150
|
+
const accountId = id(account.id, 'account id')
|
|
151
|
+
if (typeof account.settled !== 'boolean') throw new Error('Invalid token budget settled flag')
|
|
152
|
+
const limit = count(account.limit, 'limit')
|
|
153
|
+
if (accountId !== rootAccountId && limit === 0) {
|
|
154
|
+
throw new Error('A child token budget must have a positive limit')
|
|
155
|
+
}
|
|
156
|
+
const usage: Record<string, TokenUsage> = {}
|
|
157
|
+
for (const [runId, rawUsage] of Object.entries(object(account.usage, 'account usage'))) {
|
|
158
|
+
id(runId, 'usage run id')
|
|
159
|
+
if (runOwners.has(runId)) throw new Error('A run belongs to more than one token budget')
|
|
160
|
+
runOwners.set(runId, accountId)
|
|
161
|
+
usage[runId] = usageValue(rawUsage)
|
|
162
|
+
}
|
|
163
|
+
if (account.runId !== undefined) {
|
|
164
|
+
const runId = id(account.runId, 'bound run id')
|
|
165
|
+
if (!Object.hasOwn(usage, runId) || Object.keys(usage).length !== 1) {
|
|
166
|
+
throw new Error('Bound token budget run has no usage watermark')
|
|
167
|
+
}
|
|
168
|
+
} else if (Object.keys(usage).length !== 0) {
|
|
169
|
+
throw new Error('Unbound token budget cannot contain usage')
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
id: accountId,
|
|
173
|
+
...(account.parentId === undefined ? {} : { parentId: id(account.parentId, 'parent id') }),
|
|
174
|
+
limit,
|
|
175
|
+
settled: account.settled,
|
|
176
|
+
...(account.runId === undefined ? {} : { runId: account.runId as RunId }),
|
|
177
|
+
usage,
|
|
178
|
+
}
|
|
179
|
+
})
|
|
180
|
+
const byId = new Map(accounts.map((account) => [account.id, account]))
|
|
181
|
+
if (byId.size !== accounts.length) throw new Error('Duplicate token budget account')
|
|
182
|
+
const root = byId.get(rootAccountId)
|
|
183
|
+
if (!root || root.parentId !== undefined || !Object.hasOwn(root.usage, source.rootRunId)) {
|
|
184
|
+
throw new Error('Invalid token budget root account')
|
|
185
|
+
}
|
|
186
|
+
for (const account of accounts) {
|
|
187
|
+
const visited = new Set<string>()
|
|
188
|
+
let cursor: TokenBudgetAccountSnapshot | undefined = account
|
|
189
|
+
while (cursor.id !== rootAccountId) {
|
|
190
|
+
if (visited.has(cursor.id)) throw new Error('Cycle in token budget accounts')
|
|
191
|
+
visited.add(cursor.id)
|
|
192
|
+
cursor = cursor.parentId === undefined ? undefined : byId.get(cursor.parentId)
|
|
193
|
+
if (!cursor) throw new Error('Disconnected token budget account')
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const requestIds = new Set<string>()
|
|
197
|
+
const parseRequest = (raw: unknown): TokenBudgetRequestSnapshot => {
|
|
198
|
+
const request = object(raw, 'request')
|
|
199
|
+
const requestId = id(request.id, 'request id')
|
|
200
|
+
const accountId = id(request.accountId, 'request account id')
|
|
201
|
+
if (!isEntityId(request.runId, 'run')) throw new Error('Invalid token budget request run id')
|
|
202
|
+
if (requestIds.has(requestId)) throw new Error('Duplicate token budget request')
|
|
203
|
+
requestIds.add(requestId)
|
|
204
|
+
if (!byId.has(accountId) || runOwners.get(request.runId) !== accountId) {
|
|
205
|
+
throw new Error('Token budget request has no owning run')
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
id: requestId,
|
|
209
|
+
accountId,
|
|
210
|
+
runId: request.runId,
|
|
211
|
+
...(request.usage === undefined ? {} : { usage: usageValue(request.usage) }),
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const requests = source.requests.map(parseRequest)
|
|
215
|
+
const completedRequests = source.completedRequests.map(parseRequest)
|
|
216
|
+
const requestAccounts = new Set<string>()
|
|
217
|
+
for (const request of requests) {
|
|
218
|
+
if (requestAccounts.has(request.accountId))
|
|
219
|
+
throw new Error('Multiple requests on one token budget account')
|
|
220
|
+
requestAccounts.add(request.accountId)
|
|
221
|
+
}
|
|
222
|
+
const measuredByRun = new Map<string, TokenUsage>()
|
|
223
|
+
for (const request of [...requests, ...completedRequests]) {
|
|
224
|
+
if (request.usage === undefined) continue
|
|
225
|
+
const measured = addUsage(
|
|
226
|
+
measuredByRun.get(request.runId) ?? { ...EMPTY_TOKEN_USAGE },
|
|
227
|
+
request.usage,
|
|
228
|
+
)
|
|
229
|
+
measuredByRun.set(request.runId, measured)
|
|
230
|
+
const account = byId.get(request.accountId)
|
|
231
|
+
const own = account?.usage[request.runId]
|
|
232
|
+
if (
|
|
233
|
+
!own ||
|
|
234
|
+
measured.totalTokens > own.totalTokens ||
|
|
235
|
+
measured.promptTokens > own.promptTokens ||
|
|
236
|
+
measured.completionTokens > own.completionTokens
|
|
237
|
+
) {
|
|
238
|
+
throw new Error('Request usage exceeds its owning run watermark')
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// Account sums must also remain representable, including malicious snapshots
|
|
242
|
+
// with individually valid counters whose sum exceeds safe integer arithmetic.
|
|
243
|
+
let aggregate = { ...EMPTY_TOKEN_USAGE }
|
|
244
|
+
for (const account of accounts) {
|
|
245
|
+
for (const usage of Object.values(account.usage)) aggregate = addUsage(aggregate, usage)
|
|
246
|
+
}
|
|
247
|
+
const children = new Map<string, TokenBudgetAccountSnapshot[]>()
|
|
248
|
+
for (const account of accounts) {
|
|
249
|
+
if (account.parentId === undefined) continue
|
|
250
|
+
const siblings = children.get(account.parentId) ?? []
|
|
251
|
+
siblings.push(account)
|
|
252
|
+
children.set(account.parentId, siblings)
|
|
253
|
+
}
|
|
254
|
+
function checkReservations(account: TokenBudgetAccountSnapshot): {
|
|
255
|
+
tree: number
|
|
256
|
+
closed: boolean
|
|
257
|
+
} {
|
|
258
|
+
const descendants = (children.get(account.id) ?? []).map((child) => ({
|
|
259
|
+
child,
|
|
260
|
+
measured: checkReservations(child),
|
|
261
|
+
}))
|
|
262
|
+
count(
|
|
263
|
+
descendants.reduce(
|
|
264
|
+
(sum, { child, measured }) =>
|
|
265
|
+
sum + (measured.closed ? 0 : Math.max(0, child.limit - measured.tree)),
|
|
266
|
+
0,
|
|
267
|
+
),
|
|
268
|
+
'reserved allowance',
|
|
269
|
+
)
|
|
270
|
+
return {
|
|
271
|
+
tree:
|
|
272
|
+
Object.values(account.usage).reduce((sum, usage) => sum + usage.totalTokens, 0) +
|
|
273
|
+
descendants.reduce((sum, { measured }) => sum + measured.tree, 0),
|
|
274
|
+
closed:
|
|
275
|
+
account.settled &&
|
|
276
|
+
!requestAccounts.has(account.id) &&
|
|
277
|
+
descendants.every(({ measured }) => measured.closed),
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
checkReservations(root)
|
|
281
|
+
return {
|
|
282
|
+
version: 1,
|
|
283
|
+
rootAccountId,
|
|
284
|
+
rootRunId: source.rootRunId,
|
|
285
|
+
accounts,
|
|
286
|
+
requests,
|
|
287
|
+
completedRequests,
|
|
288
|
+
...(source.poisoned === undefined ? {} : { poisoned: source.poisoned }),
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* One conserved authority for a run and all of its descendants.
|
|
294
|
+
*
|
|
295
|
+
* A child's unused reservation is unavailable to its parent and siblings.
|
|
296
|
+
* Measured spend replaces that reservation, including overshoot debt. Provider
|
|
297
|
+
* usage is known after a request: this enforces admission, not an exact upper
|
|
298
|
+
* bound on the tokens an already admitted request can report.
|
|
299
|
+
*/
|
|
300
|
+
export class TokenBudget {
|
|
301
|
+
private constructor(
|
|
302
|
+
private readonly ledger: SharedLedger,
|
|
303
|
+
readonly accountId: string,
|
|
304
|
+
) {}
|
|
305
|
+
|
|
306
|
+
static create(
|
|
307
|
+
limit: number,
|
|
308
|
+
rootRunId: RunId,
|
|
309
|
+
persistence?: TokenBudgetPersistence,
|
|
310
|
+
): TokenBudget {
|
|
311
|
+
count(limit, 'limit')
|
|
312
|
+
id(rootRunId, 'root run id')
|
|
313
|
+
const accountId = randomUUID()
|
|
314
|
+
const budget = new TokenBudget(
|
|
315
|
+
{
|
|
316
|
+
rootAccountId: accountId,
|
|
317
|
+
rootRunId,
|
|
318
|
+
accounts: new Map([
|
|
319
|
+
[
|
|
320
|
+
accountId,
|
|
321
|
+
{
|
|
322
|
+
id: accountId,
|
|
323
|
+
limit,
|
|
324
|
+
settled: false,
|
|
325
|
+
runId: rootRunId,
|
|
326
|
+
usage: { [rootRunId]: { ...EMPTY_TOKEN_USAGE } },
|
|
327
|
+
},
|
|
328
|
+
],
|
|
329
|
+
]),
|
|
330
|
+
requests: new Map(),
|
|
331
|
+
finishedRequests: new Map(),
|
|
332
|
+
poisoned: false,
|
|
333
|
+
persistence,
|
|
334
|
+
writes: Promise.resolve(),
|
|
335
|
+
},
|
|
336
|
+
accountId,
|
|
337
|
+
)
|
|
338
|
+
budget.persist()
|
|
339
|
+
return budget
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
static restore(snapshot: TokenBudgetSnapshot, persistence?: TokenBudgetPersistence): TokenBudget {
|
|
343
|
+
const state = validateTokenBudgetSnapshot(snapshot)
|
|
344
|
+
return new TokenBudget(
|
|
345
|
+
{
|
|
346
|
+
rootAccountId: state.rootAccountId,
|
|
347
|
+
rootRunId: state.rootRunId,
|
|
348
|
+
accounts: new Map(state.accounts.map((account) => [account.id, account])),
|
|
349
|
+
requests: new Map(state.requests.map((request) => [request.id, request])),
|
|
350
|
+
finishedRequests: new Map(state.completedRequests.map((request) => [request.id, request])),
|
|
351
|
+
poisoned: state.poisoned === true || state.requests.length > 0,
|
|
352
|
+
persistence,
|
|
353
|
+
writes: Promise.resolve(),
|
|
354
|
+
},
|
|
355
|
+
state.rootAccountId,
|
|
356
|
+
)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
get limit(): number {
|
|
360
|
+
return this.node.limit
|
|
361
|
+
}
|
|
362
|
+
get runId(): RunId | undefined {
|
|
363
|
+
return this.node.runId
|
|
364
|
+
}
|
|
365
|
+
get rootRunId(): RunId {
|
|
366
|
+
return this.ledger.rootRunId
|
|
367
|
+
}
|
|
368
|
+
get binding(): TokenBudgetBinding | undefined {
|
|
369
|
+
const scope = this.ledger.persistence?.scope
|
|
370
|
+
return scope ? { scope: { ...scope }, accountId: this.accountId } : undefined
|
|
371
|
+
}
|
|
372
|
+
get ownUsage(): TokenUsage {
|
|
373
|
+
return Object.values(this.node.usage).reduce(addUsage, { ...EMPTY_TOKEN_USAGE })
|
|
374
|
+
}
|
|
375
|
+
get ownTokens(): number {
|
|
376
|
+
return this.ownUsage.totalTokens
|
|
377
|
+
}
|
|
378
|
+
get treeTokens(): number {
|
|
379
|
+
return this.totals(this.node).tree
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
get remaining(): number {
|
|
383
|
+
if (this.ledger.poisoned || this.node.settled) return 0
|
|
384
|
+
let cursor = this.node
|
|
385
|
+
let available = this.free(cursor)
|
|
386
|
+
while (cursor.parentId !== undefined) {
|
|
387
|
+
const branch = this.totals(cursor)
|
|
388
|
+
const held = branch.closed ? 0 : Math.max(0, cursor.limit - branch.tree)
|
|
389
|
+
cursor = this.requireAccount(cursor.parentId)
|
|
390
|
+
// A sibling's overshoot reduces this branch's promise. A parent's
|
|
391
|
+
// zero unreserved space alone does not exhaust its reserved child.
|
|
392
|
+
available = Math.min(available, this.free(cursor) + held)
|
|
393
|
+
}
|
|
394
|
+
return Math.max(0, available)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
account(accountId: string): TokenBudget {
|
|
398
|
+
if (!this.descendants(this.node).has(accountId))
|
|
399
|
+
throw new Error('Token budget account is outside this subtree')
|
|
400
|
+
return new TokenBudget(this.ledger, accountId)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
findRun(runId: RunId): TokenBudget | undefined {
|
|
404
|
+
const descendants = this.descendants(this.node)
|
|
405
|
+
const account = [...this.ledger.accounts.values()].find(
|
|
406
|
+
(node) => descendants.has(node.id) && Object.hasOwn(node.usage, runId),
|
|
407
|
+
)
|
|
408
|
+
return account ? this.account(account.id) : undefined
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
bindRun(runId: RunId): void {
|
|
412
|
+
id(runId, 'bound run id')
|
|
413
|
+
const owner = [...this.ledger.accounts.values()].find((node) =>
|
|
414
|
+
Object.hasOwn(node.usage, runId),
|
|
415
|
+
)
|
|
416
|
+
if (owner && owner.id !== this.accountId)
|
|
417
|
+
throw new Error('Run already belongs to another token budget')
|
|
418
|
+
if (this.node.runId !== undefined && this.node.runId !== runId)
|
|
419
|
+
throw new Error('Token budget run binding is immutable')
|
|
420
|
+
if (this.node.settled && this.node.runId !== runId) throw new Error('Token budget is settled')
|
|
421
|
+
this.node.runId = runId
|
|
422
|
+
this.node.usage[runId] ??= { ...EMPTY_TOKEN_USAGE }
|
|
423
|
+
this.persist()
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
reserve(tokens: number): TokenBudget {
|
|
427
|
+
count(tokens, 'reservation')
|
|
428
|
+
if (this.hasRequest(this.node)) throw new Error('Token budget has an in-flight request')
|
|
429
|
+
if (tokens === 0 || tokens > this.remaining)
|
|
430
|
+
throw new Error('Token budget cannot reserve the requested tokens')
|
|
431
|
+
count(this.totals(this.node).reserved + tokens, 'reserved allowance')
|
|
432
|
+
const accountId = randomUUID()
|
|
433
|
+
this.ledger.accounts.set(accountId, {
|
|
434
|
+
id: accountId,
|
|
435
|
+
parentId: this.accountId,
|
|
436
|
+
limit: tokens,
|
|
437
|
+
settled: false,
|
|
438
|
+
usage: {},
|
|
439
|
+
})
|
|
440
|
+
this.persist()
|
|
441
|
+
return this.account(accountId)
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/** Narrow the entire subtree; already measured spend and reservations remain. */
|
|
445
|
+
narrow(limit: number): void {
|
|
446
|
+
count(limit, 'limit')
|
|
447
|
+
// Numeric run configuration uses zero for "no additional limit". It
|
|
448
|
+
// cannot remove an inherited finite authority.
|
|
449
|
+
if (limit === 0 || limit === this.limit) return
|
|
450
|
+
if (this.limit !== 0 && limit > this.limit)
|
|
451
|
+
throw new Error('Token budget cannot widen its limit')
|
|
452
|
+
this.node.limit = limit
|
|
453
|
+
this.persist()
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/** Reconcile a cumulative OWN run counter; a completion is not another spend. */
|
|
457
|
+
recordUsage(usage: TokenUsage, runId: RunId | undefined = this.runId): void {
|
|
458
|
+
const previous = this.requireOwnUsage(runId)
|
|
459
|
+
const next = maxUsage(previous, usageValue(usage))
|
|
460
|
+
this.checkAggregateReplacement(previous, next)
|
|
461
|
+
if (runId === undefined) throw new Error('Token budget run is not bound')
|
|
462
|
+
this.node.usage[runId] = next
|
|
463
|
+
this.persist()
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
async beginRequest(): Promise<string> {
|
|
467
|
+
if (this.ledger.poisoned) throw new Error('Token budget has unresolved spend')
|
|
468
|
+
if (this.node.settled) throw new Error('Token budget is settled')
|
|
469
|
+
if (this.remaining <= 0) throw new Error('Token budget is exhausted')
|
|
470
|
+
const runId = this.runId
|
|
471
|
+
if (runId === undefined) throw new Error('Token budget run is not bound')
|
|
472
|
+
// Requests on different child scopes may run concurrently. The same
|
|
473
|
+
// account cannot promise its unmeasured allowance twice.
|
|
474
|
+
if (this.hasRequest(this.node)) throw new Error('Token budget has an in-flight request')
|
|
475
|
+
const requestId = randomUUID()
|
|
476
|
+
this.ledger.requests.set(requestId, { id: requestId, accountId: this.accountId, runId })
|
|
477
|
+
this.persist()
|
|
478
|
+
await this.flush()
|
|
479
|
+
return requestId
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
async finishRequest(requestId: string, usage: TokenUsage): Promise<void> {
|
|
483
|
+
if (this.hasReceipt(requestId)) return this.flush()
|
|
484
|
+
const request = this.requireRequest(requestId)
|
|
485
|
+
try {
|
|
486
|
+
this.recordRequestUsage(request, usage)
|
|
487
|
+
} catch (error) {
|
|
488
|
+
this.ledger.poisoned = true
|
|
489
|
+
this.persist()
|
|
490
|
+
await this.flush()
|
|
491
|
+
throw error
|
|
492
|
+
}
|
|
493
|
+
this.ledger.requests.delete(requestId)
|
|
494
|
+
this.ledger.finishedRequests.set(requestId, request)
|
|
495
|
+
this.persist()
|
|
496
|
+
await this.flush()
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/** Unknown spend cannot be converted into a refund, even after cancellation. */
|
|
500
|
+
async failRequest(requestId: string, usage?: TokenUsage): Promise<void> {
|
|
501
|
+
if (this.hasReceipt(requestId)) return this.flush()
|
|
502
|
+
const request = this.requireRequest(requestId)
|
|
503
|
+
this.ledger.poisoned = true
|
|
504
|
+
try {
|
|
505
|
+
if (usage !== undefined) this.recordRequestUsage(request, usage)
|
|
506
|
+
} catch (error) {
|
|
507
|
+
this.persist()
|
|
508
|
+
await this.flush()
|
|
509
|
+
throw error
|
|
510
|
+
}
|
|
511
|
+
this.persist()
|
|
512
|
+
await this.flush()
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Explicit host recovery after obtaining the provider's final usage receipt.
|
|
517
|
+
* Ordinary completion never reopens an uncertain ledger. Reconciliation can
|
|
518
|
+
* do so only after every outstanding request has a measured receipt and all
|
|
519
|
+
* accounting writes succeeded; it never resets usage or settled accounts.
|
|
520
|
+
*/
|
|
521
|
+
async reconcileRequest(requestId: string, usage: TokenUsage): Promise<void> {
|
|
522
|
+
const measured = usageValue(usage)
|
|
523
|
+
if (this.hasReceipt(requestId)) {
|
|
524
|
+
const receipt = this.ledger.finishedRequests.get(requestId)
|
|
525
|
+
if (!receipt) throw new Error('Unknown token budget receipt')
|
|
526
|
+
// A recovered receipt may contain more usage than an earlier report.
|
|
527
|
+
// Preserve that evidence even though ordinary duplicate completion is
|
|
528
|
+
// a no-op. Repeating this same receipt still charges nothing twice.
|
|
529
|
+
this.recordRequestUsage(receipt, measured)
|
|
530
|
+
this.persist()
|
|
531
|
+
await this.flush()
|
|
532
|
+
} else {
|
|
533
|
+
await this.finishRequest(requestId, measured)
|
|
534
|
+
}
|
|
535
|
+
if (this.ledger.requests.size === 0 && this.ledger.persistenceError === undefined) {
|
|
536
|
+
this.ledger.poisoned = false
|
|
537
|
+
this.persist()
|
|
538
|
+
await this.flush()
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
settle(ownTotalTokens?: number): void {
|
|
543
|
+
if (ownTotalTokens !== undefined) {
|
|
544
|
+
count(ownTotalTokens, 'settled own tokens')
|
|
545
|
+
if (ownTotalTokens > this.ownTokens) {
|
|
546
|
+
if (this.runId === undefined)
|
|
547
|
+
throw new Error('Unbound token budget cannot settle nonzero usage')
|
|
548
|
+
const previous = this.requireOwnUsage(this.runId)
|
|
549
|
+
this.recordUsage({
|
|
550
|
+
...previous,
|
|
551
|
+
totalTokens: previous.totalTokens + ownTotalTokens - this.ownTokens,
|
|
552
|
+
})
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
this.node.settled = true
|
|
556
|
+
this.persist()
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
summary(): TokenBudgetSummary {
|
|
560
|
+
const totals = this.totals(this.node)
|
|
561
|
+
const descendants = this.descendants(this.node)
|
|
562
|
+
return {
|
|
563
|
+
limit: this.limit,
|
|
564
|
+
ownTokens: totals.own,
|
|
565
|
+
treeTokens: totals.tree,
|
|
566
|
+
reservedTokens: totals.reserved,
|
|
567
|
+
remainingTokens: Number.isFinite(this.remaining) ? this.remaining : null,
|
|
568
|
+
inFlightRequests: [...this.ledger.requests.values()].filter((request) =>
|
|
569
|
+
descendants.has(request.accountId),
|
|
570
|
+
).length,
|
|
571
|
+
unsettledChildren: this.children(this.node).filter((child) => !this.totals(child).closed)
|
|
572
|
+
.length,
|
|
573
|
+
poisoned: this.ledger.poisoned,
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
snapshot(): TokenBudgetSnapshot {
|
|
578
|
+
return structuredClone({
|
|
579
|
+
version: 1,
|
|
580
|
+
rootAccountId: this.ledger.rootAccountId,
|
|
581
|
+
rootRunId: this.rootRunId,
|
|
582
|
+
accounts: [...this.ledger.accounts.values()],
|
|
583
|
+
requests: [...this.ledger.requests.values()],
|
|
584
|
+
completedRequests: [...this.ledger.finishedRequests.values()],
|
|
585
|
+
...(this.ledger.poisoned ? { poisoned: true } : {}),
|
|
586
|
+
})
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
async flush(): Promise<void> {
|
|
590
|
+
await this.ledger.writes
|
|
591
|
+
if (this.ledger.persistenceError !== undefined) throw this.ledger.persistenceError
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
private get node(): TokenBudgetAccountSnapshot {
|
|
595
|
+
return this.requireAccount(this.accountId)
|
|
596
|
+
}
|
|
597
|
+
private requireAccount(accountId: string): TokenBudgetAccountSnapshot {
|
|
598
|
+
const account = this.ledger.accounts.get(accountId)
|
|
599
|
+
if (!account) throw new Error('Unknown token budget account')
|
|
600
|
+
return account
|
|
601
|
+
}
|
|
602
|
+
private requireOwnUsage(runId: RunId | undefined): TokenUsage {
|
|
603
|
+
const usage = runId === undefined ? undefined : this.node.usage[runId]
|
|
604
|
+
if (!usage) throw new Error('Token budget run is not bound')
|
|
605
|
+
return usage
|
|
606
|
+
}
|
|
607
|
+
private hasReceipt(requestId: string): boolean {
|
|
608
|
+
const receipt = this.ledger.finishedRequests.get(requestId)
|
|
609
|
+
if (receipt && receipt.accountId !== this.accountId)
|
|
610
|
+
throw new Error('Token budget receipt belongs to another account')
|
|
611
|
+
return receipt !== undefined
|
|
612
|
+
}
|
|
613
|
+
private recordRequestUsage(request: TokenBudgetRequestSnapshot, usage: TokenUsage): void {
|
|
614
|
+
const observed = request.usage ?? { ...EMPTY_TOKEN_USAGE }
|
|
615
|
+
const measured = maxUsage(observed, usageValue(usage))
|
|
616
|
+
const delta: TokenUsage = {
|
|
617
|
+
promptTokens: measured.promptTokens - observed.promptTokens,
|
|
618
|
+
completionTokens: measured.completionTokens - observed.completionTokens,
|
|
619
|
+
totalTokens: measured.totalTokens - observed.totalTokens,
|
|
620
|
+
cachedTokens: measured.cachedTokens - observed.cachedTokens,
|
|
621
|
+
cacheWriteTokens: measured.cacheWriteTokens - observed.cacheWriteTokens,
|
|
622
|
+
...(measured.reasoningTokens === undefined
|
|
623
|
+
? {}
|
|
624
|
+
: { reasoningTokens: measured.reasoningTokens - (observed.reasoningTokens ?? 0) }),
|
|
625
|
+
}
|
|
626
|
+
const previous = this.requireOwnUsage(request.runId)
|
|
627
|
+
const next = addUsage(previous, delta)
|
|
628
|
+
this.checkAggregateReplacement(previous, next)
|
|
629
|
+
this.node.usage[request.runId] = next
|
|
630
|
+
request.usage = measured
|
|
631
|
+
}
|
|
632
|
+
private children(node: TokenBudgetAccountSnapshot): TokenBudgetAccountSnapshot[] {
|
|
633
|
+
return [...this.ledger.accounts.values()].filter((account) => account.parentId === node.id)
|
|
634
|
+
}
|
|
635
|
+
private descendants(node: TokenBudgetAccountSnapshot): Set<string> {
|
|
636
|
+
return new Set([
|
|
637
|
+
node.id,
|
|
638
|
+
...this.children(node).flatMap((child) => [...this.descendants(child)]),
|
|
639
|
+
])
|
|
640
|
+
}
|
|
641
|
+
private hasRequest(node: TokenBudgetAccountSnapshot): boolean {
|
|
642
|
+
return [...this.ledger.requests.values()].some((request) => request.accountId === node.id)
|
|
643
|
+
}
|
|
644
|
+
private totals(node: TokenBudgetAccountSnapshot): AccountTotals {
|
|
645
|
+
const children = this.children(node).map((child) => ({ child, totals: this.totals(child) }))
|
|
646
|
+
const own = Object.values(node.usage).reduce((sum, usage) => sum + usage.totalTokens, 0)
|
|
647
|
+
return {
|
|
648
|
+
own,
|
|
649
|
+
tree: own + children.reduce((sum, { totals }) => sum + totals.tree, 0),
|
|
650
|
+
reserved: children.reduce(
|
|
651
|
+
(sum, { child, totals }) =>
|
|
652
|
+
sum + (totals.closed ? 0 : Math.max(0, child.limit - totals.tree)),
|
|
653
|
+
0,
|
|
654
|
+
),
|
|
655
|
+
closed:
|
|
656
|
+
node.settled && !this.hasRequest(node) && children.every(({ totals }) => totals.closed),
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
private free(node: TokenBudgetAccountSnapshot): number {
|
|
660
|
+
if (node.limit === 0) return Number.POSITIVE_INFINITY
|
|
661
|
+
const totals = this.totals(node)
|
|
662
|
+
return node.limit - totals.tree - totals.reserved
|
|
663
|
+
}
|
|
664
|
+
private requireRequest(requestId: string): TokenBudgetRequestSnapshot {
|
|
665
|
+
const request = this.ledger.requests.get(requestId)
|
|
666
|
+
if (!request || request.accountId !== this.accountId)
|
|
667
|
+
throw new Error('Unknown token budget request')
|
|
668
|
+
return request
|
|
669
|
+
}
|
|
670
|
+
private checkAggregateReplacement(previous: TokenUsage, next: TokenUsage): void {
|
|
671
|
+
count(
|
|
672
|
+
this.totals(this.requireAccount(this.ledger.rootAccountId)).tree -
|
|
673
|
+
previous.totalTokens +
|
|
674
|
+
next.totalTokens,
|
|
675
|
+
'tree usage',
|
|
676
|
+
)
|
|
677
|
+
}
|
|
678
|
+
private persist(): void {
|
|
679
|
+
if (!this.ledger.persistence) return
|
|
680
|
+
const snapshot = this.snapshot()
|
|
681
|
+
const persistence = this.ledger.persistence
|
|
682
|
+
this.ledger.writes = this.ledger.writes.then(async () => {
|
|
683
|
+
if (this.ledger.persistenceError !== undefined) return
|
|
684
|
+
try {
|
|
685
|
+
await persistence.save(snapshot)
|
|
686
|
+
} catch (error) {
|
|
687
|
+
this.ledger.persistenceError = error ?? new Error('Token budget persistence failed')
|
|
688
|
+
this.ledger.poisoned = true
|
|
689
|
+
}
|
|
690
|
+
})
|
|
691
|
+
}
|
|
692
|
+
}
|