@wardby/cli 0.1.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 +131 -0
- package/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.md +282 -0
- package/bin/wardby.js +3 -0
- package/deploy/local/docker-compose.yml +19 -0
- package/dist/claude-coding-worker/driver.d.ts +70 -0
- package/dist/claude-coding-worker/driver.js +128 -0
- package/dist/claude-coding-worker/main.d.ts +2 -0
- package/dist/claude-coding-worker/main.js +41 -0
- package/dist/claude-coding-worker/sdk.d.ts +4 -0
- package/dist/claude-coding-worker/sdk.js +43 -0
- package/dist/claude-coding-worker/tool-relay.d.ts +1 -0
- package/dist/claude-coding-worker/tool-relay.js +9 -0
- package/dist/cli-help.d.ts +1 -0
- package/dist/cli-help.js +20 -0
- package/dist/cli.d.ts +18 -0
- package/dist/cli.js +629 -0
- package/dist/coding/observability.d.ts +59 -0
- package/dist/coding/observability.js +77 -0
- package/dist/coding/profile.d.ts +88 -0
- package/dist/coding/profile.js +137 -0
- package/dist/coding/protocol.d.ts +345 -0
- package/dist/coding/protocol.js +445 -0
- package/dist/coding/provider.d.ts +4 -0
- package/dist/coding/provider.js +18 -0
- package/dist/coding-proxy/main.d.ts +1 -0
- package/dist/coding-proxy/main.js +51 -0
- package/dist/coding-worker/artifact.d.ts +16 -0
- package/dist/coding-worker/artifact.js +37 -0
- package/dist/coding-worker/driver.d.ts +46 -0
- package/dist/coding-worker/driver.js +158 -0
- package/dist/coding-worker/errors.d.ts +1 -0
- package/dist/coding-worker/errors.js +23 -0
- package/dist/coding-worker/isolation-probe.d.ts +1 -0
- package/dist/coding-worker/isolation-probe.js +146 -0
- package/dist/coding-worker/keeper.d.ts +4 -0
- package/dist/coding-worker/keeper.js +29 -0
- package/dist/coding-worker/main.d.ts +2 -0
- package/dist/coding-worker/main.js +43 -0
- package/dist/coding-worker/sdk.d.ts +26 -0
- package/dist/coding-worker/sdk.js +28 -0
- package/dist/coding-worker/types.d.ts +64 -0
- package/dist/coding-worker/types.js +1 -0
- package/dist/config/providers.d.ts +158 -0
- package/dist/config/providers.js +162 -0
- package/dist/core/budget-groups.d.ts +72 -0
- package/dist/core/budget-groups.js +149 -0
- package/dist/core/budget.d.ts +63 -0
- package/dist/core/budget.js +69 -0
- package/dist/core/coding-queue.d.ts +32 -0
- package/dist/core/coding-queue.js +54 -0
- package/dist/core/cron.d.ts +31 -0
- package/dist/core/cron.js +43 -0
- package/dist/core/datastores.d.ts +41 -0
- package/dist/core/datastores.js +105 -0
- package/dist/core/db.d.ts +2 -0
- package/dist/core/db.js +2 -0
- package/dist/core/dispatch.d.ts +81 -0
- package/dist/core/dispatch.js +184 -0
- package/dist/core/engine-native.d.ts +29 -0
- package/dist/core/engine-native.js +259 -0
- package/dist/core/http-runtime.d.ts +7 -0
- package/dist/core/http-runtime.js +101 -0
- package/dist/core/lease.d.ts +13 -0
- package/dist/core/lease.js +22 -0
- package/dist/core/logger.d.ts +13 -0
- package/dist/core/logger.js +45 -0
- package/dist/core/memory-tools.d.ts +11 -0
- package/dist/core/memory-tools.js +122 -0
- package/dist/core/reconciler.d.ts +45 -0
- package/dist/core/reconciler.js +152 -0
- package/dist/core/runner.d.ts +87 -0
- package/dist/core/runner.js +529 -0
- package/dist/core/scheduler.d.ts +47 -0
- package/dist/core/scheduler.js +121 -0
- package/dist/core/secrets.d.ts +46 -0
- package/dist/core/secrets.js +100 -0
- package/dist/core/subagent-memory-tools.d.ts +18 -0
- package/dist/core/subagent-memory-tools.js +121 -0
- package/dist/core/timing.d.ts +7 -0
- package/dist/core/timing.js +7 -0
- package/dist/core/webhooks.d.ts +23 -0
- package/dist/core/webhooks.js +113 -0
- package/dist/env.d.ts +11 -0
- package/dist/env.js +13 -0
- package/dist/import/budgets.d.ts +16 -0
- package/dist/import/budgets.js +21 -0
- package/dist/import/bundle.d.ts +16 -0
- package/dist/import/bundle.js +61 -0
- package/dist/import/cli-args.d.ts +2 -0
- package/dist/import/cli-args.js +44 -0
- package/dist/import/create.d.ts +34 -0
- package/dist/import/create.js +363 -0
- package/dist/import/index.d.ts +25 -0
- package/dist/import/index.js +130 -0
- package/dist/import/model-gate.d.ts +1 -0
- package/dist/import/model-gate.js +3 -0
- package/dist/import/neutral-schema.d.ts +369 -0
- package/dist/import/neutral-schema.js +109 -0
- package/dist/import/preflight.d.ts +41 -0
- package/dist/import/preflight.js +71 -0
- package/dist/import/report.d.ts +5 -0
- package/dist/import/report.js +98 -0
- package/dist/import/tool-scan.d.ts +8 -0
- package/dist/import/tool-scan.js +20 -0
- package/dist/mcp/auth/ownership.d.ts +113 -0
- package/dist/mcp/auth/ownership.js +87 -0
- package/dist/mcp/auth/principal.d.ts +9 -0
- package/dist/mcp/auth/principal.js +9 -0
- package/dist/mcp/auth/resource-server.d.ts +47 -0
- package/dist/mcp/auth/resource-server.js +82 -0
- package/dist/mcp/auth/self-hosted/browser.d.ts +3 -0
- package/dist/mcp/auth/self-hosted/browser.js +273 -0
- package/dist/mcp/auth/self-hosted/cli.d.ts +2 -0
- package/dist/mcp/auth/self-hosted/cli.js +39 -0
- package/dist/mcp/auth/self-hosted/credentials.d.ts +58 -0
- package/dist/mcp/auth/self-hosted/credentials.js +106 -0
- package/dist/mcp/auth/self-hosted/rate-limit.d.ts +13 -0
- package/dist/mcp/auth/self-hosted/rate-limit.js +20 -0
- package/dist/mcp/auth/self-hosted/session.d.ts +36 -0
- package/dist/mcp/auth/self-hosted/session.js +110 -0
- package/dist/mcp/capabilities.d.ts +11 -0
- package/dist/mcp/capabilities.js +10 -0
- package/dist/mcp/context.d.ts +46 -0
- package/dist/mcp/context.js +1 -0
- package/dist/mcp/errors.d.ts +13 -0
- package/dist/mcp/errors.js +22 -0
- package/dist/mcp/index.d.ts +32 -0
- package/dist/mcp/index.js +187 -0
- package/dist/mcp/jsonrpc.d.ts +8 -0
- package/dist/mcp/jsonrpc.js +1 -0
- package/dist/mcp/server.d.ts +107 -0
- package/dist/mcp/server.js +203 -0
- package/dist/mcp/tasks/manager.d.ts +63 -0
- package/dist/mcp/tasks/manager.js +103 -0
- package/dist/mcp/tools/agents.d.ts +2 -0
- package/dist/mcp/tools/agents.js +375 -0
- package/dist/mcp/tools/budget-groups.d.ts +2 -0
- package/dist/mcp/tools/budget-groups.js +136 -0
- package/dist/mcp/tools/datastore.d.ts +2 -0
- package/dist/mcp/tools/datastore.js +217 -0
- package/dist/mcp/tools/memory.d.ts +3 -0
- package/dist/mcp/tools/memory.js +60 -0
- package/dist/mcp/tools/models.d.ts +8 -0
- package/dist/mcp/tools/models.js +15 -0
- package/dist/mcp/tools/runs.d.ts +2 -0
- package/dist/mcp/tools/runs.js +61 -0
- package/dist/mcp/tools/scheduling.d.ts +2 -0
- package/dist/mcp/tools/scheduling.js +48 -0
- package/dist/mcp/tools/secret-elicitation-form.d.ts +22 -0
- package/dist/mcp/tools/secret-elicitation-form.js +93 -0
- package/dist/mcp/tools/secret-elicitation-server.d.ts +5 -0
- package/dist/mcp/tools/secret-elicitation-server.js +57 -0
- package/dist/mcp/tools/secret-elicitation.d.ts +38 -0
- package/dist/mcp/tools/secret-elicitation.js +52 -0
- package/dist/mcp/tools/secrets.d.ts +13 -0
- package/dist/mcp/tools/secrets.js +124 -0
- package/dist/mcp/tools/subagents.d.ts +2 -0
- package/dist/mcp/tools/subagents.js +133 -0
- package/dist/mcp/tools/text-result.d.ts +6 -0
- package/dist/mcp/tools/text-result.js +3 -0
- package/dist/mcp/tools/tools.d.ts +2 -0
- package/dist/mcp/tools/tools.js +211 -0
- package/dist/mcp/tools/trigger.d.ts +2 -0
- package/dist/mcp/tools/trigger.js +120 -0
- package/dist/mcp/tools/webhooks.d.ts +2 -0
- package/dist/mcp/tools/webhooks.js +34 -0
- package/dist/mcp/transport/http-limits.d.ts +17 -0
- package/dist/mcp/transport/http-limits.js +92 -0
- package/dist/mcp/transport/stdio.d.ts +23 -0
- package/dist/mcp/transport/stdio.js +6 -0
- package/dist/mcp/transport/streamable-http.d.ts +34 -0
- package/dist/mcp/transport/streamable-http.js +158 -0
- package/dist/mcp/unattended-schedules.d.ts +15 -0
- package/dist/mcp/unattended-schedules.js +21 -0
- package/dist/mcp/webhooks/ingress.d.ts +17 -0
- package/dist/mcp/webhooks/ingress.js +33 -0
- package/dist/observability/config.d.ts +6 -0
- package/dist/observability/config.js +20 -0
- package/dist/observability/metrics-server.d.ts +11 -0
- package/dist/observability/metrics-server.js +47 -0
- package/dist/observability/metrics.d.ts +36 -0
- package/dist/observability/metrics.js +158 -0
- package/dist/providers/auth/authorization-server.d.ts +41 -0
- package/dist/providers/auth/authorization-server.js +1 -0
- package/dist/providers/auth/delegating.d.ts +44 -0
- package/dist/providers/auth/delegating.js +105 -0
- package/dist/providers/auth/index.d.ts +11 -0
- package/dist/providers/auth/index.js +20 -0
- package/dist/providers/auth/release-gate.d.ts +1 -0
- package/dist/providers/auth/release-gate.js +4 -0
- package/dist/providers/auth/self-hosted.d.ts +78 -0
- package/dist/providers/auth/self-hosted.js +377 -0
- package/dist/providers/auth/subject.d.ts +1 -0
- package/dist/providers/auth/subject.js +6 -0
- package/dist/providers/auth/types.d.ts +63 -0
- package/dist/providers/auth/types.js +22 -0
- package/dist/providers/coding-proxy/deny-port.d.ts +5 -0
- package/dist/providers/coding-proxy/deny-port.js +45 -0
- package/dist/providers/coding-proxy/environment-credentials.d.ts +7 -0
- package/dist/providers/coding-proxy/environment-credentials.js +16 -0
- package/dist/providers/coding-proxy/index.d.ts +7 -0
- package/dist/providers/coding-proxy/index.js +7 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +17 -0
- package/dist/providers/coding-proxy/memory-ledger.js +103 -0
- package/dist/providers/coding-proxy/metering.d.ts +20 -0
- package/dist/providers/coding-proxy/metering.js +161 -0
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +15 -0
- package/dist/providers/coding-proxy/prisma-ledger.js +213 -0
- package/dist/providers/coding-proxy/proxy.d.ts +75 -0
- package/dist/providers/coding-proxy/proxy.js +721 -0
- package/dist/providers/coding-proxy/runtime.d.ts +18 -0
- package/dist/providers/coding-proxy/runtime.js +39 -0
- package/dist/providers/coding-proxy/secure-fetch.d.ts +9 -0
- package/dist/providers/coding-proxy/secure-fetch.js +72 -0
- package/dist/providers/coding-proxy/server.d.ts +17 -0
- package/dist/providers/coding-proxy/server.js +139 -0
- package/dist/providers/coding-proxy/types.d.ts +99 -0
- package/dist/providers/coding-proxy/types.js +1 -0
- package/dist/providers/datastore/index.d.ts +2 -0
- package/dist/providers/datastore/index.js +2 -0
- package/dist/providers/datastore/postgres.d.ts +20 -0
- package/dist/providers/datastore/postgres.js +130 -0
- package/dist/providers/datastore/scoped.d.ts +10 -0
- package/dist/providers/datastore/scoped.js +46 -0
- package/dist/providers/datastore/types.d.ts +31 -0
- package/dist/providers/datastore/types.js +7 -0
- package/dist/providers/email/types.d.ts +46 -0
- package/dist/providers/email/types.js +8 -0
- package/dist/providers/engine/index.d.ts +1 -0
- package/dist/providers/engine/index.js +1 -0
- package/dist/providers/engine/types.d.ts +67 -0
- package/dist/providers/engine/types.js +16 -0
- package/dist/providers/executor/build.d.ts +6 -0
- package/dist/providers/executor/build.js +15 -0
- package/dist/providers/executor/composition.d.ts +14 -0
- package/dist/providers/executor/composition.js +123 -0
- package/dist/providers/executor/container.d.ts +184 -0
- package/dist/providers/executor/container.js +936 -0
- package/dist/providers/executor/dbos-status.d.ts +50 -0
- package/dist/providers/executor/dbos-status.js +64 -0
- package/dist/providers/executor/dbos.d.ts +53 -0
- package/dist/providers/executor/dbos.js +269 -0
- package/dist/providers/executor/in-process.d.ts +18 -0
- package/dist/providers/executor/in-process.js +38 -0
- package/dist/providers/executor/index.d.ts +8 -0
- package/dist/providers/executor/index.js +8 -0
- package/dist/providers/executor/routing.d.ts +48 -0
- package/dist/providers/executor/routing.js +70 -0
- package/dist/providers/executor/types.d.ts +55 -0
- package/dist/providers/executor/types.js +1 -0
- package/dist/providers/index.d.ts +43 -0
- package/dist/providers/index.js +18 -0
- package/dist/providers/jobs/docker-isolation.d.ts +166 -0
- package/dist/providers/jobs/docker-isolation.js +724 -0
- package/dist/providers/jobs/docker.d.ts +127 -0
- package/dist/providers/jobs/docker.js +1027 -0
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +98 -0
- package/dist/providers/jobs/fake-kubernetes-api.js +142 -0
- package/dist/providers/jobs/fake.d.ts +23 -0
- package/dist/providers/jobs/fake.js +132 -0
- package/dist/providers/jobs/kubernetes-api.d.ts +60 -0
- package/dist/providers/jobs/kubernetes-api.js +20 -0
- package/dist/providers/jobs/kubernetes-client.d.ts +36 -0
- package/dist/providers/jobs/kubernetes-client.js +171 -0
- package/dist/providers/jobs/kubernetes-dry-run-fixture.d.ts +49 -0
- package/dist/providers/jobs/kubernetes-dry-run-fixture.js +141 -0
- package/dist/providers/jobs/kubernetes-isolation.d.ts +147 -0
- package/dist/providers/jobs/kubernetes-isolation.js +607 -0
- package/dist/providers/jobs/kubernetes-platform.d.ts +177 -0
- package/dist/providers/jobs/kubernetes-platform.js +280 -0
- package/dist/providers/jobs/kubernetes-preflight.d.ts +59 -0
- package/dist/providers/jobs/kubernetes-preflight.js +339 -0
- package/dist/providers/jobs/kubernetes-witness.d.ts +10 -0
- package/dist/providers/jobs/kubernetes-witness.js +193 -0
- package/dist/providers/jobs/kubernetes.d.ts +127 -0
- package/dist/providers/jobs/kubernetes.js +874 -0
- package/dist/providers/jobs/safe-extract.d.ts +9 -0
- package/dist/providers/jobs/safe-extract.js +325 -0
- package/dist/providers/jobs/types.d.ts +78 -0
- package/dist/providers/jobs/types.js +6 -0
- package/dist/providers/jobs/workspace-swap.d.ts +2 -0
- package/dist/providers/jobs/workspace-swap.js +47 -0
- package/dist/providers/llm/anthropic.d.ts +13 -0
- package/dist/providers/llm/anthropic.js +21 -0
- package/dist/providers/llm/bedrock.d.ts +19 -0
- package/dist/providers/llm/bedrock.js +33 -0
- package/dist/providers/llm/claude-messages.d.ts +106 -0
- package/dist/providers/llm/claude-messages.js +157 -0
- package/dist/providers/llm/claude-provider.d.ts +40 -0
- package/dist/providers/llm/claude-provider.js +31 -0
- package/dist/providers/llm/index.d.ts +10 -0
- package/dist/providers/llm/index.js +10 -0
- package/dist/providers/llm/openai.d.ts +23 -0
- package/dist/providers/llm/openai.js +156 -0
- package/dist/providers/llm/pricing-anthropic.d.ts +4 -0
- package/dist/providers/llm/pricing-anthropic.js +30 -0
- package/dist/providers/llm/pricing-bedrock-claude.d.ts +12 -0
- package/dist/providers/llm/pricing-bedrock-claude.js +37 -0
- package/dist/providers/llm/pricing-core.d.ts +26 -0
- package/dist/providers/llm/pricing-core.js +17 -0
- package/dist/providers/llm/pricing.d.ts +30 -0
- package/dist/providers/llm/pricing.js +74 -0
- package/dist/providers/llm/registration.d.ts +8 -0
- package/dist/providers/llm/registration.js +39 -0
- package/dist/providers/llm/routing.d.ts +25 -0
- package/dist/providers/llm/routing.js +32 -0
- package/dist/providers/llm/types.d.ts +85 -0
- package/dist/providers/llm/types.js +12 -0
- package/dist/providers/memory/index.d.ts +2 -0
- package/dist/providers/memory/index.js +2 -0
- package/dist/providers/memory/postgres.d.ts +20 -0
- package/dist/providers/memory/postgres.js +72 -0
- package/dist/providers/memory/types.d.ts +32 -0
- package/dist/providers/memory/types.js +17 -0
- package/dist/providers/secrets/app-key.d.ts +8 -0
- package/dist/providers/secrets/app-key.js +50 -0
- package/dist/providers/secrets/index.d.ts +10 -0
- package/dist/providers/secrets/index.js +13 -0
- package/dist/providers/secrets/transfer-envelope.d.ts +12 -0
- package/dist/providers/secrets/transfer-envelope.js +35 -0
- package/dist/providers/secrets/types.d.ts +18 -0
- package/dist/providers/secrets/types.js +12 -0
- package/dist/providers/storage/types.d.ts +18 -0
- package/dist/providers/storage/types.js +7 -0
- package/dist/providers/vcs/git.d.ts +105 -0
- package/dist/providers/vcs/git.js +710 -0
- package/dist/providers/vcs/github.d.ts +105 -0
- package/dist/providers/vcs/github.js +394 -0
- package/dist/providers/vcs/index.d.ts +6 -0
- package/dist/providers/vcs/index.js +26 -0
- package/dist/providers/vcs/types.d.ts +118 -0
- package/dist/providers/vcs/types.js +1 -0
- package/dist/sandbox/bounded-json.d.ts +2 -0
- package/dist/sandbox/bounded-json.js +36 -0
- package/dist/sandbox/bridge.d.ts +15 -0
- package/dist/sandbox/bridge.js +78 -0
- package/dist/sandbox/eval-core.d.ts +37 -0
- package/dist/sandbox/eval-core.js +159 -0
- package/dist/sandbox/fetch-policy.d.ts +24 -0
- package/dist/sandbox/fetch-policy.js +96 -0
- package/dist/sandbox/generated/zod-to-json-schema.bundle.js +5 -0
- package/dist/sandbox/generated/zod.bundle.js +1 -0
- package/dist/sandbox/host-functions.d.ts +29 -0
- package/dist/sandbox/host-functions.js +168 -0
- package/dist/sandbox/limits.d.ts +39 -0
- package/dist/sandbox/limits.js +39 -0
- package/dist/sandbox/parser-worker/__fixtures__/crash.d.ts +1 -0
- package/dist/sandbox/parser-worker/__fixtures__/crash.js +6 -0
- package/dist/sandbox/parser-worker/__fixtures__/echo.d.ts +1 -0
- package/dist/sandbox/parser-worker/__fixtures__/echo.js +5 -0
- package/dist/sandbox/parser-worker/__fixtures__/reject.d.ts +1 -0
- package/dist/sandbox/parser-worker/__fixtures__/reject.js +5 -0
- package/dist/sandbox/parser-worker/__fixtures__/spin-forever.d.ts +1 -0
- package/dist/sandbox/parser-worker/__fixtures__/spin-forever.js +11 -0
- package/dist/sandbox/parser-worker/pool.d.ts +13 -0
- package/dist/sandbox/parser-worker/pool.js +111 -0
- package/dist/sandbox/parser-worker/worker.d.ts +14 -0
- package/dist/sandbox/parser-worker/worker.js +63 -0
- package/dist/sandbox/pii-redaction.d.ts +1 -0
- package/dist/sandbox/pii-redaction.js +29 -0
- package/dist/sandbox/prelude.d.ts +26 -0
- package/dist/sandbox/prelude.js +408 -0
- package/dist/sandbox/quickjs-module.d.ts +13 -0
- package/dist/sandbox/quickjs-module.js +19 -0
- package/dist/sandbox/run-in-sandbox.d.ts +34 -0
- package/dist/sandbox/run-in-sandbox.js +53 -0
- package/dist/sandbox/safe-fetch.d.ts +26 -0
- package/dist/sandbox/safe-fetch.js +140 -0
- package/dist/sandbox/tool-capabilities.d.ts +66 -0
- package/dist/sandbox/tool-capabilities.js +103 -0
- package/dist/sandbox/zod-params.d.ts +22 -0
- package/dist/sandbox/zod-params.js +60 -0
- package/dist/serve.d.ts +14 -0
- package/dist/serve.js +54 -0
- package/dist/wardby-bin.d.ts +2 -0
- package/dist/wardby-bin.js +20 -0
- package/package.json +123 -0
- package/prisma/migrations/20260905000000_init/migration.sql +34 -0
- package/prisma/migrations/20260905010000_scheduler_durability/migration.sql +31 -0
- package/prisma/migrations/20260905020000_tools_multiturn/migration.sql +47 -0
- package/prisma/migrations/20260905030000_cache_tool_json_schema/migration.sql +13 -0
- package/prisma/migrations/20260906000000_mcp_phase4/migration.sql +115 -0
- package/prisma/migrations/20260906010000_run_final_text/migration.sql +7 -0
- package/prisma/migrations/20260906020000_run_turns/migration.sql +6 -0
- package/prisma/migrations/20260906030000_task_principal_id/migration.sql +5 -0
- package/prisma/migrations/20260906040000_secure_self_hosted_oauth/migration.sql +67 -0
- package/prisma/migrations/20260906050000_phase5_coding_agents/migration.sql +52 -0
- package/prisma/migrations/20260906060000_tool_capability_scoping/migration.sql +22 -0
- package/prisma/migrations/20260906070000_datastore_pii_flag/migration.sql +6 -0
- package/prisma/migrations/20260906080000_secret_elicitation_outcomes/migration.sql +13 -0
- package/prisma/migrations/20260907010000_coding_proxy_ledger/migration.sql +57 -0
- package/prisma/migrations/20260907020000_budget_groups/migration.sql +24 -0
- package/prisma/migrations/20260907030000_coding_webhook_task_override/migration.sql +4 -0
- package/prisma/migrations/20260908010000_run_execution_backend/migration.sql +2 -0
- package/prisma/migrations/20260908020000_coding_run_audit_metadata/migration.sql +7 -0
- package/prisma/migrations/20260910010000_agent_secret_bound_name/migration.sql +16 -0
- package/prisma/migrations/20260911010000_coding_worker_toolchains/migration.sql +5 -0
- package/prisma/migrations/20260912010000_coding_provider_contract/migration.sql +24 -0
- package/prisma/migrations/20260912020000_coding_proxy_protocol/migration.sql +10 -0
- package/prisma/migrations/20260912030000_agent_memory/migration.sql +18 -0
- package/prisma/migrations/20260912040000_named_shared_datastores/migration.sql +68 -0
- package/prisma/migrations/20260913000000_run_trigger_webhook/migration.sql +2 -0
- package/prisma/migrations/20260913010000_agent_sub_agent/migration.sql +32 -0
- package/prisma/migrations/20260913020000_run_task_override/migration.sql +5 -0
- package/prisma/migrations/20260913030000_coding_run_root/migration.sql +11 -0
- package/prisma/migrations/20260922010000_coding_run_queued_at/migration.sql +8 -0
- package/prisma/migrations/20260922020000_coding_workspace_disk/migration.sql +8 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +723 -0
- package/scripts/git-askpass.sh +11 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
function cloneSession(session) {
|
|
2
|
+
return { ...session, allowedModels: [...session.allowedModels], deadlineAt: new Date(session.deadlineAt) };
|
|
3
|
+
}
|
|
4
|
+
function cloneRequest(request) {
|
|
5
|
+
return {
|
|
6
|
+
...request,
|
|
7
|
+
pricing: { ...request.pricing },
|
|
8
|
+
usage: request.usage ? { ...request.usage } : undefined,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/** Deterministic contract adapter. Sharing one instance simulates a proxy restart. */
|
|
12
|
+
export class MemoryProxyLedger {
|
|
13
|
+
sessions = new Map();
|
|
14
|
+
sessionByCapability = new Map();
|
|
15
|
+
requests = new Map();
|
|
16
|
+
requestBySessionKey = new Map();
|
|
17
|
+
async createSession(input) {
|
|
18
|
+
if ([...this.sessions.values()].some((session) => session.runId === input.runId)) {
|
|
19
|
+
throw new Error("proxy_session_exists");
|
|
20
|
+
}
|
|
21
|
+
if (this.sessionByCapability.has(input.capabilityHash))
|
|
22
|
+
throw new Error("proxy_capability_collision");
|
|
23
|
+
const session = { ...input, allowedModels: [...input.allowedModels], status: "active" };
|
|
24
|
+
this.sessions.set(session.id, session);
|
|
25
|
+
this.sessionByCapability.set(session.capabilityHash, session.id);
|
|
26
|
+
}
|
|
27
|
+
async findSessionByCapabilityHash(capabilityHash) {
|
|
28
|
+
const id = this.sessionByCapability.get(capabilityHash);
|
|
29
|
+
const session = id ? this.sessions.get(id) : undefined;
|
|
30
|
+
return session ? cloneSession(session) : null;
|
|
31
|
+
}
|
|
32
|
+
async reserve(input) {
|
|
33
|
+
const session = this.sessions.get(input.sessionId);
|
|
34
|
+
if (!session || session.status === "cancelled")
|
|
35
|
+
return { outcome: "inactive", reason: "cancelled" };
|
|
36
|
+
if (input.now.getTime() >= session.deadlineAt.getTime())
|
|
37
|
+
return { outcome: "inactive", reason: "expired" };
|
|
38
|
+
const lookupKey = `${input.sessionId}\0${input.requestKey}`;
|
|
39
|
+
const existingId = this.requestBySessionKey.get(lookupKey);
|
|
40
|
+
if (existingId)
|
|
41
|
+
return { outcome: "duplicate", request: cloneRequest(this.requests.get(existingId)) };
|
|
42
|
+
let committedOrHeld = 0;
|
|
43
|
+
for (const request of this.requests.values()) {
|
|
44
|
+
if (request.sessionId !== input.sessionId)
|
|
45
|
+
continue;
|
|
46
|
+
if (request.status === "completed")
|
|
47
|
+
committedOrHeld += request.actualCostUsd;
|
|
48
|
+
if (request.status === "reserved" || request.status === "uncertain")
|
|
49
|
+
committedOrHeld += request.reservationUsd;
|
|
50
|
+
}
|
|
51
|
+
if (committedOrHeld + input.reservationUsd >= session.budgetUsd)
|
|
52
|
+
return { outcome: "budget_exhausted" };
|
|
53
|
+
const request = { ...input, pricing: { ...input.pricing }, status: "reserved" };
|
|
54
|
+
this.requests.set(request.id, request);
|
|
55
|
+
this.requestBySessionKey.set(lookupKey, request.id);
|
|
56
|
+
return { outcome: "reserved", request: cloneRequest(request) };
|
|
57
|
+
}
|
|
58
|
+
async complete(requestId, usage, actualCostUsd, upstreamStatus) {
|
|
59
|
+
const request = this.requiredRequest(requestId);
|
|
60
|
+
if (request.status === "completed") {
|
|
61
|
+
if (request.actualCostUsd !== actualCostUsd || JSON.stringify(request.usage) !== JSON.stringify(usage)) {
|
|
62
|
+
throw new Error("proxy_completion_conflict");
|
|
63
|
+
}
|
|
64
|
+
return cloneRequest(request);
|
|
65
|
+
}
|
|
66
|
+
if (request.status === "released")
|
|
67
|
+
throw new Error("proxy_request_released");
|
|
68
|
+
request.status = "completed";
|
|
69
|
+
request.actualCostUsd = actualCostUsd;
|
|
70
|
+
request.usage = { ...usage };
|
|
71
|
+
request.upstreamStatus = upstreamStatus;
|
|
72
|
+
return cloneRequest(request);
|
|
73
|
+
}
|
|
74
|
+
async release(requestId, upstreamStatus) {
|
|
75
|
+
const request = this.requiredRequest(requestId);
|
|
76
|
+
if (request.status === "completed" || request.status === "uncertain")
|
|
77
|
+
return;
|
|
78
|
+
request.status = "released";
|
|
79
|
+
request.upstreamStatus = upstreamStatus;
|
|
80
|
+
}
|
|
81
|
+
async markUncertain(requestId, upstreamStatus) {
|
|
82
|
+
const request = this.requiredRequest(requestId);
|
|
83
|
+
if (request.status !== "reserved")
|
|
84
|
+
return;
|
|
85
|
+
request.status = "uncertain";
|
|
86
|
+
request.upstreamStatus = upstreamStatus;
|
|
87
|
+
}
|
|
88
|
+
async cancelSession(sessionId) {
|
|
89
|
+
const session = this.sessions.get(sessionId);
|
|
90
|
+
if (session)
|
|
91
|
+
session.status = "cancelled";
|
|
92
|
+
}
|
|
93
|
+
async getRequest(requestId) {
|
|
94
|
+
const request = this.requests.get(requestId);
|
|
95
|
+
return request ? cloneRequest(request) : null;
|
|
96
|
+
}
|
|
97
|
+
requiredRequest(requestId) {
|
|
98
|
+
const request = this.requests.get(requestId);
|
|
99
|
+
if (!request)
|
|
100
|
+
throw new Error("unknown_proxy_request");
|
|
101
|
+
return request;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ModelPricing } from "../llm/pricing.js";
|
|
2
|
+
import type { PricingSnapshot, ProxyUsage } from "./types.js";
|
|
3
|
+
export declare function parseAuthoritativeUsage(value: unknown): ProxyUsage;
|
|
4
|
+
export declare function parseAnthropicAuthoritativeUsage(value: unknown): ProxyUsage;
|
|
5
|
+
export declare function actualCostUsd(usage: ProxyUsage, pricing: ModelPricing): number;
|
|
6
|
+
export declare function estimateReservationUsd(bodyBytes: number, maxOutputTokens: number, pricing: ModelPricing): number;
|
|
7
|
+
export declare function fingerprintRequest(body: string): string;
|
|
8
|
+
export interface TerminalUsage {
|
|
9
|
+
terminal: boolean;
|
|
10
|
+
usage?: ProxyUsage;
|
|
11
|
+
}
|
|
12
|
+
export declare function terminalUsageFromSseFrame(frame: string): TerminalUsage;
|
|
13
|
+
/** Accumulates Anthropic's split input/output usage until message_stop. */
|
|
14
|
+
export declare class AnthropicSseUsageTracker {
|
|
15
|
+
private startUsage?;
|
|
16
|
+
private outputTokens?;
|
|
17
|
+
private stopped;
|
|
18
|
+
consume(frame: string): TerminalUsage;
|
|
19
|
+
}
|
|
20
|
+
export declare function pricingSnapshot(version: string, pricing: ModelPricing): PricingSnapshot;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { computeCost } from "../llm/pricing.js";
|
|
3
|
+
const MAX_SAFE_TOKENS = 2_000_000_000;
|
|
4
|
+
function tokenCount(value, name) {
|
|
5
|
+
if (!Number.isSafeInteger(value) || value < 0 || value > MAX_SAFE_TOKENS) {
|
|
6
|
+
throw new Error(`invalid_authoritative_usage:${name}`);
|
|
7
|
+
}
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
export function parseAuthoritativeUsage(value) {
|
|
11
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
12
|
+
throw new Error("invalid_authoritative_usage");
|
|
13
|
+
const usage = value;
|
|
14
|
+
const inputDetails = usage.input_tokens_details && typeof usage.input_tokens_details === "object"
|
|
15
|
+
? usage.input_tokens_details
|
|
16
|
+
: {};
|
|
17
|
+
const outputDetails = usage.output_tokens_details && typeof usage.output_tokens_details === "object"
|
|
18
|
+
? usage.output_tokens_details
|
|
19
|
+
: {};
|
|
20
|
+
const parsed = {
|
|
21
|
+
inputTokens: tokenCount(usage.input_tokens, "input_tokens"),
|
|
22
|
+
outputTokens: tokenCount(usage.output_tokens, "output_tokens"),
|
|
23
|
+
cachedInputTokens: tokenCount(inputDetails.cached_tokens ?? 0, "cached_tokens"),
|
|
24
|
+
cacheWriteTokens: tokenCount(inputDetails.cache_write_tokens ?? 0, "cache_write_tokens"),
|
|
25
|
+
reasoningTokens: tokenCount(outputDetails.reasoning_tokens ?? 0, "reasoning_tokens"),
|
|
26
|
+
};
|
|
27
|
+
if (parsed.cachedInputTokens > parsed.inputTokens || parsed.reasoningTokens > parsed.outputTokens) {
|
|
28
|
+
throw new Error("invalid_authoritative_usage:details_exceed_total");
|
|
29
|
+
}
|
|
30
|
+
return parsed;
|
|
31
|
+
}
|
|
32
|
+
export function parseAnthropicAuthoritativeUsage(value) {
|
|
33
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
34
|
+
throw new Error("invalid_authoritative_usage");
|
|
35
|
+
const usage = value;
|
|
36
|
+
const cachedInputTokens = tokenCount(usage.cache_read_input_tokens ?? 0, "cache_read_input_tokens");
|
|
37
|
+
return {
|
|
38
|
+
inputTokens: tokenCount(usage.input_tokens, "input_tokens") + cachedInputTokens,
|
|
39
|
+
outputTokens: tokenCount(usage.output_tokens, "output_tokens"),
|
|
40
|
+
cachedInputTokens,
|
|
41
|
+
cacheWriteTokens: tokenCount(usage.cache_creation_input_tokens ?? 0, "cache_creation_input_tokens"),
|
|
42
|
+
reasoningTokens: 0,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function actualCostUsd(usage, pricing) {
|
|
46
|
+
return computeCost(pricing, usage);
|
|
47
|
+
}
|
|
48
|
+
export function estimateReservationUsd(bodyBytes, maxOutputTokens, pricing) {
|
|
49
|
+
if (!Number.isSafeInteger(bodyBytes) || bodyBytes < 1)
|
|
50
|
+
throw new Error("invalid_request_size");
|
|
51
|
+
if (!Number.isSafeInteger(maxOutputTokens) || maxOutputTokens < 1)
|
|
52
|
+
throw new Error("invalid_max_output_tokens");
|
|
53
|
+
const cacheWriteRate = pricing.cacheWritePerMTok ?? pricing.inputPerMTok;
|
|
54
|
+
return (bodyBytes * (pricing.inputPerMTok + cacheWriteRate) + maxOutputTokens * pricing.outputPerMTok) / 1_000_000;
|
|
55
|
+
}
|
|
56
|
+
export function fingerprintRequest(body) {
|
|
57
|
+
return createHash("sha256").update(body).digest("base64url");
|
|
58
|
+
}
|
|
59
|
+
export function terminalUsageFromSseFrame(frame) {
|
|
60
|
+
const data = frame
|
|
61
|
+
.split("\n")
|
|
62
|
+
.filter((line) => line.startsWith("data:"))
|
|
63
|
+
.map((line) => line.slice(5).trimStart())
|
|
64
|
+
.join("\n");
|
|
65
|
+
if (!data || data === "[DONE]")
|
|
66
|
+
return { terminal: false };
|
|
67
|
+
let event;
|
|
68
|
+
try {
|
|
69
|
+
event = JSON.parse(data);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
throw new Error("invalid_upstream_sse");
|
|
73
|
+
}
|
|
74
|
+
if (!event || typeof event !== "object")
|
|
75
|
+
return { terminal: false };
|
|
76
|
+
const record = event;
|
|
77
|
+
if (record.type !== "response.completed" && record.type !== "response.failed")
|
|
78
|
+
return { terminal: false };
|
|
79
|
+
const response = record.response;
|
|
80
|
+
if (!response || typeof response !== "object")
|
|
81
|
+
throw new Error("invalid_upstream_terminal_event");
|
|
82
|
+
const usage = response.usage;
|
|
83
|
+
return { terminal: true, usage: usage == null ? undefined : parseAuthoritativeUsage(usage) };
|
|
84
|
+
}
|
|
85
|
+
function sseEvent(frame) {
|
|
86
|
+
const lines = frame.split("\n");
|
|
87
|
+
const name = lines
|
|
88
|
+
.find((line) => line.startsWith("event:"))
|
|
89
|
+
?.slice(6)
|
|
90
|
+
.trim();
|
|
91
|
+
const data = lines
|
|
92
|
+
.filter((line) => line.startsWith("data:"))
|
|
93
|
+
.map((line) => line.slice(5).trimStart())
|
|
94
|
+
.join("\n");
|
|
95
|
+
if (!data || data === "[DONE]")
|
|
96
|
+
return { name };
|
|
97
|
+
let value;
|
|
98
|
+
try {
|
|
99
|
+
value = JSON.parse(data);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
throw new Error("invalid_upstream_sse");
|
|
103
|
+
}
|
|
104
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
105
|
+
throw new Error("invalid_upstream_sse");
|
|
106
|
+
const record = value;
|
|
107
|
+
if (typeof record.type !== "string" || (name && name !== record.type))
|
|
108
|
+
throw new Error("invalid_upstream_sse");
|
|
109
|
+
return { name, value: record };
|
|
110
|
+
}
|
|
111
|
+
/** Accumulates Anthropic's split input/output usage until message_stop. */
|
|
112
|
+
export class AnthropicSseUsageTracker {
|
|
113
|
+
startUsage;
|
|
114
|
+
outputTokens;
|
|
115
|
+
stopped = false;
|
|
116
|
+
consume(frame) {
|
|
117
|
+
const { value } = sseEvent(frame);
|
|
118
|
+
if (!value)
|
|
119
|
+
return { terminal: false };
|
|
120
|
+
switch (value.type) {
|
|
121
|
+
case "message_start": {
|
|
122
|
+
if (this.startUsage || !value.message || typeof value.message !== "object") {
|
|
123
|
+
throw new Error("invalid_upstream_sse");
|
|
124
|
+
}
|
|
125
|
+
const usage = value.message.usage;
|
|
126
|
+
if (!usage || typeof usage !== "object" || Array.isArray(usage))
|
|
127
|
+
throw new Error("invalid_upstream_sse");
|
|
128
|
+
const record = usage;
|
|
129
|
+
tokenCount(record.input_tokens, "input_tokens");
|
|
130
|
+
tokenCount(record.cache_read_input_tokens ?? 0, "cache_read_input_tokens");
|
|
131
|
+
tokenCount(record.cache_creation_input_tokens ?? 0, "cache_creation_input_tokens");
|
|
132
|
+
this.startUsage = record;
|
|
133
|
+
if (record.output_tokens !== undefined)
|
|
134
|
+
this.outputTokens = tokenCount(record.output_tokens, "output_tokens");
|
|
135
|
+
return { terminal: false };
|
|
136
|
+
}
|
|
137
|
+
case "message_delta": {
|
|
138
|
+
const usage = value.usage;
|
|
139
|
+
if (!usage || typeof usage !== "object" || Array.isArray(usage))
|
|
140
|
+
throw new Error("invalid_upstream_sse");
|
|
141
|
+
this.outputTokens = tokenCount(usage.output_tokens, "output_tokens");
|
|
142
|
+
return { terminal: false };
|
|
143
|
+
}
|
|
144
|
+
case "message_stop": {
|
|
145
|
+
if (this.stopped || !this.startUsage || this.outputTokens === undefined) {
|
|
146
|
+
return { terminal: true };
|
|
147
|
+
}
|
|
148
|
+
this.stopped = true;
|
|
149
|
+
return {
|
|
150
|
+
terminal: true,
|
|
151
|
+
usage: parseAnthropicAuthoritativeUsage({ ...this.startUsage, output_tokens: this.outputTokens }),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
default:
|
|
155
|
+
return { terminal: false };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
export function pricingSnapshot(version, pricing) {
|
|
160
|
+
return { version, ...pricing };
|
|
161
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PrismaClient } from "@prisma/client";
|
|
2
|
+
import type { CreateProxySessionInput, ProxyLedger, ProxyRequest, ProxySession, ProxyUsage, ReserveProxyRequestInput, ReserveProxyRequestResult } from "./types.js";
|
|
3
|
+
export type PrismaProxyLedgerDb = Pick<PrismaClient, "$transaction" | "$queryRaw" | "$executeRaw">;
|
|
4
|
+
export declare class PrismaProxyLedger implements ProxyLedger {
|
|
5
|
+
private readonly db;
|
|
6
|
+
constructor(db: PrismaProxyLedgerDb);
|
|
7
|
+
createSession(input: CreateProxySessionInput): Promise<void>;
|
|
8
|
+
findSessionByCapabilityHash(capabilityHash: string): Promise<ProxySession | null>;
|
|
9
|
+
reserve(input: ReserveProxyRequestInput): Promise<ReserveProxyRequestResult>;
|
|
10
|
+
complete(requestId: string, usage: ProxyUsage, actualCostUsd: number, upstreamStatus: number): Promise<ProxyRequest>;
|
|
11
|
+
release(requestId: string, upstreamStatus: number): Promise<void>;
|
|
12
|
+
markUncertain(requestId: string, upstreamStatus?: number): Promise<void>;
|
|
13
|
+
cancelSession(sessionId: string): Promise<void>;
|
|
14
|
+
getRequest(requestId: string): Promise<ProxyRequest | null>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
function sessionFromRow(row) {
|
|
2
|
+
if (!Array.isArray(row.allowedModels) || !row.allowedModels.every((model) => typeof model === "string")) {
|
|
3
|
+
throw new Error("invalid_proxy_session_models");
|
|
4
|
+
}
|
|
5
|
+
if (row.protocol !== "openai-responses" && row.protocol !== "anthropic-messages") {
|
|
6
|
+
throw new Error("invalid_proxy_session_protocol");
|
|
7
|
+
}
|
|
8
|
+
return {
|
|
9
|
+
id: row.id,
|
|
10
|
+
runId: row.runId,
|
|
11
|
+
capabilityHash: row.capabilityHash,
|
|
12
|
+
credentialRef: row.credentialRef,
|
|
13
|
+
protocol: row.protocol,
|
|
14
|
+
allowedModels: row.allowedModels,
|
|
15
|
+
deadlineAt: row.deadlineAt,
|
|
16
|
+
budgetUsd: Number(row.budgetUsd),
|
|
17
|
+
status: row.status,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function requestFromRow(row) {
|
|
21
|
+
const pricing = row.pricingSnapshot;
|
|
22
|
+
const hasUsage = row.inputTokens !== null && row.outputTokens !== null;
|
|
23
|
+
return {
|
|
24
|
+
id: row.id,
|
|
25
|
+
sessionId: row.sessionId,
|
|
26
|
+
requestKey: row.requestKey,
|
|
27
|
+
requestFingerprint: row.requestFingerprint,
|
|
28
|
+
model: row.model,
|
|
29
|
+
status: row.status,
|
|
30
|
+
reservationUsd: Number(row.reservationUsd),
|
|
31
|
+
actualCostUsd: row.actualCostUsd === null ? undefined : Number(row.actualCostUsd),
|
|
32
|
+
pricing: { ...pricing, version: row.pricingVersion },
|
|
33
|
+
usage: hasUsage
|
|
34
|
+
? {
|
|
35
|
+
inputTokens: row.inputTokens,
|
|
36
|
+
outputTokens: row.outputTokens,
|
|
37
|
+
cachedInputTokens: row.cachedInputTokens ?? 0,
|
|
38
|
+
cacheWriteTokens: row.cacheWriteTokens ?? 0,
|
|
39
|
+
reasoningTokens: row.reasoningTokens ?? 0,
|
|
40
|
+
}
|
|
41
|
+
: undefined,
|
|
42
|
+
upstreamStatus: row.upstreamStatus ?? undefined,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
async function requestById(db, id) {
|
|
46
|
+
const rows = await db.$queryRaw `
|
|
47
|
+
SELECT "id", "sessionId", "requestKey", "requestFingerprint", "model", "status",
|
|
48
|
+
"reservationUsd", "actualCostUsd", "pricingVersion", "pricingSnapshot",
|
|
49
|
+
"inputTokens", "outputTokens", "cachedInputTokens", "cacheWriteTokens",
|
|
50
|
+
"reasoningTokens", "upstreamStatus"
|
|
51
|
+
FROM "CodingProxyRequest" WHERE "id" = ${id}
|
|
52
|
+
`;
|
|
53
|
+
return rows[0] ? requestFromRow(rows[0]) : null;
|
|
54
|
+
}
|
|
55
|
+
export class PrismaProxyLedger {
|
|
56
|
+
db;
|
|
57
|
+
constructor(db) {
|
|
58
|
+
this.db = db;
|
|
59
|
+
}
|
|
60
|
+
async createSession(input) {
|
|
61
|
+
const models = JSON.stringify(input.allowedModels);
|
|
62
|
+
await this.db.$executeRaw `
|
|
63
|
+
INSERT INTO "CodingProxySession"
|
|
64
|
+
("id", "runId", "capabilityHash", "credentialRef", "protocol", "allowedModels", "deadlineAt",
|
|
65
|
+
"budgetUsd", "status", "createdAt", "updatedAt")
|
|
66
|
+
VALUES
|
|
67
|
+
(${input.id}, ${input.runId}, ${input.capabilityHash}, ${input.credentialRef}, ${input.protocol}, ${models}::jsonb,
|
|
68
|
+
${input.deadlineAt}, ${input.budgetUsd}, 'active', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
69
|
+
`;
|
|
70
|
+
}
|
|
71
|
+
async findSessionByCapabilityHash(capabilityHash) {
|
|
72
|
+
const rows = await this.db.$queryRaw `
|
|
73
|
+
SELECT "id", "runId", "capabilityHash", "credentialRef", "protocol", "allowedModels", "deadlineAt", "budgetUsd", "status"
|
|
74
|
+
FROM "CodingProxySession" WHERE "capabilityHash" = ${capabilityHash}
|
|
75
|
+
`;
|
|
76
|
+
return rows[0] ? sessionFromRow(rows[0]) : null;
|
|
77
|
+
}
|
|
78
|
+
async reserve(input) {
|
|
79
|
+
return this.db.$transaction(async (tx) => {
|
|
80
|
+
const sessions = await tx.$queryRaw `
|
|
81
|
+
SELECT "id", "runId", "capabilityHash", "credentialRef", "protocol", "allowedModels", "deadlineAt", "budgetUsd", "status"
|
|
82
|
+
FROM "CodingProxySession" WHERE "id" = ${input.sessionId} FOR UPDATE
|
|
83
|
+
`;
|
|
84
|
+
const row = sessions[0];
|
|
85
|
+
if (!row || row.status !== "active")
|
|
86
|
+
return { outcome: "inactive", reason: "cancelled" };
|
|
87
|
+
if (input.now.getTime() >= row.deadlineAt.getTime())
|
|
88
|
+
return { outcome: "inactive", reason: "expired" };
|
|
89
|
+
const duplicate = await tx.$queryRaw `
|
|
90
|
+
SELECT "id", "sessionId", "requestKey", "requestFingerprint", "model", "status",
|
|
91
|
+
"reservationUsd", "actualCostUsd", "pricingVersion", "pricingSnapshot",
|
|
92
|
+
"inputTokens", "outputTokens", "cachedInputTokens", "cacheWriteTokens",
|
|
93
|
+
"reasoningTokens", "upstreamStatus"
|
|
94
|
+
FROM "CodingProxyRequest"
|
|
95
|
+
WHERE "sessionId" = ${input.sessionId} AND "requestKey" = ${input.requestKey}
|
|
96
|
+
`;
|
|
97
|
+
if (duplicate[0])
|
|
98
|
+
return { outcome: "duplicate", request: requestFromRow(duplicate[0]) };
|
|
99
|
+
const totals = await tx.$queryRaw `
|
|
100
|
+
SELECT COALESCE(SUM(
|
|
101
|
+
CASE
|
|
102
|
+
WHEN "status" = 'completed' THEN "actualCostUsd"
|
|
103
|
+
WHEN "status" IN ('reserved', 'uncertain') THEN "reservationUsd"
|
|
104
|
+
ELSE 0
|
|
105
|
+
END
|
|
106
|
+
), 0) AS "held"
|
|
107
|
+
FROM "CodingProxyRequest" WHERE "sessionId" = ${input.sessionId}
|
|
108
|
+
`;
|
|
109
|
+
if (Number(totals[0]?.held ?? 0) + input.reservationUsd >= Number(row.budgetUsd)) {
|
|
110
|
+
return { outcome: "budget_exhausted" };
|
|
111
|
+
}
|
|
112
|
+
const pricing = JSON.stringify({
|
|
113
|
+
encoding: input.pricing.encoding,
|
|
114
|
+
inputPerMTok: input.pricing.inputPerMTok,
|
|
115
|
+
outputPerMTok: input.pricing.outputPerMTok,
|
|
116
|
+
cachedInputPerMTok: input.pricing.cachedInputPerMTok,
|
|
117
|
+
cacheWritePerMTok: input.pricing.cacheWritePerMTok,
|
|
118
|
+
});
|
|
119
|
+
await tx.$executeRaw `
|
|
120
|
+
INSERT INTO "CodingProxyRequest"
|
|
121
|
+
("id", "sessionId", "requestKey", "requestFingerprint", "model", "status",
|
|
122
|
+
"reservationUsd", "pricingVersion", "pricingSnapshot", "createdAt", "updatedAt")
|
|
123
|
+
VALUES
|
|
124
|
+
(${input.id}, ${input.sessionId}, ${input.requestKey}, ${input.requestFingerprint}, ${input.model},
|
|
125
|
+
'reserved', ${input.reservationUsd}, ${input.pricing.version}, ${pricing}::jsonb,
|
|
126
|
+
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
127
|
+
`;
|
|
128
|
+
const request = await requestById(tx, input.id);
|
|
129
|
+
if (!request)
|
|
130
|
+
throw new Error("proxy_reservation_not_persisted");
|
|
131
|
+
return { outcome: "reserved", request };
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
async complete(requestId, usage, actualCostUsd, upstreamStatus) {
|
|
135
|
+
return this.db.$transaction(async (tx) => {
|
|
136
|
+
const initial = await requestById(tx, requestId);
|
|
137
|
+
if (!initial)
|
|
138
|
+
throw new Error("unknown_proxy_request");
|
|
139
|
+
await tx.$queryRaw `
|
|
140
|
+
SELECT "id" FROM "CodingProxySession" WHERE "id" = ${initial.sessionId} FOR UPDATE
|
|
141
|
+
`;
|
|
142
|
+
const locked = await tx.$queryRaw `
|
|
143
|
+
SELECT "id", "sessionId", "requestKey", "requestFingerprint", "model", "status",
|
|
144
|
+
"reservationUsd", "actualCostUsd", "pricingVersion", "pricingSnapshot",
|
|
145
|
+
"inputTokens", "outputTokens", "cachedInputTokens", "cacheWriteTokens",
|
|
146
|
+
"reasoningTokens", "upstreamStatus"
|
|
147
|
+
FROM "CodingProxyRequest" WHERE "id" = ${requestId} FOR UPDATE
|
|
148
|
+
`;
|
|
149
|
+
const prior = locked[0] ? requestFromRow(locked[0]) : null;
|
|
150
|
+
if (!prior)
|
|
151
|
+
throw new Error("unknown_proxy_request");
|
|
152
|
+
if (prior.status === "released")
|
|
153
|
+
throw new Error("proxy_request_released");
|
|
154
|
+
if (prior.status === "completed") {
|
|
155
|
+
if (Math.abs((prior.actualCostUsd ?? -1) - actualCostUsd) > 1e-12 ||
|
|
156
|
+
JSON.stringify(prior.usage) !== JSON.stringify(usage)) {
|
|
157
|
+
throw new Error("proxy_completion_conflict");
|
|
158
|
+
}
|
|
159
|
+
return prior;
|
|
160
|
+
}
|
|
161
|
+
await tx.$executeRaw `
|
|
162
|
+
UPDATE "CodingProxyRequest"
|
|
163
|
+
SET "status" = 'completed', "actualCostUsd" = ${actualCostUsd},
|
|
164
|
+
"inputTokens" = ${usage.inputTokens}, "outputTokens" = ${usage.outputTokens},
|
|
165
|
+
"cachedInputTokens" = ${usage.cachedInputTokens}, "cacheWriteTokens" = ${usage.cacheWriteTokens},
|
|
166
|
+
"reasoningTokens" = ${usage.reasoningTokens}, "upstreamStatus" = ${upstreamStatus},
|
|
167
|
+
"completedAt" = CURRENT_TIMESTAMP, "updatedAt" = CURRENT_TIMESTAMP
|
|
168
|
+
WHERE "id" = ${requestId} AND "status" IN ('reserved', 'uncertain')
|
|
169
|
+
`;
|
|
170
|
+
await tx.$executeRaw `
|
|
171
|
+
UPDATE "Run" AS r
|
|
172
|
+
SET "tokensIn" = totals."tokensIn", "tokensOut" = totals."tokensOut", "costUsd" = totals."costUsd"
|
|
173
|
+
FROM (
|
|
174
|
+
SELECT s."runId", COALESCE(SUM(q."inputTokens"), 0)::integer AS "tokensIn",
|
|
175
|
+
COALESCE(SUM(q."outputTokens"), 0)::integer AS "tokensOut",
|
|
176
|
+
COALESCE(SUM(q."actualCostUsd"), 0) AS "costUsd"
|
|
177
|
+
FROM "CodingProxySession" s
|
|
178
|
+
JOIN "CodingProxyRequest" q ON q."sessionId" = s."id" AND q."status" = 'completed'
|
|
179
|
+
WHERE s."id" = ${prior.sessionId}
|
|
180
|
+
GROUP BY s."runId"
|
|
181
|
+
) AS totals
|
|
182
|
+
WHERE r."id" = totals."runId"
|
|
183
|
+
`;
|
|
184
|
+
const request = await requestById(tx, requestId);
|
|
185
|
+
if (!request)
|
|
186
|
+
throw new Error("proxy_completion_not_persisted");
|
|
187
|
+
return request;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
async release(requestId, upstreamStatus) {
|
|
191
|
+
await this.db.$executeRaw `
|
|
192
|
+
UPDATE "CodingProxyRequest"
|
|
193
|
+
SET "status" = 'released', "upstreamStatus" = ${upstreamStatus}, "updatedAt" = CURRENT_TIMESTAMP
|
|
194
|
+
WHERE "id" = ${requestId} AND "status" = 'reserved'
|
|
195
|
+
`;
|
|
196
|
+
}
|
|
197
|
+
async markUncertain(requestId, upstreamStatus) {
|
|
198
|
+
await this.db.$executeRaw `
|
|
199
|
+
UPDATE "CodingProxyRequest"
|
|
200
|
+
SET "status" = 'uncertain', "upstreamStatus" = ${upstreamStatus ?? null}, "updatedAt" = CURRENT_TIMESTAMP
|
|
201
|
+
WHERE "id" = ${requestId} AND "status" = 'reserved'
|
|
202
|
+
`;
|
|
203
|
+
}
|
|
204
|
+
async cancelSession(sessionId) {
|
|
205
|
+
await this.db.$executeRaw `
|
|
206
|
+
UPDATE "CodingProxySession" SET "status" = 'cancelled', "updatedAt" = CURRENT_TIMESTAMP
|
|
207
|
+
WHERE "id" = ${sessionId} AND "status" = 'active'
|
|
208
|
+
`;
|
|
209
|
+
}
|
|
210
|
+
getRequest(requestId) {
|
|
211
|
+
return requestById(this.db, requestId);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type ModelPricing } from "../llm/pricing.js";
|
|
2
|
+
import type { CredentialResolver, ProxyAuditSink, ProxyLedger, ProxyProtocol } from "./types.js";
|
|
3
|
+
export declare const PROXY_MAX_BODY_BYTES: number;
|
|
4
|
+
export declare const PROXY_MAX_OUTPUT_TOKENS = 1000000;
|
|
5
|
+
export declare const PROXY_DEFAULT_MAX_OUTPUT_TOKENS = 4096;
|
|
6
|
+
export declare const CLAUDE_CODE_ANTHROPIC_BETAS: readonly ["claude-code-20250219", "context-management-2025-06-27", "effort-2025-11-24", "interleaved-thinking-2025-05-14", "mid-conversation-system-2026-04-07", "prompt-caching-scope-2026-01-05", "thinking-token-count-2026-05-13"];
|
|
7
|
+
export declare class CodingProxyError extends Error {
|
|
8
|
+
readonly status: number;
|
|
9
|
+
readonly code: string;
|
|
10
|
+
constructor(status: number, code: string);
|
|
11
|
+
}
|
|
12
|
+
export interface ProxyResponseSink {
|
|
13
|
+
start(status: number, headers: Record<string, string>): void;
|
|
14
|
+
write(chunk: Uint8Array): void | Promise<void>;
|
|
15
|
+
end(): void;
|
|
16
|
+
destroy(): void;
|
|
17
|
+
}
|
|
18
|
+
export interface ExecuteProxyRequest {
|
|
19
|
+
bearer: string;
|
|
20
|
+
protocol: ProxyProtocol;
|
|
21
|
+
rawBody: string;
|
|
22
|
+
requestKey?: string;
|
|
23
|
+
anthropicBeta?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface CreateCodingProxySession {
|
|
26
|
+
runId: string;
|
|
27
|
+
credentialRef: string;
|
|
28
|
+
protocol: ProxyProtocol;
|
|
29
|
+
allowedModels: string[];
|
|
30
|
+
deadlineAt: Date;
|
|
31
|
+
budgetUsd: number;
|
|
32
|
+
}
|
|
33
|
+
export interface CreatedCodingProxySession {
|
|
34
|
+
id: string;
|
|
35
|
+
runId: string;
|
|
36
|
+
capability: string;
|
|
37
|
+
protocol: ProxyProtocol;
|
|
38
|
+
allowedModels: string[];
|
|
39
|
+
deadlineAt: Date;
|
|
40
|
+
budgetUsd: number;
|
|
41
|
+
}
|
|
42
|
+
export interface CodingProxyOptions {
|
|
43
|
+
ledger: ProxyLedger;
|
|
44
|
+
credentials: CredentialResolver;
|
|
45
|
+
upstreamUrl?: string;
|
|
46
|
+
anthropicUpstreamUrl?: string;
|
|
47
|
+
upstreamAllowedHosts?: string[];
|
|
48
|
+
fetch?: typeof globalThis.fetch;
|
|
49
|
+
now?: () => Date;
|
|
50
|
+
audit?: ProxyAuditSink;
|
|
51
|
+
pricing?: (model: string, protocol: ProxyProtocol) => ModelPricing;
|
|
52
|
+
pricingVersion?: string;
|
|
53
|
+
}
|
|
54
|
+
export declare class CodingProxy {
|
|
55
|
+
private readonly ledger;
|
|
56
|
+
private readonly credentials;
|
|
57
|
+
private readonly upstreamUrls;
|
|
58
|
+
private readonly fetchImpl;
|
|
59
|
+
private readonly now;
|
|
60
|
+
private readonly audit;
|
|
61
|
+
private readonly getPricing;
|
|
62
|
+
private readonly priceVersion;
|
|
63
|
+
private readonly activeRequests;
|
|
64
|
+
constructor(options: CodingProxyOptions);
|
|
65
|
+
createSession(input: CreateCodingProxySession): Promise<CreatedCodingProxySession>;
|
|
66
|
+
cancelSession(sessionId: string): Promise<void>;
|
|
67
|
+
execute(input: ExecuteProxyRequest, sink: ProxyResponseSink): Promise<void>;
|
|
68
|
+
private authenticate;
|
|
69
|
+
private trackActive;
|
|
70
|
+
private handleDuplicate;
|
|
71
|
+
private complete;
|
|
72
|
+
private forwardJson;
|
|
73
|
+
private forwardStream;
|
|
74
|
+
private markUncertain;
|
|
75
|
+
}
|