@wardby/cli 0.6.0 → 0.8.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/.env.example +5 -5
- package/README.md +6 -1
- package/deploy/local/docker-compose.quickstart-coding.yml +19 -0
- package/dist/claude-coding-worker/driver.d.ts +33 -3
- package/dist/claude-coding-worker/driver.js +151 -21
- package/dist/claude-coding-worker/main.js +38 -6
- package/dist/claude-coding-worker/sdk.js +29 -2
- package/dist/claude-coding-worker/tool-socket.d.ts +27 -0
- package/dist/claude-coding-worker/tool-socket.js +111 -0
- package/dist/claude-coding-worker/transcript.d.ts +18 -0
- package/dist/claude-coding-worker/transcript.js +77 -0
- package/dist/claude-plugin/command.d.ts +61 -0
- package/dist/claude-plugin/command.js +376 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +3 -1
- package/dist/cli.js +71 -13
- package/dist/coding/code-host.d.ts +16 -0
- package/dist/coding/code-host.js +60 -0
- package/dist/coding/continuation-wording.d.ts +35 -0
- package/dist/coding/continuation-wording.js +73 -0
- package/dist/coding/linked-issue-note.d.ts +23 -0
- package/dist/coding/linked-issue-note.js +47 -0
- package/dist/coding/mcp-token.d.ts +12 -0
- package/dist/coding/mcp-token.js +26 -0
- package/dist/coding/merge-wording.d.ts +28 -0
- package/dist/coding/merge-wording.js +78 -0
- package/dist/coding/pr-description-note.d.ts +9 -0
- package/dist/coding/pr-description-note.js +32 -0
- package/dist/coding/profile.d.ts +46 -126
- package/dist/coding/profile.js +45 -4
- package/dist/coding/protected-path-wording.d.ts +8 -0
- package/dist/coding/protected-path-wording.js +1 -1
- package/dist/coding/protocol.d.ts +264 -346
- package/dist/coding/protocol.js +237 -12
- package/dist/coding/redact.d.ts +3 -0
- package/dist/coding/redact.js +17 -0
- package/dist/coding/registry/allowlist.d.ts +12 -0
- package/dist/coding/registry/allowlist.js +14 -1
- package/dist/coding/registry/audit-mode.d.ts +153 -0
- package/dist/coding/registry/audit-mode.js +304 -0
- package/dist/coding/registry/lockfile-shared.d.ts +36 -0
- package/dist/coding/registry/lockfile-shared.js +128 -0
- package/dist/coding/registry/lockfiles.js +1 -1
- package/dist/coding/registry/npm-lockfile-formats.d.ts +11 -0
- package/dist/coding/registry/npm-lockfile-formats.js +41 -0
- package/dist/coding/registry/npm-lockfile.d.ts +8 -4
- package/dist/coding/registry/npm-lockfile.js +4 -6
- package/dist/coding/registry/npm.js +78 -8
- package/dist/coding/registry/pnpm-lockfile.d.ts +17 -0
- package/dist/coding/registry/pnpm-lockfile.js +189 -0
- package/dist/coding/registry/pypi-plan.d.ts +50 -0
- package/dist/coding/registry/pypi-plan.js +406 -0
- package/dist/coding/registry/pypi-requirements.d.ts +58 -0
- package/dist/coding/registry/pypi-requirements.js +155 -0
- package/dist/coding/registry/pypi.d.ts +3 -27
- package/dist/coding/registry/pypi.js +40 -154
- package/dist/coding/registry/refusal-hints.d.ts +20 -0
- package/dist/coding/registry/refusal-hints.js +61 -0
- package/dist/coding/registry/refusal-wording.d.ts +11 -0
- package/dist/coding/registry/refusal-wording.js +56 -0
- package/dist/coding/registry/report.d.ts +70 -8
- package/dist/coding/registry/report.js +112 -5
- package/dist/coding/registry/types.d.ts +58 -8
- package/dist/coding/registry/yarn-lockfile.d.ts +30 -0
- package/dist/coding/registry/yarn-lockfile.js +367 -0
- package/dist/coding/services/catalog.d.ts +37 -351
- package/dist/coding/transcript/claude-code.d.ts +13 -0
- package/dist/coding/transcript/claude-code.js +273 -0
- package/dist/coding/transcript/download-protocol.d.ts +30 -0
- package/dist/coding/transcript/download-protocol.js +18 -0
- package/dist/coding/transcript/format.d.ts +20 -0
- package/dist/coding/transcript/format.js +32 -0
- package/dist/coding/transcript/recorder.d.ts +52 -0
- package/dist/coding/transcript/recorder.js +225 -0
- package/dist/coding/transcript/resume/checkout.d.ts +31 -0
- package/dist/coding/transcript/resume/checkout.js +203 -0
- package/dist/coding/transcript/resume/command.d.ts +17 -0
- package/dist/coding/transcript/resume/command.js +176 -0
- package/dist/coding/transcript/resume/download.d.ts +20 -0
- package/dist/coding/transcript/resume/download.js +153 -0
- package/dist/coding/transcript/resume/launch.d.ts +38 -0
- package/dist/coding/transcript/resume/launch.js +128 -0
- package/dist/coding/transcript/resume/session-file.d.ts +13 -0
- package/dist/coding/transcript/resume/session-file.js +79 -0
- package/dist/coding/transcript/resume/stand-in.d.ts +11 -0
- package/dist/coding/transcript/resume/stand-in.js +49 -0
- package/dist/coding-worker/artifact.d.ts +8 -3
- package/dist/coding-worker/artifact.js +3 -1
- package/dist/coding-worker/debug-trace.d.ts +9 -6
- package/dist/coding-worker/debug-trace.js +16 -19
- package/dist/coding-worker/dependency-cache.d.mts +229 -0
- package/dist/coding-worker/dependency-cache.mjs +2123 -0
- package/dist/coding-worker/driver.d.ts +4 -3
- package/dist/coding-worker/driver.js +124 -39
- package/dist/coding-worker/errors.d.ts +1 -0
- package/dist/coding-worker/errors.js +32 -0
- package/dist/coding-worker/main.js +5 -1
- package/dist/coding-worker/mcp-config.d.ts +27 -0
- package/dist/coding-worker/mcp-config.js +32 -0
- package/dist/coding-worker/sdk.d.ts +36 -0
- package/dist/coding-worker/sdk.js +28 -1
- package/dist/coding-worker/task-note.d.ts +11 -0
- package/dist/coding-worker/task-note.js +13 -0
- package/dist/coding-worker/types.d.ts +15 -0
- package/dist/config/dependency-cache.d.ts +23 -0
- package/dist/config/dependency-cache.js +32 -0
- package/dist/config/mcp-call-limit.d.ts +8 -0
- package/dist/config/mcp-call-limit.js +18 -0
- package/dist/config/mcp-servers.d.ts +8 -0
- package/dist/config/mcp-servers.js +12 -0
- package/dist/config/providers.d.ts +12 -3
- package/dist/config/providers.js +55 -5
- package/dist/config/test-simulators.d.ts +92 -0
- package/dist/config/test-simulators.js +333 -0
- package/dist/config/tool-timeout.d.ts +1 -0
- package/dist/config/tool-timeout.js +18 -0
- package/dist/core/async-context.d.ts +2 -0
- package/dist/core/async-context.js +12 -0
- package/dist/core/budget-events.d.ts +108 -0
- package/dist/core/budget-events.js +346 -0
- package/dist/core/budget-groups.d.ts +50 -1
- package/dist/core/budget-groups.js +236 -27
- package/dist/core/budget-stopped-head.d.ts +46 -0
- package/dist/core/budget-stopped-head.js +81 -0
- package/dist/core/budget-topup.d.ts +51 -0
- package/dist/core/budget-topup.js +156 -0
- package/dist/core/budget-wording.d.ts +16 -2
- package/dist/core/budget-wording.js +40 -2
- package/dist/core/coding-mcp.d.ts +36 -0
- package/dist/core/coding-mcp.js +253 -0
- package/dist/core/coding-queue.d.ts +2 -1
- package/dist/core/coding-queue.js +7 -0
- package/dist/core/coding-repository.d.ts +46 -0
- package/dist/core/coding-repository.js +92 -0
- package/dist/core/cron.d.ts +19 -1
- package/dist/core/cron.js +17 -0
- package/dist/core/datastores.js +1 -1
- package/dist/core/deferred-coding.d.ts +14 -0
- package/dist/core/deferred-coding.js +62 -0
- package/dist/core/delegation-child.d.ts +25 -0
- package/dist/core/delegation-child.js +37 -0
- package/dist/core/delegation-slots.d.ts +27 -0
- package/dist/core/delegation-slots.js +44 -0
- package/dist/core/dispatch.d.ts +48 -2
- package/dist/core/dispatch.js +166 -66
- package/dist/core/engine-native.js +1 -1
- package/dist/core/error-code.d.ts +38 -0
- package/dist/core/error-code.js +169 -0
- package/dist/core/grants.d.ts +46 -8
- package/dist/core/grants.js +99 -8
- package/dist/core/host-events.d.ts +18 -2
- package/dist/core/host-events.js +291 -27
- package/dist/core/host-status.d.ts +85 -3
- package/dist/core/host-status.js +300 -22
- package/dist/core/issue-bridge.d.ts +50 -6
- package/dist/core/issue-bridge.js +124 -33
- package/dist/core/issue-events.d.ts +1 -1
- package/dist/core/issue-events.js +4 -10
- package/dist/core/issue-status.d.ts +1 -1
- package/dist/core/issue-status.js +58 -29
- package/dist/core/logger.d.ts +5 -0
- package/dist/core/logger.js +14 -1
- package/dist/core/lost-run-children.d.ts +6 -0
- package/dist/core/lost-run-children.js +32 -0
- package/dist/core/mark-ready.d.ts +34 -0
- package/dist/core/mark-ready.js +152 -0
- package/dist/core/markdown-code.d.ts +8 -0
- package/dist/core/markdown-code.js +107 -0
- package/dist/core/mcp-client/client.d.ts +47 -0
- package/dist/core/mcp-client/client.js +141 -0
- package/dist/core/mcp-client/jsonrpc.d.ts +15 -0
- package/dist/core/mcp-client/jsonrpc.js +54 -0
- package/dist/core/mcp-client/results.d.ts +13 -0
- package/dist/core/mcp-client/results.js +42 -0
- package/dist/core/mcp-servers.d.ts +90 -0
- package/dist/core/mcp-servers.js +230 -0
- package/dist/core/mcp-tools.d.ts +108 -0
- package/dist/core/mcp-tools.js +287 -0
- package/dist/core/memory-tools.js +7 -0
- package/dist/core/merge-order-check.d.ts +2 -1
- package/dist/core/merge-order-check.js +44 -18
- package/dist/core/notification-dispatcher.js +2 -1
- package/dist/core/notification-templates.d.ts +1 -7
- package/dist/core/notification-templates.js +36 -2
- package/dist/core/pull-request-state-sync.js +2 -2
- package/dist/core/reconciler.d.ts +4 -3
- package/dist/core/reconciler.js +8 -1
- package/dist/core/related-pull-requests.d.ts +11 -3
- package/dist/core/related-pull-requests.js +49 -20
- package/dist/core/reply-parts.d.ts +21 -0
- package/dist/core/reply-parts.js +73 -0
- package/dist/core/review-fix.d.ts +1 -1
- package/dist/core/review-fix.js +4 -3
- package/dist/core/review-host-checks.d.ts +1 -1
- package/dist/core/review-host-tools.d.ts +3 -0
- package/dist/core/review-host-tools.js +102 -14
- package/dist/core/review-reads.d.ts +55 -0
- package/dist/core/review-reads.js +233 -0
- package/dist/core/run-status.d.ts +12 -0
- package/dist/core/run-status.js +12 -0
- package/dist/core/runner.d.ts +145 -49
- package/dist/core/runner.js +719 -216
- package/dist/core/schedule-start.d.ts +12 -0
- package/dist/core/schedule-start.js +14 -0
- package/dist/core/secret-broker-config.d.ts +12 -74
- package/dist/core/secret-broker-config.js +1 -1
- package/dist/core/secret-guard.d.ts +49 -0
- package/dist/core/secret-guard.js +119 -0
- package/dist/core/secret-guarded-stores.d.ts +12 -0
- package/dist/core/secret-guarded-stores.js +41 -0
- package/dist/core/secret-spans.d.ts +50 -0
- package/dist/core/secret-spans.js +94 -0
- package/dist/core/secrets.d.ts +11 -1
- package/dist/core/secrets.js +27 -1
- package/dist/core/serializable-retry.d.ts +18 -0
- package/dist/core/serializable-retry.js +43 -0
- package/dist/core/stop-run.d.ts +46 -0
- package/dist/core/stop-run.js +61 -0
- package/dist/core/tool-admin.d.ts +5 -1
- package/dist/core/tool-admin.js +7 -2
- package/dist/core/tool-names.d.ts +13 -0
- package/dist/core/tool-names.js +20 -0
- package/dist/core/webhooks.d.ts +4 -0
- package/dist/core/webhooks.js +68 -49
- package/dist/core/workflow-events.d.ts +14 -2
- package/dist/core/workflow-events.js +4 -0
- package/dist/core/workflow-run-events.d.ts +5 -3
- package/dist/core/workflow-run-events.js +45 -9
- package/dist/core/zod-json-schema.d.ts +4 -0
- package/dist/core/zod-json-schema.js +39 -0
- package/dist/core/zod3-window.d.ts +35 -0
- package/dist/core/zod3-window.js +124 -0
- package/dist/generated/prisma/browser.d.ts +65 -3
- package/dist/generated/prisma/client.d.ts +65 -3
- package/dist/generated/prisma/commonInputTypes.d.ts +266 -106
- package/dist/generated/prisma/enums.d.ts +10 -0
- package/dist/generated/prisma/enums.js +8 -0
- package/dist/generated/prisma/internal/class.d.ts +110 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +957 -9
- package/dist/generated/prisma/internal/prismaNamespace.js +166 -2
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +174 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +166 -2
- package/dist/generated/prisma/models/Agent.d.ts +377 -8
- package/dist/generated/prisma/models/AgentMcpServer.d.ts +1412 -0
- package/dist/generated/prisma/models/AgentMcpServer.js +1 -0
- package/dist/generated/prisma/models/AgentRepository.d.ts +81 -1
- package/dist/generated/prisma/models/AuthUser.d.ts +1 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +228 -7
- package/dist/generated/prisma/models/CodingProxyRequest.d.ts +207 -3
- package/dist/generated/prisma/models/CodingProxySession.d.ts +232 -2
- package/dist/generated/prisma/models/CodingRun.d.ts +1712 -1
- package/dist/generated/prisma/models/DependencySnapshot.d.ts +1668 -0
- package/dist/generated/prisma/models/DependencySnapshot.js +1 -0
- package/dist/generated/prisma/models/HostProvider.d.ts +2176 -0
- package/dist/generated/prisma/models/HostProvider.js +1 -0
- package/dist/generated/prisma/models/HostProviderCredential.d.ts +1285 -0
- package/dist/generated/prisma/models/HostProviderCredential.js +1 -0
- package/dist/generated/prisma/models/HostProviderOperation.d.ts +1470 -0
- package/dist/generated/prisma/models/HostProviderOperation.js +1 -0
- package/dist/generated/prisma/models/HostProviderTrustedAccount.d.ts +1145 -0
- package/dist/generated/prisma/models/HostProviderTrustedAccount.js +1 -0
- package/dist/generated/prisma/models/IssuePullRequest.d.ts +6 -2
- package/dist/generated/prisma/models/LocalReview.d.ts +40 -1
- package/dist/generated/prisma/models/McpServer.d.ts +1500 -0
- package/dist/generated/prisma/models/McpServer.js +1 -0
- package/dist/generated/prisma/models/NativeGatewayCall.d.ts +77 -1
- package/dist/generated/prisma/models/NativeGatewaySession.d.ts +35 -1
- package/dist/generated/prisma/models/Principal.d.ts +167 -0
- package/dist/generated/prisma/models/RegistryAllowance.d.ts +41 -1
- package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +75 -1
- package/dist/generated/prisma/models/RegistryAuditRecord.d.ts +1364 -0
- package/dist/generated/prisma/models/RegistryAuditRecord.js +1 -0
- package/dist/generated/prisma/models/ResourceGrant.d.ts +1 -1
- package/dist/generated/prisma/models/Run.d.ts +738 -2
- package/dist/generated/prisma/models/RunBudgetEvent.d.ts +1211 -0
- package/dist/generated/prisma/models/RunBudgetEvent.js +1 -0
- package/dist/generated/prisma/models/RunTranscript.d.ts +1339 -0
- package/dist/generated/prisma/models/RunTranscript.js +1 -0
- package/dist/generated/prisma/models/Secret.d.ts +387 -0
- package/dist/generated/prisma/models/Tool.d.ts +223 -1
- package/dist/generated/prisma/models.d.ts +10 -0
- package/dist/help/search.js +12 -3
- package/dist/help-index.json +2033 -110
- package/dist/import/create.js +48 -4
- package/dist/import/neutral-schema.d.ts +51 -281
- package/dist/import/neutral-schema.js +7 -4
- package/dist/knowledge/check.d.ts +1 -1
- package/dist/knowledge/cli.d.ts +11 -1
- package/dist/knowledge/cli.js +37 -4
- package/dist/knowledge/concept.d.ts +3 -19
- package/dist/knowledge/location.d.ts +31 -0
- package/dist/knowledge/location.js +78 -0
- package/dist/mcp/auth/access.d.ts +10 -2
- package/dist/mcp/auth/access.js +15 -1
- package/dist/mcp/auth/grants-cli.d.ts +6 -4
- package/dist/mcp/auth/grants-cli.js +42 -3
- package/dist/mcp/auth/ownership.d.ts +7 -7
- package/dist/mcp/auth/resource-server.d.ts +3 -1
- package/dist/mcp/auth/resource-server.js +17 -2
- package/dist/mcp/auth/self-hosted/credentials.d.ts +3 -3
- package/dist/mcp/auth/self-hosted/session.d.ts +2 -2
- package/dist/mcp/context.d.ts +3 -0
- package/dist/mcp/host-events/github-ingress.d.ts +2 -0
- package/dist/mcp/host-events/github-ingress.js +2 -1
- package/dist/mcp/index.d.ts +6 -0
- package/dist/mcp/index.js +69 -10
- package/dist/mcp/server.d.ts +13 -0
- package/dist/mcp/server.js +33 -14
- package/dist/mcp/tools/agents.d.ts +4 -0
- package/dist/mcp/tools/agents.js +295 -28
- package/dist/mcp/tools/datastore.js +17 -8
- package/dist/mcp/tools/grants.js +73 -29
- package/dist/mcp/tools/host-providers.d.ts +2 -0
- package/dist/mcp/tools/host-providers.js +215 -0
- package/dist/mcp/tools/mcp-servers.d.ts +26 -0
- package/dist/mcp/tools/mcp-servers.js +481 -0
- package/dist/mcp/tools/memory.js +11 -1
- package/dist/mcp/tools/notification-channels.js +2 -2
- package/dist/mcp/tools/packages.d.ts +2 -0
- package/dist/mcp/tools/packages.js +195 -0
- package/dist/mcp/tools/repositories.js +65 -12
- package/dist/mcp/tools/run-access.d.ts +16 -0
- package/dist/mcp/tools/run-access.js +25 -0
- package/dist/mcp/tools/run-transcript-download.d.ts +30 -0
- package/dist/mcp/tools/run-transcript-download.js +129 -0
- package/dist/mcp/tools/run-transcript.d.ts +19 -0
- package/dist/mcp/tools/run-transcript.js +70 -0
- package/dist/mcp/tools/runs.js +209 -27
- package/dist/mcp/tools/scheduling.js +3 -1
- package/dist/mcp/tools/secret-elicitation-form.js +5 -0
- package/dist/mcp/tools/secret-elicitation-server.d.ts +6 -0
- package/dist/mcp/tools/secret-elicitation-server.js +10 -61
- package/dist/mcp/tools/secret-elicitation.d.ts +2 -0
- package/dist/mcp/tools/secret-elicitation.js +25 -2
- package/dist/mcp/tools/secrets.js +40 -6
- package/dist/mcp/tools/stop-run.d.ts +2 -0
- package/dist/mcp/tools/stop-run.js +82 -0
- package/dist/mcp/tools/subagents.js +2 -2
- package/dist/mcp/tools/tools.d.ts +7 -5
- package/dist/mcp/tools/tools.js +64 -16
- package/dist/mcp/tools/trigger.js +28 -6
- package/dist/mcp/transport/http-limits.d.ts +2 -0
- package/dist/mcp/transport/http-limits.js +6 -2
- package/dist/mcp/transport/stdio-link-host.d.ts +20 -0
- package/dist/mcp/transport/stdio-link-host.js +67 -0
- package/dist/mcp/transport/streamable-http.d.ts +13 -0
- package/dist/mcp/transport/streamable-http.js +41 -1
- package/dist/mcp/webhooks/ingress.js +10 -0
- package/dist/native-worker/docker-launcher.d.ts +38 -2
- package/dist/native-worker/docker-launcher.js +65 -5
- package/dist/native-worker/gateway-server.d.ts +55 -3
- package/dist/native-worker/gateway-server.js +152 -34
- package/dist/native-worker/gateway.d.ts +29 -1
- package/dist/native-worker/gateway.js +74 -4
- package/dist/native-worker/http-server.d.ts +7 -1
- package/dist/native-worker/http-server.js +62 -7
- package/dist/native-worker/http-transport.js +1 -1
- package/dist/native-worker/kubernetes-launcher.d.ts +7 -3
- package/dist/native-worker/kubernetes-launcher.js +43 -3
- package/dist/native-worker/launch.d.ts +18 -1
- package/dist/native-worker/launch.js +6 -1
- package/dist/native-worker/ledger.d.ts +24 -4
- package/dist/native-worker/ledger.js +62 -8
- package/dist/native-worker/loopback.js +3 -8
- package/dist/native-worker/main.d.ts +2 -1
- package/dist/native-worker/main.js +14 -3
- package/dist/native-worker/protocol.d.ts +130 -577
- package/dist/native-worker/protocol.js +51 -2
- package/dist/native-worker/sandbox-executor.d.ts +17 -2
- package/dist/native-worker/sandbox-executor.js +78 -4
- package/dist/native-worker/stderr-redaction.d.ts +77 -0
- package/dist/native-worker/stderr-redaction.js +237 -0
- package/dist/native-worker/stdio.d.ts +1 -1
- package/dist/native-worker/stdio.js +6 -5
- package/dist/native-worker/warm-pool.d.ts +2 -2
- package/dist/native-worker/warm-pool.js +4 -4
- package/dist/native-worker/worker.js +28 -2
- package/dist/observability/metrics.js +6 -1
- package/dist/providers/auth/self-hosted.d.ts +1 -1
- package/dist/providers/coding-proxy/capability-hash.d.ts +2 -0
- package/dist/providers/coding-proxy/capability-hash.js +5 -0
- package/dist/providers/coding-proxy/dependency-cache/eviction.d.ts +29 -0
- package/dist/providers/coding-proxy/dependency-cache/eviction.js +87 -0
- package/dist/providers/coding-proxy/dependency-cache/key.d.ts +33 -0
- package/dist/providers/coding-proxy/dependency-cache/key.js +86 -0
- package/dist/providers/coding-proxy/dependency-cache/model-request-seal.d.ts +18 -0
- package/dist/providers/coding-proxy/dependency-cache/model-request-seal.js +40 -0
- package/dist/providers/coding-proxy/dependency-cache/prisma-store.d.ts +25 -0
- package/dist/providers/coding-proxy/dependency-cache/prisma-store.js +131 -0
- package/dist/providers/coding-proxy/dependency-cache/service.d.ts +70 -0
- package/dist/providers/coding-proxy/dependency-cache/service.js +409 -0
- package/dist/providers/coding-proxy/dependency-cache/store.d.ts +85 -0
- package/dist/providers/coding-proxy/dependency-cache/store.js +95 -0
- package/dist/providers/coding-proxy/hold.d.ts +32 -0
- package/dist/providers/coding-proxy/hold.js +86 -0
- package/dist/providers/coding-proxy/interrupted.d.ts +70 -0
- package/dist/providers/coding-proxy/interrupted.js +104 -0
- package/dist/providers/coding-proxy/mcp-route.d.ts +34 -0
- package/dist/providers/coding-proxy/mcp-route.js +166 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +17 -2
- package/dist/providers/coding-proxy/memory-ledger.js +99 -8
- package/dist/providers/coding-proxy/metering.d.ts +16 -0
- package/dist/providers/coding-proxy/metering.js +27 -4
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +16 -2
- package/dist/providers/coding-proxy/prisma-ledger.js +209 -69
- package/dist/providers/coding-proxy/proxy.d.ts +21 -2
- package/dist/providers/coding-proxy/proxy.js +300 -48
- package/dist/providers/coding-proxy/registry/audit.d.ts +53 -10
- package/dist/providers/coding-proxy/registry/audit.js +94 -17
- package/dist/providers/coding-proxy/registry/lockfile-parse-worker.d.ts +36 -0
- package/dist/providers/coding-proxy/registry/lockfile-parse-worker.js +67 -0
- package/dist/providers/coding-proxy/registry/lockfile-parse.d.ts +16 -0
- package/dist/providers/coding-proxy/registry/lockfile-parse.js +110 -0
- package/dist/providers/coding-proxy/registry/plan.d.ts +9 -0
- package/dist/providers/coding-proxy/registry/plan.js +201 -48
- package/dist/providers/coding-proxy/registry/prisma-store.d.ts +11 -5
- package/dist/providers/coding-proxy/registry/prisma-store.js +120 -17
- package/dist/providers/coding-proxy/registry/service.d.ts +97 -16
- package/dist/providers/coding-proxy/registry/service.js +579 -112
- package/dist/providers/coding-proxy/registry/store.d.ts +84 -9
- package/dist/providers/coding-proxy/registry/store.js +54 -1
- package/dist/providers/coding-proxy/runtime.d.ts +10 -0
- package/dist/providers/coding-proxy/runtime.js +109 -16
- package/dist/providers/coding-proxy/server.d.ts +9 -1
- package/dist/providers/coding-proxy/server.js +55 -1
- package/dist/providers/coding-proxy/types.d.ts +78 -1
- package/dist/providers/datastore/types.d.ts +5 -0
- package/dist/providers/engine/types.d.ts +17 -2
- package/dist/providers/engine/types.js +17 -0
- package/dist/providers/executor/composition.d.ts +10 -3
- package/dist/providers/executor/composition.js +52 -7
- package/dist/providers/executor/container.d.ts +394 -9
- package/dist/providers/executor/container.js +1210 -96
- package/dist/providers/executor/dbos-status.d.ts +31 -0
- package/dist/providers/executor/dbos-status.js +64 -0
- package/dist/providers/executor/dbos.d.ts +50 -1
- package/dist/providers/executor/dbos.js +207 -20
- package/dist/providers/executor/deferred.d.ts +12 -24
- package/dist/providers/executor/deferred.js +10 -29
- package/dist/providers/executor/in-process.d.ts +9 -1
- package/dist/providers/executor/in-process.js +14 -4
- package/dist/providers/executor/linked-issue-reader.d.ts +16 -0
- package/dist/providers/executor/linked-issue-reader.js +30 -0
- package/dist/providers/executor/routing.d.ts +2 -0
- package/dist/providers/executor/routing.js +4 -0
- package/dist/providers/executor/types.d.ts +13 -0
- package/dist/providers/host-tools/invoke.d.ts +37 -0
- package/dist/providers/host-tools/invoke.js +117 -0
- package/dist/providers/host-tools/operations.d.ts +65 -0
- package/dist/providers/host-tools/operations.js +149 -0
- package/dist/providers/host-tools/registry.d.ts +79 -0
- package/dist/providers/host-tools/registry.js +362 -0
- package/dist/providers/host-tools/request-guard.d.ts +17 -0
- package/dist/providers/host-tools/request-guard.js +99 -0
- package/dist/providers/host-tools/self-test.d.ts +64 -0
- package/dist/providers/host-tools/self-test.js +142 -0
- package/dist/providers/index.d.ts +2 -1
- package/dist/providers/issue-tracker/types.d.ts +17 -0
- package/dist/providers/issue-tracker/types.js +39 -1
- package/dist/providers/jobs/claude-tool-setup.d.ts +4 -0
- package/dist/providers/jobs/claude-tool-setup.js +5 -2
- package/dist/providers/jobs/docker-isolation.d.ts +10 -2
- package/dist/providers/jobs/docker-isolation.js +47 -8
- package/dist/providers/jobs/docker.d.ts +9 -0
- package/dist/providers/jobs/docker.js +60 -11
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +2 -0
- package/dist/providers/jobs/fake-kubernetes-api.js +3 -0
- package/dist/providers/jobs/kubernetes-api.d.ts +8 -0
- package/dist/providers/jobs/kubernetes-api.js +10 -0
- package/dist/providers/jobs/kubernetes-client.js +10 -1
- package/dist/providers/jobs/kubernetes-isolation.d.ts +5 -1
- package/dist/providers/jobs/kubernetes-isolation.js +32 -3
- package/dist/providers/jobs/kubernetes.d.ts +11 -1
- package/dist/providers/jobs/kubernetes.js +111 -17
- package/dist/providers/jobs/types.d.ts +39 -0
- package/dist/providers/jobs/types.js +13 -1
- package/dist/providers/llm/anthropic.js +5 -1
- package/dist/providers/llm/bedrock.d.ts +0 -12
- package/dist/providers/llm/bedrock.js +4 -0
- package/dist/providers/llm/claude-messages.d.ts +3 -1
- package/dist/providers/llm/claude-messages.js +4 -1
- package/dist/providers/llm/claude-provider.d.ts +2 -2
- package/dist/providers/llm/claude-provider.js +2 -2
- package/dist/providers/llm/openai.d.ts +2 -2
- package/dist/providers/llm/openai.js +6 -2
- package/dist/providers/llm/routing.d.ts +2 -2
- package/dist/providers/llm/routing.js +2 -2
- package/dist/providers/llm/types.d.ts +12 -2
- package/dist/providers/review-host/github-events.d.ts +9 -1
- package/dist/providers/review-host/github-events.js +34 -12
- package/dist/providers/review-host/github.d.ts +29 -2
- package/dist/providers/review-host/github.js +224 -29
- package/dist/providers/review-host/local.d.ts +8 -4
- package/dist/providers/review-host/local.js +49 -31
- package/dist/providers/review-host/patch-pages.d.ts +41 -0
- package/dist/providers/review-host/patch-pages.js +176 -0
- package/dist/providers/review-host/review-format.d.ts +19 -1
- package/dist/providers/review-host/review-format.js +38 -7
- package/dist/providers/review-host/types.d.ts +140 -6
- package/dist/providers/review-host/types.js +9 -1
- package/dist/providers/storage/gcs.d.ts +63 -0
- package/dist/providers/storage/gcs.js +69 -0
- package/dist/providers/storage/index.d.ts +15 -0
- package/dist/providers/storage/index.js +14 -0
- package/dist/providers/storage/local.d.ts +24 -0
- package/dist/providers/storage/local.js +92 -0
- package/dist/providers/storage/meter.d.ts +14 -0
- package/dist/providers/storage/meter.js +34 -0
- package/dist/providers/storage/s3.d.ts +41 -0
- package/dist/providers/storage/s3.js +107 -0
- package/dist/providers/storage/types.d.ts +37 -3
- package/dist/providers/storage/types.js +21 -4
- package/dist/providers/vcs/git.d.ts +39 -4
- package/dist/providers/vcs/git.js +339 -26
- package/dist/providers/vcs/github-remote.d.ts +26 -3
- package/dist/providers/vcs/github-remote.js +124 -14
- package/dist/providers/vcs/github.d.ts +183 -44
- package/dist/providers/vcs/github.js +455 -56
- package/dist/providers/vcs/index.d.ts +1 -1
- package/dist/providers/vcs/index.js +9 -2
- package/dist/providers/vcs/pr-description.d.ts +127 -0
- package/dist/providers/vcs/pr-description.js +270 -0
- package/dist/providers/vcs/remote.d.ts +37 -1
- package/dist/providers/vcs/remote.js +7 -1
- package/dist/providers/vcs/routing.d.ts +16 -8
- package/dist/providers/vcs/routing.js +27 -6
- package/dist/providers/vcs/types.d.ts +150 -5
- package/dist/quickstart/coding-seed.d.ts +14 -3
- package/dist/quickstart/coding-seed.js +213 -4
- package/dist/quickstart/index.d.ts +5 -0
- package/dist/quickstart/index.js +21 -0
- package/dist/quickstart/reviewer-prompt.d.ts +1 -1
- package/dist/quickstart/reviewer-prompt.js +6 -6
- package/dist/quickstart-images.json +1 -1
- package/dist/sandbox/eval-core.d.ts +12 -2
- package/dist/sandbox/eval-core.js +20 -4
- package/dist/sandbox/generated/zod.bundle.js +62 -1
- package/dist/sandbox/generated/zod3.bundle.js +5 -0
- package/dist/sandbox/host-functions.d.ts +5 -0
- package/dist/sandbox/host-functions.js +13 -13
- package/dist/sandbox/limits.d.ts +4 -1
- package/dist/sandbox/limits.js +4 -1
- package/dist/sandbox/run-in-sandbox.d.ts +6 -0
- package/dist/sandbox/run-in-sandbox.js +14 -2
- package/dist/sandbox/safe-fetch.d.ts +2 -0
- package/dist/sandbox/safe-fetch.js +1 -1
- package/dist/sandbox/secret-broker.d.ts +1 -0
- package/dist/sandbox/secret-broker.js +4 -5
- package/dist/sandbox/tool-capabilities.d.ts +10 -30
- package/dist/sandbox/tool-timeout.d.ts +32 -0
- package/dist/sandbox/tool-timeout.js +52 -0
- package/dist/sandbox/user-tool.d.ts +8 -1
- package/dist/sandbox/user-tool.js +9 -3
- package/dist/sandbox/zod-params.d.ts +37 -5
- package/dist/sandbox/zod-params.js +181 -26
- package/dist/serve.js +8 -1
- package/dist/viewer/api-schema.d.ts +349 -2627
- package/dist/viewer/api-schema.js +7 -0
- package/dist/viewer/build-schemas.js +6 -2
- package/dist/viewer/graph.js +2 -1
- package/dist/viewer/openapi.js +2 -4
- package/dist/wardby-bin.js +15 -0
- package/docs/README.md +11 -1
- package/docs/agent-recipes.md +95 -24
- package/docs/architecture-runtime.md +1 -1
- package/docs/claude-code-plugin.md +164 -0
- package/docs/code-review-agents.md +557 -41
- package/docs/coding-agent-setup.md +566 -8
- package/docs/coding-packages.md +1033 -90
- package/docs/coding-services.md +27 -12
- package/docs/coding-worker-byo-images.md +24 -2
- package/docs/coding-worker-isolation.md +183 -37
- package/docs/debugging-agent-runs.md +242 -0
- package/docs/getting-started-gke.md +129 -141
- package/docs/getting-started-identity-provider.md +53 -38
- package/docs/getting-started.md +17 -0
- package/docs/host-providers.md +294 -0
- package/docs/jira-agents.md +17 -8
- package/docs/knowledge/blob-not-found-semantics.md +41 -0
- package/docs/knowledge/bounded-file-descriptor.md +52 -0
- package/docs/knowledge/delegation-admission-slots.md +52 -0
- package/docs/knowledge/http-dispatcher-bootstrap.md +61 -0
- package/docs/knowledge/import-rename-target.md +52 -0
- package/docs/knowledge/index.md +57 -0
- package/docs/knowledge/log.md +9 -0
- package/docs/knowledge/mcp-cancelled-task-precedence.md +41 -0
- package/docs/knowledge/mcp-http-prompt-discovery.md +41 -0
- package/docs/knowledge/mcp-schema-module-boundary.md +41 -0
- package/docs/knowledge/nullable-tool-uniqueness.md +52 -0
- package/docs/knowledge/overview-cli.md +74 -0
- package/docs/knowledge/overview-coding-proxy.md +72 -0
- package/docs/knowledge/overview-coding.md +72 -0
- package/docs/knowledge/overview-config.md +60 -0
- package/docs/knowledge/overview-core.md +72 -0
- package/docs/knowledge/overview-deploy.md +83 -0
- package/docs/knowledge/overview-mcp.md +61 -0
- package/docs/knowledge/overview-prisma.md +72 -0
- package/docs/knowledge/overview-providers.md +89 -0
- package/docs/knowledge/overview-sandbox.md +83 -0
- package/docs/knowledge/overview-surfaces.md +83 -0
- package/docs/knowledge/private-datastore-shared-boundary.md +54 -0
- package/docs/knowledge/proxy-settlement-projections.md +41 -0
- package/docs/knowledge/run-pricing-first-writer.md +43 -0
- package/docs/knowledge/schedule-claim-transaction.md +41 -0
- package/docs/knowledge/secret-entry-replica-contract.md +52 -0
- package/docs/knowledge/terminal-run-first-writer.md +41 -0
- package/docs/knowledge/transactional-agent-authorization.md +52 -0
- package/docs/knowledge/viewer-notify-write-isolation.md +30 -0
- package/docs/knowledge/warm-input-publication.md +30 -0
- package/docs/knowledge/workspace-staging-failure.md +54 -0
- package/docs/knowledge.md +54 -14
- package/docs/mcp-servers.md +254 -0
- package/docs/native-sandbox.md +143 -31
- package/docs/observability.md +4 -0
- package/docs/security-deployment.md +139 -29
- package/docs/slack-notifications.md +35 -19
- package/docs/tool-secrets.md +45 -10
- package/docs/viewer-api.md +6 -0
- package/docs/viewer-api.openapi.yml +1 -1
- package/help/admin-viewer.md +28 -2
- package/help/agent-recipes.md +33 -2
- package/help/architecture-agent.md +11 -6
- package/help/brokered-secrets.md +3 -0
- package/help/build-claude-code-image.md +1 -1
- package/help/build-worker-image.md +3 -1
- package/help/builder-agent.md +19 -6
- package/help/claude-code-run-pane.md +40 -0
- package/help/code-review-agents.md +177 -6
- package/help/coding-packages.md +156 -6
- package/help/cost-attribution.md +9 -5
- package/help/creating-agents.md +14 -1
- package/help/dependency-cache.md +143 -0
- package/help/deploy-gke.md +15 -8
- package/help/errors/budget-exhausted.md +187 -0
- package/help/errors/budget-group-exhausted.md +5 -1
- package/help/errors/coding-mcp-pins-unavailable.md +35 -0
- package/help/errors/coding-stream-quota-exceeded.md +33 -0
- package/help/errors/coding-workspace-disk-exceeds-limit.md +39 -0
- package/help/errors/continuation-closed.md +9 -12
- package/help/errors/database-auth-invalid.md +1 -1
- package/help/errors/delegation-conflict.md +24 -0
- package/help/errors/file-list-truncated.md +45 -0
- package/help/errors/full-read-required.md +49 -0
- package/help/errors/github-account-not-allowed.md +4 -0
- package/help/errors/graph-incomplete.md +43 -0
- package/help/errors/graph-limit.md +43 -0
- package/help/errors/host-provider-error.md +40 -0
- package/help/errors/host-tool-failed.md +36 -0
- package/help/errors/mcp-call-limit.md +22 -0
- package/help/errors/mcp-call-timeout.md +22 -0
- package/help/errors/mcp-server-auth-failed.md +22 -0
- package/help/errors/mcp-server-credentials-unavailable.md +45 -0
- package/help/errors/mcp-server-error.md +28 -0
- package/help/errors/mcp-server-unreachable.md +22 -0
- package/help/errors/mcp-servers-off.md +21 -0
- package/help/errors/mcp-tool-not-allowed.md +26 -0
- package/help/errors/mcp-tool-removed.md +22 -0
- package/help/errors/mcp-unavailable.md +25 -0
- package/help/errors/merge-conflict.md +49 -0
- package/help/errors/native-sandbox-capacity.md +1 -1
- package/help/errors/native-sandbox-deadline-exceeded.md +1 -1
- package/help/errors/native-sandbox-docker-failed.md +1 -1
- package/help/errors/native-sandbox-gateway-error.md +20 -0
- package/help/errors/native-sandbox-gateway-unavailable.md +1 -1
- package/help/errors/native-sandbox-image-not-pinned.md +1 -1
- package/help/errors/native-sandbox-isolation-mismatch.md +1 -1
- package/help/errors/native-sandbox-launch-abandoned.md +21 -0
- package/help/errors/native-sandbox-network-unenforced.md +1 -1
- package/help/errors/native-sandbox-requires-catalog.md +1 -1
- package/help/errors/native-sandbox-unavailable.md +1 -1
- package/help/errors/native-sandbox-warm-delivery-failed.md +1 -1
- package/help/errors/native-sandbox-worker-exited.md +4 -3
- package/help/errors/native-sandbox-worker-lost.md +1 -1
- package/help/errors/native-sandbox-worker-unready.md +1 -1
- package/help/errors/package-not-allowed-pending.md +52 -0
- package/help/errors/package-not-allowed.md +51 -0
- package/help/errors/package-refusals.md +43 -0
- package/help/errors/patch-cursor-malformed.md +31 -0
- package/help/errors/repository-not-listed.md +36 -0
- package/help/errors/repository-required.md +36 -0
- package/help/errors/run-lost.md +39 -0
- package/help/errors/run-transcript-download.md +44 -0
- package/help/errors/secret-in-value.md +69 -0
- package/help/errors/target-merge.md +40 -0
- package/help/errors/tool-timeout.md +38 -0
- package/help/fix-merge-conflicts.md +73 -0
- package/help/github-app-setup.md +132 -0
- package/help/github.md +22 -0
- package/help/host-providers.md +40 -0
- package/help/identity-and-access.md +6 -1
- package/help/jira.md +23 -6
- package/help/knowledge.md +16 -7
- package/help/local-repositories.md +1 -1
- package/help/low-power-mode.md +7 -1
- package/help/mcp-servers.md +73 -0
- package/help/mcp.md +7 -2
- package/help/merge-order-check.md +5 -0
- package/help/native-capabilities.md +55 -1
- package/help/native-sandbox.md +16 -2
- package/help/operating-agents.md +21 -0
- package/help/package-audit-mode.md +91 -0
- package/help/related-pull-requests.md +17 -10
- package/help/repo-instructions-and-skills.md +3 -1
- package/help/resume-runs-locally.md +134 -0
- package/help/review-fix-rounds.md +33 -1
- package/help/sharing-agents.md +59 -0
- package/help/slack-notifications.md +19 -5
- package/help/status-comments.md +31 -0
- package/help/stop-run.md +58 -0
- package/help/troubleshooting/budgets.md +28 -0
- package/help/troubleshooting/coding-workers.md +86 -7
- package/help/troubleshooting/debugging-agent-runs.md +29 -0
- package/package.json +18 -10
- package/prisma/migrations/20261016000000_registry_approved_accepted_advisories/migration.sql +3 -0
- package/prisma/migrations/20261016000001_mark_ready_when_passing/migration.sql +3 -0
- package/prisma/migrations/20261017000000_debug_trace_max_mb/migration.sql +4 -0
- package/prisma/migrations/20261018000000_dependency_cache/migration.sql +53 -0
- package/prisma/migrations/20261019000000_claude_max_output_tokens/migration.sql +5 -0
- package/prisma/migrations/20261021000000_pr_description_hash/migration.sql +4 -0
- package/prisma/migrations/20261022000000_dependency_snapshot_validation_metadata/migration.sql +4 -0
- package/prisma/migrations/20261023000000_package_audit_mode/migration.sql +27 -0
- package/prisma/migrations/20261024000000_budget_events/migration.sql +26 -0
- package/prisma/migrations/20261025000000_cache_aware_holds/migration.sql +9 -0
- package/prisma/migrations/20261026000000_budget_overrun/migration.sql +4 -0
- package/prisma/migrations/20261027000000_coding_run_request/migration.sql +1 -0
- package/prisma/migrations/20261028000000_native_gateway_review_reads/migration.sql +6 -0
- package/prisma/migrations/20261029000000_lead_survives_deploy/migration.sql +6 -0
- package/prisma/migrations/20261030000000_local_review_covered_sha/migration.sql +2 -0
- package/prisma/migrations/20261031000000_coding_run_budget_limit/migration.sql +9 -0
- package/prisma/migrations/20261101000000_run_transcripts/migration.sql +22 -0
- package/prisma/migrations/20261102000000_host_providers/migration.sql +56 -0
- package/prisma/migrations/20261104000000_coding_agent_repository_list/migration.sql +5 -0
- package/prisma/migrations/20261106000000_tool_timeout/migration.sql +4 -0
- package/prisma/migrations/20261107000000_mcp_servers/migration.sql +33 -0
- package/prisma/migrations/20261108000000_coding_mcp/migration.sql +5 -0
- package/prisma/migrations/20261109000000_task_run_index/migration.sql +3 -0
- package/prisma/schema.prisma +468 -109
- package/scripts/git-askpass.sh +1 -1
- package/dist/sandbox/generated/zod-to-json-schema.bundle.js +0 -5
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomBytes, randomUUID } from "node:crypto";
|
|
2
2
|
import { shippedCatalog } from "../llm/catalog.js";
|
|
3
3
|
import { SHIPPED_CATALOG_VERSION } from "../llm/catalog-shipped.js";
|
|
4
4
|
import { deriveRegistryToken } from "../../coding/registry/token.js";
|
|
5
|
-
import {
|
|
5
|
+
import { capabilityHash } from "./capability-hash.js";
|
|
6
|
+
import { CLAUDE_MCP_TOOL_PREFIX } from "../../coding/mcp-token.js";
|
|
7
|
+
import { AnthropicSseUsageTracker, actualCostUsd, fingerprintRequest, parseAnthropicAuthoritativeUsage, parseAuthoritativeUsage, pricingSnapshot, safeUpstreamErrorCode, terminalUsageFromSseFrame, } from "./metering.js";
|
|
8
|
+
import { conversationKey, estimateHold } from "./hold.js";
|
|
9
|
+
import { estimateInterrupted, previousInput } from "./interrupted.js";
|
|
6
10
|
import { createPinnedProxyFetch } from "./secure-fetch.js";
|
|
7
11
|
export const PROXY_MAX_BODY_BYTES = 8 * 1024 * 1024;
|
|
8
12
|
export const PROXY_MAX_OUTPUT_TOKENS = 1_000_000;
|
|
@@ -46,20 +50,19 @@ export class CodingProxyError extends Error {
|
|
|
46
50
|
this.code = code;
|
|
47
51
|
}
|
|
48
52
|
}
|
|
49
|
-
export
|
|
50
|
-
return createHash("sha256").update(capability).digest("base64url");
|
|
51
|
-
}
|
|
53
|
+
export { capabilityHash };
|
|
52
54
|
// OpenAI Responses allowlist. Everything a request may carry is listed here;
|
|
53
55
|
// anything else is refused before a credential is resolved. It mirrors what the
|
|
54
56
|
// pinned Codex CLI (src/coding-worker/package.json) actually sends, recorded in
|
|
55
57
|
// fixtures/codex-<version>-responses-requests.json. Only client-side tools
|
|
56
|
-
// (function, custom,
|
|
57
|
-
// search, remote MCP, code interpreter, image
|
|
58
|
-
// remote image or file, a stored prompt, or a
|
|
59
|
-
// OpenAI fetch or run something outside the
|
|
60
|
-
// the token-metered budget. Tool names vary
|
|
61
|
-
// are checked for shape, not against a
|
|
62
|
-
//
|
|
58
|
+
// (function, custom, namespaces of them, and the client-executed tool_search)
|
|
59
|
+
// are allowed: a hosted tool (web search, remote MCP, code interpreter, image
|
|
60
|
+
// generation, file search, ...), a remote image or file, a stored prompt, or a
|
|
61
|
+
// previous response would make OpenAI fetch or run something outside the
|
|
62
|
+
// worker's egress policy and outside the token-metered budget. Tool names vary
|
|
63
|
+
// by model and Codex version, so they are checked for shape, not against a
|
|
64
|
+
// fixed list: a client-side tool the model calls is executed by the worker
|
|
65
|
+
// itself, which the container boundary governs.
|
|
63
66
|
const OPENAI_REQUEST_KEYS = new Set([
|
|
64
67
|
"model",
|
|
65
68
|
"instructions",
|
|
@@ -149,13 +152,32 @@ function openAiToolName(value, code = "invalid_openai_request") {
|
|
|
149
152
|
if (typeof value !== "string" || !OPENAI_TOOL_NAME.test(value))
|
|
150
153
|
throw new CodingProxyError(400, code);
|
|
151
154
|
}
|
|
152
|
-
function validateOpenAiFunctionTool(tool) {
|
|
153
|
-
openAiOnlyKeys(tool,
|
|
155
|
+
function validateOpenAiFunctionTool(tool, inSearchOutput) {
|
|
156
|
+
openAiOnlyKeys(tool, inSearchOutput
|
|
157
|
+
? ["type", "name", "description", "strict", "defer_loading", "parameters"]
|
|
158
|
+
: ["type", "name", "description", "strict", "parameters"], "invalid_openai_tool");
|
|
154
159
|
openAiToolName(tool.name, "invalid_openai_tool");
|
|
155
160
|
openAiOptional(tool.description, (value) => openAiString(value, MAX_TOOL_TEXT_BYTES, "invalid_openai_tool"));
|
|
156
161
|
if (tool.strict !== undefined && typeof tool.strict !== "boolean") {
|
|
157
162
|
throw new CodingProxyError(400, "invalid_openai_tool");
|
|
158
163
|
}
|
|
164
|
+
// Codex marks the tools a tool_search_output loads with defer_loading: true,
|
|
165
|
+
// and sends it nowhere else. It only delays when the model sees the
|
|
166
|
+
// definition, which is checked in full.
|
|
167
|
+
if (tool.defer_loading !== undefined && tool.defer_loading !== true) {
|
|
168
|
+
throw new CodingProxyError(400, "invalid_openai_tool");
|
|
169
|
+
}
|
|
170
|
+
openAiOptional(tool.parameters, (value) => openAiRecord(value, "invalid_openai_tool"));
|
|
171
|
+
}
|
|
172
|
+
// The tool_search tool Codex sends for models whose metadata asks for it. Only
|
|
173
|
+
// client execution: Codex runs the search over its own deferred tools and
|
|
174
|
+
// returns them in a tool_search_output item. A server-executed search would
|
|
175
|
+
// have OpenAI run it.
|
|
176
|
+
function validateOpenAiToolSearchTool(tool) {
|
|
177
|
+
openAiOnlyKeys(tool, ["type", "execution", "description", "parameters"], "invalid_openai_tool");
|
|
178
|
+
if (tool.execution !== "client")
|
|
179
|
+
throw new CodingProxyError(400, "openai_tool_not_allowed:tool_search");
|
|
180
|
+
openAiOptional(tool.description, (value) => openAiString(value, MAX_TOOL_TEXT_BYTES, "invalid_openai_tool"));
|
|
159
181
|
openAiOptional(tool.parameters, (value) => openAiRecord(value, "invalid_openai_tool"));
|
|
160
182
|
}
|
|
161
183
|
function validateOpenAiCustomTool(tool) {
|
|
@@ -173,30 +195,57 @@ function validateOpenAiCustomTool(tool) {
|
|
|
173
195
|
}
|
|
174
196
|
openAiString(format.definition, MAX_TOOL_TEXT_BYTES, "invalid_openai_tool");
|
|
175
197
|
}
|
|
176
|
-
|
|
198
|
+
// Every tool list (the request's tools, each additional_tools item, each
|
|
199
|
+
// tool_search_output item) is capped at MAX_OPENAI_TOOLS entries on its own.
|
|
200
|
+
// Across the request, `distinct` holds the `namespace/name` of each callable
|
|
201
|
+
// tool and is capped too. Codex replays every earlier tool_search_output on
|
|
202
|
+
// each turn, so a long run repeats the same definitions many times: each copy
|
|
203
|
+
// is still validated, but only a tool not seen before adds to the shared cap.
|
|
204
|
+
function validateOpenAiTools(value, distinct, { toolSearch = false, inSearchOutput = false, namespace, listed = { value: 0 }, } = {}) {
|
|
177
205
|
if (!Array.isArray(value))
|
|
178
206
|
throw new CodingProxyError(400, "invalid_openai_tool");
|
|
207
|
+
const callable = (name) => {
|
|
208
|
+
distinct.add(`${namespace ?? ""}/${name}`);
|
|
209
|
+
if (distinct.size > MAX_OPENAI_TOOLS)
|
|
210
|
+
throw new CodingProxyError(400, "invalid_openai_tool");
|
|
211
|
+
};
|
|
179
212
|
for (const candidate of value) {
|
|
180
|
-
if (++
|
|
213
|
+
if (++listed.value > MAX_OPENAI_TOOLS)
|
|
181
214
|
throw new CodingProxyError(400, "invalid_openai_tool");
|
|
182
215
|
const tool = openAiRecord(candidate, "invalid_openai_tool");
|
|
183
216
|
if (tool.type === "function") {
|
|
184
|
-
validateOpenAiFunctionTool(tool);
|
|
217
|
+
validateOpenAiFunctionTool(tool, inSearchOutput);
|
|
218
|
+
callable(tool.name);
|
|
185
219
|
}
|
|
186
220
|
else if (tool.type === "custom") {
|
|
187
221
|
validateOpenAiCustomTool(tool);
|
|
222
|
+
callable(tool.name);
|
|
188
223
|
}
|
|
189
|
-
else if (tool.type === "namespace" &&
|
|
224
|
+
else if (tool.type === "namespace" && namespace === undefined) {
|
|
190
225
|
openAiOnlyKeys(tool, ["type", "name", "description", "tools"], "invalid_openai_tool");
|
|
191
226
|
openAiToolName(tool.name, "invalid_openai_tool");
|
|
192
227
|
openAiOptional(tool.description, (value) => openAiString(value, MAX_TOOL_TEXT_BYTES, "invalid_openai_tool"));
|
|
193
|
-
validateOpenAiTools(tool.tools,
|
|
228
|
+
validateOpenAiTools(tool.tools, distinct, { inSearchOutput, namespace: tool.name, listed });
|
|
229
|
+
}
|
|
230
|
+
else if (tool.type === "tool_search" && toolSearch) {
|
|
231
|
+
validateOpenAiToolSearchTool(tool);
|
|
232
|
+
callable("tool_search");
|
|
194
233
|
}
|
|
195
234
|
else {
|
|
196
235
|
throw new CodingProxyError(400, openAiCode("openai_tool_not_allowed", tool.type));
|
|
197
236
|
}
|
|
198
237
|
}
|
|
199
238
|
}
|
|
239
|
+
// The arguments the model gave the client-side tool_search, as its parameters
|
|
240
|
+
// in the captures declare them: a query and an optional result limit.
|
|
241
|
+
function validateOpenAiToolSearchArguments(value) {
|
|
242
|
+
const args = openAiRecord(value);
|
|
243
|
+
openAiOnlyKeys(args, ["query", "limit"]);
|
|
244
|
+
openAiString(args.query, MAX_TOOL_TEXT_BYTES);
|
|
245
|
+
if (args.limit !== undefined && !(Number.isSafeInteger(args.limit) && args.limit > 0)) {
|
|
246
|
+
throw new CodingProxyError(400, "invalid_openai_request");
|
|
247
|
+
}
|
|
248
|
+
}
|
|
200
249
|
function validateOpenAiContentPart(value, allowed) {
|
|
201
250
|
const part = openAiRecord(value);
|
|
202
251
|
if (typeof part.type !== "string" || !allowed.has(part.type)) {
|
|
@@ -239,7 +288,7 @@ function validateOpenAiTextParts(value, type) {
|
|
|
239
288
|
openAiString(part.text, PROXY_MAX_BODY_BYTES);
|
|
240
289
|
}
|
|
241
290
|
}
|
|
242
|
-
function validateOpenAiInputItem(value,
|
|
291
|
+
function validateOpenAiInputItem(value, distinctTools) {
|
|
243
292
|
const item = openAiRecord(value);
|
|
244
293
|
switch (item.type) {
|
|
245
294
|
case undefined:
|
|
@@ -298,12 +347,32 @@ function validateOpenAiInputItem(value, toolCount) {
|
|
|
298
347
|
openAiOptional(item.recipient, (value) => openAiString(value, MAX_OPENAI_ID_LENGTH));
|
|
299
348
|
validateOpenAiContent(item.content, OPENAI_AGENT_MESSAGE_PARTS, false);
|
|
300
349
|
return;
|
|
350
|
+
case "tool_search_call":
|
|
351
|
+
openAiOnlyKeys(item, ["type", "id", "call_id", "status", "execution", "arguments"]);
|
|
352
|
+
openAiOptional(item.id, openAiId);
|
|
353
|
+
openAiId(item.call_id);
|
|
354
|
+
openAiOptional(item.status, (value) => openAiString(value, 64));
|
|
355
|
+
if (item.execution !== "client")
|
|
356
|
+
throw new CodingProxyError(400, openAiCode("openai_input_not_allowed", item.type));
|
|
357
|
+
validateOpenAiToolSearchArguments(item.arguments);
|
|
358
|
+
return;
|
|
359
|
+
case "tool_search_output":
|
|
360
|
+
// The tools the client-side search loaded: each one passes the same checks
|
|
361
|
+
// as a top-level tool, so this cannot bring in a shape the proxy refuses.
|
|
362
|
+
openAiOnlyKeys(item, ["type", "id", "call_id", "status", "execution", "tools"]);
|
|
363
|
+
openAiOptional(item.id, openAiId);
|
|
364
|
+
openAiId(item.call_id);
|
|
365
|
+
openAiOptional(item.status, (value) => openAiString(value, 64));
|
|
366
|
+
if (item.execution !== "client")
|
|
367
|
+
throw new CodingProxyError(400, openAiCode("openai_input_not_allowed", item.type));
|
|
368
|
+
validateOpenAiTools(item.tools, distinctTools, { inSearchOutput: true });
|
|
369
|
+
return;
|
|
301
370
|
case "additional_tools":
|
|
302
371
|
openAiOnlyKeys(item, ["type", "id", "role", "tools"]);
|
|
303
372
|
openAiOptional(item.id, openAiId);
|
|
304
373
|
if (item.role !== undefined && item.role !== "developer")
|
|
305
374
|
throw new CodingProxyError(400, "invalid_openai_request");
|
|
306
|
-
validateOpenAiTools(item.tools,
|
|
375
|
+
validateOpenAiTools(item.tools, distinctTools);
|
|
307
376
|
return;
|
|
308
377
|
default:
|
|
309
378
|
throw new CodingProxyError(400, openAiCode("openai_input_not_allowed", item.type));
|
|
@@ -370,9 +439,9 @@ function validateOpenAiBody(body) {
|
|
|
370
439
|
throw new CodingProxyError(400, "invalid_openai_request");
|
|
371
440
|
}
|
|
372
441
|
openAiOptional(body.instructions, (value) => openAiString(value, PROXY_MAX_BODY_BYTES));
|
|
373
|
-
const
|
|
442
|
+
const distinctTools = new Set();
|
|
374
443
|
if (body.tools !== undefined)
|
|
375
|
-
validateOpenAiTools(body.tools,
|
|
444
|
+
validateOpenAiTools(body.tools, distinctTools, { toolSearch: true });
|
|
376
445
|
if (body.tool_choice !== undefined) {
|
|
377
446
|
if (typeof body.tool_choice !== "string" || !OPENAI_TOOL_CHOICES.has(body.tool_choice)) {
|
|
378
447
|
throw new CodingProxyError(400, "openai_tool_choice_not_allowed");
|
|
@@ -399,7 +468,7 @@ function validateOpenAiBody(body) {
|
|
|
399
468
|
throw new CodingProxyError(400, "invalid_openai_request");
|
|
400
469
|
}
|
|
401
470
|
for (const item of body.input)
|
|
402
|
-
validateOpenAiInputItem(item,
|
|
471
|
+
validateOpenAiInputItem(item, distinctTools);
|
|
403
472
|
}
|
|
404
473
|
function parseOpenAiRequest(rawBody) {
|
|
405
474
|
if (Buffer.byteLength(rawBody) > PROXY_MAX_BODY_BYTES)
|
|
@@ -514,15 +583,33 @@ function validateStructuredOutputTool(value) {
|
|
|
514
583
|
if (tool.cache_control !== undefined)
|
|
515
584
|
validateCacheControl(tool.cache_control);
|
|
516
585
|
}
|
|
517
|
-
|
|
586
|
+
/**
|
|
587
|
+
* One of the run's pinned MCP tools, as Claude Code advertises it: a plain client tool named
|
|
588
|
+
* `mcp__wardby_mcp__<exposed name>` (fixtures/claude-code-2.1.286-mcp-requests.json). Only the name
|
|
589
|
+
* is checked against the pins; the gateway checks the call itself again.
|
|
590
|
+
*/
|
|
591
|
+
function validateMcpTool(tool) {
|
|
592
|
+
onlyKeys(tool, ["name", "description", "input_schema", "cache_control", "type"]);
|
|
593
|
+
if ((tool.description !== undefined &&
|
|
594
|
+
(typeof tool.description !== "string" || tool.description.length > MAX_TOOL_TEXT_BYTES)) ||
|
|
595
|
+
(tool.type !== undefined && tool.type !== "custom")) {
|
|
596
|
+
throw new CodingProxyError(400, "tools_not_allowed");
|
|
597
|
+
}
|
|
598
|
+
record(tool.input_schema, "tools_not_allowed");
|
|
599
|
+
if (tool.cache_control !== undefined)
|
|
600
|
+
validateCacheControl(tool.cache_control);
|
|
601
|
+
}
|
|
602
|
+
function validateApprovedTool(value, mcpTools) {
|
|
518
603
|
const tool = record(value, "tools_not_allowed");
|
|
519
604
|
if (tool.name === WARDBY_COMMAND_TOOL)
|
|
520
605
|
return validateCommandTool(tool);
|
|
521
606
|
if (tool.name === STRUCTURED_OUTPUT_TOOL)
|
|
522
607
|
return validateStructuredOutputTool(tool);
|
|
608
|
+
if (typeof tool.name === "string" && mcpTools.has(tool.name))
|
|
609
|
+
return validateMcpTool(tool);
|
|
523
610
|
throw new CodingProxyError(400, "tools_not_allowed");
|
|
524
611
|
}
|
|
525
|
-
function validateToolUseBlock(value) {
|
|
612
|
+
function validateToolUseBlock(value, mcpTools) {
|
|
526
613
|
onlyKeys(value, ["type", "id", "name", "input"]);
|
|
527
614
|
if (typeof value.id !== "string" || value.id.length < 1 || value.id.length > 512) {
|
|
528
615
|
throw new CodingProxyError(400, "unsupported_anthropic_feature");
|
|
@@ -531,6 +618,9 @@ function validateToolUseBlock(value) {
|
|
|
531
618
|
return validateCommandInput(value.input);
|
|
532
619
|
if (value.name === STRUCTURED_OUTPUT_TOOL)
|
|
533
620
|
return void record(value.input, "unsupported_anthropic_feature");
|
|
621
|
+
if (typeof value.name === "string" && mcpTools.has(value.name)) {
|
|
622
|
+
return void record(value.input, "unsupported_anthropic_feature");
|
|
623
|
+
}
|
|
534
624
|
throw new CodingProxyError(400, "unsupported_anthropic_feature");
|
|
535
625
|
}
|
|
536
626
|
function validateToolResultBlock(value) {
|
|
@@ -557,7 +647,7 @@ function validateThinkingBlock(value) {
|
|
|
557
647
|
throw new CodingProxyError(400, "unsupported_anthropic_feature");
|
|
558
648
|
}
|
|
559
649
|
}
|
|
560
|
-
function validateMessageBlocks(value, role) {
|
|
650
|
+
function validateMessageBlocks(value, role, mcpTools) {
|
|
561
651
|
if (!Array.isArray(value) || value.length > 10_000)
|
|
562
652
|
throw new CodingProxyError(400, "invalid_anthropic_request");
|
|
563
653
|
for (const candidate of value) {
|
|
@@ -566,7 +656,7 @@ function validateMessageBlocks(value, role) {
|
|
|
566
656
|
validateTextBlock(block);
|
|
567
657
|
}
|
|
568
658
|
else if (block.type === "tool_use" && role === "assistant") {
|
|
569
|
-
validateToolUseBlock(block);
|
|
659
|
+
validateToolUseBlock(block, mcpTools);
|
|
570
660
|
}
|
|
571
661
|
else if (block.type === "tool_result" && role === "user") {
|
|
572
662
|
validateToolResultBlock(block);
|
|
@@ -622,7 +712,7 @@ function validateEffortConfig(value, { thinkingMode, efforts }, betas) {
|
|
|
622
712
|
throw new CodingProxyError(400, "unsupported_anthropic_feature");
|
|
623
713
|
}
|
|
624
714
|
}
|
|
625
|
-
function parseAnthropicRequest(rawBody, betaHeader, shape) {
|
|
715
|
+
function parseAnthropicRequest(rawBody, betaHeader, shape, mcpTools) {
|
|
626
716
|
const beta = parseAnthropicBeta(betaHeader);
|
|
627
717
|
if (Buffer.byteLength(rawBody) > PROXY_MAX_BODY_BYTES)
|
|
628
718
|
throw new CodingProxyError(413, "payload_too_large");
|
|
@@ -686,18 +776,20 @@ function parseAnthropicRequest(rawBody, betaHeader, shape) {
|
|
|
686
776
|
continue;
|
|
687
777
|
}
|
|
688
778
|
}
|
|
689
|
-
validateMessageBlocks(message.content, message.role);
|
|
779
|
+
validateMessageBlocks(message.content, message.role, mcpTools);
|
|
690
780
|
}
|
|
691
781
|
if (body.tools !== undefined) {
|
|
692
|
-
|
|
782
|
+
// run_command and StructuredOutput, plus one per pinned MCP tool.
|
|
783
|
+
if (!Array.isArray(body.tools) || body.tools.length > 2 + mcpTools.size) {
|
|
693
784
|
throw new CodingProxyError(400, "tools_not_allowed");
|
|
785
|
+
}
|
|
694
786
|
const names = new Set();
|
|
695
787
|
for (const tool of body.tools) {
|
|
696
788
|
const definition = record(tool, "tools_not_allowed");
|
|
697
789
|
if (typeof definition.name !== "string" || names.has(definition.name))
|
|
698
790
|
throw new CodingProxyError(400, "tools_not_allowed");
|
|
699
791
|
names.add(definition.name);
|
|
700
|
-
validateApprovedTool(definition);
|
|
792
|
+
validateApprovedTool(definition, mcpTools);
|
|
701
793
|
}
|
|
702
794
|
}
|
|
703
795
|
if (body.metadata !== undefined) {
|
|
@@ -758,13 +850,17 @@ function parseAnthropicRequest(rawBody, betaHeader, shape) {
|
|
|
758
850
|
anthropicBeta: beta.header,
|
|
759
851
|
};
|
|
760
852
|
}
|
|
761
|
-
|
|
853
|
+
/** The Claude tool names of a session's pinned MCP tools; empty for a session without pins. */
|
|
854
|
+
function claudeMcpToolNames(pins) {
|
|
855
|
+
return new Set((pins ?? []).map((pin) => `${CLAUDE_MCP_TOOL_PREFIX}${pin.exposedName}`));
|
|
856
|
+
}
|
|
857
|
+
function parseRequest(protocol, rawBody, anthropicBeta, shape, mcpTools) {
|
|
762
858
|
if (protocol !== "anthropic-messages" && anthropicBeta !== undefined) {
|
|
763
859
|
throw new CodingProxyError(400, "invalid_anthropic_beta");
|
|
764
860
|
}
|
|
765
861
|
try {
|
|
766
862
|
return protocol === "anthropic-messages"
|
|
767
|
-
? parseAnthropicRequest(rawBody, anthropicBeta, shape)
|
|
863
|
+
? parseAnthropicRequest(rawBody, anthropicBeta, shape, mcpTools)
|
|
768
864
|
: parseOpenAiRequest(rawBody);
|
|
769
865
|
}
|
|
770
866
|
catch (error) {
|
|
@@ -818,8 +914,10 @@ export class CodingProxy {
|
|
|
818
914
|
getPricing;
|
|
819
915
|
priceVersion;
|
|
820
916
|
activeRequests = new Map();
|
|
917
|
+
onModelRequest;
|
|
821
918
|
constructor(options) {
|
|
822
919
|
this.ledger = options.ledger;
|
|
920
|
+
this.onModelRequest = options.onModelRequest;
|
|
823
921
|
this.credentials = options.credentials;
|
|
824
922
|
this.upstreamUrls = {
|
|
825
923
|
"openai-responses": options.upstreamUrl ?? "https://api.openai.com/v1/responses",
|
|
@@ -870,6 +968,11 @@ export class CodingProxy {
|
|
|
870
968
|
throw new Error("invalid_proxy_deadline");
|
|
871
969
|
if (!input.credentialRef || input.credentialRef.length > 200)
|
|
872
970
|
throw new Error("invalid_proxy_credential_reference");
|
|
971
|
+
// Both or neither: pins without a token could never be called, a token without pins lists nothing.
|
|
972
|
+
const mcp = input.mcpTools !== undefined || input.mcpTokenHash !== undefined;
|
|
973
|
+
if (mcp && (!input.mcpTools || input.mcpTools.length === 0 || !input.mcpTokenHash)) {
|
|
974
|
+
throw new Error("invalid_proxy_mcp_tools");
|
|
975
|
+
}
|
|
873
976
|
const capability = `rrp_${randomBytes(32).toString("base64url")}`;
|
|
874
977
|
const id = randomUUID();
|
|
875
978
|
const registryTokenHash = capabilityHash(deriveRegistryToken(capability));
|
|
@@ -884,6 +987,7 @@ export class CodingProxy {
|
|
|
884
987
|
budgetUsd: input.budgetUsd,
|
|
885
988
|
registryTokenHash,
|
|
886
989
|
terms: input.terms,
|
|
990
|
+
...(mcp ? { mcpTools: input.mcpTools, mcpTokenHash: input.mcpTokenHash } : {}),
|
|
887
991
|
});
|
|
888
992
|
this.audit({ type: "session.created", runId: input.runId });
|
|
889
993
|
return {
|
|
@@ -905,12 +1009,19 @@ export class CodingProxy {
|
|
|
905
1009
|
budgetExhausted(sessionId) {
|
|
906
1010
|
return this.ledger.budgetExhausted(sessionId);
|
|
907
1011
|
}
|
|
1012
|
+
/** The numbers behind this session's first budget refusal, if any. */
|
|
1013
|
+
budgetRefusal(sessionId) {
|
|
1014
|
+
return this.ledger.budgetRefusal(sessionId);
|
|
1015
|
+
}
|
|
908
1016
|
/** The code of the first upstream failure relayed for this session's run, if any. */
|
|
909
1017
|
upstreamFailure(sessionId) {
|
|
910
1018
|
return this.ledger.upstreamFailure(sessionId);
|
|
911
1019
|
}
|
|
912
1020
|
async execute(input, sink) {
|
|
913
1021
|
const session = await this.authenticate(input.bearer);
|
|
1022
|
+
// The run's agent exists from its first model request on (both protocols arrive here).
|
|
1023
|
+
if (this.onModelRequest)
|
|
1024
|
+
await this.onModelRequest(session.runId).catch(() => undefined);
|
|
914
1025
|
if (session.protocol !== input.protocol) {
|
|
915
1026
|
this.audit({ type: "request.rejected", runId: session.runId, reason: "protocol_mismatch" });
|
|
916
1027
|
throw new CodingProxyError(403, "protocol_mismatch");
|
|
@@ -923,7 +1034,7 @@ export class CodingProxy {
|
|
|
923
1034
|
};
|
|
924
1035
|
let parsed;
|
|
925
1036
|
try {
|
|
926
|
-
parsed = parseRequest(input.protocol, input.rawBody, input.anthropicBeta, shape);
|
|
1037
|
+
parsed = parseRequest(input.protocol, input.rawBody, input.anthropicBeta, shape, claudeMcpToolNames(session.mcpTools));
|
|
927
1038
|
}
|
|
928
1039
|
catch (error) {
|
|
929
1040
|
// The session is already authenticated and no credential has been
|
|
@@ -948,7 +1059,32 @@ export class CodingProxy {
|
|
|
948
1059
|
}
|
|
949
1060
|
const requestKey = safeRequestKey(input.requestKey, parsed.fingerprint);
|
|
950
1061
|
const snapshot = pricingSnapshot(terms.version, terms.pricing);
|
|
951
|
-
const
|
|
1062
|
+
const bodyBytes = Buffer.byteLength(parsed.encoded);
|
|
1063
|
+
const conversation = conversationKey(session.protocol, parsed.body);
|
|
1064
|
+
// A failed lookup holds the ceiling: never a reason to fail the request.
|
|
1065
|
+
const previous = conversation ? await this.ledger.previousReply(session.id, conversation).catch(() => null) : null;
|
|
1066
|
+
const holdNow = this.now();
|
|
1067
|
+
const hold = estimateHold({
|
|
1068
|
+
protocol: session.protocol,
|
|
1069
|
+
model: parsed.model,
|
|
1070
|
+
bodyBytes,
|
|
1071
|
+
maxOutputTokens: parsed.maxOutputTokens,
|
|
1072
|
+
pricing: snapshot,
|
|
1073
|
+
previous,
|
|
1074
|
+
now: holdNow,
|
|
1075
|
+
});
|
|
1076
|
+
// An interrupted reply's input, from the same previous reply and clock as the hold.
|
|
1077
|
+
const previousPrompt = previousInput({ model: parsed.model, bodyBytes, previous, pricing: snapshot, now: holdNow });
|
|
1078
|
+
const reservationUsd = hold.holdUsd;
|
|
1079
|
+
const holdAudit = {
|
|
1080
|
+
reservationUsd,
|
|
1081
|
+
ceilingUsd: hold.ceilingUsd,
|
|
1082
|
+
reservationBasis: hold.basis,
|
|
1083
|
+
...(hold.fallback ? { holdFallback: hold.fallback } : {}),
|
|
1084
|
+
...(hold.cachedTokens !== undefined
|
|
1085
|
+
? { holdCachedTokens: hold.cachedTokens, holdUncachedTokens: hold.uncachedTokens }
|
|
1086
|
+
: {}),
|
|
1087
|
+
};
|
|
952
1088
|
const reservation = await this.ledger.reserve({
|
|
953
1089
|
id: randomUUID(),
|
|
954
1090
|
sessionId: session.id,
|
|
@@ -956,6 +1092,10 @@ export class CodingProxy {
|
|
|
956
1092
|
requestFingerprint: parsed.fingerprint,
|
|
957
1093
|
model: parsed.model,
|
|
958
1094
|
reservationUsd,
|
|
1095
|
+
requestBytes: bodyBytes,
|
|
1096
|
+
ceilingUsd: hold.ceilingUsd,
|
|
1097
|
+
reservationBasis: hold.basis,
|
|
1098
|
+
conversationKey: conversation,
|
|
959
1099
|
pricing: snapshot,
|
|
960
1100
|
now: this.now(),
|
|
961
1101
|
});
|
|
@@ -969,7 +1109,12 @@ export class CodingProxy {
|
|
|
969
1109
|
runId: session.runId,
|
|
970
1110
|
model: parsed.model,
|
|
971
1111
|
reason: "budget_exhausted",
|
|
972
|
-
|
|
1112
|
+
...holdAudit,
|
|
1113
|
+
budgetUsd: reservation.details.budgetUsd,
|
|
1114
|
+
spentUsd: reservation.details.spentUsd,
|
|
1115
|
+
heldReservedUsd: reservation.details.heldReservedUsd,
|
|
1116
|
+
heldUncertainUsd: reservation.details.heldUncertainUsd,
|
|
1117
|
+
turn: reservation.details.turn,
|
|
973
1118
|
});
|
|
974
1119
|
throw new CodingProxyError(429, "wardby_budget_exhausted");
|
|
975
1120
|
}
|
|
@@ -982,7 +1127,7 @@ export class CodingProxy {
|
|
|
982
1127
|
runId: session.runId,
|
|
983
1128
|
requestId: request.id,
|
|
984
1129
|
model: request.model,
|
|
985
|
-
|
|
1130
|
+
...holdAudit,
|
|
986
1131
|
});
|
|
987
1132
|
let key;
|
|
988
1133
|
try {
|
|
@@ -1010,6 +1155,13 @@ export class CodingProxy {
|
|
|
1010
1155
|
const deadlineTimer = setTimeout(() => controller.abort(), remainingMs);
|
|
1011
1156
|
deadlineTimer.unref();
|
|
1012
1157
|
this.trackActive(session.id, controller);
|
|
1158
|
+
const progress = {
|
|
1159
|
+
maxOutputTokens: parsed.maxOutputTokens,
|
|
1160
|
+
stream: parsed.stream,
|
|
1161
|
+
bodyBytes,
|
|
1162
|
+
...(previousPrompt ? { previousInput: previousPrompt } : {}),
|
|
1163
|
+
relayedOutputBytes: 0,
|
|
1164
|
+
};
|
|
1013
1165
|
const cleanupActive = () => {
|
|
1014
1166
|
clearTimeout(deadlineTimer);
|
|
1015
1167
|
const active = this.activeRequests.get(session.id);
|
|
@@ -1042,10 +1194,11 @@ export class CodingProxy {
|
|
|
1042
1194
|
redirect: "error",
|
|
1043
1195
|
signal: controller.signal,
|
|
1044
1196
|
});
|
|
1197
|
+
progress.responseStarted = true;
|
|
1045
1198
|
}
|
|
1046
1199
|
catch {
|
|
1047
1200
|
cleanupActive();
|
|
1048
|
-
await this.markUncertain(session, request, undefined, controller.signal.aborted ? "upstream_aborted" : "upstream_transport");
|
|
1201
|
+
await this.markUncertain(session, request, undefined, controller.signal.aborted ? "upstream_aborted" : "upstream_transport", {}, progress);
|
|
1049
1202
|
throw new CodingProxyError(502, controller.signal.aborted ? "upstream_aborted" : "upstream_transport_error");
|
|
1050
1203
|
}
|
|
1051
1204
|
if (!upstream.ok) {
|
|
@@ -1068,12 +1221,12 @@ export class CodingProxy {
|
|
|
1068
1221
|
}
|
|
1069
1222
|
try {
|
|
1070
1223
|
if (parsed.stream)
|
|
1071
|
-
await this.forwardStream(session, request, upstream, sink);
|
|
1224
|
+
await this.forwardStream(session, request, upstream, sink, progress);
|
|
1072
1225
|
else
|
|
1073
1226
|
await this.forwardJson(session, request, upstream, sink);
|
|
1074
1227
|
}
|
|
1075
1228
|
catch (error) {
|
|
1076
|
-
await this.markUncertain(session, request, upstream.status, failureReason(error), responseHeadersOf(upstream));
|
|
1229
|
+
await this.markUncertain(session, request, upstream.status, failureReason(error), responseHeadersOf(upstream), progress);
|
|
1077
1230
|
sink.destroy();
|
|
1078
1231
|
throw error instanceof CodingProxyError ? error : new CodingProxyError(502, "invalid_upstream_response");
|
|
1079
1232
|
}
|
|
@@ -1117,6 +1270,7 @@ export class CodingProxy {
|
|
|
1117
1270
|
async complete(session, request, usage, upstreamStatus) {
|
|
1118
1271
|
const costUsd = actualCostUsd(usage, request.pricing);
|
|
1119
1272
|
await this.ledger.complete(request.id, usage, costUsd, upstreamStatus);
|
|
1273
|
+
const overshootUsd = costUsd - request.reservationUsd;
|
|
1120
1274
|
this.audit({
|
|
1121
1275
|
type: "response.completed",
|
|
1122
1276
|
runId: session.runId,
|
|
@@ -1124,13 +1278,17 @@ export class CodingProxy {
|
|
|
1124
1278
|
model: request.model,
|
|
1125
1279
|
status: upstreamStatus,
|
|
1126
1280
|
costUsd,
|
|
1281
|
+
reservationUsd: request.reservationUsd,
|
|
1282
|
+
...(overshootUsd > Number.EPSILON ? { reason: "reservation_exceeded", overshootUsd } : {}),
|
|
1127
1283
|
inputTokens: usage.inputTokens,
|
|
1128
1284
|
outputTokens: usage.outputTokens,
|
|
1129
1285
|
cachedInputTokens: usage.cachedInputTokens,
|
|
1130
1286
|
cacheWriteTokens: usage.cacheWriteTokens,
|
|
1131
1287
|
reasoningTokens: usage.reasoningTokens,
|
|
1132
1288
|
});
|
|
1133
|
-
|
|
1289
|
+
// A cached hold may be exceeded when the cache misses (settled, audited above); the ceiling may not.
|
|
1290
|
+
const ceilingUsd = request.ceilingUsd ?? request.reservationUsd;
|
|
1291
|
+
if (costUsd > ceilingUsd + Number.EPSILON) {
|
|
1134
1292
|
await this.ledger.cancelSession(session.id);
|
|
1135
1293
|
throw new CodingProxyError(502, "reservation_invariant_violated");
|
|
1136
1294
|
}
|
|
@@ -1156,7 +1314,7 @@ export class CodingProxy {
|
|
|
1156
1314
|
await safeWrite(sink, bytes, { value: true });
|
|
1157
1315
|
sink.end();
|
|
1158
1316
|
}
|
|
1159
|
-
async forwardStream(session, request, upstream, sink) {
|
|
1317
|
+
async forwardStream(session, request, upstream, sink, progress) {
|
|
1160
1318
|
if (!upstream.body)
|
|
1161
1319
|
throw new Error("missing_upstream_body");
|
|
1162
1320
|
sink.start(upstream.status, { "content-type": "text/event-stream", "cache-control": "no-store" });
|
|
@@ -1166,7 +1324,7 @@ export class CodingProxy {
|
|
|
1166
1324
|
let receivedBytes = 0;
|
|
1167
1325
|
let completed = false;
|
|
1168
1326
|
let failedWithoutUsage = false;
|
|
1169
|
-
const anthropicUsage = session.protocol === "anthropic-messages" ? new AnthropicSseUsageTracker() : undefined;
|
|
1327
|
+
const anthropicUsage = session.protocol === "anthropic-messages" ? (progress.anthropic = new AnthropicSseUsageTracker()) : undefined;
|
|
1170
1328
|
const body = upstream.body;
|
|
1171
1329
|
for await (const chunk of body) {
|
|
1172
1330
|
receivedBytes += chunk.byteLength;
|
|
@@ -1181,6 +1339,7 @@ export class CodingProxy {
|
|
|
1181
1339
|
buffer = buffer.slice(boundary + 2);
|
|
1182
1340
|
const terminal = anthropicUsage ? anthropicUsage.consume(frame) : terminalUsageFromSseFrame(frame);
|
|
1183
1341
|
if (!terminal.terminal) {
|
|
1342
|
+
progress.relayedOutputBytes += relayedTextBytes(frame, session.protocol);
|
|
1184
1343
|
await safeWrite(sink, Buffer.from(`${frame}\n\n`), connected);
|
|
1185
1344
|
continue;
|
|
1186
1345
|
}
|
|
@@ -1191,7 +1350,7 @@ export class CodingProxy {
|
|
|
1191
1350
|
// client sees the real error instead of a cut connection; the reservation stays held.
|
|
1192
1351
|
await this.recordUpstreamFailure(session, terminal.failure);
|
|
1193
1352
|
if (!completed) {
|
|
1194
|
-
await this.markUncertain(session, request, upstream.status, `upstream_failed:${terminal.failure}`, responseHeadersOf(upstream));
|
|
1353
|
+
await this.markUncertain(session, request, upstream.status, `upstream_failed:${terminal.failure}`, responseHeadersOf(upstream), progress);
|
|
1195
1354
|
}
|
|
1196
1355
|
failedWithoutUsage = true;
|
|
1197
1356
|
await safeWrite(sink, Buffer.from(`${frame}\n\n`), connected);
|
|
@@ -1214,9 +1373,102 @@ export class CodingProxy {
|
|
|
1214
1373
|
async recordUpstreamFailure(session, code) {
|
|
1215
1374
|
await this.ledger.recordUpstreamFailure(session.id, code).catch(() => undefined);
|
|
1216
1375
|
}
|
|
1217
|
-
async markUncertain(session, request, status, reason, headers
|
|
1218
|
-
|
|
1219
|
-
|
|
1376
|
+
async markUncertain(session, request, status, reason, headers, progress) {
|
|
1377
|
+
if (progress.marked)
|
|
1378
|
+
return;
|
|
1379
|
+
progress.marked = true;
|
|
1380
|
+
let estimate = await this.estimateInterrupted(session, request, progress);
|
|
1381
|
+
if (estimate) {
|
|
1382
|
+
try {
|
|
1383
|
+
await this.ledger.markUncertain(request.id, status, estimate);
|
|
1384
|
+
}
|
|
1385
|
+
catch {
|
|
1386
|
+
// Settling at the estimate failed: mark it uncertain without one, so it keeps its full hold.
|
|
1387
|
+
estimate = undefined;
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
if (!estimate)
|
|
1391
|
+
await this.ledger.markUncertain(request.id, status);
|
|
1392
|
+
this.audit({
|
|
1393
|
+
type: "request.uncertain",
|
|
1394
|
+
runId: session.runId,
|
|
1395
|
+
requestId: request.id,
|
|
1396
|
+
status,
|
|
1397
|
+
reason,
|
|
1398
|
+
...(estimate ? { estimatedCostUsd: estimate.costUsd } : {}),
|
|
1399
|
+
...headers,
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
/**
|
|
1403
|
+
* The best known cost of a request that broke before its final usage (interrupted.ts), or
|
|
1404
|
+
* undefined when none can be made: the request then keeps its full hold. Never throws.
|
|
1405
|
+
*/
|
|
1406
|
+
async estimateInterrupted(session, request, progress) {
|
|
1407
|
+
try {
|
|
1408
|
+
const hints = await this.ledger.settlementHints(session.id, request.model);
|
|
1409
|
+
const estimate = estimateInterrupted({
|
|
1410
|
+
...progress.anthropic?.partial(),
|
|
1411
|
+
beforeResponse: progress.stream && !progress.responseStarted,
|
|
1412
|
+
relayedOutputBytes: progress.relayedOutputBytes,
|
|
1413
|
+
...hints,
|
|
1414
|
+
...(progress.previousInput ? { previous: progress.previousInput } : {}),
|
|
1415
|
+
bodyBytes: progress.bodyBytes,
|
|
1416
|
+
reservationUsd: request.reservationUsd,
|
|
1417
|
+
maxOutputTokens: progress.maxOutputTokens,
|
|
1418
|
+
ceilingUsd: request.ceilingUsd ?? request.reservationUsd,
|
|
1419
|
+
}, request.pricing);
|
|
1420
|
+
return Number.isFinite(estimate.costUsd) ? estimate : undefined;
|
|
1421
|
+
}
|
|
1422
|
+
catch {
|
|
1423
|
+
return undefined;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
/** OpenAI Responses events whose string `delta` is generated output: text, tool input, reasoning. */
|
|
1428
|
+
const OPENAI_OUTPUT_DELTAS = new Set([
|
|
1429
|
+
"response.output_text.delta",
|
|
1430
|
+
"response.refusal.delta",
|
|
1431
|
+
"response.function_call_arguments.delta",
|
|
1432
|
+
"response.custom_tool_call_input.delta",
|
|
1433
|
+
"response.reasoning_text.delta",
|
|
1434
|
+
"response.reasoning_summary_text.delta",
|
|
1435
|
+
]);
|
|
1436
|
+
/** The generated-output field of each Anthropic content_block_delta type (signatures are not output). */
|
|
1437
|
+
const ANTHROPIC_OUTPUT_DELTAS = {
|
|
1438
|
+
text_delta: "text",
|
|
1439
|
+
input_json_delta: "partial_json",
|
|
1440
|
+
thinking_delta: "thinking",
|
|
1441
|
+
};
|
|
1442
|
+
/**
|
|
1443
|
+
* UTF-8 bytes of generated output in a relayed SSE frame: text, tool-call input and thinking or
|
|
1444
|
+
* reasoning deltas, which are most of a coding agent's output.
|
|
1445
|
+
*/
|
|
1446
|
+
function relayedTextBytes(frame, protocol) {
|
|
1447
|
+
if (!frame.includes(protocol === "anthropic-messages" ? "content_block_delta" : ".delta"))
|
|
1448
|
+
return 0;
|
|
1449
|
+
try {
|
|
1450
|
+
const data = frame
|
|
1451
|
+
.split("\n")
|
|
1452
|
+
.filter((line) => line.startsWith("data:"))
|
|
1453
|
+
.map((line) => line.slice(5).trimStart())
|
|
1454
|
+
.join("\n");
|
|
1455
|
+
const value = JSON.parse(data);
|
|
1456
|
+
if (!value || typeof value.type !== "string")
|
|
1457
|
+
return 0;
|
|
1458
|
+
if (protocol === "openai-responses") {
|
|
1459
|
+
return OPENAI_OUTPUT_DELTAS.has(value.type) && typeof value.delta === "string"
|
|
1460
|
+
? Buffer.byteLength(value.delta)
|
|
1461
|
+
: 0;
|
|
1462
|
+
}
|
|
1463
|
+
if (value.type !== "content_block_delta")
|
|
1464
|
+
return 0;
|
|
1465
|
+
const delta = value.delta;
|
|
1466
|
+
const field = typeof delta?.type === "string" ? ANTHROPIC_OUTPUT_DELTAS[delta.type] : undefined;
|
|
1467
|
+
const text = field ? delta?.[field] : undefined;
|
|
1468
|
+
return typeof text === "string" ? Buffer.byteLength(text) : 0;
|
|
1469
|
+
}
|
|
1470
|
+
catch {
|
|
1471
|
+
return 0;
|
|
1220
1472
|
}
|
|
1221
1473
|
}
|
|
1222
1474
|
/** The safe error code in a rejected upstream's JSON body, or undefined when it names none or cannot be read. */
|