From 4154638e71fed800f17b368c1cc161a11b9142f4 Mon Sep 17 00:00:00 2001 From: Devin Foley Date: Fri, 2 Oct 2026 16:44:54 -0700 Subject: [PATCH] fix: make run accounting durable and budget updates atomic Co-Authored-By: Paperclip --- doc/COST-ACCOUNTING.md | 155 + doc/SPEC-implementation.md | 17 +- doc/SPEC.md | 4 + doc/examples/accounting-invoice.json | 15 + package.json | 6 +- .../db/src/cost-accounting-migration.test.ts | 130 + .../migrations/0295_lean_baron_strucker.sql | 229 + .../db/src/migrations/meta/0295_snapshot.json | 51689 ++++++++++++++++ packages/db/src/migrations/meta/_journal.json | 7 + packages/db/src/schema/accounting.ts | 121 + packages/db/src/schema/agent_runtime_state.ts | 4 +- packages/db/src/schema/agents.ts | 4 +- packages/db/src/schema/budget_incidents.ts | 6 +- packages/db/src/schema/budget_policies.ts | 8 +- packages/db/src/schema/companies.ts | 5 +- packages/db/src/schema/cost_events.ts | 15 +- packages/db/src/schema/finance_events.ts | 7 +- packages/db/src/schema/heartbeat_runs.ts | 7 + packages/db/src/schema/index.ts | 2 + packages/shared/src/accounting.ts | 115 + packages/shared/src/constants.ts | 2 +- packages/shared/src/index.ts | 3 + packages/shared/src/money.test.ts | 29 + packages/shared/src/money.ts | 53 + packages/shared/src/types/budget.ts | 9 + packages/shared/src/types/cost.ts | 21 + packages/shared/src/types/finance.ts | 36 + packages/shared/src/validators/budget.ts | 7 +- packages/shared/src/validators/cost.ts | 14 +- packages/shared/src/validators/finance.ts | 8 +- scripts/test-accounting-mutations.mjs | 34 + server/scripts/benchmark-accounting.ts | 80 + server/src/__tests__/budgets-service.test.ts | 308 +- server/src/__tests__/codex-pricing.test.ts | 118 + .../cost-accounting-checkpoints.test.ts | 55 + .../cost-accounting-completion.test.ts | 645 + .../cost-accounting-concurrency.test.ts | 113 + .../__tests__/cost-accounting-crash.test.ts | 126 + .../cost-accounting-edge-cases.test.ts | 241 + .../cost-accounting-mutation-targets.test.ts | 45 + .../cost-accounting-operations-edges.test.ts | 217 + .../cost-accounting-operators.test.ts | 181 + .../cost-accounting-processes.test.ts | 110 + .../cost-accounting-reliability.test.ts | 363 + .../__tests__/cost-accounting-routes.test.ts | 50 + server/src/__tests__/costs-service.test.ts | 40 +- .../__tests__/fixtures/accounting-writer.ts | 20 + .../heartbeat-cost-accounting.test.ts | 22 + server/src/__tests__/heartbeat-list.test.ts | 3 +- .../heartbeat-process-recovery.test.ts | 27 + server/src/__tests__/openapi-routes.test.ts | 30 + .../__tests__/plugin-managed-agents.test.ts | 3 + .../__tests__/provider-billing-import.test.ts | 236 + .../server-startup-feedback-export.test.ts | 25 + server/src/index.ts | 13 +- server/src/routes/costs.ts | 133 +- server/src/routes/openapi.ts | 58 +- server/src/services/accounting-integrity.ts | 121 + server/src/services/accounting-transaction.ts | 29 + server/src/services/agents.ts | 12 +- server/src/services/billing-reconciliation.ts | 140 + server/src/services/budget-reservations.ts | 45 + server/src/services/budgets.ts | 401 +- server/src/services/codex-pricing.ts | 97 + server/src/services/companies.ts | 16 +- server/src/services/company-skills.ts | 2 +- server/src/services/cost-date-range.ts | 12 + server/src/services/costs.ts | 204 +- server/src/services/dashboard.ts | 3 +- server/src/services/finance.ts | 119 +- server/src/services/heartbeat.ts | 300 +- .../native-session-executor.test.ts | 30 + .../native-runtime/native-session-executor.ts | 82 +- server/src/services/plugin-managed-agents.ts | 4 +- .../src/services/provider-billing-import.ts | 367 + server/src/services/receipt-fingerprint.ts | 12 + server/src/services/run-cost-accounting.ts | 131 + server/src/services/runtime-skill-cache.ts | 16 +- server/src/services/status-cards.ts | 2 +- server/src/services/usage-receipts.ts | 185 + server/tsconfig.accounting-tests.json | 19 + server/vitest.accounting-mutations.config.ts | 27 + server/vitest.accounting.config.ts | 43 + server/vitest.config.ts | 1 - ui/src/pages/AgentDetail.production.tsx | 4 +- ui/src/pages/ProjectDetail.tsx | 2 + ui/storybook/fixtures/paperclipData.ts | 8 + .../stories/budget-finance.stories.tsx | 10 + 88 files changed, 57570 insertions(+), 898 deletions(-) create mode 100644 doc/COST-ACCOUNTING.md create mode 100644 doc/examples/accounting-invoice.json create mode 100644 packages/db/src/cost-accounting-migration.test.ts create mode 100644 packages/db/src/migrations/0295_lean_baron_strucker.sql create mode 100644 packages/db/src/migrations/meta/0295_snapshot.json create mode 100644 packages/db/src/schema/accounting.ts create mode 100644 packages/shared/src/accounting.ts create mode 100644 packages/shared/src/money.test.ts create mode 100644 packages/shared/src/money.ts create mode 100644 scripts/test-accounting-mutations.mjs create mode 100644 server/scripts/benchmark-accounting.ts create mode 100644 server/src/__tests__/codex-pricing.test.ts create mode 100644 server/src/__tests__/cost-accounting-checkpoints.test.ts create mode 100644 server/src/__tests__/cost-accounting-completion.test.ts create mode 100644 server/src/__tests__/cost-accounting-concurrency.test.ts create mode 100644 server/src/__tests__/cost-accounting-crash.test.ts create mode 100644 server/src/__tests__/cost-accounting-edge-cases.test.ts create mode 100644 server/src/__tests__/cost-accounting-mutation-targets.test.ts create mode 100644 server/src/__tests__/cost-accounting-operations-edges.test.ts create mode 100644 server/src/__tests__/cost-accounting-operators.test.ts create mode 100644 server/src/__tests__/cost-accounting-processes.test.ts create mode 100644 server/src/__tests__/cost-accounting-reliability.test.ts create mode 100644 server/src/__tests__/cost-accounting-routes.test.ts create mode 100644 server/src/__tests__/fixtures/accounting-writer.ts create mode 100644 server/src/__tests__/provider-billing-import.test.ts create mode 100644 server/src/services/accounting-integrity.ts create mode 100644 server/src/services/accounting-transaction.ts create mode 100644 server/src/services/billing-reconciliation.ts create mode 100644 server/src/services/budget-reservations.ts create mode 100644 server/src/services/codex-pricing.ts create mode 100644 server/src/services/cost-date-range.ts create mode 100644 server/src/services/provider-billing-import.ts create mode 100644 server/src/services/receipt-fingerprint.ts create mode 100644 server/src/services/run-cost-accounting.ts create mode 100644 server/src/services/usage-receipts.ts create mode 100644 server/tsconfig.accounting-tests.json create mode 100644 server/vitest.accounting-mutations.config.ts create mode 100644 server/vitest.accounting.config.ts diff --git a/doc/COST-ACCOUNTING.md b/doc/COST-ACCOUNTING.md new file mode 100644 index 00000000000..085824748c9 --- /dev/null +++ b/doc/COST-ACCOUNTING.md @@ -0,0 +1,155 @@ +# Cost accounting and budget enforcement + +This document describes the implementation after the September 2026 reliability audit. The execution ledger and the finance ledger serve different purposes: `cost_events` records USD usage attributed to agent work; `finance_events` records billing charges and credits in their original currencies. Provider invoices can be imported explicitly for comparison and reviewed corrections. Operators can also fetch OpenAI and Anthropic organization cost reports using a company-owned admin credential. Provider reports, invoices, and run estimates remain separate; Paperclip does not guess exchange rates. + +## From a run to a report + +1. Before provider dispatch, the heartbeat stores issue/project/billing-code attribution and marks accounting pending. Adapters normalize usage into input tokens (excluding cache reads, including cache writes), cached input tokens, output tokens, billing identity, and an optional USD price. Usage is per run unless the adapter explicitly declares session-cumulative counters. A missing price is distinct from a reported zero. +2. Eligible direct OpenAI API receipts receive a versioned token-price estimate when the provider supplies no dollars (see below). Native execution and supported CLI/ACPX adapters checkpoint usage before workspace and issue finalization. Each checkpoint is versioned and fsynced to a private instance spool before its database write; database outages leave the file for replay. The heartbeat stores the final adapter receipt on `heartbeat_runs`, including partial usage returned for failed or timed-out execution. `subscription_included` usage contributes tokens but no incremental monetary charge. An explicit cache-adjusted price takes precedence over the nominal price. +3. `accountRunCost` acquires the company accounting lock and then locks the run. In one PostgreSQL transaction it inserts idempotent cost receipts, updates monthly spend projections, evaluates budgets, increments lifetime runtime totals, settles any reservation, and acknowledges run accounting. The original reported amount is preserved; a reviewed correction appends an audit record and changes only the effective valuation. Every new run receipt has a company-scoped idempotency key. Duplicate delivery cannot increment totals again. Complete per-model receipts are split only when both their tokens and prices reconcile to the run total; otherwise the aggregate receipt is retained. +4. Startup and periodic recovery scan terminal pending runs, independently of heartbeat scheduling being enabled. Failed writes roll back, leave the marker pending, and retry. A bounded batch rotates failed attempts so one bad receipt cannot starve other pending runs. Dispatched runs that have not produced a final receipt remain pending, including when cancellation marks a run terminal before its provider stops. A late final receipt can then be recorded exactly once. Recovery does not invent a zero-valued receipt or price. Proven pre-provider failures are acknowledged without creating a charge. +5. Reports aggregate `cost_events`. Company and agent monthly counters and runtime lifetime counters are projections. New receipts use the attribution captured for the run. Deleted issue/project targets become unallocated; foreign-company references are rejected. Legacy project attribution is inferred only when run activity identifies one project. The unallocated bucket is included so project totals conserve company spend. + +Amounts remain denominated in **cents**, including fractional cents. Ledger amounts and spend projections use PostgreSQL `numeric(24,7)`, preserving nanodollar precision instead of rounding every run to a whole cent. Arithmetic uses integer nanodollars, with one half-away-from-zero rounding step at the storage boundary. Monetary inputs accept decimal strings; use strings when exact representation matters. Unsafe large numeric inputs are rejected. Existing JSON number fields remain for compatibility, alongside exact decimal fields such as `costCentsExact`, `spendCentsExact`, `amountCentsExact`, `netCentsExact`, and budget `observedAmountExact`. Display rounding never changes storage or admission decisions. Budget limits remain whole cents. + +## Ingestion and retries + +`POST /api/companies/:companyId/cost-events` accepts an optional `idempotencyKey` (1–200 characters). Retrying the same normalized receipt with the same key returns its existing event. Reusing the key with different content returns `409`. Keys are scoped to a company; callers must reuse the original `occurredAt`, not regenerate it on retry. Without a key, each POST intentionally creates a new event. Automatic run accounting always supplies keys. + +All linked agent, run, issue, project, and goal references must belong to the reporting company, and a linked run must belong to the reported agent. Non-finite/negative costs and negative/fractional token counts are rejected. The receipt, projections, budget incidents, approvals, and local activity-log rows commit together. Live activity publication and provider cancellation happen after commit; a notification or cancellation transport failure cannot undo a recorded charge. + +Finance ingestion uses the same company-scoped key/conflict semantics. Credits remain separate nonnegative events with `direction: credit`. The top-level finance summary is explicitly USD; `currencies` contains independent totals for every recorded currency. Biller/kind groups retain currency. No exchange rate is guessed and unlike currencies are never added into one monetary total. + +## Budgets and incomplete accounting + +Company, agent, and project policies all apply. Monthly windows use UTC calendar months; lifetime policies coexist with monthly policies. A zero or inactive limit does not enforce a stop. Generic company/agent budget updates synchronize the policy in the same transaction as the legacy budget field. + +Active hard-stop policies block admission when recorded spend reaches the limit, when a terminal run still awaits accounting, or when usage lacks a reliable price. `unpricedUsagePolicy: block` is the default. An operator can explicitly choose `allow` to permit work despite missing prices; reports continue to identify incomplete pricing. This choice does not bypass pending accounting transactions. Subscription-included usage does not count as a monetary pricing gap. + +At a hard stop the budget service pauses the scope, creates one open incident/approval for the policy window, and persists a cancellation delivery version. Cancellation is retried after failures and may be delivered more than once. It rechecks the policy and limits cancellation to work that existed at the check, so a delayed delivery does not cancel newly admitted work after a budget grant. Provider shutdown is best effort. + +Raising a limit must exceed **current** observed spend. Resuming also requires all other hard-stop policies on that scope to permit work. Calendar rollover or disabling a policy releases budget-owned pauses only; manual pauses, terminated agents, and archived companies are preserved. Choosing to keep a scope paused dismisses the incident without generating a fresh approval on each admission check. A subsequent limit increase followed by another threshold crossing creates a new incident. + +A legacy budget pause with no policy remains blocked until a policy or explicit operator resume resolves it. Manual agent pauses keep the normal agent admission error. Accounting activity is excluded from task-progress evidence, so recording a receipt cannot suppress plan-only recovery. + +Policies optionally configure `reservationCents` (default zero), also editable as “Reserve per run (USD)” on the Costs budget cards. Before dispatch, Paperclip locks the company, checks all relevant policies, and reserves the maximum configured estimate against company, agent, and project capacity. Concurrent dispatches cannot claim the same capacity. Zero disables the estimate; a zero-valued reservation still fences duplicate dispatch of the same run. Existing reservations retain their original amount after policy edits and carry across UTC month boundaries until settled. + +A final accounting transaction settles the reservation against the actual charge. Positive pre-provider failure evidence releases it without a charge. Time passing, a missing process, cancellation intent, or a policy change alone does not prove provider work has stopped and never releases a held reservation. The health panel displays held capacity. A run permanently missing its final receipt requires evidence recovery; the system has no “assume zero” button. + +Reservations are estimates, not provider-enforced spending caps. Delayed receipts and in-progress calls can exceed the estimate. Provider spending controls are needed for an external invoice ceiling. + +## Dates, completeness, and UI + +Cost and finance endpoints default to the current UTC month through now. Explicit `from`/`to` bounds remain inclusive; `period=all` requests all time. Reversed or malformed bounds are rejected. CLI cost/finance reads expose `--from`, `--to`, and `--all-time`. The board sends explicit ranges, and month/year-to-date starts use UTC. Rolling spend excludes future events. + +Cost summaries expose `eventCount`, `estimatedEventCount`, `unpricedEventCount`, `pendingRunCount`, and `pricingComplete`. Estimated charges count toward budgets, while the UI distinguishes estimates from provider-reported dollars. Agent totals and expanded model rows show **Estimated** when every charge in that group is estimated, or **Partially estimated** for a mix. Groups with no estimates have no estimate badge. These labels follow the selected date range and use ledger-event counts, not run counts; they do not infer pricing status from the provider or model name. `pricingComplete` means no missing prices or pending receipts; it does not mean invoice reconciliation has occurred. The Costs page warns when totals are incomplete. Provider cards include cached tokens and calculate subscription shares using each token once. Provider spend is compared with the real company budget, not a fabricated proportional allocation. Run displays prefer cache-adjusted prices and preserve explicit zeroes. + +Gemini token normalization follows the [CLI stream statistics](https://github.com/google-gemini/gemini-cli/blob/main/packages/core/src/output/stream-json-formatter.ts) and [API usage metadata](https://ai.google.dev/api/generate-content#UsageMetadata): cached prompt tokens are separated from input, and thinking tokens contribute to output. + +## Upgrade and operational limits + +The generated migrations widen monetary columns, add receipt identities and run recovery markers, and restrict incident uniqueness to open incidents. Existing whole-cent values retain their value. Column type changes and index creation acquire database locks; schedule upgrades according to database size. The new nonnegative check rejects invalid legacy ledger rows rather than silently rewriting them. + +Historical rounded charges, missing receipts, ambiguous token semantics, and absent provider prices cannot be reconstructed from these migrations. Old runs are not automatically re-billed. Imported invoices and reviewed corrections can resolve historical prices when there is evidence. Historical runtime totals are retained as an explicit baseline; the inspector identifies legacy totals that lack one instead of fabricating a reconstruction. When a receipt is available but has no price, operators can explicitly allow unpriced usage; doing so does not make the displayed total complete. Runs that permanently lose their final receipt remain pending and require recovery or explicit disabling of the hard-stop policy. Automated reconstruction of missing receipts from arbitrary provider logs is not implemented. + +Tests use disposable PostgreSQL databases and cover company isolation, concurrent duplicate delivery, sub-cent threshold crossings, complete rollback after a late write failure, pending-run recovery, mixed-model conservation, currency separation, monthly rollover, manual pause preservation, simultaneous policies, cancellation delivery failures, and recovery foreign-key lock compatibility. Adapter tests cover timeout receipts and cache semantics, alongside UI amount/share tests. Provider report tests exercise request/response contracts, credential isolation, pagination, revisions, transaction rollback and duplicate delivery with fixture HTTP responses. They do not establish live billing-account permissions. + +## Reproducing reliability checks + +Run `pnpm test:accounting` for the accounting test typecheck, focused server tests, historical migration tests, V8 coverage, and mutation sentinels. The thirteen accounting service modules each require at least 98% line coverage, 90% branch coverage, 96% statement coverage, and 96% function coverage. HTML and JSON reports are written to `coverage/accounting/`. These percentages do not include the full heartbeat service, adapters, browser UI, or child server processes. + +The expanded accounting gate includes prospective Codex pricing and provider-report imports. All four deliberately introduced accounting mutations must be caught by their intended assertions. Separate regressions cover late pre-provider cancellation proof, reservation release without replacing the stop result, lost-channel receipt incompleteness, and exact reservation editing. The full test runner includes every adapter project configured in the root Vitest configuration; a roster check prevents silent omissions. + +The server suite includes four SIGKILL cases: before receipt insertion, after the ledger write but before runtime totals, at the last write before commit, and after commit but before acknowledging cancellation delivery. SQL barriers make these boundaries deterministic. After killing the server process group, the harness terminates the orphan database connection still waiting at the barrier, restarts the real server, and checks exactly one receipt, one set of totals, and one incident/approval. A second restart proves replay does not duplicate them. This tests application crash recovery, not a database power failure. Eight fixed random seeds exercise 640 mixed operations with concurrent receipt retries, policy changes, admission checks, recovery, and injected cancellation failures. + +The migration fixtures restore the predecessor column/index/journal shape, populate historical maximum integer amounts, rounded-zero receipts, finance credits/currencies, and closed incidents, then use the production migrator. An invalid negative legacy receipt must reject the upgrade without changing the schema or migration journal; an explicit repair allows retry. + +Run `pnpm exec playwright test --config tests/e2e/playwright.config.ts tests/e2e/cost-accounting.spec.ts` for the browser workflow. It boots a throwaway instance and the built UI, invokes a deterministic local provider through the real Claude adapter, checks cost display and the budget stop, rejects a manual wake while paused, raises the budget in the UI, and starts another run. It asserts exact cents and cache tokens through the API, then imports an invoice, reviews and applies a sub-cent correction, and independently verifies the resulting totals. Stopped, resumed, and reconciled screenshots are attached to the Playwright report. It uses no paid provider credentials or calls. + + +## Durable capture and operator recovery + +The spool lives at `/accounting-receipts`. Directories use mode 0700 and receipt files 0600. Files contain accounting fields only; transcript text, prompts, tools, and credentials are excluded. Publication awaits file and directory fsync on POSIX. Each record carries company/run identity, a controller source ID, sequence, timestamp, normalized usage, completeness, and price. Replay validates the schema and fingerprint and rotates failures behind fresh entries in bounded batches. Old controller snapshots are retained as evidence but cannot overwrite a replacement controller or an acknowledged run. Multiple provider attempts are accounted together; incomplete attempts and capture failures remain incomplete. + +The spool survives process death on persistent local storage, including the tested boundary before the first database write. It does not survive destruction of that storage, and cannot recover provider activity that was never emitted as usage. Back up persistent instance storage together with PostgreSQL. No arbitrary provider-log scraping or automatic provider re-execution occurs during accounting recovery. + +Board-only routes under `/api/companies/:companyId/accounting` provide: + +| Route | Behavior | +| --- | --- | +| `GET /health` | Pending and unpriced counts, oldest pending time, last errors/retry counts, cancellation backlog, held capacity | +| `GET /inspect` | Independent ledger/projection and original run-receipt comparison; no data repair | +| `POST /repair` | Rebuild repairable totals using the reviewed `fingerprint` and a required `reason`; stale review returns 409 | +| `POST /retry` | Retry a company-owned `runId`; record the request and any failed attempt | +| `GET` / `POST /invoices` | List or idempotently import normalized invoice evidence and financial events | +| `POST /provider-costs/import` | Fetch completed daily provider reports for explicitly selected projects/workspaces | +| `GET /invoices/:invoiceId` | Compare exact amounts and expose unmatched, ambiguous, non-inference, or unsupported-currency lines | +| `GET` / `POST /events/:eventId/adjustments` | Read correction history or apply a reviewed correction | + +Agents cannot use operator endpoints. Board viewers may inspect but cannot mutate. Repairs and corrections record the operator and reason in local activity history. An inspection fingerprint binds the repair to the observed discrepancies; the service recomputes them under the company lock before writing. Missing original evidence and legacy runtime uncertainty are never silently repaired. New runtime totals reconcile against an explicit historical baseline plus acknowledged v2 run receipts, including backdated receipts. + +The CLI exposes `accounting health`, `accounting inspect`, `accounting repair`, `accounting retry`, `accounting invoices`, `accounting invoice:import`, `accounting invoice:review `, `accounting provider:import`, and `accounting event:correct `, using the ordinary authenticated company context. Mutations take `--payload-json`; inspection is the dry run for repair. + +## Invoice review and corrections + +Imports use the normalized format in [the invoice template](examples/accounting-invoice.json). Map provider exports to this format explicitly. Invoice identity is `(company, biller, externalId)`; same-content replay is harmless, different-content reuse returns 409. Line order does not affect identity. No provider credentials or remote API calls are needed. + +A match requires a unique company/biller-scoped `costEventId`, `runId` (optionally with model), or `providerRequestId`. All supplied identifiers must agree. Multiple candidates or multiple lines claiming one charge are ambiguous and cannot be applied as invoice-backed corrections. Non-USD invoices, fees, and credits remain visible evidence and are never silently converted into USD inference spend. Every imported invoice line also creates an idempotent finance event in the same transaction: inference charges, fees, and credits retain their kind and currency. An import either commits invoice evidence and timeline entries together or commits neither. Replaying an invoice creates no duplicate charge. Importing an invoice does not automatically change run valuations. + +A correction requires `idempotencyKey`, exact `expectedCents`, `correctedCents`, `reason`, and `pricing` provenance. An optional `invoiceLineId` must uniquely match and support the corrected amount. The expected value guards against stale review; the correction key prevents duplicate application after an ambiguous response. `reportedCostCents` and the original receipt fingerprint remain unchanged. `cost_adjustments` preserves every prior valuation, actor, reason, evidence, and pricing revision, including the original provenance in `previousPricing`. The effective `costCents`, projections, runtime totals for supported new receipts, and budget state update atomically. Pricing an unpriced receipt can release a budget-owned pause; other policies and manual pauses still apply. + +## Scale and fault qualification + +`pnpm benchmark:accounting` creates its own disposable PostgreSQL database, seeds one million events across twelve months, measures report latency and eight concurrent writers on one company, exercises two active budget policies, recovers 250 pending receipts, records an analyzed project-budget query plan, and runs the independent integrity checker. `PAPERCLIP_ACCOUNTING_BENCH_ROWS` optionally sets 1,000–10,000,000 events. Results go to `coverage/accounting/scale.json`. There are no machine-dependent latency assertions in CI. + +The September 28 local run on macOS arm64 / Node 25.6.1 measured all-time summary p95 39 ms and project grouping p95 64 ms. With eight concurrent writers, throughput was about 325 writes/s without active policies and 22 writes/s with two active policies; p95 was 72 ms and 359 ms respectively. The budgeted 250-receipt backlog drained in three bounded batches in about 12.7 seconds, with zero integrity discrepancies. Active policies deliberately read the authoritative ledger, which is more expensive than updating a projection. These synthetic warm local results are evidence, not production service-level guarantees. + +Monthly projections carry an explicit UTC month marker. The first write after upgrade or rollover initializes from the ledger; subsequent same-month writes use exact atomic increments. Backdated events do not increment the current month. Reports and budget decisions continue using the ledger. Composite project/date and partial unpriced indexes support targeted queries; pending recovery is indexed in its actual update-time order. + +Additional fault tests use independent Node writer processes, kill a recorder after fsync but before database persistence, and proxy PostgreSQL to discard the `COMMIT` acknowledgement after the server commits. Retrying all of these preserves exactly one charge and correct projections. Mutation tests remove deduplication, company filtering, projection updates, and the inclusive hard-stop comparison in Vite memory. Each must fail its named assertion after an unchanged baseline passes; import errors and skipped tests do not count as detected mutations. Workspace source is never rewritten by the mutation runner. + + +## Prospective Codex estimates + +Codex commonly reports tokens without dollars. For new receipts, `codex-pricing.ts` applies the immutable `openai-standard-2026-09-30` catalog only when both provider and biller are OpenAI, billing is metered API, usage is per run, and the exact model ID is supported. The initial catalog covers `gpt-6-astra`, `gpt-6-sol`, `gpt-6.1-sol`, `gpt-6-luna`, and `gpt-5.6-sol`, from the [official pricing page](https://developers.openai.com/api/docs/pricing). Unknown models, unsupported tiers, session-cumulative receipts and mixed-model receipts remain unpriced. OpenAI-compatible custom endpoints do not establish OpenAI billing. Existing provider-reported amounts, including zero, take precedence. + +Calculation separates ordinary input, cached reads, cache writes, and output. Cache writes are a subset of input, not extra tokens. Integer arithmetic rounds once to nanodollars. An explicit pricing context can select the supported service/context tier; when request-level context is unavailable, the estimate records standard processing and short-request assumptions. A large cumulative run is not evidence that any individual request crossed a long-context threshold. Such estimates can differ from a bill because of tier/context assumptions, negotiated prices, or fees. They are useful estimates, not externally enforced budget ceilings. + +The recorder calculates and freezes the amount, rate version, base rates and assumptions **before** writing its durable spool. Finalization, a process restart, or a later catalog change cannot silently reprice that captured receipt. Native runs carry their managed AI connection's billing identity into this path. Estimated costs feed the same exact, idempotent accounting and budget transaction as provider-reported costs. The original receipt remains available after an invoice correction. Historical missing dollars are not backfilled automatically. + +## Getting financial events into Paperclip + +Open **Costs → Recent financial events → Record or import charges**. There are three entry paths: + +1. **Record charge** records a subscription payment, fee, credit or refund. Enter USD, the UTC date and a description. The client keeps its idempotency key through an ambiguous failed response, so retrying the same entry cannot charge twice. +2. **Import invoice** accepts the normalized [invoice JSON format](examples/accounting-invoice.json). Stable biller/invoice/line IDs provide idempotency. Invoice import produces timeline events atomically and preserves evidence for separately reviewed run corrections. +3. **Provider report** fetches API cost reports from OpenAI or Anthropic. Save a provider organization admin key as a **company secret**, then select it, the matching provider organization ID, explicit project/workspace IDs, and a UTC interval. Dates cover at most 31 completed days; the end date is exclusive. Use `default` only to intentionally include provider rows with no project/workspace ID. + +The provider importer uses fixed HTTPS endpoints, refuses redirects, verifies Anthropic organization identity, sends the selected OpenAI organization header, filters selected scopes and bounds time, pages and response size. It reads all pages before writing. Missing days, overlapping scoped results or invalid currencies/amounts reject the import. OpenAI amounts arrive in USD; Anthropic reports decimal cents. A complete bucket without a selected scope is an explicit zero. + +Each daily company/provider/account/scope snapshot has a revision. Reimporting unchanged data writes no event. Increases append debit deltas; decreases append credits, including corrections back to a previous total. A slow older fetch cannot overwrite a newer changed snapshot. The snapshot, finance event and activity record commit together. + +**Provider report totals are separate from invoice/manual-charge totals.** The finance summary exposes `providerReportedCentsExact`; report rows remain labeled in the timeline but are excluded from net charges and biller/kind charge groupings. This prevents importing a report and its invoice from adding the same expenditure twice. Neither is added to run-cost estimates. A manually entered charge and an invoice representing the same payment must still be kept distinct by the operator; Paperclip cannot infer that identity from unrelated IDs. + +API: `POST /api/companies/:companyId/accounting/provider-costs/import`. CLI: `paperclipai accounting provider:import --payload-json ''`, with the normal authenticated company context. Payload: + +```json +{ + "provider": "openai", + "secretId": "00000000-0000-4000-8000-000000000001", + "accountId": "org_example", + "scopeIds": ["proj_example"], + "from": "2026-09-01", + "to": "2026-09-02" +} +``` + +Imports are initiated explicitly; this implementation does not schedule background billing synchronization. Repeated API/CLI invocations are safe for an external scheduler. It does not retrieve ChatGPT/Claude subscription invoices, apply exchange rates, or claim provider API reports cover every invoice item. [Anthropic's report](https://platform.claude.com/docs/en/manage-claude/usage-cost-api) excludes Priority Tier costs. The [OpenAI Costs API](https://platform.openai.com/docs/api-reference/usage/audio_transcriptions_object) requires organization billing access; an ordinary inference key may be insufficient. These accounts need to be configured before live imports work. + +## Subscription quotas and refresh behavior + +Company quota requests resolve the caller's accessible subscription AI connections and read each account with its own managed credential. They do not substitute the server host's login for a remote agent account. Private, revoked or cross-company connections are filtered through AI connection authorization. Unmanaged remote credentials require connecting the account in AI connections before its quota is available. + +Responses carry an opaque account identity and successful capture timestamp. The 30-second cache is scoped to company, user, connection, grant and credential revision; authorization and revision metadata are checked before reading cached results. Transient failures retain the last successful UI observation with its original timestamp. Rotation, revocation and explicit authentication rejection clear obsolete windows. Public messages never contain provider subprocess commands or raw diagnostics. + +Both Costs page variants retain loaded reports and expanded rows during background refresh failures, with a small stale-data notice. Initial failures remain visible. Company/date changes isolate cached reports. The run-count label is `runs: 0 api · 11 sub`: distinct recorded runs, not tasks or individual model requests. Input totals include cache reads, with the cached portion shown explicitly. diff --git a/doc/SPEC-implementation.md b/doc/SPEC-implementation.md index f6bda971fab..8901987b857 100644 --- a/doc/SPEC-implementation.md +++ b/doc/SPEC-implementation.md @@ -141,7 +141,7 @@ Human auth tables (`users`, `sessions`, and provider-specific auth artifacts) ar - `issue_prefix` text not null - `issue_counter` int not null - `budget_monthly_cents` int not null default 0 -- `spent_monthly_cents` int not null default 0 +- `spent_monthly_cents` numeric(24,7) not null default 0 - `require_board_approval_for_new_agents` boolean not null default false - feedback sharing consent fields @@ -164,7 +164,7 @@ Invariant: every business record belongs to exactly one company. - `default_environment_id` uuid fk `environments.id` null - `context_mode` enum: `thin | fat` default `thin` - `budget_monthly_cents` int not null default 0 -- `spent_monthly_cents` int not null default 0 +- `spent_monthly_cents` numeric(24,7) not null default 0 - pause fields: `pause_reason`, `paused_at` - `permissions` jsonb not null default `{}` - `last_heartbeat_at` timestamptz null @@ -325,7 +325,9 @@ Invariants: - `cost_status` text not null default `reported`; `unpriced` when usage exists but no price was reported - `input_tokens` int not null default 0 - `output_tokens` int not null default 0 -- `cost_cents` int not null +- `cost_cents` numeric(24,7) not null +- `idempotency_key` text null; unique within a company when present +- `receipt_hash` text null; canonical immutable-receipt fingerprint - `occurred_at` timestamptz not null Invariant: each event must attach to agent and company; rollups are aggregation, never manually edited. @@ -1451,7 +1453,9 @@ for contracts, recovery behavior, Storybook, and acceptance workflows. - block new checkout/invocation for that agent - emit high-priority activity event -Board may override by raising budget or explicitly resuming agent. +Board may raise the budget or disable its hard stop. An explicit resume cannot bypass an active blocking policy. Budget-owned pauses release when the applicable UTC window expires; manual pauses remain unchanged. Active hard stops also block pending terminal-run accounting and unpriced usage unless the operator explicitly allows unpriced usage. + +Optional per-run budget reservations serialize available-capacity admission with ledger writes. Durable usage checkpoints, exact decimal reporting, operator integrity inspection and guarded repairs, and reviewed invoice corrections support accounting recovery. Reservations are estimates; provider charges can exceed them. See [Cost accounting and budget enforcement](COST-ACCOUNTING.md) for receipt recovery, retry guarantees, and provider-side limits. ## 13.3 Cost Event Ingestion @@ -1474,7 +1478,8 @@ Board may override by raising budget or explicitly resuming agent. Validation: - non-negative token counts -- `costCents >= 0` +- finite `costCents >= 0`, retaining fractional cents to seven decimal places +- optional company-scoped `idempotencyKey`: identical retries return the original receipt; changed content conflicts - company ownership checks for all linked entities ## 13.4 Rollups @@ -1482,6 +1487,8 @@ Validation: Read-time aggregate queries are acceptable for V1. Materialized rollups can be added later if query latency exceeds targets. +Cost and finance queries default to month-to-date in UTC; explicit bounds are inclusive and `period=all` requests all time. Project rollups retain an unallocated bucket and must conserve total spend. Cost summaries expose missing-price and pending-run counts. Finance summaries retain original currencies without adding unlike currencies together. + ## 14. UI Requirements (Board App) V1 UI routes: diff --git a/doc/SPEC.md b/doc/SPEC.md index ab162e3df95..d61527ffdc1 100644 --- a/doc/SPEC.md +++ b/doc/SPEC.md @@ -326,6 +326,8 @@ Full hierarchy: **Initiative** (company goal) → Projects → Milestones → Is ## 6. Cost Tracking [DRAFT] +The implemented receipt, recovery, precision, currency, and budget-admission contracts are described in [Cost accounting and budget enforcement](COST-ACCOUNTING.md) and [the V1 implementation spec](SPEC-implementation.md#13-cost-and-budget-system). + Token/LLM cost budgeting is a core part of Paperclip. External revenue and expense tracking is a future plugin. ### Cost Reporting @@ -349,6 +351,8 @@ Three tiers: 2. **Soft alerts** — configurable thresholds (e.g. warn at 80% of budget) 3. **Hard ceiling** — auto-pause the Agent when budget is hit. Board notified. Board can override/raise the limit. +The implementation also supports estimated per-run reservations, durable receipt recovery, exact decimal reporting, independent integrity inspection, and audited invoice corrections. Recorded spend and reservations do not replace a provider-enforced invoice cap. See [cost accounting](COST-ACCOUNTING.md). + Budgets can be set to **unlimited** (no ceiling). ### Open Questions diff --git a/doc/examples/accounting-invoice.json b/doc/examples/accounting-invoice.json new file mode 100644 index 00000000000..abc43fa9062 --- /dev/null +++ b/doc/examples/accounting-invoice.json @@ -0,0 +1,15 @@ +{ + "biller": "anthropic", + "externalId": "example-invoice-2026-09", + "currency": "USD", + "lines": [ + { + "externalId": "example-line-1", + "kind": "inference", + "amountCents": "12.5000000", + "occurredAt": "2026-09-01T00:00:00Z", + "providerRequestId": "replace-with-the-provider-request-id", + "pricing": { "source": "provider_invoice", "version": "2026-09", "evidence": "example-invoice-2026-09" } + } + ] +} diff --git a/package.json b/package.json index 2fbd9d95a99..bde5e4fa08d 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "typecheck:build-gaps": "pnpm run preflight:workspace-links && pnpm --filter @paperclipai/plugin-sdk ensure-build-deps && pnpm --filter @paperclipai/server build && node scripts/run-typecheck-build-gaps.mjs", "test": "pnpm run test:run", "test:watch": "pnpm run preflight:workspace-links && vitest", + "test:accounting": "pnpm run preflight:workspace-links && pnpm --filter @paperclipai/server exec tsc -p tsconfig.accounting-tests.json && vitest run --config server/vitest.accounting.config.ts && vitest run --project @paperclipai/db packages/db/src/cost-accounting-migration.test.ts && pnpm test:accounting:mutations", "test:run": "pnpm run preflight:workspace-links && node scripts/run-vitest-stable.mjs", "test:runner-acceptance": "vitest run --config tests/runner-acceptance/vitest.config.ts", "test:runner-acceptance:typecheck": "tsc -p tests/runner-acceptance/tsconfig.json", @@ -90,10 +91,13 @@ "connections:ingest-app-definitions": "node scripts/ingest-app-definitions.mjs", "test:lifecycle-baseline": "node tests/lifecycle-baseline/run.mjs", "test:lifecycle-baseline:support": "node --test tests/lifecycle-baseline/report.test.mjs", - "test:lifecycle-baseline:typecheck": "tsc -p tests/lifecycle-baseline/tsconfig.json" + "test:lifecycle-baseline:typecheck": "tsc -p tests/lifecycle-baseline/tsconfig.json", + "test:accounting:mutations": "node scripts/test-accounting-mutations.mjs", + "benchmark:accounting": "node cli/node_modules/tsx/dist/cli.mjs server/scripts/benchmark-accounting.ts" }, "devDependencies": { "@playwright/test": "^1.62.1", + "@vitest/coverage-v8": "^4.1.11", "agentmail": "^0.5.31", "cross-env": "^10.1.0", "esbuild": "^0.28.2", diff --git a/packages/db/src/cost-accounting-migration.test.ts b/packages/db/src/cost-accounting-migration.test.ts new file mode 100644 index 00000000000..c1149041d90 --- /dev/null +++ b/packages/db/src/cost-accounting-migration.test.ts @@ -0,0 +1,130 @@ +import { randomUUID } from "node:crypto"; +import { createHash } from "node:crypto"; +import { readFileSync } from "node:fs"; +import postgres from "postgres"; +import { describe, expect, it } from "vitest"; +import { applyPendingMigrations, inspectMigrations } from "./client.js"; +import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase } from "./test-embedded-postgres.js"; + +const support = await getEmbeddedPostgresTestSupport(); +const journal = JSON.parse(readFileSync(new URL("./migrations/meta/_journal.json", import.meta.url), "utf8")); +const hashes = journal.entries.filter((entry: { idx: number }) => entry.idx >= 295).map((entry: { tag: string }) => `${entry.tag}.sql`).map((name: string) => + createHash("sha256").update(readFileSync(new URL(`./migrations/${name}`, import.meta.url))).digest("hex")); + +async function historicalDatabase() { + const database = await startEmbeddedPostgresTestDatabase("paperclip-accounting-upgrade-"); + const sql = postgres(database.connectionString, { max: 1, onnotice: () => {} }); + // Restore the complete predecessor shape for the changed tables, including + // the old uniqueness predicate and migration journal. Upgrade uses the real + // production migrator, not hand-picked statements from the migration. + await sql.unsafe(` + DROP TABLE provider_billing_snapshots, cost_adjustments, billing_invoice_lines, billing_invoices, budget_reservations, run_usage_receipts, accounting_runtime_baselines; + DROP INDEX cost_events_company_project_occurred_idx; + DROP INDEX cost_events_unpriced_idx; + ALTER TABLE cost_events DROP COLUMN provider_request_id, DROP COLUMN reported_cost_cents, DROP COLUMN pricing_provenance; + ALTER TABLE heartbeat_runs DROP COLUMN accounting_projection_version, DROP COLUMN accounting_last_attempt_at, DROP COLUMN accounting_last_error, DROP COLUMN accounting_attempt_count; + ALTER TABLE budget_policies DROP COLUMN reservation_cents; + ALTER TABLE companies DROP COLUMN spend_month_utc; + ALTER TABLE agents DROP COLUMN spend_month_utc; + ALTER TABLE finance_events ALTER COLUMN amount_cents TYPE integer; + ALTER TABLE cost_events DROP CONSTRAINT cost_events_nonnegative_amounts; + ALTER TABLE cost_events DROP COLUMN idempotency_key, DROP COLUMN receipt_hash; + ALTER TABLE finance_events DROP COLUMN idempotency_key, DROP COLUMN receipt_hash; + ALTER TABLE heartbeat_runs DROP COLUMN cost_accounting_pending, DROP COLUMN cost_accounted_at; + ALTER TABLE budget_policies DROP COLUMN enforcement_version, DROP COLUMN enforcement_delivered_version, DROP COLUMN unpriced_usage_policy; + ALTER TABLE cost_events ALTER COLUMN cost_cents TYPE integer; + ALTER TABLE companies ALTER COLUMN spent_monthly_cents TYPE integer; + ALTER TABLE agents ALTER COLUMN spent_monthly_cents TYPE integer; + ALTER TABLE agent_runtime_state ALTER COLUMN total_cost_cents TYPE integer; + ALTER TABLE budget_incidents ALTER COLUMN amount_observed TYPE integer; + DROP INDEX budget_incidents_policy_window_threshold_idx; + CREATE UNIQUE INDEX budget_incidents_policy_window_threshold_idx + ON budget_incidents (policy_id, window_start, threshold_type) WHERE status <> 'dismissed'; + `); + await sql`DELETE FROM drizzle.__drizzle_migrations WHERE hash IN ${sql(hashes)}`; + return { database, sql, close: async () => { await sql.end(); await database.cleanup(); } }; +} + +(support.supported ? describe : describe.skip)("cost accounting historical upgrade", () => { + it("preserves historical receipts, counters, references, finance currencies and incident history", async () => { + const f = await historicalDatabase(); + const { sql } = f; + const company = randomUUID(), agent = randomUUID(), run = randomUUID(), policy = randomUUID(); + try { + await sql`INSERT INTO companies (id,name,issue_prefix,spent_monthly_cents) VALUES (${company},'Historical','HIST',2147483647)`; + await sql`INSERT INTO agents (id,company_id,name,spent_monthly_cents) VALUES (${agent},${company},'Historical worker',2147483647)`; + await sql`INSERT INTO heartbeat_runs (id,company_id,agent_id,status,usage_json,finished_at) + VALUES (${run},${company},${agent},'succeeded','{"inputTokens":42,"costUsd":0.004}', '2025-12-31T23:59:59Z')`; + await sql`INSERT INTO agent_runtime_state (agent_id,company_id,adapter_type,total_cost_cents,total_input_tokens) + VALUES (${agent},${company},'codex_local',2147483647,42)`; + await sql`INSERT INTO cost_events (company_id,agent_id,heartbeat_run_id,provider,model,input_tokens,output_tokens,cost_cents,occurred_at) + VALUES (${company},${agent},${run},'historical','old-model',42,5,2147483647,'2025-12-31T23:59:59Z'), + (${company},${agent},${run},'historical','old-model',0,0,0,'2026-01-01T00:00:00Z')`; + await sql`INSERT INTO finance_events (company_id,event_kind,direction,biller,amount_cents,currency,occurred_at,external_invoice_id) + VALUES (${company},'adjustment','debit','historical',12345,'EUR',now(),'invoice-1'), + (${company},'adjustment','credit','historical',789,'USD',now(),'invoice-1')`; + await sql`INSERT INTO budget_policies (id,company_id,scope_type,scope_id,metric,window_kind,amount) + VALUES (${policy},${company},'agent',${agent},'billed_cents','calendar_month_utc',100)`; + await sql`INSERT INTO budget_incidents (company_id,policy_id,scope_type,scope_id,metric,window_kind,window_start,window_end,threshold_type,amount_limit,amount_observed,status) + VALUES (${company},${policy},'agent',${agent},'billed_cents','calendar_month_utc','2026-01-01Z','2026-02-01Z','hard',100,101,'resolved'), + (${company},${policy},'agent',${agent},'billed_cents','calendar_month_utc','2026-01-01Z','2026-02-01Z','hard',100,101,'dismissed')`; + const before = await sql`SELECT id,company_id,agent_id,heartbeat_run_id,provider,model,cost_cents::text,input_tokens,occurred_at FROM cost_events ORDER BY id`; + const financeBefore = await sql`SELECT id,event_kind,direction,amount_cents,currency,external_invoice_id FROM finance_events ORDER BY id`; + await applyPendingMigrations(f.database.connectionString); + await applyPendingMigrations(f.database.connectionString); + expect((await inspectMigrations(f.database.connectionString)).status).toBe("upToDate"); + const after = await sql`SELECT id,company_id,agent_id,heartbeat_run_id,provider,model,cost_cents::text,input_tokens,occurred_at FROM cost_events ORDER BY id`; + expect(after.map(row => ({ ...row, cost_cents: Number(row.cost_cents) }))).toEqual(before.map(row => ({ ...row, cost_cents: Number(row.cost_cents) }))); + expect((await sql`SELECT id,event_kind,direction,amount_cents,currency,external_invoice_id FROM finance_events ORDER BY id`).map(row => ({ ...row, amount_cents: Number(row.amount_cents) }))).toEqual(financeBefore); + expect(await sql`SELECT cost_accounting_pending,cost_accounted_at FROM heartbeat_runs WHERE id=${run}`).toEqual([{ cost_accounting_pending: false, cost_accounted_at: null }]); + expect(await sql`SELECT enforcement_version,enforcement_delivered_version,unpriced_usage_policy FROM budget_policies WHERE id=${policy}`) + .toEqual([{ enforcement_version: 0, enforcement_delivered_version: 0, unpriced_usage_policy: "block" }]); + for (const [table, column] of [["companies","spent_monthly_cents"],["agents","spent_monthly_cents"],["agent_runtime_state","total_cost_cents"]]) { + expect((await sql.unsafe(`SELECT ${column}::text AS value FROM ${table}`))[0].value).toBe("2147483647.0000000"); + } + expect(await sql`SELECT DISTINCT idempotency_key,receipt_hash FROM cost_events`).toEqual([{ idempotency_key: null, receipt_hash: null }]); + expect((await sql`SELECT reservation_cents::text FROM budget_policies WHERE id=${policy}`)[0].reservation_cents).toBe("0.0000000"); + expect((await sql`SELECT spend_month_utc FROM companies WHERE id=${company}`)[0].spend_month_utc).toBeNull(); + await sql`INSERT INTO finance_events (company_id,event_kind,biller,amount_cents,currency,occurred_at) VALUES (${company},'adjustment','new',0.0000001,'USD',now())`; + expect((await sql`SELECT amount_cents::text FROM finance_events WHERE biller='new'`)[0].amount_cents).toBe("0.0000001"); + // Closed historical incidents must not prevent a fresh threshold crossing. + const insertIncident = () => sql`INSERT INTO budget_incidents (company_id,policy_id,scope_type,scope_id,metric,window_kind,window_start,window_end,threshold_type,amount_limit,amount_observed,status) + VALUES (${company},${policy},'agent',${agent},'billed_cents','calendar_month_utc','2026-01-01Z','2026-02-01Z','hard',150,150.4,'open')`; + await insertIncident(); + await expect(insertIncident()).rejects.toMatchObject({ code: "23505" }); + expect(await sql`SELECT status FROM budget_incidents ORDER BY status`).toEqual([{ status:"dismissed" },{ status:"open" },{ status:"resolved" }]); + await sql`INSERT INTO cost_events (company_id,agent_id,provider,model,cost_cents,occurred_at,idempotency_key) + VALUES (${company},${agent},'new','model',0.0000001,now(),'new-receipt')`; + expect((await sql`SELECT cost_cents::text FROM cost_events WHERE idempotency_key='new-receipt'`)[0].cost_cents).toBe("0.0000001"); + await expect(sql`INSERT INTO cost_events (company_id,agent_id,provider,model,cost_cents,occurred_at,idempotency_key) + VALUES (${company},${agent},'new','model',1,now(),'new-receipt')`).rejects.toMatchObject({ code: "23505" }); + // A development deployment can have applied this schema under an older + // migration number. Replaying the SQL must preserve both old and new rows. + const beforeReplay = await sql`SELECT id,cost_cents::text FROM cost_events ORDER BY id`; + const migration = readFileSync(new URL("./migrations/0295_lean_baron_strucker.sql", import.meta.url), "utf8"); + await sql.begin(async (tx) => { + for (const statement of migration.split("--> statement-breakpoint")) await tx.unsafe(statement); + }); + expect(await sql`SELECT id,cost_cents::text FROM cost_events ORDER BY id`).toEqual(beforeReplay); + } finally { await f.close(); } + }, 60_000); + + it("rejects invalid historical amounts atomically and can retry after explicit repair", async () => { + const f = await historicalDatabase(); const { sql } = f; + const company = randomUUID(), agent = randomUUID(); + try { + await sql`INSERT INTO companies (id,name,issue_prefix) VALUES (${company},'Invalid historical','BAD')`; + await sql`INSERT INTO agents (id,company_id,name) VALUES (${agent},${company},'Worker')`; + await sql`INSERT INTO cost_events (company_id,agent_id,provider,model,cost_cents,occurred_at) VALUES (${company},${agent},'old','old',-1,now())`; + const journal = await sql`SELECT * FROM drizzle.__drizzle_migrations ORDER BY id`; + await expect(applyPendingMigrations(f.database.connectionString)).rejects.toThrow(); + expect(await sql`SELECT * FROM drizzle.__drizzle_migrations ORDER BY id`).toEqual(journal); + expect((await sql`SELECT data_type FROM information_schema.columns WHERE table_name='cost_events' AND column_name='cost_cents'`)[0].data_type).toBe("integer"); + expect(await sql`SELECT column_name FROM information_schema.columns WHERE table_name='cost_events' AND column_name='idempotency_key'`).toHaveLength(0); + expect((await sql`SELECT cost_cents FROM cost_events`)[0].cost_cents).toBe(-1); + await sql`UPDATE cost_events SET cost_cents=0`; + await applyPendingMigrations(f.database.connectionString); + expect((await inspectMigrations(f.database.connectionString)).status).toBe("upToDate"); + } finally { await f.close(); } + }, 60_000); +}); diff --git a/packages/db/src/migrations/0295_lean_baron_strucker.sql b/packages/db/src/migrations/0295_lean_baron_strucker.sql new file mode 100644 index 00000000000..a1619f0ade0 --- /dev/null +++ b/packages/db/src/migrations/0295_lean_baron_strucker.sql @@ -0,0 +1,229 @@ +-- Preserve values while upgrading exact accounting. Additions are replay-safe. +CREATE TABLE IF NOT EXISTS "accounting_runtime_baselines" ( + "agent_id" uuid PRIMARY KEY NOT NULL, + "company_id" uuid NOT NULL, + "cost_cents" numeric(24, 7) NOT NULL, + "input_tokens" numeric(30, 0) NOT NULL, + "cached_input_tokens" numeric(30, 0) NOT NULL, + "output_tokens" numeric(30, 0) NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "billing_invoice_lines" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "company_id" uuid NOT NULL, + "invoice_id" uuid NOT NULL, + "external_id" text NOT NULL, + "kind" text NOT NULL, + "amount_cents" numeric(24, 7) NOT NULL, + "occurred_at" timestamp with time zone NOT NULL, + "cost_event_id" uuid, + "run_id" uuid, + "provider_request_id" text, + "model" text, + "pricing" jsonb DEFAULT '{}'::jsonb NOT NULL, + CONSTRAINT "billing_invoice_lines_amount_check" CHECK ("billing_invoice_lines"."amount_cents" >= 0), + CONSTRAINT "billing_invoice_lines_kind_check" CHECK ("billing_invoice_lines"."kind" in ('inference', 'fee', 'credit')) +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "billing_invoices" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "company_id" uuid NOT NULL, + "biller" text NOT NULL, + "external_id" text NOT NULL, + "currency" text NOT NULL, + "receipt_hash" text NOT NULL, + "imported_by" text NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "budget_reservations" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "company_id" uuid NOT NULL, + "run_id" uuid NOT NULL, + "agent_id" uuid NOT NULL, + "project_id" uuid, + "amount_cents" numeric(24, 7) NOT NULL, + "state" text DEFAULT 'held' NOT NULL, + "provider_started_at" timestamp with time zone, + "settled_at" timestamp with time zone, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "budget_reservations_state_check" CHECK ("budget_reservations"."state" in ('held', 'settled', 'released')), + CONSTRAINT "budget_reservations_amount_check" CHECK ("budget_reservations"."amount_cents" >= 0) +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "cost_adjustments" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "company_id" uuid NOT NULL, + "cost_event_id" uuid NOT NULL, + "invoice_line_id" uuid, + "idempotency_key" text NOT NULL, + "receipt_hash" text NOT NULL, + "previous_cents" numeric(24, 7) NOT NULL, + "corrected_cents" numeric(24, 7) NOT NULL, + "previous_status" text NOT NULL, + "previous_pricing" jsonb DEFAULT '{}'::jsonb NOT NULL, + "reason" text NOT NULL, + "actor_id" text NOT NULL, + "pricing" jsonb DEFAULT '{}'::jsonb NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "cost_adjustments_amount_check" CHECK ("cost_adjustments"."previous_cents" >= 0 and "cost_adjustments"."corrected_cents" >= 0) +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "provider_billing_snapshots" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "company_id" uuid NOT NULL, + "provider" text NOT NULL, + "account_id" text NOT NULL, + "scope_id" text NOT NULL, + "day" text NOT NULL, + "amount_cents" numeric(24, 7) NOT NULL, + "revision" integer DEFAULT 1 NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "provider_billing_snapshots_amount_check" CHECK ("provider_billing_snapshots"."amount_cents" >= 0) +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "run_usage_receipts" ( + "id" uuid PRIMARY KEY NOT NULL, + "company_id" uuid NOT NULL, + "run_id" uuid NOT NULL, + "source_id" uuid NOT NULL, + "sequence" integer NOT NULL, + "receipt_hash" text NOT NULL, + "receipt_json" jsonb NOT NULL, + "complete" boolean NOT NULL, + "received_at" timestamp with time zone NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "run_usage_receipts_positive_sequence" CHECK ("run_usage_receipts"."sequence" > 0) +); +--> statement-breakpoint +DROP INDEX IF EXISTS "budget_incidents_policy_window_threshold_idx";--> statement-breakpoint +ALTER TABLE "agent_runtime_state" ALTER COLUMN "total_cost_cents" SET DATA TYPE numeric(24, 7);--> statement-breakpoint +ALTER TABLE "agents" ALTER COLUMN "spent_monthly_cents" SET DATA TYPE numeric(24, 7);--> statement-breakpoint +ALTER TABLE "budget_incidents" ALTER COLUMN "amount_observed" SET DATA TYPE numeric(24, 7);--> statement-breakpoint +ALTER TABLE "companies" ALTER COLUMN "spent_monthly_cents" SET DATA TYPE numeric(24, 7);--> statement-breakpoint +ALTER TABLE "cost_events" ALTER COLUMN "cost_cents" SET DATA TYPE numeric(24, 7);--> statement-breakpoint +ALTER TABLE "finance_events" ALTER COLUMN "amount_cents" SET DATA TYPE numeric(24, 7);--> statement-breakpoint +ALTER TABLE "agents" ADD COLUMN IF NOT EXISTS "spend_month_utc" text;--> statement-breakpoint +ALTER TABLE "budget_policies" ADD COLUMN IF NOT EXISTS "reservation_cents" numeric(24, 7) DEFAULT '0' NOT NULL;--> statement-breakpoint +ALTER TABLE "budget_policies" ADD COLUMN IF NOT EXISTS "unpriced_usage_policy" text DEFAULT 'block' NOT NULL;--> statement-breakpoint +ALTER TABLE "budget_policies" ADD COLUMN IF NOT EXISTS "enforcement_version" integer DEFAULT 0 NOT NULL;--> statement-breakpoint +ALTER TABLE "budget_policies" ADD COLUMN IF NOT EXISTS "enforcement_delivered_version" integer DEFAULT 0 NOT NULL;--> statement-breakpoint +ALTER TABLE "companies" ADD COLUMN IF NOT EXISTS "spend_month_utc" text;--> statement-breakpoint +ALTER TABLE "cost_events" ADD COLUMN IF NOT EXISTS "idempotency_key" text;--> statement-breakpoint +ALTER TABLE "cost_events" ADD COLUMN IF NOT EXISTS "receipt_hash" text;--> statement-breakpoint +ALTER TABLE "cost_events" ADD COLUMN IF NOT EXISTS "provider_request_id" text;--> statement-breakpoint +ALTER TABLE "cost_events" ADD COLUMN IF NOT EXISTS "reported_cost_cents" numeric(24, 7);--> statement-breakpoint +ALTER TABLE "cost_events" ADD COLUMN IF NOT EXISTS "pricing_provenance" jsonb;--> statement-breakpoint +ALTER TABLE "finance_events" ADD COLUMN IF NOT EXISTS "idempotency_key" text;--> statement-breakpoint +ALTER TABLE "finance_events" ADD COLUMN IF NOT EXISTS "receipt_hash" text;--> statement-breakpoint +ALTER TABLE "heartbeat_runs" ADD COLUMN IF NOT EXISTS "cost_accounting_pending" boolean DEFAULT false NOT NULL;--> statement-breakpoint +ALTER TABLE "heartbeat_runs" ADD COLUMN IF NOT EXISTS "cost_accounted_at" timestamp with time zone;--> statement-breakpoint +ALTER TABLE "heartbeat_runs" ADD COLUMN IF NOT EXISTS "accounting_projection_version" text;--> statement-breakpoint +ALTER TABLE "heartbeat_runs" ADD COLUMN IF NOT EXISTS "accounting_last_attempt_at" timestamp with time zone;--> statement-breakpoint +ALTER TABLE "heartbeat_runs" ADD COLUMN IF NOT EXISTS "accounting_last_error" text;--> statement-breakpoint +ALTER TABLE "heartbeat_runs" ADD COLUMN IF NOT EXISTS "accounting_attempt_count" integer DEFAULT 0 NOT NULL;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'accounting_runtime_baselines_agent_id_agents_id_fk' AND conrelid = 'public.accounting_runtime_baselines'::regclass) THEN + ALTER TABLE "accounting_runtime_baselines" ADD CONSTRAINT "accounting_runtime_baselines_agent_id_agents_id_fk" FOREIGN KEY ("agent_id") REFERENCES "public"."agents"("id") ON DELETE cascade ON UPDATE no action; + END IF; +END $$;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'accounting_runtime_baselines_company_id_companies_id_fk' AND conrelid = 'public.accounting_runtime_baselines'::regclass) THEN + ALTER TABLE "accounting_runtime_baselines" ADD CONSTRAINT "accounting_runtime_baselines_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action; + END IF; +END $$;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'billing_invoice_lines_company_id_companies_id_fk' AND conrelid = 'public.billing_invoice_lines'::regclass) THEN + ALTER TABLE "billing_invoice_lines" ADD CONSTRAINT "billing_invoice_lines_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action; + END IF; +END $$;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'billing_invoice_lines_invoice_id_billing_invoices_id_fk' AND conrelid = 'public.billing_invoice_lines'::regclass) THEN + ALTER TABLE "billing_invoice_lines" ADD CONSTRAINT "billing_invoice_lines_invoice_id_billing_invoices_id_fk" FOREIGN KEY ("invoice_id") REFERENCES "public"."billing_invoices"("id") ON DELETE cascade ON UPDATE no action; + END IF; +END $$;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'billing_invoice_lines_cost_event_id_cost_events_id_fk' AND conrelid = 'public.billing_invoice_lines'::regclass) THEN + ALTER TABLE "billing_invoice_lines" ADD CONSTRAINT "billing_invoice_lines_cost_event_id_cost_events_id_fk" FOREIGN KEY ("cost_event_id") REFERENCES "public"."cost_events"("id") ON DELETE cascade ON UPDATE no action; + END IF; +END $$;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'billing_invoices_company_id_companies_id_fk' AND conrelid = 'public.billing_invoices'::regclass) THEN + ALTER TABLE "billing_invoices" ADD CONSTRAINT "billing_invoices_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action; + END IF; +END $$;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'budget_reservations_company_id_companies_id_fk' AND conrelid = 'public.budget_reservations'::regclass) THEN + ALTER TABLE "budget_reservations" ADD CONSTRAINT "budget_reservations_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action; + END IF; +END $$;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'budget_reservations_run_id_heartbeat_runs_id_fk' AND conrelid = 'public.budget_reservations'::regclass) THEN + ALTER TABLE "budget_reservations" ADD CONSTRAINT "budget_reservations_run_id_heartbeat_runs_id_fk" FOREIGN KEY ("run_id") REFERENCES "public"."heartbeat_runs"("id") ON DELETE cascade ON UPDATE no action; + END IF; +END $$;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'budget_reservations_agent_id_agents_id_fk' AND conrelid = 'public.budget_reservations'::regclass) THEN + ALTER TABLE "budget_reservations" ADD CONSTRAINT "budget_reservations_agent_id_agents_id_fk" FOREIGN KEY ("agent_id") REFERENCES "public"."agents"("id") ON DELETE cascade ON UPDATE no action; + END IF; +END $$;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'cost_adjustments_company_id_companies_id_fk' AND conrelid = 'public.cost_adjustments'::regclass) THEN + ALTER TABLE "cost_adjustments" ADD CONSTRAINT "cost_adjustments_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action; + END IF; +END $$;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'cost_adjustments_cost_event_id_cost_events_id_fk' AND conrelid = 'public.cost_adjustments'::regclass) THEN + ALTER TABLE "cost_adjustments" ADD CONSTRAINT "cost_adjustments_cost_event_id_cost_events_id_fk" FOREIGN KEY ("cost_event_id") REFERENCES "public"."cost_events"("id") ON DELETE cascade ON UPDATE no action; + END IF; +END $$;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'cost_adjustments_invoice_line_id_billing_invoice_lines_id_fk' AND conrelid = 'public.cost_adjustments'::regclass) THEN + ALTER TABLE "cost_adjustments" ADD CONSTRAINT "cost_adjustments_invoice_line_id_billing_invoice_lines_id_fk" FOREIGN KEY ("invoice_line_id") REFERENCES "public"."billing_invoice_lines"("id") ON DELETE cascade ON UPDATE no action; + END IF; +END $$;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'provider_billing_snapshots_company_id_companies_id_fk' AND conrelid = 'public.provider_billing_snapshots'::regclass) THEN + ALTER TABLE "provider_billing_snapshots" ADD CONSTRAINT "provider_billing_snapshots_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action; + END IF; +END $$;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'run_usage_receipts_company_id_companies_id_fk' AND conrelid = 'public.run_usage_receipts'::regclass) THEN + ALTER TABLE "run_usage_receipts" ADD CONSTRAINT "run_usage_receipts_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action; + END IF; +END $$;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'run_usage_receipts_run_id_heartbeat_runs_id_fk' AND conrelid = 'public.run_usage_receipts'::regclass) THEN + ALTER TABLE "run_usage_receipts" ADD CONSTRAINT "run_usage_receipts_run_id_heartbeat_runs_id_fk" FOREIGN KEY ("run_id") REFERENCES "public"."heartbeat_runs"("id") ON DELETE cascade ON UPDATE no action; + END IF; +END $$;--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "accounting_runtime_baselines_company_idx" ON "accounting_runtime_baselines" USING btree ("company_id");--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_invoice_lines_identity_idx" ON "billing_invoice_lines" USING btree ("invoice_id","external_id");--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "billing_invoice_lines_company_idx" ON "billing_invoice_lines" USING btree ("company_id","invoice_id");--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_invoices_identity_idx" ON "billing_invoices" USING btree ("company_id","biller","external_id");--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "budget_reservations_run_idx" ON "budget_reservations" USING btree ("company_id","run_id");--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "budget_reservations_active_idx" ON "budget_reservations" USING btree ("company_id","agent_id","project_id") WHERE "budget_reservations"."state" = 'held';--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "cost_adjustments_receipt_idx" ON "cost_adjustments" USING btree ("company_id","idempotency_key");--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "cost_adjustments_event_idx" ON "cost_adjustments" USING btree ("company_id","cost_event_id","created_at");--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "cost_adjustments_invoice_line_idx" ON "cost_adjustments" USING btree ("company_id","invoice_line_id");--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "provider_billing_snapshots_identity_idx" ON "provider_billing_snapshots" USING btree ("company_id","provider","account_id","scope_id","day");--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "run_usage_receipts_source_sequence_idx" ON "run_usage_receipts" USING btree ("company_id","run_id","source_id","sequence");--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "run_usage_receipts_run_latest_idx" ON "run_usage_receipts" USING btree ("company_id","run_id","received_at");--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "cost_events_company_receipt_idx" ON "cost_events" USING btree ("company_id","idempotency_key");--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "cost_events_provider_request_idx" ON "cost_events" USING btree ("company_id","biller","provider_request_id");--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "cost_events_company_project_occurred_idx" ON "cost_events" USING btree ("company_id","project_id","occurred_at");--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "cost_events_unpriced_idx" ON "cost_events" USING btree ("company_id","occurred_at","id") WHERE "cost_events"."cost_status" = 'unpriced' and "cost_events"."billing_type" <> 'subscription_included';--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "finance_events_company_receipt_idx" ON "finance_events" USING btree ("company_id","idempotency_key");--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "heartbeat_runs_cost_accounting_pending_idx" ON "heartbeat_runs" USING btree ("updated_at","id") WHERE "heartbeat_runs"."cost_accounting_pending" = true;--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "budget_incidents_policy_window_threshold_idx" ON "budget_incidents" USING btree ("policy_id","window_start","threshold_type") WHERE "budget_incidents"."status" = 'open';--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'budget_policies_reservation_nonnegative' AND conrelid = 'public.budget_policies'::regclass) THEN + ALTER TABLE "budget_policies" ADD CONSTRAINT "budget_policies_reservation_nonnegative" CHECK ("budget_policies"."reservation_cents" >= 0); + END IF; +END $$;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'cost_events_nonnegative_amounts' AND conrelid = 'public.cost_events'::regclass) THEN + ALTER TABLE "cost_events" ADD CONSTRAINT "cost_events_nonnegative_amounts" CHECK ("cost_events"."cost_cents" >= 0 and "cost_events"."input_tokens" >= 0 and "cost_events"."cached_input_tokens" >= 0 and "cost_events"."output_tokens" >= 0); + END IF; +END $$; \ No newline at end of file diff --git a/packages/db/src/migrations/meta/0295_snapshot.json b/packages/db/src/migrations/meta/0295_snapshot.json new file mode 100644 index 00000000000..272402b1c5f --- /dev/null +++ b/packages/db/src/migrations/meta/0295_snapshot.json @@ -0,0 +1,51689 @@ +{ + "id": "f54f3d6f-8832-45af-bcd6-edd182326f08", + "prevId": "66a0b3c9-91bb-46b8-a868-a6facedbc14b", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.accounting_runtime_baselines": { + "name": "accounting_runtime_baselines", + "schema": "", + "columns": { + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "cost_cents": { + "name": "cost_cents", + "type": "numeric(24, 7)", + "primaryKey": false, + "notNull": true + }, + "input_tokens": { + "name": "input_tokens", + "type": "numeric(30, 0)", + "primaryKey": false, + "notNull": true + }, + "cached_input_tokens": { + "name": "cached_input_tokens", + "type": "numeric(30, 0)", + "primaryKey": false, + "notNull": true + }, + "output_tokens": { + "name": "output_tokens", + "type": "numeric(30, 0)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "accounting_runtime_baselines_company_idx": { + "name": "accounting_runtime_baselines_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "accounting_runtime_baselines_agent_id_agents_id_fk": { + "name": "accounting_runtime_baselines_agent_id_agents_id_fk", + "tableFrom": "accounting_runtime_baselines", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "accounting_runtime_baselines_company_id_companies_id_fk": { + "name": "accounting_runtime_baselines_company_id_companies_id_fk", + "tableFrom": "accounting_runtime_baselines", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing_invoice_lines": { + "name": "billing_invoice_lines", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "invoice_id": { + "name": "invoice_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount_cents": { + "name": "amount_cents", + "type": "numeric(24, 7)", + "primaryKey": false, + "notNull": true + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "cost_event_id": { + "name": "cost_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider_request_id": { + "name": "provider_request_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pricing": { + "name": "pricing", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": { + "billing_invoice_lines_identity_idx": { + "name": "billing_invoice_lines_identity_idx", + "columns": [ + { + "expression": "invoice_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_invoice_lines_company_idx": { + "name": "billing_invoice_lines_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "invoice_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_invoice_lines_company_id_companies_id_fk": { + "name": "billing_invoice_lines_company_id_companies_id_fk", + "tableFrom": "billing_invoice_lines", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "billing_invoice_lines_invoice_id_billing_invoices_id_fk": { + "name": "billing_invoice_lines_invoice_id_billing_invoices_id_fk", + "tableFrom": "billing_invoice_lines", + "tableTo": "billing_invoices", + "columnsFrom": [ + "invoice_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "billing_invoice_lines_cost_event_id_cost_events_id_fk": { + "name": "billing_invoice_lines_cost_event_id_cost_events_id_fk", + "tableFrom": "billing_invoice_lines", + "tableTo": "cost_events", + "columnsFrom": [ + "cost_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_invoice_lines_amount_check": { + "name": "billing_invoice_lines_amount_check", + "value": "\"billing_invoice_lines\".\"amount_cents\" >= 0" + }, + "billing_invoice_lines_kind_check": { + "name": "billing_invoice_lines_kind_check", + "value": "\"billing_invoice_lines\".\"kind\" in ('inference', 'fee', 'credit')" + } + }, + "isRLSEnabled": false + }, + "public.billing_invoices": { + "name": "billing_invoices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "biller": { + "name": "biller", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "receipt_hash": { + "name": "receipt_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imported_by": { + "name": "imported_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "billing_invoices_identity_idx": { + "name": "billing_invoices_identity_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "biller", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_invoices_company_id_companies_id_fk": { + "name": "billing_invoices_company_id_companies_id_fk", + "tableFrom": "billing_invoices", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.budget_reservations": { + "name": "budget_reservations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "amount_cents": { + "name": "amount_cents", + "type": "numeric(24, 7)", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'held'" + }, + "provider_started_at": { + "name": "provider_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "settled_at": { + "name": "settled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "budget_reservations_run_idx": { + "name": "budget_reservations_run_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "budget_reservations_active_idx": { + "name": "budget_reservations_active_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"budget_reservations\".\"state\" = 'held'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "budget_reservations_company_id_companies_id_fk": { + "name": "budget_reservations_company_id_companies_id_fk", + "tableFrom": "budget_reservations", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "budget_reservations_run_id_heartbeat_runs_id_fk": { + "name": "budget_reservations_run_id_heartbeat_runs_id_fk", + "tableFrom": "budget_reservations", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "budget_reservations_agent_id_agents_id_fk": { + "name": "budget_reservations_agent_id_agents_id_fk", + "tableFrom": "budget_reservations", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "budget_reservations_state_check": { + "name": "budget_reservations_state_check", + "value": "\"budget_reservations\".\"state\" in ('held', 'settled', 'released')" + }, + "budget_reservations_amount_check": { + "name": "budget_reservations_amount_check", + "value": "\"budget_reservations\".\"amount_cents\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.cost_adjustments": { + "name": "cost_adjustments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "cost_event_id": { + "name": "cost_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "invoice_line_id": { + "name": "invoice_line_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "receipt_hash": { + "name": "receipt_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previous_cents": { + "name": "previous_cents", + "type": "numeric(24, 7)", + "primaryKey": false, + "notNull": true + }, + "corrected_cents": { + "name": "corrected_cents", + "type": "numeric(24, 7)", + "primaryKey": false, + "notNull": true + }, + "previous_status": { + "name": "previous_status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previous_pricing": { + "name": "previous_pricing", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pricing": { + "name": "pricing", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "cost_adjustments_receipt_idx": { + "name": "cost_adjustments_receipt_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cost_adjustments_event_idx": { + "name": "cost_adjustments_event_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cost_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cost_adjustments_invoice_line_idx": { + "name": "cost_adjustments_invoice_line_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "invoice_line_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cost_adjustments_company_id_companies_id_fk": { + "name": "cost_adjustments_company_id_companies_id_fk", + "tableFrom": "cost_adjustments", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cost_adjustments_cost_event_id_cost_events_id_fk": { + "name": "cost_adjustments_cost_event_id_cost_events_id_fk", + "tableFrom": "cost_adjustments", + "tableTo": "cost_events", + "columnsFrom": [ + "cost_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cost_adjustments_invoice_line_id_billing_invoice_lines_id_fk": { + "name": "cost_adjustments_invoice_line_id_billing_invoice_lines_id_fk", + "tableFrom": "cost_adjustments", + "tableTo": "billing_invoice_lines", + "columnsFrom": [ + "invoice_line_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "cost_adjustments_amount_check": { + "name": "cost_adjustments_amount_check", + "value": "\"cost_adjustments\".\"previous_cents\" >= 0 and \"cost_adjustments\".\"corrected_cents\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.provider_billing_snapshots": { + "name": "provider_billing_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope_id": { + "name": "scope_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "day": { + "name": "day", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount_cents": { + "name": "amount_cents", + "type": "numeric(24, 7)", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "provider_billing_snapshots_identity_idx": { + "name": "provider_billing_snapshots_identity_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "day", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "provider_billing_snapshots_company_id_companies_id_fk": { + "name": "provider_billing_snapshots_company_id_companies_id_fk", + "tableFrom": "provider_billing_snapshots", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "provider_billing_snapshots_amount_check": { + "name": "provider_billing_snapshots_amount_check", + "value": "\"provider_billing_snapshots\".\"amount_cents\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.run_usage_receipts": { + "name": "run_usage_receipts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_id": { + "name": "source_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sequence": { + "name": "sequence", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "receipt_hash": { + "name": "receipt_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "receipt_json": { + "name": "receipt_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "complete": { + "name": "complete", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "received_at": { + "name": "received_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "run_usage_receipts_source_sequence_idx": { + "name": "run_usage_receipts_source_sequence_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sequence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "run_usage_receipts_run_latest_idx": { + "name": "run_usage_receipts_run_latest_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "received_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "run_usage_receipts_company_id_companies_id_fk": { + "name": "run_usage_receipts_company_id_companies_id_fk", + "tableFrom": "run_usage_receipts", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "run_usage_receipts_run_id_heartbeat_runs_id_fk": { + "name": "run_usage_receipts_run_id_heartbeat_runs_id_fk", + "tableFrom": "run_usage_receipts", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "run_usage_receipts_positive_sequence": { + "name": "run_usage_receipts_positive_sequence", + "value": "\"run_usage_receipts\".\"sequence\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.activity_log": { + "name": "activity_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'system'" + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "responsible_user_id": { + "name": "responsible_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "details": { + "name": "details", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "activity_log_company_created_idx": { + "name": "activity_log_company_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "activity_log_company_agent_created_idx": { + "name": "activity_log_company_agent_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "activity_log_company_responsible_user_created_idx": { + "name": "activity_log_company_responsible_user_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "responsible_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "activity_log_run_id_idx": { + "name": "activity_log_run_id_idx", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "activity_log_entity_type_id_idx": { + "name": "activity_log_entity_type_id_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "activity_log_company_id_companies_id_fk": { + "name": "activity_log_company_id_companies_id_fk", + "tableFrom": "activity_log", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "activity_log_agent_id_agents_id_fk": { + "name": "activity_log_agent_id_agents_id_fk", + "tableFrom": "activity_log", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "activity_log_run_id_heartbeat_runs_id_fk": { + "name": "activity_log_run_id_heartbeat_runs_id_fk", + "tableFrom": "activity_log", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.adapter_auth_sessions": { + "name": "adapter_auth_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "environment_id": { + "name": "environment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "ai_connection": { + "name": "ai_connection", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "connection_grant_id": { + "name": "connection_grant_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "connection_method": { + "name": "connection_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "adapter_type": { + "name": "adapter_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_by_user_id": { + "name": "started_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "public_session_id": { + "name": "public_session_id", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "provider_lease_id": { + "name": "provider_lease_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'starting'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "promotion_expires_at": { + "name": "promotion_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "bound_at": { + "name": "bound_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "result_claim": { + "name": "result_claim", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "adapter_auth_sessions_company_status_idx": { + "name": "adapter_auth_sessions_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "adapter_auth_sessions_company_owner_adapter_active_uq": { + "name": "adapter_auth_sessions_company_owner_adapter_active_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "adapter_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"adapter_auth_sessions\".\"status\" IN ('starting', 'waiting_for_user', 'promoting', 'awaiting_code', 'submitting')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "adapter_auth_sessions_public_session_id_uq": { + "name": "adapter_auth_sessions_public_session_id_uq", + "columns": [ + { + "expression": "public_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "adapter_auth_sessions_environment_idx": { + "name": "adapter_auth_sessions_environment_idx", + "columns": [ + { + "expression": "environment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "adapter_auth_sessions_expires_idx": { + "name": "adapter_auth_sessions_expires_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "adapter_auth_sessions_provider_lease_idx": { + "name": "adapter_auth_sessions_provider_lease_idx", + "columns": [ + { + "expression": "provider_lease_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "adapter_auth_sessions_company_id_companies_id_fk": { + "name": "adapter_auth_sessions_company_id_companies_id_fk", + "tableFrom": "adapter_auth_sessions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "adapter_auth_sessions_environment_id_environments_id_fk": { + "name": "adapter_auth_sessions_environment_id_environments_id_fk", + "tableFrom": "adapter_auth_sessions", + "tableTo": "environments", + "columnsFrom": [ + "environment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_api_keys": { + "name": "agent_api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key_hash": { + "name": "key_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "responsible_user_id": { + "name": "responsible_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scope_config": { + "name": "scope_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_api_keys_key_hash_idx": { + "name": "agent_api_keys_key_hash_idx", + "columns": [ + { + "expression": "key_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_api_keys_company_agent_idx": { + "name": "agent_api_keys_company_agent_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_api_keys_agent_id_agents_id_fk": { + "name": "agent_api_keys_agent_id_agents_id_fk", + "tableFrom": "agent_api_keys", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_api_keys_company_id_companies_id_fk": { + "name": "agent_api_keys_company_id_companies_id_fk", + "tableFrom": "agent_api_keys", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_config_revisions": { + "name": "agent_config_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'patch'" + }, + "rolled_back_from_revision_id": { + "name": "rolled_back_from_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "changed_keys": { + "name": "changed_keys", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "before_config": { + "name": "before_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "after_config": { + "name": "after_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_config_revisions_company_agent_created_idx": { + "name": "agent_config_revisions_company_agent_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_config_revisions_agent_created_idx": { + "name": "agent_config_revisions_agent_created_idx", + "columns": [ + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_config_revisions_company_id_companies_id_fk": { + "name": "agent_config_revisions_company_id_companies_id_fk", + "tableFrom": "agent_config_revisions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_config_revisions_agent_id_agents_id_fk": { + "name": "agent_config_revisions_agent_id_agents_id_fk", + "tableFrom": "agent_config_revisions", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "agent_config_revisions_created_by_agent_id_agents_id_fk": { + "name": "agent_config_revisions_created_by_agent_id_agents_id_fk", + "tableFrom": "agent_config_revisions", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_instruction_heads": { + "name": "agent_instruction_heads", + "schema": "", + "columns": { + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "entry_file": { + "name": "entry_file", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "revision_id": { + "name": "revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "agent_instruction_heads_company_id_agent_id_entry_file_revision_id_agent_instruction_revisions_company_id_agent_id_entry_file_id_fk": { + "name": "agent_instruction_heads_company_id_agent_id_entry_file_revision_id_agent_instruction_revisions_company_id_agent_id_entry_file_id_fk", + "tableFrom": "agent_instruction_heads", + "tableTo": "agent_instruction_revisions", + "columnsFrom": [ + "company_id", + "agent_id", + "entry_file", + "revision_id" + ], + "columnsTo": [ + "company_id", + "agent_id", + "entry_file", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "agent_instruction_heads_identity_uq": { + "name": "agent_instruction_heads_identity_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "agent_id", + "entry_file" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_instruction_revisions": { + "name": "agent_instruction_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "entry_file": { + "name": "entry_file", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_base64": { + "name": "content_base64", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "byte_length": { + "name": "byte_length", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "parent_revision_id": { + "name": "parent_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "base_revision_id": { + "name": "base_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "restored_from_revision_id": { + "name": "restored_from_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "actor_agent_id": { + "name": "actor_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "responsible_user_id": { + "name": "responsible_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_run_id": { + "name": "source_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_instruction_revisions_history_idx": { + "name": "agent_instruction_revisions_history_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entry_file", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_instruction_revisions_company_id_agent_id_agents_company_id_id_fk": { + "name": "agent_instruction_revisions_company_id_agent_id_agents_company_id_id_fk", + "tableFrom": "agent_instruction_revisions", + "tableTo": "agents", + "columnsFrom": [ + "company_id", + "agent_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "agent_instruction_revisions_identity_uq": { + "name": "agent_instruction_revisions_identity_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "agent_id", + "entry_file", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_instruction_working_copies": { + "name": "agent_instruction_working_copies", + "schema": "", + "columns": { + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "responsible_user_id": { + "name": "responsible_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entry_file": { + "name": "entry_file", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "base_revision_id": { + "name": "base_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "base_hash": { + "name": "base_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "local_root": { + "name": "local_root", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_root": { + "name": "execution_root", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'prepared'" + }, + "candidate_base64": { + "name": "candidate_base64", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "candidate_hash": { + "name": "candidate_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "receipt": { + "name": "receipt", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "process_stopped_at": { + "name": "process_stopped_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_instruction_copies_pending_idx": { + "name": "agent_instruction_copies_pending_idx", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_instruction_copies_agent_idx": { + "name": "agent_instruction_copies_agent_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_instruction_working_copies_run_id_heartbeat_runs_id_fk": { + "name": "agent_instruction_working_copies_run_id_heartbeat_runs_id_fk", + "tableFrom": "agent_instruction_working_copies", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "agent_instruction_working_copies_company_id_agent_id_agents_company_id_id_fk": { + "name": "agent_instruction_working_copies_company_id_agent_id_agents_company_id_id_fk", + "tableFrom": "agent_instruction_working_copies", + "tableTo": "agents", + "columnsFrom": [ + "company_id", + "agent_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_memberships": { + "name": "agent_memberships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'joined'" + }, + "starred_at": { + "name": "starred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_memberships_company_user_idx": { + "name": "agent_memberships_company_user_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_memberships_company_user_starred_idx": { + "name": "agent_memberships_company_user_starred_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "starred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_memberships_agent_idx": { + "name": "agent_memberships_agent_idx", + "columns": [ + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_memberships_company_user_agent_uq": { + "name": "agent_memberships_company_user_agent_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_memberships_company_id_companies_id_fk": { + "name": "agent_memberships_company_id_companies_id_fk", + "tableFrom": "agent_memberships", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "agent_memberships_agent_id_agents_id_fk": { + "name": "agent_memberships_agent_id_agents_id_fk", + "tableFrom": "agent_memberships", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_runtime_state": { + "name": "agent_runtime_state", + "schema": "", + "columns": { + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "adapter_type": { + "name": "adapter_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state_json": { + "name": "state_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "last_run_id": { + "name": "last_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "last_run_status": { + "name": "last_run_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "total_input_tokens": { + "name": "total_input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_output_tokens": { + "name": "total_output_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_cached_input_tokens": { + "name": "total_cached_input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_cost_cents": { + "name": "total_cost_cents", + "type": "numeric(24, 7)", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_runtime_state_company_agent_idx": { + "name": "agent_runtime_state_company_agent_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_runtime_state_company_updated_idx": { + "name": "agent_runtime_state_company_updated_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_runtime_state_agent_id_agents_id_fk": { + "name": "agent_runtime_state_agent_id_agents_id_fk", + "tableFrom": "agent_runtime_state", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_runtime_state_company_id_companies_id_fk": { + "name": "agent_runtime_state_company_id_companies_id_fk", + "tableFrom": "agent_runtime_state", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_session_goal_actions": { + "name": "agent_session_goal_actions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload_json": { + "name": "payload_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "result_json": { + "name": "result_json", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "delivered_at": { + "name": "delivered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_session_goal_actions_session_request_uniq": { + "name": "agent_session_goal_actions_session_request_uniq", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_session_goal_actions_company_status_created_idx": { + "name": "agent_session_goal_actions_company_status_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_session_goal_actions_session_created_idx": { + "name": "agent_session_goal_actions_session_created_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_session_goal_actions_company_id_companies_id_fk": { + "name": "agent_session_goal_actions_company_id_companies_id_fk", + "tableFrom": "agent_session_goal_actions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_session_goal_actions_session_id_agent_task_sessions_id_fk": { + "name": "agent_session_goal_actions_session_id_agent_task_sessions_id_fk", + "tableFrom": "agent_session_goal_actions", + "tableTo": "agent_task_sessions", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_task_sessions": { + "name": "agent_task_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "adapter_type": { + "name": "adapter_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "task_key": { + "name": "task_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_params_json": { + "name": "session_params_json", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "session_display_id": { + "name": "session_display_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_run_id": { + "name": "last_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "goal_capability_json": { + "name": "goal_capability_json", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "goal_json": { + "name": "goal_json", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "goal_status": { + "name": "goal_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "goal_desired_state": { + "name": "goal_desired_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "goal_source_id": { + "name": "goal_source_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "goal_source_cursor": { + "name": "goal_source_cursor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "goal_revision": { + "name": "goal_revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "goal_observed_at": { + "name": "goal_observed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_task_sessions_company_agent_adapter_task_uniq": { + "name": "agent_task_sessions_company_agent_adapter_task_uniq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "adapter_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "task_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_task_sessions_company_agent_updated_idx": { + "name": "agent_task_sessions_company_agent_updated_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_task_sessions_company_task_updated_idx": { + "name": "agent_task_sessions_company_task_updated_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "task_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_task_sessions_company_id_companies_id_fk": { + "name": "agent_task_sessions_company_id_companies_id_fk", + "tableFrom": "agent_task_sessions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_task_sessions_agent_id_agents_id_fk": { + "name": "agent_task_sessions_agent_id_agents_id_fk", + "tableFrom": "agent_task_sessions", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_task_sessions_last_run_id_heartbeat_runs_id_fk": { + "name": "agent_task_sessions_last_run_id_heartbeat_runs_id_fk", + "tableFrom": "agent_task_sessions", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "last_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_wakeup_requests": { + "name": "agent_wakeup_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trigger_detail": { + "name": "trigger_detail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "coalesced_count": { + "name": "coalesced_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "requested_by_actor_type": { + "name": "requested_by_actor_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "requested_at": { + "name": "requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_wakeup_requests_company_agent_status_idx": { + "name": "agent_wakeup_requests_company_agent_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_wakeup_requests_company_requested_idx": { + "name": "agent_wakeup_requests_company_requested_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "requested_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_wakeup_requests_agent_requested_idx": { + "name": "agent_wakeup_requests_agent_requested_idx", + "columns": [ + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "requested_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_wakeup_requests_review_path_recovery_idempotency_uq": { + "name": "agent_wakeup_requests_review_path_recovery_idempotency_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"agent_wakeup_requests\".\"idempotency_key\" LIKE 'issue_review_path_lost:%' AND \"agent_wakeup_requests\".\"status\" <> 'skipped'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_wakeup_requests_disposition_repair_idempotency_uq": { + "name": "agent_wakeup_requests_disposition_repair_idempotency_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"agent_wakeup_requests\".\"idempotency_key\" LIKE 'issue_disposition_repair:%' AND \"agent_wakeup_requests\".\"status\" <> 'skipped'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_wakeup_requests_question_response_delivery_idempotency_uq": { + "name": "agent_wakeup_requests_question_response_delivery_idempotency_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "(\"agent_wakeup_requests\".\"idempotency_key\" LIKE 'question-response:%' OR \"agent_wakeup_requests\".\"idempotency_key\" LIKE 'interaction:%') AND \"agent_wakeup_requests\".\"status\" NOT IN ('skipped', 'failed', 'cancelled')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_wakeup_requests_connection_intent_delivery_idempotency_uq": { + "name": "agent_wakeup_requests_connection_intent_delivery_idempotency_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"agent_wakeup_requests\".\"idempotency_key\" LIKE 'connection-intent:%' AND \"agent_wakeup_requests\".\"status\" NOT IN ('skipped', 'failed', 'cancelled')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_wakeup_requests_tool_action_delivery_uq": { + "name": "agent_wakeup_requests_tool_action_delivery_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"agent_wakeup_requests\".\"idempotency_key\" LIKE 'tool-action-response:%' AND \"agent_wakeup_requests\".\"status\" NOT IN ('skipped', 'failed', 'cancelled')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_wakeup_requests_chat_completion_uq": { + "name": "agent_wakeup_requests_chat_completion_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"agent_wakeup_requests\".\"idempotency_key\" LIKE 'chat-completion:%'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_wakeup_requests_company_payload_issue_idx": { + "name": "agent_wakeup_requests_company_payload_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "(\"payload\" ->> 'issueId')", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_wakeup_requests_company_id_companies_id_fk": { + "name": "agent_wakeup_requests_company_id_companies_id_fk", + "tableFrom": "agent_wakeup_requests", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_wakeup_requests_agent_id_agents_id_fk": { + "name": "agent_wakeup_requests_agent_id_agents_id_fk", + "tableFrom": "agent_wakeup_requests", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agents": { + "name": "agents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'general'" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appearance": { + "name": "appearance", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "reports_to": { + "name": "reports_to", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "capabilities": { + "name": "capabilities", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "adapter_type": { + "name": "adapter_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'process'" + }, + "adapter_config": { + "name": "adapter_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "runtime_config": { + "name": "runtime_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "default_environment_id": { + "name": "default_environment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "budget_monthly_cents": { + "name": "budget_monthly_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "spend_month_utc": { + "name": "spend_month_utc", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "spent_monthly_cents": { + "name": "spent_monthly_cents", + "type": "numeric(24, 7)", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "pause_reason": { + "name": "pause_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "paused_at": { + "name": "paused_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error_reason": { + "name": "error_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "permissions": { + "name": "permissions", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "last_heartbeat_at": { + "name": "last_heartbeat_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agents_company_status_idx": { + "name": "agents_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agents_company_reports_to_idx": { + "name": "agents_company_reports_to_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "reports_to", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agents_company_default_environment_idx": { + "name": "agents_company_default_environment_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "default_environment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agents_company_id_companies_id_fk": { + "name": "agents_company_id_companies_id_fk", + "tableFrom": "agents", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agents_reports_to_agents_id_fk": { + "name": "agents_reports_to_agents_id_fk", + "tableFrom": "agents", + "tableTo": "agents", + "columnsFrom": [ + "reports_to" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agents_default_environment_id_environments_id_fk": { + "name": "agents_default_environment_id_environments_id_fk", + "tableFrom": "agents", + "tableTo": "environments", + "columnsFrom": [ + "default_environment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "agents_company_id_uq": { + "name": "agents_company_id_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_connection_defaults": { + "name": "ai_connection_defaults", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "method": { + "name": "method", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "grant_id": { + "name": "grant_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ai_connection_defaults_owner_method_uq": { + "name": "ai_connection_defaults_owner_method_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "method", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ai_connection_defaults_company_id_companies_id_fk": { + "name": "ai_connection_defaults_company_id_companies_id_fk", + "tableFrom": "ai_connection_defaults", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "ai_connection_defaults_company_grant_fk": { + "name": "ai_connection_defaults_company_grant_fk", + "tableFrom": "ai_connection_defaults", + "tableTo": "connection_grants", + "columnsFrom": [ + "company_id", + "grant_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "ai_connection_defaults_provider_check": { + "name": "ai_connection_defaults_provider_check", + "value": "\"ai_connection_defaults\".\"provider\" in ('anthropic','openai','openrouter','xai')" + }, + "ai_connection_defaults_method_check": { + "name": "ai_connection_defaults_method_check", + "value": "\"ai_connection_defaults\".\"method\" in ('subscription','api_key')" + } + }, + "isRLSEnabled": false + }, + "public.ai_provider_defaults": { + "name": "ai_provider_defaults", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "grant_id": { + "name": "grant_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ai_provider_defaults_owner_provider_uq": { + "name": "ai_provider_defaults_owner_provider_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ai_provider_defaults_company_id_companies_id_fk": { + "name": "ai_provider_defaults_company_id_companies_id_fk", + "tableFrom": "ai_provider_defaults", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "ai_provider_defaults_company_grant_fk": { + "name": "ai_provider_defaults_company_grant_fk", + "tableFrom": "ai_provider_defaults", + "tableTo": "connection_grants", + "columnsFrom": [ + "company_id", + "grant_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "ai_provider_defaults_provider_check": { + "name": "ai_provider_defaults_provider_check", + "value": "\"ai_provider_defaults\".\"provider\" in ('anthropic','openai','openrouter','xai')" + } + }, + "isRLSEnabled": false + }, + "public.announcement_dismissals": { + "name": "announcement_dismissals", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "announcement_id": { + "name": "announcement_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dismissed_at": { + "name": "dismissed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "announcement_dismissals_user_id_announcement_id_pk": { + "name": "announcement_dismissals_user_id_announcement_id_pk", + "columns": [ + "user_id", + "announcement_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.announcement_publications": { + "name": "announcement_publications", + "schema": "", + "columns": { + "announcement_id": { + "name": "announcement_id", + "type": "text", + "primaryKey": true, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.approval_comments": { + "name": "approval_comments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_agent_id": { + "name": "author_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "author_user_id": { + "name": "author_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "approval_comments_company_idx": { + "name": "approval_comments_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "approval_comments_approval_idx": { + "name": "approval_comments_approval_idx", + "columns": [ + { + "expression": "approval_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "approval_comments_approval_created_idx": { + "name": "approval_comments_approval_created_idx", + "columns": [ + { + "expression": "approval_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "approval_comments_company_id_companies_id_fk": { + "name": "approval_comments_company_id_companies_id_fk", + "tableFrom": "approval_comments", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "approval_comments_approval_id_approvals_id_fk": { + "name": "approval_comments_approval_id_approvals_id_fk", + "tableFrom": "approval_comments", + "tableTo": "approvals", + "columnsFrom": [ + "approval_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "approval_comments_author_agent_id_agents_id_fk": { + "name": "approval_comments_author_agent_id_agents_id_fk", + "tableFrom": "approval_comments", + "tableTo": "agents", + "columnsFrom": [ + "author_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.approvals": { + "name": "approvals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_by_agent_id": { + "name": "requested_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "requested_by_user_id": { + "name": "requested_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "decision_note": { + "name": "decision_note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "decided_by_user_id": { + "name": "decided_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "decided_at": { + "name": "decided_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "approvals_company_status_type_idx": { + "name": "approvals_company_status_type_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "approvals_company_id_companies_id_fk": { + "name": "approvals_company_id_companies_id_fk", + "tableFrom": "approvals", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "approvals_requested_by_agent_id_agents_id_fk": { + "name": "approvals_requested_by_agent_id_agents_id_fk", + "tableFrom": "approvals", + "tableTo": "agents", + "columnsFrom": [ + "requested_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.assets": { + "name": "assets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "object_key": { + "name": "object_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "byte_size": { + "name": "byte_size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "original_filename": { + "name": "original_filename", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "assets_company_created_idx": { + "name": "assets_company_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "assets_company_provider_idx": { + "name": "assets_company_provider_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "assets_company_object_key_uq": { + "name": "assets_company_object_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "object_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "assets_company_id_companies_id_fk": { + "name": "assets_company_id_companies_id_fk", + "tableFrom": "assets", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "assets_created_by_agent_id_agents_id_fk": { + "name": "assets_created_by_agent_id_agents_id_fk", + "tableFrom": "assets", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "issuer": { + "name": "issuer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "account_issuer_account_id_uq": { + "name": "account_issuer_account_id_uq", + "columns": [ + { + "expression": "issuer", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.board_api_keys": { + "name": "board_api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key_hash": { + "name": "key_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "board_api_keys_key_hash_idx": { + "name": "board_api_keys_key_hash_idx", + "columns": [ + { + "expression": "key_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "board_api_keys_user_idx": { + "name": "board_api_keys_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "board_api_keys_user_id_user_id_fk": { + "name": "board_api_keys_user_id_user_id_fk", + "tableFrom": "board_api_keys", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.browser_use_browsers": { + "name": "browser_use_browsers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider_browser_id": { + "name": "provider_browser_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "browser_use_browsers_provider_uq": { + "name": "browser_use_browsers_provider_uq", + "columns": [ + { + "expression": "provider_browser_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "browser_use_browsers_session_idx": { + "name": "browser_use_browsers_session_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "browser_use_browsers_company_id_companies_id_fk": { + "name": "browser_use_browsers_company_id_companies_id_fk", + "tableFrom": "browser_use_browsers", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "browser_use_browsers_session_id_browser_use_sessions_id_fk": { + "name": "browser_use_browsers_session_id_browser_use_sessions_id_fk", + "tableFrom": "browser_use_browsers", + "tableTo": "browser_use_sessions", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.browser_use_runs": { + "name": "browser_use_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "heartbeat_run_id": { + "name": "heartbeat_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "invocation_id": { + "name": "invocation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider_run_id": { + "name": "provider_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "recovery_cursor": { + "name": "recovery_cursor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'creating'" + }, + "detached_until": { + "name": "detached_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "event_cursor": { + "name": "event_cursor", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "events_drained": { + "name": "events_drained", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "accounted_cents": { + "name": "accounted_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "progress": { + "name": "progress", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "browser_use_runs_invocation_uq": { + "name": "browser_use_runs_invocation_uq", + "columns": [ + { + "expression": "invocation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "browser_use_runs_provider_uq": { + "name": "browser_use_runs_provider_uq", + "columns": [ + { + "expression": "provider_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "browser_use_runs_session_idx": { + "name": "browser_use_runs_session_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "browser_use_runs_company_id_companies_id_fk": { + "name": "browser_use_runs_company_id_companies_id_fk", + "tableFrom": "browser_use_runs", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "browser_use_runs_session_id_browser_use_sessions_id_fk": { + "name": "browser_use_runs_session_id_browser_use_sessions_id_fk", + "tableFrom": "browser_use_runs", + "tableTo": "browser_use_sessions", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "browser_use_runs_heartbeat_run_id_heartbeat_runs_id_fk": { + "name": "browser_use_runs_heartbeat_run_id_heartbeat_runs_id_fk", + "tableFrom": "browser_use_runs", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "heartbeat_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.browser_use_sessions": { + "name": "browser_use_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "grant_id": { + "name": "grant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider_session_id": { + "name": "provider_session_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'starting'" + }, + "idle_deadline": { + "name": "idle_deadline", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "stop_requested": { + "name": "stop_requested", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_token": { + "name": "lease_token", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "lease_until": { + "name": "lease_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "next_poll_at": { + "name": "next_poll_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "browser_use_sessions_task_idx": { + "name": "browser_use_sessions_task_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "browser_use_sessions_provider_uq": { + "name": "browser_use_sessions_provider_uq", + "columns": [ + { + "expression": "provider_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "browser_use_sessions_company_id_companies_id_fk": { + "name": "browser_use_sessions_company_id_companies_id_fk", + "tableFrom": "browser_use_sessions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "browser_use_sessions_issue_id_issues_id_fk": { + "name": "browser_use_sessions_issue_id_issues_id_fk", + "tableFrom": "browser_use_sessions", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "browser_use_sessions_agent_id_agents_id_fk": { + "name": "browser_use_sessions_agent_id_agents_id_fk", + "tableFrom": "browser_use_sessions", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "browser_use_sessions_connection_id_tool_connections_id_fk": { + "name": "browser_use_sessions_connection_id_tool_connections_id_fk", + "tableFrom": "browser_use_sessions", + "tableTo": "tool_connections", + "columnsFrom": [ + "connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "browser_use_sessions_grant_id_connection_grants_id_fk": { + "name": "browser_use_sessions_grant_id_connection_grants_id_fk", + "tableFrom": "browser_use_sessions", + "tableTo": "connection_grants", + "columnsFrom": [ + "grant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.browser_use_settings": { + "name": "browser_use_settings", + "schema": "", + "columns": { + "grant_id": { + "name": "grant_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "allowed_profile_ids": { + "name": "allowed_profile_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "max_cost_usd": { + "name": "max_cost_usd", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "browser_use_settings_grant_id_connection_grants_id_fk": { + "name": "browser_use_settings_grant_id_connection_grants_id_fk", + "tableFrom": "browser_use_settings", + "tableTo": "connection_grants", + "columnsFrom": [ + "grant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "browser_use_settings_company_id_companies_id_fk": { + "name": "browser_use_settings_company_id_companies_id_fk", + "tableFrom": "browser_use_settings", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.budget_incidents": { + "name": "budget_incidents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "policy_id": { + "name": "policy_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "scope_type": { + "name": "scope_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope_id": { + "name": "scope_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "metric": { + "name": "metric", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "window_kind": { + "name": "window_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "window_start": { + "name": "window_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "window_end": { + "name": "window_end", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "threshold_type": { + "name": "threshold_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount_limit": { + "name": "amount_limit", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "amount_observed": { + "name": "amount_observed", + "type": "numeric(24, 7)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "budget_incidents_company_status_idx": { + "name": "budget_incidents_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "budget_incidents_company_scope_idx": { + "name": "budget_incidents_company_scope_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "budget_incidents_policy_window_threshold_idx": { + "name": "budget_incidents_policy_window_threshold_idx", + "columns": [ + { + "expression": "policy_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "window_start", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "threshold_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"budget_incidents\".\"status\" = 'open'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "budget_incidents_company_id_companies_id_fk": { + "name": "budget_incidents_company_id_companies_id_fk", + "tableFrom": "budget_incidents", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "budget_incidents_policy_id_budget_policies_id_fk": { + "name": "budget_incidents_policy_id_budget_policies_id_fk", + "tableFrom": "budget_incidents", + "tableTo": "budget_policies", + "columnsFrom": [ + "policy_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "budget_incidents_approval_id_approvals_id_fk": { + "name": "budget_incidents_approval_id_approvals_id_fk", + "tableFrom": "budget_incidents", + "tableTo": "approvals", + "columnsFrom": [ + "approval_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.budget_policies": { + "name": "budget_policies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "scope_type": { + "name": "scope_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope_id": { + "name": "scope_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "metric": { + "name": "metric", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'billed_cents'" + }, + "window_kind": { + "name": "window_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "reservation_cents": { + "name": "reservation_cents", + "type": "numeric(24, 7)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "warn_percent": { + "name": "warn_percent", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 80 + }, + "hard_stop_enabled": { + "name": "hard_stop_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "unpriced_usage_policy": { + "name": "unpriced_usage_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'block'" + }, + "notify_enabled": { + "name": "notify_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "enforcement_version": { + "name": "enforcement_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "enforcement_delivered_version": { + "name": "enforcement_delivered_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_by_user_id": { + "name": "updated_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "budget_policies_company_scope_active_idx": { + "name": "budget_policies_company_scope_active_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "is_active", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "budget_policies_company_window_idx": { + "name": "budget_policies_company_window_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "window_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "metric", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "budget_policies_company_scope_metric_unique_idx": { + "name": "budget_policies_company_scope_metric_unique_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "metric", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "window_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "budget_policies_company_id_companies_id_fk": { + "name": "budget_policies_company_id_companies_id_fk", + "tableFrom": "budget_policies", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "budget_policies_reservation_nonnegative": { + "name": "budget_policies_reservation_nonnegative", + "value": "\"budget_policies\".\"reservation_cents\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.built_in_managed_resources": { + "name": "built_in_managed_resources", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "bundle_key": { + "name": "bundle_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_kind": { + "name": "resource_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_key": { + "name": "resource_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "stock_version": { + "name": "stock_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stock_hash": { + "name": "stock_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "defaults_json": { + "name": "defaults_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "built_in_managed_resources_company_idx": { + "name": "built_in_managed_resources_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "built_in_managed_resources_resource_idx": { + "name": "built_in_managed_resources_resource_idx", + "columns": [ + { + "expression": "resource_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "built_in_managed_resources_company_bundle_resource_uq": { + "name": "built_in_managed_resources_company_bundle_resource_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "bundle_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "built_in_managed_resources_company_id_companies_id_fk": { + "name": "built_in_managed_resources_company_id_companies_id_fk", + "tableFrom": "built_in_managed_resources", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.case_attachments": { + "name": "case_attachments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "case_id": { + "name": "case_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "asset_id": { + "name": "asset_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "case_attachments_company_case_idx": { + "name": "case_attachments_company_case_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "case_attachments_asset_uq": { + "name": "case_attachments_asset_uq", + "columns": [ + { + "expression": "asset_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "case_attachments_company_id_companies_id_fk": { + "name": "case_attachments_company_id_companies_id_fk", + "tableFrom": "case_attachments", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "case_attachments_case_id_cases_id_fk": { + "name": "case_attachments_case_id_cases_id_fk", + "tableFrom": "case_attachments", + "tableTo": "cases", + "columnsFrom": [ + "case_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "case_attachments_asset_id_assets_id_fk": { + "name": "case_attachments_asset_id_assets_id_fk", + "tableFrom": "case_attachments", + "tableTo": "assets", + "columnsFrom": [ + "asset_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.case_documents": { + "name": "case_documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "case_id": { + "name": "case_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "case_documents_company_case_key_uq": { + "name": "case_documents_company_case_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "case_documents_document_uq": { + "name": "case_documents_document_uq", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "case_documents_company_case_updated_idx": { + "name": "case_documents_company_case_updated_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "case_documents_company_id_companies_id_fk": { + "name": "case_documents_company_id_companies_id_fk", + "tableFrom": "case_documents", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "case_documents_case_id_cases_id_fk": { + "name": "case_documents_case_id_cases_id_fk", + "tableFrom": "case_documents", + "tableTo": "cases", + "columnsFrom": [ + "case_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "case_documents_document_id_documents_id_fk": { + "name": "case_documents_document_id_documents_id_fk", + "tableFrom": "case_documents", + "tableTo": "documents", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.case_events": { + "name": "case_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "case_id": { + "name": "case_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_agent_id": { + "name": "actor_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "case_events_case_created_idx": { + "name": "case_events_case_created_idx", + "columns": [ + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "case_events_company_case_idx": { + "name": "case_events_company_case_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "case_events_company_id_companies_id_fk": { + "name": "case_events_company_id_companies_id_fk", + "tableFrom": "case_events", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "case_events_case_id_cases_id_fk": { + "name": "case_events_case_id_cases_id_fk", + "tableFrom": "case_events", + "tableTo": "cases", + "columnsFrom": [ + "case_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "case_events_actor_agent_id_agents_id_fk": { + "name": "case_events_actor_agent_id_agents_id_fk", + "tableFrom": "case_events", + "tableTo": "agents", + "columnsFrom": [ + "actor_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "case_events_kind_check": { + "name": "case_events_kind_check", + "value": "\"case_events\".\"kind\" in (\n 'created',\n 'updated',\n 'fields_changed',\n 'status_changed',\n 'issue_linked',\n 'issue_unlinked',\n 'document_revised',\n 'child_linked',\n 'attachment_added',\n 'label_added',\n 'label_removed'\n )" + }, + "case_events_actor_type_check": { + "name": "case_events_actor_type_check", + "value": "\"case_events\".\"actor_type\" in ('user', 'agent', 'system')" + } + }, + "isRLSEnabled": false + }, + "public.case_issue_links": { + "name": "case_issue_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "case_id": { + "name": "case_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_run_id": { + "name": "created_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "case_issue_links_case_issue_uq": { + "name": "case_issue_links_case_issue_uq", + "columns": [ + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "case_issue_links_company_case_idx": { + "name": "case_issue_links_company_case_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "case_issue_links_issue_idx": { + "name": "case_issue_links_issue_idx", + "columns": [ + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "case_issue_links_company_id_companies_id_fk": { + "name": "case_issue_links_company_id_companies_id_fk", + "tableFrom": "case_issue_links", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "case_issue_links_case_id_cases_id_fk": { + "name": "case_issue_links_case_id_cases_id_fk", + "tableFrom": "case_issue_links", + "tableTo": "cases", + "columnsFrom": [ + "case_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "case_issue_links_issue_id_issues_id_fk": { + "name": "case_issue_links_issue_id_issues_id_fk", + "tableFrom": "case_issue_links", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "case_issue_links_role_check": { + "name": "case_issue_links_role_check", + "value": "\"case_issue_links\".\"role\" in ('origin', 'work', 'reference')" + } + }, + "isRLSEnabled": false + }, + "public.case_labels": { + "name": "case_labels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "case_id": { + "name": "case_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label_id": { + "name": "label_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "case_labels_case_label_uq": { + "name": "case_labels_case_label_uq", + "columns": [ + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "label_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "case_labels_company_case_idx": { + "name": "case_labels_company_case_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "case_labels_label_idx": { + "name": "case_labels_label_idx", + "columns": [ + { + "expression": "label_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "case_labels_company_id_companies_id_fk": { + "name": "case_labels_company_id_companies_id_fk", + "tableFrom": "case_labels", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "case_labels_case_id_cases_id_fk": { + "name": "case_labels_case_id_cases_id_fk", + "tableFrom": "case_labels", + "tableTo": "cases", + "columnsFrom": [ + "case_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "case_labels_label_id_labels_id_fk": { + "name": "case_labels_label_id_labels_id_fk", + "tableFrom": "case_labels", + "tableTo": "labels", + "columnsFrom": [ + "label_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cases": { + "name": "cases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "case_number": { + "name": "case_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "case_type": { + "name": "case_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "fields": { + "name": "fields", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "parent_case_id": { + "name": "parent_case_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "cases_company_case_number_uq": { + "name": "cases_company_case_number_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "case_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cases_identifier_uq": { + "name": "cases_identifier_uq", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cases_company_type_key_uq": { + "name": "cases_company_type_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "case_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cases_company_status_idx": { + "name": "cases_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cases_company_type_idx": { + "name": "cases_company_type_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "case_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cases_company_project_idx": { + "name": "cases_company_project_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cases_parent_idx": { + "name": "cases_parent_idx", + "columns": [ + { + "expression": "parent_case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cases_title_search_idx": { + "name": "cases_title_search_idx", + "columns": [ + { + "expression": "title", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "cases_identifier_search_idx": { + "name": "cases_identifier_search_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "cases_summary_search_idx": { + "name": "cases_summary_search_idx", + "columns": [ + { + "expression": "summary", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "cases_company_id_companies_id_fk": { + "name": "cases_company_id_companies_id_fk", + "tableFrom": "cases", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cases_project_id_projects_id_fk": { + "name": "cases_project_id_projects_id_fk", + "tableFrom": "cases", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "cases_parent_case_id_cases_id_fk": { + "name": "cases_parent_case_id_cases_id_fk", + "tableFrom": "cases", + "tableTo": "cases", + "columnsFrom": [ + "parent_case_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "cases_created_by_agent_id_agents_id_fk": { + "name": "cases_created_by_agent_id_agents_id_fk", + "tableFrom": "cases", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "cases_status_check": { + "name": "cases_status_check", + "value": "\"cases\".\"status\" in ('draft', 'in_progress', 'in_review', 'approved', 'done', 'cancelled')" + } + }, + "isRLSEnabled": false + }, + "public.chat_actions": { + "name": "chat_actions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "endpoint_id": { + "name": "endpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "delivery_id": { + "name": "delivery_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "conversation_id": { + "name": "conversation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "principal_id": { + "name": "principal_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_action_id": { + "name": "provider_action_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'received'" + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_actions_provider_action_uq": { + "name": "chat_actions_provider_action_uq", + "columns": [ + { + "expression": "endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_action_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_actions_company_id_companies_id_fk": { + "name": "chat_actions_company_id_companies_id_fk", + "tableFrom": "chat_actions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_actions_delivery_id_chat_deliveries_id_fk": { + "name": "chat_actions_delivery_id_chat_deliveries_id_fk", + "tableFrom": "chat_actions", + "tableTo": "chat_deliveries", + "columnsFrom": [ + "delivery_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "chat_actions_company_delivery_fk": { + "name": "chat_actions_company_delivery_fk", + "tableFrom": "chat_actions", + "tableTo": "chat_deliveries", + "columnsFrom": [ + "company_id", + "delivery_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_actions_company_conversation_fk": { + "name": "chat_actions_company_conversation_fk", + "tableFrom": "chat_actions", + "tableTo": "chat_conversations", + "columnsFrom": [ + "company_id", + "conversation_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_actions_company_principal_fk": { + "name": "chat_actions_company_principal_fk", + "tableFrom": "chat_actions", + "tableTo": "chat_external_principals", + "columnsFrom": [ + "company_id", + "principal_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_actions_company_endpoint_fk": { + "name": "chat_actions_company_endpoint_fk", + "tableFrom": "chat_actions", + "tableTo": "chat_endpoints", + "columnsFrom": [ + "company_id", + "endpoint_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_agent_routes": { + "name": "chat_agent_routes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_endpoint_id": { + "name": "source_endpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "destination_endpoint_id": { + "name": "destination_endpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "trigger_mode": { + "name": "trigger_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'explicit_mention'" + }, + "max_hops": { + "name": "max_hops", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_agent_routes_pair_uq": { + "name": "chat_agent_routes_pair_uq", + "columns": [ + { + "expression": "source_endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "destination_endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_agent_routes_company_id_companies_id_fk": { + "name": "chat_agent_routes_company_id_companies_id_fk", + "tableFrom": "chat_agent_routes", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_agent_routes_company_source_fk": { + "name": "chat_agent_routes_company_source_fk", + "tableFrom": "chat_agent_routes", + "tableTo": "chat_endpoints", + "columnsFrom": [ + "company_id", + "source_endpoint_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_agent_routes_company_destination_fk": { + "name": "chat_agent_routes_company_destination_fk", + "tableFrom": "chat_agent_routes", + "tableTo": "chat_endpoints", + "columnsFrom": [ + "company_id", + "destination_endpoint_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_agent_routes_hops_check": { + "name": "chat_agent_routes_hops_check", + "value": "\"chat_agent_routes\".\"max_hops\" between 1 and 8" + } + }, + "isRLSEnabled": false + }, + "public.chat_conversations": { + "name": "chat_conversations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "endpoint_id": { + "name": "endpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_conversation_id": { + "name": "external_conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_thread_id": { + "name": "external_thread_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "session_generation": { + "name": "session_generation", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "external_label": { + "name": "external_label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_url": { + "name": "provider_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_direct_message": { + "name": "is_direct_message", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "communication_guidance": { + "name": "communication_guidance", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "last_activity_at": { + "name": "last_activity_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_conversations_issue_idx": { + "name": "chat_conversations_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_conversations_thread_uq": { + "name": "chat_conversations_thread_uq", + "columns": [ + { + "expression": "endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_conversation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "session_generation", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_conversations_company_id_companies_id_fk": { + "name": "chat_conversations_company_id_companies_id_fk", + "tableFrom": "chat_conversations", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_conversations_resource_id_chat_endpoint_resources_id_fk": { + "name": "chat_conversations_resource_id_chat_endpoint_resources_id_fk", + "tableFrom": "chat_conversations", + "tableTo": "chat_endpoint_resources", + "columnsFrom": [ + "resource_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "chat_conversations_issue_id_issues_id_fk": { + "name": "chat_conversations_issue_id_issues_id_fk", + "tableFrom": "chat_conversations", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "chat_conversations_company_issue_fk": { + "name": "chat_conversations_company_issue_fk", + "tableFrom": "chat_conversations", + "tableTo": "issues", + "columnsFrom": [ + "company_id", + "issue_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_conversations_company_endpoint_fk": { + "name": "chat_conversations_company_endpoint_fk", + "tableFrom": "chat_conversations", + "tableTo": "chat_endpoints", + "columnsFrom": [ + "company_id", + "endpoint_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_conversations_company_resource_fk": { + "name": "chat_conversations_company_resource_fk", + "tableFrom": "chat_conversations", + "tableTo": "chat_endpoint_resources", + "columnsFrom": [ + "company_id", + "resource_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_conversations_company_id_uq": { + "name": "chat_conversations_company_id_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "chat_conversations_state_check": { + "name": "chat_conversations_state_check", + "value": "\"chat_conversations\".\"state\" in ('active', 'waiting', 'completed', 'unavailable', 'endpoint_removed')" + } + }, + "isRLSEnabled": false + }, + "public.chat_deliveries": { + "name": "chat_deliveries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "endpoint_id": { + "name": "endpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "principal_id": { + "name": "principal_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider_event_id": { + "name": "provider_event_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deduplication_key": { + "name": "deduplication_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_kind": { + "name": "event_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "normalized_event": { + "name": "normalized_event", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'received'" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "redacted_error": { + "name": "redacted_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "received_at": { + "name": "received_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_deliveries_work_idx": { + "name": "chat_deliveries_work_idx", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_deliveries_event_uq": { + "name": "chat_deliveries_event_uq", + "columns": [ + { + "expression": "endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_deliveries_dedupe_uq": { + "name": "chat_deliveries_dedupe_uq", + "columns": [ + { + "expression": "endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deduplication_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_deliveries_company_id_companies_id_fk": { + "name": "chat_deliveries_company_id_companies_id_fk", + "tableFrom": "chat_deliveries", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_deliveries_conversation_id_chat_conversations_id_fk": { + "name": "chat_deliveries_conversation_id_chat_conversations_id_fk", + "tableFrom": "chat_deliveries", + "tableTo": "chat_conversations", + "columnsFrom": [ + "conversation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "chat_deliveries_principal_id_chat_external_principals_id_fk": { + "name": "chat_deliveries_principal_id_chat_external_principals_id_fk", + "tableFrom": "chat_deliveries", + "tableTo": "chat_external_principals", + "columnsFrom": [ + "principal_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "chat_deliveries_company_endpoint_fk": { + "name": "chat_deliveries_company_endpoint_fk", + "tableFrom": "chat_deliveries", + "tableTo": "chat_endpoints", + "columnsFrom": [ + "company_id", + "endpoint_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_deliveries_company_conversation_fk": { + "name": "chat_deliveries_company_conversation_fk", + "tableFrom": "chat_deliveries", + "tableTo": "chat_conversations", + "columnsFrom": [ + "company_id", + "conversation_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_deliveries_company_principal_fk": { + "name": "chat_deliveries_company_principal_fk", + "tableFrom": "chat_deliveries", + "tableTo": "chat_external_principals", + "columnsFrom": [ + "company_id", + "principal_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_deliveries_company_id_uq": { + "name": "chat_deliveries_company_id_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "chat_deliveries_state_check": { + "name": "chat_deliveries_state_check", + "value": "\"chat_deliveries\".\"state\" in ('received', 'filtered', 'processing', 'processed', 'retry', 'failed')" + } + }, + "isRLSEnabled": false + }, + "public.chat_endpoint_leases": { + "name": "chat_endpoint_leases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "endpoint_id": { + "name": "endpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "lease_key": { + "name": "lease_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_endpoint_leases_active_uq": { + "name": "chat_endpoint_leases_active_uq", + "columns": [ + { + "expression": "endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lease_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_endpoint_leases_expiry_idx": { + "name": "chat_endpoint_leases_expiry_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_endpoint_leases_company_id_companies_id_fk": { + "name": "chat_endpoint_leases_company_id_companies_id_fk", + "tableFrom": "chat_endpoint_leases", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_endpoint_leases_company_endpoint_fk": { + "name": "chat_endpoint_leases_company_endpoint_fk", + "tableFrom": "chat_endpoint_leases", + "tableTo": "chat_endpoints", + "columnsFrom": [ + "company_id", + "endpoint_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_endpoint_resources": { + "name": "chat_endpoint_resources", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "endpoint_id": { + "name": "endpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_resource_id": { + "name": "provider_resource_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_provider_resource_id": { + "name": "parent_provider_resource_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "detail": { + "name": "detail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_url": { + "name": "provider_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "availability": { + "name": "availability", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'available'" + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_endpoint_resources_endpoint_idx": { + "name": "chat_endpoint_resources_endpoint_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_endpoint_resources_external_uq": { + "name": "chat_endpoint_resources_external_uq", + "columns": [ + { + "expression": "endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_endpoint_resources_company_id_companies_id_fk": { + "name": "chat_endpoint_resources_company_id_companies_id_fk", + "tableFrom": "chat_endpoint_resources", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_endpoint_resources_company_endpoint_fk": { + "name": "chat_endpoint_resources_company_endpoint_fk", + "tableFrom": "chat_endpoint_resources", + "tableTo": "chat_endpoints", + "columnsFrom": [ + "company_id", + "endpoint_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_endpoint_resources_company_id_uq": { + "name": "chat_endpoint_resources_company_id_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "chat_endpoint_resources_availability_check": { + "name": "chat_endpoint_resources_availability_check", + "value": "\"chat_endpoint_resources\".\"availability\" in ('available', 'unavailable', 'removed')" + } + }, + "isRLSEnabled": false + }, + "public.chat_endpoints": { + "name": "chat_endpoints", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "publication_mode": { + "name": "publication_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'automatic'" + }, + "external_execution_policy": { + "name": "external_execution_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'restricted'" + }, + "assigned_agent_id": { + "name": "assigned_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sponsor_user_id": { + "name": "sponsor_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "deployment_mode": { + "name": "deployment_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'direct'" + }, + "provider_account_id": { + "name": "provider_account_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_account_label": { + "name": "provider_account_label", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bot_external_id": { + "name": "bot_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bot_username": { + "name": "bot_username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bot_display_name": { + "name": "bot_display_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bot_avatar_url": { + "name": "bot_avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "communication_instructions": { + "name": "communication_instructions", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "allow_direct_messages": { + "name": "allow_direct_messages", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "allow_group_chats": { + "name": "allow_group_chats", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "allow_unlinked_people": { + "name": "allow_unlinked_people", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "concurrency_policy": { + "name": "concurrency_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queue'" + }, + "capabilities": { + "name": "capabilities", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"threads\":false,\"directMessages\":false,\"nativeStreaming\":false,\"messageEdits\":false,\"messageDeletes\":false,\"reactions\":false,\"files\":false,\"cards\":false,\"actions\":false,\"modals\":false,\"slashCommands\":false,\"ephemeralMessages\":false,\"proactiveDirectMessages\":false}'::jsonb" + }, + "setup": { + "name": "setup", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"step\":\"provider_setup\"}'::jsonb" + }, + "health_message": { + "name": "health_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_event_at": { + "name": "last_event_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_publication_at": { + "name": "last_publication_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "activated_at": { + "name": "activated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_endpoints_company_idx": { + "name": "chat_endpoints_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_endpoints_agent_idx": { + "name": "chat_endpoints_agent_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "assigned_agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_endpoints_status_idx": { + "name": "chat_endpoints_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_endpoints_public_id_uq": { + "name": "chat_endpoints_public_id_uq", + "columns": [ + { + "expression": "public_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_endpoints_agentmail_inbox_uq": { + "name": "chat_endpoints_agentmail_inbox_uq", + "columns": [ + { + "expression": "bot_external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"chat_endpoints\".\"provider\" = 'agentmail' and \"chat_endpoints\".\"status\" != 'archived' and \"chat_endpoints\".\"bot_external_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_endpoints_connection_uq": { + "name": "chat_endpoints_connection_uq", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_endpoints_live_bot_external_uq": { + "name": "chat_endpoints_live_bot_external_uq", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "bot_external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"chat_endpoints\".\"status\" in ('verifying', 'active', 'paused', 'attention')\n and \"chat_endpoints\".\"provider_account_id\" is not null\n and \"chat_endpoints\".\"bot_external_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_endpoints_photon_number_uq": { + "name": "chat_endpoints_photon_number_uq", + "columns": [ + { + "expression": "bot_external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"chat_endpoints\".\"provider\" = 'imessage-photon' and \"chat_endpoints\".\"status\" <> 'archived' and \"chat_endpoints\".\"bot_external_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_endpoints_live_discord_bot_external_uq": { + "name": "chat_endpoints_live_discord_bot_external_uq", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "bot_external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"chat_endpoints\".\"provider\" = 'discord'\n and \"chat_endpoints\".\"status\" in ('verifying', 'active', 'paused', 'attention')\n and \"chat_endpoints\".\"bot_external_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_endpoints_live_global_app_bot_external_uq": { + "name": "chat_endpoints_live_global_app_bot_external_uq", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "bot_external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"chat_endpoints\".\"provider\" in ('github', 'microsoft-teams')\n and \"chat_endpoints\".\"status\" in ('verifying', 'active', 'paused', 'attention')\n and \"chat_endpoints\".\"bot_external_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_endpoints_live_bot_username_uq": { + "name": "chat_endpoints_live_bot_username_uq", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "bot_username", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"chat_endpoints\".\"status\" in ('verifying', 'active', 'paused', 'attention')\n and \"chat_endpoints\".\"provider_account_id\" is not null\n and \"chat_endpoints\".\"bot_username\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_endpoints_company_id_companies_id_fk": { + "name": "chat_endpoints_company_id_companies_id_fk", + "tableFrom": "chat_endpoints", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_endpoints_assigned_agent_id_agents_id_fk": { + "name": "chat_endpoints_assigned_agent_id_agents_id_fk", + "tableFrom": "chat_endpoints", + "tableTo": "agents", + "columnsFrom": [ + "assigned_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "chat_endpoints_company_agent_fk": { + "name": "chat_endpoints_company_agent_fk", + "tableFrom": "chat_endpoints", + "tableTo": "agents", + "columnsFrom": [ + "company_id", + "assigned_agent_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_endpoints_company_connection_fk": { + "name": "chat_endpoints_company_connection_fk", + "tableFrom": "chat_endpoints", + "tableTo": "tool_connections", + "columnsFrom": [ + "company_id", + "connection_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_endpoints_company_id_uq": { + "name": "chat_endpoints_company_id_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "chat_endpoints_publication_mode_check": { + "name": "chat_endpoints_publication_mode_check", + "value": "\"chat_endpoints\".\"publication_mode\" in ('automatic', 'explicit')" + }, + "chat_endpoints_execution_policy_check": { + "name": "chat_endpoints_execution_policy_check", + "value": "\"chat_endpoints\".\"external_execution_policy\" in ('restricted', 'agent')" + }, + "chat_endpoints_email_policy_check": { + "name": "chat_endpoints_email_policy_check", + "value": "\"chat_endpoints\".\"provider\" <> 'agentmail' or (\"chat_endpoints\".\"publication_mode\" = 'explicit' and \"chat_endpoints\".\"external_execution_policy\" = 'agent')" + }, + "chat_endpoints_provider_check": { + "name": "chat_endpoints_provider_check", + "value": "\"chat_endpoints\".\"provider\" in ('slack', 'github', 'discord', 'microsoft-teams', 'telegram', 'agentmail', 'imessage-photon')" + }, + "chat_endpoints_status_check": { + "name": "chat_endpoints_status_check", + "value": "\"chat_endpoints\".\"status\" in ('draft', 'verifying', 'active', 'paused', 'attention', 'revoked', 'archived')" + }, + "chat_endpoints_deployment_check": { + "name": "chat_endpoints_deployment_check", + "value": "\"chat_endpoints\".\"deployment_mode\" in ('direct', 'relay')" + }, + "chat_endpoints_concurrency_check": { + "name": "chat_endpoints_concurrency_check", + "value": "\"chat_endpoints\".\"concurrency_policy\" in ('burst', 'queue', 'debounce', 'drop', 'concurrent')" + } + }, + "isRLSEnabled": false + }, + "public.chat_external_principals": { + "name": "chat_external_principals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_account_id": { + "name": "provider_account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "handle": { + "name": "handle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_bot": { + "name": "is_bot", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_external_principals_company_idx": { + "name": "chat_external_principals_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_external_principals_external_uq": { + "name": "chat_external_principals_external_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_external_principals_company_id_companies_id_fk": { + "name": "chat_external_principals_company_id_companies_id_fk", + "tableFrom": "chat_external_principals", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_external_principals_company_id_uq": { + "name": "chat_external_principals_company_id_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "chat_external_principals_provider_check": { + "name": "chat_external_principals_provider_check", + "value": "\"chat_external_principals\".\"provider\" in ('slack', 'github', 'discord', 'microsoft-teams', 'telegram', 'agentmail', 'imessage-photon')" + }, + "chat_external_principals_kind_check": { + "name": "chat_external_principals_kind_check", + "value": "\"chat_external_principals\".\"kind\" in ('user', 'bot', 'app', 'system')" + } + }, + "isRLSEnabled": false + }, + "public.chat_identity_links": { + "name": "chat_identity_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "endpoint_id": { + "name": "endpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "principal_id": { + "name": "principal_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "paperclip_user_id": { + "name": "paperclip_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "confirmation_token_hash": { + "name": "confirmation_token_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_identity_links_user_idx": { + "name": "chat_identity_links_user_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "paperclip_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_identity_links_endpoint_principal_uq": { + "name": "chat_identity_links_endpoint_principal_uq", + "columns": [ + { + "expression": "endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "principal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_identity_links_company_id_companies_id_fk": { + "name": "chat_identity_links_company_id_companies_id_fk", + "tableFrom": "chat_identity_links", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_identity_links_company_endpoint_fk": { + "name": "chat_identity_links_company_endpoint_fk", + "tableFrom": "chat_identity_links", + "tableTo": "chat_endpoints", + "columnsFrom": [ + "company_id", + "endpoint_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_identity_links_company_principal_fk": { + "name": "chat_identity_links_company_principal_fk", + "tableFrom": "chat_identity_links", + "tableTo": "chat_external_principals", + "columnsFrom": [ + "company_id", + "principal_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_identity_links_status_check": { + "name": "chat_identity_links_status_check", + "value": "\"chat_identity_links\".\"status\" in ('pending', 'linked', 'revoked', 'expired')" + } + }, + "isRLSEnabled": false + }, + "public.chat_message_links": { + "name": "chat_message_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "endpoint_id": { + "name": "endpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "delivery_id": { + "name": "delivery_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "publication_id": { + "name": "publication_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "comment_id": { + "name": "comment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider_message_id": { + "name": "provider_message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "direction": { + "name": "direction", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_message_links_provider_message_uq": { + "name": "chat_message_links_provider_message_uq", + "columns": [ + { + "expression": "endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "conversation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_message_links_company_id_companies_id_fk": { + "name": "chat_message_links_company_id_companies_id_fk", + "tableFrom": "chat_message_links", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_message_links_delivery_id_chat_deliveries_id_fk": { + "name": "chat_message_links_delivery_id_chat_deliveries_id_fk", + "tableFrom": "chat_message_links", + "tableTo": "chat_deliveries", + "columnsFrom": [ + "delivery_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "chat_message_links_publication_id_chat_publications_id_fk": { + "name": "chat_message_links_publication_id_chat_publications_id_fk", + "tableFrom": "chat_message_links", + "tableTo": "chat_publications", + "columnsFrom": [ + "publication_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "chat_message_links_comment_id_issue_comments_id_fk": { + "name": "chat_message_links_comment_id_issue_comments_id_fk", + "tableFrom": "chat_message_links", + "tableTo": "issue_comments", + "columnsFrom": [ + "comment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "chat_message_links_company_endpoint_fk": { + "name": "chat_message_links_company_endpoint_fk", + "tableFrom": "chat_message_links", + "tableTo": "chat_endpoints", + "columnsFrom": [ + "company_id", + "endpoint_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_message_links_company_delivery_fk": { + "name": "chat_message_links_company_delivery_fk", + "tableFrom": "chat_message_links", + "tableTo": "chat_deliveries", + "columnsFrom": [ + "company_id", + "delivery_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_message_links_company_publication_fk": { + "name": "chat_message_links_company_publication_fk", + "tableFrom": "chat_message_links", + "tableTo": "chat_publications", + "columnsFrom": [ + "company_id", + "publication_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_message_links_company_comment_fk": { + "name": "chat_message_links_company_comment_fk", + "tableFrom": "chat_message_links", + "tableTo": "issue_comments", + "columnsFrom": [ + "company_id", + "comment_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_message_links_company_conversation_fk": { + "name": "chat_message_links_company_conversation_fk", + "tableFrom": "chat_message_links", + "tableTo": "chat_conversations", + "columnsFrom": [ + "company_id", + "conversation_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_message_links_direction_check": { + "name": "chat_message_links_direction_check", + "value": "\"chat_message_links\".\"direction\" in ('inbound', 'outbound')" + } + }, + "isRLSEnabled": false + }, + "public.chat_publications": { + "name": "chat_publications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "endpoint_id": { + "name": "endpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "comment_id": { + "name": "comment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "provider_message_id": { + "name": "provider_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_url": { + "name": "provider_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "redacted_error": { + "name": "redacted_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "published_at": { + "name": "published_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_publications_company_id_uq": { + "name": "chat_publications_company_id_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_publications_work_idx": { + "name": "chat_publications_work_idx", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_publications_idempotency_uq": { + "name": "chat_publications_idempotency_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_publications_company_id_companies_id_fk": { + "name": "chat_publications_company_id_companies_id_fk", + "tableFrom": "chat_publications", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_publications_issue_id_issues_id_fk": { + "name": "chat_publications_issue_id_issues_id_fk", + "tableFrom": "chat_publications", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "chat_publications_comment_id_issue_comments_id_fk": { + "name": "chat_publications_comment_id_issue_comments_id_fk", + "tableFrom": "chat_publications", + "tableTo": "issue_comments", + "columnsFrom": [ + "comment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "chat_publications_company_issue_fk": { + "name": "chat_publications_company_issue_fk", + "tableFrom": "chat_publications", + "tableTo": "issues", + "columnsFrom": [ + "company_id", + "issue_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_publications_company_comment_fk": { + "name": "chat_publications_company_comment_fk", + "tableFrom": "chat_publications", + "tableTo": "issue_comments", + "columnsFrom": [ + "company_id", + "comment_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_publications_company_endpoint_fk": { + "name": "chat_publications_company_endpoint_fk", + "tableFrom": "chat_publications", + "tableTo": "chat_endpoints", + "columnsFrom": [ + "company_id", + "endpoint_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_publications_company_conversation_fk": { + "name": "chat_publications_company_conversation_fk", + "tableFrom": "chat_publications", + "tableTo": "chat_conversations", + "columnsFrom": [ + "company_id", + "conversation_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_publications_state_check": { + "name": "chat_publications_state_check", + "value": "\"chat_publications\".\"state\" in ('pending', 'streaming', 'published', 'retry', 'delivery_unknown', 'failed', 'cancelled', 'awaiting_consent')" + } + }, + "isRLSEnabled": false + }, + "public.chat_sdk_state": { + "name": "chat_sdk_state", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "endpoint_id": { + "name": "endpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "state_key": { + "name": "state_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "value": { + "name": "value", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_sdk_state_key_uq": { + "name": "chat_sdk_state_key_uq", + "columns": [ + { + "expression": "endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "state_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_sdk_state_expiry_idx": { + "name": "chat_sdk_state_expiry_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_sdk_state_company_id_companies_id_fk": { + "name": "chat_sdk_state_company_id_companies_id_fk", + "tableFrom": "chat_sdk_state", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_sdk_state_company_endpoint_fk": { + "name": "chat_sdk_state_company_endpoint_fk", + "tableFrom": "chat_sdk_state", + "tableTo": "chat_endpoints", + "columnsFrom": [ + "company_id", + "endpoint_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_completion_deliveries": { + "name": "chat_completion_deliveries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "task_id": { + "name": "task_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status_version": { + "name": "status_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "target_run_id": { + "name": "target_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "response_comment_id": { + "name": "response_comment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_completion_deliveries_transition_uq": { + "name": "chat_completion_deliveries_transition_uq", + "columns": [ + { + "expression": "task_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status_version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_completion_deliveries_pending_idx": { + "name": "chat_completion_deliveries_pending_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_completion_deliveries_company_id_companies_id_fk": { + "name": "chat_completion_deliveries_company_id_companies_id_fk", + "tableFrom": "chat_completion_deliveries", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_completion_deliveries_task_id_chat_task_handoffs_task_id_fk": { + "name": "chat_completion_deliveries_task_id_chat_task_handoffs_task_id_fk", + "tableFrom": "chat_completion_deliveries", + "tableTo": "chat_task_handoffs", + "columnsFrom": [ + "task_id" + ], + "columnsTo": [ + "task_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_completion_deliveries_target_run_id_heartbeat_runs_id_fk": { + "name": "chat_completion_deliveries_target_run_id_heartbeat_runs_id_fk", + "tableFrom": "chat_completion_deliveries", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "target_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "chat_completion_deliveries_response_comment_id_issue_comments_id_fk": { + "name": "chat_completion_deliveries_response_comment_id_issue_comments_id_fk", + "tableFrom": "chat_completion_deliveries", + "tableTo": "issue_comments", + "columnsFrom": [ + "response_comment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_task_handoffs": { + "name": "chat_task_handoffs", + "schema": "", + "columns": { + "task_id": { + "name": "task_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "session_generation": { + "name": "session_generation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_task_handoffs_source_idx": { + "name": "chat_task_handoffs_source_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "conversation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_task_handoffs_task_id_issues_id_fk": { + "name": "chat_task_handoffs_task_id_issues_id_fk", + "tableFrom": "chat_task_handoffs", + "tableTo": "issues", + "columnsFrom": [ + "task_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_task_handoffs_company_id_companies_id_fk": { + "name": "chat_task_handoffs_company_id_companies_id_fk", + "tableFrom": "chat_task_handoffs", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_task_handoffs_conversation_id_issues_id_fk": { + "name": "chat_task_handoffs_conversation_id_issues_id_fk", + "tableFrom": "chat_task_handoffs", + "tableTo": "issues", + "columnsFrom": [ + "conversation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_task_handoffs_agent_id_agents_id_fk": { + "name": "chat_task_handoffs_agent_id_agents_id_fk", + "tableFrom": "chat_task_handoffs", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_discord_command_owners": { + "name": "chat_discord_command_owners", + "schema": "", + "columns": { + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "endpoint_id": { + "name": "endpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "action_id": { + "name": "action_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_discord_command_owners_application_check": { + "name": "chat_discord_command_owners_application_check", + "value": "\"chat_discord_command_owners\".\"application_id\" ~ '^[1-9][0-9]{16,19}$'" + } + }, + "isRLSEnabled": false + }, + "public.chat_github_configurations": { + "name": "chat_github_configurations", + "schema": "", + "columns": { + "endpoint_id": { + "name": "endpoint_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "configuration": { + "name": "configuration", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "updated_by_user_id": { + "name": "updated_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chat_github_configurations_company_id_endpoint_id_chat_endpoints_company_id_id_fk": { + "name": "chat_github_configurations_company_id_endpoint_id_chat_endpoints_company_id_id_fk", + "tableFrom": "chat_github_configurations", + "tableTo": "chat_endpoints", + "columnsFrom": [ + "company_id", + "endpoint_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_github_config_revision_check": { + "name": "chat_github_config_revision_check", + "value": "\"chat_github_configurations\".\"revision\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.chat_github_registrations": { + "name": "chat_github_registrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "endpoint_id": { + "name": "endpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state_hash": { + "name": "state_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trusted_origin": { + "name": "trusted_origin", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "consumed_at": { + "name": "consumed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_github_registration_state_uq": { + "name": "chat_github_registration_state_uq", + "columns": [ + { + "expression": "state_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_github_registration_endpoint_idx": { + "name": "chat_github_registration_endpoint_idx", + "columns": [ + { + "expression": "endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_github_registrations_company_id_endpoint_id_chat_endpoints_company_id_id_fk": { + "name": "chat_github_registrations_company_id_endpoint_id_chat_endpoints_company_id_id_fk", + "tableFrom": "chat_github_registrations", + "tableTo": "chat_endpoints", + "columnsFrom": [ + "company_id", + "endpoint_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_github_registration_status_check": { + "name": "chat_github_registration_status_check", + "value": "\"chat_github_registrations\".\"status\" in ('pending', 'exchanging', 'completed', 'failed')" + } + }, + "isRLSEnabled": false + }, + "public.chat_github_reviews": { + "name": "chat_github_reviews", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "endpoint_id": { + "name": "endpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pull_number": { + "name": "pull_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "head_sha": { + "name": "head_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "delivery_id": { + "name": "delivery_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "configuration_revision": { + "name": "configuration_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "policy_snapshot": { + "name": "policy_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "event": { + "name": "event", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "assessment": { + "name": "assessment", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "conclusion": { + "name": "conclusion", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "check_id": { + "name": "check_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "check_url": { + "name": "check_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "summary_id": { + "name": "summary_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "summary_url": { + "name": "summary_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "publication_receipts": { + "name": "publication_receipts", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_github_reviews_delivery_uq": { + "name": "chat_github_reviews_delivery_uq", + "columns": [ + { + "expression": "endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "delivery_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_github_reviews_pull_idx": { + "name": "chat_github_reviews_pull_idx", + "columns": [ + { + "expression": "endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pull_number", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_github_reviews_task_idx": { + "name": "chat_github_reviews_task_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_github_reviews_run_id_heartbeat_runs_id_fk": { + "name": "chat_github_reviews_run_id_heartbeat_runs_id_fk", + "tableFrom": "chat_github_reviews", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "chat_github_reviews_company_id_endpoint_id_chat_endpoints_company_id_id_fk": { + "name": "chat_github_reviews_company_id_endpoint_id_chat_endpoints_company_id_id_fk", + "tableFrom": "chat_github_reviews", + "tableTo": "chat_endpoints", + "columnsFrom": [ + "company_id", + "endpoint_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_github_reviews_company_id_issue_id_issues_company_id_id_fk": { + "name": "chat_github_reviews_company_id_issue_id_issues_company_id_id_fk", + "tableFrom": "chat_github_reviews", + "tableTo": "issues", + "columnsFrom": [ + "company_id", + "issue_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_github_reviews_company_id_uq": { + "name": "chat_github_reviews_company_id_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "chat_github_review_pull_number_check": { + "name": "chat_github_review_pull_number_check", + "value": "\"chat_github_reviews\".\"pull_number\" > 0" + }, + "chat_github_review_state_check": { + "name": "chat_github_review_state_check", + "value": "\"chat_github_reviews\".\"state\" in ('queued', 'running', 'completed', 'incomplete', 'error', 'superseded', 'manual_required')" + } + }, + "isRLSEnabled": false + }, + "public.chat_teams_file_transfers": { + "name": "chat_teams_file_transfers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "endpoint_id": { + "name": "endpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "publication_id": { + "name": "publication_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "comment_id": { + "name": "comment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "attachment_id": { + "name": "attachment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "principal_id": { + "name": "principal_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "authorized_user_id": { + "name": "authorized_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "runtime_generation": { + "name": "runtime_generation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "credential_fingerprint": { + "name": "credential_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "conversation_generation": { + "name": "conversation_generation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "source_digest": { + "name": "source_digest", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authority_digest": { + "name": "authority_digest", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenant_id": { + "name": "tenant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "bot_app_id": { + "name": "bot_app_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "aad_object_id": { + "name": "aad_object_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider_conversation_id": { + "name": "provider_conversation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_user_id": { + "name": "provider_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "byte_size": { + "name": "byte_size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "filename": { + "name": "filename", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_sha256": { + "name": "token_sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "phase": { + "name": "phase", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'consent_pending'" + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "attempt_id": { + "name": "attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "attempt_expires_at": { + "name": "attempt_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "consent_message_id": { + "name": "consent_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_info_message_id": { + "name": "file_info_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "response_activity_id": { + "name": "response_activity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "response_digest": { + "name": "response_digest", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "private_state": { + "name": "private_state", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chat_teams_file_transfers_publication_uq": { + "name": "chat_teams_file_transfers_publication_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "publication_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_teams_file_transfers_token_uq": { + "name": "chat_teams_file_transfers_token_uq", + "columns": [ + { + "expression": "endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "token_sha256", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_teams_file_transfers_work_idx": { + "name": "chat_teams_file_transfers_work_idx", + "columns": [ + { + "expression": "phase", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "attempt_expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_teams_file_transfers_company_id_companies_id_fk": { + "name": "chat_teams_file_transfers_company_id_companies_id_fk", + "tableFrom": "chat_teams_file_transfers", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_teams_file_transfers_issue_id_issues_id_fk": { + "name": "chat_teams_file_transfers_issue_id_issues_id_fk", + "tableFrom": "chat_teams_file_transfers", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "chat_teams_file_transfers_company_id_endpoint_id_chat_endpoints_company_id_id_fk": { + "name": "chat_teams_file_transfers_company_id_endpoint_id_chat_endpoints_company_id_id_fk", + "tableFrom": "chat_teams_file_transfers", + "tableTo": "chat_endpoints", + "columnsFrom": [ + "company_id", + "endpoint_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_teams_file_transfers_company_id_publication_id_chat_publications_company_id_id_fk": { + "name": "chat_teams_file_transfers_company_id_publication_id_chat_publications_company_id_id_fk", + "tableFrom": "chat_teams_file_transfers", + "tableTo": "chat_publications", + "columnsFrom": [ + "company_id", + "publication_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_teams_file_transfers_company_id_conversation_id_chat_conversations_company_id_id_fk": { + "name": "chat_teams_file_transfers_company_id_conversation_id_chat_conversations_company_id_id_fk", + "tableFrom": "chat_teams_file_transfers", + "tableTo": "chat_conversations", + "columnsFrom": [ + "company_id", + "conversation_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_teams_file_transfers_company_id_principal_id_chat_external_principals_company_id_id_fk": { + "name": "chat_teams_file_transfers_company_id_principal_id_chat_external_principals_company_id_id_fk", + "tableFrom": "chat_teams_file_transfers", + "tableTo": "chat_external_principals", + "columnsFrom": [ + "company_id", + "principal_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chat_teams_file_transfers_phase_check": { + "name": "chat_teams_file_transfers_phase_check", + "value": "\"chat_teams_file_transfers\".\"phase\" in ('consent_pending','consent_sending','consent_unknown','awaiting_consent','upload_pending','uploading','upload_unknown','file_info_pending','file_info_sending','file_info_unknown','delivered','declined','expired','cancelled','conflict')" + }, + "chat_teams_file_transfers_bounds_check": { + "name": "chat_teams_file_transfers_bounds_check", + "value": "\"chat_teams_file_transfers\".\"version\" > 0 and \"chat_teams_file_transfers\".\"runtime_generation\" >= 0 and \"chat_teams_file_transfers\".\"conversation_generation\" > 0 and \"chat_teams_file_transfers\".\"byte_size\" > 0 and \"chat_teams_file_transfers\".\"byte_size\" < 62914560" + }, + "chat_teams_file_transfers_hash_check": { + "name": "chat_teams_file_transfers_hash_check", + "value": "\"chat_teams_file_transfers\".\"source_digest\" ~ '^[a-f0-9]{64}$' and \"chat_teams_file_transfers\".\"authority_digest\" ~ '^[a-f0-9]{64}$' and \"chat_teams_file_transfers\".\"sha256\" ~ '^[a-f0-9]{64}$' and \"chat_teams_file_transfers\".\"token_sha256\" ~ '^[a-f0-9]{64}$'" + }, + "chat_teams_file_transfers_attempt_check": { + "name": "chat_teams_file_transfers_attempt_check", + "value": "(\"chat_teams_file_transfers\".\"attempt_id\" is null) = (\"chat_teams_file_transfers\".\"attempt_expires_at\" is null)" + } + }, + "isRLSEnabled": false + }, + "public.cli_auth_challenges": { + "name": "cli_auth_challenges", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "secret_hash": { + "name": "secret_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_name": { + "name": "client_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "requested_access": { + "name": "requested_access", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'board'" + }, + "requested_company_id": { + "name": "requested_company_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "pending_key_hash": { + "name": "pending_key_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pending_key_name": { + "name": "pending_key_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "approved_by_user_id": { + "name": "approved_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "board_api_key_id": { + "name": "board_api_key_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "cli_auth_challenges_secret_hash_idx": { + "name": "cli_auth_challenges_secret_hash_idx", + "columns": [ + { + "expression": "secret_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cli_auth_challenges_approved_by_idx": { + "name": "cli_auth_challenges_approved_by_idx", + "columns": [ + { + "expression": "approved_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cli_auth_challenges_requested_company_idx": { + "name": "cli_auth_challenges_requested_company_idx", + "columns": [ + { + "expression": "requested_company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cli_auth_challenges_requested_company_id_companies_id_fk": { + "name": "cli_auth_challenges_requested_company_id_companies_id_fk", + "tableFrom": "cli_auth_challenges", + "tableTo": "companies", + "columnsFrom": [ + "requested_company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "cli_auth_challenges_approved_by_user_id_user_id_fk": { + "name": "cli_auth_challenges_approved_by_user_id_user_id_fk", + "tableFrom": "cli_auth_challenges", + "tableTo": "user", + "columnsFrom": [ + "approved_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "cli_auth_challenges_board_api_key_id_board_api_keys_id_fk": { + "name": "cli_auth_challenges_board_api_key_id_board_api_keys_id_fk", + "tableFrom": "cli_auth_challenges", + "tableTo": "board_api_keys", + "columnsFrom": [ + "board_api_key_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.companies": { + "name": "companies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "pause_reason": { + "name": "pause_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "paused_at": { + "name": "paused_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "issue_prefix": { + "name": "issue_prefix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'PAP'" + }, + "issue_counter": { + "name": "issue_counter", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "budget_monthly_cents": { + "name": "budget_monthly_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "spend_month_utc": { + "name": "spend_month_utc", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "spent_monthly_cents": { + "name": "spent_monthly_cents", + "type": "numeric(24, 7)", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "default_responsible_user_id": { + "name": "default_responsible_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "require_board_approval_for_new_agents": { + "name": "require_board_approval_for_new_agents", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "interaction_resolver_governance": { + "name": "interaction_resolver_governance", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "feedback_data_sharing_enabled": { + "name": "feedback_data_sharing_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "feedback_data_sharing_consent_at": { + "name": "feedback_data_sharing_consent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "feedback_data_sharing_consent_by_user_id": { + "name": "feedback_data_sharing_consent_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feedback_data_sharing_terms_version": { + "name": "feedback_data_sharing_terms_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "companies_issue_prefix_idx": { + "name": "companies_issue_prefix_idx", + "columns": [ + { + "expression": "issue_prefix", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.company_logos": { + "name": "company_logos", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "asset_id": { + "name": "asset_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "company_logos_company_uq": { + "name": "company_logos_company_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_logos_asset_uq": { + "name": "company_logos_asset_uq", + "columns": [ + { + "expression": "asset_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_logos_company_id_companies_id_fk": { + "name": "company_logos_company_id_companies_id_fk", + "tableFrom": "company_logos", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_logos_asset_id_assets_id_fk": { + "name": "company_logos_asset_id_assets_id_fk", + "tableFrom": "company_logos", + "tableTo": "assets", + "columnsFrom": [ + "asset_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.company_memberships": { + "name": "company_memberships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "principal_type": { + "name": "principal_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "principal_id": { + "name": "principal_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "membership_role": { + "name": "membership_role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "company_memberships_company_principal_unique_idx": { + "name": "company_memberships_company_principal_unique_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "principal_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "principal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_memberships_principal_status_idx": { + "name": "company_memberships_principal_status_idx", + "columns": [ + { + "expression": "principal_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "principal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_memberships_company_status_idx": { + "name": "company_memberships_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_memberships_company_id_companies_id_fk": { + "name": "company_memberships_company_id_companies_id_fk", + "tableFrom": "company_memberships", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.company_onboarding_seeds": { + "name": "company_onboarding_seeds", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mission": { + "name": "mission", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent_name": { + "name": "agent_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent_role": { + "name": "agent_role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "first_task_title": { + "name": "first_task_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "first_task_details": { + "name": "first_task_details", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "goal_id": { + "name": "goal_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "company_onboarding_seeds_company_uq": { + "name": "company_onboarding_seeds_company_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_onboarding_seeds_company_id_companies_id_fk": { + "name": "company_onboarding_seeds_company_id_companies_id_fk", + "tableFrom": "company_onboarding_seeds", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_onboarding_seeds_goal_id_goals_id_fk": { + "name": "company_onboarding_seeds_goal_id_goals_id_fk", + "tableFrom": "company_onboarding_seeds", + "tableTo": "goals", + "columnsFrom": [ + "goal_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "company_onboarding_seeds_agent_id_agents_id_fk": { + "name": "company_onboarding_seeds_agent_id_agents_id_fk", + "tableFrom": "company_onboarding_seeds", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "company_onboarding_seeds_issue_id_issues_id_fk": { + "name": "company_onboarding_seeds_issue_id_issues_id_fk", + "tableFrom": "company_onboarding_seeds", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.company_secret_bindings": { + "name": "company_secret_bindings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "secret_id": { + "name": "secret_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config_path": { + "name": "config_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version_selector": { + "name": "version_selector", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'latest'" + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projection_class": { + "name": "projection_class", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unclassified'" + }, + "projection_allowlist_key": { + "name": "projection_allowlist_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "company_secret_bindings_company_idx": { + "name": "company_secret_bindings_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secret_bindings_secret_idx": { + "name": "company_secret_bindings_secret_idx", + "columns": [ + { + "expression": "secret_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secret_bindings_target_idx": { + "name": "company_secret_bindings_target_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secret_bindings_target_path_uq": { + "name": "company_secret_bindings_target_path_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "config_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_secret_bindings_company_id_companies_id_fk": { + "name": "company_secret_bindings_company_id_companies_id_fk", + "tableFrom": "company_secret_bindings", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "company_secret_bindings_secret_id_company_secrets_id_fk": { + "name": "company_secret_bindings_secret_id_company_secrets_id_fk", + "tableFrom": "company_secret_bindings", + "tableTo": "company_secrets", + "columnsFrom": [ + "secret_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.company_secret_proposals": { + "name": "company_secret_proposals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "proposed_name": { + "name": "proposed_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "proposed_key": { + "name": "proposed_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "proposed_description": { + "name": "proposed_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "justification": { + "name": "justification", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value_ciphertext": { + "name": "value_ciphertext", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "value_fingerprint_sha256": { + "name": "value_fingerprint_sha256", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "value_length": { + "name": "value_length", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "secret_id": { + "name": "secret_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "secret_proposal_id": { + "name": "secret_proposal_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "config_path": { + "name": "config_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projection_class": { + "name": "projection_class", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unclassified'" + }, + "binding_target_policy_snapshot": { + "name": "binding_target_policy_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "proposer_ancestor_ids_snapshot": { + "name": "proposer_ancestor_ids_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "target_ancestor_ids_snapshot": { + "name": "target_ancestor_ids_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "proposed_by_agent_id": { + "name": "proposed_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "origin_issue_id": { + "name": "origin_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "origin_run_id": { + "name": "origin_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "interaction_id": { + "name": "interaction_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "resolved_by_user_id": { + "name": "resolved_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "resolution_reason": { + "name": "resolution_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_secret_id": { + "name": "created_secret_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "applied_binding_config_path": { + "name": "applied_binding_config_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ciphertext_scrubbed_at": { + "name": "ciphertext_scrubbed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "company_secret_proposals_company_status_idx": { + "name": "company_secret_proposals_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secret_proposals_proposer_status_idx": { + "name": "company_secret_proposals_proposer_status_idx", + "columns": [ + { + "expression": "proposed_by_agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secret_proposals_expiry_idx": { + "name": "company_secret_proposals_expiry_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secret_proposals_secret_proposal_idx": { + "name": "company_secret_proposals_secret_proposal_idx", + "columns": [ + { + "expression": "secret_proposal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secret_proposals_interaction_idx": { + "name": "company_secret_proposals_interaction_idx", + "columns": [ + { + "expression": "interaction_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_secret_proposals_company_id_companies_id_fk": { + "name": "company_secret_proposals_company_id_companies_id_fk", + "tableFrom": "company_secret_proposals", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "company_secret_proposals_secret_id_company_secrets_id_fk": { + "name": "company_secret_proposals_secret_id_company_secrets_id_fk", + "tableFrom": "company_secret_proposals", + "tableTo": "company_secrets", + "columnsFrom": [ + "secret_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "company_secret_proposals_secret_proposal_id_company_secret_proposals_id_fk": { + "name": "company_secret_proposals_secret_proposal_id_company_secret_proposals_id_fk", + "tableFrom": "company_secret_proposals", + "tableTo": "company_secret_proposals", + "columnsFrom": [ + "secret_proposal_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_secret_proposals_target_id_agents_id_fk": { + "name": "company_secret_proposals_target_id_agents_id_fk", + "tableFrom": "company_secret_proposals", + "tableTo": "agents", + "columnsFrom": [ + "target_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_secret_proposals_proposed_by_agent_id_agents_id_fk": { + "name": "company_secret_proposals_proposed_by_agent_id_agents_id_fk", + "tableFrom": "company_secret_proposals", + "tableTo": "agents", + "columnsFrom": [ + "proposed_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_secret_proposals_origin_issue_id_issues_id_fk": { + "name": "company_secret_proposals_origin_issue_id_issues_id_fk", + "tableFrom": "company_secret_proposals", + "tableTo": "issues", + "columnsFrom": [ + "origin_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "company_secret_proposals_origin_run_id_heartbeat_runs_id_fk": { + "name": "company_secret_proposals_origin_run_id_heartbeat_runs_id_fk", + "tableFrom": "company_secret_proposals", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "origin_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_secret_proposals_interaction_id_issue_thread_interactions_id_fk": { + "name": "company_secret_proposals_interaction_id_issue_thread_interactions_id_fk", + "tableFrom": "company_secret_proposals", + "tableTo": "issue_thread_interactions", + "columnsFrom": [ + "interaction_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "company_secret_proposals_created_secret_id_company_secrets_id_fk": { + "name": "company_secret_proposals_created_secret_id_company_secrets_id_fk", + "tableFrom": "company_secret_proposals", + "tableTo": "company_secrets", + "columnsFrom": [ + "created_secret_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "company_secret_proposals_kind_check": { + "name": "company_secret_proposals_kind_check", + "value": "\"company_secret_proposals\".\"kind\" in ('secret', 'binding')" + }, + "company_secret_proposals_status_check": { + "name": "company_secret_proposals_status_check", + "value": "\"company_secret_proposals\".\"status\" in ('pending', 'approved', 'rejected', 'withdrawn', 'expired')" + }, + "company_secret_proposals_projection_check": { + "name": "company_secret_proposals_projection_check", + "value": "\"company_secret_proposals\".\"projection_class\" = 'unclassified'" + }, + "company_secret_proposals_shape_check": { + "name": "company_secret_proposals_shape_check", + "value": "(\n \"company_secret_proposals\".\"kind\" = 'secret'\n and \"company_secret_proposals\".\"proposed_name\" is not null\n and \"company_secret_proposals\".\"proposed_key\" is not null\n and \"company_secret_proposals\".\"secret_id\" is null\n and \"company_secret_proposals\".\"secret_proposal_id\" is null\n and \"company_secret_proposals\".\"target_type\" is null\n and \"company_secret_proposals\".\"target_id\" is null\n and \"company_secret_proposals\".\"config_path\" is null\n ) or (\n \"company_secret_proposals\".\"kind\" = 'binding'\n and ((\"company_secret_proposals\".\"secret_id\" is not null)::int + (\"company_secret_proposals\".\"secret_proposal_id\" is not null)::int) = 1\n and \"company_secret_proposals\".\"target_type\" = 'agent'\n and \"company_secret_proposals\".\"target_id\" is not null\n and \"company_secret_proposals\".\"config_path\" is not null\n )" + } + }, + "isRLSEnabled": false + }, + "public.company_secret_provider_configs": { + "name": "company_secret_provider_configs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'ready'" + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "health_status": { + "name": "health_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "health_checked_at": { + "name": "health_checked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "health_message": { + "name": "health_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "health_details": { + "name": "health_details", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "disabled_at": { + "name": "disabled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "company_secret_provider_configs_company_idx": { + "name": "company_secret_provider_configs_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secret_provider_configs_company_provider_idx": { + "name": "company_secret_provider_configs_company_provider_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secret_provider_configs_default_uq": { + "name": "company_secret_provider_configs_default_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"company_secret_provider_configs\".\"is_default\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_secret_provider_configs_company_id_companies_id_fk": { + "name": "company_secret_provider_configs_company_id_companies_id_fk", + "tableFrom": "company_secret_provider_configs", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_secret_provider_configs_created_by_agent_id_agents_id_fk": { + "name": "company_secret_provider_configs_created_by_agent_id_agents_id_fk", + "tableFrom": "company_secret_provider_configs", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.company_secret_versions": { + "name": "company_secret_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "secret_id": { + "name": "secret_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "material": { + "name": "material", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "value_sha256": { + "name": "value_sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_version_ref": { + "name": "provider_version_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'current'" + }, + "fingerprint_sha256": { + "name": "fingerprint_sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rotation_job_id": { + "name": "rotation_job_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "company_secret_versions_secret_idx": { + "name": "company_secret_versions_secret_idx", + "columns": [ + { + "expression": "secret_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secret_versions_value_sha256_idx": { + "name": "company_secret_versions_value_sha256_idx", + "columns": [ + { + "expression": "value_sha256", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secret_versions_fingerprint_idx": { + "name": "company_secret_versions_fingerprint_idx", + "columns": [ + { + "expression": "fingerprint_sha256", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secret_versions_secret_version_uq": { + "name": "company_secret_versions_secret_version_uq", + "columns": [ + { + "expression": "secret_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_secret_versions_secret_id_company_secrets_id_fk": { + "name": "company_secret_versions_secret_id_company_secrets_id_fk", + "tableFrom": "company_secret_versions", + "tableTo": "company_secrets", + "columnsFrom": [ + "secret_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_secret_versions_created_by_agent_id_agents_id_fk": { + "name": "company_secret_versions_created_by_agent_id_agents_id_fk", + "tableFrom": "company_secret_versions", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.company_secrets": { + "name": "company_secrets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'company'" + }, + "owner_user_id": { + "name": "owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_secret_definition_id": { + "name": "user_secret_definition_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'local_encrypted'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "managed_mode": { + "name": "managed_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'paperclip_managed'" + }, + "external_ref": { + "name": "external_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_config_id": { + "name": "provider_config_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider_metadata": { + "name": "provider_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "latest_version": { + "name": "latest_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_resolved_at": { + "name": "last_resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_rotated_at": { + "name": "last_rotated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "company_secrets_company_idx": { + "name": "company_secrets_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secrets_company_scope_idx": { + "name": "company_secrets_company_scope_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secrets_company_owner_idx": { + "name": "company_secrets_company_owner_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secrets_user_definition_owner_idx": { + "name": "company_secrets_user_definition_owner_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_secret_definition_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secrets_company_provider_idx": { + "name": "company_secrets_company_provider_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secrets_provider_config_idx": { + "name": "company_secrets_provider_config_idx", + "columns": [ + { + "expression": "provider_config_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secrets_company_name_uq": { + "name": "company_secrets_company_name_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"company_secrets\".\"scope\" = 'company' and \"company_secrets\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secrets_company_key_uq": { + "name": "company_secrets_company_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"company_secrets\".\"scope\" = 'company' and \"company_secrets\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_secrets_user_definition_owner_uq": { + "name": "company_secrets_user_definition_owner_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_secret_definition_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"company_secrets\".\"scope\" = 'user' and \"company_secrets\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_secrets_company_id_companies_id_fk": { + "name": "company_secrets_company_id_companies_id_fk", + "tableFrom": "company_secrets", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "company_secrets_user_secret_definition_id_user_secret_definitions_id_fk": { + "name": "company_secrets_user_secret_definition_id_user_secret_definitions_id_fk", + "tableFrom": "company_secrets", + "tableTo": "user_secret_definitions", + "columnsFrom": [ + "user_secret_definition_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "company_secrets_provider_config_id_company_secret_provider_configs_id_fk": { + "name": "company_secrets_provider_config_id_company_secret_provider_configs_id_fk", + "tableFrom": "company_secrets", + "tableTo": "company_secret_provider_configs", + "columnsFrom": [ + "provider_config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "company_secrets_created_by_agent_id_agents_id_fk": { + "name": "company_secrets_created_by_agent_id_agents_id_fk", + "tableFrom": "company_secrets", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "company_secrets_scope_shape_check": { + "name": "company_secrets_scope_shape_check", + "value": "(\n \"company_secrets\".\"scope\" = 'company'\n and \"company_secrets\".\"owner_user_id\" is null\n and \"company_secrets\".\"user_secret_definition_id\" is null\n ) or (\n \"company_secrets\".\"scope\" = 'user'\n and \"company_secrets\".\"owner_user_id\" is not null\n and \"company_secrets\".\"user_secret_definition_id\" is not null\n )" + } + }, + "isRLSEnabled": false + }, + "public.company_skill_policies": { + "name": "company_skill_policies", + "schema": "", + "columns": { + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "schema_version": { + "name": "schema_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "default_effect": { + "name": "default_effect", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rules": { + "name": "rules", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "company_skill_policies_company_id_companies_id_fk": { + "name": "company_skill_policies_company_id_companies_id_fk", + "tableFrom": "company_skill_policies", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.company_skill_source_entries": { + "name": "company_skill_source_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_id": { + "name": "source_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "inspection": { + "name": "inspection", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "skill_id": { + "name": "skill_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "selection": { + "name": "selection", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'new'" + }, + "present": { + "name": "present", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "company_skill_source_entries_source_path_idx": { + "name": "company_skill_source_entries_source_path_idx", + "columns": [ + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_skill_source_entries_company_skill_idx": { + "name": "company_skill_source_entries_company_skill_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "skill_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_skill_source_entries_company_id_companies_id_fk": { + "name": "company_skill_source_entries_company_id_companies_id_fk", + "tableFrom": "company_skill_source_entries", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_skill_source_entries_source_id_company_skill_sources_id_fk": { + "name": "company_skill_source_entries_source_id_company_skill_sources_id_fk", + "tableFrom": "company_skill_source_entries", + "tableTo": "company_skill_sources", + "columnsFrom": [ + "source_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_skill_source_entries_skill_id_company_skills_id_fk": { + "name": "company_skill_source_entries_skill_id_company_skills_id_fk", + "tableFrom": "company_skill_source_entries", + "tableTo": "company_skills", + "columnsFrom": [ + "skill_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.company_skill_sources": { + "name": "company_skill_sources", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repository_url": { + "name": "repository_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tracking_ref": { + "name": "tracking_ref", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "excluded_folders": { + "name": "excluded_folders", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_attempt_at": { + "name": "last_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_success_at": { + "name": "last_success_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_scan_commit": { + "name": "last_scan_commit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_token": { + "name": "lease_token", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "company_skill_sources_repository_ref_idx": { + "name": "company_skill_sources_repository_ref_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repository_url", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tracking_ref", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_skill_sources_identity_ref_idx": { + "name": "company_skill_sources_identity_ref_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tracking_ref", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_skill_sources_company_id_companies_id_fk": { + "name": "company_skill_sources_company_id_companies_id_fk", + "tableFrom": "company_skill_sources", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_skill_sources_connection_id_tool_connections_id_fk": { + "name": "company_skill_sources_connection_id_tool_connections_id_fk", + "tableFrom": "company_skill_sources", + "tableTo": "tool_connections", + "columnsFrom": [ + "connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.company_skill_comments": { + "name": "company_skill_comments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "company_skill_id": { + "name": "company_skill_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "parent_comment_id": { + "name": "parent_comment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "author_agent_id": { + "name": "author_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "author_user_id": { + "name": "author_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "company_skill_comments_company_skill_created_idx": { + "name": "company_skill_comments_company_skill_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "company_skill_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_skill_comments_parent_idx": { + "name": "company_skill_comments_parent_idx", + "columns": [ + { + "expression": "parent_comment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_skill_comments_company_id_companies_id_fk": { + "name": "company_skill_comments_company_id_companies_id_fk", + "tableFrom": "company_skill_comments", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_skill_comments_company_skill_id_company_skills_id_fk": { + "name": "company_skill_comments_company_skill_id_company_skills_id_fk", + "tableFrom": "company_skill_comments", + "tableTo": "company_skills", + "columnsFrom": [ + "company_skill_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_skill_comments_parent_comment_id_company_skill_comments_id_fk": { + "name": "company_skill_comments_parent_comment_id_company_skill_comments_id_fk", + "tableFrom": "company_skill_comments", + "tableTo": "company_skill_comments", + "columnsFrom": [ + "parent_comment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "company_skill_comments_author_agent_id_agents_id_fk": { + "name": "company_skill_comments_author_agent_id_agents_id_fk", + "tableFrom": "company_skill_comments", + "tableTo": "agents", + "columnsFrom": [ + "author_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.company_skill_stars": { + "name": "company_skill_stars", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "company_skill_id": { + "name": "company_skill_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "company_skill_stars_skill_agent_idx": { + "name": "company_skill_stars_skill_agent_idx", + "columns": [ + { + "expression": "company_skill_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_skill_stars_skill_user_idx": { + "name": "company_skill_stars_skill_user_idx", + "columns": [ + { + "expression": "company_skill_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_skill_stars_company_skill_created_idx": { + "name": "company_skill_stars_company_skill_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "company_skill_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_skill_stars_company_id_companies_id_fk": { + "name": "company_skill_stars_company_id_companies_id_fk", + "tableFrom": "company_skill_stars", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_skill_stars_company_skill_id_company_skills_id_fk": { + "name": "company_skill_stars_company_skill_id_company_skills_id_fk", + "tableFrom": "company_skill_stars", + "tableTo": "company_skills", + "columnsFrom": [ + "company_skill_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_skill_stars_agent_id_agents_id_fk": { + "name": "company_skill_stars_agent_id_agents_id_fk", + "tableFrom": "company_skill_stars", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.company_skill_test_inputs": { + "name": "company_skill_test_inputs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "skill_id": { + "name": "skill_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "company_skill_test_inputs_company_skill_name_idx": { + "name": "company_skill_test_inputs_company_skill_name_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "skill_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_skill_test_inputs_company_skill_active_idx": { + "name": "company_skill_test_inputs_company_skill_active_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "skill_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_skill_test_inputs_company_id_companies_id_fk": { + "name": "company_skill_test_inputs_company_id_companies_id_fk", + "tableFrom": "company_skill_test_inputs", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_skill_test_inputs_skill_id_company_skills_id_fk": { + "name": "company_skill_test_inputs_skill_id_company_skills_id_fk", + "tableFrom": "company_skill_test_inputs", + "tableTo": "company_skills", + "columnsFrom": [ + "skill_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.company_skill_test_run_templates": { + "name": "company_skill_test_run_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_by_agent_id": { + "name": "updated_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "updated_by_user_id": { + "name": "updated_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "company_skill_test_run_templates_company_active_idx": { + "name": "company_skill_test_run_templates_company_active_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_skill_test_run_templates_company_id_companies_id_fk": { + "name": "company_skill_test_run_templates_company_id_companies_id_fk", + "tableFrom": "company_skill_test_run_templates", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_skill_test_run_templates_created_by_agent_id_agents_id_fk": { + "name": "company_skill_test_run_templates_created_by_agent_id_agents_id_fk", + "tableFrom": "company_skill_test_run_templates", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "company_skill_test_run_templates_updated_by_agent_id_agents_id_fk": { + "name": "company_skill_test_run_templates_updated_by_agent_id_agents_id_fk", + "tableFrom": "company_skill_test_run_templates", + "tableTo": "agents", + "columnsFrom": [ + "updated_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.company_skill_test_runs": { + "name": "company_skill_test_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "skill_id": { + "name": "skill_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "input_id": { + "name": "input_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "input_snapshot": { + "name": "input_snapshot", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "skill_version_id": { + "name": "skill_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_config_snapshot": { + "name": "agent_config_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "template_name": { + "name": "template_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "template_body": { + "name": "template_body", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rendered_template_body": { + "name": "rendered_template_body", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "harness_issue_description": { + "name": "harness_issue_description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "output_document_key": { + "name": "output_document_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'output'" + }, + "output_snapshot": { + "name": "output_snapshot", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "superseded_at": { + "name": "superseded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "harness_issue_expires_at": { + "name": "harness_issue_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "harness_issue_deleted_at": { + "name": "harness_issue_deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "company_skill_test_runs_company_skill_created_idx": { + "name": "company_skill_test_runs_company_skill_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "skill_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_skill_test_runs_company_issue_idx": { + "name": "company_skill_test_runs_company_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_skill_test_runs_company_input_created_idx": { + "name": "company_skill_test_runs_company_input_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "input_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_skill_test_runs_company_status_idx": { + "name": "company_skill_test_runs_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_skill_test_runs_company_harness_expires_idx": { + "name": "company_skill_test_runs_company_harness_expires_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "harness_issue_expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_skill_test_runs_company_id_companies_id_fk": { + "name": "company_skill_test_runs_company_id_companies_id_fk", + "tableFrom": "company_skill_test_runs", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_skill_test_runs_skill_id_company_skills_id_fk": { + "name": "company_skill_test_runs_skill_id_company_skills_id_fk", + "tableFrom": "company_skill_test_runs", + "tableTo": "company_skills", + "columnsFrom": [ + "skill_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_skill_test_runs_input_id_company_skill_test_inputs_id_fk": { + "name": "company_skill_test_runs_input_id_company_skill_test_inputs_id_fk", + "tableFrom": "company_skill_test_runs", + "tableTo": "company_skill_test_inputs", + "columnsFrom": [ + "input_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "company_skill_test_runs_skill_version_id_company_skill_versions_id_fk": { + "name": "company_skill_test_runs_skill_version_id_company_skill_versions_id_fk", + "tableFrom": "company_skill_test_runs", + "tableTo": "company_skill_versions", + "columnsFrom": [ + "skill_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "company_skill_test_runs_agent_id_agents_id_fk": { + "name": "company_skill_test_runs_agent_id_agents_id_fk", + "tableFrom": "company_skill_test_runs", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "company_skill_test_runs_issue_id_issues_id_fk": { + "name": "company_skill_test_runs_issue_id_issues_id_fk", + "tableFrom": "company_skill_test_runs", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.company_skill_versions": { + "name": "company_skill_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "company_skill_id": { + "name": "company_skill_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision_number": { + "name": "revision_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "release_id": { + "name": "release_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "release_name": { + "name": "release_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "released_at": { + "name": "released_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "file_inventory": { + "name": "file_inventory", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "author_agent_id": { + "name": "author_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "author_user_id": { + "name": "author_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "company_skill_versions_skill_revision_idx": { + "name": "company_skill_versions_skill_revision_idx", + "columns": [ + { + "expression": "company_skill_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "revision_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_skill_versions_skill_release_idx": { + "name": "company_skill_versions_skill_release_idx", + "columns": [ + { + "expression": "company_skill_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "release_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"company_skill_versions\".\"release_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_skill_versions_company_skill_created_idx": { + "name": "company_skill_versions_company_skill_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "company_skill_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_skill_versions_company_id_companies_id_fk": { + "name": "company_skill_versions_company_id_companies_id_fk", + "tableFrom": "company_skill_versions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_skill_versions_company_skill_id_company_skills_id_fk": { + "name": "company_skill_versions_company_skill_id_company_skills_id_fk", + "tableFrom": "company_skill_versions", + "tableTo": "company_skills", + "columnsFrom": [ + "company_skill_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_skill_versions_author_agent_id_agents_id_fk": { + "name": "company_skill_versions_author_agent_id_agents_id_fk", + "tableFrom": "company_skill_versions", + "tableTo": "agents", + "columnsFrom": [ + "author_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.company_skills": { + "name": "company_skills", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "folder_id": { + "name": "folder_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "markdown": { + "name": "markdown", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_type": { + "name": "source_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'local_path'" + }, + "source_locator": { + "name": "source_locator", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_ref": { + "name": "source_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "trust_level": { + "name": "trust_level", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'markdown_only'" + }, + "compatibility": { + "name": "compatibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'compatible'" + }, + "file_inventory": { + "name": "file_inventory", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "icon_url": { + "name": "icon_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tagline": { + "name": "tagline", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "author_name": { + "name": "author_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "homepage_url": { + "name": "homepage_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "categories": { + "name": "categories", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "sharing_scope": { + "name": "sharing_scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'company'" + }, + "public_share_token": { + "name": "public_share_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "forked_from_skill_id": { + "name": "forked_from_skill_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "forked_from_company_id": { + "name": "forked_from_company_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "star_count": { + "name": "star_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "install_count": { + "name": "install_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "fork_count": { + "name": "fork_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "current_version_id": { + "name": "current_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "company_skills_company_key_idx": { + "name": "company_skills_company_key_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_skills_company_name_idx": { + "name": "company_skills_company_name_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_skills_company_folder_idx": { + "name": "company_skills_company_folder_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "folder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_skills_company_categories_idx": { + "name": "company_skills_company_categories_idx", + "columns": [ + { + "expression": "categories", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "company_skills_company_sharing_scope_idx": { + "name": "company_skills_company_sharing_scope_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sharing_scope", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_skills_company_current_version_idx": { + "name": "company_skills_company_current_version_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "current_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_skills_company_forked_from_idx": { + "name": "company_skills_company_forked_from_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "forked_from_skill_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_skills_company_id_companies_id_fk": { + "name": "company_skills_company_id_companies_id_fk", + "tableFrom": "company_skills", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "company_skills_folder_id_folders_id_fk": { + "name": "company_skills_folder_id_folders_id_fk", + "tableFrom": "company_skills", + "tableTo": "folders", + "columnsFrom": [ + "folder_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "company_skills_forked_from_skill_id_company_skills_id_fk": { + "name": "company_skills_forked_from_skill_id_company_skills_id_fk", + "tableFrom": "company_skills", + "tableTo": "company_skills", + "columnsFrom": [ + "forked_from_skill_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "company_skills_forked_from_company_id_companies_id_fk": { + "name": "company_skills_forked_from_company_id_companies_id_fk", + "tableFrom": "company_skills", + "tableTo": "companies", + "columnsFrom": [ + "forked_from_company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "company_skills_current_version_id_company_skill_versions_id_fk": { + "name": "company_skills_current_version_id_company_skill_versions_id_fk", + "tableFrom": "company_skills", + "tableTo": "company_skill_versions", + "columnsFrom": [ + "current_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.company_transfer_runs": { + "name": "company_transfer_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "direction": { + "name": "direction", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "actor_key": { + "name": "actor_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "container_ref": { + "name": "container_ref", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "manifest_sha256": { + "name": "manifest_sha256", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "manifest": { + "name": "manifest", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "chunk_count": { + "name": "chunk_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "blob_count": { + "name": "blob_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "completed_parts": { + "name": "completed_parts", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "company_transfer_runs_company_idx": { + "name": "company_transfer_runs_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_transfer_runs_idempotency_direction_idx": { + "name": "company_transfer_runs_idempotency_direction_idx", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "direction", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_transfer_runs_actor_status_idx": { + "name": "company_transfer_runs_actor_status_idx", + "columns": [ + { + "expression": "actor_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_transfer_runs_company_id_companies_id_fk": { + "name": "company_transfer_runs_company_id_companies_id_fk", + "tableFrom": "company_transfer_runs", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.company_user_sidebar_preferences": { + "name": "company_user_sidebar_preferences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "project_order": { + "name": "project_order", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "company_user_sidebar_preferences_company_idx": { + "name": "company_user_sidebar_preferences_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_user_sidebar_preferences_user_idx": { + "name": "company_user_sidebar_preferences_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_user_sidebar_preferences_company_user_uq": { + "name": "company_user_sidebar_preferences_company_user_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_user_sidebar_preferences_company_id_companies_id_fk": { + "name": "company_user_sidebar_preferences_company_id_companies_id_fk", + "tableFrom": "company_user_sidebar_preferences", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.completion_contracts": { + "name": "completion_contracts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "schema_version": { + "name": "schema_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "policy_version": { + "name": "policy_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "risk": { + "name": "risk", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "completion_authority": { + "name": "completion_authority", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "incomplete_criteria_policy": { + "name": "incomplete_criteria_policy", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "contract_json": { + "name": "contract_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "canonical_sha256": { + "name": "canonical_sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_actor_type": { + "name": "created_by_actor_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "supersedes_contract_id": { + "name": "supersedes_contract_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "completion_contracts_issue_revision_uq": { + "name": "completion_contracts_issue_revision_uq", + "columns": [ + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "revision", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "completion_contracts_issue_hash_uq": { + "name": "completion_contracts_issue_hash_uq", + "columns": [ + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "canonical_sha256", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "completion_contracts_company_id_companies_id_fk": { + "name": "completion_contracts_company_id_companies_id_fk", + "tableFrom": "completion_contracts", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "completion_contracts_issue_company_fk": { + "name": "completion_contracts_issue_company_fk", + "tableFrom": "completion_contracts", + "tableTo": "issues", + "columnsFrom": [ + "company_id", + "issue_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "completion_contracts_supersedes_owner_fk": { + "name": "completion_contracts_supersedes_owner_fk", + "tableFrom": "completion_contracts", + "tableTo": "completion_contracts", + "columnsFrom": [ + "company_id", + "issue_id", + "supersedes_contract_id" + ], + "columnsTo": [ + "company_id", + "issue_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "completion_contracts_company_issue_id_uq": { + "name": "completion_contracts_company_issue_id_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "issue_id", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.connection_event_deliveries": { + "name": "connection_event_deliveries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_delivery_id": { + "name": "provider_delivery_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event": { + "name": "event", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "installation_id": { + "name": "installation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "normalized_payload": { + "name": "normalized_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'received'" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_created_at": { + "name": "provider_created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "connection_event_deliveries_company_provider_id_uq": { + "name": "connection_event_deliveries_company_provider_id_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_delivery_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "connection_event_deliveries_company_status_idx": { + "name": "connection_event_deliveries_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "connection_event_deliveries_company_id_companies_id_fk": { + "name": "connection_event_deliveries_company_id_companies_id_fk", + "tableFrom": "connection_event_deliveries", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.connection_intent_deliveries": { + "name": "connection_intent_deliveries", + "schema": "", + "columns": { + "interaction_id": { + "name": "interaction_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "delivered_at": { + "name": "delivered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "connection_intent_deliveries_pending_idx": { + "name": "connection_intent_deliveries_pending_idx", + "columns": [ + { + "expression": "delivered_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "connection_intent_deliveries_interaction_id_issue_thread_interactions_id_fk": { + "name": "connection_intent_deliveries_interaction_id_issue_thread_interactions_id_fk", + "tableFrom": "connection_intent_deliveries", + "tableTo": "issue_thread_interactions", + "columnsFrom": [ + "interaction_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "connection_intent_deliveries_company_id_companies_id_fk": { + "name": "connection_intent_deliveries_company_id_companies_id_fk", + "tableFrom": "connection_intent_deliveries", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cost_events": { + "name": "cost_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "goal_id": { + "name": "goal_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "heartbeat_run_id": { + "name": "heartbeat_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "billing_code": { + "name": "billing_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "receipt_hash": { + "name": "receipt_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_request_id": { + "name": "provider_request_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reported_cost_cents": { + "name": "reported_cost_cents", + "type": "numeric(24, 7)", + "primaryKey": false, + "notNull": false + }, + "pricing_provenance": { + "name": "pricing_provenance", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "biller": { + "name": "biller", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "billing_type": { + "name": "billing_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "cost_status": { + "name": "cost_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'reported'" + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "input_tokens": { + "name": "input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "cached_input_tokens": { + "name": "cached_input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "output_tokens": { + "name": "output_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "cost_cents": { + "name": "cost_cents", + "type": "numeric(24, 7)", + "primaryKey": false, + "notNull": true + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "cost_events_company_receipt_idx": { + "name": "cost_events_company_receipt_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cost_events_provider_request_idx": { + "name": "cost_events_provider_request_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "biller", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cost_events_company_project_occurred_idx": { + "name": "cost_events_company_project_occurred_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cost_events_unpriced_idx": { + "name": "cost_events_unpriced_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"cost_events\".\"cost_status\" = 'unpriced' and \"cost_events\".\"billing_type\" <> 'subscription_included'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "cost_events_company_occurred_idx": { + "name": "cost_events_company_occurred_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cost_events_company_agent_occurred_idx": { + "name": "cost_events_company_agent_occurred_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cost_events_company_provider_occurred_idx": { + "name": "cost_events_company_provider_occurred_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cost_events_company_biller_occurred_idx": { + "name": "cost_events_company_biller_occurred_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "biller", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cost_events_company_heartbeat_run_idx": { + "name": "cost_events_company_heartbeat_run_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "heartbeat_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cost_events_company_id_companies_id_fk": { + "name": "cost_events_company_id_companies_id_fk", + "tableFrom": "cost_events", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "cost_events_agent_id_agents_id_fk": { + "name": "cost_events_agent_id_agents_id_fk", + "tableFrom": "cost_events", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "cost_events_issue_id_issues_id_fk": { + "name": "cost_events_issue_id_issues_id_fk", + "tableFrom": "cost_events", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "cost_events_project_id_projects_id_fk": { + "name": "cost_events_project_id_projects_id_fk", + "tableFrom": "cost_events", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "cost_events_goal_id_goals_id_fk": { + "name": "cost_events_goal_id_goals_id_fk", + "tableFrom": "cost_events", + "tableTo": "goals", + "columnsFrom": [ + "goal_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "cost_events_heartbeat_run_id_heartbeat_runs_id_fk": { + "name": "cost_events_heartbeat_run_id_heartbeat_runs_id_fk", + "tableFrom": "cost_events", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "heartbeat_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "cost_events_nonnegative_amounts": { + "name": "cost_events_nonnegative_amounts", + "value": "\"cost_events\".\"cost_cents\" >= 0 and \"cost_events\".\"input_tokens\" >= 0 and \"cost_events\".\"cached_input_tokens\" >= 0 and \"cost_events\".\"output_tokens\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.decision_archive_notification_outbox": { + "name": "decision_archive_notification_outbox", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_kind": { + "name": "source_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_id": { + "name": "source_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "archive_version": { + "name": "archive_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "origin_agent_id": { + "name": "origin_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "origin_issue_id": { + "name": "origin_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_attempt_at": { + "name": "last_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "delivered_at": { + "name": "delivered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "decision_archive_notification_outbox_uq": { + "name": "decision_archive_notification_outbox_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "archive_version", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "decision_archive_notification_outbox_pending_idx": { + "name": "decision_archive_notification_outbox_pending_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "decision_archive_notification_outbox_company_id_companies_id_fk": { + "name": "decision_archive_notification_outbox_company_id_companies_id_fk", + "tableFrom": "decision_archive_notification_outbox", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "decision_archive_notification_outbox_origin_agent_id_agents_id_fk": { + "name": "decision_archive_notification_outbox_origin_agent_id_agents_id_fk", + "tableFrom": "decision_archive_notification_outbox", + "tableTo": "agents", + "columnsFrom": [ + "origin_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "decision_archive_notification_outbox_status_check": { + "name": "decision_archive_notification_outbox_status_check", + "value": "\"decision_archive_notification_outbox\".\"status\" IN ('pending', 'delivering', 'delivered')" + } + }, + "isRLSEnabled": false + }, + "public.decision_queue_items": { + "name": "decision_queue_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "queue_id": { + "name": "queue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_kind": { + "name": "source_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_id": { + "name": "source_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "added_by_type": { + "name": "added_by_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "added_by_agent_id": { + "name": "added_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "added_by_user_id": { + "name": "added_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "added_by_run_id": { + "name": "added_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "added_by_agent_api_key_id": { + "name": "added_by_agent_api_key_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "responsible_user_id": { + "name": "responsible_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "decision_queue_items_queue_source_uq": { + "name": "decision_queue_items_queue_source_uq", + "columns": [ + { + "expression": "queue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "decision_queue_items_company_source_idx": { + "name": "decision_queue_items_company_source_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "decision_queue_items_company_id_companies_id_fk": { + "name": "decision_queue_items_company_id_companies_id_fk", + "tableFrom": "decision_queue_items", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "decision_queue_items_added_by_agent_id_agents_id_fk": { + "name": "decision_queue_items_added_by_agent_id_agents_id_fk", + "tableFrom": "decision_queue_items", + "tableTo": "agents", + "columnsFrom": [ + "added_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decision_queue_items_added_by_run_id_heartbeat_runs_id_fk": { + "name": "decision_queue_items_added_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "decision_queue_items", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "added_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decision_queue_items_added_by_agent_api_key_id_agent_api_keys_id_fk": { + "name": "decision_queue_items_added_by_agent_api_key_id_agent_api_keys_id_fk", + "tableFrom": "decision_queue_items", + "tableTo": "agent_api_keys", + "columnsFrom": [ + "added_by_agent_api_key_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decision_queue_items_queue_company_fk": { + "name": "decision_queue_items_queue_company_fk", + "tableFrom": "decision_queue_items", + "tableTo": "decision_queues", + "columnsFrom": [ + "queue_id", + "company_id" + ], + "columnsTo": [ + "id", + "company_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "decision_queue_items_actor_check": { + "name": "decision_queue_items_actor_check", + "value": "(\n (\"decision_queue_items\".\"added_by_type\" = 'agent' AND \"decision_queue_items\".\"added_by_agent_id\" IS NOT NULL AND \"decision_queue_items\".\"added_by_user_id\" IS NULL)\n OR (\"decision_queue_items\".\"added_by_type\" = 'user' AND \"decision_queue_items\".\"added_by_agent_id\" IS NULL AND \"decision_queue_items\".\"added_by_user_id\" IS NOT NULL)\n OR (\"decision_queue_items\".\"added_by_type\" = 'system' AND \"decision_queue_items\".\"added_by_agent_id\" IS NULL AND \"decision_queue_items\".\"added_by_user_id\" IS NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.decision_queues": { + "name": "decision_queues", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_type": { + "name": "created_by_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_run_id": { + "name": "created_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_api_key_id": { + "name": "created_by_agent_api_key_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "retention_days": { + "name": "retention_days", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "seed_rules": { + "name": "seed_rules", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "seed_rules_enabled": { + "name": "seed_rules_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "decision_queues_company_key_uq": { + "name": "decision_queues_company_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "decision_queues_company_updated_idx": { + "name": "decision_queues_company_updated_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "decision_queues_company_id_companies_id_fk": { + "name": "decision_queues_company_id_companies_id_fk", + "tableFrom": "decision_queues", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "decision_queues_created_by_agent_id_agents_id_fk": { + "name": "decision_queues_created_by_agent_id_agents_id_fk", + "tableFrom": "decision_queues", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decision_queues_created_by_run_id_heartbeat_runs_id_fk": { + "name": "decision_queues_created_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "decision_queues", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "created_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decision_queues_created_by_agent_api_key_id_agent_api_keys_id_fk": { + "name": "decision_queues_created_by_agent_api_key_id_agent_api_keys_id_fk", + "tableFrom": "decision_queues", + "tableTo": "agent_api_keys", + "columnsFrom": [ + "created_by_agent_api_key_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "decision_queues_id_company_uq": { + "name": "decision_queues_id_company_uq", + "nullsNotDistinct": false, + "columns": [ + "id", + "company_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "decision_queues_creator_check": { + "name": "decision_queues_creator_check", + "value": "(\n (\"decision_queues\".\"created_by_type\" = 'agent' AND \"decision_queues\".\"created_by_agent_id\" IS NOT NULL AND \"decision_queues\".\"created_by_user_id\" IS NULL)\n OR (\"decision_queues\".\"created_by_type\" = 'user' AND \"decision_queues\".\"created_by_agent_id\" IS NULL AND \"decision_queues\".\"created_by_user_id\" IS NOT NULL)\n OR (\"decision_queues\".\"created_by_type\" = 'system' AND \"decision_queues\".\"created_by_agent_id\" IS NULL AND \"decision_queues\".\"created_by_user_id\" IS NULL)\n )" + }, + "decision_queues_retention_days_check": { + "name": "decision_queues_retention_days_check", + "value": "\"decision_queues\".\"retention_days\" IS NULL OR (\"decision_queues\".\"retention_days\" >= 1 AND \"decision_queues\".\"retention_days\" <= 3650)" + } + }, + "isRLSEnabled": false + }, + "public.decision_retention": { + "name": "decision_retention", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_kind": { + "name": "source_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_id": { + "name": "source_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_activity_at": { + "name": "source_activity_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "keep": { + "name": "keep", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_reason": { + "name": "archived_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived_by_type": { + "name": "archived_by_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived_by_agent_id": { + "name": "archived_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "archived_by_user_id": { + "name": "archived_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived_by_run_id": { + "name": "archived_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "archive_version": { + "name": "archive_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "decision_retention_company_source_uq": { + "name": "decision_retention_company_source_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "decision_retention_company_archived_idx": { + "name": "decision_retention_company_archived_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "decision_retention_company_id_companies_id_fk": { + "name": "decision_retention_company_id_companies_id_fk", + "tableFrom": "decision_retention", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "decision_retention_archived_by_agent_id_agents_id_fk": { + "name": "decision_retention_archived_by_agent_id_agents_id_fk", + "tableFrom": "decision_retention", + "tableTo": "agents", + "columnsFrom": [ + "archived_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decision_retention_archived_by_run_id_heartbeat_runs_id_fk": { + "name": "decision_retention_archived_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "decision_retention", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "archived_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "decision_retention_archive_actor_check": { + "name": "decision_retention_archive_actor_check", + "value": "(\n (\"decision_retention\".\"archived_at\" IS NULL AND \"decision_retention\".\"archived_by_type\" IS NULL AND \"decision_retention\".\"archived_by_agent_id\" IS NULL AND \"decision_retention\".\"archived_by_user_id\" IS NULL)\n OR (\"decision_retention\".\"archived_at\" IS NOT NULL AND \"decision_retention\".\"archived_by_type\" = 'system' AND \"decision_retention\".\"archived_by_agent_id\" IS NULL AND \"decision_retention\".\"archived_by_user_id\" IS NULL)\n OR (\"decision_retention\".\"archived_at\" IS NOT NULL AND \"decision_retention\".\"archived_by_type\" = 'agent' AND \"decision_retention\".\"archived_by_agent_id\" IS NOT NULL AND \"decision_retention\".\"archived_by_user_id\" IS NULL)\n OR (\"decision_retention\".\"archived_at\" IS NOT NULL AND \"decision_retention\".\"archived_by_type\" = 'user' AND \"decision_retention\".\"archived_by_agent_id\" IS NULL AND \"decision_retention\".\"archived_by_user_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.decision_triage": { + "name": "decision_triage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_kind": { + "name": "source_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_id": { + "name": "source_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decide_by": { + "name": "decide_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "decide_by_date": { + "name": "decide_by_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "snoozed_until": { + "name": "snoozed_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "set_by_type": { + "name": "set_by_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "set_by_agent_id": { + "name": "set_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "set_by_user_id": { + "name": "set_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "set_by_run_id": { + "name": "set_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "set_by_agent_api_key_id": { + "name": "set_by_agent_api_key_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "responsible_user_id": { + "name": "responsible_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "decision_triage_company_source_uq": { + "name": "decision_triage_company_source_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "decision_triage_company_decide_by_idx": { + "name": "decision_triage_company_decide_by_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "decide_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "decision_triage_company_id_companies_id_fk": { + "name": "decision_triage_company_id_companies_id_fk", + "tableFrom": "decision_triage", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "decision_triage_set_by_agent_id_agents_id_fk": { + "name": "decision_triage_set_by_agent_id_agents_id_fk", + "tableFrom": "decision_triage", + "tableTo": "agents", + "columnsFrom": [ + "set_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decision_triage_set_by_run_id_heartbeat_runs_id_fk": { + "name": "decision_triage_set_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "decision_triage", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "set_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decision_triage_set_by_agent_api_key_id_agent_api_keys_id_fk": { + "name": "decision_triage_set_by_agent_api_key_id_agent_api_keys_id_fk", + "tableFrom": "decision_triage", + "tableTo": "agent_api_keys", + "columnsFrom": [ + "set_by_agent_api_key_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "decision_triage_actor_check": { + "name": "decision_triage_actor_check", + "value": "(\n (\"decision_triage\".\"set_by_type\" = 'agent' AND \"decision_triage\".\"set_by_agent_id\" IS NOT NULL AND \"decision_triage\".\"set_by_user_id\" IS NULL)\n OR (\"decision_triage\".\"set_by_type\" = 'user' AND \"decision_triage\".\"set_by_agent_id\" IS NULL AND \"decision_triage\".\"set_by_user_id\" IS NOT NULL)\n )" + }, + "decision_triage_decide_by_check": { + "name": "decision_triage_decide_by_check", + "value": "(\n (\"decision_triage\".\"decide_by\" IS NULL AND \"decision_triage\".\"decide_by_date\" IS NULL)\n OR (\"decision_triage\".\"decide_by\" IN ('today', 'this_week', 'whenever') AND \"decision_triage\".\"decide_by_date\" IS NULL)\n OR (\"decision_triage\".\"decide_by\" = 'date' AND \"decision_triage\".\"decide_by_date\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.decision_triage_events": { + "name": "decision_triage_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "queue_id": { + "name": "queue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source_kind": { + "name": "source_kind", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_id": { + "name": "source_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_agent_id": { + "name": "actor_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_run_id": { + "name": "actor_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "agent_api_key_id": { + "name": "agent_api_key_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "responsible_user_id": { + "name": "responsible_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "details": { + "name": "details", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "decision_triage_events_company_source_created_idx": { + "name": "decision_triage_events_company_source_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "decision_triage_events_queue_created_idx": { + "name": "decision_triage_events_queue_created_idx", + "columns": [ + { + "expression": "queue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "decision_triage_events_company_id_companies_id_fk": { + "name": "decision_triage_events_company_id_companies_id_fk", + "tableFrom": "decision_triage_events", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "decision_triage_events_queue_id_decision_queues_id_fk": { + "name": "decision_triage_events_queue_id_decision_queues_id_fk", + "tableFrom": "decision_triage_events", + "tableTo": "decision_queues", + "columnsFrom": [ + "queue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decision_triage_events_actor_agent_id_agents_id_fk": { + "name": "decision_triage_events_actor_agent_id_agents_id_fk", + "tableFrom": "decision_triage_events", + "tableTo": "agents", + "columnsFrom": [ + "actor_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decision_triage_events_actor_run_id_heartbeat_runs_id_fk": { + "name": "decision_triage_events_actor_run_id_heartbeat_runs_id_fk", + "tableFrom": "decision_triage_events", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "actor_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decision_triage_events_agent_api_key_id_agent_api_keys_id_fk": { + "name": "decision_triage_events_agent_api_key_id_agent_api_keys_id_fk", + "tableFrom": "decision_triage_events", + "tableTo": "agent_api_keys", + "columnsFrom": [ + "agent_api_key_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "decision_triage_events_actor_check": { + "name": "decision_triage_events_actor_check", + "value": "(\n (\"decision_triage_events\".\"actor_type\" = 'agent' AND \"decision_triage_events\".\"actor_agent_id\" IS NOT NULL AND \"decision_triage_events\".\"actor_user_id\" IS NULL)\n OR (\"decision_triage_events\".\"actor_type\" = 'user' AND \"decision_triage_events\".\"actor_agent_id\" IS NULL AND \"decision_triage_events\".\"actor_user_id\" IS NOT NULL)\n OR (\"decision_triage_events\".\"actor_type\" = 'system' AND \"decision_triage_events\".\"actor_agent_id\" IS NULL AND \"decision_triage_events\".\"actor_user_id\" IS NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.decision_training_examples": { + "name": "decision_training_examples", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_kind": { + "name": "source_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_id": { + "name": "source_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "cutoff_at": { + "name": "cutoff_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "notes_history": { + "name": "notes_history", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "decision_outcome": { + "name": "decision_outcome", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "retention_policy": { + "name": "retention_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'scrub_deleted_comments_v1'" + }, + "snapshot": { + "name": "snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "decision_training_examples_company_created_at_idx": { + "name": "decision_training_examples_company_created_at_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "decision_training_examples_issue_idx": { + "name": "decision_training_examples_issue_idx", + "columns": [ + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "decision_training_examples_source_author_uq": { + "name": "decision_training_examples_source_author_uq", + "columns": [ + { + "expression": "source_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "decision_training_examples_company_id_companies_id_fk": { + "name": "decision_training_examples_company_id_companies_id_fk", + "tableFrom": "decision_training_examples", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "decision_training_examples_issue_id_issues_id_fk": { + "name": "decision_training_examples_issue_id_issues_id_fk", + "tableFrom": "decision_training_examples", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.decision_bundles": { + "name": "decision_bundles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "origin_agent_id": { + "name": "origin_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "origin_issue_id": { + "name": "origin_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "origin_run_id": { + "name": "origin_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "decision_bundles_company_created_at_idx": { + "name": "decision_bundles_company_created_at_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "decision_bundles_company_id_companies_id_fk": { + "name": "decision_bundles_company_id_companies_id_fk", + "tableFrom": "decision_bundles", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decision_bundles_origin_agent_id_agents_id_fk": { + "name": "decision_bundles_origin_agent_id_agents_id_fk", + "tableFrom": "decision_bundles", + "tableTo": "agents", + "columnsFrom": [ + "origin_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decision_bundles_origin_issue_id_issues_id_fk": { + "name": "decision_bundles_origin_issue_id_issues_id_fk", + "tableFrom": "decision_bundles", + "tableTo": "issues", + "columnsFrom": [ + "origin_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decision_bundles_origin_run_id_heartbeat_runs_id_fk": { + "name": "decision_bundles_origin_run_id_heartbeat_runs_id_fk", + "tableFrom": "decision_bundles", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "origin_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.decision_effect_executions": { + "name": "decision_effect_executions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "decision_id": { + "name": "decision_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "effect_index": { + "name": "effect_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "effect_type": { + "name": "effect_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_issue_id": { + "name": "target_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'claimed'" + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "activity_log_id": { + "name": "activity_log_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "executed_at": { + "name": "executed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "decision_effect_executions_decision_effect_uq": { + "name": "decision_effect_executions_decision_effect_uq", + "columns": [ + { + "expression": "decision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "effect_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "decision_effect_executions_target_issue_idx": { + "name": "decision_effect_executions_target_issue_idx", + "columns": [ + { + "expression": "target_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "decision_effect_executions_decision_id_decisions_id_fk": { + "name": "decision_effect_executions_decision_id_decisions_id_fk", + "tableFrom": "decision_effect_executions", + "tableTo": "decisions", + "columnsFrom": [ + "decision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "decision_effect_executions_target_issue_id_issues_id_fk": { + "name": "decision_effect_executions_target_issue_id_issues_id_fk", + "tableFrom": "decision_effect_executions", + "tableTo": "issues", + "columnsFrom": [ + "target_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decision_effect_executions_activity_log_id_activity_log_id_fk": { + "name": "decision_effect_executions_activity_log_id_activity_log_id_fk", + "tableFrom": "decision_effect_executions", + "tableTo": "activity_log", + "columnsFrom": [ + "activity_log_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.decision_target_issues": { + "name": "decision_target_issues", + "schema": "", + "columns": { + "decision_id": { + "name": "decision_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "decision_target_issues_decision_idx": { + "name": "decision_target_issues_decision_idx", + "columns": [ + { + "expression": "decision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "decision_target_issues_issue_idx": { + "name": "decision_target_issues_issue_idx", + "columns": [ + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "decision_target_issues_decision_id_decisions_id_fk": { + "name": "decision_target_issues_decision_id_decisions_id_fk", + "tableFrom": "decision_target_issues", + "tableTo": "decisions", + "columnsFrom": [ + "decision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "decision_target_issues_issue_id_issues_id_fk": { + "name": "decision_target_issues_issue_id_issues_id_fk", + "tableFrom": "decision_target_issues", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "decision_target_issues_company_id_companies_id_fk": { + "name": "decision_target_issues_company_id_companies_id_fk", + "tableFrom": "decision_target_issues", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "decision_target_issues_decision_id_issue_id_pk": { + "name": "decision_target_issues_decision_id_issue_id_pk", + "columns": [ + "decision_id", + "issue_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.decisions": { + "name": "decisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "bundle_id": { + "name": "bundle_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "origin_agent_id": { + "name": "origin_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "origin_issue_id": { + "name": "origin_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "origin_run_id": { + "name": "origin_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "rule_key": { + "name": "rule_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "options": { + "name": "options", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "inputs": { + "name": "inputs", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "execution_status": { + "name": "execution_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "chosen_option_id": { + "name": "chosen_option_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "input_values": { + "name": "input_values", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "decided_by_user_id": { + "name": "decided_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "decided_at": { + "name": "decided_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signed_spec": { + "name": "signed_spec", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_snapshots": { + "name": "target_snapshots", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "continuation_policy": { + "name": "continuation_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "decisions_company_status_expires_at_idx": { + "name": "decisions_company_status_expires_at_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "decisions_bundle_idx": { + "name": "decisions_bundle_idx", + "columns": [ + { + "expression": "bundle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "decisions_origin_issue_idx": { + "name": "decisions_origin_issue_idx", + "columns": [ + { + "expression": "origin_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "decisions_company_idempotency_uq": { + "name": "decisions_company_idempotency_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"decisions\".\"idempotency_key\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "decisions_company_id_companies_id_fk": { + "name": "decisions_company_id_companies_id_fk", + "tableFrom": "decisions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decisions_bundle_id_decision_bundles_id_fk": { + "name": "decisions_bundle_id_decision_bundles_id_fk", + "tableFrom": "decisions", + "tableTo": "decision_bundles", + "columnsFrom": [ + "bundle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "decisions_origin_agent_id_agents_id_fk": { + "name": "decisions_origin_agent_id_agents_id_fk", + "tableFrom": "decisions", + "tableTo": "agents", + "columnsFrom": [ + "origin_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decisions_origin_issue_id_issues_id_fk": { + "name": "decisions_origin_issue_id_issues_id_fk", + "tableFrom": "decisions", + "tableTo": "issues", + "columnsFrom": [ + "origin_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decisions_origin_run_id_heartbeat_runs_id_fk": { + "name": "decisions_origin_run_id_heartbeat_runs_id_fk", + "tableFrom": "decisions", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "origin_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.document_annotation_anchor_snapshots": { + "name": "document_annotation_anchor_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "thread_id": { + "name": "thread_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "from_revision_id": { + "name": "from_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "from_revision_number": { + "name": "from_revision_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "to_revision_id": { + "name": "to_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "to_revision_number": { + "name": "to_revision_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "previous_anchor": { + "name": "previous_anchor", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "next_anchor": { + "name": "next_anchor", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "anchor_state": { + "name": "anchor_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "anchor_confidence": { + "name": "anchor_confidence", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "document_annotation_anchor_snapshots_company_thread_created_at_idx": { + "name": "document_annotation_anchor_snapshots_company_thread_created_at_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_annotation_anchor_snapshots_company_document_revision_idx": { + "name": "document_annotation_anchor_snapshots_company_document_revision_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "to_revision_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "document_annotation_anchor_snapshots_company_id_companies_id_fk": { + "name": "document_annotation_anchor_snapshots_company_id_companies_id_fk", + "tableFrom": "document_annotation_anchor_snapshots", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "document_annotation_anchor_snapshots_thread_id_document_annotation_threads_id_fk": { + "name": "document_annotation_anchor_snapshots_thread_id_document_annotation_threads_id_fk", + "tableFrom": "document_annotation_anchor_snapshots", + "tableTo": "document_annotation_threads", + "columnsFrom": [ + "thread_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "document_annotation_anchor_snapshots_document_id_documents_id_fk": { + "name": "document_annotation_anchor_snapshots_document_id_documents_id_fk", + "tableFrom": "document_annotation_anchor_snapshots", + "tableTo": "documents", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "document_annotation_anchor_snapshots_from_revision_id_document_revisions_id_fk": { + "name": "document_annotation_anchor_snapshots_from_revision_id_document_revisions_id_fk", + "tableFrom": "document_annotation_anchor_snapshots", + "tableTo": "document_revisions", + "columnsFrom": [ + "from_revision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "document_annotation_anchor_snapshots_to_revision_id_document_revisions_id_fk": { + "name": "document_annotation_anchor_snapshots_to_revision_id_document_revisions_id_fk", + "tableFrom": "document_annotation_anchor_snapshots", + "tableTo": "document_revisions", + "columnsFrom": [ + "to_revision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.document_annotation_comments": { + "name": "document_annotation_comments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "thread_id": { + "name": "thread_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "routine_id": { + "name": "routine_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "case_id": { + "name": "case_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "document_id": { + "name": "document_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_type": { + "name": "author_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_agent_id": { + "name": "author_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "author_user_id": { + "name": "author_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_run_id": { + "name": "created_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issue_comment_id": { + "name": "issue_comment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source_trust": { + "name": "source_trust", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "document_annotation_comments_company_thread_created_at_idx": { + "name": "document_annotation_comments_company_thread_created_at_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_annotation_comments_company_issue_created_at_idx": { + "name": "document_annotation_comments_company_issue_created_at_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_annotation_comments_company_routine_created_at_idx": { + "name": "document_annotation_comments_company_routine_created_at_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "routine_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_annotation_comments_company_case_created_at_idx": { + "name": "document_annotation_comments_company_case_created_at_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_annotation_comments_company_document_created_at_idx": { + "name": "document_annotation_comments_company_document_created_at_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_annotation_comments_issue_comment_idx": { + "name": "document_annotation_comments_issue_comment_idx", + "columns": [ + { + "expression": "issue_comment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_annotation_comments_body_search_idx": { + "name": "document_annotation_comments_body_search_idx", + "columns": [ + { + "expression": "body", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "document_annotation_comments_company_id_companies_id_fk": { + "name": "document_annotation_comments_company_id_companies_id_fk", + "tableFrom": "document_annotation_comments", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "document_annotation_comments_thread_id_document_annotation_threads_id_fk": { + "name": "document_annotation_comments_thread_id_document_annotation_threads_id_fk", + "tableFrom": "document_annotation_comments", + "tableTo": "document_annotation_threads", + "columnsFrom": [ + "thread_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "document_annotation_comments_issue_id_issues_id_fk": { + "name": "document_annotation_comments_issue_id_issues_id_fk", + "tableFrom": "document_annotation_comments", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "document_annotation_comments_routine_id_routines_id_fk": { + "name": "document_annotation_comments_routine_id_routines_id_fk", + "tableFrom": "document_annotation_comments", + "tableTo": "routines", + "columnsFrom": [ + "routine_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "document_annotation_comments_case_id_cases_id_fk": { + "name": "document_annotation_comments_case_id_cases_id_fk", + "tableFrom": "document_annotation_comments", + "tableTo": "cases", + "columnsFrom": [ + "case_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "document_annotation_comments_document_id_documents_id_fk": { + "name": "document_annotation_comments_document_id_documents_id_fk", + "tableFrom": "document_annotation_comments", + "tableTo": "documents", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "document_annotation_comments_author_agent_id_agents_id_fk": { + "name": "document_annotation_comments_author_agent_id_agents_id_fk", + "tableFrom": "document_annotation_comments", + "tableTo": "agents", + "columnsFrom": [ + "author_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "document_annotation_comments_created_by_run_id_heartbeat_runs_id_fk": { + "name": "document_annotation_comments_created_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "document_annotation_comments", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "created_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "document_annotation_comments_issue_comment_id_issue_comments_id_fk": { + "name": "document_annotation_comments_issue_comment_id_issue_comments_id_fk", + "tableFrom": "document_annotation_comments", + "tableTo": "issue_comments", + "columnsFrom": [ + "issue_comment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "document_annotation_comments_exactly_one_owner_chk": { + "name": "document_annotation_comments_exactly_one_owner_chk", + "value": "num_nonnulls(\"document_annotation_comments\".\"issue_id\", \"document_annotation_comments\".\"routine_id\", \"document_annotation_comments\".\"case_id\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.document_annotation_threads": { + "name": "document_annotation_threads", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "routine_id": { + "name": "routine_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "case_id": { + "name": "case_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "document_id": { + "name": "document_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "document_key": { + "name": "document_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "anchor_state": { + "name": "anchor_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "original_revision_id": { + "name": "original_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "original_revision_number": { + "name": "original_revision_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "current_revision_id": { + "name": "current_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "current_revision_number": { + "name": "current_revision_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "selected_text": { + "name": "selected_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix_text": { + "name": "prefix_text", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "suffix_text": { + "name": "suffix_text", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "normalized_start": { + "name": "normalized_start", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "normalized_end": { + "name": "normalized_end", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "markdown_start": { + "name": "markdown_start", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "markdown_end": { + "name": "markdown_end", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "anchor_confidence": { + "name": "anchor_confidence", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'exact'" + }, + "anchor_selector": { + "name": "anchor_selector", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resolved_by_agent_id": { + "name": "resolved_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "resolved_by_user_id": { + "name": "resolved_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "document_annotation_threads_company_document_status_idx": { + "name": "document_annotation_threads_company_document_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_annotation_threads_company_issue_status_idx": { + "name": "document_annotation_threads_company_issue_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_annotation_threads_company_routine_status_idx": { + "name": "document_annotation_threads_company_routine_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "routine_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_annotation_threads_company_case_status_idx": { + "name": "document_annotation_threads_company_case_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_annotation_threads_company_current_revision_open_idx": { + "name": "document_annotation_threads_company_current_revision_open_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "current_revision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_annotation_threads_company_anchor_state_idx": { + "name": "document_annotation_threads_company_anchor_state_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "anchor_state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "document_annotation_threads_company_id_companies_id_fk": { + "name": "document_annotation_threads_company_id_companies_id_fk", + "tableFrom": "document_annotation_threads", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "document_annotation_threads_issue_id_issues_id_fk": { + "name": "document_annotation_threads_issue_id_issues_id_fk", + "tableFrom": "document_annotation_threads", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "document_annotation_threads_routine_id_routines_id_fk": { + "name": "document_annotation_threads_routine_id_routines_id_fk", + "tableFrom": "document_annotation_threads", + "tableTo": "routines", + "columnsFrom": [ + "routine_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "document_annotation_threads_case_id_cases_id_fk": { + "name": "document_annotation_threads_case_id_cases_id_fk", + "tableFrom": "document_annotation_threads", + "tableTo": "cases", + "columnsFrom": [ + "case_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "document_annotation_threads_document_id_documents_id_fk": { + "name": "document_annotation_threads_document_id_documents_id_fk", + "tableFrom": "document_annotation_threads", + "tableTo": "documents", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "document_annotation_threads_original_revision_id_document_revisions_id_fk": { + "name": "document_annotation_threads_original_revision_id_document_revisions_id_fk", + "tableFrom": "document_annotation_threads", + "tableTo": "document_revisions", + "columnsFrom": [ + "original_revision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "document_annotation_threads_current_revision_id_document_revisions_id_fk": { + "name": "document_annotation_threads_current_revision_id_document_revisions_id_fk", + "tableFrom": "document_annotation_threads", + "tableTo": "document_revisions", + "columnsFrom": [ + "current_revision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "document_annotation_threads_created_by_agent_id_agents_id_fk": { + "name": "document_annotation_threads_created_by_agent_id_agents_id_fk", + "tableFrom": "document_annotation_threads", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "document_annotation_threads_resolved_by_agent_id_agents_id_fk": { + "name": "document_annotation_threads_resolved_by_agent_id_agents_id_fk", + "tableFrom": "document_annotation_threads", + "tableTo": "agents", + "columnsFrom": [ + "resolved_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "document_annotation_threads_exactly_one_owner_chk": { + "name": "document_annotation_threads_exactly_one_owner_chk", + "value": "num_nonnulls(\"document_annotation_threads\".\"issue_id\", \"document_annotation_threads\".\"routine_id\", \"document_annotation_threads\".\"case_id\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.document_memberships": { + "name": "document_memberships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "starred_at": { + "name": "starred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "document_memberships_company_user_starred_idx": { + "name": "document_memberships_company_user_starred_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "starred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_memberships_company_user_document_uq": { + "name": "document_memberships_company_user_document_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "document_memberships_company_id_companies_id_fk": { + "name": "document_memberships_company_id_companies_id_fk", + "tableFrom": "document_memberships", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "document_memberships_document_id_documents_id_fk": { + "name": "document_memberships_document_id_documents_id_fk", + "tableFrom": "document_memberships", + "tableTo": "documents", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.document_revisions": { + "name": "document_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision_number": { + "name": "revision_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "format": { + "name": "format", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'markdown'" + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "change_summary": { + "name": "change_summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_run_id": { + "name": "created_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "document_revisions_document_revision_uq": { + "name": "document_revisions_document_revision_uq", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "revision_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_revisions_company_document_created_idx": { + "name": "document_revisions_company_document_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "document_revisions_company_id_companies_id_fk": { + "name": "document_revisions_company_id_companies_id_fk", + "tableFrom": "document_revisions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "document_revisions_document_id_documents_id_fk": { + "name": "document_revisions_document_id_documents_id_fk", + "tableFrom": "document_revisions", + "tableTo": "documents", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "document_revisions_created_by_agent_id_agents_id_fk": { + "name": "document_revisions_created_by_agent_id_agents_id_fk", + "tableFrom": "document_revisions", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "document_revisions_created_by_run_id_heartbeat_runs_id_fk": { + "name": "document_revisions_created_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "document_revisions", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "created_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documents": { + "name": "documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "format": { + "name": "format", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'markdown'" + }, + "latest_body": { + "name": "latest_body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "latest_revision_id": { + "name": "latest_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "latest_revision_number": { + "name": "latest_revision_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_by_agent_id": { + "name": "updated_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "updated_by_user_id": { + "name": "updated_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "locked_at": { + "name": "locked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "locked_by_agent_id": { + "name": "locked_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "locked_by_user_id": { + "name": "locked_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_trust": { + "name": "source_trust", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "documents_company_updated_idx": { + "name": "documents_company_updated_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documents_company_created_idx": { + "name": "documents_company_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documents_title_search_idx": { + "name": "documents_title_search_idx", + "columns": [ + { + "expression": "title", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "documents_latest_body_search_idx": { + "name": "documents_latest_body_search_idx", + "columns": [ + { + "expression": "latest_body", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "documents_company_id_companies_id_fk": { + "name": "documents_company_id_companies_id_fk", + "tableFrom": "documents", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "documents_created_by_agent_id_agents_id_fk": { + "name": "documents_created_by_agent_id_agents_id_fk", + "tableFrom": "documents", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "documents_updated_by_agent_id_agents_id_fk": { + "name": "documents_updated_by_agent_id_agents_id_fk", + "tableFrom": "documents", + "tableTo": "agents", + "columnsFrom": [ + "updated_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "documents_locked_by_agent_id_agents_id_fk": { + "name": "documents_locked_by_agent_id_agents_id_fk", + "tableFrom": "documents", + "tableTo": "agents", + "columnsFrom": [ + "locked_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email_endpoints": { + "name": "email_endpoints", + "schema": "", + "columns": { + "endpoint_id": { + "name": "endpoint_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "receive_mode": { + "name": "receive_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "webhook_id": { + "name": "webhook_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owned_api_key_id": { + "name": "owned_api_key_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "activation_at": { + "name": "activation_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sync_checkpoint": { + "name": "sync_checkpoint", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "email_endpoints_company_id_endpoint_id_chat_endpoints_company_id_id_fk": { + "name": "email_endpoints_company_id_endpoint_id_chat_endpoints_company_id_id_fk", + "tableFrom": "email_endpoints", + "tableTo": "chat_endpoints", + "columnsFrom": [ + "company_id", + "endpoint_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "email_endpoints_receive_mode_check": { + "name": "email_endpoints_receive_mode_check", + "value": "\"email_endpoints\".\"receive_mode\" in ('websocket', 'webhook')" + } + }, + "isRLSEnabled": false + }, + "public.email_messages": { + "name": "email_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "endpoint_id": { + "name": "endpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider_message_id": { + "name": "provider_message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "envelope": { + "name": "envelope", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "full_text": { + "name": "full_text", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "direction": { + "name": "direction", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "automatic": { + "name": "automatic", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "attachment_ids": { + "name": "attachment_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "email_messages_provider_uq": { + "name": "email_messages_provider_uq", + "columns": [ + { + "expression": "endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "email_messages_conversation_idx": { + "name": "email_messages_conversation_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "conversation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "timestamp", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "email_messages_company_id_endpoint_id_chat_endpoints_company_id_id_fk": { + "name": "email_messages_company_id_endpoint_id_chat_endpoints_company_id_id_fk", + "tableFrom": "email_messages", + "tableTo": "chat_endpoints", + "columnsFrom": [ + "company_id", + "endpoint_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_messages_company_id_conversation_id_chat_conversations_company_id_id_fk": { + "name": "email_messages_company_id_conversation_id_chat_conversations_company_id_id_fk", + "tableFrom": "email_messages", + "tableTo": "chat_conversations", + "columnsFrom": [ + "company_id", + "conversation_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "email_messages_direction_check": { + "name": "email_messages_direction_check", + "value": "\"email_messages\".\"direction\" in ('inbound', 'outbound')" + } + }, + "isRLSEnabled": false + }, + "public.email_sends": { + "name": "email_sends", + "schema": "", + "columns": { + "publication_id": { + "name": "publication_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "endpoint_id": { + "name": "endpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "request": { + "name": "request", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "actor": { + "name": "actor", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "digest": { + "name": "digest", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "first_attempt_at": { + "name": "first_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "email_sends_pending_idx": { + "name": "email_sends_pending_idx", + "columns": [ + { + "expression": "endpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "outcome", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"email_sends\".\"outcome\" in ('queued', 'uncertain')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "email_sends_company_id_endpoint_id_chat_endpoints_company_id_id_fk": { + "name": "email_sends_company_id_endpoint_id_chat_endpoints_company_id_id_fk", + "tableFrom": "email_sends", + "tableTo": "chat_endpoints", + "columnsFrom": [ + "company_id", + "endpoint_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_sends_company_id_publication_id_chat_publications_company_id_id_fk": { + "name": "email_sends_company_id_publication_id_chat_publications_company_id_id_fk", + "tableFrom": "email_sends", + "tableTo": "chat_publications", + "columnsFrom": [ + "company_id", + "publication_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "email_sends_outcome_check": { + "name": "email_sends_outcome_check", + "value": "\"email_sends\".\"outcome\" in ('queued', 'sent', 'delivered', 'failed', 'uncertain')" + } + }, + "isRLSEnabled": false + }, + "public.environment_custom_image_setup_sessions": { + "name": "environment_custom_image_setup_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "environment_id": { + "name": "environment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "promoted_template_id": { + "name": "promoted_template_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_lease_id": { + "name": "provider_lease_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "environment_lease_id": { + "name": "environment_lease_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'starting'" + }, + "started_by_user_id": { + "name": "started_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_by_agent_id": { + "name": "started_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "base_template_ref": { + "name": "base_template_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "connection_summary": { + "name": "connection_summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "connection_secret_ref": { + "name": "connection_secret_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "environment_custom_image_setup_sessions_environment_status_idx": { + "name": "environment_custom_image_setup_sessions_environment_status_idx", + "columns": [ + { + "expression": "environment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_custom_image_setup_sessions_environment_active_uq": { + "name": "environment_custom_image_setup_sessions_environment_active_uq", + "columns": [ + { + "expression": "environment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"environment_custom_image_setup_sessions\".\"status\" IN ('starting', 'waiting_for_user', 'capturing')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_custom_image_setup_sessions_template_idx": { + "name": "environment_custom_image_setup_sessions_template_idx", + "columns": [ + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_custom_image_setup_sessions_promoted_template_idx": { + "name": "environment_custom_image_setup_sessions_promoted_template_idx", + "columns": [ + { + "expression": "promoted_template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_custom_image_setup_sessions_expires_idx": { + "name": "environment_custom_image_setup_sessions_expires_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_custom_image_setup_sessions_provider_lease_idx": { + "name": "environment_custom_image_setup_sessions_provider_lease_idx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_lease_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "environment_custom_image_setup_sessions_environment_id_environments_id_fk": { + "name": "environment_custom_image_setup_sessions_environment_id_environments_id_fk", + "tableFrom": "environment_custom_image_setup_sessions", + "tableTo": "environments", + "columnsFrom": [ + "environment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "environment_custom_image_setup_sessions_template_id_environment_custom_image_templates_id_fk": { + "name": "environment_custom_image_setup_sessions_template_id_environment_custom_image_templates_id_fk", + "tableFrom": "environment_custom_image_setup_sessions", + "tableTo": "environment_custom_image_templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "environment_custom_image_setup_sessions_promoted_template_id_environment_custom_image_templates_id_fk": { + "name": "environment_custom_image_setup_sessions_promoted_template_id_environment_custom_image_templates_id_fk", + "tableFrom": "environment_custom_image_setup_sessions", + "tableTo": "environment_custom_image_templates", + "columnsFrom": [ + "promoted_template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "environment_custom_image_setup_sessions_environment_lease_id_environment_leases_id_fk": { + "name": "environment_custom_image_setup_sessions_environment_lease_id_environment_leases_id_fk", + "tableFrom": "environment_custom_image_setup_sessions", + "tableTo": "environment_leases", + "columnsFrom": [ + "environment_lease_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "environment_custom_image_setup_sessions_started_by_agent_id_agents_id_fk": { + "name": "environment_custom_image_setup_sessions_started_by_agent_id_agents_id_fk", + "tableFrom": "environment_custom_image_setup_sessions", + "tableTo": "agents", + "columnsFrom": [ + "started_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.environment_custom_image_templates": { + "name": "environment_custom_image_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "environment_id": { + "name": "environment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "template_kind": { + "name": "template_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "template_ref": { + "name": "template_ref", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_template_ref": { + "name": "source_template_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_environment_config_fingerprint": { + "name": "source_environment_config_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "captured_at": { + "name": "captured_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "superseded_by_template_id": { + "name": "superseded_by_template_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "environment_custom_image_templates_environment_status_idx": { + "name": "environment_custom_image_templates_environment_status_idx", + "columns": [ + { + "expression": "environment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_custom_image_templates_environment_provider_status_idx": { + "name": "environment_custom_image_templates_environment_provider_status_idx", + "columns": [ + { + "expression": "environment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_custom_image_templates_environment_active_uq": { + "name": "environment_custom_image_templates_environment_active_uq", + "columns": [ + { + "expression": "environment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"environment_custom_image_templates\".\"status\" = 'active'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_custom_image_templates_superseded_by_idx": { + "name": "environment_custom_image_templates_superseded_by_idx", + "columns": [ + { + "expression": "superseded_by_template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_custom_image_templates_last_used_idx": { + "name": "environment_custom_image_templates_last_used_idx", + "columns": [ + { + "expression": "last_used_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "environment_custom_image_templates_environment_id_environments_id_fk": { + "name": "environment_custom_image_templates_environment_id_environments_id_fk", + "tableFrom": "environment_custom_image_templates", + "tableTo": "environments", + "columnsFrom": [ + "environment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "environment_custom_image_templates_created_by_agent_id_agents_id_fk": { + "name": "environment_custom_image_templates_created_by_agent_id_agents_id_fk", + "tableFrom": "environment_custom_image_templates", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "environment_custom_image_templates_superseded_by_template_id_environment_custom_image_templates_id_fk": { + "name": "environment_custom_image_templates_superseded_by_template_id_environment_custom_image_templates_id_fk", + "tableFrom": "environment_custom_image_templates", + "tableTo": "environment_custom_image_templates", + "columnsFrom": [ + "superseded_by_template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.environment_leases": { + "name": "environment_leases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "environment_id": { + "name": "environment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "execution_workspace_id": { + "name": "execution_workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "heartbeat_run_id": { + "name": "heartbeat_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "lease_policy": { + "name": "lease_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'ephemeral'" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_lease_id": { + "name": "provider_lease_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "acquired_at": { + "name": "acquired_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "released_at": { + "name": "released_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cleanup_status": { + "name": "cleanup_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "environment_leases_company_environment_status_idx": { + "name": "environment_leases_company_environment_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "environment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_leases_company_execution_workspace_idx": { + "name": "environment_leases_company_execution_workspace_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_leases_company_issue_idx": { + "name": "environment_leases_company_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_leases_heartbeat_run_idx": { + "name": "environment_leases_heartbeat_run_idx", + "columns": [ + { + "expression": "heartbeat_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_leases_company_last_used_idx": { + "name": "environment_leases_company_last_used_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_used_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_leases_provider_lease_idx": { + "name": "environment_leases_provider_lease_idx", + "columns": [ + { + "expression": "provider_lease_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "environment_leases_company_id_companies_id_fk": { + "name": "environment_leases_company_id_companies_id_fk", + "tableFrom": "environment_leases", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "environment_leases_environment_id_environments_id_fk": { + "name": "environment_leases_environment_id_environments_id_fk", + "tableFrom": "environment_leases", + "tableTo": "environments", + "columnsFrom": [ + "environment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "environment_leases_execution_workspace_id_execution_workspaces_id_fk": { + "name": "environment_leases_execution_workspace_id_execution_workspaces_id_fk", + "tableFrom": "environment_leases", + "tableTo": "execution_workspaces", + "columnsFrom": [ + "execution_workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "environment_leases_issue_id_issues_id_fk": { + "name": "environment_leases_issue_id_issues_id_fk", + "tableFrom": "environment_leases", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "environment_leases_heartbeat_run_id_heartbeat_runs_id_fk": { + "name": "environment_leases_heartbeat_run_id_heartbeat_runs_id_fk", + "tableFrom": "environment_leases", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "heartbeat_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.environments": { + "name": "environments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "driver": { + "name": "driver", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'local'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "env_vars": { + "name": "env_vars", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "environments_status_idx": { + "name": "environments_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environments_local_driver_idx": { + "name": "environments_local_driver_idx", + "columns": [ + { + "expression": "driver", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"environments\".\"driver\" = 'local'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "environments_managed_sandbox_idx": { + "name": "environments_managed_sandbox_idx", + "columns": [ + { + "expression": "driver", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"environments\".\"driver\" = 'sandbox' AND (\"environments\".\"metadata\" ->> 'managedByPaperclip')::boolean = true", + "concurrently": false, + "method": "btree", + "with": {} + }, + "environments_name_idx": { + "name": "environments_name_idx", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.execution_workspace_runtime_leases": { + "name": "execution_workspace_runtime_leases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "execution_workspace_id": { + "name": "execution_workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "owner_key": { + "name": "owner_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_issue_id": { + "name": "owner_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owner_run_id": { + "name": "owner_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owner_agent_id": { + "name": "owner_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "last_action": { + "name": "last_action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "renewed_at": { + "name": "renewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "execution_workspace_runtime_leases_company_workspace_idx": { + "name": "execution_workspace_runtime_leases_company_workspace_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_workspace_runtime_leases_company_owner_idx": { + "name": "execution_workspace_runtime_leases_company_owner_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_workspace_runtime_leases_expires_at_idx": { + "name": "execution_workspace_runtime_leases_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "execution_workspace_runtime_leases_company_id_companies_id_fk": { + "name": "execution_workspace_runtime_leases_company_id_companies_id_fk", + "tableFrom": "execution_workspace_runtime_leases", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "execution_workspace_runtime_leases_execution_workspace_id_execution_workspaces_id_fk": { + "name": "execution_workspace_runtime_leases_execution_workspace_id_execution_workspaces_id_fk", + "tableFrom": "execution_workspace_runtime_leases", + "tableTo": "execution_workspaces", + "columnsFrom": [ + "execution_workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "execution_workspace_runtime_leases_owner_issue_id_issues_id_fk": { + "name": "execution_workspace_runtime_leases_owner_issue_id_issues_id_fk", + "tableFrom": "execution_workspace_runtime_leases", + "tableTo": "issues", + "columnsFrom": [ + "owner_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "execution_workspace_runtime_leases_owner_run_id_heartbeat_runs_id_fk": { + "name": "execution_workspace_runtime_leases_owner_run_id_heartbeat_runs_id_fk", + "tableFrom": "execution_workspace_runtime_leases", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "owner_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "execution_workspace_runtime_leases_owner_agent_id_agents_id_fk": { + "name": "execution_workspace_runtime_leases_owner_agent_id_agents_id_fk", + "tableFrom": "execution_workspace_runtime_leases", + "tableTo": "agents", + "columnsFrom": [ + "owner_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "execution_workspace_runtime_leases_execution_workspace_id_unique": { + "name": "execution_workspace_runtime_leases_execution_workspace_id_unique", + "nullsNotDistinct": false, + "columns": [ + "execution_workspace_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.execution_workspaces": { + "name": "execution_workspaces", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "project_workspace_id": { + "name": "project_workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source_issue_id": { + "name": "source_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "strategy_type": { + "name": "strategy_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "cwd": { + "name": "cwd", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repo_url": { + "name": "repo_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "base_ref": { + "name": "base_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch_name": { + "name": "branch_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_type": { + "name": "provider_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'local_fs'" + }, + "provider_ref": { + "name": "provider_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "derived_from_execution_workspace_id": { + "name": "derived_from_execution_workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "opened_at": { + "name": "opened_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "closed_at": { + "name": "closed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cleanup_eligible_at": { + "name": "cleanup_eligible_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cleanup_reason": { + "name": "cleanup_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "execution_workspaces_company_project_status_idx": { + "name": "execution_workspaces_company_project_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_workspaces_company_project_workspace_status_idx": { + "name": "execution_workspaces_company_project_workspace_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_workspaces_company_source_issue_idx": { + "name": "execution_workspaces_company_source_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_workspaces_company_last_used_idx": { + "name": "execution_workspaces_company_last_used_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_used_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_workspaces_company_branch_idx": { + "name": "execution_workspaces_company_branch_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "branch_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "execution_workspaces_company_id_companies_id_fk": { + "name": "execution_workspaces_company_id_companies_id_fk", + "tableFrom": "execution_workspaces", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "execution_workspaces_project_id_projects_id_fk": { + "name": "execution_workspaces_project_id_projects_id_fk", + "tableFrom": "execution_workspaces", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "execution_workspaces_project_workspace_id_project_workspaces_id_fk": { + "name": "execution_workspaces_project_workspace_id_project_workspaces_id_fk", + "tableFrom": "execution_workspaces", + "tableTo": "project_workspaces", + "columnsFrom": [ + "project_workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "execution_workspaces_source_issue_id_issues_id_fk": { + "name": "execution_workspaces_source_issue_id_issues_id_fk", + "tableFrom": "execution_workspaces", + "tableTo": "issues", + "columnsFrom": [ + "source_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "execution_workspaces_derived_from_execution_workspace_id_execution_workspaces_id_fk": { + "name": "execution_workspaces_derived_from_execution_workspace_id_execution_workspaces_id_fk", + "tableFrom": "execution_workspaces", + "tableTo": "execution_workspaces", + "columnsFrom": [ + "derived_from_execution_workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.external_object_mentions": { + "name": "external_object_mentions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_issue_id": { + "name": "source_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_kind": { + "name": "source_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_record_id": { + "name": "source_record_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "document_key": { + "name": "document_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "property_key": { + "name": "property_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "matched_text_redacted": { + "name": "matched_text_redacted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sanitized_display_url": { + "name": "sanitized_display_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "canonical_identity_hash": { + "name": "canonical_identity_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "canonical_identity": { + "name": "canonical_identity", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "object_id": { + "name": "object_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider_key": { + "name": "provider_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "detector_key": { + "name": "detector_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "object_type": { + "name": "object_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confidence": { + "name": "confidence", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'exact'" + }, + "created_by_plugin_id": { + "name": "created_by_plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "external_object_mentions_company_source_issue_idx": { + "name": "external_object_mentions_company_source_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "external_object_mentions_company_object_idx": { + "name": "external_object_mentions_company_object_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "object_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "external_object_mentions_company_provider_idx": { + "name": "external_object_mentions_company_provider_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "object_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "external_object_mentions_company_source_record_uq": { + "name": "external_object_mentions_company_source_record_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_record_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "document_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "property_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "canonical_identity_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"external_object_mentions\".\"source_record_id\" is not null and \"external_object_mentions\".\"canonical_identity_hash\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "external_object_mentions_company_source_null_record_uq": { + "name": "external_object_mentions_company_source_null_record_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "document_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "property_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "canonical_identity_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"external_object_mentions\".\"source_record_id\" is null and \"external_object_mentions\".\"canonical_identity_hash\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "external_object_mentions_company_id_companies_id_fk": { + "name": "external_object_mentions_company_id_companies_id_fk", + "tableFrom": "external_object_mentions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "external_object_mentions_source_issue_id_issues_id_fk": { + "name": "external_object_mentions_source_issue_id_issues_id_fk", + "tableFrom": "external_object_mentions", + "tableTo": "issues", + "columnsFrom": [ + "source_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "external_object_mentions_object_id_external_objects_id_fk": { + "name": "external_object_mentions_object_id_external_objects_id_fk", + "tableFrom": "external_object_mentions", + "tableTo": "external_objects", + "columnsFrom": [ + "object_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "external_object_mentions_created_by_plugin_id_plugins_id_fk": { + "name": "external_object_mentions_created_by_plugin_id_plugins_id_fk", + "tableFrom": "external_object_mentions", + "tableTo": "plugins", + "columnsFrom": [ + "created_by_plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.external_objects": { + "name": "external_objects", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider_key": { + "name": "provider_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "object_type": { + "name": "object_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sanitized_canonical_url": { + "name": "sanitized_canonical_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "canonical_identity_hash": { + "name": "canonical_identity_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "display_key": { + "name": "display_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon_key": { + "name": "icon_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "display_title": { + "name": "display_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status_key": { + "name": "status_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status_label": { + "name": "status_label", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status_icon_key": { + "name": "status_icon_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status_category": { + "name": "status_category", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "status_tone": { + "name": "status_tone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'neutral'" + }, + "liveness": { + "name": "liveness", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "is_terminal": { + "name": "is_terminal", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "remote_version": { + "name": "remote_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "etag": { + "name": "etag", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_resolved_at": { + "name": "last_resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_changed_at": { + "name": "last_changed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_error_at": { + "name": "last_error_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "next_refresh_at": { + "name": "next_refresh_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "refresh_started_at": { + "name": "refresh_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "last_error_code": { + "name": "last_error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_error_message": { + "name": "last_error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "external_objects_company_provider_object_idx": { + "name": "external_objects_company_provider_object_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "object_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "external_objects_company_provider_status_idx": { + "name": "external_objects_company_provider_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status_category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "external_objects_company_refresh_idx": { + "name": "external_objects_company_refresh_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_refresh_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "external_objects_company_external_id_uq": { + "name": "external_objects_company_external_id_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "object_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "external_objects_company_identity_uq": { + "name": "external_objects_company_identity_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "object_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "canonical_identity_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "external_objects_company_id_companies_id_fk": { + "name": "external_objects_company_id_companies_id_fk", + "tableFrom": "external_objects", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "external_objects_plugin_id_plugins_id_fk": { + "name": "external_objects_plugin_id_plugins_id_fk", + "tableFrom": "external_objects", + "tableTo": "plugins", + "columnsFrom": [ + "plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.feedback_exports": { + "name": "feedback_exports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "feedback_vote_id": { + "name": "feedback_vote_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "author_user_id": { + "name": "author_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vote": { + "name": "vote", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'local_only'" + }, + "destination": { + "name": "destination", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "export_id": { + "name": "export_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "consent_version": { + "name": "consent_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "schema_version": { + "name": "schema_version", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'paperclip-feedback-envelope-v2'" + }, + "bundle_version": { + "name": "bundle_version", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'paperclip-feedback-bundle-v2'" + }, + "payload_version": { + "name": "payload_version", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'paperclip-feedback-v1'" + }, + "payload_digest": { + "name": "payload_digest", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload_snapshot": { + "name": "payload_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "target_summary": { + "name": "target_summary", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "redaction_summary": { + "name": "redaction_summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_attempted_at": { + "name": "last_attempted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "exported_at": { + "name": "exported_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "feedback_exports_feedback_vote_idx": { + "name": "feedback_exports_feedback_vote_idx", + "columns": [ + { + "expression": "feedback_vote_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "feedback_exports_company_created_idx": { + "name": "feedback_exports_company_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "feedback_exports_company_status_idx": { + "name": "feedback_exports_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "feedback_exports_company_issue_idx": { + "name": "feedback_exports_company_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "feedback_exports_company_project_idx": { + "name": "feedback_exports_company_project_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "feedback_exports_company_author_idx": { + "name": "feedback_exports_company_author_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "author_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "feedback_exports_company_id_companies_id_fk": { + "name": "feedback_exports_company_id_companies_id_fk", + "tableFrom": "feedback_exports", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "feedback_exports_feedback_vote_id_feedback_votes_id_fk": { + "name": "feedback_exports_feedback_vote_id_feedback_votes_id_fk", + "tableFrom": "feedback_exports", + "tableTo": "feedback_votes", + "columnsFrom": [ + "feedback_vote_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "feedback_exports_issue_id_issues_id_fk": { + "name": "feedback_exports_issue_id_issues_id_fk", + "tableFrom": "feedback_exports", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "feedback_exports_project_id_projects_id_fk": { + "name": "feedback_exports_project_id_projects_id_fk", + "tableFrom": "feedback_exports", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.feedback_votes": { + "name": "feedback_votes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_user_id": { + "name": "author_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vote": { + "name": "vote", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shared_with_labs": { + "name": "shared_with_labs", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "shared_at": { + "name": "shared_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "consent_version": { + "name": "consent_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redaction_summary": { + "name": "redaction_summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "feedback_votes_company_issue_idx": { + "name": "feedback_votes_company_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "feedback_votes_issue_target_idx": { + "name": "feedback_votes_issue_target_idx", + "columns": [ + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "feedback_votes_author_idx": { + "name": "feedback_votes_author_idx", + "columns": [ + { + "expression": "author_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "feedback_votes_company_target_author_idx": { + "name": "feedback_votes_company_target_author_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "author_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "feedback_votes_company_id_companies_id_fk": { + "name": "feedback_votes_company_id_companies_id_fk", + "tableFrom": "feedback_votes", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "feedback_votes_issue_id_issues_id_fk": { + "name": "feedback_votes_issue_id_issues_id_fk", + "tableFrom": "feedback_votes", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.finance_events": { + "name": "finance_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "goal_id": { + "name": "goal_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "heartbeat_run_id": { + "name": "heartbeat_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "cost_event_id": { + "name": "cost_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "receipt_hash": { + "name": "receipt_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "billing_code": { + "name": "billing_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "event_kind": { + "name": "event_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "direction": { + "name": "direction", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'debit'" + }, + "biller": { + "name": "biller", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "execution_adapter_type": { + "name": "execution_adapter_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pricing_tier": { + "name": "pricing_tier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "unit": { + "name": "unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount_cents": { + "name": "amount_cents", + "type": "numeric(24, 7)", + "primaryKey": false, + "notNull": true + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'USD'" + }, + "estimated": { + "name": "estimated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "external_invoice_id": { + "name": "external_invoice_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata_json": { + "name": "metadata_json", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "finance_events_company_receipt_idx": { + "name": "finance_events_company_receipt_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "finance_events_company_occurred_idx": { + "name": "finance_events_company_occurred_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "finance_events_company_biller_occurred_idx": { + "name": "finance_events_company_biller_occurred_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "biller", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "finance_events_company_kind_occurred_idx": { + "name": "finance_events_company_kind_occurred_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "finance_events_company_direction_occurred_idx": { + "name": "finance_events_company_direction_occurred_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "direction", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "finance_events_company_heartbeat_run_idx": { + "name": "finance_events_company_heartbeat_run_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "heartbeat_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "finance_events_company_cost_event_idx": { + "name": "finance_events_company_cost_event_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cost_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "finance_events_company_id_companies_id_fk": { + "name": "finance_events_company_id_companies_id_fk", + "tableFrom": "finance_events", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "finance_events_agent_id_agents_id_fk": { + "name": "finance_events_agent_id_agents_id_fk", + "tableFrom": "finance_events", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "finance_events_issue_id_issues_id_fk": { + "name": "finance_events_issue_id_issues_id_fk", + "tableFrom": "finance_events", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "finance_events_project_id_projects_id_fk": { + "name": "finance_events_project_id_projects_id_fk", + "tableFrom": "finance_events", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "finance_events_goal_id_goals_id_fk": { + "name": "finance_events_goal_id_goals_id_fk", + "tableFrom": "finance_events", + "tableTo": "goals", + "columnsFrom": [ + "goal_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "finance_events_heartbeat_run_id_heartbeat_runs_id_fk": { + "name": "finance_events_heartbeat_run_id_heartbeat_runs_id_fk", + "tableFrom": "finance_events", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "heartbeat_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "finance_events_cost_event_id_cost_events_id_fk": { + "name": "finance_events_cost_event_id_cost_events_id_fk", + "tableFrom": "finance_events", + "tableTo": "cost_events", + "columnsFrom": [ + "cost_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.folders": { + "name": "folders", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "system_key": { + "name": "system_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "folders_company_kind_position_idx": { + "name": "folders_company_kind_position_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "position", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "folders_company_kind_root_slug_uq": { + "name": "folders_company_kind_root_slug_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"folders\".\"parent_id\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "folders_company_kind_parent_slug_uq": { + "name": "folders_company_kind_parent_slug_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"folders\".\"parent_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "folders_company_kind_system_key_uq": { + "name": "folders_company_kind_system_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "system_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"folders\".\"system_key\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "folders_company_kind_parent_position_idx": { + "name": "folders_company_kind_parent_position_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "position", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "folders_company_id_companies_id_fk": { + "name": "folders_company_id_companies_id_fk", + "tableFrom": "folders", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "folders_parent_id_folders_id_fk": { + "name": "folders_parent_id_folders_id_fk", + "tableFrom": "folders", + "tableTo": "folders", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.goals": { + "name": "goals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "level": { + "name": "level", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'task'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'planned'" + }, + "parent_id": { + "name": "parent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owner_agent_id": { + "name": "owner_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "goals_company_idx": { + "name": "goals_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "goals_company_id_companies_id_fk": { + "name": "goals_company_id_companies_id_fk", + "tableFrom": "goals", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "goals_parent_id_goals_id_fk": { + "name": "goals_parent_id_goals_id_fk", + "tableFrom": "goals", + "tableTo": "goals", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "goals_owner_agent_id_agents_id_fk": { + "name": "goals_owner_agent_id_agents_id_fk", + "tableFrom": "goals", + "tableTo": "agents", + "columnsFrom": [ + "owner_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.heartbeat_run_events": { + "name": "heartbeat_run_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "seq": { + "name": "seq", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stream": { + "name": "stream", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "level": { + "name": "level", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "source_instance_id": { + "name": "source_instance_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_event_id": { + "name": "source_event_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_seq": { + "name": "source_seq", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "source_payload_sha256": { + "name": "source_payload_sha256", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "protocol_schema_version": { + "name": "protocol_schema_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "heartbeat_run_events_run_seq_uq": { + "name": "heartbeat_run_events_run_seq_uq", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "seq", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_run_events_run_source_event_uq": { + "name": "heartbeat_run_events_run_source_event_uq", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"heartbeat_run_events\".\"source_event_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_run_events_run_source_seq_uq": { + "name": "heartbeat_run_events_run_source_seq_uq", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_seq", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"heartbeat_run_events\".\"source_instance_id\" is not null and \"heartbeat_run_events\".\"source_seq\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_run_events_company_run_idx": { + "name": "heartbeat_run_events_company_run_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_run_events_company_created_idx": { + "name": "heartbeat_run_events_company_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "heartbeat_run_events_company_id_companies_id_fk": { + "name": "heartbeat_run_events_company_id_companies_id_fk", + "tableFrom": "heartbeat_run_events", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "heartbeat_run_events_run_id_heartbeat_runs_id_fk": { + "name": "heartbeat_run_events_run_id_heartbeat_runs_id_fk", + "tableFrom": "heartbeat_run_events", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "heartbeat_run_events_agent_id_agents_id_fk": { + "name": "heartbeat_run_events_agent_id_agents_id_fk", + "tableFrom": "heartbeat_run_events", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.heartbeat_run_watchdog_decisions": { + "name": "heartbeat_run_watchdog_decisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "evaluation_issue_id": { + "name": "evaluation_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "decision": { + "name": "decision", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "snoozed_until": { + "name": "snoozed_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_run_id": { + "name": "created_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "heartbeat_run_watchdog_decisions_company_run_created_idx": { + "name": "heartbeat_run_watchdog_decisions_company_run_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_run_watchdog_decisions_company_run_snooze_idx": { + "name": "heartbeat_run_watchdog_decisions_company_run_snooze_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "snoozed_until", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "heartbeat_run_watchdog_decisions_company_id_companies_id_fk": { + "name": "heartbeat_run_watchdog_decisions_company_id_companies_id_fk", + "tableFrom": "heartbeat_run_watchdog_decisions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "heartbeat_run_watchdog_decisions_run_id_heartbeat_runs_id_fk": { + "name": "heartbeat_run_watchdog_decisions_run_id_heartbeat_runs_id_fk", + "tableFrom": "heartbeat_run_watchdog_decisions", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "heartbeat_run_watchdog_decisions_evaluation_issue_id_issues_id_fk": { + "name": "heartbeat_run_watchdog_decisions_evaluation_issue_id_issues_id_fk", + "tableFrom": "heartbeat_run_watchdog_decisions", + "tableTo": "issues", + "columnsFrom": [ + "evaluation_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "heartbeat_run_watchdog_decisions_created_by_agent_id_agents_id_fk": { + "name": "heartbeat_run_watchdog_decisions_created_by_agent_id_agents_id_fk", + "tableFrom": "heartbeat_run_watchdog_decisions", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "heartbeat_run_watchdog_decisions_created_by_run_id_heartbeat_runs_id_fk": { + "name": "heartbeat_run_watchdog_decisions_created_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "heartbeat_run_watchdog_decisions", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "created_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.heartbeat_runs": { + "name": "heartbeat_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "invocation_source": { + "name": "invocation_source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'on_demand'" + }, + "trigger_detail": { + "name": "trigger_detail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "responsible_user_id": { + "name": "responsible_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_identity_context_id": { + "name": "active_identity_context_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "execution_control_deadline_at": { + "name": "execution_control_deadline_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "execution_status_delivery_id": { + "name": "execution_status_delivery_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "wakeup_request_id": { + "name": "wakeup_request_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "exit_code": { + "name": "exit_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "signal": { + "name": "signal", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cost_accounting_pending": { + "name": "cost_accounting_pending", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cost_accounted_at": { + "name": "cost_accounted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "accounting_projection_version": { + "name": "accounting_projection_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accounting_last_attempt_at": { + "name": "accounting_last_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "accounting_last_error": { + "name": "accounting_last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accounting_attempt_count": { + "name": "accounting_attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "usage_json": { + "name": "usage_json", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "result_json": { + "name": "result_json", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "runtime_mode": { + "name": "runtime_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'legacy'" + }, + "runtime_mode_resolver_version": { + "name": "runtime_mode_resolver_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "runtime_mode_reason": { + "name": "runtime_mode_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "runtime_mode_resolved_at": { + "name": "runtime_mode_resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "runner_profile_json": { + "name": "runner_profile_json", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "runner_instance_id": { + "name": "runner_instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "native_session_id": { + "name": "native_session_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "native_issue_id": { + "name": "native_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "driver_kind": { + "name": "driver_kind", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "driver_version": { + "name": "driver_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completion_contract_id": { + "name": "completion_contract_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "completion_contract_sha256": { + "name": "completion_contract_sha256", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_event_seq": { + "name": "next_event_seq", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "native_phase": { + "name": "native_phase", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "native_phase_updated_at": { + "name": "native_phase_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "session_id_before": { + "name": "session_id_before", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "session_id_after": { + "name": "session_id_after", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "log_store": { + "name": "log_store", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "log_ref": { + "name": "log_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "log_bytes": { + "name": "log_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "log_sha256": { + "name": "log_sha256", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "log_compressed": { + "name": "log_compressed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "stdout_excerpt": { + "name": "stdout_excerpt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stderr_excerpt": { + "name": "stderr_excerpt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_run_id": { + "name": "external_run_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "controller_boot_id": { + "name": "controller_boot_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "controller_lease_expires_at": { + "name": "controller_lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "execution_stage": { + "name": "execution_stage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "process_pid": { + "name": "process_pid", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "process_group_id": { + "name": "process_group_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "process_started_at": { + "name": "process_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_output_at": { + "name": "last_output_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_output_seq": { + "name": "last_output_seq", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_output_stream": { + "name": "last_output_stream", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_output_bytes": { + "name": "last_output_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "retry_of_run_id": { + "name": "retry_of_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "process_loss_retry_count": { + "name": "process_loss_retry_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "scheduled_retry_at": { + "name": "scheduled_retry_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "scheduled_retry_attempt": { + "name": "scheduled_retry_attempt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "scheduled_retry_reason": { + "name": "scheduled_retry_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "issue_comment_status": { + "name": "issue_comment_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'not_applicable'" + }, + "issue_comment_satisfied_by_comment_id": { + "name": "issue_comment_satisfied_by_comment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issue_comment_retry_queued_at": { + "name": "issue_comment_retry_queued_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "liveness_state": { + "name": "liveness_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "liveness_reason": { + "name": "liveness_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "continuation_attempt": { + "name": "continuation_attempt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_useful_action_at": { + "name": "last_useful_action_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "next_action": { + "name": "next_action", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "context_snapshot": { + "name": "context_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "heartbeat_runs_cost_accounting_pending_idx": { + "name": "heartbeat_runs_cost_accounting_pending_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"heartbeat_runs\".\"cost_accounting_pending\" = true", + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_runs_execution_status_delivery_idx": { + "name": "heartbeat_runs_execution_status_delivery_idx", + "columns": [ + { + "expression": "execution_status_delivery_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"heartbeat_runs\".\"execution_status_delivery_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_runs_execution_control_deadline_idx": { + "name": "heartbeat_runs_execution_control_deadline_idx", + "columns": [ + { + "expression": "execution_control_deadline_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"heartbeat_runs\".\"execution_control_deadline_at\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_runs_native_replacement_predecessor_uq": { + "name": "heartbeat_runs_native_replacement_predecessor_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "retry_of_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"heartbeat_runs\".\"scheduled_retry_reason\" = 'native_safe_replacement'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_runs_company_agent_started_idx": { + "name": "heartbeat_runs_company_agent_started_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_runs_company_responsible_user_idx": { + "name": "heartbeat_runs_company_responsible_user_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "responsible_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_runs_company_liveness_idx": { + "name": "heartbeat_runs_company_liveness_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "liveness_state", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_runs_company_status_last_output_idx": { + "name": "heartbeat_runs_company_status_last_output_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_output_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_runs_company_status_process_started_idx": { + "name": "heartbeat_runs_company_status_process_started_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "process_started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_runs_company_created_at_desc_idx": { + "name": "heartbeat_runs_company_created_at_desc_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_runs_company_ctx_issue_created_idx": { + "name": "heartbeat_runs_company_ctx_issue_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "(\"context_snapshot\" ->> 'issueId')", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_runs_company_ctx_task_created_idx": { + "name": "heartbeat_runs_company_ctx_task_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "(\"context_snapshot\" ->> 'taskId')", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_runs_company_ctx_taskkey_created_idx": { + "name": "heartbeat_runs_company_ctx_taskkey_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "(\"context_snapshot\" ->> 'taskKey')", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "heartbeat_runs_company_id_companies_id_fk": { + "name": "heartbeat_runs_company_id_companies_id_fk", + "tableFrom": "heartbeat_runs", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "heartbeat_runs_agent_id_agents_id_fk": { + "name": "heartbeat_runs_agent_id_agents_id_fk", + "tableFrom": "heartbeat_runs", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "heartbeat_runs_wakeup_request_id_agent_wakeup_requests_id_fk": { + "name": "heartbeat_runs_wakeup_request_id_agent_wakeup_requests_id_fk", + "tableFrom": "heartbeat_runs", + "tableTo": "agent_wakeup_requests", + "columnsFrom": [ + "wakeup_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "heartbeat_runs_retry_of_run_id_heartbeat_runs_id_fk": { + "name": "heartbeat_runs_retry_of_run_id_heartbeat_runs_id_fk", + "tableFrom": "heartbeat_runs", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "retry_of_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "heartbeat_runs_company_native_issue_id_uq": { + "name": "heartbeat_runs_company_native_issue_id_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "native_issue_id", + "id" + ] + }, + "heartbeat_runs_company_native_issue_contract_id_uq": { + "name": "heartbeat_runs_company_native_issue_contract_id_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "native_issue_id", + "id", + "completion_contract_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inbox_dismissals": { + "name": "inbox_dismissals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "item_key": { + "name": "item_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'dismiss'" + }, + "dismissed_at": { + "name": "dismissed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "snoozed_until": { + "name": "snoozed_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "inbox_dismissals_company_user_idx": { + "name": "inbox_dismissals_company_user_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inbox_dismissals_company_item_idx": { + "name": "inbox_dismissals_company_item_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "item_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inbox_dismissals_company_user_item_idx": { + "name": "inbox_dismissals_company_user_item_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "item_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "inbox_dismissals_company_id_companies_id_fk": { + "name": "inbox_dismissals_company_id_companies_id_fk", + "tableFrom": "inbox_dismissals", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.connection_grant_delegations": { + "name": "connection_grant_delegations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "grant_id": { + "name": "grant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "connection_grant_delegations_company_agent_idx": { + "name": "connection_grant_delegations_company_agent_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "connection_grant_delegations_grant_agent_uq": { + "name": "connection_grant_delegations_grant_agent_uq", + "columns": [ + { + "expression": "grant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "connection_grant_delegations_company_id_companies_id_fk": { + "name": "connection_grant_delegations_company_id_companies_id_fk", + "tableFrom": "connection_grant_delegations", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "connection_grant_delegations_agent_id_agents_id_fk": { + "name": "connection_grant_delegations_agent_id_agents_id_fk", + "tableFrom": "connection_grant_delegations", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "connection_grant_delegations_company_grant_fk": { + "name": "connection_grant_delegations_company_grant_fk", + "tableFrom": "connection_grant_delegations", + "tableTo": "connection_grants", + "columnsFrom": [ + "company_id", + "grant_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.connection_grant_members": { + "name": "connection_grant_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "grant_id": { + "name": "grant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subject_type": { + "name": "subject_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subject_id": { + "name": "subject_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "connection_grant_members_company_subject_idx": { + "name": "connection_grant_members_company_subject_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subject_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subject_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "connection_grant_members_grant_subject_uq": { + "name": "connection_grant_members_grant_subject_uq", + "columns": [ + { + "expression": "grant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subject_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subject_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "connection_grant_members_company_id_companies_id_fk": { + "name": "connection_grant_members_company_id_companies_id_fk", + "tableFrom": "connection_grant_members", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "connection_grant_members_company_grant_fk": { + "name": "connection_grant_members_company_grant_fk", + "tableFrom": "connection_grant_members", + "tableTo": "connection_grants", + "columnsFrom": [ + "company_id", + "grant_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "connection_grant_members_subject_type_check": { + "name": "connection_grant_members_subject_type_check", + "value": "\"connection_grant_members\".\"subject_type\" in ('user')" + } + }, + "isRLSEnabled": false + }, + "public.connection_grants": { + "name": "connection_grants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subject_user_id": { + "name": "subject_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subject_agent_id": { + "name": "subject_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider_tenant": { + "name": "provider_tenant", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "credential_secret_refs": { + "name": "credential_secret_refs", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "external_credential": { + "name": "external_credential", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_by_agent_id": { + "name": "revoked_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "revoked_by_user_id": { + "name": "revoked_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "connection_grants_company_connection_idx": { + "name": "connection_grants_company_connection_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "connection_grants_subject_user_idx": { + "name": "connection_grants_subject_user_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subject_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "connection_grants_subject_agent_idx": { + "name": "connection_grants_subject_agent_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subject_agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "connection_grants_user_uq": { + "name": "connection_grants_user_uq", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subject_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "connection_grants_agent_uq": { + "name": "connection_grants_agent_uq", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subject_agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "connection_grants_default_uq": { + "name": "connection_grants_default_uq", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"connection_grants\".\"is_default\" = true and \"connection_grants\".\"kind\" = 'organization'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "connection_grants_company_id_companies_id_fk": { + "name": "connection_grants_company_id_companies_id_fk", + "tableFrom": "connection_grants", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "connection_grants_subject_agent_id_agents_id_fk": { + "name": "connection_grants_subject_agent_id_agents_id_fk", + "tableFrom": "connection_grants", + "tableTo": "agents", + "columnsFrom": [ + "subject_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "connection_grants_created_by_agent_id_agents_id_fk": { + "name": "connection_grants_created_by_agent_id_agents_id_fk", + "tableFrom": "connection_grants", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "connection_grants_revoked_by_agent_id_agents_id_fk": { + "name": "connection_grants_revoked_by_agent_id_agents_id_fk", + "tableFrom": "connection_grants", + "tableTo": "agents", + "columnsFrom": [ + "revoked_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "connection_grants_company_connection_fk": { + "name": "connection_grants_company_connection_fk", + "tableFrom": "connection_grants", + "tableTo": "tool_connections", + "columnsFrom": [ + "company_id", + "connection_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "connection_grants_company_id_uq": { + "name": "connection_grants_company_id_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "connection_grants_kind_check": { + "name": "connection_grants_kind_check", + "value": "\"connection_grants\".\"kind\" in ('organization', 'user', 'agent')" + }, + "connection_grants_status_check": { + "name": "connection_grants_status_check", + "value": "\"connection_grants\".\"status\" in ('active', 'revoked', 'expired', 'needs_reauthorization')" + }, + "connection_grants_credential_source_one_of_check": { + "name": "connection_grants_credential_source_one_of_check", + "value": "\"connection_grants\".\"external_credential\" is null or jsonb_array_length(\"connection_grants\".\"credential_secret_refs\") = 0" + }, + "connection_grants_subject_check": { + "name": "connection_grants_subject_check", + "value": "(\"connection_grants\".\"kind\" = 'user' and \"connection_grants\".\"subject_user_id\" is not null and \"connection_grants\".\"subject_agent_id\" is null) or (\"connection_grants\".\"kind\" = 'agent' and \"connection_grants\".\"subject_agent_id\" is not null and \"connection_grants\".\"subject_user_id\" is null) or (\"connection_grants\".\"kind\" = 'organization' and \"connection_grants\".\"subject_user_id\" is null and \"connection_grants\".\"subject_agent_id\" is null)" + }, + "connection_grants_default_check": { + "name": "connection_grants_default_check", + "value": "\"connection_grants\".\"is_default\" = false or \"connection_grants\".\"kind\" = 'organization'" + } + }, + "isRLSEnabled": false + }, + "public.connection_token_issuances": { + "name": "connection_token_issuances", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "application_id": { + "name": "application_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "responsible_user_id": { + "name": "responsible_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_scope": { + "name": "requested_scope", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "issued_scope": { + "name": "issued_scope", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "ttl_seconds": { + "name": "ttl_seconds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "connection_token_issuances_company_created_idx": { + "name": "connection_token_issuances_company_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "connection_token_issuances_connection_created_idx": { + "name": "connection_token_issuances_connection_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "connection_token_issuances_agent_connection_idx": { + "name": "connection_token_issuances_agent_connection_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "connection_token_issuances_run_idx": { + "name": "connection_token_issuances_run_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "connection_token_issuances_company_id_companies_id_fk": { + "name": "connection_token_issuances_company_id_companies_id_fk", + "tableFrom": "connection_token_issuances", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "connection_token_issuances_application_id_tool_applications_id_fk": { + "name": "connection_token_issuances_application_id_tool_applications_id_fk", + "tableFrom": "connection_token_issuances", + "tableTo": "tool_applications", + "columnsFrom": [ + "application_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "connection_token_issuances_connection_id_tool_connections_id_fk": { + "name": "connection_token_issuances_connection_id_tool_connections_id_fk", + "tableFrom": "connection_token_issuances", + "tableTo": "tool_connections", + "columnsFrom": [ + "connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "connection_token_issuances_agent_id_agents_id_fk": { + "name": "connection_token_issuances_agent_id_agents_id_fk", + "tableFrom": "connection_token_issuances", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "connection_token_issuances_run_id_heartbeat_runs_id_fk": { + "name": "connection_token_issuances_run_id_heartbeat_runs_id_fk", + "tableFrom": "connection_token_issuances", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "connection_token_issuances_issue_id_issues_id_fk": { + "name": "connection_token_issuances_issue_id_issues_id_fk", + "tableFrom": "connection_token_issuances", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "connection_token_issuances_project_id_projects_id_fk": { + "name": "connection_token_issuances_project_id_projects_id_fk", + "tableFrom": "connection_token_issuances", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.instance_settings": { + "name": "instance_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "singleton_key": { + "name": "singleton_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'default'" + }, + "default_environment_id": { + "name": "default_environment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "general": { + "name": "general", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "experimental": { + "name": "experimental", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "instance_settings_singleton_key_idx": { + "name": "instance_settings_singleton_key_idx", + "columns": [ + { + "expression": "singleton_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "instance_settings_default_environment_id_environments_id_fk": { + "name": "instance_settings_default_environment_id_environments_id_fk", + "tableFrom": "instance_settings", + "tableTo": "environments", + "columnsFrom": [ + "default_environment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.instance_user_roles": { + "name": "instance_user_roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'instance_admin'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "instance_user_roles_user_role_unique_idx": { + "name": "instance_user_roles_user_role_unique_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "role", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "instance_user_roles_role_idx": { + "name": "instance_user_roles_role_idx", + "columns": [ + { + "expression": "role", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invites": { + "name": "invites", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "invite_type": { + "name": "invite_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'company_join'" + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "allowed_join_types": { + "name": "allowed_join_types", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'both'" + }, + "defaults_payload": { + "name": "defaults_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "invited_by_user_id": { + "name": "invited_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "accepted_at": { + "name": "accepted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "invites_token_hash_unique_idx": { + "name": "invites_token_hash_unique_idx", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "invites_company_invite_state_idx": { + "name": "invites_company_invite_state_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "invite_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "revoked_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "invites_company_id_companies_id_fk": { + "name": "invites_company_id_companies_id_fk", + "tableFrom": "invites", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_approvals": { + "name": "issue_approvals", + "schema": "", + "columns": { + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "linked_by_agent_id": { + "name": "linked_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "linked_by_user_id": { + "name": "linked_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_approvals_issue_idx": { + "name": "issue_approvals_issue_idx", + "columns": [ + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_approvals_approval_idx": { + "name": "issue_approvals_approval_idx", + "columns": [ + { + "expression": "approval_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_approvals_company_idx": { + "name": "issue_approvals_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_approvals_company_id_companies_id_fk": { + "name": "issue_approvals_company_id_companies_id_fk", + "tableFrom": "issue_approvals", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_approvals_issue_id_issues_id_fk": { + "name": "issue_approvals_issue_id_issues_id_fk", + "tableFrom": "issue_approvals", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_approvals_approval_id_approvals_id_fk": { + "name": "issue_approvals_approval_id_approvals_id_fk", + "tableFrom": "issue_approvals", + "tableTo": "approvals", + "columnsFrom": [ + "approval_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_approvals_linked_by_agent_id_agents_id_fk": { + "name": "issue_approvals_linked_by_agent_id_agents_id_fk", + "tableFrom": "issue_approvals", + "tableTo": "agents", + "columnsFrom": [ + "linked_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "issue_approvals_pk": { + "name": "issue_approvals_pk", + "columns": [ + "issue_id", + "approval_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_attachments": { + "name": "issue_attachments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "asset_id": { + "name": "asset_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_comment_id": { + "name": "issue_comment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "originating_run_id": { + "name": "originating_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_attachments_company_issue_idx": { + "name": "issue_attachments_company_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_attachments_issue_comment_idx": { + "name": "issue_attachments_issue_comment_idx", + "columns": [ + { + "expression": "issue_comment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_attachments_originating_run_idx": { + "name": "issue_attachments_originating_run_idx", + "columns": [ + { + "expression": "originating_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_attachments_asset_uq": { + "name": "issue_attachments_asset_uq", + "columns": [ + { + "expression": "asset_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_attachments_company_id_companies_id_fk": { + "name": "issue_attachments_company_id_companies_id_fk", + "tableFrom": "issue_attachments", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_attachments_issue_id_issues_id_fk": { + "name": "issue_attachments_issue_id_issues_id_fk", + "tableFrom": "issue_attachments", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_attachments_asset_id_assets_id_fk": { + "name": "issue_attachments_asset_id_assets_id_fk", + "tableFrom": "issue_attachments", + "tableTo": "assets", + "columnsFrom": [ + "asset_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_attachments_issue_comment_id_issue_comments_id_fk": { + "name": "issue_attachments_issue_comment_id_issue_comments_id_fk", + "tableFrom": "issue_attachments", + "tableTo": "issue_comments", + "columnsFrom": [ + "issue_comment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_attachments_originating_run_id_heartbeat_runs_id_fk": { + "name": "issue_attachments_originating_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_attachments", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "originating_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_comments": { + "name": "issue_comments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_agent_id": { + "name": "author_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "author_user_id": { + "name": "author_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "on_behalf_of_user_id": { + "name": "on_behalf_of_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "author_type": { + "name": "author_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_run_id": { + "name": "created_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "derived_author_agent_id": { + "name": "derived_author_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "derived_created_by_run_id": { + "name": "derived_created_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "derived_author_source": { + "name": "derived_author_source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_request_id": { + "name": "client_request_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "conversation_session_generation": { + "name": "conversation_session_generation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "presentation": { + "name": "presentation", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_by_type": { + "name": "deleted_by_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted_by_agent_id": { + "name": "deleted_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "deleted_by_user_id": { + "name": "deleted_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted_by_run_id": { + "name": "deleted_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source_trust": { + "name": "source_trust", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_comments_issue_idx": { + "name": "issue_comments_issue_idx", + "columns": [ + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_comments_company_idx": { + "name": "issue_comments_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_comments_company_issue_created_at_idx": { + "name": "issue_comments_company_issue_created_at_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_comments_company_author_issue_created_at_idx": { + "name": "issue_comments_company_author_issue_created_at_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "author_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_comments_body_search_idx": { + "name": "issue_comments_body_search_idx", + "columns": [ + { + "expression": "body", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "issue_comments_company_id_companies_id_fk": { + "name": "issue_comments_company_id_companies_id_fk", + "tableFrom": "issue_comments", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_comments_issue_id_issues_id_fk": { + "name": "issue_comments_issue_id_issues_id_fk", + "tableFrom": "issue_comments", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_comments_author_agent_id_agents_id_fk": { + "name": "issue_comments_author_agent_id_agents_id_fk", + "tableFrom": "issue_comments", + "tableTo": "agents", + "columnsFrom": [ + "author_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_comments_on_behalf_of_user_id_user_id_fk": { + "name": "issue_comments_on_behalf_of_user_id_user_id_fk", + "tableFrom": "issue_comments", + "tableTo": "user", + "columnsFrom": [ + "on_behalf_of_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_comments_created_by_run_id_heartbeat_runs_id_fk": { + "name": "issue_comments_created_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_comments", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "created_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_comments_derived_author_agent_id_agents_id_fk": { + "name": "issue_comments_derived_author_agent_id_agents_id_fk", + "tableFrom": "issue_comments", + "tableTo": "agents", + "columnsFrom": [ + "derived_author_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_comments_derived_created_by_run_id_heartbeat_runs_id_fk": { + "name": "issue_comments_derived_created_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_comments", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "derived_created_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_comments_deleted_by_agent_id_agents_id_fk": { + "name": "issue_comments_deleted_by_agent_id_agents_id_fk", + "tableFrom": "issue_comments", + "tableTo": "agents", + "columnsFrom": [ + "deleted_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_comments_deleted_by_run_id_heartbeat_runs_id_fk": { + "name": "issue_comments_deleted_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_comments", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "deleted_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "issue_comments_client_request_uq": { + "name": "issue_comments_client_request_uq", + "nullsNotDistinct": false, + "columns": [ + "issue_id", + "author_user_id", + "client_request_id" + ] + }, + "issue_comments_company_id_uq": { + "name": "issue_comments_company_id_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_create_idempotency_keys": { + "name": "issue_create_idempotency_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_create_idempotency_keys_company_key_uq": { + "name": "issue_create_idempotency_keys_company_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_create_idempotency_keys_issue_idx": { + "name": "issue_create_idempotency_keys_issue_idx", + "columns": [ + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_create_idempotency_keys_company_created_at_idx": { + "name": "issue_create_idempotency_keys_company_created_at_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_create_idempotency_keys_company_id_companies_id_fk": { + "name": "issue_create_idempotency_keys_company_id_companies_id_fk", + "tableFrom": "issue_create_idempotency_keys", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_create_idempotency_keys_issue_id_issues_id_fk": { + "name": "issue_create_idempotency_keys_issue_id_issues_id_fk", + "tableFrom": "issue_create_idempotency_keys", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_documents": { + "name": "issue_documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_documents_company_issue_key_uq": { + "name": "issue_documents_company_issue_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_documents_document_uq": { + "name": "issue_documents_document_uq", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_documents_company_issue_updated_idx": { + "name": "issue_documents_company_issue_updated_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_documents_company_id_companies_id_fk": { + "name": "issue_documents_company_id_companies_id_fk", + "tableFrom": "issue_documents", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_documents_issue_id_issues_id_fk": { + "name": "issue_documents_issue_id_issues_id_fk", + "tableFrom": "issue_documents", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_documents_document_id_documents_id_fk": { + "name": "issue_documents_document_id_documents_id_fk", + "tableFrom": "issue_documents", + "tableTo": "documents", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_execution_decisions": { + "name": "issue_execution_decisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "stage_id": { + "name": "stage_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "stage_type": { + "name": "stage_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_agent_id": { + "name": "actor_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_run_id": { + "name": "created_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_execution_decisions_company_issue_idx": { + "name": "issue_execution_decisions_company_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_execution_decisions_stage_idx": { + "name": "issue_execution_decisions_stage_idx", + "columns": [ + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "stage_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_execution_decisions_company_id_companies_id_fk": { + "name": "issue_execution_decisions_company_id_companies_id_fk", + "tableFrom": "issue_execution_decisions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_execution_decisions_issue_id_issues_id_fk": { + "name": "issue_execution_decisions_issue_id_issues_id_fk", + "tableFrom": "issue_execution_decisions", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_execution_decisions_actor_agent_id_agents_id_fk": { + "name": "issue_execution_decisions_actor_agent_id_agents_id_fk", + "tableFrom": "issue_execution_decisions", + "tableTo": "agents", + "columnsFrom": [ + "actor_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_execution_decisions_created_by_run_id_heartbeat_runs_id_fk": { + "name": "issue_execution_decisions_created_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_execution_decisions", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "created_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_inbox_archives": { + "name": "issue_inbox_archives", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "archived_by_actor_type": { + "name": "archived_by_actor_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "archived_by_agent_id": { + "name": "archived_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "archived_by_run_id": { + "name": "archived_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_inbox_archives_company_issue_idx": { + "name": "issue_inbox_archives_company_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_inbox_archives_company_user_idx": { + "name": "issue_inbox_archives_company_user_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_inbox_archives_company_issue_user_idx": { + "name": "issue_inbox_archives_company_issue_user_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_inbox_archives_company_id_companies_id_fk": { + "name": "issue_inbox_archives_company_id_companies_id_fk", + "tableFrom": "issue_inbox_archives", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_inbox_archives_issue_id_issues_id_fk": { + "name": "issue_inbox_archives_issue_id_issues_id_fk", + "tableFrom": "issue_inbox_archives", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_inbox_archives_archived_by_agent_id_agents_id_fk": { + "name": "issue_inbox_archives_archived_by_agent_id_agents_id_fk", + "tableFrom": "issue_inbox_archives", + "tableTo": "agents", + "columnsFrom": [ + "archived_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_inbox_archives_archived_by_run_id_heartbeat_runs_id_fk": { + "name": "issue_inbox_archives_archived_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_inbox_archives", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "archived_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "issue_inbox_archives_archived_by_actor_type_check": { + "name": "issue_inbox_archives_archived_by_actor_type_check", + "value": "\"issue_inbox_archives\".\"archived_by_actor_type\" in ('user', 'agent')" + } + }, + "isRLSEnabled": false + }, + "public.issue_labels": { + "name": "issue_labels", + "schema": "", + "columns": { + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label_id": { + "name": "label_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_labels_issue_idx": { + "name": "issue_labels_issue_idx", + "columns": [ + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_labels_label_idx": { + "name": "issue_labels_label_idx", + "columns": [ + { + "expression": "label_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_labels_company_idx": { + "name": "issue_labels_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_labels_issue_id_issues_id_fk": { + "name": "issue_labels_issue_id_issues_id_fk", + "tableFrom": "issue_labels", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_labels_label_id_labels_id_fk": { + "name": "issue_labels_label_id_labels_id_fk", + "tableFrom": "issue_labels", + "tableTo": "labels", + "columnsFrom": [ + "label_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_labels_company_id_companies_id_fk": { + "name": "issue_labels_company_id_companies_id_fk", + "tableFrom": "issue_labels", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "issue_labels_pk": { + "name": "issue_labels_pk", + "columns": [ + "issue_id", + "label_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_plan_decompositions": { + "name": "issue_plan_decompositions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_issue_id": { + "name": "source_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "accepted_plan_revision_id": { + "name": "accepted_plan_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "accepted_interaction_id": { + "name": "accepted_interaction_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'in_flight'" + }, + "request_fingerprint": { + "name": "request_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_child_count": { + "name": "requested_child_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "requested_children": { + "name": "requested_children", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "child_issue_ids": { + "name": "child_issue_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "owner_agent_id": { + "name": "owner_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owner_user_id": { + "name": "owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_run_id": { + "name": "owner_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_plan_decompositions_company_source_status_idx": { + "name": "issue_plan_decompositions_company_source_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_plan_decompositions_active_owner_idx": { + "name": "issue_plan_decompositions_active_owner_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner_agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"issue_plan_decompositions\".\"status\" = 'in_flight'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_plan_decompositions_source_revision_uq": { + "name": "issue_plan_decompositions_source_revision_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "accepted_plan_revision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_plan_decompositions_company_id_companies_id_fk": { + "name": "issue_plan_decompositions_company_id_companies_id_fk", + "tableFrom": "issue_plan_decompositions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_plan_decompositions_source_issue_id_issues_id_fk": { + "name": "issue_plan_decompositions_source_issue_id_issues_id_fk", + "tableFrom": "issue_plan_decompositions", + "tableTo": "issues", + "columnsFrom": [ + "source_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_plan_decompositions_accepted_plan_revision_id_document_revisions_id_fk": { + "name": "issue_plan_decompositions_accepted_plan_revision_id_document_revisions_id_fk", + "tableFrom": "issue_plan_decompositions", + "tableTo": "document_revisions", + "columnsFrom": [ + "accepted_plan_revision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_plan_decompositions_accepted_interaction_id_issue_thread_interactions_id_fk": { + "name": "issue_plan_decompositions_accepted_interaction_id_issue_thread_interactions_id_fk", + "tableFrom": "issue_plan_decompositions", + "tableTo": "issue_thread_interactions", + "columnsFrom": [ + "accepted_interaction_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_plan_decompositions_owner_agent_id_agents_id_fk": { + "name": "issue_plan_decompositions_owner_agent_id_agents_id_fk", + "tableFrom": "issue_plan_decompositions", + "tableTo": "agents", + "columnsFrom": [ + "owner_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_plan_decompositions_owner_run_id_heartbeat_runs_id_fk": { + "name": "issue_plan_decompositions_owner_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_plan_decompositions", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "owner_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_question_response_deliveries": { + "name": "issue_question_response_deliveries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "interaction_id": { + "name": "interaction_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_run_id": { + "name": "source_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "target_run_id": { + "name": "target_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "target_turn_id": { + "name": "target_turn_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "correlation_id": { + "name": "correlation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload_sha256": { + "name": "payload_sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "delivery_mode": { + "name": "delivery_mode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "error_count": { + "name": "error_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_attempt_at": { + "name": "last_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "acknowledged_at": { + "name": "acknowledged_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_error_code": { + "name": "last_error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_question_response_deliveries_interaction_uq": { + "name": "issue_question_response_deliveries_interaction_uq", + "columns": [ + { + "expression": "interaction_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_question_response_deliveries_correlation_uq": { + "name": "issue_question_response_deliveries_correlation_uq", + "columns": [ + { + "expression": "correlation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_question_response_deliveries_pending_idx": { + "name": "issue_question_response_deliveries_pending_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_question_response_deliveries_company_issue_idx": { + "name": "issue_question_response_deliveries_company_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_question_response_deliveries_company_id_companies_id_fk": { + "name": "issue_question_response_deliveries_company_id_companies_id_fk", + "tableFrom": "issue_question_response_deliveries", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_question_response_deliveries_issue_id_issues_id_fk": { + "name": "issue_question_response_deliveries_issue_id_issues_id_fk", + "tableFrom": "issue_question_response_deliveries", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_question_response_deliveries_interaction_id_issue_thread_interactions_id_fk": { + "name": "issue_question_response_deliveries_interaction_id_issue_thread_interactions_id_fk", + "tableFrom": "issue_question_response_deliveries", + "tableTo": "issue_thread_interactions", + "columnsFrom": [ + "interaction_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_question_response_deliveries_source_run_id_heartbeat_runs_id_fk": { + "name": "issue_question_response_deliveries_source_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_question_response_deliveries", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "source_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_question_response_deliveries_target_run_id_heartbeat_runs_id_fk": { + "name": "issue_question_response_deliveries_target_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_question_response_deliveries", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "target_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "issue_question_response_deliveries_status_check": { + "name": "issue_question_response_deliveries_status_check", + "value": "\"issue_question_response_deliveries\".\"status\" IN ('pending', 'delivering', 'delivered', 'fallback_queued', 'failed')" + }, + "issue_question_response_deliveries_mode_check": { + "name": "issue_question_response_deliveries_mode_check", + "value": "\"issue_question_response_deliveries\".\"delivery_mode\" IS NULL OR \"issue_question_response_deliveries\".\"delivery_mode\" IN ('steered', 'coalesced', 'wake_fallback')" + } + }, + "isRLSEnabled": false + }, + "public.issue_read_states": { + "name": "issue_read_states", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_read_at": { + "name": "last_read_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_read_states_company_issue_idx": { + "name": "issue_read_states_company_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_read_states_company_user_idx": { + "name": "issue_read_states_company_user_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_read_states_company_issue_user_idx": { + "name": "issue_read_states_company_issue_user_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_read_states_company_id_companies_id_fk": { + "name": "issue_read_states_company_id_companies_id_fk", + "tableFrom": "issue_read_states", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_read_states_issue_id_issues_id_fk": { + "name": "issue_read_states_issue_id_issues_id_fk", + "tableFrom": "issue_read_states", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_recovery_actions": { + "name": "issue_recovery_actions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_issue_id": { + "name": "source_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recovery_issue_id": { + "name": "recovery_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "owner_type": { + "name": "owner_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'agent'" + }, + "owner_agent_id": { + "name": "owner_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owner_user_id": { + "name": "owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previous_owner_agent_id": { + "name": "previous_owner_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "return_owner_agent_id": { + "name": "return_owner_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "cause": { + "name": "cause", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fingerprint": { + "name": "fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "evidence": { + "name": "evidence", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "next_action": { + "name": "next_action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "wake_policy": { + "name": "wake_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "monitor_policy": { + "name": "monitor_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "max_attempts": { + "name": "max_attempts", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "timeout_at": { + "name": "timeout_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_attempt_at": { + "name": "last_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resolution_note": { + "name": "resolution_note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_recovery_actions_company_source_status_idx": { + "name": "issue_recovery_actions_company_source_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_recovery_actions_company_owner_status_idx": { + "name": "issue_recovery_actions_company_owner_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner_agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_recovery_actions_company_recovery_issue_idx": { + "name": "issue_recovery_actions_company_recovery_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "recovery_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_recovery_actions_active_source_uq": { + "name": "issue_recovery_actions_active_source_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issue_recovery_actions\".\"status\" in ('active', 'escalated')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_recovery_actions_active_fingerprint_uq": { + "name": "issue_recovery_actions_active_fingerprint_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cause", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issue_recovery_actions\".\"status\" in ('active', 'escalated')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_recovery_actions_company_id_companies_id_fk": { + "name": "issue_recovery_actions_company_id_companies_id_fk", + "tableFrom": "issue_recovery_actions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_recovery_actions_source_issue_id_issues_id_fk": { + "name": "issue_recovery_actions_source_issue_id_issues_id_fk", + "tableFrom": "issue_recovery_actions", + "tableTo": "issues", + "columnsFrom": [ + "source_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_recovery_actions_recovery_issue_id_issues_id_fk": { + "name": "issue_recovery_actions_recovery_issue_id_issues_id_fk", + "tableFrom": "issue_recovery_actions", + "tableTo": "issues", + "columnsFrom": [ + "recovery_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_recovery_actions_owner_agent_id_agents_id_fk": { + "name": "issue_recovery_actions_owner_agent_id_agents_id_fk", + "tableFrom": "issue_recovery_actions", + "tableTo": "agents", + "columnsFrom": [ + "owner_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_recovery_actions_previous_owner_agent_id_agents_id_fk": { + "name": "issue_recovery_actions_previous_owner_agent_id_agents_id_fk", + "tableFrom": "issue_recovery_actions", + "tableTo": "agents", + "columnsFrom": [ + "previous_owner_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_recovery_actions_return_owner_agent_id_agents_id_fk": { + "name": "issue_recovery_actions_return_owner_agent_id_agents_id_fk", + "tableFrom": "issue_recovery_actions", + "tableTo": "agents", + "columnsFrom": [ + "return_owner_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_reference_mentions": { + "name": "issue_reference_mentions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_issue_id": { + "name": "source_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "target_issue_id": { + "name": "target_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_kind": { + "name": "source_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_record_id": { + "name": "source_record_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "document_key": { + "name": "document_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "matched_text": { + "name": "matched_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_reference_mentions_company_source_issue_idx": { + "name": "issue_reference_mentions_company_source_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_reference_mentions_company_target_issue_idx": { + "name": "issue_reference_mentions_company_target_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_reference_mentions_company_issue_pair_idx": { + "name": "issue_reference_mentions_company_issue_pair_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_reference_mentions_company_source_mention_record_uq": { + "name": "issue_reference_mentions_company_source_mention_record_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_record_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issue_reference_mentions\".\"source_record_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_reference_mentions_company_source_mention_null_record_uq": { + "name": "issue_reference_mentions_company_source_mention_null_record_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issue_reference_mentions\".\"source_record_id\" is null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_reference_mentions_company_id_companies_id_fk": { + "name": "issue_reference_mentions_company_id_companies_id_fk", + "tableFrom": "issue_reference_mentions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_reference_mentions_source_issue_id_issues_id_fk": { + "name": "issue_reference_mentions_source_issue_id_issues_id_fk", + "tableFrom": "issue_reference_mentions", + "tableTo": "issues", + "columnsFrom": [ + "source_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_reference_mentions_target_issue_id_issues_id_fk": { + "name": "issue_reference_mentions_target_issue_id_issues_id_fk", + "tableFrom": "issue_reference_mentions", + "tableTo": "issues", + "columnsFrom": [ + "target_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_relations": { + "name": "issue_relations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "related_issue_id": { + "name": "related_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_relations_company_issue_idx": { + "name": "issue_relations_company_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_relations_company_related_issue_idx": { + "name": "issue_relations_company_related_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "related_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_relations_company_type_idx": { + "name": "issue_relations_company_type_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_relations_company_edge_uq": { + "name": "issue_relations_company_edge_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "related_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_relations_company_id_companies_id_fk": { + "name": "issue_relations_company_id_companies_id_fk", + "tableFrom": "issue_relations", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_relations_issue_id_issues_id_fk": { + "name": "issue_relations_issue_id_issues_id_fk", + "tableFrom": "issue_relations", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_relations_related_issue_id_issues_id_fk": { + "name": "issue_relations_related_issue_id_issues_id_fk", + "tableFrom": "issue_relations", + "tableTo": "issues", + "columnsFrom": [ + "related_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_relations_created_by_agent_id_agents_id_fk": { + "name": "issue_relations_created_by_agent_id_agents_id_fk", + "tableFrom": "issue_relations", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_thread_interactions": { + "name": "issue_thread_interactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "continuation_policy": { + "name": "continuation_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'wake_assignee'" + }, + "requested_resolver_policy": { + "name": "requested_resolver_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'anyone'" + }, + "effective_resolver_policy": { + "name": "effective_resolver_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'anyone'" + }, + "resolver_policy_provenance": { + "name": "resolver_policy_provenance", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'inherited'" + }, + "effective_resolver_policy_source": { + "name": "effective_resolver_policy_source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'requested'" + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "origin_comment_ids": { + "name": "origin_comment_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "source_comment_id": { + "name": "source_comment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source_identity_context_id": { + "name": "source_identity_context_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source_run_id": { + "name": "source_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "addressee_agent_id": { + "name": "addressee_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "addressee_user_id": { + "name": "addressee_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resolved_by_agent_id": { + "name": "resolved_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "resolved_by_run_id": { + "name": "resolved_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "resolved_by_user_id": { + "name": "resolved_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_thread_interactions_issue_idx": { + "name": "issue_thread_interactions_issue_idx", + "columns": [ + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_thread_interactions_company_issue_created_at_idx": { + "name": "issue_thread_interactions_company_issue_created_at_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_thread_interactions_company_issue_status_idx": { + "name": "issue_thread_interactions_company_issue_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_thread_interactions_company_issue_idempotency_uq": { + "name": "issue_thread_interactions_company_issue_idempotency_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issue_thread_interactions\".\"idempotency_key\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_thread_interactions_source_comment_idx": { + "name": "issue_thread_interactions_source_comment_idx", + "columns": [ + { + "expression": "source_comment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_thread_interactions_addressee_agent_idx": { + "name": "issue_thread_interactions_addressee_agent_idx", + "columns": [ + { + "expression": "addressee_agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_thread_interactions_addressee_user_idx": { + "name": "issue_thread_interactions_addressee_user_idx", + "columns": [ + { + "expression": "addressee_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_thread_interactions_company_id_companies_id_fk": { + "name": "issue_thread_interactions_company_id_companies_id_fk", + "tableFrom": "issue_thread_interactions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_thread_interactions_issue_id_issues_id_fk": { + "name": "issue_thread_interactions_issue_id_issues_id_fk", + "tableFrom": "issue_thread_interactions", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_thread_interactions_source_comment_id_issue_comments_id_fk": { + "name": "issue_thread_interactions_source_comment_id_issue_comments_id_fk", + "tableFrom": "issue_thread_interactions", + "tableTo": "issue_comments", + "columnsFrom": [ + "source_comment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_thread_interactions_source_run_id_heartbeat_runs_id_fk": { + "name": "issue_thread_interactions_source_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_thread_interactions", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "source_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_thread_interactions_created_by_agent_id_agents_id_fk": { + "name": "issue_thread_interactions_created_by_agent_id_agents_id_fk", + "tableFrom": "issue_thread_interactions", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_thread_interactions_addressee_agent_id_agents_id_fk": { + "name": "issue_thread_interactions_addressee_agent_id_agents_id_fk", + "tableFrom": "issue_thread_interactions", + "tableTo": "agents", + "columnsFrom": [ + "addressee_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_thread_interactions_resolved_by_agent_id_agents_id_fk": { + "name": "issue_thread_interactions_resolved_by_agent_id_agents_id_fk", + "tableFrom": "issue_thread_interactions", + "tableTo": "agents", + "columnsFrom": [ + "resolved_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_thread_interactions_resolved_by_run_id_heartbeat_runs_id_fk": { + "name": "issue_thread_interactions_resolved_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_thread_interactions", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "resolved_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_tree_hold_members": { + "name": "issue_tree_hold_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "hold_id": { + "name": "hold_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "parent_issue_id": { + "name": "parent_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "depth": { + "name": "depth", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "issue_identifier": { + "name": "issue_identifier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "issue_title": { + "name": "issue_title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_status": { + "name": "issue_status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "assignee_agent_id": { + "name": "assignee_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "assignee_user_id": { + "name": "assignee_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_run_id": { + "name": "active_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "active_run_status": { + "name": "active_run_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "skipped": { + "name": "skipped", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "skip_reason": { + "name": "skip_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_tree_hold_members_hold_issue_uq": { + "name": "issue_tree_hold_members_hold_issue_uq", + "columns": [ + { + "expression": "hold_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_tree_hold_members_company_issue_idx": { + "name": "issue_tree_hold_members_company_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_tree_hold_members_hold_depth_idx": { + "name": "issue_tree_hold_members_hold_depth_idx", + "columns": [ + { + "expression": "hold_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "depth", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_tree_hold_members_company_id_companies_id_fk": { + "name": "issue_tree_hold_members_company_id_companies_id_fk", + "tableFrom": "issue_tree_hold_members", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_tree_hold_members_hold_id_issue_tree_holds_id_fk": { + "name": "issue_tree_hold_members_hold_id_issue_tree_holds_id_fk", + "tableFrom": "issue_tree_hold_members", + "tableTo": "issue_tree_holds", + "columnsFrom": [ + "hold_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_tree_hold_members_issue_id_issues_id_fk": { + "name": "issue_tree_hold_members_issue_id_issues_id_fk", + "tableFrom": "issue_tree_hold_members", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_tree_hold_members_parent_issue_id_issues_id_fk": { + "name": "issue_tree_hold_members_parent_issue_id_issues_id_fk", + "tableFrom": "issue_tree_hold_members", + "tableTo": "issues", + "columnsFrom": [ + "parent_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_tree_hold_members_assignee_agent_id_agents_id_fk": { + "name": "issue_tree_hold_members_assignee_agent_id_agents_id_fk", + "tableFrom": "issue_tree_hold_members", + "tableTo": "agents", + "columnsFrom": [ + "assignee_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_tree_hold_members_active_run_id_heartbeat_runs_id_fk": { + "name": "issue_tree_hold_members_active_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_tree_hold_members", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "active_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_tree_holds": { + "name": "issue_tree_holds", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "root_issue_id": { + "name": "root_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "release_policy": { + "name": "release_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_by_actor_type": { + "name": "created_by_actor_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'system'" + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_run_id": { + "name": "created_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "released_at": { + "name": "released_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "released_by_actor_type": { + "name": "released_by_actor_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "released_by_agent_id": { + "name": "released_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "released_by_user_id": { + "name": "released_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "released_by_run_id": { + "name": "released_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "release_reason": { + "name": "release_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "release_metadata": { + "name": "release_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_tree_holds_company_root_status_idx": { + "name": "issue_tree_holds_company_root_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "root_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_tree_holds_company_status_mode_idx": { + "name": "issue_tree_holds_company_status_mode_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "mode", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_tree_holds_company_id_companies_id_fk": { + "name": "issue_tree_holds_company_id_companies_id_fk", + "tableFrom": "issue_tree_holds", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_tree_holds_root_issue_id_issues_id_fk": { + "name": "issue_tree_holds_root_issue_id_issues_id_fk", + "tableFrom": "issue_tree_holds", + "tableTo": "issues", + "columnsFrom": [ + "root_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_tree_holds_created_by_agent_id_agents_id_fk": { + "name": "issue_tree_holds_created_by_agent_id_agents_id_fk", + "tableFrom": "issue_tree_holds", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_tree_holds_created_by_run_id_heartbeat_runs_id_fk": { + "name": "issue_tree_holds_created_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_tree_holds", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "created_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_tree_holds_released_by_agent_id_agents_id_fk": { + "name": "issue_tree_holds_released_by_agent_id_agents_id_fk", + "tableFrom": "issue_tree_holds", + "tableTo": "agents", + "columnsFrom": [ + "released_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_tree_holds_released_by_run_id_heartbeat_runs_id_fk": { + "name": "issue_tree_holds_released_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_tree_holds", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "released_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_watchdogs": { + "name": "issue_watchdogs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "watchdog_agent_id": { + "name": "watchdog_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "instructions": { + "name": "instructions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "watchdog_issue_id": { + "name": "watchdog_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "last_observed_fingerprint": { + "name": "last_observed_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_reviewed_fingerprint": { + "name": "last_reviewed_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_observed_stop_snapshot": { + "name": "last_observed_stop_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "last_reviewed_stop_snapshot": { + "name": "last_reviewed_stop_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "last_triggered_at": { + "name": "last_triggered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_completed_at": { + "name": "last_completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "trigger_count": { + "name": "trigger_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_run_id": { + "name": "created_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "updated_by_agent_id": { + "name": "updated_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "updated_by_user_id": { + "name": "updated_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_by_run_id": { + "name": "updated_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_watchdogs_company_issue_uq": { + "name": "issue_watchdogs_company_issue_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_watchdogs_company_status_idx": { + "name": "issue_watchdogs_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_watchdogs_company_agent_idx": { + "name": "issue_watchdogs_company_agent_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "watchdog_agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_watchdogs_company_watchdog_issue_uq": { + "name": "issue_watchdogs_company_watchdog_issue_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "watchdog_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issue_watchdogs\".\"watchdog_issue_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_watchdogs_company_id_companies_id_fk": { + "name": "issue_watchdogs_company_id_companies_id_fk", + "tableFrom": "issue_watchdogs", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_watchdogs_issue_id_issues_id_fk": { + "name": "issue_watchdogs_issue_id_issues_id_fk", + "tableFrom": "issue_watchdogs", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_watchdogs_watchdog_agent_id_agents_id_fk": { + "name": "issue_watchdogs_watchdog_agent_id_agents_id_fk", + "tableFrom": "issue_watchdogs", + "tableTo": "agents", + "columnsFrom": [ + "watchdog_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_watchdogs_watchdog_issue_id_issues_id_fk": { + "name": "issue_watchdogs_watchdog_issue_id_issues_id_fk", + "tableFrom": "issue_watchdogs", + "tableTo": "issues", + "columnsFrom": [ + "watchdog_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_watchdogs_created_by_agent_id_agents_id_fk": { + "name": "issue_watchdogs_created_by_agent_id_agents_id_fk", + "tableFrom": "issue_watchdogs", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_watchdogs_created_by_run_id_heartbeat_runs_id_fk": { + "name": "issue_watchdogs_created_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_watchdogs", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "created_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_watchdogs_updated_by_agent_id_agents_id_fk": { + "name": "issue_watchdogs_updated_by_agent_id_agents_id_fk", + "tableFrom": "issue_watchdogs", + "tableTo": "agents", + "columnsFrom": [ + "updated_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_watchdogs_updated_by_run_id_heartbeat_runs_id_fk": { + "name": "issue_watchdogs_updated_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_watchdogs", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "updated_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_work_products": { + "name": "issue_work_products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "execution_workspace_id": { + "name": "execution_workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "runtime_service_id": { + "name": "runtime_service_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "review_state": { + "name": "review_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "is_primary": { + "name": "is_primary", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "health_status": { + "name": "health_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "source_trust": { + "name": "source_trust", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_by_run_id": { + "name": "created_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_work_products_company_issue_type_idx": { + "name": "issue_work_products_company_issue_type_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_work_products_company_execution_workspace_type_idx": { + "name": "issue_work_products_company_execution_workspace_type_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_work_products_company_provider_external_id_idx": { + "name": "issue_work_products_company_provider_external_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_work_products_company_updated_idx": { + "name": "issue_work_products_company_updated_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_work_products_company_id_companies_id_fk": { + "name": "issue_work_products_company_id_companies_id_fk", + "tableFrom": "issue_work_products", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_work_products_project_id_projects_id_fk": { + "name": "issue_work_products_project_id_projects_id_fk", + "tableFrom": "issue_work_products", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_work_products_issue_id_issues_id_fk": { + "name": "issue_work_products_issue_id_issues_id_fk", + "tableFrom": "issue_work_products", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_work_products_execution_workspace_id_execution_workspaces_id_fk": { + "name": "issue_work_products_execution_workspace_id_execution_workspaces_id_fk", + "tableFrom": "issue_work_products", + "tableTo": "execution_workspaces", + "columnsFrom": [ + "execution_workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_work_products_runtime_service_id_workspace_runtime_services_id_fk": { + "name": "issue_work_products_runtime_service_id_workspace_runtime_services_id_fk", + "tableFrom": "issue_work_products", + "tableTo": "workspace_runtime_services", + "columnsFrom": [ + "runtime_service_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_work_products_created_by_run_id_heartbeat_runs_id_fk": { + "name": "issue_work_products_created_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_work_products", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "created_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issues": { + "name": "issues", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "conversation_agent_id": { + "name": "conversation_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "conversation_user_id": { + "name": "conversation_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "conversation_state": { + "name": "conversation_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "conversation_session_generation": { + "name": "conversation_session_generation", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "conversation_boundary_comment_id": { + "name": "conversation_boundary_comment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "project_workspace_id": { + "name": "project_workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "goal_id": { + "name": "goal_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title_needs_generation": { + "name": "title_needs_generation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'backlog'" + }, + "status_version": { + "name": "status_version", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_status_decision_id": { + "name": "last_status_decision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "work_mode": { + "name": "work_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'standard'" + }, + "harness_kind": { + "name": "harness_kind", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "priority": { + "name": "priority", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'medium'" + }, + "review_policy": { + "name": "review_policy", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assignee_agent_id": { + "name": "assignee_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "assignee_user_id": { + "name": "assignee_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "checkout_run_id": { + "name": "checkout_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "execution_run_id": { + "name": "execution_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "execution_agent_name_key": { + "name": "execution_agent_name_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "execution_locked_at": { + "name": "execution_locked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "responsible_user_id": { + "name": "responsible_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "issue_number": { + "name": "issue_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "origin_kind": { + "name": "origin_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "origin_id": { + "name": "origin_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "origin_run_id": { + "name": "origin_run_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "origin_identity_context_id": { + "name": "origin_identity_context_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "continuation_identity_context_id": { + "name": "continuation_identity_context_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "origin_fingerprint": { + "name": "origin_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'default'" + }, + "request_depth": { + "name": "request_depth", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "billing_code": { + "name": "billing_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assignee_adapter_overrides": { + "name": "assignee_adapter_overrides", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "execution_policy": { + "name": "execution_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "execution_state": { + "name": "execution_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "monitor_next_check_at": { + "name": "monitor_next_check_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "monitor_wake_requested_at": { + "name": "monitor_wake_requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "monitor_last_triggered_at": { + "name": "monitor_last_triggered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "monitor_attempt_count": { + "name": "monitor_attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "monitor_notes": { + "name": "monitor_notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "monitor_scheduled_by": { + "name": "monitor_scheduled_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "execution_workspace_id": { + "name": "execution_workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "execution_workspace_preference": { + "name": "execution_workspace_preference", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "execution_workspace_settings": { + "name": "execution_workspace_settings", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "source_trust": { + "name": "source_trust", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "unblock_descriptor": { + "name": "unblock_descriptor", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "blocked_transition_at": { + "name": "blocked_transition_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "blocked_owner_notified_at": { + "name": "blocked_owner_notified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "hidden_at": { + "name": "hidden_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issues_conversation_identity_idx": { + "name": "issues_conversation_identity_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "conversation_agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "conversation_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_company_status_idx": { + "name": "issues_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_company_harness_kind_idx": { + "name": "issues_company_harness_kind_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "harness_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_company_assignee_status_idx": { + "name": "issues_company_assignee_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "assignee_agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_company_assignee_user_status_idx": { + "name": "issues_company_assignee_user_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "assignee_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_company_responsible_user_idx": { + "name": "issues_company_responsible_user_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "responsible_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_company_parent_idx": { + "name": "issues_company_parent_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_company_project_idx": { + "name": "issues_company_project_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_company_origin_idx": { + "name": "issues_company_origin_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_company_project_workspace_idx": { + "name": "issues_company_project_workspace_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_company_execution_workspace_idx": { + "name": "issues_company_execution_workspace_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_company_monitor_due_idx": { + "name": "issues_company_monitor_due_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "monitor_next_check_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_company_updated_idx": { + "name": "issues_company_updated_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_company_created_idx": { + "name": "issues_company_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_open_normalized_title_created_idx": { + "name": "issues_open_normalized_title_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(regexp_replace(btrim(\"title\"), '\\s+', ' ', 'g'))", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"issues\".\"hidden_at\" is null and \"issues\".\"status\" not in ('done', 'cancelled')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_company_priority_idx": { + "name": "issues_company_priority_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "priority", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_identifier_idx": { + "name": "issues_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_title_search_idx": { + "name": "issues_title_search_idx", + "columns": [ + { + "expression": "title", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "issues_identifier_search_idx": { + "name": "issues_identifier_search_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "issues_description_search_idx": { + "name": "issues_description_search_idx", + "columns": [ + { + "expression": "description", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "issues_open_routine_execution_uq": { + "name": "issues_open_routine_execution_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issues\".\"origin_kind\" = 'routine_execution'\n and \"issues\".\"origin_id\" is not null\n and \"issues\".\"hidden_at\" is null\n and \"issues\".\"execution_run_id\" is not null\n and \"issues\".\"status\" in ('backlog', 'todo', 'in_progress', 'in_review', 'blocked')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_active_liveness_recovery_incident_uq": { + "name": "issues_active_liveness_recovery_incident_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issues\".\"origin_kind\" = 'harness_liveness_escalation'\n and \"issues\".\"origin_id\" is not null\n and \"issues\".\"hidden_at\" is null\n and \"issues\".\"status\" not in ('done', 'cancelled')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_active_liveness_recovery_leaf_uq": { + "name": "issues_active_liveness_recovery_leaf_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issues\".\"origin_kind\" = 'harness_liveness_escalation'\n and \"issues\".\"origin_fingerprint\" <> 'default'\n and \"issues\".\"hidden_at\" is null\n and \"issues\".\"status\" not in ('done', 'cancelled')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_active_stale_run_evaluation_uq": { + "name": "issues_active_stale_run_evaluation_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issues\".\"origin_kind\" = 'stale_active_run_evaluation'\n and \"issues\".\"origin_id\" is not null\n and \"issues\".\"hidden_at\" is null\n and \"issues\".\"status\" not in ('done', 'cancelled')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_active_task_watchdog_uq": { + "name": "issues_active_task_watchdog_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issues\".\"origin_kind\" = 'task_watchdog'\n and \"issues\".\"origin_id\" is not null\n and \"issues\".\"hidden_at\" is null\n and \"issues\".\"status\" not in ('done', 'cancelled')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_active_productivity_review_uq": { + "name": "issues_active_productivity_review_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issues\".\"origin_kind\" = 'issue_productivity_review'\n and \"issues\".\"origin_id\" is not null\n and \"issues\".\"hidden_at\" is null\n and \"issues\".\"status\" not in ('done', 'cancelled')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_active_stranded_issue_recovery_uq": { + "name": "issues_active_stranded_issue_recovery_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issues\".\"origin_kind\" = 'stranded_issue_recovery'\n and \"issues\".\"origin_id\" is not null\n and \"issues\".\"hidden_at\" is null\n and \"issues\".\"status\" not in ('done', 'cancelled')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_onboarding_first_task_uq": { + "name": "issues_onboarding_first_task_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issues\".\"origin_kind\" = 'onboarding_first_task'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issues_company_id_companies_id_fk": { + "name": "issues_company_id_companies_id_fk", + "tableFrom": "issues", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issues_conversation_agent_id_agents_id_fk": { + "name": "issues_conversation_agent_id_agents_id_fk", + "tableFrom": "issues", + "tableTo": "agents", + "columnsFrom": [ + "conversation_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issues_project_id_projects_id_fk": { + "name": "issues_project_id_projects_id_fk", + "tableFrom": "issues", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issues_project_workspace_id_project_workspaces_id_fk": { + "name": "issues_project_workspace_id_project_workspaces_id_fk", + "tableFrom": "issues", + "tableTo": "project_workspaces", + "columnsFrom": [ + "project_workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issues_goal_id_goals_id_fk": { + "name": "issues_goal_id_goals_id_fk", + "tableFrom": "issues", + "tableTo": "goals", + "columnsFrom": [ + "goal_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issues_parent_id_issues_id_fk": { + "name": "issues_parent_id_issues_id_fk", + "tableFrom": "issues", + "tableTo": "issues", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issues_assignee_agent_id_agents_id_fk": { + "name": "issues_assignee_agent_id_agents_id_fk", + "tableFrom": "issues", + "tableTo": "agents", + "columnsFrom": [ + "assignee_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issues_checkout_run_id_heartbeat_runs_id_fk": { + "name": "issues_checkout_run_id_heartbeat_runs_id_fk", + "tableFrom": "issues", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "checkout_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issues_execution_run_id_heartbeat_runs_id_fk": { + "name": "issues_execution_run_id_heartbeat_runs_id_fk", + "tableFrom": "issues", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "execution_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issues_created_by_agent_id_agents_id_fk": { + "name": "issues_created_by_agent_id_agents_id_fk", + "tableFrom": "issues", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issues_execution_workspace_id_execution_workspaces_id_fk": { + "name": "issues_execution_workspace_id_execution_workspaces_id_fk", + "tableFrom": "issues", + "tableTo": "execution_workspaces", + "columnsFrom": [ + "execution_workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "issues_company_id_uq": { + "name": "issues_company_id_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "issues_conversation_identity_check": { + "name": "issues_conversation_identity_check", + "value": "(\n \"issues\".\"conversation_agent_id\" is null and \"issues\".\"conversation_user_id\" is null and \"issues\".\"conversation_state\" is null\n ) or (\n \"issues\".\"conversation_agent_id\" is not null and \"issues\".\"conversation_user_id\" is not null\n and \"issues\".\"assignee_agent_id\" = \"issues\".\"conversation_agent_id\" and \"issues\".\"assignee_agent_id\" is not null\n and \"issues\".\"assignee_user_id\" is null and \"issues\".\"conversation_state\" is not null\n and \"issues\".\"conversation_state\" in ('active', 'waiting')\n and \"issues\".\"status\" not in ('done', 'cancelled')\n )" + } + }, + "isRLSEnabled": false + }, + "public.join_requests": { + "name": "join_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "invite_id": { + "name": "invite_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "request_type": { + "name": "request_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending_approval'" + }, + "request_ip": { + "name": "request_ip", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requesting_user_id": { + "name": "requesting_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "request_email_snapshot": { + "name": "request_email_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent_name": { + "name": "agent_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "adapter_type": { + "name": "adapter_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "capabilities": { + "name": "capabilities", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent_defaults_payload": { + "name": "agent_defaults_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "claim_secret_hash": { + "name": "claim_secret_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "claim_secret_expires_at": { + "name": "claim_secret_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "claim_secret_consumed_at": { + "name": "claim_secret_consumed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_agent_id": { + "name": "created_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approved_by_user_id": { + "name": "approved_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rejected_by_user_id": { + "name": "rejected_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rejected_at": { + "name": "rejected_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "join_requests_invite_unique_idx": { + "name": "join_requests_invite_unique_idx", + "columns": [ + { + "expression": "invite_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "join_requests_company_status_type_created_idx": { + "name": "join_requests_company_status_type_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "request_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "join_requests_pending_human_user_uq": { + "name": "join_requests_pending_human_user_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "requesting_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"join_requests\".\"request_type\" = 'human' AND \"join_requests\".\"status\" = 'pending_approval' AND \"join_requests\".\"requesting_user_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "join_requests_pending_human_email_uq": { + "name": "join_requests_pending_human_email_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"request_email_snapshot\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"join_requests\".\"request_type\" = 'human' AND \"join_requests\".\"status\" = 'pending_approval' AND \"join_requests\".\"request_email_snapshot\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "join_requests_invite_id_invites_id_fk": { + "name": "join_requests_invite_id_invites_id_fk", + "tableFrom": "join_requests", + "tableTo": "invites", + "columnsFrom": [ + "invite_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "join_requests_company_id_companies_id_fk": { + "name": "join_requests_company_id_companies_id_fk", + "tableFrom": "join_requests", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "join_requests_created_agent_id_agents_id_fk": { + "name": "join_requests_created_agent_id_agents_id_fk", + "tableFrom": "join_requests", + "tableTo": "agents", + "columnsFrom": [ + "created_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.labels": { + "name": "labels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "labels_company_idx": { + "name": "labels_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "labels_company_name_idx": { + "name": "labels_company_name_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "labels_company_id_companies_id_fk": { + "name": "labels_company_id_companies_id_fk", + "tableFrom": "labels", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.managed_agent_profiles": { + "name": "managed_agent_profiles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "profile_key": { + "name": "profile_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "service": { + "name": "service", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'anthropic_managed_agents'" + }, + "anthropic_agent_id": { + "name": "anthropic_agent_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "agent_version": { + "name": "agent_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environment_id": { + "name": "environment_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "beta_version": { + "name": "beta_version", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'managed-agents-2026-04-01'" + }, + "default_model": { + "name": "default_model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'claude-sonnet-5'" + }, + "default_max_list_cost_cents": { + "name": "default_max_list_cost_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 100 + }, + "api_key_secret_id": { + "name": "api_key_secret_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "retention_acknowledged": { + "name": "retention_acknowledged", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "qualification": { + "name": "qualification", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "qualified_at": { + "name": "qualified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "qualified_revision": { + "name": "qualified_revision", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "managed_agent_profiles_company_idx": { + "name": "managed_agent_profiles_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "managed_agent_profiles_company_key_uq": { + "name": "managed_agent_profiles_company_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "profile_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "managed_agent_profiles_company_resource_uq": { + "name": "managed_agent_profiles_company_resource_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "anthropic_agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_version", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "environment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "managed_agent_profiles_company_id_companies_id_fk": { + "name": "managed_agent_profiles_company_id_companies_id_fk", + "tableFrom": "managed_agent_profiles", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "managed_agent_profiles_api_key_secret_id_company_secrets_id_fk": { + "name": "managed_agent_profiles_api_key_secret_id_company_secrets_id_fk", + "tableFrom": "managed_agent_profiles", + "tableTo": "company_secrets", + "columnsFrom": [ + "api_key_secret_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "managed_agent_profiles_service_check": { + "name": "managed_agent_profiles_service_check", + "value": "\"managed_agent_profiles\".\"service\" = 'anthropic_managed_agents'" + }, + "managed_agent_profiles_beta_check": { + "name": "managed_agent_profiles_beta_check", + "value": "\"managed_agent_profiles\".\"beta_version\" = 'managed-agents-2026-04-01'" + }, + "managed_agent_profiles_positive_budget_check": { + "name": "managed_agent_profiles_positive_budget_check", + "value": "\"managed_agent_profiles\".\"default_max_list_cost_cents\" > 0" + }, + "managed_agent_profiles_qualified_revision_check": { + "name": "managed_agent_profiles_qualified_revision_check", + "value": "(\"managed_agent_profiles\".\"qualified_at\" IS NULL AND \"managed_agent_profiles\".\"qualified_revision\" IS NULL) OR (\"managed_agent_profiles\".\"qualified_at\" IS NOT NULL AND \"managed_agent_profiles\".\"qualification\" <> '{}'::jsonb AND \"managed_agent_profiles\".\"qualified_revision\" ~ '^sha256:[0-9a-f]{64}$')" + } + }, + "isRLSEnabled": false + }, + "public.native_run_finalizations": { + "name": "native_run_finalizations", + "schema": "", + "columns": { + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "phase": { + "name": "phase", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "attempt": { + "name": "attempt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "lease_owner": { + "name": "lease_owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "controller_boot_id": { + "name": "controller_boot_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "controller_pid": { + "name": "controller_pid", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "controller_process_started_at": { + "name": "controller_process_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "controller_generation": { + "name": "controller_generation", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "recovery_state": { + "name": "recovery_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "recovery_request_id": { + "name": "recovery_request_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "recovery_history": { + "name": "recovery_history", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "result_id": { + "name": "result_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "assessment_id": { + "name": "assessment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "decision_id": { + "name": "decision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "failure_code": { + "name": "failure_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "failure_detail": { + "name": "failure_detail", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "control_deadline_at": { + "name": "control_deadline_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "native_run_finalizations_control_deadline_idx": { + "name": "native_run_finalizations_control_deadline_idx", + "columns": [ + { + "expression": "control_deadline_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"native_run_finalizations\".\"control_deadline_at\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "native_run_finalizations_company_id_companies_id_fk": { + "name": "native_run_finalizations_company_id_companies_id_fk", + "tableFrom": "native_run_finalizations", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "native_run_finalizations_issue_company_fk": { + "name": "native_run_finalizations_issue_company_fk", + "tableFrom": "native_run_finalizations", + "tableTo": "issues", + "columnsFrom": [ + "company_id", + "issue_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "native_run_finalizations_run_owner_fk": { + "name": "native_run_finalizations_run_owner_fk", + "tableFrom": "native_run_finalizations", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "company_id", + "issue_id", + "run_id" + ], + "columnsTo": [ + "company_id", + "native_issue_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "native_run_finalizations_result_owner_fk": { + "name": "native_run_finalizations_result_owner_fk", + "tableFrom": "native_run_finalizations", + "tableTo": "native_run_results", + "columnsFrom": [ + "company_id", + "issue_id", + "run_id", + "result_id" + ], + "columnsTo": [ + "company_id", + "issue_id", + "run_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "native_run_finalizations_assessment_owner_fk": { + "name": "native_run_finalizations_assessment_owner_fk", + "tableFrom": "native_run_finalizations", + "tableTo": "work_assessments", + "columnsFrom": [ + "company_id", + "issue_id", + "run_id", + "assessment_id" + ], + "columnsTo": [ + "company_id", + "issue_id", + "run_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "native_run_finalizations_decision_owner_fk": { + "name": "native_run_finalizations_decision_owner_fk", + "tableFrom": "native_run_finalizations", + "tableTo": "status_decisions", + "columnsFrom": [ + "company_id", + "issue_id", + "run_id", + "assessment_id", + "decision_id" + ], + "columnsTo": [ + "company_id", + "issue_id", + "run_id", + "assessment_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "native_run_finalizations_assessment_requires_result_check": { + "name": "native_run_finalizations_assessment_requires_result_check", + "value": "\"native_run_finalizations\".\"assessment_id\" is null or \"native_run_finalizations\".\"result_id\" is not null" + }, + "native_run_finalizations_decision_requires_assessment_check": { + "name": "native_run_finalizations_decision_requires_assessment_check", + "value": "\"native_run_finalizations\".\"decision_id\" is null or \"native_run_finalizations\".\"assessment_id\" is not null" + } + }, + "isRLSEnabled": false + }, + "public.native_run_results": { + "name": "native_run_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "turn_id": { + "name": "turn_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completion_contract_id": { + "name": "completion_contract_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "caller_result_id": { + "name": "caller_result_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "caller_dedupe_key": { + "name": "caller_dedupe_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "server_fingerprint": { + "name": "server_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schema_status": { + "name": "schema_status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rejection_code": { + "name": "rejection_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "result_json": { + "name": "result_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "canonical_sha256": { + "name": "canonical_sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "native_run_results_run_fingerprint_uq": { + "name": "native_run_results_run_fingerprint_uq", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "server_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "native_run_results_run_caller_result_uq": { + "name": "native_run_results_run_caller_result_uq", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "caller_result_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "native_run_results_run_caller_dedupe_uq": { + "name": "native_run_results_run_caller_dedupe_uq", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "caller_dedupe_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "native_run_results_company_id_companies_id_fk": { + "name": "native_run_results_company_id_companies_id_fk", + "tableFrom": "native_run_results", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "native_run_results_issue_company_fk": { + "name": "native_run_results_issue_company_fk", + "tableFrom": "native_run_results", + "tableTo": "issues", + "columnsFrom": [ + "company_id", + "issue_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "native_run_results_run_contract_owner_fk": { + "name": "native_run_results_run_contract_owner_fk", + "tableFrom": "native_run_results", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "company_id", + "issue_id", + "run_id", + "completion_contract_id" + ], + "columnsTo": [ + "company_id", + "native_issue_id", + "id", + "completion_contract_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "native_run_results_completion_contract_owner_fk": { + "name": "native_run_results_completion_contract_owner_fk", + "tableFrom": "native_run_results", + "tableTo": "completion_contracts", + "columnsFrom": [ + "company_id", + "issue_id", + "completion_contract_id" + ], + "columnsTo": [ + "company_id", + "issue_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "native_run_results_company_issue_run_id_uq": { + "name": "native_run_results_company_issue_run_id_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "issue_id", + "run_id", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pipeline_automation_executions": { + "name": "pipeline_automation_executions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "case_id": { + "name": "case_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "automation_id": { + "name": "automation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "triggering_event_id": { + "name": "triggering_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "routine_id": { + "name": "routine_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_issue_id": { + "name": "execution_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "retry_of_execution_id": { + "name": "retry_of_execution_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "generation": { + "name": "generation", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pipeline_automation_executions_idempotency_uq": { + "name": "pipeline_automation_executions_idempotency_uq", + "columns": [ + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "automation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "triggering_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_automation_executions_company_case_idx": { + "name": "pipeline_automation_executions_company_case_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_automation_executions_routine_idx": { + "name": "pipeline_automation_executions_routine_idx", + "columns": [ + { + "expression": "routine_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_automation_executions_execution_issue_idx": { + "name": "pipeline_automation_executions_execution_issue_idx", + "columns": [ + { + "expression": "execution_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_automation_executions_retry_of_execution_idx": { + "name": "pipeline_automation_executions_retry_of_execution_idx", + "columns": [ + { + "expression": "retry_of_execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pipeline_automation_executions_company_id_companies_id_fk": { + "name": "pipeline_automation_executions_company_id_companies_id_fk", + "tableFrom": "pipeline_automation_executions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pipeline_automation_executions_case_id_pipeline_cases_id_fk": { + "name": "pipeline_automation_executions_case_id_pipeline_cases_id_fk", + "tableFrom": "pipeline_automation_executions", + "tableTo": "pipeline_cases", + "columnsFrom": [ + "case_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pipeline_automation_executions_routine_id_routines_id_fk": { + "name": "pipeline_automation_executions_routine_id_routines_id_fk", + "tableFrom": "pipeline_automation_executions", + "tableTo": "routines", + "columnsFrom": [ + "routine_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pipeline_automation_executions_execution_issue_id_issues_id_fk": { + "name": "pipeline_automation_executions_execution_issue_id_issues_id_fk", + "tableFrom": "pipeline_automation_executions", + "tableTo": "issues", + "columnsFrom": [ + "execution_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "pipeline_automation_executions_status_check": { + "name": "pipeline_automation_executions_status_check", + "value": "\"pipeline_automation_executions\".\"status\" in ('succeeded', 'failed')" + } + }, + "isRLSEnabled": false + }, + "public.pipeline_case_blockers": { + "name": "pipeline_case_blockers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "case_id": { + "name": "case_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "blocked_by_case_id": { + "name": "blocked_by_case_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pipeline_case_blockers_case_blocked_by_uq": { + "name": "pipeline_case_blockers_case_blocked_by_uq", + "columns": [ + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "blocked_by_case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_case_blockers_blocked_by_idx": { + "name": "pipeline_case_blockers_blocked_by_idx", + "columns": [ + { + "expression": "blocked_by_case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_case_blockers_company_case_idx": { + "name": "pipeline_case_blockers_company_case_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pipeline_case_blockers_company_id_companies_id_fk": { + "name": "pipeline_case_blockers_company_id_companies_id_fk", + "tableFrom": "pipeline_case_blockers", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pipeline_case_blockers_case_id_pipeline_cases_id_fk": { + "name": "pipeline_case_blockers_case_id_pipeline_cases_id_fk", + "tableFrom": "pipeline_case_blockers", + "tableTo": "pipeline_cases", + "columnsFrom": [ + "case_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pipeline_case_blockers_blocked_by_case_id_pipeline_cases_id_fk": { + "name": "pipeline_case_blockers_blocked_by_case_id_pipeline_cases_id_fk", + "tableFrom": "pipeline_case_blockers", + "tableTo": "pipeline_cases", + "columnsFrom": [ + "blocked_by_case_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "pipeline_case_blockers_no_self_block_check": { + "name": "pipeline_case_blockers_no_self_block_check", + "value": "\"pipeline_case_blockers\".\"case_id\" <> \"pipeline_case_blockers\".\"blocked_by_case_id\"" + } + }, + "isRLSEnabled": false + }, + "public.pipeline_case_documents": { + "name": "pipeline_case_documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "case_id": { + "name": "case_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pipeline_case_documents_company_case_key_uq": { + "name": "pipeline_case_documents_company_case_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_case_documents_document_uq": { + "name": "pipeline_case_documents_document_uq", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_case_documents_company_case_updated_idx": { + "name": "pipeline_case_documents_company_case_updated_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pipeline_case_documents_company_id_companies_id_fk": { + "name": "pipeline_case_documents_company_id_companies_id_fk", + "tableFrom": "pipeline_case_documents", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pipeline_case_documents_case_id_pipeline_cases_id_fk": { + "name": "pipeline_case_documents_case_id_pipeline_cases_id_fk", + "tableFrom": "pipeline_case_documents", + "tableTo": "pipeline_cases", + "columnsFrom": [ + "case_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pipeline_case_documents_document_id_documents_id_fk": { + "name": "pipeline_case_documents_document_id_documents_id_fk", + "tableFrom": "pipeline_case_documents", + "tableTo": "documents", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pipeline_case_events": { + "name": "pipeline_case_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "case_id": { + "name": "case_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_agent_id": { + "name": "actor_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "from_stage_id": { + "name": "from_stage_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "to_stage_id": { + "name": "to_stage_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pipeline_case_events_case_created_idx": { + "name": "pipeline_case_events_case_created_idx", + "columns": [ + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_case_events_company_case_idx": { + "name": "pipeline_case_events_company_case_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pipeline_case_events_company_id_companies_id_fk": { + "name": "pipeline_case_events_company_id_companies_id_fk", + "tableFrom": "pipeline_case_events", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pipeline_case_events_case_id_pipeline_cases_id_fk": { + "name": "pipeline_case_events_case_id_pipeline_cases_id_fk", + "tableFrom": "pipeline_case_events", + "tableTo": "pipeline_cases", + "columnsFrom": [ + "case_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pipeline_case_events_actor_agent_id_agents_id_fk": { + "name": "pipeline_case_events_actor_agent_id_agents_id_fk", + "tableFrom": "pipeline_case_events", + "tableTo": "agents", + "columnsFrom": [ + "actor_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "pipeline_case_events_from_stage_id_pipeline_stages_id_fk": { + "name": "pipeline_case_events_from_stage_id_pipeline_stages_id_fk", + "tableFrom": "pipeline_case_events", + "tableTo": "pipeline_stages", + "columnsFrom": [ + "from_stage_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "pipeline_case_events_to_stage_id_pipeline_stages_id_fk": { + "name": "pipeline_case_events_to_stage_id_pipeline_stages_id_fk", + "tableFrom": "pipeline_case_events", + "tableTo": "pipeline_stages", + "columnsFrom": [ + "to_stage_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "pipeline_case_events_type_check": { + "name": "pipeline_case_events_type_check", + "value": "\"pipeline_case_events\".\"type\" in (\n 'ingested',\n 'updated',\n 'claimed',\n 'lease_released',\n 'lease_expired',\n 'transitioned',\n 'transition_forced',\n 'transition_suggested',\n 'suggestion_resolved',\n 'review_decided',\n 'conversation_opened',\n 'issue_linked',\n 'issue_unlinked',\n 'automation_executed',\n 'automation_failed',\n 'automation_retry_requested',\n 'automation_effects_retired',\n 'automation_retry_dispatched',\n 'blockers_set',\n 'blockers_resolved',\n 'children_terminal',\n 'upstream_drift',\n 'drift_acknowledged'\n )" + }, + "pipeline_case_events_actor_type_check": { + "name": "pipeline_case_events_actor_type_check", + "value": "\"pipeline_case_events\".\"actor_type\" in ('user', 'agent', 'system')" + }, + "pipeline_case_events_agent_run_check": { + "name": "pipeline_case_events_agent_run_check", + "value": "\"pipeline_case_events\".\"actor_type\" <> 'agent' or \"pipeline_case_events\".\"run_id\" is not null" + } + }, + "isRLSEnabled": false + }, + "public.pipeline_case_issue_links": { + "name": "pipeline_case_issue_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "case_id": { + "name": "case_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_run_id": { + "name": "created_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "automation_attempt_id": { + "name": "automation_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "retired_at": { + "name": "retired_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "retired_by_attempt_id": { + "name": "retired_by_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "retired_reason": { + "name": "retired_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pipeline_case_issue_links_case_issue_uq": { + "name": "pipeline_case_issue_links_case_issue_uq", + "columns": [ + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_case_issue_links_issue_idx": { + "name": "pipeline_case_issue_links_issue_idx", + "columns": [ + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_case_issue_links_company_case_idx": { + "name": "pipeline_case_issue_links_company_case_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_case_issue_links_automation_attempt_idx": { + "name": "pipeline_case_issue_links_automation_attempt_idx", + "columns": [ + { + "expression": "automation_attempt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pipeline_case_issue_links_company_id_companies_id_fk": { + "name": "pipeline_case_issue_links_company_id_companies_id_fk", + "tableFrom": "pipeline_case_issue_links", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pipeline_case_issue_links_case_id_pipeline_cases_id_fk": { + "name": "pipeline_case_issue_links_case_id_pipeline_cases_id_fk", + "tableFrom": "pipeline_case_issue_links", + "tableTo": "pipeline_cases", + "columnsFrom": [ + "case_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pipeline_case_issue_links_issue_id_issues_id_fk": { + "name": "pipeline_case_issue_links_issue_id_issues_id_fk", + "tableFrom": "pipeline_case_issue_links", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "pipeline_case_issue_links_role_check": { + "name": "pipeline_case_issue_links_role_check", + "value": "\"pipeline_case_issue_links\".\"role\" in ('origin', 'conversation', 'work', 'automation')" + } + }, + "isRLSEnabled": false + }, + "public.pipeline_cases": { + "name": "pipeline_cases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pipeline_id": { + "name": "pipeline_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "stage_id": { + "name": "stage_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "case_key": { + "name": "case_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fields": { + "name": "fields", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "workspace_ref": { + "name": "workspace_ref", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "parent_case_id": { + "name": "parent_case_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "parent_case_version": { + "name": "parent_case_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "request_key": { + "name": "request_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "automation_attempt_id": { + "name": "automation_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "pending_suggestion": { + "name": "pending_suggestion", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "lease_owner_type": { + "name": "lease_owner_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_agent_id": { + "name": "lease_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "lease_user_id": { + "name": "lease_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_token": { + "name": "lease_token", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "terminal_kind": { + "name": "terminal_kind", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "terminal_at": { + "name": "terminal_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "retired_at": { + "name": "retired_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "retired_by_attempt_id": { + "name": "retired_by_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "retired_reason": { + "name": "retired_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "hidden_from_board_at": { + "name": "hidden_from_board_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "child_count": { + "name": "child_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "terminal_child_count": { + "name": "terminal_child_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "origin_run_id": { + "name": "origin_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pipeline_cases_pipeline_case_key_uq": { + "name": "pipeline_cases_pipeline_case_key_uq", + "columns": [ + { + "expression": "pipeline_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "case_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_cases_parent_request_key_uq": { + "name": "pipeline_cases_parent_request_key_uq", + "columns": [ + { + "expression": "parent_case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "request_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"pipeline_cases\".\"request_key\" is not null and \"pipeline_cases\".\"retired_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_cases_company_idx": { + "name": "pipeline_cases_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_cases_pipeline_stage_idx": { + "name": "pipeline_cases_pipeline_stage_idx", + "columns": [ + { + "expression": "pipeline_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "stage_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_cases_parent_idx": { + "name": "pipeline_cases_parent_idx", + "columns": [ + { + "expression": "parent_case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_cases_automation_attempt_idx": { + "name": "pipeline_cases_automation_attempt_idx", + "columns": [ + { + "expression": "automation_attempt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_cases_retired_idx": { + "name": "pipeline_cases_retired_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "retired_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_cases_lease_expires_idx": { + "name": "pipeline_cases_lease_expires_idx", + "columns": [ + { + "expression": "lease_expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"pipeline_cases\".\"lease_expires_at\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pipeline_cases_company_id_companies_id_fk": { + "name": "pipeline_cases_company_id_companies_id_fk", + "tableFrom": "pipeline_cases", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pipeline_cases_pipeline_id_pipelines_id_fk": { + "name": "pipeline_cases_pipeline_id_pipelines_id_fk", + "tableFrom": "pipeline_cases", + "tableTo": "pipelines", + "columnsFrom": [ + "pipeline_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pipeline_cases_stage_id_pipeline_stages_id_fk": { + "name": "pipeline_cases_stage_id_pipeline_stages_id_fk", + "tableFrom": "pipeline_cases", + "tableTo": "pipeline_stages", + "columnsFrom": [ + "stage_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pipeline_cases_parent_case_id_pipeline_cases_id_fk": { + "name": "pipeline_cases_parent_case_id_pipeline_cases_id_fk", + "tableFrom": "pipeline_cases", + "tableTo": "pipeline_cases", + "columnsFrom": [ + "parent_case_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "pipeline_cases_lease_agent_id_agents_id_fk": { + "name": "pipeline_cases_lease_agent_id_agents_id_fk", + "tableFrom": "pipeline_cases", + "tableTo": "agents", + "columnsFrom": [ + "lease_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "pipeline_cases_created_by_agent_id_agents_id_fk": { + "name": "pipeline_cases_created_by_agent_id_agents_id_fk", + "tableFrom": "pipeline_cases", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "pipeline_cases_terminal_kind_check": { + "name": "pipeline_cases_terminal_kind_check", + "value": "\"pipeline_cases\".\"terminal_kind\" is null or \"pipeline_cases\".\"terminal_kind\" in ('done', 'cancelled')" + }, + "pipeline_cases_lease_owner_type_check": { + "name": "pipeline_cases_lease_owner_type_check", + "value": "\"pipeline_cases\".\"lease_owner_type\" is null or \"pipeline_cases\".\"lease_owner_type\" in ('user', 'agent')" + } + }, + "isRLSEnabled": false + }, + "public.pipeline_documents": { + "name": "pipeline_documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pipeline_id": { + "name": "pipeline_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pipeline_documents_company_pipeline_key_uq": { + "name": "pipeline_documents_company_pipeline_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pipeline_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_documents_document_uq": { + "name": "pipeline_documents_document_uq", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_documents_company_pipeline_updated_idx": { + "name": "pipeline_documents_company_pipeline_updated_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pipeline_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pipeline_documents_company_id_companies_id_fk": { + "name": "pipeline_documents_company_id_companies_id_fk", + "tableFrom": "pipeline_documents", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pipeline_documents_pipeline_id_pipelines_id_fk": { + "name": "pipeline_documents_pipeline_id_pipelines_id_fk", + "tableFrom": "pipeline_documents", + "tableTo": "pipelines", + "columnsFrom": [ + "pipeline_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pipeline_documents_document_id_documents_id_fk": { + "name": "pipeline_documents_document_id_documents_id_fk", + "tableFrom": "pipeline_documents", + "tableTo": "documents", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pipeline_stages": { + "name": "pipeline_stages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "pipeline_id": { + "name": "pipeline_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pipeline_stages_pipeline_key_uq": { + "name": "pipeline_stages_pipeline_key_uq", + "columns": [ + { + "expression": "pipeline_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_stages_pipeline_position_idx": { + "name": "pipeline_stages_pipeline_position_idx", + "columns": [ + { + "expression": "pipeline_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "position", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pipeline_stages_pipeline_id_pipelines_id_fk": { + "name": "pipeline_stages_pipeline_id_pipelines_id_fk", + "tableFrom": "pipeline_stages", + "tableTo": "pipelines", + "columnsFrom": [ + "pipeline_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "pipeline_stages_kind_check": { + "name": "pipeline_stages_kind_check", + "value": "\"pipeline_stages\".\"kind\" in ('working', 'review', 'done', 'cancelled')" + } + }, + "isRLSEnabled": false + }, + "public.pipeline_transitions": { + "name": "pipeline_transitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "pipeline_id": { + "name": "pipeline_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "from_stage_id": { + "name": "from_stage_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "to_stage_id": { + "name": "to_stage_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pipeline_transitions_pipeline_edge_uq": { + "name": "pipeline_transitions_pipeline_edge_uq", + "columns": [ + { + "expression": "pipeline_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "from_stage_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "to_stage_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_transitions_pipeline_from_idx": { + "name": "pipeline_transitions_pipeline_from_idx", + "columns": [ + { + "expression": "pipeline_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "from_stage_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipeline_transitions_pipeline_to_idx": { + "name": "pipeline_transitions_pipeline_to_idx", + "columns": [ + { + "expression": "pipeline_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "to_stage_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pipeline_transitions_pipeline_id_pipelines_id_fk": { + "name": "pipeline_transitions_pipeline_id_pipelines_id_fk", + "tableFrom": "pipeline_transitions", + "tableTo": "pipelines", + "columnsFrom": [ + "pipeline_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pipeline_transitions_from_stage_id_pipeline_stages_id_fk": { + "name": "pipeline_transitions_from_stage_id_pipeline_stages_id_fk", + "tableFrom": "pipeline_transitions", + "tableTo": "pipeline_stages", + "columnsFrom": [ + "from_stage_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pipeline_transitions_to_stage_id_pipeline_stages_id_fk": { + "name": "pipeline_transitions_to_stage_id_pipeline_stages_id_fk", + "tableFrom": "pipeline_transitions", + "tableTo": "pipeline_stages", + "columnsFrom": [ + "to_stage_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pipelines": { + "name": "pipelines", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enforce_transitions": { + "name": "enforce_transitions", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pipelines_company_key_uq": { + "name": "pipelines_company_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipelines_company_idx": { + "name": "pipelines_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pipelines_company_project_idx": { + "name": "pipelines_company_project_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pipelines_company_id_companies_id_fk": { + "name": "pipelines_company_id_companies_id_fk", + "tableFrom": "pipelines", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pipelines_project_id_projects_id_fk": { + "name": "pipelines_project_id_projects_id_fk", + "tableFrom": "pipelines", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "pipelines_created_by_agent_id_agents_id_fk": { + "name": "pipelines_created_by_agent_id_agents_id_fk", + "tableFrom": "pipelines", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plugin_company_settings": { + "name": "plugin_company_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "settings_json": { + "name": "settings_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "plugin_company_settings_company_idx": { + "name": "plugin_company_settings_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_company_settings_plugin_idx": { + "name": "plugin_company_settings_plugin_idx", + "columns": [ + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_company_settings_company_plugin_uq": { + "name": "plugin_company_settings_company_plugin_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "plugin_company_settings_company_id_companies_id_fk": { + "name": "plugin_company_settings_company_id_companies_id_fk", + "tableFrom": "plugin_company_settings", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "plugin_company_settings_plugin_id_plugins_id_fk": { + "name": "plugin_company_settings_plugin_id_plugins_id_fk", + "tableFrom": "plugin_company_settings", + "tableTo": "plugins", + "columnsFrom": [ + "plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plugin_config": { + "name": "plugin_config", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "config_json": { + "name": "config_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "plugin_config_plugin_company_idx": { + "name": "plugin_config_plugin_company_idx", + "columns": [ + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "plugin_config_plugin_id_plugins_id_fk": { + "name": "plugin_config_plugin_id_plugins_id_fk", + "tableFrom": "plugin_config", + "tableTo": "plugins", + "columnsFrom": [ + "plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "plugin_config_company_id_companies_id_fk": { + "name": "plugin_config_company_id_companies_id_fk", + "tableFrom": "plugin_config", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plugin_database_namespaces": { + "name": "plugin_database_namespaces", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "plugin_key": { + "name": "plugin_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "namespace_name": { + "name": "namespace_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "namespace_mode": { + "name": "namespace_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'schema'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "plugin_database_namespaces_plugin_idx": { + "name": "plugin_database_namespaces_plugin_idx", + "columns": [ + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_database_namespaces_namespace_idx": { + "name": "plugin_database_namespaces_namespace_idx", + "columns": [ + { + "expression": "namespace_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_database_namespaces_status_idx": { + "name": "plugin_database_namespaces_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "plugin_database_namespaces_plugin_id_plugins_id_fk": { + "name": "plugin_database_namespaces_plugin_id_plugins_id_fk", + "tableFrom": "plugin_database_namespaces", + "tableTo": "plugins", + "columnsFrom": [ + "plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plugin_entities": { + "name": "plugin_entities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope_kind": { + "name": "scope_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope_id": { + "name": "scope_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "plugin_entities_plugin_idx": { + "name": "plugin_entities_plugin_idx", + "columns": [ + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_entities_company_idx": { + "name": "plugin_entities_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_entities_type_idx": { + "name": "plugin_entities_type_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_entities_scope_idx": { + "name": "plugin_entities_scope_idx", + "columns": [ + { + "expression": "scope_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "plugin_entities_plugin_id_plugins_id_fk": { + "name": "plugin_entities_plugin_id_plugins_id_fk", + "tableFrom": "plugin_entities", + "tableTo": "plugins", + "columnsFrom": [ + "plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "plugin_entities_company_id_companies_id_fk": { + "name": "plugin_entities_company_id_companies_id_fk", + "tableFrom": "plugin_entities", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "plugin_entities_external_idx": { + "name": "plugin_entities_external_idx", + "nullsNotDistinct": true, + "columns": [ + "company_id", + "plugin_id", + "entity_type", + "external_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plugin_job_runs": { + "name": "plugin_job_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "job_id": { + "name": "job_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "duration_ms": { + "name": "duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logs": { + "name": "logs", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "plugin_job_runs_job_idx": { + "name": "plugin_job_runs_job_idx", + "columns": [ + { + "expression": "job_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_job_runs_plugin_idx": { + "name": "plugin_job_runs_plugin_idx", + "columns": [ + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_job_runs_company_idx": { + "name": "plugin_job_runs_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_job_runs_status_idx": { + "name": "plugin_job_runs_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "plugin_job_runs_job_id_plugin_jobs_id_fk": { + "name": "plugin_job_runs_job_id_plugin_jobs_id_fk", + "tableFrom": "plugin_job_runs", + "tableTo": "plugin_jobs", + "columnsFrom": [ + "job_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "plugin_job_runs_plugin_id_plugins_id_fk": { + "name": "plugin_job_runs_plugin_id_plugins_id_fk", + "tableFrom": "plugin_job_runs", + "tableTo": "plugins", + "columnsFrom": [ + "plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "plugin_job_runs_company_id_companies_id_fk": { + "name": "plugin_job_runs_company_id_companies_id_fk", + "tableFrom": "plugin_job_runs", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plugin_jobs": { + "name": "plugin_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "job_key": { + "name": "job_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "last_run_at": { + "name": "last_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "next_run_at": { + "name": "next_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "plugin_jobs_plugin_idx": { + "name": "plugin_jobs_plugin_idx", + "columns": [ + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_jobs_next_run_idx": { + "name": "plugin_jobs_next_run_idx", + "columns": [ + { + "expression": "next_run_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_jobs_unique_idx": { + "name": "plugin_jobs_unique_idx", + "columns": [ + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "job_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "plugin_jobs_plugin_id_plugins_id_fk": { + "name": "plugin_jobs_plugin_id_plugins_id_fk", + "tableFrom": "plugin_jobs", + "tableTo": "plugins", + "columnsFrom": [ + "plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plugin_logs": { + "name": "plugin_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "level": { + "name": "level", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'info'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "meta": { + "name": "meta", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "plugin_logs_plugin_time_idx": { + "name": "plugin_logs_plugin_time_idx", + "columns": [ + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_logs_company_idx": { + "name": "plugin_logs_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_logs_level_idx": { + "name": "plugin_logs_level_idx", + "columns": [ + { + "expression": "level", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "plugin_logs_plugin_id_plugins_id_fk": { + "name": "plugin_logs_plugin_id_plugins_id_fk", + "tableFrom": "plugin_logs", + "tableTo": "plugins", + "columnsFrom": [ + "plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "plugin_logs_company_id_companies_id_fk": { + "name": "plugin_logs_company_id_companies_id_fk", + "tableFrom": "plugin_logs", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plugin_managed_resources": { + "name": "plugin_managed_resources", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "plugin_key": { + "name": "plugin_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_kind": { + "name": "resource_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_key": { + "name": "resource_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "defaults_json": { + "name": "defaults_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "plugin_managed_resources_company_idx": { + "name": "plugin_managed_resources_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_managed_resources_plugin_idx": { + "name": "plugin_managed_resources_plugin_idx", + "columns": [ + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_managed_resources_resource_idx": { + "name": "plugin_managed_resources_resource_idx", + "columns": [ + { + "expression": "resource_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_managed_resources_company_plugin_resource_uq": { + "name": "plugin_managed_resources_company_plugin_resource_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "plugin_managed_resources_company_id_companies_id_fk": { + "name": "plugin_managed_resources_company_id_companies_id_fk", + "tableFrom": "plugin_managed_resources", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "plugin_managed_resources_plugin_id_plugins_id_fk": { + "name": "plugin_managed_resources_plugin_id_plugins_id_fk", + "tableFrom": "plugin_managed_resources", + "tableTo": "plugins", + "columnsFrom": [ + "plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plugin_migrations": { + "name": "plugin_migrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "plugin_key": { + "name": "plugin_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "namespace_name": { + "name": "namespace_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "migration_key": { + "name": "migration_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "checksum": { + "name": "checksum", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plugin_version": { + "name": "plugin_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "plugin_migrations_plugin_key_idx": { + "name": "plugin_migrations_plugin_key_idx", + "columns": [ + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "migration_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_migrations_plugin_idx": { + "name": "plugin_migrations_plugin_idx", + "columns": [ + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_migrations_status_idx": { + "name": "plugin_migrations_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "plugin_migrations_plugin_id_plugins_id_fk": { + "name": "plugin_migrations_plugin_id_plugins_id_fk", + "tableFrom": "plugin_migrations", + "tableTo": "plugins", + "columnsFrom": [ + "plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plugin_state": { + "name": "plugin_state", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "scope_kind": { + "name": "scope_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope_id": { + "name": "scope_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "namespace": { + "name": "namespace", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'default'" + }, + "state_key": { + "name": "state_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value_json": { + "name": "value_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "plugin_state_plugin_scope_idx": { + "name": "plugin_state_plugin_scope_idx", + "columns": [ + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "plugin_state_plugin_id_plugins_id_fk": { + "name": "plugin_state_plugin_id_plugins_id_fk", + "tableFrom": "plugin_state", + "tableTo": "plugins", + "columnsFrom": [ + "plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "plugin_state_unique_entry_idx": { + "name": "plugin_state_unique_entry_idx", + "nullsNotDistinct": true, + "columns": [ + "plugin_id", + "scope_kind", + "scope_id", + "namespace", + "state_key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plugin_webhook_deliveries": { + "name": "plugin_webhook_deliveries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "webhook_key": { + "name": "webhook_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "duration_ms": { + "name": "duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "headers": { + "name": "headers", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "plugin_webhook_deliveries_plugin_idx": { + "name": "plugin_webhook_deliveries_plugin_idx", + "columns": [ + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_webhook_deliveries_company_idx": { + "name": "plugin_webhook_deliveries_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_webhook_deliveries_status_idx": { + "name": "plugin_webhook_deliveries_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_webhook_deliveries_key_idx": { + "name": "plugin_webhook_deliveries_key_idx", + "columns": [ + { + "expression": "webhook_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "plugin_webhook_deliveries_plugin_id_plugins_id_fk": { + "name": "plugin_webhook_deliveries_plugin_id_plugins_id_fk", + "tableFrom": "plugin_webhook_deliveries", + "tableTo": "plugins", + "columnsFrom": [ + "plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "plugin_webhook_deliveries_company_id_companies_id_fk": { + "name": "plugin_webhook_deliveries_company_id_companies_id_fk", + "tableFrom": "plugin_webhook_deliveries", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plugins": { + "name": "plugins", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "plugin_key": { + "name": "plugin_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "package_name": { + "name": "package_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "api_version": { + "name": "api_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "categories": { + "name": "categories", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "manifest_json": { + "name": "manifest_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'installed'" + }, + "install_order": { + "name": "install_order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "package_path": { + "name": "package_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "installed_at": { + "name": "installed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "plugins_plugin_key_idx": { + "name": "plugins_plugin_key_idx", + "columns": [ + { + "expression": "plugin_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugins_status_idx": { + "name": "plugins_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.principal_permission_grants": { + "name": "principal_permission_grants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "principal_type": { + "name": "principal_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "principal_id": { + "name": "principal_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission_key": { + "name": "permission_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "granted_by_user_id": { + "name": "granted_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "principal_permission_grants_unique_idx": { + "name": "principal_permission_grants_unique_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "principal_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "principal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "permission_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "principal_permission_grants_company_permission_idx": { + "name": "principal_permission_grants_company_permission_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "permission_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "principal_permission_grants_company_id_companies_id_fk": { + "name": "principal_permission_grants_company_id_companies_id_fk", + "tableFrom": "principal_permission_grants", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project_goals": { + "name": "project_goals", + "schema": "", + "columns": { + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "goal_id": { + "name": "goal_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "project_goals_project_idx": { + "name": "project_goals_project_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "project_goals_goal_idx": { + "name": "project_goals_goal_idx", + "columns": [ + { + "expression": "goal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "project_goals_company_idx": { + "name": "project_goals_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "project_goals_project_id_projects_id_fk": { + "name": "project_goals_project_id_projects_id_fk", + "tableFrom": "project_goals", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "project_goals_goal_id_goals_id_fk": { + "name": "project_goals_goal_id_goals_id_fk", + "tableFrom": "project_goals", + "tableTo": "goals", + "columnsFrom": [ + "goal_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "project_goals_company_id_companies_id_fk": { + "name": "project_goals_company_id_companies_id_fk", + "tableFrom": "project_goals", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "project_goals_project_id_goal_id_pk": { + "name": "project_goals_project_id_goal_id_pk", + "columns": [ + "project_id", + "goal_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project_memberships": { + "name": "project_memberships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'joined'" + }, + "starred_at": { + "name": "starred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "project_memberships_company_user_idx": { + "name": "project_memberships_company_user_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "project_memberships_company_user_starred_idx": { + "name": "project_memberships_company_user_starred_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "starred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "project_memberships_project_idx": { + "name": "project_memberships_project_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "project_memberships_company_user_project_uq": { + "name": "project_memberships_company_user_project_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "project_memberships_company_id_companies_id_fk": { + "name": "project_memberships_company_id_companies_id_fk", + "tableFrom": "project_memberships", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "project_memberships_project_id_projects_id_fk": { + "name": "project_memberships_project_id_projects_id_fk", + "tableFrom": "project_memberships", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project_workspaces": { + "name": "project_workspaces", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_type": { + "name": "source_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'local_path'" + }, + "cwd": { + "name": "cwd", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repo_url": { + "name": "repo_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repo_ref": { + "name": "repo_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "default_ref": { + "name": "default_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'default'" + }, + "setup_command": { + "name": "setup_command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cleanup_command": { + "name": "cleanup_command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "remote_provider": { + "name": "remote_provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "remote_workspace_ref": { + "name": "remote_workspace_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shared_workspace_key": { + "name": "shared_workspace_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "is_primary": { + "name": "is_primary", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "project_workspaces_company_project_idx": { + "name": "project_workspaces_company_project_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "project_workspaces_project_primary_idx": { + "name": "project_workspaces_project_primary_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "is_primary", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "project_workspaces_project_source_type_idx": { + "name": "project_workspaces_project_source_type_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "project_workspaces_company_shared_key_idx": { + "name": "project_workspaces_company_shared_key_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "shared_workspace_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "project_workspaces_project_remote_ref_idx": { + "name": "project_workspaces_project_remote_ref_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "remote_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "remote_workspace_ref", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "project_workspaces_company_id_companies_id_fk": { + "name": "project_workspaces_company_id_companies_id_fk", + "tableFrom": "project_workspaces", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "project_workspaces_project_id_projects_id_fk": { + "name": "project_workspaces_project_id_projects_id_fk", + "tableFrom": "project_workspaces", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.projects": { + "name": "projects", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "goal_id": { + "name": "goal_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'backlog'" + }, + "lead_agent_id": { + "name": "lead_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "target_date": { + "name": "target_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "pause_reason": { + "name": "pause_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "paused_at": { + "name": "paused_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "execution_workspace_policy": { + "name": "execution_workspace_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "projects_company_idx": { + "name": "projects_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "projects_company_id_companies_id_fk": { + "name": "projects_company_id_companies_id_fk", + "tableFrom": "projects", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "projects_goal_id_goals_id_fk": { + "name": "projects_goal_id_goals_id_fk", + "tableFrom": "projects", + "tableTo": "goals", + "columnsFrom": [ + "goal_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "projects_lead_agent_id_agents_id_fk": { + "name": "projects_lead_agent_id_agents_id_fk", + "tableFrom": "projects", + "tableTo": "agents", + "columnsFrom": [ + "lead_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.provider_trace_records": { + "name": "provider_trace_records", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'capturing'" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trace_ref": { + "name": "trace_ref", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "frame_count": { + "name": "frame_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "byte_count": { + "name": "byte_count", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "digest": { + "name": "digest", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "requested_by": { + "name": "requested_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "provider_trace_records_run_unique": { + "name": "provider_trace_records_run_unique", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "provider_trace_records_expiry_idx": { + "name": "provider_trace_records_expiry_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "provider_trace_records_company_created_idx": { + "name": "provider_trace_records_company_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "provider_trace_records_company_id_companies_id_fk": { + "name": "provider_trace_records_company_id_companies_id_fk", + "tableFrom": "provider_trace_records", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "provider_trace_records_run_id_heartbeat_runs_id_fk": { + "name": "provider_trace_records_run_id_heartbeat_runs_id_fk", + "tableFrom": "provider_trace_records", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.remote_agent_profiles": { + "name": "remote_agent_profiles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "profile_key": { + "name": "profile_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "service": { + "name": "service", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "configuration": { + "name": "configuration", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "retention_acknowledged": { + "name": "retention_acknowledged", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "qualification": { + "name": "qualification", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "qualified_at": { + "name": "qualified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "qualified_revision": { + "name": "qualified_revision", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "remote_agent_profiles_company_idx": { + "name": "remote_agent_profiles_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "remote_agent_profiles_company_key_uq": { + "name": "remote_agent_profiles_company_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "profile_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "remote_agent_profiles_company_id_companies_id_fk": { + "name": "remote_agent_profiles_company_id_companies_id_fk", + "tableFrom": "remote_agent_profiles", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "remote_agent_profiles_service_check": { + "name": "remote_agent_profiles_service_check", + "value": "\"remote_agent_profiles\".\"service\" = 'aws_bedrock_agentcore_harness'" + }, + "remote_agent_profiles_qualified_revision_check": { + "name": "remote_agent_profiles_qualified_revision_check", + "value": "(\"remote_agent_profiles\".\"qualified_at\" IS NULL AND \"remote_agent_profiles\".\"qualified_revision\" IS NULL) OR (\"remote_agent_profiles\".\"qualified_at\" IS NOT NULL AND \"remote_agent_profiles\".\"qualification\" <> '{}'::jsonb AND \"remote_agent_profiles\".\"qualified_revision\" ~ '^sha256:[0-9a-f]{64}$')" + } + }, + "isRLSEnabled": false + }, + "public.routine_documents": { + "name": "routine_documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "routine_id": { + "name": "routine_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "routine_documents_company_routine_key_uq": { + "name": "routine_documents_company_routine_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "routine_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routine_documents_document_uq": { + "name": "routine_documents_document_uq", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routine_documents_company_routine_updated_idx": { + "name": "routine_documents_company_routine_updated_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "routine_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "routine_documents_company_id_companies_id_fk": { + "name": "routine_documents_company_id_companies_id_fk", + "tableFrom": "routine_documents", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "routine_documents_routine_id_routines_id_fk": { + "name": "routine_documents_routine_id_routines_id_fk", + "tableFrom": "routine_documents", + "tableTo": "routines", + "columnsFrom": [ + "routine_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "routine_documents_document_id_documents_id_fk": { + "name": "routine_documents_document_id_documents_id_fk", + "tableFrom": "routine_documents", + "tableTo": "documents", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.routine_revisions": { + "name": "routine_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "routine_id": { + "name": "routine_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision_number": { + "name": "revision_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "snapshot": { + "name": "snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "change_summary": { + "name": "change_summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "restored_from_revision_id": { + "name": "restored_from_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_run_id": { + "name": "created_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "responsible_user_id": { + "name": "responsible_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "routine_revisions_routine_revision_uq": { + "name": "routine_revisions_routine_revision_uq", + "columns": [ + { + "expression": "routine_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "revision_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routine_revisions_company_routine_created_idx": { + "name": "routine_revisions_company_routine_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "routine_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routine_revisions_company_responsible_user_idx": { + "name": "routine_revisions_company_responsible_user_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "responsible_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "routine_revisions_company_id_companies_id_fk": { + "name": "routine_revisions_company_id_companies_id_fk", + "tableFrom": "routine_revisions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "routine_revisions_routine_id_routines_id_fk": { + "name": "routine_revisions_routine_id_routines_id_fk", + "tableFrom": "routine_revisions", + "tableTo": "routines", + "columnsFrom": [ + "routine_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "routine_revisions_restored_from_revision_id_routine_revisions_id_fk": { + "name": "routine_revisions_restored_from_revision_id_routine_revisions_id_fk", + "tableFrom": "routine_revisions", + "tableTo": "routine_revisions", + "columnsFrom": [ + "restored_from_revision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "routine_revisions_created_by_agent_id_agents_id_fk": { + "name": "routine_revisions_created_by_agent_id_agents_id_fk", + "tableFrom": "routine_revisions", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "routine_revisions_created_by_run_id_heartbeat_runs_id_fk": { + "name": "routine_revisions_created_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "routine_revisions", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "created_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.routine_runs": { + "name": "routine_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "routine_id": { + "name": "routine_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "trigger_id": { + "name": "trigger_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'received'" + }, + "triggered_at": { + "name": "triggered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "routine_revision_id": { + "name": "routine_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "responsible_user_id": { + "name": "responsible_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "trigger_payload": { + "name": "trigger_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "dispatch_fingerprint": { + "name": "dispatch_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "linked_issue_id": { + "name": "linked_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "coalesced_into_run_id": { + "name": "coalesced_into_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "routine_runs_company_routine_idx": { + "name": "routine_runs_company_routine_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "routine_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routine_runs_revision_idx": { + "name": "routine_runs_revision_idx", + "columns": [ + { + "expression": "routine_revision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routine_runs_company_responsible_user_idx": { + "name": "routine_runs_company_responsible_user_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "responsible_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routine_runs_trigger_idx": { + "name": "routine_runs_trigger_idx", + "columns": [ + { + "expression": "trigger_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routine_runs_dispatch_fingerprint_idx": { + "name": "routine_runs_dispatch_fingerprint_idx", + "columns": [ + { + "expression": "routine_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "dispatch_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routine_runs_linked_issue_idx": { + "name": "routine_runs_linked_issue_idx", + "columns": [ + { + "expression": "linked_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routine_runs_trigger_idempotency_idx": { + "name": "routine_runs_trigger_idempotency_idx", + "columns": [ + { + "expression": "trigger_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "routine_runs_company_id_companies_id_fk": { + "name": "routine_runs_company_id_companies_id_fk", + "tableFrom": "routine_runs", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "routine_runs_routine_id_routines_id_fk": { + "name": "routine_runs_routine_id_routines_id_fk", + "tableFrom": "routine_runs", + "tableTo": "routines", + "columnsFrom": [ + "routine_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "routine_runs_trigger_id_routine_triggers_id_fk": { + "name": "routine_runs_trigger_id_routine_triggers_id_fk", + "tableFrom": "routine_runs", + "tableTo": "routine_triggers", + "columnsFrom": [ + "trigger_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "routine_runs_routine_revision_id_routine_revisions_id_fk": { + "name": "routine_runs_routine_revision_id_routine_revisions_id_fk", + "tableFrom": "routine_runs", + "tableTo": "routine_revisions", + "columnsFrom": [ + "routine_revision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "routine_runs_linked_issue_id_issues_id_fk": { + "name": "routine_runs_linked_issue_id_issues_id_fk", + "tableFrom": "routine_runs", + "tableTo": "issues", + "columnsFrom": [ + "linked_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.routine_triggers": { + "name": "routine_triggers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "routine_id": { + "name": "routine_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "setup_pending": { + "name": "setup_pending", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "last_webhook_delivery": { + "name": "last_webhook_delivery", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "cron_expression": { + "name": "cron_expression", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_run_at": { + "name": "next_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_fired_at": { + "name": "last_fired_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "public_id": { + "name": "public_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret_id": { + "name": "secret_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "signing_mode": { + "name": "signing_mode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replay_window_sec": { + "name": "replay_window_sec", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_rotated_at": { + "name": "last_rotated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_result": { + "name": "last_result", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_by_agent_id": { + "name": "updated_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "updated_by_user_id": { + "name": "updated_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "routine_triggers_company_routine_idx": { + "name": "routine_triggers_company_routine_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "routine_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routine_triggers_company_kind_idx": { + "name": "routine_triggers_company_kind_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routine_triggers_next_run_idx": { + "name": "routine_triggers_next_run_idx", + "columns": [ + { + "expression": "next_run_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routine_triggers_public_id_idx": { + "name": "routine_triggers_public_id_idx", + "columns": [ + { + "expression": "public_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routine_triggers_public_id_uq": { + "name": "routine_triggers_public_id_uq", + "columns": [ + { + "expression": "public_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "routine_triggers_company_id_companies_id_fk": { + "name": "routine_triggers_company_id_companies_id_fk", + "tableFrom": "routine_triggers", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "routine_triggers_routine_id_routines_id_fk": { + "name": "routine_triggers_routine_id_routines_id_fk", + "tableFrom": "routine_triggers", + "tableTo": "routines", + "columnsFrom": [ + "routine_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "routine_triggers_secret_id_company_secrets_id_fk": { + "name": "routine_triggers_secret_id_company_secrets_id_fk", + "tableFrom": "routine_triggers", + "tableTo": "company_secrets", + "columnsFrom": [ + "secret_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "routine_triggers_created_by_agent_id_agents_id_fk": { + "name": "routine_triggers_created_by_agent_id_agents_id_fk", + "tableFrom": "routine_triggers", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "routine_triggers_updated_by_agent_id_agents_id_fk": { + "name": "routine_triggers_updated_by_agent_id_agents_id_fk", + "tableFrom": "routine_triggers", + "tableTo": "agents", + "columnsFrom": [ + "updated_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.routine_webhook_test_receipts": { + "name": "routine_webhook_test_receipts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "trigger_id": { + "name": "trigger_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "delivery_key_hash": { + "name": "delivery_key_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "received_at": { + "name": "received_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "routine_webhook_test_receipts_delivery_uq": { + "name": "routine_webhook_test_receipts_delivery_uq", + "columns": [ + { + "expression": "trigger_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "delivery_key_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "routine_webhook_test_receipts_company_id_companies_id_fk": { + "name": "routine_webhook_test_receipts_company_id_companies_id_fk", + "tableFrom": "routine_webhook_test_receipts", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "routine_webhook_test_receipts_trigger_id_routine_triggers_id_fk": { + "name": "routine_webhook_test_receipts_trigger_id_routine_triggers_id_fk", + "tableFrom": "routine_webhook_test_receipts", + "tableTo": "routine_triggers", + "columnsFrom": [ + "trigger_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.routines": { + "name": "routines", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "folder_id": { + "name": "folder_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "goal_id": { + "name": "goal_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "parent_issue_id": { + "name": "parent_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assignee_agent_id": { + "name": "assignee_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "priority": { + "name": "priority", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'medium'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "concurrency_policy": { + "name": "concurrency_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'coalesce_if_active'" + }, + "catch_up_policy": { + "name": "catch_up_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'skip_missed'" + }, + "activity_gate_policy": { + "name": "activity_gate_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'always'" + }, + "activity_gate_scope": { + "name": "activity_gate_scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'company'" + }, + "origin_kind": { + "name": "origin_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "origin_id": { + "name": "origin_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "variables": { + "name": "variables", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "env": { + "name": "env", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "latest_revision_id": { + "name": "latest_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "latest_revision_number": { + "name": "latest_revision_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "responsible_user_id": { + "name": "responsible_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_by_agent_id": { + "name": "updated_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "updated_by_user_id": { + "name": "updated_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_triggered_at": { + "name": "last_triggered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_enqueued_at": { + "name": "last_enqueued_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "routines_company_status_idx": { + "name": "routines_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routines_company_assignee_idx": { + "name": "routines_company_assignee_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "assignee_agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routines_company_project_idx": { + "name": "routines_company_project_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routines_company_folder_idx": { + "name": "routines_company_folder_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "folder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routines_company_responsible_user_idx": { + "name": "routines_company_responsible_user_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "responsible_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routines_company_origin_idx": { + "name": "routines_company_origin_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "routines_company_id_companies_id_fk": { + "name": "routines_company_id_companies_id_fk", + "tableFrom": "routines", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "routines_project_id_projects_id_fk": { + "name": "routines_project_id_projects_id_fk", + "tableFrom": "routines", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "routines_folder_id_folders_id_fk": { + "name": "routines_folder_id_folders_id_fk", + "tableFrom": "routines", + "tableTo": "folders", + "columnsFrom": [ + "folder_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "routines_goal_id_goals_id_fk": { + "name": "routines_goal_id_goals_id_fk", + "tableFrom": "routines", + "tableTo": "goals", + "columnsFrom": [ + "goal_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "routines_parent_issue_id_issues_id_fk": { + "name": "routines_parent_issue_id_issues_id_fk", + "tableFrom": "routines", + "tableTo": "issues", + "columnsFrom": [ + "parent_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "routines_assignee_agent_id_agents_id_fk": { + "name": "routines_assignee_agent_id_agents_id_fk", + "tableFrom": "routines", + "tableTo": "agents", + "columnsFrom": [ + "assignee_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "routines_created_by_agent_id_agents_id_fk": { + "name": "routines_created_by_agent_id_agents_id_fk", + "tableFrom": "routines", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "routines_updated_by_agent_id_agents_id_fk": { + "name": "routines_updated_by_agent_id_agents_id_fk", + "tableFrom": "routines", + "tableTo": "agents", + "columnsFrom": [ + "updated_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.run_identity_contexts": { + "name": "run_identity_contexts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "responsible_user_id": { + "name": "responsible_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "parent_context_id": { + "name": "parent_context_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "cause": { + "name": "cause", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "correlation_id": { + "name": "correlation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'accepted'" + }, + "accepted_at": { + "name": "accepted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "github": { + "name": "github", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "run_identity_contexts_run_revision_idx": { + "name": "run_identity_contexts_run_revision_idx", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "revision", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "run_identity_contexts_run_correlation_idx": { + "name": "run_identity_contexts_run_correlation_idx", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "correlation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "run_identity_contexts_company_run_idx": { + "name": "run_identity_contexts_company_run_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "run_identity_contexts_company_id_companies_id_fk": { + "name": "run_identity_contexts_company_id_companies_id_fk", + "tableFrom": "run_identity_contexts", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.runner_api_response_reservations": { + "name": "runner_api_response_reservations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "asset_id": { + "name": "asset_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "reserved_bytes": { + "name": "reserved_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "runner_api_response_reservations_company_idx": { + "name": "runner_api_response_reservations_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "runner_api_response_reservations_company_id_companies_id_fk": { + "name": "runner_api_response_reservations_company_id_companies_id_fk", + "tableFrom": "runner_api_response_reservations", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "runner_api_response_reservations_run_id_heartbeat_runs_id_fk": { + "name": "runner_api_response_reservations_run_id_heartbeat_runs_id_fk", + "tableFrom": "runner_api_response_reservations", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "runner_api_response_reservations_asset_id_assets_id_fk": { + "name": "runner_api_response_reservations_asset_id_assets_id_fk", + "tableFrom": "runner_api_response_reservations", + "tableTo": "assets", + "columnsFrom": [ + "asset_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.secret_access_events": { + "name": "secret_access_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "secret_id": { + "name": "secret_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "user_secret_definition_id": { + "name": "user_secret_definition_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "secret_scope": { + "name": "secret_scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'company'" + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "responsible_user_id": { + "name": "responsible_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credential_owner_user_id": { + "name": "credential_owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credential_subject_type": { + "name": "credential_subject_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credential_subject_id": { + "name": "credential_subject_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "consumer_type": { + "name": "consumer_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "consumer_id": { + "name": "consumer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config_path": { + "name": "config_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "heartbeat_run_id": { + "name": "heartbeat_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "secret_access_events_company_created_idx": { + "name": "secret_access_events_company_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "secret_access_events_secret_created_idx": { + "name": "secret_access_events_secret_created_idx", + "columns": [ + { + "expression": "secret_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "secret_access_events_user_definition_created_idx": { + "name": "secret_access_events_user_definition_created_idx", + "columns": [ + { + "expression": "user_secret_definition_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "secret_access_events_company_credential_owner_idx": { + "name": "secret_access_events_company_credential_owner_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "credential_owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "secret_access_events_consumer_idx": { + "name": "secret_access_events_consumer_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "consumer_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "consumer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "secret_access_events_run_idx": { + "name": "secret_access_events_run_idx", + "columns": [ + { + "expression": "heartbeat_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "secret_access_events_company_id_companies_id_fk": { + "name": "secret_access_events_company_id_companies_id_fk", + "tableFrom": "secret_access_events", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "secret_access_events_secret_id_company_secrets_id_fk": { + "name": "secret_access_events_secret_id_company_secrets_id_fk", + "tableFrom": "secret_access_events", + "tableTo": "company_secrets", + "columnsFrom": [ + "secret_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "secret_access_events_user_secret_definition_id_user_secret_definitions_id_fk": { + "name": "secret_access_events_user_secret_definition_id_user_secret_definitions_id_fk", + "tableFrom": "secret_access_events", + "tableTo": "user_secret_definitions", + "columnsFrom": [ + "user_secret_definition_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "secret_access_events_issue_id_issues_id_fk": { + "name": "secret_access_events_issue_id_issues_id_fk", + "tableFrom": "secret_access_events", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "secret_access_events_heartbeat_run_id_heartbeat_runs_id_fk": { + "name": "secret_access_events_heartbeat_run_id_heartbeat_runs_id_fk", + "tableFrom": "secret_access_events", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "heartbeat_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "secret_access_events_plugin_id_plugins_id_fk": { + "name": "secret_access_events_plugin_id_plugins_id_fk", + "tableFrom": "secret_access_events", + "tableTo": "plugins", + "columnsFrom": [ + "plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.smoke_run_steps": { + "name": "smoke_run_steps", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scenario_step": { + "name": "scenario_step", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "detail": { + "name": "detail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "screenshot_artifact_ref": { + "name": "screenshot_artifact_ref", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "duration_ms": { + "name": "duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "smoke_run_steps_company_run_idx": { + "name": "smoke_run_steps_company_run_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "smoke_run_steps_company_path_idx": { + "name": "smoke_run_steps_company_path_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "smoke_run_steps_company_id_companies_id_fk": { + "name": "smoke_run_steps_company_id_companies_id_fk", + "tableFrom": "smoke_run_steps", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "smoke_run_steps_run_id_smoke_runs_id_fk": { + "name": "smoke_run_steps_run_id_smoke_runs_id_fk", + "tableFrom": "smoke_run_steps", + "tableTo": "smoke_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.smoke_runs": { + "name": "smoke_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'running'" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "smoke_runs_company_started_idx": { + "name": "smoke_runs_company_started_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "smoke_runs_company_status_idx": { + "name": "smoke_runs_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "smoke_runs_company_id_companies_id_fk": { + "name": "smoke_runs_company_id_companies_id_fk", + "tableFrom": "smoke_runs", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.status_card_updates": { + "name": "status_card_updates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "card_id": { + "name": "card_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "generation_issue_id": { + "name": "generation_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "changes": { + "name": "changes", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "input_tokens": { + "name": "input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "output_tokens": { + "name": "output_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "cost_cents": { + "name": "cost_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "query_version": { + "name": "query_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "change_summary": { + "name": "change_summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "status_card_updates_card_started_idx": { + "name": "status_card_updates_card_started_idx", + "columns": [ + { + "expression": "card_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "status_card_updates_generation_issue_idx": { + "name": "status_card_updates_generation_issue_idx", + "columns": [ + { + "expression": "generation_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "status_card_updates_card_id_status_cards_id_fk": { + "name": "status_card_updates_card_id_status_cards_id_fk", + "tableFrom": "status_card_updates", + "tableTo": "status_cards", + "columnsFrom": [ + "card_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "status_card_updates_generation_issue_id_issues_id_fk": { + "name": "status_card_updates_generation_issue_id_issues_id_fk", + "tableFrom": "status_card_updates", + "tableTo": "issues", + "columnsFrom": [ + "generation_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "status_card_updates_run_id_heartbeat_runs_id_fk": { + "name": "status_card_updates_run_id_heartbeat_runs_id_fk", + "tableFrom": "status_card_updates", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.status_cards": { + "name": "status_cards", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title_pinned": { + "name": "title_pinned", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "interest_prompt": { + "name": "interest_prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "queries": { + "name": "queries", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "query_version": { + "name": "query_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "query_compiled_at": { + "name": "query_compiled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "query_compiled_by_agent_id": { + "name": "query_compiled_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "refresh_policy": { + "name": "refresh_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'compiling'" + }, + "pending_change_count": { + "name": "pending_change_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "pending_change_hash": { + "name": "pending_change_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_change_at": { + "name": "last_change_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "fingerprint": { + "name": "fingerprint", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "fingerprint_at": { + "name": "fingerprint_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "mentioned_issue_ids": { + "name": "mentioned_issue_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "document_id": { + "name": "document_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "last_update_run_kind": { + "name": "last_update_run_kind", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_generated_at": { + "name": "last_generated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_model": { + "name": "last_model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "generating_issue_id": { + "name": "generating_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_eval_at": { + "name": "next_eval_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_by_user_id": { + "name": "archived_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived_by_agent_id": { + "name": "archived_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "status_cards_company_archived_idx": { + "name": "status_cards_company_archived_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "status_cards_company_next_eval_idx": { + "name": "status_cards_company_next_eval_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_eval_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "status_cards_company_id_companies_id_fk": { + "name": "status_cards_company_id_companies_id_fk", + "tableFrom": "status_cards", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "status_cards_created_by_agent_id_agents_id_fk": { + "name": "status_cards_created_by_agent_id_agents_id_fk", + "tableFrom": "status_cards", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "status_cards_query_compiled_by_agent_id_agents_id_fk": { + "name": "status_cards_query_compiled_by_agent_id_agents_id_fk", + "tableFrom": "status_cards", + "tableTo": "agents", + "columnsFrom": [ + "query_compiled_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "status_cards_agent_id_agents_id_fk": { + "name": "status_cards_agent_id_agents_id_fk", + "tableFrom": "status_cards", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "status_cards_document_id_documents_id_fk": { + "name": "status_cards_document_id_documents_id_fk", + "tableFrom": "status_cards", + "tableTo": "documents", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "status_cards_generating_issue_id_issues_id_fk": { + "name": "status_cards_generating_issue_id_issues_id_fk", + "tableFrom": "status_cards", + "tableTo": "issues", + "columnsFrom": [ + "generating_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "status_cards_archived_by_agent_id_agents_id_fk": { + "name": "status_cards_archived_by_agent_id_agents_id_fk", + "tableFrom": "status_cards", + "tableTo": "agents", + "columnsFrom": [ + "archived_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.status_decision_effects": { + "name": "status_decision_effects", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "decision_id": { + "name": "decision_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "ordinal": { + "name": "ordinal", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "effect_kind": { + "name": "effect_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "delivery_state": { + "name": "delivery_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "delivered_at": { + "name": "delivered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "status_decision_effects_decision_ordinal_uq": { + "name": "status_decision_effects_decision_ordinal_uq", + "columns": [ + { + "expression": "decision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "ordinal", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "status_decision_effects_company_idempotency_uq": { + "name": "status_decision_effects_company_idempotency_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "status_decision_effects_company_id_companies_id_fk": { + "name": "status_decision_effects_company_id_companies_id_fk", + "tableFrom": "status_decision_effects", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "status_decision_effects_issue_company_fk": { + "name": "status_decision_effects_issue_company_fk", + "tableFrom": "status_decision_effects", + "tableTo": "issues", + "columnsFrom": [ + "company_id", + "issue_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "status_decision_effects_decision_owner_fk": { + "name": "status_decision_effects_decision_owner_fk", + "tableFrom": "status_decision_effects", + "tableTo": "status_decisions", + "columnsFrom": [ + "company_id", + "issue_id", + "decision_id" + ], + "columnsTo": [ + "company_id", + "issue_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.status_decisions": { + "name": "status_decisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "assessment_id": { + "name": "assessment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "decision_version": { + "name": "decision_version", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "policy_version": { + "name": "policy_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "from_status": { + "name": "from_status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "to_status": { + "name": "to_status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reason_code": { + "name": "reason_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decision_json": { + "name": "decision_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "decision_digest": { + "name": "decision_digest", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "application_state": { + "name": "application_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'proposed'" + }, + "supersedes_decision_id": { + "name": "supersedes_decision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "status_decisions_company_issue_version_uq": { + "name": "status_decisions_company_issue_version_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "decision_version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "status_decisions_company_assessment_uq": { + "name": "status_decisions_company_assessment_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "assessment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "status_decisions_company_issue_digest_uq": { + "name": "status_decisions_company_issue_digest_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "decision_digest", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "status_decisions_company_id_companies_id_fk": { + "name": "status_decisions_company_id_companies_id_fk", + "tableFrom": "status_decisions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "status_decisions_issue_company_fk": { + "name": "status_decisions_issue_company_fk", + "tableFrom": "status_decisions", + "tableTo": "issues", + "columnsFrom": [ + "company_id", + "issue_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "status_decisions_assessment_owner_fk": { + "name": "status_decisions_assessment_owner_fk", + "tableFrom": "status_decisions", + "tableTo": "work_assessments", + "columnsFrom": [ + "company_id", + "issue_id", + "run_id", + "assessment_id" + ], + "columnsTo": [ + "company_id", + "issue_id", + "run_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "status_decisions_supersedes_owner_fk": { + "name": "status_decisions_supersedes_owner_fk", + "tableFrom": "status_decisions", + "tableTo": "status_decisions", + "columnsFrom": [ + "company_id", + "issue_id", + "supersedes_decision_id" + ], + "columnsTo": [ + "company_id", + "issue_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "status_decisions_company_issue_id_uq": { + "name": "status_decisions_company_issue_id_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "issue_id", + "id" + ] + }, + "status_decisions_company_issue_run_assessment_id_uq": { + "name": "status_decisions_company_issue_run_assessment_id_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "issue_id", + "run_id", + "assessment_id", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.summary_slots": { + "name": "summary_slots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "scope_kind": { + "name": "scope_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope_id": { + "name": "scope_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "slot_key": { + "name": "slot_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "generating_issue_id": { + "name": "generating_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "last_generated_at": { + "name": "last_generated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_generated_by_agent_id": { + "name": "last_generated_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "last_model": { + "name": "last_model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "summary_slots_document_uq": { + "name": "summary_slots_document_uq", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "summary_slots_company_scope_idx": { + "name": "summary_slots_company_scope_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "summary_slots_company_generating_issue_idx": { + "name": "summary_slots_company_generating_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "generating_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "summary_slots_company_updated_idx": { + "name": "summary_slots_company_updated_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "summary_slots_company_id_companies_id_fk": { + "name": "summary_slots_company_id_companies_id_fk", + "tableFrom": "summary_slots", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "summary_slots_document_id_documents_id_fk": { + "name": "summary_slots_document_id_documents_id_fk", + "tableFrom": "summary_slots", + "tableTo": "documents", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "summary_slots_generating_issue_id_issues_id_fk": { + "name": "summary_slots_generating_issue_id_issues_id_fk", + "tableFrom": "summary_slots", + "tableTo": "issues", + "columnsFrom": [ + "generating_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "summary_slots_last_generated_by_agent_id_agents_id_fk": { + "name": "summary_slots_last_generated_by_agent_id_agents_id_fk", + "tableFrom": "summary_slots", + "tableTo": "agents", + "columnsFrom": [ + "last_generated_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "summary_slots_company_scope_slot_uq": { + "name": "summary_slots_company_scope_slot_uq", + "nullsNotDistinct": true, + "columns": [ + "company_id", + "scope_kind", + "scope_id", + "slot_key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_access_audit_events": { + "name": "tool_access_audit_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "gateway_id": { + "name": "gateway_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "gateway_token_id": { + "name": "gateway_token_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "gateway_public_id": { + "name": "gateway_public_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_name": { + "name": "client_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "correlation_id": { + "name": "correlation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "catalog_entry_id": { + "name": "catalog_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'system'" + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reason_code": { + "name": "reason_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "details": { + "name": "details", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_access_audit_company_created_idx": { + "name": "tool_access_audit_company_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_access_audit_connection_idx": { + "name": "tool_access_audit_connection_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_access_audit_gateway_idx": { + "name": "tool_access_audit_gateway_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "gateway_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_access_audit_events_company_id_companies_id_fk": { + "name": "tool_access_audit_events_company_id_companies_id_fk", + "tableFrom": "tool_access_audit_events", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_access_audit_events_gateway_id_tool_mcp_gateways_id_fk": { + "name": "tool_access_audit_events_gateway_id_tool_mcp_gateways_id_fk", + "tableFrom": "tool_access_audit_events", + "tableTo": "tool_mcp_gateways", + "columnsFrom": [ + "gateway_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_access_audit_events_gateway_token_id_tool_mcp_gateway_tokens_id_fk": { + "name": "tool_access_audit_events_gateway_token_id_tool_mcp_gateway_tokens_id_fk", + "tableFrom": "tool_access_audit_events", + "tableTo": "tool_mcp_gateway_tokens", + "columnsFrom": [ + "gateway_token_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_access_audit_events_connection_id_tool_connections_id_fk": { + "name": "tool_access_audit_events_connection_id_tool_connections_id_fk", + "tableFrom": "tool_access_audit_events", + "tableTo": "tool_connections", + "columnsFrom": [ + "connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_access_audit_events_catalog_entry_id_tool_catalog_entries_id_fk": { + "name": "tool_access_audit_events_catalog_entry_id_tool_catalog_entries_id_fk", + "tableFrom": "tool_access_audit_events", + "tableTo": "tool_catalog_entries", + "columnsFrom": [ + "catalog_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_action_deliveries": { + "name": "tool_action_deliveries", + "schema": "", + "columns": { + "action_request_id": { + "name": "action_request_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "interaction_id": { + "name": "interaction_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "delivered_at": { + "name": "delivered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_action_deliveries_pending_idx": { + "name": "tool_action_deliveries_pending_idx", + "columns": [ + { + "expression": "delivered_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_action_deliveries_action_request_id_tool_action_requests_id_fk": { + "name": "tool_action_deliveries_action_request_id_tool_action_requests_id_fk", + "tableFrom": "tool_action_deliveries", + "tableTo": "tool_action_requests", + "columnsFrom": [ + "action_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_action_deliveries_company_id_companies_id_fk": { + "name": "tool_action_deliveries_company_id_companies_id_fk", + "tableFrom": "tool_action_deliveries", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_action_deliveries_issue_id_issues_id_fk": { + "name": "tool_action_deliveries_issue_id_issues_id_fk", + "tableFrom": "tool_action_deliveries", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_action_deliveries_interaction_id_issue_thread_interactions_id_fk": { + "name": "tool_action_deliveries_interaction_id_issue_thread_interactions_id_fk", + "tableFrom": "tool_action_deliveries", + "tableTo": "issue_thread_interactions", + "columnsFrom": [ + "interaction_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_action_requests": { + "name": "tool_action_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "invocation_id": { + "name": "invocation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "interaction_id": { + "name": "interaction_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "canonical_arguments_hash": { + "name": "canonical_arguments_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canonical_arguments_summary": { + "name": "canonical_arguments_summary", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "signed_arguments": { + "name": "signed_arguments", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "preview_markdown": { + "name": "preview_markdown", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "requested_by_agent_id": { + "name": "requested_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "requested_by_user_id": { + "name": "requested_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resolved_by_agent_id": { + "name": "resolved_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "resolved_by_user_id": { + "name": "resolved_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "decided_by_agent_id": { + "name": "decided_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "decided_by_user_id": { + "name": "decided_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "decided_at": { + "name": "decided_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_action_requests_company_status_idx": { + "name": "tool_action_requests_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_action_requests_invocation_idx": { + "name": "tool_action_requests_invocation_idx", + "columns": [ + { + "expression": "invocation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_action_requests_issue_idx": { + "name": "tool_action_requests_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_action_requests_company_id_companies_id_fk": { + "name": "tool_action_requests_company_id_companies_id_fk", + "tableFrom": "tool_action_requests", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_action_requests_invocation_id_tool_invocations_id_fk": { + "name": "tool_action_requests_invocation_id_tool_invocations_id_fk", + "tableFrom": "tool_action_requests", + "tableTo": "tool_invocations", + "columnsFrom": [ + "invocation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_action_requests_issue_id_issues_id_fk": { + "name": "tool_action_requests_issue_id_issues_id_fk", + "tableFrom": "tool_action_requests", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_action_requests_interaction_id_issue_thread_interactions_id_fk": { + "name": "tool_action_requests_interaction_id_issue_thread_interactions_id_fk", + "tableFrom": "tool_action_requests", + "tableTo": "issue_thread_interactions", + "columnsFrom": [ + "interaction_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_action_requests_approval_id_approvals_id_fk": { + "name": "tool_action_requests_approval_id_approvals_id_fk", + "tableFrom": "tool_action_requests", + "tableTo": "approvals", + "columnsFrom": [ + "approval_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_action_requests_requested_by_agent_id_agents_id_fk": { + "name": "tool_action_requests_requested_by_agent_id_agents_id_fk", + "tableFrom": "tool_action_requests", + "tableTo": "agents", + "columnsFrom": [ + "requested_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_action_requests_resolved_by_agent_id_agents_id_fk": { + "name": "tool_action_requests_resolved_by_agent_id_agents_id_fk", + "tableFrom": "tool_action_requests", + "tableTo": "agents", + "columnsFrom": [ + "resolved_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_action_requests_decided_by_agent_id_agents_id_fk": { + "name": "tool_action_requests_decided_by_agent_id_agents_id_fk", + "tableFrom": "tool_action_requests", + "tableTo": "agents", + "columnsFrom": [ + "decided_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_applications": { + "name": "tool_applications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "application_key": { + "name": "application_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owner_agent_id": { + "name": "owner_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owner_user_id": { + "name": "owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_applications_company_idx": { + "name": "tool_applications_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_applications_company_status_idx": { + "name": "tool_applications_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_applications_company_name_uq": { + "name": "tool_applications_company_name_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_applications_company_key_uq": { + "name": "tool_applications_company_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "application_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_applications_company_id_companies_id_fk": { + "name": "tool_applications_company_id_companies_id_fk", + "tableFrom": "tool_applications", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_applications_plugin_id_plugins_id_fk": { + "name": "tool_applications_plugin_id_plugins_id_fk", + "tableFrom": "tool_applications", + "tableTo": "plugins", + "columnsFrom": [ + "plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_applications_owner_agent_id_agents_id_fk": { + "name": "tool_applications_owner_agent_id_agents_id_fk", + "tableFrom": "tool_applications", + "tableTo": "agents", + "columnsFrom": [ + "owner_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_call_events": { + "name": "tool_call_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'system'" + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "gateway_id": { + "name": "gateway_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "gateway_token_id": { + "name": "gateway_token_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "gateway_public_id": { + "name": "gateway_public_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_subject_type": { + "name": "client_subject_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_subject_id": { + "name": "client_subject_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_name": { + "name": "client_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mcp_session_id": { + "name": "mcp_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "correlation_id": { + "name": "correlation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "application_id": { + "name": "application_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "catalog_entry_id": { + "name": "catalog_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "invocation_id": { + "name": "invocation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "action_request_id": { + "name": "action_request_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "runtime_slot_id": { + "name": "runtime_slot_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "tool_name": { + "name": "tool_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "decision": { + "name": "decision", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "matched_policy_ids": { + "name": "matched_policy_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "reason_code": { + "name": "reason_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "policy_explanation": { + "name": "policy_explanation", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "credential_scope_summary": { + "name": "credential_scope_summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "header_policy_summary": { + "name": "header_policy_summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "latency_ms": { + "name": "latency_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "arguments_summary": { + "name": "arguments_summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "request_hash": { + "name": "request_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "request_summary": { + "name": "request_summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "result_hash": { + "name": "result_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "result_summary": { + "name": "result_summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "result_size_bytes": { + "name": "result_size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "redaction_plan": { + "name": "redaction_plan", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "rate_limit_state": { + "name": "rate_limit_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_call_events_company_created_idx": { + "name": "tool_call_events_company_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_call_events_run_idx": { + "name": "tool_call_events_run_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_call_events_issue_idx": { + "name": "tool_call_events_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_call_events_invocation_idx": { + "name": "tool_call_events_invocation_idx", + "columns": [ + { + "expression": "invocation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_call_events_gateway_idx": { + "name": "tool_call_events_gateway_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "gateway_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_call_events_company_id_companies_id_fk": { + "name": "tool_call_events_company_id_companies_id_fk", + "tableFrom": "tool_call_events", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_call_events_agent_id_agents_id_fk": { + "name": "tool_call_events_agent_id_agents_id_fk", + "tableFrom": "tool_call_events", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_call_events_run_id_heartbeat_runs_id_fk": { + "name": "tool_call_events_run_id_heartbeat_runs_id_fk", + "tableFrom": "tool_call_events", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_call_events_issue_id_issues_id_fk": { + "name": "tool_call_events_issue_id_issues_id_fk", + "tableFrom": "tool_call_events", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_call_events_gateway_id_tool_mcp_gateways_id_fk": { + "name": "tool_call_events_gateway_id_tool_mcp_gateways_id_fk", + "tableFrom": "tool_call_events", + "tableTo": "tool_mcp_gateways", + "columnsFrom": [ + "gateway_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_call_events_gateway_token_id_tool_mcp_gateway_tokens_id_fk": { + "name": "tool_call_events_gateway_token_id_tool_mcp_gateway_tokens_id_fk", + "tableFrom": "tool_call_events", + "tableTo": "tool_mcp_gateway_tokens", + "columnsFrom": [ + "gateway_token_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_call_events_application_id_tool_applications_id_fk": { + "name": "tool_call_events_application_id_tool_applications_id_fk", + "tableFrom": "tool_call_events", + "tableTo": "tool_applications", + "columnsFrom": [ + "application_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_call_events_connection_id_tool_connections_id_fk": { + "name": "tool_call_events_connection_id_tool_connections_id_fk", + "tableFrom": "tool_call_events", + "tableTo": "tool_connections", + "columnsFrom": [ + "connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_call_events_catalog_entry_id_tool_catalog_entries_id_fk": { + "name": "tool_call_events_catalog_entry_id_tool_catalog_entries_id_fk", + "tableFrom": "tool_call_events", + "tableTo": "tool_catalog_entries", + "columnsFrom": [ + "catalog_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_call_events_invocation_id_tool_invocations_id_fk": { + "name": "tool_call_events_invocation_id_tool_invocations_id_fk", + "tableFrom": "tool_call_events", + "tableTo": "tool_invocations", + "columnsFrom": [ + "invocation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_call_events_action_request_id_tool_action_requests_id_fk": { + "name": "tool_call_events_action_request_id_tool_action_requests_id_fk", + "tableFrom": "tool_call_events", + "tableTo": "tool_action_requests", + "columnsFrom": [ + "action_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_call_events_runtime_slot_id_tool_runtime_slots_id_fk": { + "name": "tool_call_events_runtime_slot_id_tool_runtime_slots_id_fk", + "tableFrom": "tool_call_events", + "tableTo": "tool_runtime_slots", + "columnsFrom": [ + "runtime_slot_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_catalog_entries": { + "name": "tool_catalog_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "application_id": { + "name": "application_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "entry_kind": { + "name": "entry_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'tool'" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tool_name": { + "name": "tool_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "input_schema": { + "name": "input_schema", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "output_schema": { + "name": "output_schema", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "annotations": { + "name": "annotations", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "risk_level": { + "name": "risk_level", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'read'" + }, + "is_read_only": { + "name": "is_read_only", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "is_write": { + "name": "is_write", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_destructive": { + "name": "is_destructive", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version_hash": { + "name": "version_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schema_hash": { + "name": "schema_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "first_seen_at": { + "name": "first_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "reviewed_at": { + "name": "reviewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "reviewed_by_agent_id": { + "name": "reviewed_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "reviewed_by_user_id": { + "name": "reviewed_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "quarantined_at": { + "name": "quarantined_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "quarantine_reason": { + "name": "quarantine_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_catalog_entries_company_idx": { + "name": "tool_catalog_entries_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_catalog_entries_application_idx": { + "name": "tool_catalog_entries_application_idx", + "columns": [ + { + "expression": "application_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_catalog_entries_connection_idx": { + "name": "tool_catalog_entries_connection_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_catalog_entries_company_status_idx": { + "name": "tool_catalog_entries_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_catalog_entries_connection_name_uq": { + "name": "tool_catalog_entries_connection_name_uq", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_catalog_entries_company_id_companies_id_fk": { + "name": "tool_catalog_entries_company_id_companies_id_fk", + "tableFrom": "tool_catalog_entries", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_catalog_entries_application_id_tool_applications_id_fk": { + "name": "tool_catalog_entries_application_id_tool_applications_id_fk", + "tableFrom": "tool_catalog_entries", + "tableTo": "tool_applications", + "columnsFrom": [ + "application_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_catalog_entries_connection_id_tool_connections_id_fk": { + "name": "tool_catalog_entries_connection_id_tool_connections_id_fk", + "tableFrom": "tool_catalog_entries", + "tableTo": "tool_connections", + "columnsFrom": [ + "connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_catalog_entries_reviewed_by_agent_id_agents_id_fk": { + "name": "tool_catalog_entries_reviewed_by_agent_id_agents_id_fk", + "tableFrom": "tool_catalog_entries", + "tableTo": "agents", + "columnsFrom": [ + "reviewed_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_connection_installs": { + "name": "tool_connection_installs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_connection_installs_company_target_idx": { + "name": "tool_connection_installs_company_target_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_connection_installs_connection_idx": { + "name": "tool_connection_installs_connection_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_connection_installs_target_uq": { + "name": "tool_connection_installs_target_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_connection_installs_company_id_companies_id_fk": { + "name": "tool_connection_installs_company_id_companies_id_fk", + "tableFrom": "tool_connection_installs", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_connection_installs_connection_id_tool_connections_id_fk": { + "name": "tool_connection_installs_connection_id_tool_connections_id_fk", + "tableFrom": "tool_connection_installs", + "tableTo": "tool_connections", + "columnsFrom": [ + "connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_connection_installs_created_by_agent_id_agents_id_fk": { + "name": "tool_connection_installs_created_by_agent_id_agents_id_fk", + "tableFrom": "tool_connection_installs", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "tool_connection_installs_target_type_check": { + "name": "tool_connection_installs_target_type_check", + "value": "\"tool_connection_installs\".\"target_type\" in ('company', 'agent')" + } + }, + "isRLSEnabled": false + }, + "public.tool_connections": { + "name": "tool_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "application_id": { + "name": "application_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uid": { + "name": "uid", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "connection_kind": { + "name": "connection_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'managed'" + }, + "connection_purpose": { + "name": "connection_purpose", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'tool'" + }, + "ownership": { + "name": "ownership", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'customer'" + }, + "transport": { + "name": "transport", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "auth_kind": { + "name": "auth_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "credential_source": { + "name": "credential_source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'paperclip_vault'" + }, + "external_credential": { + "name": "external_credential", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "credential_policy": { + "name": "credential_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'shared'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "transport_config": { + "name": "transport_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "credential_refs": { + "name": "credential_refs", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "credential_secret_refs": { + "name": "credential_secret_refs", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "health_status": { + "name": "health_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unchecked'" + }, + "health_message": { + "name": "health_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "health_checked_at": { + "name": "health_checked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_health_at": { + "name": "last_health_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_catalog_refresh_at": { + "name": "last_catalog_refresh_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_connections_company_idx": { + "name": "tool_connections_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_connections_application_idx": { + "name": "tool_connections_application_idx", + "columns": [ + { + "expression": "application_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_connections_company_enabled_idx": { + "name": "tool_connections_company_enabled_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_connections_company_uid_uq": { + "name": "tool_connections_company_uid_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "uid", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_connections_company_id_companies_id_fk": { + "name": "tool_connections_company_id_companies_id_fk", + "tableFrom": "tool_connections", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_connections_application_id_tool_applications_id_fk": { + "name": "tool_connections_application_id_tool_applications_id_fk", + "tableFrom": "tool_connections", + "tableTo": "tool_applications", + "columnsFrom": [ + "application_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tool_connections_created_by_agent_id_agents_id_fk": { + "name": "tool_connections_created_by_agent_id_agents_id_fk", + "tableFrom": "tool_connections", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "tool_connections_company_id_uq": { + "name": "tool_connections_company_id_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "tool_connections_ownership_check": { + "name": "tool_connections_ownership_check", + "value": "\"tool_connections\".\"ownership\" in ('platform_shared', 'platform_provisioned', 'customer', 'dcr')" + }, + "tool_connections_transport_check": { + "name": "tool_connections_transport_check", + "value": "\"tool_connections\".\"transport\" in ('mcp_remote', 'rest_api', 'local_stdio', 'chat_sdk', 'runtime_auth')" + }, + "tool_connections_purpose_check": { + "name": "tool_connections_purpose_check", + "value": "\"tool_connections\".\"connection_purpose\" in ('tool', 'channel', 'ai')" + }, + "tool_connections_channel_transport_check": { + "name": "tool_connections_channel_transport_check", + "value": "(\n (\"tool_connections\".\"connection_purpose\" = 'tool' and \"tool_connections\".\"transport\" not in ('chat_sdk', 'runtime_auth'))\n or\n (\"tool_connections\".\"connection_purpose\" = 'channel' and (\"tool_connections\".\"transport\" = 'chat_sdk' or (\"tool_connections\".\"transport\" = 'rest_api' and \"tool_connections\".\"config\"->>'provider' = 'agentmail')))\n or\n (\"tool_connections\".\"connection_purpose\" = 'ai' and \"tool_connections\".\"transport\" = 'runtime_auth')\n )" + }, + "tool_connections_auth_kind_check": { + "name": "tool_connections_auth_kind_check", + "value": "\"tool_connections\".\"auth_kind\" in ('oauth', 'api_key', 'none')" + }, + "tool_connections_credential_source_check": { + "name": "tool_connections_credential_source_check", + "value": "\"tool_connections\".\"credential_source\" in ('paperclip_vault', 'vercel_connect')" + }, + "tool_connections_credential_source_one_of_check": { + "name": "tool_connections_credential_source_one_of_check", + "value": "(\n (\"tool_connections\".\"credential_source\" = 'paperclip_vault' and \"tool_connections\".\"external_credential\" is null)\n or\n (\"tool_connections\".\"credential_source\" = 'vercel_connect' and \"tool_connections\".\"external_credential\" is not null and jsonb_array_length(\"tool_connections\".\"credential_refs\") = 0 and jsonb_array_length(\"tool_connections\".\"credential_secret_refs\") = 0)\n )" + }, + "tool_connections_credential_policy_check": { + "name": "tool_connections_credential_policy_check", + "value": "\"tool_connections\".\"credential_policy\" in ('shared', 'per_user', 'per_user_with_fallback', 'per_agent')" + } + }, + "isRLSEnabled": false + }, + "public.tool_gateway_rate_limit_counters": { + "name": "tool_gateway_rate_limit_counters", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "counter_key": { + "name": "counter_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "window_start_at": { + "name": "window_start_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "window_ms": { + "name": "window_ms", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "limit": { + "name": "limit", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "reset_at": { + "name": "reset_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_gateway_rate_limit_counters_company_idx": { + "name": "tool_gateway_rate_limit_counters_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_gateway_rate_limit_counters_window_uq": { + "name": "tool_gateway_rate_limit_counters_window_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "counter_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "window_start_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_gateway_rate_limit_counters_company_id_companies_id_fk": { + "name": "tool_gateway_rate_limit_counters_company_id_companies_id_fk", + "tableFrom": "tool_gateway_rate_limit_counters", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_gateway_sessions": { + "name": "tool_gateway_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "gateway_id": { + "name": "gateway_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "gateway_token_id": { + "name": "gateway_token_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "gateway_public_id": { + "name": "gateway_public_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_subject_type": { + "name": "client_subject_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_subject_id": { + "name": "client_subject_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_name": { + "name": "client_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mcp_session_id": { + "name": "mcp_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "correlation_id": { + "name": "correlation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_gateway_sessions_token_hash_uq": { + "name": "tool_gateway_sessions_token_hash_uq", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_gateway_sessions_company_agent_idx": { + "name": "tool_gateway_sessions_company_agent_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_gateway_sessions_company_expires_idx": { + "name": "tool_gateway_sessions_company_expires_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_gateway_sessions_run_idx": { + "name": "tool_gateway_sessions_run_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_gateway_sessions_issue_idx": { + "name": "tool_gateway_sessions_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_gateway_sessions_gateway_idx": { + "name": "tool_gateway_sessions_gateway_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "gateway_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_gateway_sessions_company_id_companies_id_fk": { + "name": "tool_gateway_sessions_company_id_companies_id_fk", + "tableFrom": "tool_gateway_sessions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_gateway_sessions_agent_id_agents_id_fk": { + "name": "tool_gateway_sessions_agent_id_agents_id_fk", + "tableFrom": "tool_gateway_sessions", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_gateway_sessions_run_id_heartbeat_runs_id_fk": { + "name": "tool_gateway_sessions_run_id_heartbeat_runs_id_fk", + "tableFrom": "tool_gateway_sessions", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_gateway_sessions_issue_id_issues_id_fk": { + "name": "tool_gateway_sessions_issue_id_issues_id_fk", + "tableFrom": "tool_gateway_sessions", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_gateway_sessions_project_id_projects_id_fk": { + "name": "tool_gateway_sessions_project_id_projects_id_fk", + "tableFrom": "tool_gateway_sessions", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_gateway_sessions_gateway_id_tool_mcp_gateways_id_fk": { + "name": "tool_gateway_sessions_gateway_id_tool_mcp_gateways_id_fk", + "tableFrom": "tool_gateway_sessions", + "tableTo": "tool_mcp_gateways", + "columnsFrom": [ + "gateway_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_gateway_sessions_gateway_token_id_tool_mcp_gateway_tokens_id_fk": { + "name": "tool_gateway_sessions_gateway_token_id_tool_mcp_gateway_tokens_id_fk", + "tableFrom": "tool_gateway_sessions", + "tableTo": "tool_mcp_gateway_tokens", + "columnsFrom": [ + "gateway_token_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_invocations": { + "name": "tool_invocations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'system'" + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "gateway_id": { + "name": "gateway_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "gateway_token_id": { + "name": "gateway_token_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "gateway_public_id": { + "name": "gateway_public_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_subject_type": { + "name": "client_subject_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_subject_id": { + "name": "client_subject_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_name": { + "name": "client_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mcp_session_id": { + "name": "mcp_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "correlation_id": { + "name": "correlation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "application_id": { + "name": "application_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "catalog_entry_id": { + "name": "catalog_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "catalog_version_hash": { + "name": "catalog_version_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "catalog_schema_hash": { + "name": "catalog_schema_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_type": { + "name": "provider_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "application_key": { + "name": "application_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "upstream_tool_name": { + "name": "upstream_tool_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "risk_level": { + "name": "risk_level", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tool_name": { + "name": "tool_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "arguments_hash": { + "name": "arguments_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "arguments_summary": { + "name": "arguments_summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "policy_decision": { + "name": "policy_decision", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "matched_policy_ids": { + "name": "matched_policy_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "policy_explanation": { + "name": "policy_explanation", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "credential_scope_summary": { + "name": "credential_scope_summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "header_policy_summary": { + "name": "header_policy_summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "approval_state": { + "name": "approval_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'not_required'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "upstream_request_id": { + "name": "upstream_request_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "result_hash": { + "name": "result_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "result_summary": { + "name": "result_summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "result_size_bytes": { + "name": "result_size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "result_artifact_id": { + "name": "result_artifact_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_invocations_company_created_idx": { + "name": "tool_invocations_company_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_invocations_run_idx": { + "name": "tool_invocations_run_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_invocations_issue_idx": { + "name": "tool_invocations_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_invocations_gateway_idx": { + "name": "tool_invocations_gateway_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "gateway_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_invocations_company_idempotency_uq": { + "name": "tool_invocations_company_idempotency_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_invocations_company_id_companies_id_fk": { + "name": "tool_invocations_company_id_companies_id_fk", + "tableFrom": "tool_invocations", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_invocations_agent_id_agents_id_fk": { + "name": "tool_invocations_agent_id_agents_id_fk", + "tableFrom": "tool_invocations", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_invocations_issue_id_issues_id_fk": { + "name": "tool_invocations_issue_id_issues_id_fk", + "tableFrom": "tool_invocations", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_invocations_run_id_heartbeat_runs_id_fk": { + "name": "tool_invocations_run_id_heartbeat_runs_id_fk", + "tableFrom": "tool_invocations", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_invocations_gateway_id_tool_mcp_gateways_id_fk": { + "name": "tool_invocations_gateway_id_tool_mcp_gateways_id_fk", + "tableFrom": "tool_invocations", + "tableTo": "tool_mcp_gateways", + "columnsFrom": [ + "gateway_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_invocations_gateway_token_id_tool_mcp_gateway_tokens_id_fk": { + "name": "tool_invocations_gateway_token_id_tool_mcp_gateway_tokens_id_fk", + "tableFrom": "tool_invocations", + "tableTo": "tool_mcp_gateway_tokens", + "columnsFrom": [ + "gateway_token_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_invocations_application_id_tool_applications_id_fk": { + "name": "tool_invocations_application_id_tool_applications_id_fk", + "tableFrom": "tool_invocations", + "tableTo": "tool_applications", + "columnsFrom": [ + "application_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_invocations_connection_id_tool_connections_id_fk": { + "name": "tool_invocations_connection_id_tool_connections_id_fk", + "tableFrom": "tool_invocations", + "tableTo": "tool_connections", + "columnsFrom": [ + "connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_invocations_catalog_entry_id_tool_catalog_entries_id_fk": { + "name": "tool_invocations_catalog_entry_id_tool_catalog_entries_id_fk", + "tableFrom": "tool_invocations", + "tableTo": "tool_catalog_entries", + "columnsFrom": [ + "catalog_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_mcp_gateway_tokens": { + "name": "tool_mcp_gateway_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "gateway_id": { + "name": "gateway_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_prefix": { + "name": "token_prefix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "subject_type": { + "name": "subject_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'gateway_client'" + }, + "subject_id": { + "name": "subject_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_label": { + "name": "client_label", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "owner_note": { + "name": "owner_note", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "allowed_actions": { + "name": "allowed_actions", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[\"tools/list\",\"tools/call\"]'::jsonb" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "expiry_override_reason": { + "name": "expiry_override_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expiry_override_by_user_id": { + "name": "expiry_override_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expiry_override_by_agent_id": { + "name": "expiry_override_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "expiry_override_at": { + "name": "expiry_override_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_mcp_gateway_tokens_token_hash_uq": { + "name": "tool_mcp_gateway_tokens_token_hash_uq", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_mcp_gateway_tokens_expiry_idx": { + "name": "tool_mcp_gateway_tokens_expiry_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_mcp_gateway_tokens_gateway_idx": { + "name": "tool_mcp_gateway_tokens_gateway_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "gateway_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_mcp_gateway_tokens_subject_idx": { + "name": "tool_mcp_gateway_tokens_subject_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subject_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subject_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_mcp_gateway_tokens_company_expires_idx": { + "name": "tool_mcp_gateway_tokens_company_expires_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_mcp_gateway_tokens_company_id_companies_id_fk": { + "name": "tool_mcp_gateway_tokens_company_id_companies_id_fk", + "tableFrom": "tool_mcp_gateway_tokens", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_mcp_gateway_tokens_gateway_id_tool_mcp_gateways_id_fk": { + "name": "tool_mcp_gateway_tokens_gateway_id_tool_mcp_gateways_id_fk", + "tableFrom": "tool_mcp_gateway_tokens", + "tableTo": "tool_mcp_gateways", + "columnsFrom": [ + "gateway_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_mcp_gateway_tokens_expiry_override_by_agent_id_agents_id_fk": { + "name": "tool_mcp_gateway_tokens_expiry_override_by_agent_id_agents_id_fk", + "tableFrom": "tool_mcp_gateway_tokens", + "tableTo": "agents", + "columnsFrom": [ + "expiry_override_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_mcp_gateway_tokens_created_by_agent_id_agents_id_fk": { + "name": "tool_mcp_gateway_tokens_created_by_agent_id_agents_id_fk", + "tableFrom": "tool_mcp_gateway_tokens", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_mcp_gateways": { + "name": "tool_mcp_gateways", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "gateway_public_id": { + "name": "gateway_public_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'gw_' || replace(gen_random_uuid()::text, '-', '')" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_slug": { + "name": "display_slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "default_profile_mode": { + "name": "default_profile_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'gateway_only'" + }, + "context_scope_type": { + "name": "context_scope_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "context_scope_id": { + "name": "context_scope_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approval_issue_id": { + "name": "approval_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "auth_config": { + "name": "auth_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"version\":1,\"bearer\":{\"enabled\":true,\"tokenPrefix\":\"pcgw\",\"defaultTtlSeconds\":7776000,\"requireFiniteExpiry\":true,\"longLivedTokenRequiresOverride\":true},\"oauth\":{\"enabled\":false,\"reservedFor\":\"v1_5\",\"dynamicClientRegistration\":false,\"authorizationCodePkce\":false}}'::jsonb" + }, + "header_policy": { + "name": "header_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"version\":1,\"callerPassthrough\":{\"enabled\":false,\"allowedHeaders\":[]},\"staticHeaders\":[],\"generatedMetadata\":{\"enabled\":false,\"allowedHeaders\":[]},\"responseHeaders\":{\"forwardMcpRequiredHeaders\":true,\"forwardSafeCacheHeaders\":true}}'::jsonb" + }, + "metadata_policy": { + "name": "metadata_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"version\":1,\"forwardCompanyId\":false,\"forwardGatewayId\":false,\"forwardProjectId\":false,\"forwardIssueId\":false,\"forwardAgentId\":false,\"forwardRunId\":false,\"forwardCorrelationId\":true}'::jsonb" + }, + "on_demand_tools_config": { + "name": "on_demand_tools_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"enabled\":false,\"searchToolName\":\"search_tools\",\"runToolName\":\"run_tool\"}'::jsonb" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_mcp_gateways_company_idx": { + "name": "tool_mcp_gateways_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_mcp_gateways_company_status_idx": { + "name": "tool_mcp_gateways_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_mcp_gateways_profile_idx": { + "name": "tool_mcp_gateways_profile_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_mcp_gateways_public_id_uq": { + "name": "tool_mcp_gateways_public_id_uq", + "columns": [ + { + "expression": "gateway_public_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_mcp_gateways_company_slug_uq": { + "name": "tool_mcp_gateways_company_slug_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_mcp_gateways_company_name_uq": { + "name": "tool_mcp_gateways_company_name_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_mcp_gateways_company_id_companies_id_fk": { + "name": "tool_mcp_gateways_company_id_companies_id_fk", + "tableFrom": "tool_mcp_gateways", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_mcp_gateways_profile_id_tool_profiles_id_fk": { + "name": "tool_mcp_gateways_profile_id_tool_profiles_id_fk", + "tableFrom": "tool_mcp_gateways", + "tableTo": "tool_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "tool_mcp_gateways_agent_id_agents_id_fk": { + "name": "tool_mcp_gateways_agent_id_agents_id_fk", + "tableFrom": "tool_mcp_gateways", + "tableTo": "agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_mcp_gateways_project_id_projects_id_fk": { + "name": "tool_mcp_gateways_project_id_projects_id_fk", + "tableFrom": "tool_mcp_gateways", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_mcp_gateways_issue_id_issues_id_fk": { + "name": "tool_mcp_gateways_issue_id_issues_id_fk", + "tableFrom": "tool_mcp_gateways", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_mcp_gateways_approval_issue_id_issues_id_fk": { + "name": "tool_mcp_gateways_approval_issue_id_issues_id_fk", + "tableFrom": "tool_mcp_gateways", + "tableTo": "issues", + "columnsFrom": [ + "approval_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_mcp_gateways_created_by_agent_id_agents_id_fk": { + "name": "tool_mcp_gateways_created_by_agent_id_agents_id_fk", + "tableFrom": "tool_mcp_gateways", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_oauth_states": { + "name": "tool_oauth_states", + "schema": "", + "columns": { + "state": { + "name": "state", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "code_verifier": { + "name": "code_verifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_actor_type": { + "name": "created_by_actor_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_session_id": { + "name": "created_by_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subject_user_id": { + "name": "subject_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subject_agent_id": { + "name": "subject_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "requested_scopes": { + "name": "requested_scopes", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "return_to": { + "name": "return_to", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "interaction_id": { + "name": "interaction_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_oauth_states_company_idx": { + "name": "tool_oauth_states_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_oauth_states_connection_idx": { + "name": "tool_oauth_states_connection_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_oauth_states_actor_idx": { + "name": "tool_oauth_states_actor_idx", + "columns": [ + { + "expression": "created_by_actor_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_by_actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_oauth_states_subject_agent_idx": { + "name": "tool_oauth_states_subject_agent_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subject_agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_oauth_states_expires_at_idx": { + "name": "tool_oauth_states_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_oauth_states_company_id_companies_id_fk": { + "name": "tool_oauth_states_company_id_companies_id_fk", + "tableFrom": "tool_oauth_states", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_oauth_states_connection_id_tool_connections_id_fk": { + "name": "tool_oauth_states_connection_id_tool_connections_id_fk", + "tableFrom": "tool_oauth_states", + "tableTo": "tool_connections", + "columnsFrom": [ + "connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_oauth_states_subject_agent_id_agents_id_fk": { + "name": "tool_oauth_states_subject_agent_id_agents_id_fk", + "tableFrom": "tool_oauth_states", + "tableTo": "agents", + "columnsFrom": [ + "subject_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_policies": { + "name": "tool_policies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "policy_type": { + "name": "policy_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 100 + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "selectors": { + "name": "selectors", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "conditions": { + "name": "conditions", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_policies_company_enabled_idx": { + "name": "tool_policies_company_enabled_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_policies_company_type_idx": { + "name": "tool_policies_company_type_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "policy_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_policies_company_name_uq": { + "name": "tool_policies_company_name_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_policies_company_id_companies_id_fk": { + "name": "tool_policies_company_id_companies_id_fk", + "tableFrom": "tool_policies", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_policies_created_by_agent_id_agents_id_fk": { + "name": "tool_policies_created_by_agent_id_agents_id_fk", + "tableFrom": "tool_policies", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_profile_bindings": { + "name": "tool_profile_bindings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 100 + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_profile_bindings_company_target_idx": { + "name": "tool_profile_bindings_company_target_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_profile_bindings_target_profile_uq": { + "name": "tool_profile_bindings_target_profile_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_profile_bindings_company_id_companies_id_fk": { + "name": "tool_profile_bindings_company_id_companies_id_fk", + "tableFrom": "tool_profile_bindings", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_profile_bindings_profile_id_tool_profiles_id_fk": { + "name": "tool_profile_bindings_profile_id_tool_profiles_id_fk", + "tableFrom": "tool_profile_bindings", + "tableTo": "tool_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_profile_bindings_created_by_agent_id_agents_id_fk": { + "name": "tool_profile_bindings_created_by_agent_id_agents_id_fk", + "tableFrom": "tool_profile_bindings", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_profile_entries": { + "name": "tool_profile_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "selector_type": { + "name": "selector_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "effect": { + "name": "effect", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'include'" + }, + "application_id": { + "name": "application_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "catalog_entry_id": { + "name": "catalog_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "tool_name": { + "name": "tool_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "risk_level": { + "name": "risk_level", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "conditions": { + "name": "conditions", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_profile_entries_company_profile_idx": { + "name": "tool_profile_entries_company_profile_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_profile_entries_application_idx": { + "name": "tool_profile_entries_application_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "application_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_profile_entries_connection_idx": { + "name": "tool_profile_entries_connection_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_profile_entries_catalog_entry_idx": { + "name": "tool_profile_entries_catalog_entry_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "catalog_entry_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_profile_entries_company_id_companies_id_fk": { + "name": "tool_profile_entries_company_id_companies_id_fk", + "tableFrom": "tool_profile_entries", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_profile_entries_profile_id_tool_profiles_id_fk": { + "name": "tool_profile_entries_profile_id_tool_profiles_id_fk", + "tableFrom": "tool_profile_entries", + "tableTo": "tool_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_profile_entries_application_id_tool_applications_id_fk": { + "name": "tool_profile_entries_application_id_tool_applications_id_fk", + "tableFrom": "tool_profile_entries", + "tableTo": "tool_applications", + "columnsFrom": [ + "application_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_profile_entries_connection_id_tool_connections_id_fk": { + "name": "tool_profile_entries_connection_id_tool_connections_id_fk", + "tableFrom": "tool_profile_entries", + "tableTo": "tool_connections", + "columnsFrom": [ + "connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_profile_entries_catalog_entry_id_tool_catalog_entries_id_fk": { + "name": "tool_profile_entries_catalog_entry_id_tool_catalog_entries_id_fk", + "tableFrom": "tool_profile_entries", + "tableTo": "tool_catalog_entries", + "columnsFrom": [ + "catalog_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_profiles": { + "name": "tool_profiles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "profile_key": { + "name": "profile_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "default_action": { + "name": "default_action", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'deny'" + }, + "new_tools_reviewed_at": { + "name": "new_tools_reviewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_profiles_company_status_idx": { + "name": "tool_profiles_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_profiles_company_key_uq": { + "name": "tool_profiles_company_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "profile_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_profiles_company_name_uq": { + "name": "tool_profiles_company_name_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_profiles_company_id_companies_id_fk": { + "name": "tool_profiles_company_id_companies_id_fk", + "tableFrom": "tool_profiles", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_rate_limit_counters": { + "name": "tool_rate_limit_counters", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "policy_id": { + "name": "policy_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "counter_key": { + "name": "counter_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope_type": { + "name": "scope_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope_id": { + "name": "scope_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "window_kind": { + "name": "window_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "window_start_at": { + "name": "window_start_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "limit": { + "name": "limit", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "remaining": { + "name": "remaining", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "reset_at": { + "name": "reset_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_rate_limit_counters_company_idx": { + "name": "tool_rate_limit_counters_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_rate_limit_counters_window_uq": { + "name": "tool_rate_limit_counters_window_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "policy_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "counter_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "window_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "window_start_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_rate_limit_counters_company_id_companies_id_fk": { + "name": "tool_rate_limit_counters_company_id_companies_id_fk", + "tableFrom": "tool_rate_limit_counters", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_rate_limit_counters_policy_id_tool_policies_id_fk": { + "name": "tool_rate_limit_counters_policy_id_tool_policies_id_fk", + "tableFrom": "tool_rate_limit_counters", + "tableTo": "tool_policies", + "columnsFrom": [ + "policy_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_runtime_metric_counters": { + "name": "tool_runtime_metric_counters", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "metric": { + "name": "metric", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket_start_at": { + "name": "bucket_start_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_runtime_metric_counters_company_metric_idx": { + "name": "tool_runtime_metric_counters_company_metric_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "metric", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "bucket_start_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_runtime_metric_counters_bucket_uq": { + "name": "tool_runtime_metric_counters_bucket_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "metric", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "bucket_start_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_runtime_metric_counters_company_id_companies_id_fk": { + "name": "tool_runtime_metric_counters_company_id_companies_id_fk", + "tableFrom": "tool_runtime_metric_counters", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_runtime_slots": { + "name": "tool_runtime_slots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "application_id": { + "name": "application_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "project_workspace_id": { + "name": "project_workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "execution_workspace_id": { + "name": "execution_workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owner_scope_type": { + "name": "owner_scope_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'connection'" + }, + "owner_scope_id": { + "name": "owner_scope_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "runtime_kind": { + "name": "runtime_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'local_stdio'" + }, + "slot_key": { + "name": "slot_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'stopped'" + }, + "reuse_key": { + "name": "reuse_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_scope": { + "name": "workspace_scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credential_scope_hash": { + "name": "credential_scope_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_ref": { + "name": "provider_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "process_id": { + "name": "process_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "command_template_key": { + "name": "command_template_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "health_status": { + "name": "health_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unchecked'" + }, + "health_message": { + "name": "health_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_health_check_at": { + "name": "last_health_check_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_started_at": { + "name": "last_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "stopped_at": { + "name": "stopped_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "idle_expires_at": { + "name": "idle_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "idle_deadline_at": { + "name": "idle_deadline_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_runtime_slots_company_idx": { + "name": "tool_runtime_slots_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_runtime_slots_connection_idx": { + "name": "tool_runtime_slots_connection_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_runtime_slots_execution_workspace_idx": { + "name": "tool_runtime_slots_execution_workspace_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_runtime_slots_slot_key_uq": { + "name": "tool_runtime_slots_slot_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slot_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_runtime_slots_company_id_companies_id_fk": { + "name": "tool_runtime_slots_company_id_companies_id_fk", + "tableFrom": "tool_runtime_slots", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_runtime_slots_application_id_tool_applications_id_fk": { + "name": "tool_runtime_slots_application_id_tool_applications_id_fk", + "tableFrom": "tool_runtime_slots", + "tableTo": "tool_applications", + "columnsFrom": [ + "application_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_runtime_slots_connection_id_tool_connections_id_fk": { + "name": "tool_runtime_slots_connection_id_tool_connections_id_fk", + "tableFrom": "tool_runtime_slots", + "tableTo": "tool_connections", + "columnsFrom": [ + "connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_runtime_slots_project_workspace_id_project_workspaces_id_fk": { + "name": "tool_runtime_slots_project_workspace_id_project_workspaces_id_fk", + "tableFrom": "tool_runtime_slots", + "tableTo": "project_workspaces", + "columnsFrom": [ + "project_workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_runtime_slots_execution_workspace_id_execution_workspaces_id_fk": { + "name": "tool_runtime_slots_execution_workspace_id_execution_workspaces_id_fk", + "tableFrom": "tool_runtime_slots", + "tableTo": "execution_workspaces", + "columnsFrom": [ + "execution_workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tool_runtime_slots_issue_id_issues_id_fk": { + "name": "tool_runtime_slots_issue_id_issues_id_fk", + "tableFrom": "tool_runtime_slots", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tool_stdio_command_templates": { + "name": "tool_stdio_command_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "template_key": { + "name": "template_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "args": { + "name": "args", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "env_keys": { + "name": "env_keys", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "tools": { + "name": "tools", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "disabled_at": { + "name": "disabled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tool_stdio_command_templates_company_idx": { + "name": "tool_stdio_command_templates_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_stdio_command_templates_company_status_idx": { + "name": "tool_stdio_command_templates_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tool_stdio_command_templates_company_key_uq": { + "name": "tool_stdio_command_templates_company_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "template_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tool_stdio_command_templates_company_id_companies_id_fk": { + "name": "tool_stdio_command_templates_company_id_companies_id_fk", + "tableFrom": "tool_stdio_command_templates", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tool_stdio_command_templates_created_by_agent_id_agents_id_fk": { + "name": "tool_stdio_command_templates_created_by_agent_id_agents_id_fk", + "tableFrom": "tool_stdio_command_templates", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_inbox_agent_policies": { + "name": "user_inbox_agent_policies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "allowed_agent_ids": { + "name": "allowed_agent_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "user_inbox_agent_policies_company_user_uq": { + "name": "user_inbox_agent_policies_company_user_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_inbox_agent_policies_allowed_agent_ids_idx": { + "name": "user_inbox_agent_policies_allowed_agent_ids_idx", + "columns": [ + { + "expression": "allowed_agent_ids", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "user_inbox_agent_policies_company_id_companies_id_fk": { + "name": "user_inbox_agent_policies_company_id_companies_id_fk", + "tableFrom": "user_inbox_agent_policies", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "user_inbox_agent_policies_mode_check": { + "name": "user_inbox_agent_policies_mode_check", + "value": "\"user_inbox_agent_policies\".\"mode\" in ('open', 'allowlist', 'disabled')" + } + }, + "isRLSEnabled": false + }, + "public.user_secret_declarations": { + "name": "user_secret_declarations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_secret_definition_id": { + "name": "user_secret_definition_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config_path": { + "name": "config_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env_key": { + "name": "env_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version_selector": { + "name": "version_selector", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'latest'" + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "allow_missing_override": { + "name": "allow_missing_override", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "user_secret_declarations_company_idx": { + "name": "user_secret_declarations_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_secret_declarations_definition_idx": { + "name": "user_secret_declarations_definition_idx", + "columns": [ + { + "expression": "user_secret_definition_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_secret_declarations_target_idx": { + "name": "user_secret_declarations_target_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_secret_declarations_company_required_idx": { + "name": "user_secret_declarations_company_required_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "required", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_secret_declarations_target_path_uq": { + "name": "user_secret_declarations_target_path_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "config_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_secret_declarations_required_override_idx": { + "name": "user_secret_declarations_required_override_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "allow_missing_override", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"user_secret_declarations\".\"allow_missing_override\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_secret_declarations_company_id_companies_id_fk": { + "name": "user_secret_declarations_company_id_companies_id_fk", + "tableFrom": "user_secret_declarations", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_secret_declarations_user_secret_definition_id_user_secret_definitions_id_fk": { + "name": "user_secret_declarations_user_secret_definition_id_user_secret_definitions_id_fk", + "tableFrom": "user_secret_declarations", + "tableTo": "user_secret_definitions", + "columnsFrom": [ + "user_secret_definition_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_secret_definitions": { + "name": "user_secret_definitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'local_encrypted'" + }, + "managed_mode": { + "name": "managed_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'paperclip_managed'" + }, + "provider_config_id": { + "name": "provider_config_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider_metadata": { + "name": "provider_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "usage_guidance": { + "name": "usage_guidance", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_by_agent_id": { + "name": "updated_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "updated_by_user_id": { + "name": "updated_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "user_secret_definitions_company_status_idx": { + "name": "user_secret_definitions_company_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_secret_definitions_company_provider_idx": { + "name": "user_secret_definitions_company_provider_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_secret_definitions_provider_config_idx": { + "name": "user_secret_definitions_provider_config_idx", + "columns": [ + { + "expression": "provider_config_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_secret_definitions_company_key_uq": { + "name": "user_secret_definitions_company_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"user_secret_definitions\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_secret_definitions_company_id_companies_id_fk": { + "name": "user_secret_definitions_company_id_companies_id_fk", + "tableFrom": "user_secret_definitions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_secret_definitions_provider_config_id_company_secret_provider_configs_id_fk": { + "name": "user_secret_definitions_provider_config_id_company_secret_provider_configs_id_fk", + "tableFrom": "user_secret_definitions", + "tableTo": "company_secret_provider_configs", + "columnsFrom": [ + "provider_config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "user_secret_definitions_created_by_agent_id_agents_id_fk": { + "name": "user_secret_definitions_created_by_agent_id_agents_id_fk", + "tableFrom": "user_secret_definitions", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "user_secret_definitions_updated_by_agent_id_agents_id_fk": { + "name": "user_secret_definitions_updated_by_agent_id_agents_id_fk", + "tableFrom": "user_secret_definitions", + "tableTo": "agents", + "columnsFrom": [ + "updated_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_sidebar_preferences": { + "name": "user_sidebar_preferences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "company_order": { + "name": "company_order", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "user_sidebar_preferences_user_uq": { + "name": "user_sidebar_preferences_user_uq", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.work_assessments": { + "name": "work_assessments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "turn_id": { + "name": "turn_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contract_id": { + "name": "contract_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "result_id": { + "name": "result_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "trigger_kind": { + "name": "trigger_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trigger_ref": { + "name": "trigger_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "trigger_capability": { + "name": "trigger_capability", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "trigger_actor_company_id": { + "name": "trigger_actor_company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "prior_issue_status": { + "name": "prior_issue_status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prior_status_version": { + "name": "prior_status_version", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "prior_decision_id": { + "name": "prior_decision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "policy_version": { + "name": "policy_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "assessment_json": { + "name": "assessment_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "input_digest": { + "name": "input_digest", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "supersedes_assessment_id": { + "name": "supersedes_assessment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "work_assessments_company_issue_input_uq": { + "name": "work_assessments_company_issue_input_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "input_digest", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "work_assessments_company_id_companies_id_fk": { + "name": "work_assessments_company_id_companies_id_fk", + "tableFrom": "work_assessments", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "work_assessments_trigger_actor_company_id_companies_id_fk": { + "name": "work_assessments_trigger_actor_company_id_companies_id_fk", + "tableFrom": "work_assessments", + "tableTo": "companies", + "columnsFrom": [ + "trigger_actor_company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "work_assessments_issue_company_fk": { + "name": "work_assessments_issue_company_fk", + "tableFrom": "work_assessments", + "tableTo": "issues", + "columnsFrom": [ + "company_id", + "issue_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "work_assessments_run_owner_fk": { + "name": "work_assessments_run_owner_fk", + "tableFrom": "work_assessments", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "company_id", + "issue_id", + "run_id" + ], + "columnsTo": [ + "company_id", + "native_issue_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "work_assessments_contract_owner_fk": { + "name": "work_assessments_contract_owner_fk", + "tableFrom": "work_assessments", + "tableTo": "completion_contracts", + "columnsFrom": [ + "company_id", + "issue_id", + "contract_id" + ], + "columnsTo": [ + "company_id", + "issue_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "work_assessments_result_owner_fk": { + "name": "work_assessments_result_owner_fk", + "tableFrom": "work_assessments", + "tableTo": "native_run_results", + "columnsFrom": [ + "company_id", + "issue_id", + "run_id", + "result_id" + ], + "columnsTo": [ + "company_id", + "issue_id", + "run_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "work_assessments_supersedes_owner_fk": { + "name": "work_assessments_supersedes_owner_fk", + "tableFrom": "work_assessments", + "tableTo": "work_assessments", + "columnsFrom": [ + "company_id", + "issue_id", + "run_id", + "supersedes_assessment_id" + ], + "columnsTo": [ + "company_id", + "issue_id", + "run_id", + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "work_assessments_company_issue_run_id_uq": { + "name": "work_assessments_company_issue_run_id_uq", + "nullsNotDistinct": false, + "columns": [ + "company_id", + "issue_id", + "run_id", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "work_assessments_trigger_actor_company_check": { + "name": "work_assessments_trigger_actor_company_check", + "value": "\"work_assessments\".\"trigger_actor_company_id\" = \"work_assessments\".\"company_id\"" + } + }, + "isRLSEnabled": false + }, + "public.workspace_operations": { + "name": "workspace_operations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "execution_workspace_id": { + "name": "execution_workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "heartbeat_run_id": { + "name": "heartbeat_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "phase": { + "name": "phase", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cwd": { + "name": "cwd", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'running'" + }, + "exit_code": { + "name": "exit_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "log_store": { + "name": "log_store", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "log_ref": { + "name": "log_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "log_bytes": { + "name": "log_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "log_sha256": { + "name": "log_sha256", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "log_compressed": { + "name": "log_compressed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "stdout_excerpt": { + "name": "stdout_excerpt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stderr_excerpt": { + "name": "stderr_excerpt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_operations_company_run_started_idx": { + "name": "workspace_operations_company_run_started_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "heartbeat_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_operations_company_workspace_started_idx": { + "name": "workspace_operations_company_workspace_started_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_operations_company_workspace_issue_started_idx": { + "name": "workspace_operations_company_workspace_issue_started_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_operations_company_id_companies_id_fk": { + "name": "workspace_operations_company_id_companies_id_fk", + "tableFrom": "workspace_operations", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_operations_execution_workspace_id_execution_workspaces_id_fk": { + "name": "workspace_operations_execution_workspace_id_execution_workspaces_id_fk", + "tableFrom": "workspace_operations", + "tableTo": "execution_workspaces", + "columnsFrom": [ + "execution_workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "workspace_operations_heartbeat_run_id_heartbeat_runs_id_fk": { + "name": "workspace_operations_heartbeat_run_id_heartbeat_runs_id_fk", + "tableFrom": "workspace_operations", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "heartbeat_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "workspace_operations_issue_id_issues_id_fk": { + "name": "workspace_operations_issue_id_issues_id_fk", + "tableFrom": "workspace_operations", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_runtime_services": { + "name": "workspace_runtime_services", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "project_workspace_id": { + "name": "project_workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "execution_workspace_id": { + "name": "execution_workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "scope_type": { + "name": "scope_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope_id": { + "name": "scope_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "service_name": { + "name": "service_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lifecycle": { + "name": "lifecycle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reuse_key": { + "name": "reuse_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cwd": { + "name": "cwd", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_ref": { + "name": "provider_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_agent_id": { + "name": "owner_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "started_by_run_id": { + "name": "started_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "stopped_at": { + "name": "stopped_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "stop_policy": { + "name": "stop_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "exposure": { + "name": "exposure", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "exposure_handle": { + "name": "exposure_handle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "backend_url": { + "name": "backend_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "health_status": { + "name": "health_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_runtime_services_company_workspace_status_idx": { + "name": "workspace_runtime_services_company_workspace_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_runtime_services_company_execution_workspace_status_idx": { + "name": "workspace_runtime_services_company_execution_workspace_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_runtime_services_company_project_status_idx": { + "name": "workspace_runtime_services_company_project_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_runtime_services_run_idx": { + "name": "workspace_runtime_services_run_idx", + "columns": [ + { + "expression": "started_by_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_runtime_services_company_updated_idx": { + "name": "workspace_runtime_services_company_updated_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_runtime_services_company_id_companies_id_fk": { + "name": "workspace_runtime_services_company_id_companies_id_fk", + "tableFrom": "workspace_runtime_services", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workspace_runtime_services_project_id_projects_id_fk": { + "name": "workspace_runtime_services_project_id_projects_id_fk", + "tableFrom": "workspace_runtime_services", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "workspace_runtime_services_project_workspace_id_project_workspaces_id_fk": { + "name": "workspace_runtime_services_project_workspace_id_project_workspaces_id_fk", + "tableFrom": "workspace_runtime_services", + "tableTo": "project_workspaces", + "columnsFrom": [ + "project_workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "workspace_runtime_services_execution_workspace_id_execution_workspaces_id_fk": { + "name": "workspace_runtime_services_execution_workspace_id_execution_workspaces_id_fk", + "tableFrom": "workspace_runtime_services", + "tableTo": "execution_workspaces", + "columnsFrom": [ + "execution_workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "workspace_runtime_services_issue_id_issues_id_fk": { + "name": "workspace_runtime_services_issue_id_issues_id_fk", + "tableFrom": "workspace_runtime_services", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "workspace_runtime_services_owner_agent_id_agents_id_fk": { + "name": "workspace_runtime_services_owner_agent_id_agents_id_fk", + "tableFrom": "workspace_runtime_services", + "tableTo": "agents", + "columnsFrom": [ + "owner_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "workspace_runtime_services_started_by_run_id_heartbeat_runs_id_fk": { + "name": "workspace_runtime_services_started_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "workspace_runtime_services", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "started_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": { + "public.chat_telegram_draft_ids": { + "name": "chat_telegram_draft_ids", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/packages/db/src/migrations/meta/_journal.json b/packages/db/src/migrations/meta/_journal.json index 15430d35565..884ef21de64 100644 --- a/packages/db/src/migrations/meta/_journal.json +++ b/packages/db/src/migrations/meta/_journal.json @@ -2052,6 +2052,13 @@ "when": 1790891286527, "tag": "0294_chilly_marvel_apes", "breakpoints": true + }, + { + "idx": 295, + "version": "7", + "when": 1790983662974, + "tag": "0295_lean_baron_strucker", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/db/src/schema/accounting.ts b/packages/db/src/schema/accounting.ts new file mode 100644 index 00000000000..94869a84818 --- /dev/null +++ b/packages/db/src/schema/accounting.ts @@ -0,0 +1,121 @@ +import { sql } from "drizzle-orm"; +import { boolean, check, index, integer, jsonb, numeric, pgTable, text, timestamp, uniqueIndex, uuid } from "drizzle-orm/pg-core"; +import { companies } from "./companies.js"; +import { agents } from "./agents.js"; +import { heartbeatRuns } from "./heartbeat_runs.js"; + +export const providerBillingSnapshots = pgTable("provider_billing_snapshots", { + id: uuid("id").primaryKey().defaultRandom(), + companyId: uuid("company_id").notNull().references(() => companies.id, { onDelete: "cascade" }), + provider: text("provider").notNull(), + accountId: text("account_id").notNull(), + scopeId: text("scope_id").notNull(), + day: text("day").notNull(), + amountCents: numeric("amount_cents", { precision: 24, scale: 7 }).notNull(), + revision: integer("revision").notNull().default(1), + updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), +}, table => ({ + identity: uniqueIndex("provider_billing_snapshots_identity_idx").on(table.companyId, table.provider, table.accountId, table.scopeId, table.day), + amount: check("provider_billing_snapshots_amount_check", sql`${table.amountCents} >= 0`), +})); +import { costEvents } from "./cost_events.js"; + +export const runUsageReceipts = pgTable("run_usage_receipts", { + id: uuid("id").primaryKey(), + companyId: uuid("company_id").notNull().references(() => companies.id, { onDelete: "cascade" }), + runId: uuid("run_id").notNull().references(() => heartbeatRuns.id, { onDelete: "cascade" }), + sourceId: uuid("source_id").notNull(), + sequence: integer("sequence").notNull(), + receiptHash: text("receipt_hash").notNull(), + receiptJson: jsonb("receipt_json").$type>().notNull(), + complete: boolean("complete").notNull(), + receivedAt: timestamp("received_at", { withTimezone: true }).notNull(), + createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), +}, table => ({ + sourceSequence: uniqueIndex("run_usage_receipts_source_sequence_idx").on(table.companyId, table.runId, table.sourceId, table.sequence), + runLatest: index("run_usage_receipts_run_latest_idx").on(table.companyId, table.runId, table.receivedAt), + positiveSequence: check("run_usage_receipts_positive_sequence", sql`${table.sequence} > 0`), +})); + +export const accountingRuntimeBaselines = pgTable("accounting_runtime_baselines", { + agentId: uuid("agent_id").primaryKey().references(() => agents.id, { onDelete: "cascade" }), + companyId: uuid("company_id").notNull().references(() => companies.id, { onDelete: "cascade" }), + // Historical totals are preserved explicitly. Only v2 acknowledgements add + // to this baseline, including receipts whose occurrence date is backdated. + costCents: numeric("cost_cents", { precision: 24, scale: 7 }).notNull(), + inputTokens: numeric("input_tokens", { precision: 30, scale: 0 }).notNull(), + cachedInputTokens: numeric("cached_input_tokens", { precision: 30, scale: 0 }).notNull(), + outputTokens: numeric("output_tokens", { precision: 30, scale: 0 }).notNull(), + createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), +}, table => ({ company: index("accounting_runtime_baselines_company_idx").on(table.companyId) })); + +export const budgetReservations = pgTable("budget_reservations", { + id: uuid("id").primaryKey().defaultRandom(), + companyId: uuid("company_id").notNull().references(() => companies.id, { onDelete: "cascade" }), + runId: uuid("run_id").notNull().references(() => heartbeatRuns.id, { onDelete: "cascade" }), + agentId: uuid("agent_id").notNull().references(() => agents.id, { onDelete: "cascade" }), + projectId: uuid("project_id"), + amountCents: numeric("amount_cents", { precision: 24, scale: 7 }).notNull(), + state: text("state").notNull().default("held"), + providerStartedAt: timestamp("provider_started_at", { withTimezone: true }), + settledAt: timestamp("settled_at", { withTimezone: true }), + createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), +}, table => ({ + run: uniqueIndex("budget_reservations_run_idx").on(table.companyId, table.runId), + active: index("budget_reservations_active_idx").on(table.companyId, table.agentId, table.projectId).where(sql`${table.state} = 'held'`), + stateCheck: check("budget_reservations_state_check", sql`${table.state} in ('held', 'settled', 'released')`), + amountCheck: check("budget_reservations_amount_check", sql`${table.amountCents} >= 0`), +})); + +export const billingInvoices = pgTable("billing_invoices", { + id: uuid("id").primaryKey().defaultRandom(), + companyId: uuid("company_id").notNull().references(() => companies.id, { onDelete: "cascade" }), + biller: text("biller").notNull(), + externalId: text("external_id").notNull(), + currency: text("currency").notNull(), + receiptHash: text("receipt_hash").notNull(), + importedBy: text("imported_by").notNull(), + createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), +}, table => ({ identity: uniqueIndex("billing_invoices_identity_idx").on(table.companyId, table.biller, table.externalId) })); + +export const billingInvoiceLines = pgTable("billing_invoice_lines", { + id: uuid("id").primaryKey().defaultRandom(), + companyId: uuid("company_id").notNull().references(() => companies.id, { onDelete: "cascade" }), + invoiceId: uuid("invoice_id").notNull().references(() => billingInvoices.id, { onDelete: "cascade" }), + externalId: text("external_id").notNull(), + kind: text("kind").notNull(), + amountCents: numeric("amount_cents", { precision: 24, scale: 7 }).notNull(), + occurredAt: timestamp("occurred_at", { withTimezone: true }).notNull(), + costEventId: uuid("cost_event_id").references(() => costEvents.id, { onDelete: "cascade" }), + runId: uuid("run_id"), + providerRequestId: text("provider_request_id"), + model: text("model"), + pricing: jsonb("pricing").$type>().notNull().default({}), +}, table => ({ + identity: uniqueIndex("billing_invoice_lines_identity_idx").on(table.invoiceId, table.externalId), + company: index("billing_invoice_lines_company_idx").on(table.companyId, table.invoiceId), + amount: check("billing_invoice_lines_amount_check", sql`${table.amountCents} >= 0`), + kindCheck: check("billing_invoice_lines_kind_check", sql`${table.kind} in ('inference', 'fee', 'credit')`), +})); + +export const costAdjustments = pgTable("cost_adjustments", { + id: uuid("id").primaryKey().defaultRandom(), + companyId: uuid("company_id").notNull().references(() => companies.id, { onDelete: "cascade" }), + costEventId: uuid("cost_event_id").notNull().references(() => costEvents.id, { onDelete: "cascade" }), + invoiceLineId: uuid("invoice_line_id").references(() => billingInvoiceLines.id, { onDelete: "cascade" }), + idempotencyKey: text("idempotency_key").notNull(), + receiptHash: text("receipt_hash").notNull(), + previousCents: numeric("previous_cents", { precision: 24, scale: 7 }).notNull(), + correctedCents: numeric("corrected_cents", { precision: 24, scale: 7 }).notNull(), + previousStatus: text("previous_status").notNull(), + previousPricing: jsonb("previous_pricing").$type>().notNull().default({}), + reason: text("reason").notNull(), + actorId: text("actor_id").notNull(), + pricing: jsonb("pricing").$type>().notNull().default({}), + createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), +}, table => ({ + identity: uniqueIndex("cost_adjustments_receipt_idx").on(table.companyId, table.idempotencyKey), + event: index("cost_adjustments_event_idx").on(table.companyId, table.costEventId, table.createdAt), + invoiceLine: uniqueIndex("cost_adjustments_invoice_line_idx").on(table.companyId, table.invoiceLineId), + amount: check("cost_adjustments_amount_check", sql`${table.previousCents} >= 0 and ${table.correctedCents} >= 0`), +})); diff --git a/packages/db/src/schema/agent_runtime_state.ts b/packages/db/src/schema/agent_runtime_state.ts index b0095bb29b7..9da64882e69 100644 --- a/packages/db/src/schema/agent_runtime_state.ts +++ b/packages/db/src/schema/agent_runtime_state.ts @@ -1,4 +1,4 @@ -import { pgTable, uuid, text, timestamp, jsonb, bigint, index } from "drizzle-orm/pg-core"; +import { numeric, pgTable, uuid, text, timestamp, jsonb, bigint, index } from "drizzle-orm/pg-core"; import { agents } from "./agents.js"; import { companies } from "./companies.js"; @@ -15,7 +15,7 @@ export const agentRuntimeState = pgTable( totalInputTokens: bigint("total_input_tokens", { mode: "number" }).notNull().default(0), totalOutputTokens: bigint("total_output_tokens", { mode: "number" }).notNull().default(0), totalCachedInputTokens: bigint("total_cached_input_tokens", { mode: "number" }).notNull().default(0), - totalCostCents: bigint("total_cost_cents", { mode: "number" }).notNull().default(0), + totalCostCents: numeric("total_cost_cents", { precision: 24, scale: 7, mode: "number" }).notNull().default(0), lastError: text("last_error"), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), diff --git a/packages/db/src/schema/agents.ts b/packages/db/src/schema/agents.ts index 248c8c8d0b0..88d0f50dc9a 100644 --- a/packages/db/src/schema/agents.ts +++ b/packages/db/src/schema/agents.ts @@ -2,6 +2,7 @@ import type { AgentAppearance } from "@paperclipai/shared"; import { type AnyPgColumn, pgTable, + numeric, uuid, text, integer, @@ -31,7 +32,8 @@ export const agents = pgTable( runtimeConfig: jsonb("runtime_config").$type>().notNull().default({}), defaultEnvironmentId: uuid("default_environment_id").references(() => environments.id, { onDelete: "set null" }), budgetMonthlyCents: integer("budget_monthly_cents").notNull().default(0), - spentMonthlyCents: integer("spent_monthly_cents").notNull().default(0), + spendMonthUtc: text("spend_month_utc"), + spentMonthlyCents: numeric("spent_monthly_cents", { precision: 24, scale: 7, mode: "number" }).notNull().default(0), pauseReason: text("pause_reason"), pausedAt: timestamp("paused_at", { withTimezone: true }), errorReason: text("error_reason"), diff --git a/packages/db/src/schema/budget_incidents.ts b/packages/db/src/schema/budget_incidents.ts index 57b9b3b0126..e06de583342 100644 --- a/packages/db/src/schema/budget_incidents.ts +++ b/packages/db/src/schema/budget_incidents.ts @@ -1,5 +1,5 @@ import { sql } from "drizzle-orm"; -import { index, integer, pgTable, text, timestamp, uuid, uniqueIndex } from "drizzle-orm/pg-core"; +import { numeric, index, integer, pgTable, text, timestamp, uuid, uniqueIndex } from "drizzle-orm/pg-core"; import { approvals } from "./approvals.js"; import { budgetPolicies } from "./budget_policies.js"; import { companies } from "./companies.js"; @@ -18,7 +18,7 @@ export const budgetIncidents = pgTable( windowEnd: timestamp("window_end", { withTimezone: true }).notNull(), thresholdType: text("threshold_type").notNull(), amountLimit: integer("amount_limit").notNull(), - amountObserved: integer("amount_observed").notNull(), + amountObserved: numeric("amount_observed", { precision: 24, scale: 7, mode: "number" }).notNull(), status: text("status").notNull().default("open"), approvalId: uuid("approval_id").references(() => approvals.id), resolvedAt: timestamp("resolved_at", { withTimezone: true }), @@ -37,6 +37,6 @@ export const budgetIncidents = pgTable( table.policyId, table.windowStart, table.thresholdType, - ).where(sql`${table.status} <> 'dismissed'`), + ).where(sql`${table.status} = 'open'`), }), ); diff --git a/packages/db/src/schema/budget_policies.ts b/packages/db/src/schema/budget_policies.ts index 3713889b7db..07da0521cb7 100644 --- a/packages/db/src/schema/budget_policies.ts +++ b/packages/db/src/schema/budget_policies.ts @@ -1,4 +1,5 @@ -import { boolean, index, integer, pgTable, text, timestamp, uuid, uniqueIndex } from "drizzle-orm/pg-core"; +import { sql } from "drizzle-orm"; +import { check, boolean, index, integer, pgTable, text, timestamp, uuid, uniqueIndex, numeric } from "drizzle-orm/pg-core"; import { companies } from "./companies.js"; export const budgetPolicies = pgTable( @@ -11,16 +12,21 @@ export const budgetPolicies = pgTable( metric: text("metric").notNull().default("billed_cents"), windowKind: text("window_kind").notNull(), amount: integer("amount").notNull().default(0), + reservationCents: numeric("reservation_cents", { precision: 24, scale: 7 }).notNull().default("0"), warnPercent: integer("warn_percent").notNull().default(80), hardStopEnabled: boolean("hard_stop_enabled").notNull().default(true), + unpricedUsagePolicy: text("unpriced_usage_policy").notNull().default("block"), notifyEnabled: boolean("notify_enabled").notNull().default(true), isActive: boolean("is_active").notNull().default(true), + enforcementVersion: integer("enforcement_version").notNull().default(0), + enforcementDeliveredVersion: integer("enforcement_delivered_version").notNull().default(0), createdByUserId: text("created_by_user_id"), updatedByUserId: text("updated_by_user_id"), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), }, (table) => ({ + reservationNonnegative: check("budget_policies_reservation_nonnegative", sql`${table.reservationCents} >= 0`), companyScopeActiveIdx: index("budget_policies_company_scope_active_idx").on( table.companyId, table.scopeType, diff --git a/packages/db/src/schema/companies.ts b/packages/db/src/schema/companies.ts index c911fd9aa10..e31abb54491 100644 --- a/packages/db/src/schema/companies.ts +++ b/packages/db/src/schema/companies.ts @@ -1,5 +1,5 @@ import type { InteractionResolverGovernance } from "@paperclipai/shared"; -import { pgTable, uuid, text, integer, timestamp, boolean, jsonb, uniqueIndex } from "drizzle-orm/pg-core"; +import { numeric, pgTable, uuid, text, integer, timestamp, boolean, jsonb, uniqueIndex } from "drizzle-orm/pg-core"; export const companies = pgTable( "companies", @@ -13,7 +13,8 @@ export const companies = pgTable( issuePrefix: text("issue_prefix").notNull().default("PAP"), issueCounter: integer("issue_counter").notNull().default(0), budgetMonthlyCents: integer("budget_monthly_cents").notNull().default(0), - spentMonthlyCents: integer("spent_monthly_cents").notNull().default(0), + spendMonthUtc: text("spend_month_utc"), + spentMonthlyCents: numeric("spent_monthly_cents", { precision: 24, scale: 7, mode: "number" }).notNull().default(0), defaultResponsibleUserId: text("default_responsible_user_id"), requireBoardApprovalForNewAgents: boolean("require_board_approval_for_new_agents") .notNull() diff --git a/packages/db/src/schema/cost_events.ts b/packages/db/src/schema/cost_events.ts index 9ca547651f5..1abe90c7b87 100644 --- a/packages/db/src/schema/cost_events.ts +++ b/packages/db/src/schema/cost_events.ts @@ -1,4 +1,5 @@ -import { pgTable, uuid, text, timestamp, integer, index } from "drizzle-orm/pg-core"; +import { sql } from "drizzle-orm"; +import { pgTable, uuid, text, timestamp, integer, index, numeric, uniqueIndex, check, jsonb } from "drizzle-orm/pg-core"; import { companies } from "./companies.js"; import { agents } from "./agents.js"; import { issues } from "./issues.js"; @@ -17,6 +18,11 @@ export const costEvents = pgTable( goalId: uuid("goal_id").references(() => goals.id), heartbeatRunId: uuid("heartbeat_run_id").references(() => heartbeatRuns.id), billingCode: text("billing_code"), + idempotencyKey: text("idempotency_key"), + receiptHash: text("receipt_hash"), + providerRequestId: text("provider_request_id"), + reportedCostCents: numeric("reported_cost_cents", { precision: 24, scale: 7 }), + pricingProvenance: jsonb("pricing_provenance").$type>(), provider: text("provider").notNull(), biller: text("biller").notNull().default("unknown"), billingType: text("billing_type").notNull().default("unknown"), @@ -25,11 +31,16 @@ export const costEvents = pgTable( inputTokens: integer("input_tokens").notNull().default(0), cachedInputTokens: integer("cached_input_tokens").notNull().default(0), outputTokens: integer("output_tokens").notNull().default(0), - costCents: integer("cost_cents").notNull(), + costCents: numeric("cost_cents", { precision: 24, scale: 7, mode: "number" }).notNull(), occurredAt: timestamp("occurred_at", { withTimezone: true }).notNull(), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), }, (table) => ({ + receiptUniqueIdx: uniqueIndex("cost_events_company_receipt_idx").on(table.companyId, table.idempotencyKey), + providerRequestIdx: index("cost_events_provider_request_idx").on(table.companyId, table.biller, table.providerRequestId), + nonnegativeAmounts: check("cost_events_nonnegative_amounts", sql`${table.costCents} >= 0 and ${table.inputTokens} >= 0 and ${table.cachedInputTokens} >= 0 and ${table.outputTokens} >= 0`), + companyProjectOccurredIdx: index("cost_events_company_project_occurred_idx").on(table.companyId, table.projectId, table.occurredAt), + unpricedIdx: index("cost_events_unpriced_idx").on(table.companyId, table.occurredAt, table.id).where(sql`${table.costStatus} = 'unpriced' and ${table.billingType} <> 'subscription_included'`), companyOccurredIdx: index("cost_events_company_occurred_idx").on(table.companyId, table.occurredAt), companyAgentOccurredIdx: index("cost_events_company_agent_occurred_idx").on( table.companyId, diff --git a/packages/db/src/schema/finance_events.ts b/packages/db/src/schema/finance_events.ts index 976efa3ff43..bb7247497c3 100644 --- a/packages/db/src/schema/finance_events.ts +++ b/packages/db/src/schema/finance_events.ts @@ -1,4 +1,4 @@ -import { pgTable, uuid, text, timestamp, integer, index, boolean, jsonb } from "drizzle-orm/pg-core"; +import { pgTable, uuid, text, timestamp, integer, numeric, index, uniqueIndex, boolean, jsonb } from "drizzle-orm/pg-core"; import { companies } from "./companies.js"; import { agents } from "./agents.js"; import { issues } from "./issues.js"; @@ -18,6 +18,8 @@ export const financeEvents = pgTable( goalId: uuid("goal_id").references(() => goals.id), heartbeatRunId: uuid("heartbeat_run_id").references(() => heartbeatRuns.id), costEventId: uuid("cost_event_id").references(() => costEvents.id), + idempotencyKey: text("idempotency_key"), + receiptHash: text("receipt_hash"), billingCode: text("billing_code"), description: text("description"), eventKind: text("event_kind").notNull(), @@ -30,7 +32,7 @@ export const financeEvents = pgTable( model: text("model"), quantity: integer("quantity"), unit: text("unit"), - amountCents: integer("amount_cents").notNull(), + amountCents: numeric("amount_cents", { precision: 24, scale: 7, mode: "number" }).notNull(), currency: text("currency").notNull().default("USD"), estimated: boolean("estimated").notNull().default(false), externalInvoiceId: text("external_invoice_id"), @@ -39,6 +41,7 @@ export const financeEvents = pgTable( createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), }, (table) => ({ + receiptUniqueIdx: uniqueIndex("finance_events_company_receipt_idx").on(table.companyId, table.idempotencyKey), companyOccurredIdx: index("finance_events_company_occurred_idx").on(table.companyId, table.occurredAt), companyBillerOccurredIdx: index("finance_events_company_biller_occurred_idx").on( table.companyId, diff --git a/packages/db/src/schema/heartbeat_runs.ts b/packages/db/src/schema/heartbeat_runs.ts index 93e988b3e5b..ccb30944b9e 100644 --- a/packages/db/src/schema/heartbeat_runs.ts +++ b/packages/db/src/schema/heartbeat_runs.ts @@ -39,6 +39,12 @@ export const heartbeatRuns = pgTable( wakeupRequestId: uuid("wakeup_request_id").references(() => agentWakeupRequests.id), exitCode: integer("exit_code"), signal: text("signal"), + costAccountingPending: boolean("cost_accounting_pending").notNull().default(false), + costAccountedAt: timestamp("cost_accounted_at", { withTimezone: true }), + accountingProjectionVersion: text("accounting_projection_version"), + accountingLastAttemptAt: timestamp("accounting_last_attempt_at", { withTimezone: true }), + accountingLastError: text("accounting_last_error"), + accountingAttemptCount: integer("accounting_attempt_count").notNull().default(0), usageJson: jsonb("usage_json").$type>(), resultJson: jsonb("result_json").$type>(), runtimeMode: text("runtime_mode").notNull().default("legacy"), @@ -98,6 +104,7 @@ export const heartbeatRuns = pgTable( updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), }, (table) => ({ + costAccountingPendingIdx: index("heartbeat_runs_cost_accounting_pending_idx").on(table.updatedAt, table.id).where(sql`${table.costAccountingPending} = true`), executionStatusDeliveryIdx: index("heartbeat_runs_execution_status_delivery_idx") .on(table.executionStatusDeliveryId).where(sql`${table.executionStatusDeliveryId} is not null`), executionControlDeadlineIdx: index("heartbeat_runs_execution_control_deadline_idx") diff --git a/packages/db/src/schema/index.ts b/packages/db/src/schema/index.ts index 19df9235905..0626b2049e5 100644 --- a/packages/db/src/schema/index.ts +++ b/packages/db/src/schema/index.ts @@ -218,3 +218,5 @@ export { browserUseSettings, browserUseSessions, browserUseRuns, browserUseBrows export * from "./company_skill_sources.js"; + +export * from "./accounting.js"; diff --git a/packages/shared/src/accounting.ts b/packages/shared/src/accounting.ts new file mode 100644 index 00000000000..2f9eca63184 --- /dev/null +++ b/packages/shared/src/accounting.ts @@ -0,0 +1,115 @@ +import { z } from "zod"; +import { centsToUnits, normalizeCents } from "./money.js"; + +export const exactCentsSchema = z.union([z.string().max(160), z.number().finite()]).transform((value, ctx) => { + try { + if ((typeof value === "number" ? value < 0 : value.startsWith("-")) || centsToUnits(value) < 0n) throw new Error("Money cannot be negative"); + return normalizeCents(value); + } catch (error) { + ctx.addIssue({ code: "custom", message: error instanceof Error ? error.message : "Invalid money" }); + return z.NEVER; + } +}); +export const pricingProvenanceSchema = z.object({ + source: z.enum(["provider_reported", "provider_invoice", "operator", "rate_card", "unknown"]).default("unknown"), + version: z.string().max(100).optional(), + evidence: z.string().max(1000).optional(), + inputCentsPerMillion: exactCentsSchema.optional(), + cachedInputCentsPerMillion: exactCentsSchema.optional(), + outputCentsPerMillion: exactCentsSchema.optional(), + cacheWriteCentsPerMillion: exactCentsSchema.optional(), + serviceTier: z.string().max(50).optional(), + contextTier: z.enum(["short", "long"]).optional(), +}); +export const importBillingInvoiceSchema = z.object({ + biller: z.string().trim().min(1).max(150), + externalId: z.string().trim().min(1).max(200), + currency: z.string().regex(/^[A-Za-z]{3}$/).transform(value => value.toUpperCase()), + lines: z.array(z.object({ + externalId: z.string().trim().min(1).max(200), + kind: z.enum(["inference", "fee", "credit"]).default("inference"), + amountCents: exactCentsSchema, + occurredAt: z.iso.datetime({ offset: true }), + costEventId: z.string().uuid().optional(), + runId: z.string().uuid().optional(), + providerRequestId: z.string().min(1).max(250).optional(), + model: z.string().min(1).max(250).optional(), + pricing: pricingProvenanceSchema.optional(), + })).min(1).max(2000), +}).superRefine((invoice, ctx) => { + if (new Set(invoice.lines.map(line => line.externalId)).size !== invoice.lines.length) + ctx.addIssue({ code: "custom", path: ["lines"], message: "Invoice line identifiers must be unique" }); +}); +export const adjustCostSchema = z.object({ + idempotencyKey: z.string().trim().min(1).max(200), + expectedCents: exactCentsSchema, + correctedCents: exactCentsSchema, + invoiceLineId: z.string().uuid().optional(), + reason: z.string().trim().min(1).max(1000), + pricing: pricingProvenanceSchema, +}); +export const repairAccountingSchema = z.object({ fingerprint: z.string().regex(/^[a-f0-9]{64}$/), reason: z.string().trim().min(1).max(1000) }); +export const retryAccountingSchema = z.object({ runId: z.string().uuid() }); +export const importProviderCostsSchema = z.object({ + provider: z.enum(["openai", "anthropic"]), + secretId: z.string().uuid(), + accountId: z.string().trim().min(1).max(150).regex(/^[a-zA-Z0-9_-]+$/), + scopeIds: z.array(z.string().trim().min(1).max(150)).min(1).max(25), + from: z.iso.date(), + to: z.iso.date(), +}).superRefine((input, ctx) => { + const duration = Date.parse(input.to) - Date.parse(input.from); + if (duration <= 0 || duration > 31 * 86400000 || Date.parse(input.to) > Math.floor(Date.now() / 86400000) * 86400000) + ctx.addIssue({ code: "custom", message: "Choose up to 31 completed UTC days; the end date is exclusive." }); + if (new Set(input.scopeIds).size !== input.scopeIds.length) + ctx.addIssue({ code: "custom", message: "Project/workspace identifiers must be unique." }); +}); +export type ImportProviderCosts = z.input; +export type ImportBillingInvoice = z.input; +export type AdjustCost = z.input; +export interface AccountingFinding { + kind: "company_projection" | "agent_projection" | "runtime_projection" | "missing_receipt" | "missing_acknowledgement" | "legacy_runtime" | "receipt_mismatch"; + entityId: string; + repairable: boolean; + actual: Record; + expected: Record; +} +export interface AccountingInspection { + companyId: string; + fingerprint: string; + findings: AccountingFinding[]; + checkedAt: string; +} +export interface AccountingHealth { + companyId: string; + pendingRunCount: number; + unpricedEventCount: number; + oldestPendingAt: string | null; + pendingCancellationCount: number; + heldReservationCents: string; + items: Array<{ runId: string | null; costEventId?: string; agentId: string; state: "waiting_for_receipt" | "retryable" | "unpriced"; lastError: string | null; attempts: number; since: string; lastAttemptAt: string | null }>; +} +export interface BillingInvoice { + id: string; companyId: string; biller: string; externalId: string; currency: string; createdAt: string; +} +export interface CostAdjustment { + id: string; + companyId: string; + costEventId: string; + invoiceLineId: string | null; + idempotencyKey: string; + receiptHash: string; + previousCents: string; + correctedCents: string; + previousStatus: string; + previousPricing: Record; + pricing: Record; + reason: string; + actorId: string; + createdAt: string; +} +export interface BillingReconciliation { + invoice: BillingInvoice; + lines: Array<{ id: string; externalId: string; kind: string; amountCents: string; status: string; + matchedEventId: string | null; recordedCents: string | null; differenceCents: string | null }>; +} diff --git a/packages/shared/src/constants.ts b/packages/shared/src/constants.ts index 61a2b2b5bad..568112d46af 100644 --- a/packages/shared/src/constants.ts +++ b/packages/shared/src/constants.ts @@ -833,7 +833,7 @@ export const BILLING_TYPES = [ ] as const; export type BillingType = (typeof BILLING_TYPES)[number]; -export const COST_STATUSES = ["reported", "unpriced"] as const; +export const COST_STATUSES = ["reported", "estimated", "unpriced"] as const; export type CostStatus = (typeof COST_STATUSES)[number]; export const FINANCE_EVENT_KINDS = [ diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 692abd65d17..4704f3a0cbd 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -2816,3 +2816,6 @@ export * from "./browser-use.js"; export * from "./types/skill-source.js"; export * from "./validators/skill-source.js"; export * from "./github-skill-repository.js"; + +export * from "./money.js"; +export * from "./accounting.js"; diff --git a/packages/shared/src/money.test.ts b/packages/shared/src/money.test.ts new file mode 100644 index 00000000000..3edebc81510 --- /dev/null +++ b/packages/shared/src/money.test.ts @@ -0,0 +1,29 @@ +import { describe, expect, it } from "vitest"; +import { addCents, centsToUnits, centsToUsd, compareCents, formatUsdExact, MAX_MONEY_UNITS, normalizeCents, subtractCents, unitsToCents, usdToCents } from "./money.js"; + +describe("fixed-point money", () => { + it.each([ + ["0.00000005", "0.0000001"], ["-0.00000005", "-0.0000001"], + ["0.000000049", "0.0000000"], ["1e-7", "0.0000001"], + ["12345678901234567.1234567", "12345678901234567.1234567"], + ])("normalizes %s exactly", (input, expected) => expect(normalizeCents(input)).toBe(expected)); + it("does exact arithmetic beyond floating-point precision", () => { + expect(addCents("12345678901234567.1234567", "0.0000001")).toBe("12345678901234567.1234568"); + expect(subtractCents("9007199254740992.0000001", "9007199254740992")).toBe("0.0000001"); + expect(compareCents("9007199254740992.0000001", "9007199254740992")).toBe(1); + expect(addCents(0.1, 0.2)).toBe("0.3000000"); + }); + it("converts USD without intermediate multiplication or double rounding", () => { + expect(usdToCents("0.000000001")).toBe("0.0000001"); + expect(usdToCents("0.00000000049")).toBe("0.0000000"); + expect(usdToCents("0.0123456789")).toBe("1.2345679"); + expect(formatUsdExact("4.5")).toBe("$0.05"); + }); + it("round-trips the full database range", () => expect(centsToUnits(unitsToCents(MAX_MONEY_UNITS))).toBe(MAX_MONEY_UNITS)); + it.each(["0.0000001", "9007199254740992.0000001", "-0.0000001", unitsToCents(MAX_MONEY_UNITS)])("preserves an editable USD value for %s cents", cents => { + expect(usdToCents(centsToUsd(cents))).toBe(cents); + }); + it.each(["NaN", "Infinity", "", " 1", "1.2.3", "1e101", "100000000000000000", Number.NaN, Number.POSITIVE_INFINITY, Number.MAX_SAFE_INTEGER + 1])("rejects invalid/out-of-range %s", input => { + expect(() => centsToUnits(input)).toThrow(); + }); +}); diff --git a/packages/shared/src/money.ts b/packages/shared/src/money.ts new file mode 100644 index 00000000000..156663aa931 --- /dev/null +++ b/packages/shared/src/money.ts @@ -0,0 +1,53 @@ +/** Monetary storage uses nanodollars: 10^7 units per cent, 10^9 per USD. + * Numbers are accepted only at compatibility boundaries. All arithmetic uses + * integers; decimal inputs are rounded once, half away from zero. */ +export const MONEY_SCALE = 10_000_000n; +export const MAX_MONEY_UNITS = 10n ** 24n - 1n; +export type MoneyInput = string | number; + +function decimalUnits(value: MoneyInput, places: number): bigint { + if (typeof value === "number" && !Number.isFinite(value)) throw new Error("Money must be finite"); + if (typeof value === "number" && Math.abs(value) > Number.MAX_SAFE_INTEGER) throw new Error("Use a decimal string for large monetary values"); + const text = String(value); + if (text.length > 160) throw new Error("Money exceeds supported precision"); + const match = /^([+-]?)(\d+)(?:\.(\d+))?(?:e([+-]?\d+))?$/i.exec(text); + if (!match) throw new Error("Invalid decimal money amount"); + const exponent = Number(match[4] ?? 0); + if (!Number.isInteger(exponent) || Math.abs(exponent) > 100) throw new Error("Money exponent is out of range"); + const digits = BigInt(match[2] + (match[3] ?? "")); + const shift = places + exponent - (match[3]?.length ?? 0); + const divisor = shift < 0 ? 10n ** BigInt(-shift) : 1n; + const magnitude = shift >= 0 ? digits * 10n ** BigInt(shift) : (digits + divisor / 2n) / divisor; + if (magnitude > MAX_MONEY_UNITS) throw new Error("Money exceeds numeric(24,7) storage"); + return match[1] === "-" ? -magnitude : magnitude; +} + +export function centsToUnits(value: MoneyInput): bigint { return decimalUnits(value, 7); } +export function usdToUnits(value: MoneyInput): bigint { return decimalUnits(value, 9); } +export function unitsToCents(value: bigint): string { + if (value > MAX_MONEY_UNITS || value < -MAX_MONEY_UNITS) throw new Error("Money exceeds numeric(24,7) storage"); + const magnitude = value < 0n ? -value : value; + return `${value < 0n ? "-" : ""}${magnitude / MONEY_SCALE}.${String(magnitude % MONEY_SCALE).padStart(7, "0")}`; +} +export function normalizeCents(value: MoneyInput): string { return unitsToCents(centsToUnits(value)); } +export function usdToCents(value: MoneyInput): string { return unitsToCents(usdToUnits(value)); } +/** Exact editable USD value; unlike display formatting, this never rounds cents. */ +export function centsToUsd(value: MoneyInput): string { + const units = centsToUnits(value); + const magnitude = units < 0n ? -units : units; + return `${units < 0n ? "-" : ""}${magnitude / 1_000_000_000n}.${String(magnitude % 1_000_000_000n).padStart(9, "0")}`; +} +/** Approximate legacy/UI value. Never use this conversion for a money decision. */ +export function centsToNumber(value: MoneyInput): number { return Number(normalizeCents(value)); } +export function addCents(...values: MoneyInput[]): string { return unitsToCents(values.reduce((sum, value) => sum + centsToUnits(value), 0n)); } +export function subtractCents(a: MoneyInput, b: MoneyInput): string { return unitsToCents(centsToUnits(a) - centsToUnits(b)); } +export function compareCents(a: MoneyInput, b: MoneyInput): number { + const left = centsToUnits(a), right = centsToUnits(b); + return left < right ? -1 : left > right ? 1 : 0; +} +export function formatUsdExact(cents: MoneyInput): string { + const units = centsToUnits(cents); + const magnitude = units < 0n ? -units : units; + const rounded = (magnitude + MONEY_SCALE / 2n) / MONEY_SCALE; + return `${units < 0n ? "-" : ""}$${rounded / 100n}.${String(rounded % 100n).padStart(2, "0")}`; +} diff --git a/packages/shared/src/types/budget.ts b/packages/shared/src/types/budget.ts index 6907796ad27..4296b3c8219 100644 --- a/packages/shared/src/types/budget.ts +++ b/packages/shared/src/types/budget.ts @@ -16,8 +16,10 @@ export interface BudgetPolicy { metric: BudgetMetric; windowKind: BudgetWindowKind; amount: number; + reservationCents?: string; warnPercent: number; hardStopEnabled: boolean; + unpricedUsagePolicy: "block" | "allow"; notifyEnabled: boolean; isActive: boolean; createdByUserId: string | null; @@ -35,11 +37,16 @@ export interface BudgetPolicySummary { metric: BudgetMetric; windowKind: BudgetWindowKind; amount: number; + reservationCents?: string; observedAmount: number; + observedAmountExact?: string; + unpricedEventCount: number; + pendingRunCount: number; remainingAmount: number; utilizationPercent: number; warnPercent: number; hardStopEnabled: boolean; + unpricedUsagePolicy: "block" | "allow"; notifyEnabled: boolean; isActive: boolean; status: "ok" | "warning" | "hard_stop"; @@ -86,8 +93,10 @@ export interface BudgetPolicyUpsertInput { metric?: BudgetMetric; windowKind?: BudgetWindowKind; amount: number; + reservationCents?: string | number; warnPercent?: number; hardStopEnabled?: boolean; + unpricedUsagePolicy?: "block" | "allow"; notifyEnabled?: boolean; isActive?: boolean; } diff --git a/packages/shared/src/types/cost.ts b/packages/shared/src/types/cost.ts index 4e55098094d..2987591ee85 100644 --- a/packages/shared/src/types/cost.ts +++ b/packages/shared/src/types/cost.ts @@ -10,6 +10,7 @@ export interface CostEvent { goalId: string | null; heartbeatRunId: string | null; billingCode: string | null; + idempotencyKey: string | null; provider: string; biller: string; billingType: BillingType; @@ -19,13 +20,20 @@ export interface CostEvent { cachedInputTokens: number; outputTokens: number; costCents: number; + costCentsExact?: string; occurredAt: Date; createdAt: Date; } export interface CostSummary { + eventCount: number; + pendingRunCount: number; + unpricedEventCount: number; + estimatedEventCount?: number; + pricingComplete: boolean; companyId: string; spendCents: number; + spendCentsExact?: string; budgetCents: number; utilizationPercent: number; } @@ -35,6 +43,7 @@ export interface IssueCostSummary { issueCount: number; includeDescendants: boolean; costCents: number; + costCentsExact?: string; inputTokens: number; cachedInputTokens: number; outputTokens: number; @@ -51,7 +60,11 @@ export interface CostByAgent { agentAppearance?: AgentAppearance | null; avatarUrl?: string; agentStatus: string | null; + /** Ledger events in this group and selected date range, not distinct runs. */ + eventCount: number; + estimatedEventCount: number; costCents: number; + costCentsExact?: string; inputTokens: number; cachedInputTokens: number; outputTokens: number; @@ -68,6 +81,7 @@ export interface CostByProviderModel { billingType: BillingType; model: string; costCents: number; + costCentsExact?: string; inputTokens: number; cachedInputTokens: number; outputTokens: number; @@ -81,6 +95,7 @@ export interface CostByProviderModel { export interface CostByBiller { biller: string; costCents: number; + costCentsExact?: string; inputTokens: number; cachedInputTokens: number; outputTokens: number; @@ -103,7 +118,11 @@ export interface CostByAgentModel { biller: string; billingType: BillingType; model: string; + /** Ledger events in this group and selected date range, not distinct runs. */ + eventCount: number; + estimatedEventCount: number; costCents: number; + costCentsExact?: string; inputTokens: number; cachedInputTokens: number; outputTokens: number; @@ -118,6 +137,7 @@ export interface CostWindowSpendRow { /** rolling window duration in hours */ windowHours: number; costCents: number; + costCentsExact?: string; inputTokens: number; cachedInputTokens: number; outputTokens: number; @@ -128,6 +148,7 @@ export interface CostByProject { projectId: string | null; projectName: string | null; costCents: number; + costCentsExact?: string; inputTokens: number; cachedInputTokens: number; outputTokens: number; diff --git a/packages/shared/src/types/finance.ts b/packages/shared/src/types/finance.ts index 75a52084bea..5c2208c7950 100644 --- a/packages/shared/src/types/finance.ts +++ b/packages/shared/src/types/finance.ts @@ -22,39 +22,75 @@ export interface FinanceEvent { quantity: number | null; unit: FinanceUnit | null; amountCents: number; + amountCentsExact?: string; currency: string; estimated: boolean; externalInvoiceId: string | null; + idempotencyKey: string | null; metadataJson: Record | null; occurredAt: Date; createdAt: Date; } +export interface FinanceCurrencySummary { + currency: string; + providerReportedCentsExact?: string; + debitCents: number; + debitCentsExact?: string; + creditCents: number; + creditCentsExact?: string; + netCents: number; + netCentsExact?: string; + estimatedDebitCents: number; + estimatedDebitCentsExact?: string; + eventCount: number; +} + export interface FinanceSummary { + /** Provider cost reports overlap invoices and run estimates; never add these totals. */ + providerReportedCents?: number; + providerReportedCentsExact?: string; + /** Compatibility totals below are USD only; no implicit currency conversion. */ + currency: "USD"; + currencies: FinanceCurrencySummary[]; companyId: string; debitCents: number; + debitCentsExact?: string; creditCents: number; + creditCentsExact?: string; netCents: number; + netCentsExact?: string; estimatedDebitCents: number; + estimatedDebitCentsExact?: string; eventCount: number; } export interface FinanceByBiller { + currency: string; biller: string; debitCents: number; + debitCentsExact?: string; creditCents: number; + creditCentsExact?: string; netCents: number; + netCentsExact?: string; estimatedDebitCents: number; + estimatedDebitCentsExact?: string; eventCount: number; kindCount: number; } export interface FinanceByKind { + currency: string; eventKind: FinanceEventKind; debitCents: number; + debitCentsExact?: string; creditCents: number; + creditCentsExact?: string; netCents: number; + netCentsExact?: string; estimatedDebitCents: number; + estimatedDebitCentsExact?: string; eventCount: number; billerCount: number; } diff --git a/packages/shared/src/validators/budget.ts b/packages/shared/src/validators/budget.ts index 55f629c8f4d..6493edb863c 100644 --- a/packages/shared/src/validators/budget.ts +++ b/packages/shared/src/validators/budget.ts @@ -1,3 +1,4 @@ +import { exactCentsSchema } from "../accounting.js"; import { z } from "zod"; import { BUDGET_INCIDENT_RESOLUTION_ACTIONS, @@ -11,9 +12,11 @@ export const upsertBudgetPolicySchema = z.object({ scopeId: z.string().guid(), metric: z.enum(BUDGET_METRICS).optional().default("billed_cents"), windowKind: z.enum(BUDGET_WINDOW_KINDS).optional().default("calendar_month_utc"), - amount: z.number().int().nonnegative(), + amount: z.number().int().nonnegative().max(2_147_483_647), + reservationCents: exactCentsSchema.optional(), warnPercent: z.number().int().min(1).max(99).optional().default(80), hardStopEnabled: z.boolean().optional().default(true), + unpricedUsagePolicy: z.enum(["block", "allow"]).optional(), notifyEnabled: z.boolean().optional().default(true), isActive: z.boolean().optional().default(true), }); @@ -22,7 +25,7 @@ export type UpsertBudgetPolicy = z.infer; export const resolveBudgetIncidentSchema = z.object({ action: z.enum(BUDGET_INCIDENT_RESOLUTION_ACTIONS), - amount: z.number().int().nonnegative().optional(), + amount: z.number().int().nonnegative().max(2_147_483_647).optional(), decisionNote: z.string().optional().nullable(), }).superRefine((value, ctx) => { if (value.action === "raise_budget_and_resume" && typeof value.amount !== "number") { diff --git a/packages/shared/src/validators/cost.ts b/packages/shared/src/validators/cost.ts index 1ed2263178f..3002086c070 100644 --- a/packages/shared/src/validators/cost.ts +++ b/packages/shared/src/validators/cost.ts @@ -1,7 +1,9 @@ +import { exactCentsSchema, pricingProvenanceSchema } from "../accounting.js"; import { z } from "zod"; import { BILLING_TYPES, COST_STATUSES } from "../constants.js"; export const createCostEventSchema = z.object({ + idempotencyKey: z.string().trim().min(1).max(200).optional().nullable(), agentId: z.string().guid(), issueId: z.string().guid().optional().nullable(), projectId: z.string().guid().optional().nullable(), @@ -13,17 +15,19 @@ export const createCostEventSchema = z.object({ billingType: z.enum(BILLING_TYPES).optional().default("unknown"), costStatus: z.enum(COST_STATUSES).optional().default("reported"), model: z.string().min(1), - inputTokens: z.number().int().nonnegative().optional().default(0), - cachedInputTokens: z.number().int().nonnegative().optional().default(0), - outputTokens: z.number().int().nonnegative().optional().default(0), - costCents: z.number().int().nonnegative(), + inputTokens: z.number().int().nonnegative().max(2_147_483_647).optional().default(0), + cachedInputTokens: z.number().int().nonnegative().max(2_147_483_647).optional().default(0), + outputTokens: z.number().int().nonnegative().max(2_147_483_647).optional().default(0), + costCents: exactCentsSchema, + providerRequestId: z.string().min(1).max(250).nullable().optional(), + pricingProvenance: pricingProvenanceSchema.nullable().optional(), occurredAt: z.string().datetime(), }).transform((value) => ({ ...value, biller: value.biller ?? value.provider, })); -export type CreateCostEvent = z.infer; +export type CreateCostEvent = z.input; export const updateBudgetSchema = z.object({ budgetMonthlyCents: z.number().int().nonnegative(), diff --git a/packages/shared/src/validators/finance.ts b/packages/shared/src/validators/finance.ts index f09eebf2840..ded2b0bdf24 100644 --- a/packages/shared/src/validators/finance.ts +++ b/packages/shared/src/validators/finance.ts @@ -1,7 +1,9 @@ +import { exactCentsSchema } from "../accounting.js"; import { z } from "zod"; import { AGENT_ADAPTER_TYPES, FINANCE_DIRECTIONS, FINANCE_EVENT_KINDS, FINANCE_UNITS } from "../constants.js"; export const createFinanceEventSchema = z.object({ + idempotencyKey: z.string().trim().min(1).max(200).optional().nullable(), agentId: z.string().guid().optional().nullable(), issueId: z.string().guid().optional().nullable(), projectId: z.string().guid().optional().nullable(), @@ -20,8 +22,8 @@ export const createFinanceEventSchema = z.object({ model: z.string().min(1).optional().nullable(), quantity: z.number().int().nonnegative().optional().nullable(), unit: z.enum(FINANCE_UNITS).optional().nullable(), - amountCents: z.number().int().nonnegative(), - currency: z.string().length(3).optional().default("USD"), + amountCents: exactCentsSchema, + currency: z.string().regex(/^[A-Za-z]{3}$/).optional().default("USD"), estimated: z.boolean().optional().default(false), externalInvoiceId: z.string().optional().nullable(), metadataJson: z.record(z.string(), z.unknown()).optional().nullable(), @@ -31,4 +33,4 @@ export const createFinanceEventSchema = z.object({ currency: value.currency.toUpperCase(), })); -export type CreateFinanceEvent = z.infer; +export type CreateFinanceEvent = z.input; diff --git a/scripts/test-accounting-mutations.mjs b/scripts/test-accounting-mutations.mjs new file mode 100644 index 00000000000..69fbf9f8037 --- /dev/null +++ b/scripts/test-accounting-mutations.mjs @@ -0,0 +1,34 @@ +import { spawn } from "node:child_process"; +import { mkdtemp, readFile, rm, writeFile, mkdir } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +const root = fileURLToPath(new URL("..", import.meta.url)); +const directory = await mkdtemp(path.join(tmpdir(), "accounting-mutations-")); +const targets = { deduplication: "deduplicates a retried receipt", company: "isolates company reporting", projection: "conserves agent projections", threshold: "stops at the exact budget boundary" }; +const evidence = []; +try { + for (const name of ["baseline", ...Object.keys(targets)]) { + const report = path.join(directory, `${name}.json`); + const result = await new Promise((resolve, reject) => { + const child = spawn(process.execPath, ["node_modules/vitest/vitest.mjs", "run", "--config", "server/vitest.accounting-mutations.config.ts", "--reporter=json", `--outputFile=${report}`], { + cwd: root, env: { ...process.env, PAPERCLIP_ACCOUNTING_MUTATION: name === "baseline" ? "" : name }, stdio: ["ignore", "pipe", "pipe"], + }); + let log = ""; const capture = data => { log += data; }; + child.stdout.on("data", capture); child.stderr.on("data", capture); + child.once("error", reject); child.once("exit", code => resolve({ code, log })); + }); + const json = JSON.parse(await readFile(report, "utf8").catch(() => { throw new Error(`${name} produced no test evidence: ${result.log}`); })); + const assertions = json.testResults.flatMap(file => file.assertionResults); + if (name === "baseline") { + if (result.code !== 0 || assertions.length !== 4 || assertions.some(test => test.status !== "passed")) throw new Error(`Mutation baseline failed or skipped: ${result.log}`); + } else { + const sentinel = assertions.find(test => test.title === targets[name]); + if (result.code === 0 || !result.log.includes(`ACCOUNTING_MUTATION_APPLIED ${name}`) || sentinel?.status !== "failed") throw new Error(`Mutation ${name} survived or failed without its sentinel: ${result.log}`); + } + evidence.push({ mutation: name, result: name === "baseline" ? "passed" : "killed", assertions: assertions.map(({ title, status }) => ({ title, status })) }); + process.stdout.write(`${name}: ${name === "baseline" ? "passed" : "killed by expected assertion"}\n`); + } + await mkdir(path.join(root, "coverage/accounting"), { recursive: true }); + await writeFile(path.join(root, "coverage/accounting/mutations.json"), JSON.stringify({ generatedAt: new Date().toISOString(), evidence }, null, 2)); +} finally { await rm(directory, { recursive: true, force: true }); } diff --git a/server/scripts/benchmark-accounting.ts b/server/scripts/benchmark-accounting.ts new file mode 100644 index 00000000000..a18a0ec8776 --- /dev/null +++ b/server/scripts/benchmark-accounting.ts @@ -0,0 +1,80 @@ +import { budgetService } from "../src/services/budgets.js"; +import { sql } from "drizzle-orm"; +/** Reproducible synthetic PostgreSQL benchmark. Never reads DATABASE_URL or + * provider credentials; startEmbeddedPostgresTestDatabase owns all data. */ +import { performance } from "node:perf_hooks"; +import { mkdir, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { agents, companies, projects, createDb, startEmbeddedPostgresTestDatabase } from "@paperclipai/db"; +import { costService } from "../src/services/costs.js"; +import { withAccountingTransaction } from "../src/services/accounting-transaction.js"; +import { accountingIntegrityService } from "../src/services/accounting-integrity.js"; +import { reconcileRunCosts } from "../src/services/run-cost-accounting.js"; +const count = Number(process.env.PAPERCLIP_ACCOUNTING_BENCH_ROWS ?? 1_000_000); +if (!Number.isInteger(count) || count < 1000 || count > 10_000_000) throw new Error("Benchmark row count must be 1,000–10,000,000"); +const database = await startEmbeddedPostgresTestDatabase("accounting-scale-"); +const db = createDb(database.connectionString); +const started = performance.now(); +function distribution(values: number[]) { + const sorted = [...values].sort((a,b) => a-b); + const pick = (p: number) => Number(sorted[Math.min(sorted.length - 1, Math.ceil(p * sorted.length) - 1)].toFixed(2)); + return { samples: sorted.length, p50Ms: pick(0.5), p95Ms: pick(0.95), p99Ms: pick(0.99), maxMs: pick(1) }; +} +async function time(work: () => Promise) { const start = performance.now(); await work(); return performance.now() - start; } +try { + const [company] = await db.insert(companies).values({ name: "Scale fixture", issuePrefix: "SCALE" }).returning(); + const [agent] = await db.insert(agents).values({ companyId: company.id, name: "Worker", role: "engineer", adapterType: "process", status: "idle" }).returning(); + const [project] = await db.insert(projects).values({ companyId: company.id, name: "Hot project" }).returning(); + const seedMs = await time(async () => { + await db.$client`insert into cost_events (company_id,agent_id,project_id,provider,biller,model,cost_cents,input_tokens,cached_input_tokens,output_tokens,occurred_at) + select ${company.id},${agent.id}, case when i % 100 = 0 then ${project.id}::uuid else null end, 'fixture','fixture','fixture', 0.1234567,7,11,3, + date_trunc('month',now() at time zone 'UTC') at time zone 'UTC' - (i % 12) * interval '1 month' + from generate_series(1,${count}) i`; + await db.$client`analyze cost_events`; + // Seed projections through an explicit reviewed repair, not benchmark-only SQL. + const integrity = accountingIntegrityService(db); const review = await integrity.inspect(company.id); + await integrity.repair(company.id, review.fingerprint, "Initialize synthetic benchmark projections", "benchmark"); + }); + const reports: number[] = [], projectReports: number[] = []; + for (let i = 0; i < 20; i++) { + reports.push(await time(() => costService(db).summary(company.id, { allTime: true }))); + projectReports.push(await time(() => costService(db).byProject(company.id, { allTime: true }))); + } + const writes: number[] = []; const concurrency = 8; const writeStarted = performance.now(); + for (let batch = 0; batch < 8; batch++) await Promise.all(Array.from({ length: concurrency }, (_, index) => time(() => costService(db).createEvent(company.id, { + agentId: agent.id, provider: "fixture", model: "fixture", costCents: "0.0000001", idempotencyKey: `bench:${batch}:${index}`, occurredAt: new Date(), + })).then(ms => writes.push(ms)))); + const writeMs = performance.now() - writeStarted; + await budgetService(db).upsertPolicy(company.id, { scopeType: "company", scopeId: company.id, amount: 2_000_000_000 }, "benchmark"); + await budgetService(db).upsertPolicy(company.id, { scopeType: "agent", scopeId: agent.id, amount: 2_000_000_000 }, "benchmark"); + const budgetedWrites: number[] = []; + const budgetedStart = performance.now(); + for (let batch = 0; batch < 4; batch++) await Promise.all(Array.from({ length: concurrency }, (_, index) => time(() => costService(db).createEvent(company.id, { + agentId: agent.id, provider: "fixture", model: "fixture", costCents: "0.0000001", idempotencyKey: `budgeted:${batch}:${index}`, occurredAt: new Date(), + })).then(ms => budgetedWrites.push(ms)))); + const budgetedMs = performance.now() - budgetedStart; + const waits: number[] = []; + await Promise.all(Array.from({ length: concurrency }, () => { + const start = performance.now(); + return withAccountingTransaction(db, company.id, async tx => { waits.push(performance.now() - start); await tx.execute(sql`select pg_sleep(0.005)`); }); + })); + await db.$client`insert into heartbeat_runs (company_id,agent_id,status,cost_accounting_pending,usage_json,finished_at) + select ${company.id},${agent.id},'failed',true,'{"accountingReceiptReady":true,"costUsdExact":"0.000000001","inputTokens":1}'::jsonb, now() from generate_series(1,250)`; + const recovery: Array<{ milliseconds: number; scanned: number; accounted: number }> = []; + for (let i = 0; i < 3; i++) { + const before = performance.now(); const result = await reconcileRunCosts(db); recovery.push({ milliseconds: performance.now()-before, ...result }); + } + if (recovery.reduce((sum, r) => sum+r.accounted,0) !== 250) throw new Error("Benchmark recovery did not conserve receipt count"); + const integrity = await accountingIntegrityService(db).inspect(company.id); + if (integrity.findings.length) throw new Error(`Benchmark violated accounting integrity: ${JSON.stringify(integrity.findings)}`); + const plan = await db.$client`explain (analyze,buffers,format json) select sum(cost_cents) from cost_events where company_id = ${company.id} and project_id = ${project.id} + and occurred_at >= date_trunc('month',now() at time zone 'UTC') at time zone 'UTC'`; + const report = { generatedAt: new Date().toISOString(), platform: `${os.platform()} ${os.arch()}`, cpus: os.cpus().length, node: process.version, + seedRows: count, seedMs, concurrency, allTimeSummary: distribution(reports), allTimeProjects: distribution(projectReports), + hotCompanyWrites: { activePolicies: 0, ...distribution(writes), writesPerSecond: writes.length / (writeMs / 1000) }, companyLockAcquisition: distribution(waits), + budgetedCompanyWrites: { activePolicies: 2, ...distribution(budgetedWrites), writesPerSecond: budgetedWrites.length / (budgetedMs / 1000) }, + recovery, projectBudgetQueryPlan: plan, integrityFindings: integrity.findings.length, totalMs: performance.now()-started }; + const output = path.resolve("coverage/accounting/scale.json"); await mkdir(path.dirname(output), { recursive: true }); await writeFile(output, JSON.stringify(report,null,2)); + process.stdout.write(JSON.stringify({ ...report, projectBudgetQueryPlan: "see coverage/accounting/scale.json" },null,2)+"\n"); +} finally { await db.$client.end(); await database.cleanup(); } diff --git a/server/src/__tests__/budgets-service.test.ts b/server/src/__tests__/budgets-service.test.ts index 8a67682726b..52d69cecd05 100644 --- a/server/src/__tests__/budgets-service.test.ts +++ b/server/src/__tests__/budgets-service.test.ts @@ -22,308 +22,8 @@ vi.mock("../services/activity-log.js", () => ({ logActivity: mockLogActivity, })); -type SelectResult = unknown[]; - -function createDbStub(selectResults: SelectResult[]) { - const pendingSelects = [...selectResults]; - const selectWhere = vi.fn(async () => pendingSelects.shift() ?? []); - const selectThen = vi.fn((resolve: (value: unknown[]) => unknown) => Promise.resolve(resolve(pendingSelects.shift() ?? []))); - const selectOrderBy = vi.fn(async () => pendingSelects.shift() ?? []); - const selectFrom = vi.fn(() => ({ - where: selectWhere, - then: selectThen, - orderBy: selectOrderBy, - })); - const select = vi.fn(() => ({ - from: selectFrom, - })); - - const insertValues = vi.fn(); - const insertReturning = vi.fn(async () => pendingInserts.shift() ?? []); - const insert = vi.fn(() => ({ - values: insertValues.mockImplementation(() => ({ - returning: insertReturning, - })), - })); - - const updateSet = vi.fn(); - const updateWhere = vi.fn(async () => pendingUpdates.shift() ?? []); - const update = vi.fn(() => ({ - set: updateSet.mockImplementation(() => ({ - where: updateWhere, - })), - })); - - const pendingInserts: unknown[][] = []; - const pendingUpdates: unknown[][] = []; - - return { - db: { - select, - insert, - update, - }, - queueInsert: (rows: unknown[]) => { - pendingInserts.push(rows); - }, - queueUpdate: (rows: unknown[] = []) => { - pendingUpdates.push(rows); - }, - selectWhere, - insertValues, - updateSet, - }; -} - -describe("budgetService", () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - it("creates a hard-stop incident and pauses an agent when spend exceeds a budget", async () => { - const policy = { - id: "policy-1", - companyId: "company-1", - scopeType: "agent", - scopeId: "agent-1", - metric: "billed_cents", - windowKind: "calendar_month_utc", - amount: 100, - warnPercent: 80, - hardStopEnabled: true, - notifyEnabled: false, - isActive: true, - }; - - const dbStub = createDbStub([ - [policy], - [{ total: 150 }], - [], - [{ - companyId: "company-1", - name: "Budget Agent", - status: "running", - pauseReason: null, - }], - ]); - - dbStub.queueInsert([{ - id: "approval-1", - companyId: "company-1", - status: "pending", - }]); - dbStub.queueInsert([{ - id: "incident-1", - companyId: "company-1", - policyId: "policy-1", - approvalId: "approval-1", - }]); - dbStub.queueUpdate([]); - const cancelWorkForScope = vi.fn().mockResolvedValue(undefined); - - const service = budgetService(dbStub.db as any, { cancelWorkForScope }); - await service.evaluateCostEvent({ - companyId: "company-1", - agentId: "agent-1", - projectId: null, - } as any); - - expect(dbStub.insertValues).toHaveBeenCalledWith( - expect.objectContaining({ - companyId: "company-1", - type: "budget_override_required", - status: "pending", - }), - ); - expect(dbStub.insertValues).toHaveBeenCalledWith( - expect.objectContaining({ - companyId: "company-1", - policyId: "policy-1", - thresholdType: "hard", - amountLimit: 100, - amountObserved: 150, - approvalId: "approval-1", - }), - ); - expect(dbStub.updateSet).toHaveBeenCalledWith( - expect.objectContaining({ - status: "paused", - pauseReason: "budget", - pausedAt: expect.any(Date), - }), - ); - expect(mockLogActivity).toHaveBeenCalledWith( - expect.anything(), - expect.objectContaining({ - action: "budget.hard_threshold_crossed", - entityId: "incident-1", - }), - ); - expect(cancelWorkForScope).toHaveBeenCalledWith({ - companyId: "company-1", - scopeType: "agent", - scopeId: "agent-1", - }); - }); - - it("blocks new work when an agent hard-stop remains exceeded even if the agent is not paused yet", async () => { - const agentPolicy = { - id: "policy-agent-1", - companyId: "company-1", - scopeType: "agent", - scopeId: "agent-1", - metric: "billed_cents", - windowKind: "calendar_month_utc", - amount: 100, - warnPercent: 80, - hardStopEnabled: true, - notifyEnabled: true, - isActive: true, - }; - - const dbStub = createDbStub([ - [{ - status: "running", - pauseReason: null, - companyId: "company-1", - name: "Budget Agent", - }], - [{ - status: "active", - name: "Paperclip", - }], - [], - [agentPolicy], - [{ total: 120 }], - ]); - - const service = budgetService(dbStub.db as any); - const block = await service.getInvocationBlock("company-1", "agent-1"); - - expect(block).toEqual({ - scopeType: "agent", - scopeId: "agent-1", - scopeName: "Budget Agent", - reason: "Agent cannot start because its budget hard-stop is still exceeded.", - }); - }); - - it("surfaces a budget-owned company pause distinctly from a manual pause", async () => { - const dbStub = createDbStub([ - [{ - status: "idle", - pauseReason: null, - companyId: "company-1", - name: "Budget Agent", - }], - [{ - status: "paused", - pauseReason: "budget", - name: "Paperclip", - }], - ]); - - const service = budgetService(dbStub.db as any); - const block = await service.getInvocationBlock("company-1", "agent-1"); - - expect(block).toEqual({ - scopeType: "company", - scopeId: "company-1", - scopeName: "Paperclip", - reason: "Company is paused because its budget hard-stop was reached.", - }); - }); - - it("uses live observed spend when raising a budget incident", async () => { - const dbStub = createDbStub([ - [{ - id: "incident-1", - companyId: "company-1", - policyId: "policy-1", - amountObserved: 120, - approvalId: "approval-1", - }], - [{ - id: "policy-1", - companyId: "company-1", - scopeType: "company", - scopeId: "company-1", - metric: "billed_cents", - windowKind: "calendar_month_utc", - }], - [{ total: 150 }], - ]); - - const service = budgetService(dbStub.db as any); - - await expect( - service.resolveIncident( - "company-1", - "incident-1", - { action: "raise_budget_and_resume", amount: 140 }, - "board-user", - ), - ).rejects.toThrow("New budget must exceed current observed spend"); - }); - - it("syncs company monthly budget when raising and resuming a company incident", async () => { - const now = new Date(); - const dbStub = createDbStub([ - [{ - id: "incident-1", - companyId: "company-1", - policyId: "policy-1", - scopeType: "company", - scopeId: "company-1", - metric: "billed_cents", - windowKind: "calendar_month_utc", - windowStart: now, - windowEnd: now, - thresholdType: "hard", - amountLimit: 100, - amountObserved: 120, - status: "open", - approvalId: "approval-1", - resolvedAt: null, - createdAt: now, - updatedAt: now, - }], - [{ - id: "policy-1", - companyId: "company-1", - scopeType: "company", - scopeId: "company-1", - metric: "billed_cents", - windowKind: "calendar_month_utc", - amount: 100, - }], - [{ total: 120 }], - [{ id: "approval-1", status: "approved" }], - [{ - companyId: "company-1", - name: "Paperclip", - status: "paused", - pauseReason: "budget", - pausedAt: now, - }], - ]); - - const service = budgetService(dbStub.db as any); - await service.resolveIncident( - "company-1", - "incident-1", - { action: "raise_budget_and_resume", amount: 175 }, - "board-user", - ); - - expect(dbStub.updateSet).toHaveBeenCalledWith( - expect.objectContaining({ - budgetMonthlyCents: 175, - updatedAt: expect.any(Date), - }), - ); - }); -}); +// Budget behavior is exercised against PostgreSQL below and in +// cost-accounting-reliability.test.ts, including transaction and retry paths. const embeddedPostgresSupport = await getEmbeddedPostgresTestSupport(); const describeEmbeddedPostgres = embeddedPostgresSupport.supported ? describe : describe.skip; @@ -488,7 +188,7 @@ describeEmbeddedPostgres("budgetService release gate enforcement", () => { expect(agentAfterHardStop).toMatchObject({ status: "paused", pauseReason: "budget" }); expect(agentAfterHardStop?.pausedAt).toBeInstanceOf(Date); expect(cancelWorkForScope).toHaveBeenCalledTimes(2); - expect(cancelWorkForScope).toHaveBeenCalledWith({ companyId, scopeType: "agent", scopeId: agentId }); + expect(cancelWorkForScope).toHaveBeenCalledWith({ companyId, scopeType: "agent", scopeId: agentId, createdBefore: expect.any(Date) }); const block = await service.getInvocationBlock(companyId, agentId); expect(block).toEqual({ @@ -573,7 +273,7 @@ describeEmbeddedPostgres("budgetService release gate enforcement", () => { .from(projects); expect(projectAfterHardStop?.pauseReason).toBe("budget"); expect(projectAfterHardStop?.pausedAt).toBeInstanceOf(Date); - expect(cancelWorkForScope).toHaveBeenCalledWith({ companyId, scopeType: "project", scopeId: projectId }); + expect(cancelWorkForScope).toHaveBeenCalledWith({ companyId, scopeType: "project", scopeId: projectId, createdBefore: expect.any(Date) }); const overviewWhileBlocked = await service.overview(companyId); expect(overviewWhileBlocked.pausedProjectCount).toBe(1); diff --git a/server/src/__tests__/codex-pricing.test.ts b/server/src/__tests__/codex-pricing.test.ts new file mode 100644 index 00000000000..d66c241b9a3 --- /dev/null +++ b/server/src/__tests__/codex-pricing.test.ts @@ -0,0 +1,118 @@ +import { describe, expect, it } from "vitest"; +import type { AdapterUsageCheckpoint } from "@paperclipai/adapter-utils"; +import { priceCodexReceipt } from "../services/codex-pricing.js"; + +const receipt: AdapterUsageCheckpoint = { + provider: "openai", + biller: "openai", + billingType: "metered_api", + model: "gpt-6-astra", + complete: true, + usageBasis: "per_run", + costUsd: null, + usage: { + inputTokens: 123536, + cachedInputTokens: 1567209, + outputTokens: 6625, + }, +}; +describe("prospective Codex pricing", () => { + it.each([ + ["gpt-6-sol", "12.200000000"], + ["gpt-6.1-sol", "12.100000000"], + ["gpt-6-luna", "0.610000000"], + ["gpt-5.6-sol", "24.400000000"], + ])("uses the distinct published cache rate for %s", (model, expected) => { + expect( + priceCodexReceipt({ + ...receipt, + model, + usage: { + inputTokens: 1_000_000, + cachedInputTokens: 1_000_000, + outputTokens: 1_000_000, + }, + }).costUsdExact, + ).toBe(expected); + }); + it("prices the staging token shape without counting cached input twice", () => { + const result = priceCodexReceipt(receipt); + expect(result.costUsdExact).toBe("3.133819000"); + expect(result.costStatus).toBe("estimated"); + expect(result.pricingProvenance).toMatchObject({ + source: "rate_card", + version: "openai-standard-2026-09-30", + }); + expect(result.pricingProvenance?.evidence).toContain( + "short per-request context assumed", + ); + expect(receipt.costUsdExact).toBeUndefined(); + }); + it.each([ + { costUsd: 0 }, + { costUsd: 4.21 }, + { costUsdExact: "0.004" }, + { cacheAdjustedCostUsd: 0.01 }, + { provider: "other" }, + { biller: "proxy" }, + { model: "gpt-6-astra-custom" }, + { model: "unknown" }, + { model: "constructor" }, + { model: "__proto__" }, + { usageBasis: undefined }, + { usageBasis: null }, + { billingType: "unknown" }, + { billingType: "subscription_included" }, + { usageBasis: "session_cumulative" }, + { pricingContext: { serviceTier: "unsupported" } }, + { usage: undefined }, + { usage: { inputTokens: -1, outputTokens: 0 } }, + { usage: { inputTokens: 1, outputTokens: 0, cacheWriteTokens: 2 } }, + ])( + "preserves reported costs and refuses ambiguous pricing: %j", + (override) => { + const input = { ...receipt, ...override } as AdapterUsageCheckpoint; + expect(priceCodexReceipt(input)).toBe(input); + }, + ); + it.each([ + ["standard", "short", "61.000000000"], + ["fast", "short", "122.000000000"], + ["flex", "short", "30.500000000"], + ["standard", "long", "97.000000000"], + ["batch", "long", "48.500000000"], + ] as const)("handles %s/%s rates", (serviceTier, contextTier, expected) => { + const result = priceCodexReceipt({ + ...receipt, + pricingContext: { serviceTier, contextTier }, + usage: { + inputTokens: 1_000_000, + cachedInputTokens: 1_000_000, + outputTokens: 1_000_000, + cacheWriteTokens: 0, + }, + }); + expect(result.costUsdExact).toBe(expected); + }); + it("charges cache writes once as a subset of input", () => { + expect( + priceCodexReceipt({ + ...receipt, + usage: { + inputTokens: 1_000_000, + cacheWriteTokens: 1_000_000, + outputTokens: 0, + }, + }).costUsdExact, + ).toBe("12.500000000"); + }); + it("retains nanodollar precision and does not reprice frozen receipts", () => { + const priced = priceCodexReceipt({ + ...receipt, + model: "gpt-6-luna", + usage: { inputTokens: 0, cachedInputTokens: 1, outputTokens: 0 }, + }); + expect(priced.costUsdExact).toBe("0.000000010"); + expect(priceCodexReceipt(priced)).toBe(priced); + }); +}); diff --git a/server/src/__tests__/cost-accounting-checkpoints.test.ts b/server/src/__tests__/cost-accounting-checkpoints.test.ts new file mode 100644 index 00000000000..dccf8fd4b1c --- /dev/null +++ b/server/src/__tests__/cost-accounting-checkpoints.test.ts @@ -0,0 +1,55 @@ +import { describe, expect, it, vi } from "vitest"; +import { createUsageCheckpointLog } from "@paperclipai/adapter-utils/usage-checkpoint"; +import { parseClaudeStreamJson } from "../../../packages/adapters/claude-local/src/server/parse.js"; +import { parseCodexJsonl } from "../../../packages/adapters/codex-local/src/server/parse.js"; +import { parseCursorJsonl } from "../../../packages/adapters/cursor-local/src/server/parse.js"; +import { parseGeminiJsonl } from "../../../packages/adapters/gemini-local/src/server/parse.js"; +import { parseOpenCodeJsonl } from "../../../packages/adapters/opencode-local/src/server/parse.js"; +import { parsePiJsonl } from "../../../packages/adapters/pi-local/src/server/parse.js"; +const secret = "TRANSCRIPT_CONTENT_MUST_NOT_ENTER_ACCOUNTING_BUFFER"; +const fixtures = [ + { name: "claude", event: { type: "result", total_cost_usd: 0.0123, result: secret, usage: { input_tokens: 7, cache_read_input_tokens: 11, output_tokens: 3 } }, parse: parseClaudeStreamJson, input: 7, cached: 11, output: 3 }, + { name: "codex", event: { type: "turn.completed", text: secret, usage: { input_tokens: 18, cached_input_tokens: 11, output_tokens: 3 } }, parse: parseCodexJsonl, input: 7, cached: 11, output: 3 }, + { name: "cursor", event: { type: "result", result: secret, usage: { inputTokens: 7, cachedInputTokens: 11, outputTokens: 3 } }, parse: parseCursorJsonl, input: 7, cached: 11, output: 3 }, + { name: "gemini", event: { type: "result", message: secret, stats: { input_tokens: 18, cached: 11, output_tokens: 1, total_tokens: 21 } }, parse: parseGeminiJsonl, input: 7, cached: 11, output: 3 }, + { name: "opencode", event: { type: "step_finish", part: { text: secret, cost: 0.0123, tokens: { input: 5, cache: { read: 11, write: 2 }, output: 1, reasoning: 2 } } }, parse: parseOpenCodeJsonl, input: 7, cached: 11, output: 3 }, + { name: "pi", event: { type: "turn_end", message: { role: "assistant", content: secret, usage: { input: 5, cacheWrite: 2, cacheRead: 11, output: 3, cost: { total: 0.0123 } } } }, parse: parsePiJsonl, input: 7, cached: 11, output: 3 }, +]; +describe("durable accounting stream projection", () => { + it.each(fixtures)("captures split $name usage without retaining transcript content", async f => { + const onUsage = vi.fn(async (..._args: any[]) => {}), onLog = vi.fn(async (..._args: any[]) => {}); + const parse = vi.fn((source: string) => { + expect(source).not.toContain(secret); + const result = f.parse(source); return { usage: result.usage ?? undefined, complete: true }; + }); + const log = createUsageCheckpointLog(onLog, onUsage, parse); + const line = JSON.stringify(f.event) + "\n"; + for (let index = 0; index < line.length; index += 7) await log("stdout", line.slice(index,index+7)); + expect(onUsage).toHaveBeenCalledTimes(1); + expect(onUsage.mock.calls[0][0]).toMatchObject({ complete: true, attemptId: expect.any(String), usage: { inputTokens: f.input, cachedInputTokens: f.cached, outputTokens: f.output } }); + expect(onLog.mock.calls.map(call => call[1]).join("")).toBe(line); + }); + it("awaits durability before publication, suppresses identical snapshots and separates attempts", async () => { + const order: string[] = []; + const onUsage = vi.fn(async (..._args: any[]) => { order.push("durable"); }); + const parse = () => ({ usage: { inputTokens: 1, outputTokens: 2 }, complete: false }); + const log = createUsageCheckpointLog(async () => { order.push("published"); }, onUsage, parse); + const message = '{"type":"result","usage":{"input_tokens":1}}\n'; + await log("stdout", message); await log("stdout", message); + expect(order).toEqual(["durable", "published", "published"]); + const other = createUsageCheckpointLog(async () => {}, onUsage, parse); await other("stdout", message); + expect(onUsage.mock.calls[0][0].attemptId).not.toBe(onUsage.mock.calls[1][0].attemptId); + }); + it("propagates receipt failures and leaves ordinary logs and stderr alone when disabled", async () => { + const onLog = vi.fn(async (..._args: any[]) => {}); + expect(createUsageCheckpointLog(onLog, undefined, () => null)).toBe(onLog); + const onUsage = vi.fn(async () => { throw new Error("spool full"); }); + const parse = vi.fn(() => ({ complete: false })); + const log = createUsageCheckpointLog(onLog, onUsage, parse); + await log("stderr", '{"usage":1}\n'); await log("stdout", 'invalid json\n{"text":"plain"}\n'); + expect(onUsage).not.toHaveBeenCalled(); onLog.mockClear(); + await expect(log("stdout", '{"usage":{"input_tokens":1}}\n')).rejects.toThrow("spool full"); + expect(onLog).not.toHaveBeenCalled(); + await expect(log("stdout", "x".repeat(8*1024*1024+1))).rejects.toThrow("exceeds"); + }); +}); diff --git a/server/src/__tests__/cost-accounting-completion.test.ts b/server/src/__tests__/cost-accounting-completion.test.ts new file mode 100644 index 00000000000..6f94b0c9e31 --- /dev/null +++ b/server/src/__tests__/cost-accounting-completion.test.ts @@ -0,0 +1,645 @@ +import { randomUUID } from "node:crypto"; +import { mkdtemp, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { + afterAll, + afterEach, + beforeAll, + describe, + expect, + it, + vi, +} from "vitest"; +import { eq } from "drizzle-orm"; +import { + agents, + companies, + companySecrets, + costEvents, + financeEvents, + heartbeatRuns, + providerBillingSnapshots, + createDb, +} from "@paperclipai/db"; +import { startEmbeddedPostgresTestDatabase } from "./helpers/embedded-postgres.js"; +import { billingReconciliationService } from "../services/billing-reconciliation.js"; +import { financeService } from "../services/finance.js"; +import { + applyProviderDailyCosts, + importProviderDailyCosts, +} from "../services/provider-billing-import.js"; +import * as secretsModule from "../services/secrets.js"; +import { + createRunUsageRecorder, + persistUsageReceipt, +} from "../services/usage-receipts.js"; +import { accountRunCost } from "../services/run-cost-accounting.js"; +import { costService, getMonthlySpendTotal } from "../services/costs.js"; +import { budgetService } from "../services/budgets.js"; +import type { ImportProviderCosts } from "@paperclipai/shared"; + +describe("completed cost reporting paths (PostgreSQL)", () => { + let database: Awaited>; + let db: ReturnType; + let spool: string; + beforeAll(async () => { + database = await startEmbeddedPostgresTestDatabase( + "accounting-completion-", + ); + db = createDb(database.connectionString); + spool = await mkdtemp(join(tmpdir(), "completion-spool-")); + }, 90000); + afterAll(async () => { + await database?.cleanup(); + if (spool) await rm(spool, { recursive: true, force: true }); + }); + afterEach(() => vi.restoreAllMocks()); + const company = async () => + ( + await db + .insert(companies) + .values({ + name: "Completion", + issuePrefix: `C${randomUUID().slice(0, 7)}`, + }) + .returning() + )[0]; + it("reads monthly spend from the ledger with company, agent and UTC month boundaries", async () => { + const c = await company(); + const other = await company(); + const [first, second] = await db.insert(agents).values([ + { companyId: c.id, name: "First", spentMonthlyCents: 9999 }, + { companyId: c.id, name: "Second", spentMonthlyCents: 9999 }, + ]).returning(); + const [outsider] = await db.insert(agents).values({ companyId: other.id, name: "Other" }).returning(); + const now = new Date(); + const start = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)); + const end = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 1)); + const base = { companyId: c.id, agentId: first.id, provider: "test", model: "test", costCents: 1.125, occurredAt: start }; + await db.insert(costEvents).values([ + base, + { ...base, agentId: second.id, costCents: 2.25, occurredAt: new Date(end.getTime() - 1) }, + { ...base, costCents: 500, occurredAt: new Date(start.getTime() - 1) }, + { ...base, costCents: 600, occurredAt: end }, + { ...base, companyId: other.id, agentId: outsider.id, costCents: 700 }, + ]); + expect(await getMonthlySpendTotal(db, { companyId: c.id })).toBe(3.375); + expect(await getMonthlySpendTotal(db, { companyId: c.id, agentId: first.id })).toBe(1.125); + expect(await getMonthlySpendTotal(db, { companyId: c.id, agentId: second.id })).toBe(2.25); + expect(await getMonthlySpendTotal(db, { companyId: c.id, agentId: outsider.id })).toBe(0); + expect(await getMonthlySpendTotal(db, { companyId: randomUUID() })).toBe(0); + }); + + it("scopes agent and model estimate counts to the company and selected period", async () => { + const c = await company(); + const other = await company(); + const [codie, bender] = await db.insert(agents).values([ + { companyId: c.id, name: "Codie", adapterType: "paperclip_runner" }, + { companyId: c.id, name: "Bender", adapterType: "claude_local" }, + ]).returning(); + const [outsider] = await db.insert(agents).values({ companyId: other.id, name: "Other", adapterType: "process" }).returning(); + const base = { companyId: c.id, agentId: codie.id, provider: "openai", biller: "openai", billingType: "metered_api", model: "gpt-6-astra", costCents: 100, occurredAt: new Date("2026-09-10T12:00:00Z") }; + await db.insert(costEvents).values([ + { ...base, costStatus: "estimated" }, + { ...base, costStatus: "estimated" }, + { ...base, model: "gpt-6-sol", costStatus: "reported" }, + { ...base, costStatus: "reported", occurredAt: new Date("2026-08-31T23:59:59Z") }, + { ...base, costStatus: "estimated", occurredAt: new Date("2026-10-01T00:00:00Z") }, + { ...base, agentId: bender.id, costStatus: "reported" }, + { ...base, companyId: other.id, agentId: outsider.id, costStatus: "estimated" }, + ]); + const range = { from: new Date("2026-09-01T00:00:00Z"), to: new Date("2026-09-30T23:59:59Z") }; + const service = costService(db); + const totals = await service.byAgent(c.id, range); + expect(totals).toHaveLength(2); + expect(totals.find(row => row.agentId === codie.id)).toMatchObject({ costCents: 300, eventCount: 3, estimatedEventCount: 2, apiRunCount: 0 }); + expect(totals.find(row => row.agentId === bender.id)).toMatchObject({ eventCount: 1, estimatedEventCount: 0 }); + const models = (await service.byAgentModel(c.id, range)).filter(row => row.agentId === codie.id); + expect(models).toHaveLength(2); + expect(models.find(row => row.model === "gpt-6-astra")).toMatchObject({ eventCount: 2, estimatedEventCount: 2 }); + expect(models.find(row => row.model === "gpt-6-sol")).toMatchObject({ eventCount: 1, estimatedEventCount: 0 }); + }); + it("imports inference, fees and credits atomically into the timeline, without duplicating on retry", async () => { + const c = await company(); + const service = billingReconciliationService(db); + const invoice = { + biller: "openai", + externalId: "invoice-1", + currency: "USD", + lines: [ + { + externalId: "usage", + kind: "inference" as const, + amountCents: "10.0000001", + occurredAt: "2026-09-01T00:00:00Z", + }, + { + externalId: "subscription", + kind: "fee" as const, + amountCents: "2000", + occurredAt: "2026-09-01T00:00:00Z", + }, + { + externalId: "refund", + kind: "credit" as const, + amountCents: "100", + occurredAt: "2026-09-01T00:00:00Z", + }, + ], + }; + const imports = await Promise.all([ + service.importInvoice(c.id, invoice, "board"), + service.importInvoice(c.id, invoice, "board"), + ]); + expect(imports[0].id).toBe(imports[1].id); + expect( + await financeService(db).list(c.id, { + from: new Date("2026-09-01"), + to: new Date("2026-09-30"), + }), + ).toHaveLength(3); + expect( + ( + await financeService(db).summary(c.id, { + from: new Date("2026-09-01"), + to: new Date("2026-09-30"), + }) + ).netCentsExact, + ).toBe("1910.0000001"); + expect( + await db.select().from(costEvents).where(eq(costEvents.companyId, c.id)), + ).toHaveLength(0); + await expect( + service.importInvoice( + c.id, + { ...invoice, lines: [{ ...invoice.lines[0], amountCents: "99" }] }, + "board", + ), + ).rejects.toThrow("different contents"); + expect( + await db + .select() + .from(financeEvents) + .where(eq(financeEvents.companyId, c.id)), + ).toHaveLength(3); + }); + it("records provider report corrections as deltas, including decreases and returns to an earlier total", async () => { + const c = await company(); + const input: ImportProviderCosts = { + provider: "openai", + accountId: "org_test", + secretId: randomUUID(), + scopeIds: ["project"], + from: "2026-09-01", + to: "2026-09-02", + }; + const update = (amountCents: string) => + applyProviderDailyCosts( + db, + c.id, + input, + [{ day: "2026-09-01", scopeId: "project", amountCents }], + "board", + ); + await update("100"); + await update("100"); + await update("120"); + await update("90"); + await update("100"); + const rows = await financeService(db).list(c.id, { + from: new Date("2026-09-01"), + to: new Date("2026-09-30"), + }); + expect(rows).toHaveLength(4); + expect(rows.filter((row) => row.direction === "credit")).toHaveLength(1); + const summary = await financeService(db).summary(c.id, { allTime: true }); + expect(summary).toMatchObject({ + netCentsExact: "0.0000000", + providerReportedCentsExact: "100.0000000", + }); + expect(await financeService(db).byBiller(c.id, { allTime: true })).toEqual( + [], + ); + expect(await financeService(db).byKind(c.id, { allTime: true })).toEqual( + [], + ); + const [snapshot] = await db + .select() + .from(providerBillingSnapshots) + .where(eq(providerBillingSnapshots.companyId, c.id)); + expect(snapshot.amountCents).toBe("100.0000000"); + expect(snapshot.revision).toBe(4); + await expect( + applyProviderDailyCosts( + db, + c.id, + input, + [{ day: "2026-09-01", scopeId: "project", amountCents: "80" }], + "board", + new Map([["2026-09-01:project", 0]]), + ), + ).rejects.toThrow("newer provider report"); + const other = await company(); + await applyProviderDailyCosts( + db, + other.id, + input, + [{ day: "2026-09-01", scopeId: "project", amountCents: "1" }], + "board", + ); + expect( + await db + .select() + .from(financeEvents) + .where(eq(financeEvents.companyId, c.id)), + ).toHaveLength(4); + }); + it("fetches with a company credential and keeps provider reports separate from invoice charges", async () => { + const c = await company(); + const [secret] = await db + .insert(companySecrets) + .values({ companyId: c.id, key: "billing", name: "Billing admin" }) + .returning(); + const resolveSecretValue = vi.fn().mockResolvedValue("fixture-admin-key"); + vi.spyOn(secretsModule, "secretService").mockReturnValue({ + resolveSecretValue, + } as unknown as ReturnType); + const fetcher = vi.fn().mockImplementation(async () => + Response.json({ + data: [ + { + start_time: Date.parse("2026-09-01") / 1000, + end_time: Date.parse("2026-09-02") / 1000, + results: [ + { + project_id: "project", + amount: { value: 0.06, currency: "usd" }, + }, + ], + }, + ], + has_more: false, + }), + ); + const input: ImportProviderCosts = { + provider: "openai", + accountId: "org_test", + secretId: secret.id, + scopeIds: ["project"], + from: "2026-09-01", + to: "2026-09-02", + }; + expect( + await importProviderDailyCosts(db, c.id, input, "board", fetcher), + ).toEqual({ daysRead: 1, eventsCreated: 1 }); + expect(resolveSecretValue).toHaveBeenCalledWith( + c.id, + secret.id, + "latest", + expect.objectContaining({ + accessContext: expect.objectContaining({ + actorId: "board", + consumerId: "provider-billing-import", + }), + }), + ); + expect(fetcher.mock.calls[0][1]?.headers).toMatchObject({ + Authorization: "Bearer fixture-admin-key", + "OpenAI-Organization": "org_test", + }); + expect( + await importProviderDailyCosts(db, c.id, input, "board", fetcher), + ).toEqual({ daysRead: 1, eventsCreated: 0 }); + await billingReconciliationService(db).importInvoice( + c.id, + { + biller: "openai", + externalId: "separate-invoice", + currency: "USD", + lines: [ + { + externalId: "usage", + amountCents: "6", + kind: "inference", + occurredAt: "2026-09-01T00:00:00Z", + }, + ], + }, + "board", + ); + expect( + await financeService(db).summary(c.id, { allTime: true }), + ).toMatchObject({ + netCentsExact: "6.0000000", + providerReportedCentsExact: "6.0000000", + eventCount: 2, + }); + expect(await financeService(db).byBiller(c.id, { allTime: true })).toEqual([ + expect.objectContaining({ + biller: "openai", + netCentsExact: "6.0000000", + eventCount: 1, + }), + ]); + const other = await company(); + await expect( + importProviderDailyCosts(db, other.id, input, "board", fetcher), + ).rejects.toThrow("company billing credential"); + expect(resolveSecretValue).toHaveBeenCalledTimes(2); + }); + it("rejects a slow report when another importer changes the same provider snapshot", async () => { + const c = await company(); + const [secret] = await db + .insert(companySecrets) + .values({ companyId: c.id, key: "billing-race", name: "Billing admin" }) + .returning(); + vi.spyOn(secretsModule, "secretService").mockReturnValue({ + resolveSecretValue: vi.fn().mockResolvedValue("fixture-admin-key"), + } as unknown as ReturnType); + const input: ImportProviderCosts = { + provider: "openai", + accountId: "org_test", + secretId: secret.id, + scopeIds: ["project"], + from: "2026-09-01", + to: "2026-09-02", + }; + const report = (value: number) => + Response.json({ + data: [ + { + start_time: Date.parse(input.from) / 1000, + end_time: Date.parse(input.to) / 1000, + results: [ + { project_id: "project", amount: { currency: "usd", value } }, + ], + }, + ], + has_more: false, + }); + let release!: (response: Response) => void; + let markStarted!: () => void; + const slowResponse = new Promise((resolve) => { + release = resolve; + }); + const started = new Promise((resolve) => { + markStarted = resolve; + }); + const slowImport = importProviderDailyCosts( + db, + c.id, + input, + "board", + async () => { + markStarted(); + return slowResponse; + }, + ); + await started; + await importProviderDailyCosts(db, c.id, input, "board", async () => + report(2), + ); + release(report(1)); + await expect(slowImport).rejects.toThrow("newer provider report"); + expect( + await financeService(db).summary(c.id, { allTime: true }), + ).toMatchObject({ + providerReportedCentsExact: "200.0000000", + eventCount: 1, + }); + }); + it("rolls back an entire provider import when a later snapshot is invalid", async () => { + const c = await company(); + const input: ImportProviderCosts = { + provider: "openai", + accountId: "org_test", + secretId: randomUUID(), + scopeIds: ["project"], + from: "2026-09-01", + to: "2026-09-03", + }; + await expect( + applyProviderDailyCosts( + db, + c.id, + input, + [ + { day: "2026-09-01", scopeId: "project", amountCents: "5" }, + { day: "2026-09-02", scopeId: "project", amountCents: "-1" }, + ], + "board", + ), + ).rejects.toThrow(); + expect( + await db + .select() + .from(providerBillingSnapshots) + .where(eq(providerBillingSnapshots.companyId, c.id)), + ).toEqual([]); + expect( + await db + .select() + .from(financeEvents) + .where(eq(financeEvents.companyId, c.id)), + ).toEqual([]); + }); + it("stores an explicit zero report and credits an amended report down to zero only once", async () => { + const c = await company(); + const input: ImportProviderCosts = { + provider: "anthropic", + accountId: "org_test", + secretId: randomUUID(), + scopeIds: ["default"], + from: "2026-09-01", + to: "2026-09-02", + }; + const update = (amountCents: string) => + applyProviderDailyCosts( + db, + c.id, + input, + [{ day: input.from, scopeId: "default", amountCents }], + "board", + ); + expect((await update("0")).eventsCreated).toBe(0); + expect((await update("0")).eventsCreated).toBe(0); + expect((await update("1.0000001")).eventsCreated).toBe(1); + expect((await update("0")).eventsCreated).toBe(1); + expect((await update("0")).eventsCreated).toBe(0); + expect( + await financeService(db).summary(c.id, { allTime: true }), + ).toMatchObject({ + providerReportedCentsExact: "0.0000000", + netCentsExact: "0.0000000", + eventCount: 2, + }); + }); + it("freezes estimated dollars before finalization and persists them exactly once into costs and budgets", async () => { + const c = await company(); + const budgets = budgetService(db); + await budgets.upsertPolicy(c.id, { scopeType: "company", scopeId: c.id, amount: 100 }, "board"); + const [agent] = await db + .insert(agents) + .values({ + companyId: c.id, + name: "Codie", + adapterType: "paperclip_runner", + }) + .returning(); + const [run] = await db + .insert(heartbeatRuns) + .values({ + companyId: c.id, + agentId: agent.id, + invocationSource: "on_demand", + status: "running", + }) + .returning(); + const recorder = await createRunUsageRecorder( + db, + { companyId: c.id, runId: run.id, adapterType: "paperclip_runner" }, + spool, + ); + const captured = await recorder.capture({ + provider: "openai", + biller: "openai", + billingType: "metered_api", + model: "gpt-6-astra", + usageBasis: "per_run", + complete: true, + costUsd: null, + usage: { + inputTokens: 123536, + cachedInputTokens: 1567209, + outputTokens: 6625, + }, + }); + expect(captured.costUsdExact).toBe("3.133819000"); + await db + .update(heartbeatRuns) + .set({ status: "failed", finishedAt: new Date() }) + .where(eq(heartbeatRuns.id, run.id)); + await accountRunCost(db, run.id); + await accountRunCost(db, run.id); + const [event] = await db + .select() + .from(costEvents) + .where(eq(costEvents.heartbeatRunId, run.id)); + expect(event.costStatus).toBe("estimated"); + expect(event.pricingProvenance).toMatchObject({ + source: "rate_card", + version: "openai-standard-2026-09-30", + }); + const summary = await costService(db).summary(c.id); + expect(summary.spendCentsExact).toBe("313.3819000"); + expect(summary.estimatedEventCount).toBe(1); + expect(await budgets.getInvocationBlock(c.id, agent.id)).not.toBeNull(); + }); + it("preserves cache-write evidence and pricing across multiple provider attempts", async () => { + const c = await company(); + const [agent] = await db + .insert(agents) + .values({ + companyId: c.id, + name: "Retry", + adapterType: "paperclip_runner", + }) + .returning(); + const [run] = await db + .insert(heartbeatRuns) + .values({ companyId: c.id, agentId: agent.id, status: "running" }) + .returning(); + const recorder = await createRunUsageRecorder( + db, + { companyId: c.id, runId: run.id, adapterType: "paperclip_runner" }, + spool, + ); + await recorder.capture({ + attemptId: randomUUID(), + complete: true, + provider: "openai", + biller: "openai", + billingType: "api", + model: "gpt-6-astra", + usageBasis: "per_run", + usage: { inputTokens: 10, cacheWriteTokens: 4, outputTokens: 2 }, + }); + const result = await recorder.capture({ + attemptId: randomUUID(), + complete: true, + provider: "openai", + biller: "openai", + billingType: "api", + costUsd: 2, + }); + expect(result).toMatchObject({ + complete: true, + costUsdExact: "2.000210000", + costStatus: "estimated", + pricingProvenance: { source: "rate_card", version: "per-attempt/v1" }, + usage: { + inputTokens: 10, + cacheWriteTokens: 4, + cachedInputTokens: 0, + outputTokens: 2, + }, + }); + await db + .update(heartbeatRuns) + .set({ status: "succeeded", finishedAt: new Date() }) + .where(eq(heartbeatRuns.id, run.id)); + await accountRunCost(db, run.id); + expect(await costService(db).summary(c.id)).toMatchObject({ + spendCentsExact: "200.0210000", + estimatedEventCount: 1, + }); + }); + it("recovers older durable receipts without pricing metadata while preserving explicit zero", async () => { + const c = await company(); + const sourceId = randomUUID(); + const [agent] = await db + .insert(agents) + .values({ + companyId: c.id, + name: "Legacy receipt", + adapterType: "process", + }) + .returning(); + const [run] = await db + .insert(heartbeatRuns) + .values({ + companyId: c.id, + agentId: agent.id, + status: "running", + usageJson: { accountingReceiptSourceId: sourceId }, + }) + .returning(); + const cases = [ + { costUsd: null }, + { costUsd: 0 }, + { costUsdExact: "0.000000001" }, + ]; + for (const [index, receipt] of cases.entries()) { + await persistUsageReceipt(db, { + schema: "paperclip/accounting-receipt/v1", + id: randomUUID(), + companyId: c.id, + runId: run.id, + adapterType: "process", + sourceId, + sequence: index + 1, + receivedAt: new Date().toISOString(), + receipt: { ...receipt, complete: true }, + }); + const [stored] = await db + .select() + .from(heartbeatRuns) + .where(eq(heartbeatRuns.id, run.id)); + expect(stored.usageJson?.pricingProvenance).toEqual({ + source: index === 0 ? "unknown" : "provider_reported", + version: "accounting-receipt/v1", + }); + } + }); +}); diff --git a/server/src/__tests__/cost-accounting-concurrency.test.ts b/server/src/__tests__/cost-accounting-concurrency.test.ts new file mode 100644 index 00000000000..c4ccb2c577b --- /dev/null +++ b/server/src/__tests__/cost-accounting-concurrency.test.ts @@ -0,0 +1,113 @@ +import { randomUUID } from "node:crypto"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { and, eq } from "drizzle-orm"; +import { agents, agentRuntimeState, approvals, budgetIncidents, budgetPolicies, companies, costEvents, createDb, heartbeatRuns, projects } from "@paperclipai/db"; +import { costService } from "../services/costs.js"; +import { budgetService, type BudgetServiceHooks } from "../services/budgets.js"; +import { accountRunCost, reconcileRunCosts } from "../services/run-cost-accounting.js"; +import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase } from "./helpers/embedded-postgres.js"; + +function random(seed: number) { + return () => { seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0; return seed / 2 ** 32; }; +} +const support = await getEmbeddedPostgresTestSupport(); +(support.supported ? describe : describe.skip)("seeded concurrent accounting invariants", () => { + let database: Awaited>; + let db: ReturnType; + beforeAll(async () => { database = await startEmbeddedPostgresTestDatabase("paperclip-accounting-interleavings-"); db = createDb(database.connectionString); }, 30_000); + afterAll(async () => { await database?.cleanup(); }); + + it.each([1, 7, 42, 97, 31337, 65537, 104729, 0xdeadbeef])("conserves the ledger, totals, incidents and delivery state for seed %s", async (seed) => { + const rng = random(seed); + const [company] = await db.insert(companies).values({ name: `Interleaving ${seed}`, issuePrefix: `R${randomUUID().slice(0,7)}` }).returning(); + const [agent] = await db.insert(agents).values({ companyId: company.id, name: "Worker", role: "engineer", status: "idle", adapterType: "process" }).returning(); + const [project] = await db.insert(projects).values({ companyId: company.id, name: "Project" }).returning(); + const oracle = new Map(); + const operations: Array<() => Promise> = []; + let cancellationAttempts = 0; + let successfulDeliveries = 0; + const hooks: BudgetServiceHooks = { cancelWorkForScope: async (scope) => { + expect(scope.companyId).toBe(company.id); + expect(scope.createdBefore).toBeInstanceOf(Date); + cancellationAttempts++; + if (cancellationAttempts % 4 === 1) throw new Error(`Injected delivery interruption (seed ${seed})`); + successfulDeliveries++; + } }; + const budgets = budgetService(db, hooks); + const costs = costService(db, hooks); + const now = new Date(); + for (let index = 0; index < 12; index++) { + const ticks = Math.floor(rng() * 9_000_000) + 1; + const input = Math.floor(rng() * 300), cached = Math.floor(rng() * 500), output = Math.floor(rng() * 80); + const [run] = await db.insert(heartbeatRuns).values({ companyId: company.id, agentId: agent.id, + status: ["succeeded", "failed", "cancelled", "timed_out"][index % 4], finishedAt: now, costAccountingPending: true, + usageJson: { accountingReceiptReady: true, provider: "fixture", model: `model-${index % 3}`, billingType: "metered_api", inputTokens: input, cachedInputTokens: cached, outputTokens: output, costUsd: ticks / 1e9, ledgerScope: { projectId: project.id } }, + }).returning(); + oracle.set(`heartbeat:${run.id}:final`, { ticks: BigInt(ticks), input, cached, output, run: true }); + operations.push(() => accountRunCost(db, run.id, hooks), () => accountRunCost(db, run.id, hooks)); + } + for (let index = 0; index < 10; index++) { + const ticks = Math.floor(rng() * 3_000_000) + 1; + const key = `manual-${index}`; + const receipt = { idempotencyKey: key, agentId: agent.id, projectId: project.id, provider: "fixture", model: "manual", billingType: "metered_api", costCents: ticks / 1e7, occurredAt: now }; + oracle.set(key, { ticks: BigInt(ticks), input: 0, cached: 0, output: 0, run: false }); + operations.push(() => costs.createEvent(company.id, receipt), () => costs.createEvent(company.id, receipt)); + } + for (let index = 0; index < 12; index++) { + const amount = rng() < 0.5 ? 1 : 200; + const scopeType = index % 2 ? "agent" as const : "project" as const; + operations.push(() => budgets.upsertPolicy(company.id, { scopeType, scopeId: scopeType === "agent" ? agent.id : project.id, amount, notifyEnabled: false }, "board")); + operations.push(() => budgets.getInvocationBlock(company.id, agent.id, { projectId: project.id })); + operations.push(() => reconcileRunCosts(db, hooks)); + } + // Reproducible submission order and jitter; the database is free to choose + // the actual interleaving. The oracle is independent of execution order. + for (let i = operations.length - 1; i > 0; i--) { + const j = Math.floor(rng() * (i + 1)); [operations[i], operations[j]] = [operations[j], operations[i]]; + } + for (let offset = 0; offset < operations.length; offset += 8) { + await Promise.all(operations.slice(offset, offset + 8).map(async operation => { + await new Promise(resolve => setTimeout(resolve, Math.floor(rng() * 5))); + return operation(); + })); + const incidents = await db.select().from(budgetIncidents).where(and(eq(budgetIncidents.companyId, company.id), eq(budgetIncidents.status, "open"))); + expect(new Set(incidents.map(row => `${row.policyId}:${row.windowStart.toISOString()}:${row.thresholdType}`)).size).toBe(incidents.length); + } + await reconcileRunCosts(db, hooks); + const rows = await db.select().from(costEvents).where(eq(costEvents.companyId, company.id)); + expect(rows).toHaveLength(oracle.size); + for (const row of rows) { + const expected = oracle.get(row.idempotencyKey!); + expect(expected, `unexpected receipt ${row.idempotencyKey}; seed ${seed}`).toBeDefined(); + expect(BigInt(Math.round(row.costCents * 1e7))).toBe(expected!.ticks); + expect([row.inputTokens, row.cachedInputTokens, row.outputTokens]).toEqual([expected!.input, expected!.cached, expected!.output]); + } + const total = [...oracle.values()].reduce((sum, row) => sum + row.ticks, 0n); + expect(BigInt(Math.round((await costs.summary(company.id)).spendCents * 1e7))).toBe(total); + expect(BigInt(Math.round((await costs.byProject(company.id))[0].costCents * 1e7))).toBe(total); + const [companyProjection] = await db.select().from(companies).where(eq(companies.id, company.id)); + const [agentProjection] = await db.select().from(agents).where(eq(agents.id, agent.id)); + expect(BigInt(Math.round(companyProjection.spentMonthlyCents * 1e7))).toBe(total); + expect(BigInt(Math.round(agentProjection.spentMonthlyCents * 1e7))).toBe(total); + const [runtime] = await db.select().from(agentRuntimeState).where(eq(agentRuntimeState.agentId, agent.id)); + const runs = [...oracle.values()].filter(row => row.run); + expect(BigInt(Math.round(runtime.totalCostCents * 1e7))).toBe(runs.reduce((sum, row) => sum + row.ticks, 0n)); + expect([runtime.totalInputTokens,runtime.totalCachedInputTokens,runtime.totalOutputTokens]) + .toEqual(["input","cached","output"].map(key => runs.reduce((sum,row) => sum + Number(row[key as "input" | "cached" | "output"]),0))); + expect((await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.companyId, company.id))).every(run => !run.costAccountingPending && run.costAccountedAt !== null)).toBe(true); + // Finish in a known hard-stop state, deliver failed effects, then grant more + // budget. This checks the final observable state, not only receipt counts. + await budgets.upsertPolicy(company.id, { scopeType: "agent", scopeId: agent.id, amount: 1, notifyEnabled: false }, "board"); + expect(await budgets.getInvocationBlock(company.id, agent.id)).toMatchObject({ scopeType: "agent" }); + for (let pass = 0; pass < 4; pass++) await budgets.deliverPendingEnforcement(company.id); + expect(successfulDeliveries).toBeGreaterThan(0); + const policies = await db.select().from(budgetPolicies).where(eq(budgetPolicies.companyId, company.id)); + expect(policies.every(row => row.enforcementVersion === row.enforcementDeliveredVersion)).toBe(true); + const allIncidents = await db.select().from(budgetIncidents).where(eq(budgetIncidents.companyId, company.id)); + const approvalRows = await db.select().from(approvals).where(eq(approvals.companyId, company.id)); + expect(new Set(allIncidents.map(row => row.approvalId).filter(Boolean)).size).toBe(approvalRows.length); + await budgets.upsertPolicy(company.id, { scopeType: "agent", scopeId: agent.id, amount: 200 }, "board"); + await budgets.upsertPolicy(company.id, { scopeType: "project", scopeId: project.id, amount: 200 }, "board"); + expect(await budgets.getInvocationBlock(company.id, agent.id, { projectId: project.id })).toBeNull(); + }, 60_000); +}); diff --git a/server/src/__tests__/cost-accounting-crash.test.ts b/server/src/__tests__/cost-accounting-crash.test.ts new file mode 100644 index 00000000000..d4e029a26df --- /dev/null +++ b/server/src/__tests__/cost-accounting-crash.test.ts @@ -0,0 +1,126 @@ +import { spawn, type ChildProcess } from "node:child_process"; +import { randomUUID, randomInt } from "node:crypto"; +import { mkdtemp, rm } from "node:fs/promises"; +import { createServer } from "node:net"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { eq } from "drizzle-orm"; +import { agents, companies, costEvents, heartbeatRuns, budgetPolicies, createDb } from "@paperclipai/db"; +import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase } from "./helpers/embedded-postgres.js"; + +const support = await getEmbeddedPostgresTestSupport(); +const root = fileURLToPath(new URL("../../..", import.meta.url)); +async function until(read: () => Promise, accept: (value: T) => boolean, diagnostic: () => string = () => "", timeout = 40_000) { + const deadline = Date.now() + timeout; + while (Date.now() < deadline) { + const value = await read(); + if (accept(value)) return value; + await new Promise(resolve => setTimeout(resolve, 30)); + } + throw new Error(`Accounting process checkpoint timed out. ${diagnostic()}`); +} + +(support.supported && process.platform !== "win32" ? describe : describe.skip)("accounting survives real server SIGKILL", () => { + let database: Awaited>; + beforeAll(async () => { database = await startEmbeddedPostgresTestDatabase("paperclip-accounting-crash-"); }, 30_000); + afterAll(async () => { await database?.cleanup(); }); + + it.each(["before_receipt", "before_runtime_totals", "before_commit", "after_commit_before_delivery_ack"] as const)("recovers exactly once after %s", async (phase) => { + const db = createDb(database.connectionString); + const observer = createDb(database.connectionString).$client; + const blocker = await createDb(database.connectionString).$client.reserve(); + const home = await mkdtemp(path.join(tmpdir(), "pc-accounting-kill-")); + const key = randomInt(1, 2_000_000_000); + const [company] = await db.insert(companies).values({ name: "Crash accounting", issuePrefix: `K${randomUUID().slice(0,7)}` }).returning(); + const [agent] = await db.insert(agents).values({ companyId: company.id, name: "Worker", role: "engineer", status: "idle", adapterType: "process", runtimeConfig: { heartbeat: { enabled: false } } }).returning(); + const [policy] = await db.insert(budgetPolicies).values({ companyId: company.id, scopeType: "agent", scopeId: agent.id, metric: "billed_cents", windowKind: "calendar_month_utc", amount: 1, notifyEnabled: false }).returning(); + const [run] = await db.insert(heartbeatRuns).values({ companyId: company.id, agentId: agent.id, status: "failed", finishedAt: new Date(), costAccountingPending: true, + usageJson: { accountingReceiptReady: true, provider: "fixture", model: "fixture", billingType: "metered_api", inputTokens: 7, cachedInputTokens: 11, outputTokens: 3, costUsd: 0.0125 }, + }).returning(); + const table = phase === "before_receipt" ? "cost_events" : phase === "before_runtime_totals" ? "agent_runtime_state" + : phase === "before_commit" ? "heartbeat_runs" : "budget_policies"; + const event = phase === "after_commit_before_delivery_ack" || phase === "before_commit" ? "UPDATE" : "INSERT"; + const condition = phase === "after_commit_before_delivery_ack" ? "AND NEW.enforcement_delivered_version > OLD.enforcement_delivered_version" + : phase === "before_commit" ? "AND OLD.cost_accounting_pending = false AND NEW.cost_accounted_at IS NOT NULL" : ""; + let child: ChildProcess | undefined; + let logs = ""; + async function kill() { + if (!child || child.exitCode !== null || child.signalCode !== null) return; + const stopped = new Promise((resolve) => child!.once("exit", () => resolve())); + process.kill(-child.pid!, "SIGKILL"); + await stopped; + } + async function start() { + const port = await new Promise((resolve, reject) => { + const listener = createServer(); listener.on("error", reject); + listener.listen(0, "127.0.0.1", () => { const address = listener.address() as { port: number }; listener.close(() => resolve(address.port)); }); + }); + logs = ""; + child = spawn(process.execPath, ["cli/node_modules/tsx/dist/cli.mjs", "server/src/index.ts"], { + cwd: root, detached: true, stdio: ["ignore", "pipe", "pipe"], + env: { PATH: process.env.PATH, HOME: process.env.HOME, TMPDIR: process.env.TMPDIR, + NODE_ENV: "test", DATABASE_URL: database.connectionString, PORT: String(port), + PAPERCLIP_HOME: home, PAPERCLIP_CONFIG: path.join(home, "config.json"), PAPERCLIP_INSTANCE_ID: "accounting-crash", + PAPERCLIP_DEPLOYMENT_MODE: "local_trusted", PAPERCLIP_BIND: "loopback", SERVE_UI: "false", + PAPERCLIP_OPEN_ON_LISTEN: "false", PAPERCLIP_TELEMETRY_DISABLED: "1", PAPERCLIP_ANNOUNCEMENTS_ENABLED: "false", + HEARTBEAT_SCHEDULER_ENABLED: "false", PAPERCLIP_DECISION_SIGNING_SECRET: "accounting-crash-test-decision-secret", + PAPERCLIP_AGENT_JWT_SECRET: "accounting-crash-test-agent-secret", + }, + }); + const capture = (data: Buffer) => { logs = (logs + data.toString()).slice(-16000); }; + child.stdout!.on("data", capture); child.stderr!.on("data", capture); + return `http://127.0.0.1:${port}`; + } + try { + await blocker`SELECT pg_advisory_lock(${key})`; + await observer.unsafe(`CREATE FUNCTION accounting_crash_barrier() RETURNS trigger LANGUAGE plpgsql AS $$ + BEGIN IF NEW.company_id = '${company.id}'::uuid ${condition} THEN PERFORM pg_advisory_lock(${key}); END IF; RETURN NEW; END $$; + CREATE TRIGGER accounting_crash_barrier BEFORE ${event} ON ${table} FOR EACH ROW EXECUTE FUNCTION accounting_crash_barrier()`); + await start(); + const waiting = await until(() => { + if (child!.exitCode !== null || child!.signalCode !== null) throw new Error(`Accounting server exited before the barrier: ${logs}`); + return observer`SELECT pid FROM pg_locks WHERE locktype='advisory' AND objid=${key} AND NOT granted`; + }, rows => rows.length > 0, () => logs); + const committed = phase === "after_commit_before_delivery_ack"; + expect(await db.select().from(costEvents).where(eq(costEvents.heartbeatRunId, run.id))).toHaveLength(committed ? 1 : 0); + await kill(); + expect(child?.signalCode).toBe("SIGKILL"); + // A backend blocked inside our artificial barrier cannot observe its + // dead client until the query returns. Terminate that orphan connection + // before releasing the barrier, forcing PostgreSQL's disconnect rollback. + for (const row of waiting) await observer`SELECT pg_terminate_backend(${row.pid})`; + await until(() => observer`SELECT pid FROM pg_locks WHERE locktype='advisory' AND objid=${key} AND NOT granted`, rows => rows.length === 0); + await blocker`SELECT pg_advisory_unlock(${key})`; + await observer.unsafe(`DROP TRIGGER accounting_crash_barrier ON ${table}; DROP FUNCTION accounting_crash_barrier()`); + const baseUrl = await start(); + await until(() => observer`SELECT cost_accounting_pending,cost_accounted_at FROM heartbeat_runs WHERE id=${run.id}`, + rows => rows[0].cost_accounting_pending === false && rows[0].cost_accounted_at !== null, () => logs); + await until(() => observer`SELECT enforcement_version,enforcement_delivered_version FROM budget_policies WHERE id=${policy.id}`, + rows => rows[0].enforcement_version > 0 && rows[0].enforcement_version === rows[0].enforcement_delivered_version, () => logs); + const assertLedger = async () => { + expect((await observer`SELECT count(*)::int AS count,sum(cost_cents)::text AS cents FROM cost_events WHERE heartbeat_run_id=${run.id}`)[0]).toEqual({ count: 1, cents: "1.2500000" }); + expect((await observer`SELECT total_input_tokens,total_cached_input_tokens,total_output_tokens,total_cost_cents::text FROM agent_runtime_state WHERE agent_id=${agent.id}`)[0]) + .toEqual({ total_input_tokens: "7", total_cached_input_tokens: "11", total_output_tokens: "3", total_cost_cents: "1.2500000" }); + expect((await observer`SELECT spent_monthly_cents::text,status FROM agents WHERE id=${agent.id}`)[0]).toEqual({ spent_monthly_cents: "1.2500000", status: "paused" }); + expect((await observer`SELECT count(*)::int AS count FROM budget_incidents WHERE policy_id=${policy.id}`)[0].count).toBe(1); + expect((await observer`SELECT count(*)::int AS count FROM approvals WHERE company_id=${company.id}`)[0].count).toBe(1); + }; + await assertLedger(); + await until(async () => fetch(`${baseUrl}/api/health`).then(r => r.ok).catch(() => false), Boolean, () => logs); + await kill(); + const replayUrl = await start(); + await until(async () => fetch(`${replayUrl}/api/health`).then(r => r.ok).catch(() => false), Boolean, () => logs); + await until(async () => logs.includes(`Server startup recovery complete on 127.0.0.1:${new URL(replayUrl).port}`), Boolean, () => logs); + await assertLedger(); + } finally { + await kill(); + await blocker`SELECT pg_advisory_unlock_all()`; + blocker.release(); + await observer.unsafe(`DROP TRIGGER IF EXISTS accounting_crash_barrier ON ${table}; DROP FUNCTION IF EXISTS accounting_crash_barrier()`); + await observer.end(); + await rm(home, { recursive: true, force: true }); + } + }, 150_000); +}); diff --git a/server/src/__tests__/cost-accounting-edge-cases.test.ts b/server/src/__tests__/cost-accounting-edge-cases.test.ts new file mode 100644 index 00000000000..c6dc3e7f528 --- /dev/null +++ b/server/src/__tests__/cost-accounting-edge-cases.test.ts @@ -0,0 +1,241 @@ +import { randomUUID } from "node:crypto"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +import { eq } from "drizzle-orm"; +import { activityLog, agents, companies, costEvents, createDb, goals, heartbeatRuns, issues, projects } from "@paperclipai/db"; +import { costService } from "../services/costs.js"; +import { financeService } from "../services/finance.js"; +import { budgetService } from "../services/budgets.js"; +import { accountRunCost, reconcileRunCosts } from "../services/run-cost-accounting.js"; +import { receiptFingerprint } from "../services/receipt-fingerprint.js"; +import { subscribeCompanyLiveEvents } from "../services/live-events.js"; +import { logger } from "../middleware/logger.js"; +import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase } from "./helpers/embedded-postgres.js"; + +const support = await getEmbeddedPostgresTestSupport(); +(support.supported ? describe : describe.skip)("accounting validation and recovery edges (PostgreSQL)", () => { + let database: Awaited>; + let db: ReturnType; + beforeAll(async () => { database = await startEmbeddedPostgresTestDatabase("paperclip-accounting-edges-"); db = createDb(database.connectionString); }, 30_000); + afterAll(async () => { await database?.cleanup(); }); + async function fixture() { + const [company] = await db.insert(companies).values({ name: "Accounting edges", issuePrefix: `E${randomUUID().slice(0, 7)}` }).returning(); + const [agent] = await db.insert(agents).values({ companyId: company.id, name: "Worker", role: "engineer", adapterType: "process", status: "idle" }).returning(); + const [project] = await db.insert(projects).values({ companyId: company.id, name: "Project" }).returning(); + const [issue] = await db.insert(issues).values({ companyId: company.id, title: "Work", projectId: project.id }).returning(); + const [goal] = await db.insert(goals).values({ companyId: company.id, title: "Goal" }).returning(); + const [run] = await db.insert(heartbeatRuns).values({ companyId: company.id, agentId: agent.id }).returning(); + const receipt = { agentId: agent.id, provider: "fixture", model: "fixture", billingType: "metered_api", costCents: 0.1234567, occurredAt: new Date() }; + const cost = await costService(db).createEvent(company.id, receipt); + const finance = { eventKind: "inference_charge", biller: "fixture", amountCents: 123, occurredAt: new Date() }; + return { company, agent, project, issue, goal, run, cost, receipt, finance, + links: { agentId: agent.id, projectId: project.id, issueId: issue.id, goalId: goal.id, heartbeatRunId: run.id, costEventId: cost.id } }; + } + + it.each(["agentId", "projectId", "issueId", "goalId", "heartbeatRunId", "costEventId"] as const)("rejects foreign and missing finance %s, accepts company-owned links", async field => { + const a = await fixture(), b = await fixture(); + const service = financeService(db); + await expect(service.createEvent(a.company.id, { ...a.finance, [field]: b.links[field] })).rejects.toMatchObject({ status: 422 }); + await expect(service.createEvent(a.company.id, { ...a.finance, [field]: randomUUID() })).rejects.toMatchObject({ status: 404 }); + expect(await service.list(a.company.id)).toHaveLength(0); + const event = await service.createEvent(a.company.id, { ...a.finance, ...a.links }, { actorType: "user", actorId: "board", agentId: a.agent.id }); + expect(event[field]).toBe(a.links[field]); + expect(await service.list(a.company.id)).toEqual([event]); + }); + + it("normalizes finance retries and rolls back changed keys and invalid amounts/currencies", async () => { + const f = await fixture(), service = financeService(db); + const receipt = { ...f.finance, idempotencyKey: "invoice", currency: "usd", metadataJson: { entries: [{ z: 2, a: 1 }, null] } }; + const event = await service.createEvent(f.company.id, receipt); + expect((await service.createEvent(f.company.id, { ...receipt, currency: "USD", metadataJson: { entries: [{ a: 1, z: 2 }, null] } })).id).toBe(event.id); + await expect(service.createEvent(f.company.id, { ...receipt, amountCents: 124 })).rejects.toMatchObject({ status: 409 }); + for (const invalid of [{ amountCents: -1 }, { amountCents: "1.2.3" }, { amountCents: Number.POSITIVE_INFINITY }, { currency: "US" }]) { + await expect(service.createEvent(f.company.id, { ...f.finance, ...invalid })).rejects.toMatchObject({ status: 422 }); + } + expect(await service.list(f.company.id)).toHaveLength(1); + expect((await db.select().from(activityLog).where(eq(activityLog.companyId, f.company.id))).filter(row => row.action === "finance_event.reported")).toHaveLength(1); + }); + + it("keeps finance credits, estimates, currencies, dates, and companies separate in every report", async () => { + const f = await fixture(), foreign = await fixture(), service = financeService(db); + const date = new Date("2026-08-15T12:00:00Z"); + await service.createEvent(f.company.id, { ...f.finance, occurredAt: date, amountCents: 150, estimated: true }); + await service.createEvent(f.company.id, { ...f.finance, occurredAt: new Date(date.getTime() + 1), eventKind: "credit_refund", direction: "credit", amountCents: 40 }); + await service.createEvent(f.company.id, { ...f.finance, occurredAt: date, amountCents: 900, currency: "EUR" }); + await service.createEvent(f.company.id, { ...f.finance, occurredAt: new Date("2026-08-16T00:00:00Z"), amountCents: 999 }); + await service.createEvent(foreign.company.id, { ...foreign.finance, occurredAt: date, amountCents: 999 }); + const range = { from: date, to: new Date(date.getTime() + 1) }; + expect(await service.summary(f.company.id, range)).toMatchObject({ debitCents: 150, creditCents: 40, estimatedDebitCents: 150, netCents: 110, eventCount: 2, + currencies: [expect.objectContaining({ currency: "EUR", netCents: 900, eventCount: 1 }), expect.objectContaining({ currency: "USD", netCents: 110, eventCount: 2 })] }); + expect(await service.byBiller(f.company.id, range)).toEqual([ + expect.objectContaining({ biller: "fixture", currency: "EUR", netCents: 900, eventCount: 1, kindCount: 1 }), + expect.objectContaining({ biller: "fixture", currency: "USD", netCents: 110, eventCount: 2, kindCount: 2 }), + ]); + expect(await service.byKind(f.company.id, range)).toEqual(expect.arrayContaining([ + expect.objectContaining({ eventKind: "inference_charge", currency: "EUR", netCents: 900 }), + expect.objectContaining({ eventKind: "inference_charge", currency: "USD", netCents: 150, estimatedDebitCents: 150 }), + expect.objectContaining({ eventKind: "credit_refund", currency: "USD", netCents: -40, billerCount: 1 }), + ])); + expect(await service.list(f.company.id, range, 1)).toEqual([expect.objectContaining({ direction: "credit", amountCents: 40 })]); + expect(await service.summary(randomUUID())).toMatchObject({ currencies: [], debitCents: 0, creditCents: 0, netCents: 0, eventCount: 0 }); + }); + + it("rejects missing companies, missing links, run/agent mismatches, and invalid cost inputs without side effects", async () => { + const f = await fixture(), costs = costService(db); + await expect(costs.createEvent(randomUUID(), f.receipt)).rejects.toMatchObject({ status: 404 }); + await expect(costs.summary(randomUUID())).rejects.toMatchObject({ status: 404 }); + for (const field of ["agentId", "issueId", "projectId", "goalId", "heartbeatRunId"]) { + await expect(costs.createEvent(f.company.id, { ...f.receipt, [field]: randomUUID() })).rejects.toMatchObject({ status: 404 }); + } + const [otherAgent] = await db.insert(agents).values({ companyId: f.company.id, name: "Other", role: "engineer", adapterType: "process" }).returning(); + await expect(costs.createEvent(f.company.id, { ...f.receipt, agentId: otherAgent.id, heartbeatRunId: f.run.id })).rejects.toThrow("does not belong to agent"); + for (const invalid of [{ costCents: -1 }, { costCents: NaN }, { inputTokens: 0.5 }, { cachedInputTokens: -1 }, { outputTokens: 2 ** 31 }]) { + await expect(costs.createEvent(f.company.id, { ...f.receipt, ...invalid })).rejects.toMatchObject({ status: 422 }); + } + expect((await costs.summary(f.company.id)).spendCents).toBe(f.receipt.costCents); + expect(await db.select().from(costEvents).where(eq(costEvents.companyId, f.company.id))).toHaveLength(1); + }); + + it("aggregates billers with fractional cents, cache tokens, and subscription runs without cross-company leakage", async () => { + const f = await fixture(), foreign = await fixture(), costs = costService(db); + const date = new Date("2026-08-15T12:00:00Z"); + await costs.createEvent(f.company.id, { ...f.receipt, occurredAt: date, biller: "gateway", provider: "a", model: "a", heartbeatRunId: f.run.id, inputTokens: 2, cachedInputTokens: 3, outputTokens: 5 }); + await costs.createEvent(f.company.id, { ...f.receipt, occurredAt: date, biller: "gateway", provider: "b", model: "b", heartbeatRunId: f.run.id, billingType: "subscription_included", costCents: 0, inputTokens: 7, cachedInputTokens: 11, outputTokens: 13 }); + await costs.createEvent(foreign.company.id, { ...foreign.receipt, occurredAt: date, biller: "gateway", costCents: 999 }); + expect(await costs.byBiller(f.company.id, { from: date, to: date })).toEqual([{ + biller: "gateway", costCents: 0.1234567, costCentsExact: "0.1234567", inputTokens: 9, cachedInputTokens: 14, outputTokens: 18, + apiRunCount: 1, subscriptionRunCount: 1, subscriptionInputTokens: 7, subscriptionCachedInputTokens: 11, subscriptionOutputTokens: 13, providerCount: 2, modelCount: 2, + }]); + }); + + it("commits a receipt and its audit record even when a live subscriber throws", async () => { + const f = await fixture(); + const listener = vi.fn(() => { throw new Error("Injected disconnected live subscriber"); }); + const unsubscribe = subscribeCompanyLiveEvents(f.company.id, listener); + const warning = vi.spyOn(logger, "warn").mockImplementation(() => undefined); + try { + const receipt = { ...f.receipt, idempotencyKey: "committed-before-publication" }; + const event = await costService(db).createEvent(f.company.id, receipt, { actorType: "user", actorId: "board" }); + expect((await costService(db).createEvent(f.company.id, receipt)).id).toBe(event.id); + expect(listener).toHaveBeenCalledTimes(1); + expect(warning).toHaveBeenCalledOnce(); + expect((await costService(db).summary(f.company.id)).spendCents).toBe(0.2469134); + expect(await db.select().from(activityLog).where(eq(activityLog.entityId, event.id))).toHaveLength(1); + } finally { unsubscribe(); warning.mockRestore(); } + }); + + it("reports lifetime history consistently across every cost grouping and finance view", async () => { + const f = await fixture(), costs = costService(db), finance = financeService(db); + const occurredAt = new Date("2000-01-01T00:00:00Z"); + await costs.createEvent(f.company.id, { ...f.receipt, occurredAt, costCents: 5, projectId: f.project.id, inputTokens: 2 }); + await finance.createEvent(f.company.id, { ...f.finance, occurredAt }); + const lifetime = { allTime: true }; + for (const read of [costs.byAgent, costs.byAgentModel, costs.byProvider, costs.byBiller, costs.byProject]) { + const rows = await read(f.company.id, lifetime); + expect(rows.reduce((sum, row) => sum + row.costCents, 0)).toBe(5.1234567); + expect(rows.reduce((sum, row) => sum + row.inputTokens, 0)).toBe(2); + } + expect((await costs.summary(f.company.id)).spendCents).toBe(0.1234567); + expect((await costs.summary(f.company.id, lifetime)).spendCents).toBe(5.1234567); + expect((await finance.summary(f.company.id)).eventCount).toBe(0); + expect((await finance.summary(f.company.id, lifetime)).debitCents).toBe(123); + expect(await finance.list(f.company.id, lifetime)).toHaveLength(1); + expect((await finance.byBiller(f.company.id, lifetime))[0].netCents).toBe(123); + expect((await finance.byKind(f.company.id, lifetime))[0].netCents).toBe(123); + await db.update(companies).set({ budgetMonthlyCents: 1 }).where(eq(companies.id, f.company.id)); + expect((await costs.summary(f.company.id)).utilizationPercent).toBe(12.35); + }); + + it.each(["company", "agent", "project"] as const)("rejects nonexistent and foreign %s budget scopes without creating policies", async scopeType => { + const f = await fixture(), foreign = await fixture(), budgets = budgetService(db); + await expect(budgets.upsertPolicy(f.company.id, { scopeType, scopeId: randomUUID(), amount: 10 }, "board")).rejects.toMatchObject({ status: 404 }); + await expect(budgets.upsertPolicy(f.company.id, { scopeType, scopeId: foreign[scopeType].id, amount: 10 }, "board")).rejects.toMatchObject({ status: 422 }); + expect(await budgets.listPolicies(f.company.id)).toHaveLength(0); + if (scopeType !== "company") { + await expect(budgets.getInvocationBlock(f.company.id, scopeType === "agent" ? foreign.agent.id : f.agent.id, + scopeType === "project" ? { projectId: foreign.project.id } : undefined)).rejects.toMatchObject({ status: 404 }); + } + }); + + it.each(["unpriced", "pending"] as const)("refuses an incident budget raise while accounting is %s, without partial mutations", async kind => { + const f = await fixture(), budgets = budgetService(db); + await budgets.upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 10 }, "board"); + if (kind === "unpriced") { + await costService(db).createEvent(f.company.id, { ...f.receipt, costStatus: "unpriced", costCents: 0 }); + } else { + await db.update(heartbeatRuns).set({ status: "failed", costAccountingPending: true, finishedAt: new Date(), usageJson: { accountingReceiptReady: false } }).where(eq(heartbeatRuns.id, f.run.id)); + await budgets.getInvocationBlock(f.company.id, f.agent.id); + } + const [incident] = (await budgets.overview(f.company.id)).activeIncidents; + const resolution = { action: "raise_budget_and_resume" as const, amount: 100 }; + await expect(budgets.resolveIncident(f.company.id, randomUUID(), resolution, "board")).rejects.toMatchObject({ status: 404 }); + const foreign = await fixture(); + await expect(budgets.resolveIncident(foreign.company.id, incident.id, resolution, "board")).rejects.toMatchObject({ status: 404 }); + await expect(budgets.resolveIncident(f.company.id, incident.id, resolution, "board")).rejects.toThrow(kind === "unpriced" ? /unpriced usage/ : /finish accounting/); + expect((await budgets.listPolicies(f.company.id))[0].amount).toBe(10); + expect((await budgets.overview(f.company.id)).activeIncidents).toEqual([expect.objectContaining({ id: incident.id, status: "open" })]); + expect((await db.select().from(agents).where(eq(agents.id, f.agent.id)))[0].status).toBe("paused"); + }); + + it.each(["company", "project"] as const)("preserves an operator's %s pause even without a policy", async scopeType => { + const f = await fixture(); + if (scopeType === "company") await db.update(companies).set({ status: "paused", pauseReason: "manual", pausedAt: new Date() }).where(eq(companies.id, f.company.id)); + else await db.update(projects).set({ pauseReason: "manual", pausedAt: new Date() }).where(eq(projects.id, f.project.id)); + expect(await budgetService(db).getInvocationBlock(f.company.id, f.agent.id, { projectId: f.project.id })) + .toMatchObject({ scopeType, reason: expect.stringContaining("paused and cannot start") }); + }); + + it("resolves a company incident and updates the legacy monthly budget together", async () => { + const f = await fixture(), budgets = budgetService(db); + await budgets.upsertPolicy(f.company.id, { scopeType: "company", scopeId: f.company.id, amount: 1 }, "board"); + await costService(db).createEvent(f.company.id, { ...f.receipt, costCents: 2 }); + const [incident] = (await budgets.overview(f.company.id)).activeIncidents; + await budgets.resolveIncident(f.company.id, incident.id, { action: "raise_budget_and_resume", amount: 10 }, "board"); + expect((await db.select().from(companies).where(eq(companies.id, f.company.id)))[0]).toMatchObject({ status: "active", pauseReason: null, budgetMonthlyCents: 10 }); + expect((await budgets.listPolicies(f.company.id))[0].amount).toBe(10); + expect(await budgets.getInvocationBlock(f.company.id, f.agent.id)).toBeNull(); + }); + + it.each([false, true])("records subscription tokens at zero incremental charge (split=%s)", async split => { + const f = await fixture(); + await db.update(heartbeatRuns).set({ status: "succeeded", costAccountingPending: true, + usageJson: { billingType: "subscription_included", costUsd: 1, inputTokens: 3, cachedInputTokens: 5, outputTokens: 7, + ...(split ? { usageByModel: [{ model: "a", costUsd: 1, usage: { inputTokens: 3, cachedInputTokens: 5, outputTokens: 7 } }] } : {}), + }, + }).where(eq(heartbeatRuns.id, f.run.id)); + expect(await accountRunCost(db, f.run.id)).toBe(true); + expect(await db.select().from(costEvents).where(eq(costEvents.heartbeatRunId, f.run.id))).toEqual([ + expect.objectContaining({ costCents: 0, costStatus: "reported", inputTokens: 3, cachedInputTokens: 5, outputTokens: 7, billingType: "subscription_included" }), + ]); + }); + + it("rotates a full poisoned recovery batch so the next valid receipt cannot starve", async () => { + const f = await fixture(), foreign = await fixture(); + const poison = await db.insert(heartbeatRuns).values(Array.from({ length: 100 }, () => ({ + companyId: f.company.id, agentId: f.agent.id, status: "failed", costAccountingPending: true, updatedAt: new Date(0), + usageJson: { accountingReceiptReady: true, costUsd: 0.01, ledgerScope: { projectId: foreign.project.id } }, + }))).returning(); + const [valid] = await db.insert(heartbeatRuns).values({ companyId: f.company.id, agentId: f.agent.id, status: "succeeded", costAccountingPending: true, + updatedAt: new Date(1), usageJson: { accountingReceiptReady: true, costUsd: 0.02 }, + }).returning(); + const error = vi.spyOn(logger, "error").mockImplementation(() => undefined); + try { + expect(await reconcileRunCosts(db)).toEqual({ scanned: 100, accounted: 0 }); + expect(await reconcileRunCosts(db)).toEqual({ scanned: 100, accounted: 1 }); + expect(error).toHaveBeenCalledTimes(199); + expect(await db.select().from(costEvents).where(eq(costEvents.heartbeatRunId, valid.id))).toEqual([expect.objectContaining({ costCents: 2 })]); + const rows = await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.companyId, f.company.id)); + expect(rows.filter(row => row.costAccountingPending)).toHaveLength(100); + expect(rows.filter(row => poison.some(bad => bad.id === row.id)).every(row => row.costAccountedAt === null)).toBe(true); + expect((await costService(db).summary(f.company.id)).spendCents).toBe(2.1234567); + expect(await accountRunCost(db, valid.id)).toBe(false); + expect(await accountRunCost(db, randomUUID())).toBe(false); + } finally { error.mockRestore(); } + }, 30_000); +}); + +it("fingerprints nested receipts independently of object order while preserving array order and values", () => { + const a = { items: [{ z: 2, a: 1 }, null], at: new Date("2026-01-01T00:00:00Z") }; + expect(receiptFingerprint(a)).toBe(receiptFingerprint({ at: a.at, items: [{ a: 1, z: 2 }, null] })); + expect(receiptFingerprint(a)).not.toBe(receiptFingerprint({ ...a, items: [null, { z: 2, a: 1 }] })); + expect(receiptFingerprint(a)).not.toBe(receiptFingerprint({ ...a, items: [{ z: 3, a: 1 }, null] })); +}); diff --git a/server/src/__tests__/cost-accounting-mutation-targets.test.ts b/server/src/__tests__/cost-accounting-mutation-targets.test.ts new file mode 100644 index 00000000000..e841d5eae6f --- /dev/null +++ b/server/src/__tests__/cost-accounting-mutation-targets.test.ts @@ -0,0 +1,45 @@ +import { randomUUID } from "node:crypto"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { agents, companies, createDb } from "@paperclipai/db"; +import { costService } from "../services/costs.js"; +import { budgetService } from "../services/budgets.js"; +import { accountingIntegrityService } from "../services/accounting-integrity.js"; +import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase } from "./helpers/embedded-postgres.js"; +const support = await getEmbeddedPostgresTestSupport(); +(support.supported ? describe : describe.skip)("accounting mutation sentinels", () => { + let database: Awaited>; + let db: ReturnType; + beforeAll(async () => { database = await startEmbeddedPostgresTestDatabase("accounting-mutation-"); db = createDb(database.connectionString); }, 30_000); + afterAll(async () => { await database?.cleanup(); }); + async function fixture() { + const [company] = await db.insert(companies).values({ name: "Mutation", issuePrefix: `M${randomUUID().slice(0,7)}` }).returning(); + const [agent] = await db.insert(agents).values({ companyId: company.id, name: "Worker", role: "engineer", adapterType: "process", status: "idle" }).returning(); + return { company, agent, receipt: { agentId: agent.id, provider: "fixture", model: "fixture", costCents: "1.0000001", idempotencyKey: randomUUID(), occurredAt: new Date() } }; + } + it("deduplicates a retried receipt", async () => { + const f = await fixture(), service = costService(db); + const first = await service.createEvent(f.company.id, f.receipt); + const second = await service.createEvent(f.company.id, f.receipt); + expect(second.id).toBe(first.id); + expect((await service.summary(f.company.id)).eventCount).toBe(1); + }); + it("isolates company reporting", async () => { + const f = await fixture(), other = await fixture(), service = costService(db); + await service.createEvent(f.company.id, f.receipt); + await service.createEvent(other.company.id, { ...other.receipt, costCents: 1000 }); + expect((await service.summary(f.company.id)).spendCentsExact).toBe("1.0000001"); + }); + it("conserves agent projections", async () => { + const f = await fixture(); + await costService(db).createEvent(f.company.id, f.receipt); + await costService(db).createEvent(f.company.id, { ...f.receipt, idempotencyKey: "second" }); + expect((await accountingIntegrityService(db).inspect(f.company.id)).findings).toEqual([]); + }); + it("stops at the exact budget boundary", async () => { + const f = await fixture(); + const budgets = budgetService(db); + await budgets.upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 1 }, "board"); + await costService(db).createEvent(f.company.id, { ...f.receipt, costCents: 1 }); + expect(await budgets.getInvocationBlock(f.company.id, f.agent.id)).not.toBeNull(); + }); +}); diff --git a/server/src/__tests__/cost-accounting-operations-edges.test.ts b/server/src/__tests__/cost-accounting-operations-edges.test.ts new file mode 100644 index 00000000000..156d3092c0d --- /dev/null +++ b/server/src/__tests__/cost-accounting-operations-edges.test.ts @@ -0,0 +1,217 @@ +import { randomUUID } from "node:crypto"; +import { promises as fs } from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +import { and, eq, sql } from "drizzle-orm"; +import { accountingRuntimeBaselines, agents, agentRuntimeState, billingInvoiceLines, budgetPolicies, budgetReservations, companies, costEvents, createDb, heartbeatRuns, projects, runUsageReceipts } from "@paperclipai/db"; +import { accountingIntegrityService } from "../services/accounting-integrity.js"; +import { billingReconciliationService } from "../services/billing-reconciliation.js"; +import { budgetService } from "../services/budgets.js"; +import { reserveRunBudget } from "../services/budget-reservations.js"; +import { costService } from "../services/costs.js"; +import { financeService } from "../services/finance.js"; +import { accountRunCost, reconcileRunCosts } from "../services/run-cost-accounting.js"; +import { createRunUsageRecorder, persistUsageReceipt, replayUsageReceipts, spoolUsageReceipt, usageReceiptSpoolPath, type UsageReceiptEnvelope } from "../services/usage-receipts.js"; +import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase } from "./helpers/embedded-postgres.js"; +const support = await getEmbeddedPostgresTestSupport(); +(support.supported ? describe : describe.skip)("accounting operational edge cases", () => { + let database: Awaited>, db: ReturnType, directory: string; + beforeAll(async () => { database = await startEmbeddedPostgresTestDatabase("accounting-operational-edges-"); db = createDb(database.connectionString); directory = await fs.mkdtemp(path.join(os.tmpdir(), "accounting-edges-")); },30_000); + afterAll(async () => { vi.restoreAllMocks(); await database?.cleanup(); await fs.rm(directory, { recursive: true, force: true }); }); + async function fixture() { + const [company] = await db.insert(companies).values({ name: "Edges", issuePrefix: `E${randomUUID().slice(0,7)}` }).returning(); + const [agent] = await db.insert(agents).values({ companyId: company.id, name: "Worker", role: "engineer", adapterType: "process", status: "idle" }).returning(); + const [project] = await db.insert(projects).values({ companyId: company.id, name: "Work" }).returning(); + const [run] = await db.insert(heartbeatRuns).values({ companyId: company.id, agentId: agent.id, status: "running", usageJson: {} }).returning(); + return { company, agent, project, run }; + } + const usage = { inputTokens: 7, cachedInputTokens: 11, outputTokens: 3 }; + async function event(f: Awaited>, extras: Record = {}) { + return costService(db).createEvent(f.company.id, { agentId: f.agent.id, provider: "fixture", model: "model", costCents: "0.1234567", occurredAt: new Date(), ...extras }); + } + it("initializes an old month once, excludes backdated events, and repairs current-month drift", async () => { + const f = await fixture(); + await db.update(companies).set({ spendMonthUtc: "2000-01-01", spentMonthlyCents: 999 }).where(eq(companies.id,f.company.id)); + await event(f); await event(f, { occurredAt: new Date("2000-01-01") }); await event(f); + const [row] = await db.select().from(companies).where(eq(companies.id,f.company.id)); + expect(row.spentMonthlyCents).toBe(0.2469134); + expect((await accountingIntegrityService(db).inspect(f.company.id)).findings).toEqual([]); + await db.update(agents).set({ spentMonthlyCents: 4 }).where(eq(agents.id,f.agent.id)); + const review = await accountingIntegrityService(db).inspect(f.company.id); + await accountingIntegrityService(db).repair(f.company.id,review.fingerprint,"Repair drift","board"); + await event(f); + expect((await accountingIntegrityService(db).inspect(f.company.id)).findings).toEqual([]); + }); + it("keeps fractional finance credits and net totals exact beyond number precision", async () => { + const f = await fixture(), finance = financeService(db); + const base = { biller: "fixture", eventKind: "inference_charge", occurredAt: new Date() }; + await finance.createEvent(f.company.id,{ ...base, amountCents: "9007199254740992.0000001" }); + await finance.createEvent(f.company.id,{ ...base, direction: "credit", amountCents: "9007199254740992" }); + expect((await finance.summary(f.company.id)).netCentsExact).toBe("0.0000001"); + expect((await finance.byBiller(f.company.id))[0].netCentsExact).toBe("0.0000001"); + expect((await finance.byKind(f.company.id))[0].netCentsExact).toBe("0.0000001"); + }); + it("checks agent and project capacity, preserves reservations through policy edits, and releases proven bootstrap failures", async () => { + const f = await fixture(), budgets = budgetService(db); + await budgets.upsertPolicy(f.company.id,{ scopeType:"agent",scopeId:f.agent.id,amount:20,reservationCents:"3" },"board"); + await budgets.upsertPolicy(f.company.id,{ scopeType:"project",scopeId:f.project.id,windowKind:"lifetime",amount:10,reservationCents:"6" },"board"); + await budgets.upsertPolicy(f.company.id,{ scopeType:"company",scopeId:f.company.id,amount:20,reservationCents:"1" },"board"); + const reserved = await reserveRunBudget(db,f.company.id,f.run.id,f.project.id); + expect(reserved.amountCents).toBe("6.0000000"); + await budgets.upsertPolicy(f.company.id,{ scopeType:"project",scopeId:f.project.id,windowKind:"lifetime",amount:10,reservationCents:"0" },"board"); + await budgets.upsertPolicy(f.company.id,{ scopeType:"agent",scopeId:f.agent.id,amount:20,reservationCents:"0" },"board"); + expect((await db.select().from(budgetReservations).where(eq(budgetReservations.runId,f.run.id)))[0].amountCents).toBe("6.0000000"); + await db.update(heartbeatRuns).set({ status:"failed",resultJson:{executionRecovery:{providerWorkStarted:false}} }).where(eq(heartbeatRuns.id,f.run.id)); + await accountRunCost(db,f.run.id); + expect((await db.select().from(budgetReservations).where(eq(budgetReservations.runId,f.run.id)))[0].state).toBe("released"); + expect(await db.select().from(costEvents).where(eq(costEvents.companyId,f.company.id))).toEqual([]); + }); + it("reports over-limit advisory budgets without blocking, and tolerates legacy active zero limits", async () => { + const f = await fixture(), budgets = budgetService(db); + const policy = await budgets.upsertPolicy(f.company.id, { scopeType: "company", scopeId: f.company.id, amount: 1, hardStopEnabled: false }, "board"); + await event(f, { costCents: "2.0000001" }); + expect(await budgets.getInvocationBlock(f.company.id, f.agent.id)).toBeNull(); + expect((await budgets.overview(f.company.id)).policies[0]).toMatchObject({ status: "hard_stop", observedAmountExact: "2.0000001", paused: false }); + // Older databases can contain an active zero policy; it must never become + // an accidental hard stop during an upgrade or a projection inspection. + await db.update(budgetPolicies).set({ amount: 0, isActive: true }).where(eq(budgetPolicies.id, policy.policyId)); + expect((await budgets.overview(f.company.id)).policies[0]).toMatchObject({ status: "ok", amount: 0 }); + expect(await budgets.getInvocationBlock(f.company.id, f.agent.id)).toBeNull(); + }); + it("recovers late bootstrap proof without replacing the stop owner's terminal metadata", async () => { + const f = await fixture(); + await reserveRunBudget(db, f.company.id, f.run.id, null); + const stopped = { executionCancellation: { state: "acknowledged" } }; + await db.update(heartbeatRuns).set({ status: "cancelled", resultJson: stopped, + usageJson: { accountingReceiptReady: false, accountingProviderWorkStarted: false }, + }).where(eq(heartbeatRuns.id, f.run.id)); + const integrity = accountingIntegrityService(db); + expect((await integrity.health(f.company.id)).items[0].state).toBe("retryable"); + expect((await integrity.inspect(f.company.id)).findings).toEqual([]); + await reconcileRunCosts(db); + const [run] = await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, f.run.id)); + expect(run).toMatchObject({ status: "cancelled", resultJson: stopped, costAccountingPending: false }); + expect(run.costAccountedAt).not.toBeNull(); + expect((await db.select().from(budgetReservations).where(eq(budgetReservations.runId, f.run.id)))[0].state).toBe("released"); + expect(await db.select().from(costEvents).where(eq(costEvents.heartbeatRunId, f.run.id))).toEqual([]); + }); + it("rejects absent, foreign, finished and blocked dispatch without changing the ledger", async () => { + const f = await fixture(), other = await fixture(); + await expect(reserveRunBudget(db,f.company.id,randomUUID(),null)).rejects.toThrow("not found"); + await expect(reserveRunBudget(db,other.company.id,f.run.id,null)).rejects.toThrow("not found"); + await expect(reserveRunBudget(db,f.company.id,f.run.id,other.project.id)).rejects.toThrow("not found"); + await db.update(heartbeatRuns).set({status:"failed"}).where(eq(heartbeatRuns.id,f.run.id)); + await expect(reserveRunBudget(db,f.company.id,f.run.id,null)).rejects.toThrow("no longer"); + await db.update(heartbeatRuns).set({status:"running",costAccountedAt:new Date()}).where(eq(heartbeatRuns.id,f.run.id)); + await expect(reserveRunBudget(db,f.company.id,f.run.id,null)).rejects.toThrow("no longer"); + await db.update(heartbeatRuns).set({costAccountedAt:null}).where(eq(heartbeatRuns.id,f.run.id)); + await budgetService(db).upsertPolicy(f.company.id,{scopeType:"agent",scopeId:f.agent.id,amount:1},"board"); + await event(f,{costCents:1}); + await expect(reserveRunBudget(db,f.company.id,f.run.id,null)).rejects.toThrow("paused"); + await expect(budgetService(db).upsertPolicy(f.company.id,{scopeType:"agent",scopeId:f.agent.id,amount:1,reservationCents:-1},"board")).rejects.toThrow("negative"); + }); + it("distinguishes legacy totals, missing acknowledgements, missing receipts, and corrupted receipt amounts", async () => { + const f = await fixture(); const integrity = accountingIntegrityService(db); + await db.insert(agentRuntimeState).values({companyId:f.company.id,agentId:f.agent.id,adapterType:"process",totalCostCents:4}); + expect((await integrity.inspect(f.company.id)).findings[0].kind).toBe("legacy_runtime"); + await db.update(heartbeatRuns).set({status:"failed",costAccountingPending:true,usageJson:{...usage,costUsdExact:"0.01",accountingReceiptReady:true}}).where(eq(heartbeatRuns.id,f.run.id)); + expect((await integrity.inspect(f.company.id)).findings.some(f => f.kind === "missing_acknowledgement")).toBe(true); + expect(await integrity.retry(f.company.id,f.run.id,"board")).toEqual({accounted:true}); + await db.update(heartbeatRuns).set({usageJson:{...usage,costUsdExact:"0.02"}}).where(eq(heartbeatRuns.id,f.run.id)); + expect((await integrity.inspect(f.company.id)).findings.map(f=>f.kind)).toContain("receipt_mismatch"); + await db.update(heartbeatRuns).set({usageJson:{...usage,costUsdExact:"invalid"}}).where(eq(heartbeatRuns.id,f.run.id)); + expect((await integrity.inspect(f.company.id)).findings.find(f=>f.kind==="receipt_mismatch")?.expected.cents).toBe("invalid receipt"); + await db.delete(costEvents).where(eq(costEvents.heartbeatRunId,f.run.id)); + expect((await integrity.inspect(f.company.id)).findings.map(f=>f.kind)).toContain("missing_receipt"); + await expect(integrity.repair(f.company.id,"a".repeat(64)," ","board")).rejects.toThrow("reason"); + await expect(integrity.retry(f.company.id,randomUUID(),"board")).rejects.toThrow("not found"); + }); + it("records a failed operator retry and keeps it actionable", async () => { + const f = await fixture(); + await db.update(heartbeatRuns).set({status:"failed",costAccountingPending:true,usageJson:{inputTokens:0.1,accountingReceiptReady:true}}).where(eq(heartbeatRuns.id,f.run.id)); + const integrity = accountingIntegrityService(db); + await expect(integrity.retry(f.company.id,f.run.id,"board")).rejects.toThrow("Invalid cost"); + const health = await integrity.health(f.company.id); + expect(health.items[0]).toMatchObject({state:"retryable",attempts:1,lastError:"Invalid cost receipt"}); + expect(health.items[0].lastAttemptAt).not.toBeNull(); + }); + it("matches provider request IDs and run/model pairs, leaves ambiguous and non-inference lines unresolved", async () => { + const f = await fixture(), service = billingReconciliationService(db); + await event(f,{heartbeatRunId:f.run.id,providerRequestId:"request"}); await event(f,{heartbeatRunId:f.run.id,model:"second"}); + const line = {amountCents:"1",occurredAt:new Date().toISOString()}; + const invoice = await service.importInvoice(f.company.id,{biller:"fixture",externalId:"requests",currency:"USD",lines:[ + {...line,externalId:"provider",providerRequestId:"request"}, {...line,externalId:"model",runId:f.run.id,model:"second"}, + {...line,externalId:"ambiguous",runId:f.run.id}, {...line,externalId:"unknown",providerRequestId:"absent"}, + {...line,externalId:"unlinked"}, {...line,externalId:"fee",kind:"fee"}, {...line,externalId:"credit",kind:"credit"}, + ]},"board"); + expect((await service.list(f.company.id))[0].id).toBe(invoice.id); + const report = await service.reconcile(f.company.id,invoice.id); + expect(Object.fromEntries(report.lines.map(l=>[l.externalId,l.status]))).toEqual({provider:"difference",model:"difference",ambiguous:"ambiguous",unknown:"unmatched",unlinked:"unmatched",fee:"non_inference",credit:"non_inference"}); + await expect(service.importInvoice(f.company.id,{biller:"fixture",externalId:"missing-run",currency:"USD",lines:[{...line,externalId:"run",runId:randomUUID()}]},"board")).rejects.toThrow("run not found"); + }); + it("rejects contradictory invoice evidence and reused correction keys, and preserves legacy originals", async () => { + const f = await fixture(), service = billingReconciliationService(db); const charge = await event(f); + await db.update(costEvents).set({reportedCostCents:null}).where(eq(costEvents.id,charge.id)); + const input = {idempotencyKey:"correction",expectedCents:"0.1234567",correctedCents:"1",reason:"Reviewed",pricing:{source:"operator" as const}}; + await expect(service.adjust(f.company.id,charge.id,{...input,invoiceLineId:randomUUID()},"board")).rejects.toThrow("not found"); + const invoice = await service.importInvoice(f.company.id,{biller:"fixture",externalId:"bad-evidence",currency:"USD",lines:[{externalId:"line",amountCents:"2",occurredAt:new Date().toISOString(),costEventId:charge.id}]},"board"); + const [line] = (await service.reconcile(f.company.id,invoice.id)).lines; + await expect(service.adjust(f.company.id,charge.id,{...input,invoiceLineId:line.id},"board")).rejects.toThrow("does not support"); + const noMatch = await service.importInvoice(f.company.id,{biller:"fixture",externalId:"unverified",currency:"USD",lines:[{externalId:"line",amountCents:"1",occurredAt:new Date().toISOString()}]},"board"); + await expect(service.adjust(f.company.id,charge.id,{...input,invoiceLineId:(await service.reconcile(f.company.id,noMatch.id)).lines[0].id},"board")).rejects.toThrow("unverified"); + await service.adjust(f.company.id,charge.id,input,"board"); + await expect(service.adjust(f.company.id,charge.id,{...input,correctedCents:"2"},"board")).rejects.toThrow("different contents"); + expect((await db.select().from(costEvents).where(eq(costEvents.id,charge.id)))[0].reportedCostCents).toBe("0.1234567"); + }); + it("aggregates attempts exactly, rejects incomplete attempts, and never lets a late old attempt replace the current attempt", async () => { + const f = await fixture(), spool = path.join(directory,randomUUID()); + const recorder = await createRunUsageRecorder(db,{companyId:f.company.id,runId:f.run.id,adapterType:"process"},spool); + const first=randomUUID(), second=randomUUID(); + await recorder.capture({attemptId:first,usage,costUsdExact:"0.010000001",complete:true,billingType:"api"}); + await recorder.capture({attemptId:second,usage,costUsd:0.02,complete:false,billingType:"api"}); + await recorder.capture({attemptId:first,usage,costUsdExact:"0.010000001",complete:true,billingType:"api"}); + const result = await recorder.complete({exitCode:0,signal:null,timedOut:false,usage,costUsd:0.03,billingType:"api"}); + expect(result).toMatchObject({costUsdExact:"0.040000001",complete:true,usage:{inputTokens:14,cachedInputTokens:22,outputTokens:6}}); + await recorder.capture({attemptId:randomUUID(),usage,complete:false,billingType:"subscription"}); + const uncertain = await recorder.complete({exitCode:1,signal:null,timedOut:false,usage}); + expect(uncertain).toMatchObject({costUsd:null,costUsdExact:null,billingType:"unknown"}); + const partial = await recorder.complete({exitCode:1,signal:null,timedOut:false}); + expect(partial.complete).toBe(false); + expect(partial.usage?.inputTokens).toBe(21); + }); + it("refuses invalid checkpoints and does not certify a run after capture failure", async () => { + const f = await fixture(); + const recorder = await createRunUsageRecorder(db,{companyId:f.company.id,runId:f.run.id,adapterType:"process"},path.join(directory,randomUUID())); + await expect(recorder.capture({complete:true,costUsdExact:"NaN"})).rejects.toThrow(); + const result = await recorder.complete({exitCode:0,signal:null,timedOut:false,usage,costUsd:0.01}); + expect(result.complete).toBe(false); + await expect(createRunUsageRecorder(db,{companyId:f.company.id,runId:randomUUID(),adapterType:"process"},path.join(directory,randomUUID()))).rejects.toThrow("cannot accept"); + }); + it("preserves disk errors, rejects missing runs, and handles invalid spool entries without deleting evidence", async () => { + const f=await fixture(), spool=path.join(directory,randomUUID()); + const envelope:UsageReceiptEnvelope={schema:"paperclip/accounting-receipt/v1",id:randomUUID(),companyId:f.company.id,runId:randomUUID(),sourceId:randomUUID(),sequence:1,receivedAt:new Date().toISOString(),adapterType:"process",receipt:{complete:true,costUsd:1}}; + await expect(persistUsageReceipt(db,envelope)).rejects.toThrow("not found"); + const rename=vi.spyOn(fs,"rename").mockRejectedValueOnce(new Error("Disk rename unavailable")); + try { await expect(spoolUsageReceipt(envelope,spool)).rejects.toThrow("Disk rename"); } finally { rename.mockRestore(); } + expect(await fs.readdir(spool)).toEqual([]); + await fs.writeFile(path.join(spool,"large.json"),"x".repeat(1024*1024+1)); + expect((await replayUsageReceipts(db,spool)).failed).toBe(1); + const read=vi.spyOn(fs,"readdir").mockRejectedValueOnce(Object.assign(new Error("denied"),{code:"EACCES"})); + try { await expect(replayUsageReceipts(db,spool)).rejects.toThrow("denied"); } finally { read.mockRestore(); } + }); + it("uses the isolated instance spool and retains evidence when retry rotation itself fails", async () => { + const f = await fixture(); + const envelope: UsageReceiptEnvelope = { schema: "paperclip/accounting-receipt/v1", id: randomUUID(), companyId: f.company.id, runId: f.run.id, + sourceId: randomUUID(), sequence: 1, receivedAt: new Date().toISOString(), adapterType: "process", receipt: { complete: true, costUsd: 0 } }; + const file = await spoolUsageReceipt(envelope); + expect(path.dirname(file)).toBe(usageReceiptSpoolPath()); + expect((await replayUsageReceipts(db)).replayed).toBe(1); + const spool = path.join(directory, randomUUID()); await fs.mkdir(spool); + const bad = path.join(spool, "bad.json"); await fs.writeFile(bad, "invalid"); + const rename = vi.spyOn(fs, "rename").mockRejectedValueOnce(new Error("read-only directory")); + try { expect(await replayUsageReceipts(db, spool)).toEqual({ replayed: 0, failed: 1 }); } finally { rename.mockRestore(); } + expect(await fs.readFile(bad, "utf8")).toBe("invalid"); + }); + +}); diff --git a/server/src/__tests__/cost-accounting-operators.test.ts b/server/src/__tests__/cost-accounting-operators.test.ts new file mode 100644 index 00000000000..b3c32714706 --- /dev/null +++ b/server/src/__tests__/cost-accounting-operators.test.ts @@ -0,0 +1,181 @@ +import { randomUUID } from "node:crypto"; +import { promises as fs } from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +import { and, eq, sql } from "drizzle-orm"; +import { agents, agentRuntimeState, budgetReservations, companies, costEvents, costAdjustments, createDb, heartbeatRuns, runUsageReceipts } from "@paperclipai/db"; +import { accountingIntegrityService } from "../services/accounting-integrity.js"; +import { billingReconciliationService } from "../services/billing-reconciliation.js"; +import { budgetService } from "../services/budgets.js"; +import { reserveRunBudget } from "../services/budget-reservations.js"; +import { costService } from "../services/costs.js"; +import { accountRunCost } from "../services/run-cost-accounting.js"; +import { createRunUsageRecorder, persistUsageReceipt, replayUsageReceipts, spoolUsageReceipt, type UsageReceiptEnvelope } from "../services/usage-receipts.js"; +import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase } from "./helpers/embedded-postgres.js"; + +const support = await getEmbeddedPostgresTestSupport(); +(support.supported ? describe : describe.skip)("accounting operations and durable receipts (PostgreSQL)", () => { + let database: Awaited>; + let db: ReturnType; + let directory: string; + beforeAll(async () => { database = await startEmbeddedPostgresTestDatabase("paperclip-accounting-operators-"); db = createDb(database.connectionString); directory = await fs.mkdtemp(path.join(os.tmpdir(), "accounting-spool-test-")); }, 30_000); + afterAll(async () => { await database?.cleanup(); await fs.rm(directory, { recursive: true, force: true }); }); + async function fixture() { + const [company] = await db.insert(companies).values({ name: "Accounting", issuePrefix: `A${randomUUID().slice(0,7)}` }).returning(); + const [agent] = await db.insert(agents).values({ companyId: company.id, name: "Worker", role: "engineer", adapterType: "process", status: "idle" }).returning(); + return { company, agent }; + } + async function runFor(f: Awaited>, status = "running") { + return (await db.insert(heartbeatRuns).values({ companyId: f.company.id, agentId: f.agent.id, invocationSource: "on_demand", status }).returning())[0]; + } + async function receipt(f: Awaited>, costCents: number | string = "1.1234567") { + return costService(db).createEvent(f.company.id, { agentId: f.agent.id, provider: "test", model: "test-model", costCents, occurredAt: new Date(), idempotencyKey: randomUUID() }); + } + + it("keeps the last nanocent exact in storage, reports, projections and admission", async () => { + const f = await fixture(); + await budgetService(db).upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 2_000_000_000 }, "board"); + await receipt(f, "1999999999.9999999"); + expect(await budgetService(db).getInvocationBlock(f.company.id, f.agent.id)).toBeNull(); + expect((await costService(db).summary(f.company.id)).spendCentsExact).toBe("1999999999.9999999"); + await receipt(f, "0.0000001"); + expect(await budgetService(db).getInvocationBlock(f.company.id, f.agent.id)).not.toBeNull(); + const huge = await fixture(); + await receipt(huge, "99999999999999999.0000001"); + expect((await accountingIntegrityService(db).inspect(huge.company.id)).findings).toEqual([]); + }); + + it("admits only affordable concurrent reservations and settles atomically with the receipt", async () => { + const f = await fixture(); const runs = await Promise.all(Array.from({ length: 4 }, () => runFor(f))); + await budgetService(db).upsertPolicy(f.company.id, { scopeType: "company", scopeId: f.company.id, amount: 10, reservationCents: "6" }, "board"); + const results = await Promise.allSettled(runs.map(run => reserveRunBudget(db, f.company.id, run.id, null))); + expect(results.filter(r => r.status === "fulfilled")).toHaveLength(1); + const index = results.findIndex(r => r.status === "fulfilled"); const run = runs[index]; + expect((await accountingIntegrityService(db).health(f.company.id)).heldReservationCents).toBe("6.0000000"); + await expect(reserveRunBudget(db, f.company.id, run.id, null)).rejects.toThrow("already reserved"); + // Reservations never disappear merely because wall-clock time passed. + await db.update(budgetReservations).set({ createdAt: new Date("2000-01-01") }).where(eq(budgetReservations.runId, run.id)); + const denied = runs[(index + 1) % runs.length]; + await expect(reserveRunBudget(db, f.company.id, denied.id, null)).rejects.toThrow("reserved"); + await db.update(heartbeatRuns).set({ status: "succeeded", finishedAt: new Date(), usageJson: { costUsdExact: "0.04", billingType: "metered_api", accountingReceiptReady: true } }).where(eq(heartbeatRuns.id, run.id)); + expect(await accountRunCost(db, run.id)).toBe(true); + expect((await accountingIntegrityService(db).health(f.company.id)).heldReservationCents).toBe("0.0000000"); + await reserveRunBudget(db, f.company.id, denied.id, null); + expect((await accountingIntegrityService(db).inspect(f.company.id)).findings).toEqual([]); + }); + + it("independently detects and repairs corrupted projections with a stale-review guard and audit", async () => { + const f = await fixture(); const run = await runFor(f, "succeeded"); + await db.update(heartbeatRuns).set({ costAccountingPending: true, usageJson: { accountingReceiptReady: true, costUsdExact: "0.012345678", inputTokens: 7, cachedInputTokens: 3, outputTokens: 11 } }).where(eq(heartbeatRuns.id, run.id)); + await accountRunCost(db, run.id); + const service = accountingIntegrityService(db); + expect((await service.inspect(f.company.id)).findings).toEqual([]); + await db.update(companies).set({ spentMonthlyCents: 999 }).where(eq(companies.id, f.company.id)); + await db.update(agents).set({ spentMonthlyCents: 888 }).where(eq(agents.id, f.agent.id)); + await db.update(agentRuntimeState).set({ totalCostCents: 777, totalInputTokens: 600 }).where(eq(agentRuntimeState.agentId, f.agent.id)); + const review = await service.inspect(f.company.id); + expect(review.findings.map(f => f.kind)).toEqual(["agent_projection", "company_projection", "runtime_projection"]); + await db.update(companies).set({ spentMonthlyCents: 998 }).where(eq(companies.id, f.company.id)); + await expect(service.repair(f.company.id, review.fingerprint, "Fix drift", "board")).rejects.toThrow("changed since inspection"); + const fresh = await service.inspect(f.company.id); + expect((await service.repair(f.company.id, fresh.fingerprint, "Fix drift", "board")).findings).toEqual([]); + const audit = await db.execute(sql`select details from activity_log where company_id = ${f.company.id} and action = 'accounting.projections_repaired'`); + expect(audit).toHaveLength(1); + expect(await accountRunCost(db, run.id)).toBe(false); + expect((await service.inspect(f.company.id)).findings).toEqual([]); + }); + + it("imports invoices idempotently and appends reviewed corrections without losing the provider receipt", async () => { + const f = await fixture(); + const originalPricing = { source: "rate_card" as const, version: "2026-09-01", inputCentsPerMillion: "12.3456789" }; + const event = await costService(db).createEvent(f.company.id, { agentId: f.agent.id, provider: "test", model: "test-model", + costCents: "1.1234567", occurredAt: new Date(), pricingProvenance: originalPricing }); + const service = billingReconciliationService(db); + const invoice = { biller: "test", externalId: "invoice-1", currency: "usd", lines: [{ externalId: "request-1", amountCents: "2.0000001", occurredAt: new Date().toISOString(), costEventId: event.id }] }; + const imported = await service.importInvoice(f.company.id, invoice, "board"); + expect((await service.importInvoice(f.company.id, invoice, "board")).id).toBe(imported.id); + await expect(service.importInvoice(f.company.id, { ...invoice, currency: "EUR" }, "board")).rejects.toThrow("different contents"); + const review = await service.reconcile(f.company.id, imported.id); + expect(review.lines[0]).toMatchObject({ status: "difference", recordedCents: "1.1234567", differenceCents: "0.8765434" }); + const input = { idempotencyKey: "fix-1", expectedCents: "1.1234567", correctedCents: "2.0000001", invoiceLineId: review.lines[0].id, reason: "Provider invoice", pricing: { source: "provider_invoice" as const, evidence: "invoice-1" } }; + const corrected = await service.adjust(f.company.id, event.id, input, "board"); + expect(corrected.previousPricing).toEqual(originalPricing); + expect(corrected.pricing).toEqual(input.pricing); + expect((await service.adjust(f.company.id, event.id, input, "board")).id).toBe(corrected.id); + expect((await service.reconcile(f.company.id, imported.id)).lines[0].status).toBe("matched"); + const [row] = await db.select().from(costEvents).where(eq(costEvents.id, event.id)); + expect(row.reportedCostCents).toBe("1.1234567"); expect(row.receiptHash).toBe(event.receiptHash); + expect((await service.adjustments(f.company.id, event.id))).toHaveLength(1); + expect((await accountingIntegrityService(db).inspect(f.company.id)).findings).toEqual([]); + await expect(service.adjust(f.company.id, event.id, { ...input, idempotencyKey: "stale" }, "board")).rejects.toThrow("changed since review"); + const second = await service.adjust(f.company.id, event.id, { ...input, invoiceLineId: undefined, idempotencyKey: "fix-2", + expectedCents: "2.0000001", correctedCents: "3", pricing: { source: "operator" }, reason: "Reviewed updated evidence" }, "board"); + expect(second.previousPricing).toEqual(input.pricing); + expect((await service.adjustments(f.company.id, event.id))).toHaveLength(2); + }); + + it("refuses cross-company references, guesses, duplicate invoice matches, and currency conversion", async () => { + const f = await fixture(), foreign = await fixture(); const event = await receipt(f); + const service = billingReconciliationService(db); + const base = { biller: "test", externalId: "invoice", currency: "USD", lines: [{ externalId: "line", amountCents: "1", occurredAt: new Date().toISOString(), costEventId: event.id }] }; + await expect(service.importInvoice(foreign.company.id, base, "board")).rejects.toThrow("not found"); + await expect(service.adjust(foreign.company.id, event.id, { idempotencyKey: "bad", expectedCents: 0, correctedCents: 1, reason: "test", pricing: { source: "operator" } }, "board")).rejects.toThrow("not found"); + const duplicate = await service.importInvoice(f.company.id, { ...base, lines: [...base.lines, { ...base.lines[0], externalId: "second" }] }, "board"); + expect((await service.reconcile(f.company.id, duplicate.id)).lines.map(l => l.status)).toEqual(["ambiguous", "ambiguous"]); + const eur = await service.importInvoice(f.company.id, { ...base, externalId: "eur", currency: "EUR" }, "board"); + expect((await service.reconcile(f.company.id, eur.id)).lines[0].status).toBe("unsupported_currency"); + await expect(service.reconcile(foreign.company.id, eur.id)).rejects.toThrow("not found"); + expect(await db.select().from(costAdjustments).where(eq(costAdjustments.companyId, f.company.id))).toEqual([]); + }); + + it("prices unknown usage from an invoice and updates runtime, admission and independent verification", async () => { + const f = await fixture(); const run = await runFor(f, "failed"); + await budgetService(db).upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 10 }, "board"); + await db.update(heartbeatRuns).set({ costAccountingPending: true, usageJson: { accountingReceiptReady: true, inputTokens: 9, provider: "test" } }).where(eq(heartbeatRuns.id, run.id)); + await accountRunCost(db, run.id); + expect((await accountingIntegrityService(db).health(f.company.id)).unpricedEventCount).toBe(1); + expect(await budgetService(db).getInvocationBlock(f.company.id, f.agent.id)).not.toBeNull(); + const [event] = await db.select().from(costEvents).where(eq(costEvents.heartbeatRunId, run.id)); + await billingReconciliationService(db).adjust(f.company.id, event.id, { idempotencyKey: "pricing", expectedCents: 0, correctedCents: "1.0000001", reason: "Verified provider invoice", pricing: { source: "provider_invoice", evidence: "billing-export" } }, "board"); + expect(await budgetService(db).getInvocationBlock(f.company.id, f.agent.id)).toBeNull(); + expect((await accountingIntegrityService(db).health(f.company.id)).unpricedEventCount).toBe(0); + expect((await accountingIntegrityService(db).inspect(f.company.id)).findings).toEqual([]); + }); + + it("spools a receipt during database failure, replays once, fences old sources, and preserves partial uncertainty", async () => { + const f = await fixture(); const run = await runFor(f); const spool = path.join(directory, randomUUID()); + const recorder = await createRunUsageRecorder(db, { companyId: f.company.id, runId: run.id, adapterType: "process" }, spool); + const transaction = vi.spyOn(db, "transaction").mockRejectedValueOnce(new Error("Database unavailable")); + await recorder.capture({ costUsdExact: "0.010000001", usage: { inputTokens: 7, outputTokens: 2 }, complete: false }); + transaction.mockRestore(); + expect((await fs.readdir(spool)).filter(n => n.endsWith(".json"))).toHaveLength(1); + expect(await replayUsageReceipts(db, spool)).toEqual({ replayed: 1, failed: 0 }); + expect(await replayUsageReceipts(db, spool)).toEqual({ replayed: 0, failed: 0 }); + await db.update(heartbeatRuns).set({ status: "failed" }).where(eq(heartbeatRuns.id, run.id)); + expect(await accountRunCost(db, run.id)).toBe(false); + expect((await accountingIntegrityService(db).health(f.company.id)).items[0].state).toBe("waiting_for_receipt"); + await recorder.complete({ exitCode: 0, signal: null, timedOut: false, costUsdExact: "0.010000001", usage: { inputTokens: 7, outputTokens: 2 } }); + expect(await accountRunCost(db, run.id)).toBe(true); + const receipts = await db.select().from(runUsageReceipts).where(eq(runUsageReceipts.runId, run.id)); + expect(receipts).toHaveLength(2); + await persistUsageReceipt(db, receipts[0].receiptJson as UsageReceiptEnvelope); + expect((await costService(db).summary(f.company.id)).spendCentsExact).toBe("1.0000001"); + const tampered = { ...(receipts[0].receiptJson as UsageReceiptEnvelope), receipt: { complete: true, costUsd: 123 } }; + await expect(persistUsageReceipt(db, tampered)).rejects.toThrow("Conflicting usage"); + }); + + it("retains invalid spool evidence and bounds recovery work", async () => { + const spool = path.join(directory, randomUUID()); await fs.mkdir(spool); + await fs.writeFile(path.join(spool, "bad.json"), "not json"); + expect(await replayUsageReceipts(db, spool, 1)).toEqual({ replayed: 0, failed: 1 }); + expect((await fs.readdir(spool))[0]).toMatch(/^retry-/); + expect(await replayUsageReceipts(db, path.join(spool, "missing"))).toEqual({ replayed: 0, failed: 0 }); + const f = await fixture(); const run = await runFor(f); + const unknown: UsageReceiptEnvelope = { schema: "paperclip/accounting-receipt/v1", id: randomUUID(), companyId: f.company.id, runId: run.id, + sourceId: randomUUID(), sequence: 1, receivedAt: new Date().toISOString(), adapterType: "process", receipt: { complete: true, costUsd: 10 } }; + await spoolUsageReceipt(unknown, spool); + expect((await replayUsageReceipts(db, spool, 1)).replayed).toBe(1); + expect((await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, run.id)))[0].costAccountingPending).toBe(false); + }); +}); diff --git a/server/src/__tests__/cost-accounting-processes.test.ts b/server/src/__tests__/cost-accounting-processes.test.ts new file mode 100644 index 00000000000..ed30d355153 --- /dev/null +++ b/server/src/__tests__/cost-accounting-processes.test.ts @@ -0,0 +1,110 @@ +import { spawn } from "node:child_process"; +import { randomUUID } from "node:crypto"; +import { mkdtemp, rm } from "node:fs/promises"; +import { createConnection, createServer, type Socket } from "node:net"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { eq } from "drizzle-orm"; +import { agents, companies, costEvents, createDb, heartbeatRuns } from "@paperclipai/db"; +import { costService } from "../services/costs.js"; +import { accountingIntegrityService } from "../services/accounting-integrity.js"; +import { replayUsageReceipts } from "../services/usage-receipts.js"; +import { accountRunCost } from "../services/run-cost-accounting.js"; +import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase } from "./helpers/embedded-postgres.js"; +const support = await getEmbeddedPostgresTestSupport(); +const root = fileURLToPath(new URL("../../..", import.meta.url)); +(support.supported && process.platform !== "win32" ? describe : describe.skip)("accounting across process and connection failures", () => { + let database: Awaited>; + let db: ReturnType; + beforeAll(async () => { database = await startEmbeddedPostgresTestDatabase("accounting-processes-"); db = createDb(database.connectionString); }, 30_000); + afterAll(async () => { await database?.cleanup(); }); + async function fixture() { + const [company] = await db.insert(companies).values({ name: "Process", issuePrefix: `P${randomUUID().slice(0,7)}` }).returning(); + const [agent] = await db.insert(agents).values({ companyId: company.id, name: "Worker", role: "engineer", adapterType: "process", status: "idle" }).returning(); + return { company, agent }; + } + function worker(mode: string, input: unknown) { + return spawn(process.execPath, ["cli/node_modules/tsx/dist/cli.mjs", "server/src/__tests__/fixtures/accounting-writer.ts", mode, JSON.stringify(input)], { + cwd: root, detached: true, stdio: ["ignore", "pipe", "pipe"], + env: { PATH: process.env.PATH, HOME: process.env.HOME, TMPDIR: process.env.TMPDIR, PAPERCLIP_ACCOUNTING_TEST_DATABASE: database.connectionString, PAPERCLIP_TELEMETRY_DISABLED: "1" }, + }); + } + it("deduplicates the same receipt from independent server processes", async () => { + const f = await fixture(); + const receipt = { agentId: f.agent.id, provider: "fixture", model: "fixture", costCents: "0.1234567", idempotencyKey: "shared-key", occurredAt: new Date().toISOString() }; + const children = Array.from({ length: 4 }, () => worker("write", { companyId: f.company.id, receipt })); + try { + await Promise.all(children.map(child => new Promise((resolve, reject) => { + let log = ""; child.stdout.on("data", d => log += d); child.stderr.on("data", d => log += d); + child.once("error", reject); child.once("exit", code => code === 0 && log.includes("WRITES_COMMITTED") ? resolve() : reject(new Error(log))); + }))); + expect((await db.select().from(costEvents).where(eq(costEvents.companyId, f.company.id)))).toHaveLength(1); + expect((await accountingIntegrityService(db).inspect(f.company.id)).findings).toEqual([]); + } finally { for (const child of children) if (child.exitCode === null && child.signalCode === null) process.kill(-child.pid!, "SIGKILL"); } + }, 45_000); + + it("recovers a receipt after SIGKILL before its first database persistence", async () => { + const f = await fixture(); const directory = await mkdtemp(path.join(tmpdir(), "accounting-early-kill-")); + const [run] = await db.insert(heartbeatRuns).values({ companyId: f.company.id, agentId: f.agent.id, status: "running", costAccountingPending: true, usageJson: { accountingReceiptReady: false } }).returning(); + const child = worker("receipt", { companyId: f.company.id, runId: run.id, directory }); + try { + await new Promise((resolve, reject) => { + let log = ""; child.stdout.on("data", data => { log += data; if (log.includes("RECEIPT_DURABLE")) resolve(); }); + child.stderr.on("data", data => log += data); child.once("error", reject); child.once("exit", () => reject(new Error(`Exited before checkpoint: ${log}`))); + }); + const stopped = new Promise(resolve => child.once("exit", resolve)); process.kill(-child.pid!, "SIGKILL"); await stopped; + expect(await db.select().from(costEvents).where(eq(costEvents.companyId, f.company.id))).toEqual([]); + await db.update(heartbeatRuns).set({ status: "failed", finishedAt: new Date() }).where(eq(heartbeatRuns.id, run.id)); + expect(await replayUsageReceipts(db, directory)).toEqual({ replayed: 1, failed: 0 }); + expect(await accountRunCost(db, run.id)).toBe(true); + expect(await accountRunCost(db, run.id)).toBe(false); + expect((await costService(db).summary(f.company.id)).spendCentsExact).toBe("1.2345678"); + expect((await accountingIntegrityService(db).inspect(f.company.id)).findings).toEqual([]); + } finally { + if (child.exitCode === null && child.signalCode === null) process.kill(-child.pid!, "SIGKILL"); + await rm(directory, { recursive: true, force: true }); + } + }, 45_000); + + it("retries safely when PostgreSQL commits but its acknowledgement is lost", async () => { + const f = await fixture(); const target = new URL(database.connectionString); const sockets = new Set(); let dropped = false; + // Parse PostgreSQL server frames, suppress precisely CommandComplete(COMMIT), + // then break TCP. The database has committed while the writer sees failure. + const proxy = createServer(client => { + const upstream = createConnection({ host: target.hostname, port: Number(target.port) }); sockets.add(client); sockets.add(upstream); + client.on("error", () => {}); upstream.on("error", () => client.destroy()); client.pipe(upstream); + let pending = Buffer.alloc(0); + upstream.on("data", chunk => { + pending = Buffer.concat([pending, chunk]); + while (pending.length >= 5) { + const size = pending.readInt32BE(1) + 1; + if (size < 5 || size > 16 * 1024 * 1024) { client.destroy(); upstream.destroy(); return; } + if (pending.length < size) return; + const message = pending.subarray(0, size); pending = pending.subarray(size); + if (!dropped && message[0] === 67 && message.subarray(5).toString() === "COMMIT\0") { + dropped = true; client.destroy(); upstream.destroy(); return; + } + client.write(message); + } + }); + client.on("close", () => { sockets.delete(client); upstream.destroy(); }); + upstream.on("close", () => { sockets.delete(upstream); client.destroy(); }); + }); + await new Promise(resolve => proxy.listen(0, "127.0.0.1", resolve)); + const forwarded = new URL(database.connectionString); forwarded.hostname = "127.0.0.1"; forwarded.port = String((proxy.address() as { port: number }).port); forwarded.searchParams.set("sslmode", "disable"); + const writer = createDb(forwarded.toString()); + const receipt = { agentId: f.agent.id, provider: "fixture", model: "fixture", costCents: "0.9999999", idempotencyKey: "ambiguous-commit", occurredAt: new Date() }; + try { + await expect(costService(writer).createEvent(f.company.id, receipt)).rejects.toThrow(); + expect(dropped).toBe(true); + expect(await db.select().from(costEvents).where(eq(costEvents.companyId, f.company.id))).toHaveLength(1); + await costService(writer).createEvent(f.company.id, receipt); + expect((await costService(db).summary(f.company.id)).spendCentsExact).toBe("0.9999999"); + expect((await accountingIntegrityService(db).inspect(f.company.id)).findings).toEqual([]); + } finally { + await writer.$client.end({ timeout: 1 }); for (const socket of sockets) socket.destroy(); await new Promise(resolve => proxy.close(() => resolve())); + } + }, 30_000); +}); diff --git a/server/src/__tests__/cost-accounting-reliability.test.ts b/server/src/__tests__/cost-accounting-reliability.test.ts new file mode 100644 index 00000000000..e3a1153d422 --- /dev/null +++ b/server/src/__tests__/cost-accounting-reliability.test.ts @@ -0,0 +1,363 @@ +import { randomUUID } from "node:crypto"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +import { and, eq, sql } from "drizzle-orm"; +import { activityLog, agentRuntimeState, agents, approvals, budgetIncidents, budgetPolicies, companies, costEvents, createDb, financeEvents, goals, heartbeatRuns, issues, projects } from "@paperclipai/db"; +import { costService } from "../services/costs.js"; +import { budgetService } from "../services/budgets.js"; +import { accountRunCost, reconcileRunCosts } from "../services/run-cost-accounting.js"; +import { financeService } from "../services/finance.js"; +import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase } from "./helpers/embedded-postgres.js"; +import { hoistModuleGraph } from "./helpers/hoist-module-graph.js"; + +const support = await getEmbeddedPostgresTestSupport(); +const databaseDescribe = support.supported ? describe : describe.skip; + +databaseDescribe("cost accounting reliability (PostgreSQL)", () => { + const services = hoistModuleGraph(() => {}, async () => { + const [agentModule, companyModule] = await Promise.all([ + import("../services/agents.js"), import("../services/companies.js"), + ]); + return { agentService: agentModule.agentService, companyService: companyModule.companyService }; + }); + let db: ReturnType; + let database: Awaited>; + beforeAll(async () => { + database = await startEmbeddedPostgresTestDatabase("paperclip-cost-reliability-"); + db = createDb(database.connectionString); + }, 30_000); + afterAll(async () => { await database?.cleanup(); }); + + async function fixture() { + const [company] = await db.insert(companies).values({ name: "Accounting", issuePrefix: `T${randomUUID().slice(0, 7)}` }).returning(); + const [agent] = await db.insert(agents).values({ companyId: company.id, name: "Worker", role: "engineer", status: "active", adapterType: "codex_local" }).returning(); + const [project] = await db.insert(projects).values({ companyId: company.id, name: "Project", status: "in_progress" }).returning(); + const [issue] = await db.insert(issues).values({ companyId: company.id, projectId: project.id, title: "Work" }).returning(); + const [goal] = await db.insert(goals).values({ companyId: company.id, title: "Goal" }).returning(); + const [run] = await db.insert(heartbeatRuns).values({ companyId: company.id, agentId: agent.id, invocationSource: "on_demand" }).returning(); + const receipt = { agentId: agent.id, provider: "test", model: "test", billingType: "metered_api", costCents: 100, occurredAt: new Date() }; + return { company, agent, project, issue, goal, run, receipt }; + } + + it.each(["issueId", "projectId", "goalId", "heartbeatRunId"] as const)("rejects a foreign-company %s without writing anything", async (field) => { + const a = await fixture(); const b = await fixture(); + const foreignIds = { issueId: b.issue.id, projectId: b.project.id, goalId: b.goal.id, heartbeatRunId: b.run.id }; + await expect(costService(db).createEvent(a.company.id, { ...a.receipt, [field]: foreignIds[field] })).rejects.toThrow(/company/); + expect(await db.select().from(costEvents).where(eq(costEvents.companyId, a.company.id))).toHaveLength(0); + }); + + it("allows admission inside a caller transaction holding a company foreign-key lock", async () => { + const f = await fixture(); + // The recovery service dispatches through another connection while its + // transaction still owns FK KEY SHARE locks on the company. Accounting + // must serialize without requesting the conflicting FOR UPDATE mode. + await db.transaction(async (tx) => { + await tx.insert(issues).values({ companyId: f.company.id, title: "Concurrent recovery" }); + expect(await budgetService(db).getInvocationBlock(f.company.id, f.agent.id)).toBeNull(); + }); + }, 5000); + + it("deduplicates concurrent retries and rejects changed receipt content", async () => { + const f = await fixture(); + const receipt = { ...f.receipt, idempotencyKey: "provider-receipt-1" }; + const events = await Promise.all(Array.from({ length: 8 }, () => costService(db).createEvent(f.company.id, receipt))); + expect(new Set(events.map((event) => event.id)).size).toBe(1); + expect((await costService(db).summary(f.company.id)).spendCents).toBe(100); + await expect(costService(db).createEvent(f.company.id, { ...receipt, costCents: 200 })).rejects.toThrow(/different|conflict/i); + // Keys are scoped to the reporting company. + const other = await fixture(); + await expect(costService(db).createEvent(other.company.id, { ...other.receipt, idempotencyKey: receipt.idempotencyKey })).resolves.toBeTruthy(); + }); + + it("retains sub-cent spend and enforces the sum, not rounded individual receipts", async () => { + const f = await fixture(); const budgets = budgetService(db); + await budgets.upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 1 }, "board"); + for (let i = 0; i < 3; i++) await costService(db).createEvent(f.company.id, { ...f.receipt, costCents: 0.4 }); + expect((await costService(db).summary(f.company.id)).spendCents).toBe(1.2); + expect(await budgets.getInvocationBlock(f.company.id, f.agent.id)).toMatchObject({ scopeType: "agent" }); + }); + + it("conserves project totals when a run touches multiple projects and includes unallocated spend", async () => { + const f = await fixture(); + const [otherProject] = await db.insert(projects).values({ companyId: f.company.id, name: "Other" }).returning(); + const [otherIssue] = await db.insert(issues).values({ companyId: f.company.id, projectId: otherProject.id, title: "Other" }).returning(); + await db.insert(activityLog).values([f.issue, otherIssue].map((issue) => ({ companyId: f.company.id, actorType: "agent", actorId: f.agent.id, action: "issue.updated", entityType: "issue", entityId: issue.id, runId: f.run.id }))); + await costService(db).createEvent(f.company.id, { ...f.receipt, heartbeatRunId: f.run.id, projectId: f.project.id }); + await costService(db).createEvent(f.company.id, { ...f.receipt, costCents: 30 }); + const rows = await costService(db).byProject(f.company.id); + expect(rows.reduce((sum, row) => sum + row.costCents, 0)).toBe(130); + expect(rows.find((row) => row.projectId === f.project.id)?.costCents).toBe(100); + expect(rows.find((row) => row.projectId === null)?.costCents).toBe(30); + }); + + it("does not enforce inactive policies, and checks both monthly and lifetime limits", async () => { + const f = await fixture(); const budgets = budgetService(db); + await costService(db).createEvent(f.company.id, f.receipt); + await budgets.upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 1, isActive: false }, "board"); + expect(await budgets.getInvocationBlock(f.company.id, f.agent.id)).toBeNull(); + await budgets.upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 1000, windowKind: "calendar_month_utc" }, "board"); + await budgets.upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 50, windowKind: "lifetime" }, "board"); + await budgets.upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 2000, windowKind: "calendar_month_utc" }, "board"); + expect(await budgets.getInvocationBlock(f.company.id, f.agent.id)).toMatchObject({ scopeType: "agent" }); + }); + + it("creates one approval under concurrent evaluation and opens a new incident after a raised budget is reached", async () => { + const f = await fixture(); const budgets = budgetService(db); + await budgets.upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 100 }, "board"); + const event = await costService(db).createEvent(f.company.id, f.receipt); + await Promise.all(Array.from({ length: 8 }, () => budgets.evaluateCostEvent(event))); + expect(await db.select().from(approvals).where(eq(approvals.companyId, f.company.id))).toHaveLength(1); + await budgets.upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 150 }, "board"); + await costService(db).createEvent(f.company.id, f.receipt); + expect(await db.select().from(budgetIncidents).where(and(eq(budgetIncidents.companyId, f.company.id), eq(budgetIncidents.thresholdType, "hard"), eq(budgetIncidents.status, "open")))).toHaveLength(1); + expect(await db.select().from(approvals).where(eq(approvals.companyId, f.company.id))).toHaveLength(2); + }); + + it("commits accounting despite cancellation delivery failure and retries delivery safely", async () => { + const f = await fixture(); + await budgetService(db).upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 100 }, "board"); + const cancel = vi.fn().mockRejectedValueOnce(new Error("injected cancellation transport failure")).mockResolvedValue(undefined); + const costs = costService(db, { cancelWorkForScope: cancel }); + const receipt = { ...f.receipt, idempotencyKey: "retry-after-cancel" }; + await expect(costs.createEvent(f.company.id, receipt)).resolves.toBeTruthy(); + await costs.createEvent(f.company.id, receipt); + expect(cancel).toHaveBeenCalledTimes(2); + expect((await costs.summary(f.company.id)).spendCents).toBe(100); + expect(await budgetService(db).getInvocationBlock(f.company.id, f.agent.id)).toMatchObject({ scopeType: "agent" }); + }); + + it("recovers terminal run accounting exactly once across simultaneous live and recovery workers", async () => { + const f = await fixture(); + await db.update(heartbeatRuns).set({ status: "timed_out", finishedAt: new Date(), costAccountingPending: true, + usageJson: { inputTokens: 20, cachedInputTokens: 100, outputTokens: 10, costUsd: 0.004, provider: "test", model: "test", billingType: "metered_api", ledgerScope: { issueId: f.issue.id, projectId: f.project.id } }, + }).where(eq(heartbeatRuns.id, f.run.id)); + await Promise.all([accountRunCost(db, f.run.id), accountRunCost(db, f.run.id), reconcileRunCosts(db)]); + const [totals] = await db.select().from(agentRuntimeState).where(eq(agentRuntimeState.agentId, f.agent.id)); + expect(totals).toMatchObject({ totalInputTokens: 20, totalCachedInputTokens: 100, totalOutputTokens: 10, totalCostCents: 0.4 }); + const events = await db.select().from(costEvents).where(eq(costEvents.heartbeatRunId, f.run.id)); + expect(events).toHaveLength(1); + expect(events[0]).toMatchObject({ costCents: 0.4, projectId: f.project.id }); + const [run] = await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, f.run.id)); + expect(run.costAccountingPending).toBe(false); + expect(run.costAccountedAt).toBeInstanceOf(Date); + // Late terminal metadata may re-arm the pending flag. The durable + // acknowledgement, not the delivery flag, owns exactly-once totals. + await db.update(heartbeatRuns).set({ costAccountingPending: true }).where(eq(heartbeatRuns.id, f.run.id)); + await accountRunCost(db, f.run.id); + expect((await db.select().from(agentRuntimeState).where(eq(agentRuntimeState.agentId, f.agent.id)))[0].totalCostCents).toBe(0.4); + }); + + it("rolls back the ledger and counters on accounting failure, retaining a retryable run", async () => { + const f = await fixture(); const other = await fixture(); + await db.update(heartbeatRuns).set({ status: "failed", costAccountingPending: true, usageJson: { + inputTokens: 10, costUsd: 1, provider: "test", model: "test", ledgerScope: { projectId: other.project.id }, + } }).where(eq(heartbeatRuns.id, f.run.id)); + await expect(accountRunCost(db, f.run.id)).rejects.toThrow(/company/); + expect(await db.select().from(costEvents).where(eq(costEvents.companyId, f.company.id))).toHaveLength(0); + expect(await db.select().from(agentRuntimeState).where(eq(agentRuntimeState.agentId, f.agent.id))).toHaveLength(0); + expect((await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, f.run.id)))[0].costAccountingPending).toBe(true); + }); + + it("rolls back a receipt, counters, and budget incidents if a later runtime write fails", async () => { + const f = await fixture(); + await budgetService(db).upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 50 }, "board"); + await db.update(heartbeatRuns).set({ status: "failed", finishedAt: new Date(), costAccountingPending: true, usageJson: { + inputTokens: 10, costUsd: 1, provider: "test", billingType: "metered_api", model: "test", + } }).where(eq(heartbeatRuns.id, f.run.id)); + await db.execute(sql`create function reject_runtime_accounting() returns trigger language plpgsql as $$ begin raise exception 'injected runtime write failure'; end $$`); + await db.execute(sql`create trigger reject_runtime_accounting before insert on agent_runtime_state for each row execute function reject_runtime_accounting()`); + try { + await expect(accountRunCost(db, f.run.id)).rejects.toThrow(); + expect(await db.select().from(costEvents).where(eq(costEvents.companyId, f.company.id))).toHaveLength(0); + expect(await db.select().from(budgetIncidents).where(eq(budgetIncidents.companyId, f.company.id))).toHaveLength(0); + expect(await db.select().from(approvals).where(eq(approvals.companyId, f.company.id))).toHaveLength(0); + expect((await db.select().from(agents).where(eq(agents.id, f.agent.id)))[0]).toMatchObject({ spentMonthlyCents: 0, status: "active" }); + expect((await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, f.run.id)))[0]).toMatchObject({ costAccountingPending: true, costAccountedAt: null }); + } finally { + await db.execute(sql`drop trigger reject_runtime_accounting on agent_runtime_state`); + await db.execute(sql`drop function reject_runtime_accounting()`); + } + await accountRunCost(db, f.run.id); + expect((await costService(db).summary(f.company.id)).spendCents).toBe(100); + }); + + it("releases an expired monthly budget pause while preserving manual pauses", async () => { + const f = await fixture(); const budgets = budgetService(db); + await budgets.upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 50 }, "board"); + const event = await costService(db).createEvent(f.company.id, f.receipt); + const now = new Date(); + await db.update(costEvents).set({ occurredAt: new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 0)) }).where(eq(costEvents.id, event.id)); + expect(await budgets.getInvocationBlock(f.company.id, f.agent.id)).toBeNull(); + expect((await db.select().from(agents).where(eq(agents.id, f.agent.id)))[0]).toMatchObject({ status: "idle", pauseReason: null }); + await db.update(agents).set({ status: "paused", pauseReason: "manual" }).where(eq(agents.id, f.agent.id)); + await budgets.upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 200 }, "board"); + expect(await budgets.getInvocationBlock(f.company.id, f.agent.id)).toBeNull(); + expect((await db.select().from(agents).where(eq(agents.id, f.agent.id)))[0]).toMatchObject({ status: "paused", pauseReason: "manual" }); + }); + + it("uses live observed spend for incident resolution and synchronizes company budgets", async () => { + const f = await fixture(); const budgets = budgetService(db); + await budgets.upsertPolicy(f.company.id, { scopeType: "company", scopeId: f.company.id, amount: 50 }, "board"); + await costService(db).createEvent(f.company.id, f.receipt); + const [incident] = await db.select().from(budgetIncidents).where(and(eq(budgetIncidents.companyId, f.company.id), eq(budgetIncidents.thresholdType, "hard"))); + await costService(db).createEvent(f.company.id, { ...f.receipt, costCents: 25 }); + await expect(budgets.resolveIncident(f.company.id, incident.id, { action: "raise_budget_and_resume", amount: 125 }, "board")).rejects.toThrow("New budget must exceed current observed spend"); + await budgets.resolveIncident(f.company.id, incident.id, { action: "raise_budget_and_resume", amount: 175 }, "board"); + expect((await db.select().from(companies).where(eq(companies.id, f.company.id)))[0]).toMatchObject({ budgetMonthlyCents: 175, status: "active", pauseReason: null }); + }); + + it("exposes missing prices and blocks strict budgets until explicitly allowed", async () => { + const f = await fixture(); const budgets = budgetService(db); + await budgets.upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 100 }, "board"); + await costService(db).createEvent(f.company.id, { ...f.receipt, inputTokens: 500, costCents: 0, costStatus: "unpriced" }); + expect(await costService(db).summary(f.company.id)).toMatchObject({ spendCents: 0, unpricedEventCount: 1, pricingComplete: false }); + expect(await budgets.getInvocationBlock(f.company.id, f.agent.id)).toMatchObject({ reason: "Agent cannot start work because recorded usage has no reliable price." }); + await budgets.upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 100, unpricedUsagePolicy: "allow" }, "board"); + expect(await budgets.getInvocationBlock(f.company.id, f.agent.id)).toBeNull(); + expect((await budgets.overview(f.company.id)).policies[0]).toMatchObject({ unpricedEventCount: 1, unpricedUsagePolicy: "allow" }); + }); + + it("keeps subscription-included usage outside monetary pricing gaps", async () => { + const f = await fixture(); const budgets = budgetService(db); + await budgets.upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 100 }, "board"); + await costService(db).createEvent(f.company.id, { ...f.receipt, inputTokens: 500, costCents: 0, costStatus: "unpriced", billingType: "subscription_included" }); + expect(await budgets.getInvocationBlock(f.company.id, f.agent.id)).toBeNull(); + expect(await costService(db).summary(f.company.id)).toMatchObject({ pricingComplete: true }); + }); + + it("defaults reports to the UTC month and excludes future events from rolling spend", async () => { + const f = await fixture(); const costs = costService(db); const now = new Date(); + await costs.createEvent(f.company.id, { ...f.receipt, occurredAt: new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - 1, 1)) }); + await costs.createEvent(f.company.id, { ...f.receipt, occurredAt: new Date(now.getTime() + 86_400_000) }); + expect((await costs.summary(f.company.id)).spendCents).toBe(0); + expect((await costs.summary(f.company.id, { allTime: true })).spendCents).toBe(200); + expect(await costs.windowSpend(f.company.id)).toEqual([]); + }); + + it("keeps finance currencies separate and deduplicates invoice-line retries", async () => { + const f = await fixture(); const finance = financeService(db); + const usd = { biller: "vendor", eventKind: "platform_fee", amountCents: 100, currency: "usd", occurredAt: new Date(), idempotencyKey: "invoice:line:1", metadataJson: { a: 1, b: 2 } }; + await Promise.all(Array.from({ length: 6 }, () => finance.createEvent(f.company.id, usd))); + await finance.createEvent(f.company.id, { ...usd, metadataJson: { b: 2, a: 1 } }); + await finance.createEvent(f.company.id, { ...usd, currency: "EUR", idempotencyKey: "invoice:line:2" }); + const summary = await finance.summary(f.company.id); + expect(summary).toMatchObject({ currency: "USD", debitCents: 100, netCents: 100 }); + expect(summary.currencies).toEqual(expect.arrayContaining([expect.objectContaining({ currency: "USD", netCents: 100 }), expect.objectContaining({ currency: "EUR", netCents: 100 })])); + expect(await finance.byBiller(f.company.id)).toHaveLength(2); + expect(await finance.byKind(f.company.id)).toHaveLength(2); + expect(await db.select().from(financeEvents).where(eq(financeEvents.companyId, f.company.id))).toHaveLength(2); + await expect(finance.createEvent(f.company.id, { ...usd, amountCents: 200 })).rejects.toThrow(/different/); + }); + + it("synchronizes budgets through generic agent and company updates", async () => { + const f = await fixture(); + const { agentService, companyService } = services.value; + await agentService(db).update(f.agent.id, { budgetMonthlyCents: 50 }); + await companyService(db).update(f.company.id, { budgetMonthlyCents: 75 }); + await costService(db).createEvent(f.company.id, f.receipt); + const policies = await budgetService(db).listPolicies(f.company.id); + expect(policies).toEqual(expect.arrayContaining([expect.objectContaining({ scopeType: "agent", amount: 50 }), expect.objectContaining({ scopeType: "company", amount: 75 })])); + expect(await budgetService(db).getInvocationBlock(f.company.id, f.agent.id)).toMatchObject({ scopeType: "company" }); + }); + + it("conserves both spend and tokens across complete per-model receipts", async () => { + const f = await fixture(); + await db.update(heartbeatRuns).set({ status: "succeeded", finishedAt: new Date(), costAccountingPending: true, usageJson: { + provider: "anthropic", billingType: "metered_api", model: "mixed", costUsd: 0.007, + inputTokens: 30, outputTokens: 4, cachedInputTokens: 100, + usageByModel: [ + { model: "large", costUsd: 0.005, usage: { inputTokens: 20, outputTokens: 3, cachedInputTokens: 100 } }, + { model: "small", costUsd: 0.002, usage: { inputTokens: 10, outputTokens: 1 } }, + ], + } }).where(eq(heartbeatRuns.id, f.run.id)); + await accountRunCost(db, f.run.id); + const rows = await costService(db).byProvider(f.company.id); + expect(rows).toEqual(expect.arrayContaining([expect.objectContaining({ model: "large", costCents: 0.5, inputTokens: 20 }), expect.objectContaining({ model: "small", costCents: 0.2, inputTokens: 10 })])); + expect((await costService(db).summary(f.company.id)).spendCents).toBe(0.7); + const [totals] = await db.select().from(agentRuntimeState).where(eq(agentRuntimeState.agentId, f.agent.id)); + expect(totals).toMatchObject({ totalInputTokens: 30, totalCachedInputTokens: 100, totalOutputTokens: 4, totalCostCents: 0.7 }); + }); + it("blocks admission while a terminal run awaits accounting, even under an allow-unpriced policy", async () => { + const f = await fixture(); const budgets = budgetService(db); + await budgets.upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 1000, unpricedUsagePolicy: "allow" }, "board"); + await db.update(heartbeatRuns).set({ status: "failed", costAccountingPending: true }).where(eq(heartbeatRuns.id, f.run.id)); + expect(await budgets.getInvocationBlock(f.company.id, f.agent.id)).toMatchObject({ reason: expect.stringContaining("await accounting") }); + expect(await costService(db).summary(f.company.id)).toMatchObject({ pricingComplete: false, pendingRunCount: 1 }); + await accountRunCost(db, f.run.id); + expect(await costService(db).summary(f.company.id)).toMatchObject({ pricingComplete: false, pendingRunCount: 0, unpricedEventCount: 1 }); + expect(await budgets.getInvocationBlock(f.company.id, f.agent.id)).toBeNull(); + }); + + it("waits for a late provider receipt after cancellation instead of acknowledging a zero charge", async () => { + const f = await fixture(); + await db.update(heartbeatRuns).set({ status: "cancelled", costAccountingPending: true, usageJson: { accountingReceiptReady: false } }).where(eq(heartbeatRuns.id, f.run.id)); + expect(await accountRunCost(db, f.run.id)).toBe(false); + expect((await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, f.run.id)))[0]).toMatchObject({ costAccountingPending: true, costAccountedAt: null }); + expect(await db.select().from(costEvents).where(eq(costEvents.heartbeatRunId, f.run.id))).toHaveLength(0); + await db.update(heartbeatRuns).set({ usageJson: { accountingReceiptReady: true, costUsd: 0.25, inputTokens: 10 } }).where(eq(heartbeatRuns.id, f.run.id)); + await Promise.all([accountRunCost(db, f.run.id), accountRunCost(db, f.run.id)]); + expect((await db.select().from(costEvents).where(eq(costEvents.heartbeatRunId, f.run.id)))[0]).toMatchObject({ costCents: 25, inputTokens: 10 }); + expect((await db.select().from(agentRuntimeState).where(eq(agentRuntimeState.agentId, f.agent.id)))[0].totalCostCents).toBe(25); + }); + + it("acknowledges proven pre-provider failures without inventing a charge", async () => { + const f = await fixture(); + await db.update(heartbeatRuns).set({ status: "failed", costAccountingPending: true, + resultJson: { executionRecovery: { providerWorkStarted: false } }, + }).where(eq(heartbeatRuns.id, f.run.id)); + await accountRunCost(db, f.run.id); + expect(await db.select().from(costEvents).where(eq(costEvents.companyId, f.company.id))).toHaveLength(0); + expect((await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, f.run.id)))[0]).toMatchObject({ costAccountingPending: false, costAccountedAt: expect.any(Date) }); + }); + + it("records spend after attribution targets are deleted", async () => { + const f = await fixture(); + await db.update(heartbeatRuns).set({ status: "failed", costAccountingPending: true, usageJson: { + costUsd: 0.01, ledgerScope: { projectId: f.project.id, issueId: f.issue.id }, + } }).where(eq(heartbeatRuns.id, f.run.id)); + await db.delete(issues).where(eq(issues.id, f.issue.id)); + await db.delete(projects).where(eq(projects.id, f.project.id)); + await accountRunCost(db, f.run.id); + expect((await db.select().from(costEvents).where(eq(costEvents.companyId, f.company.id)))[0]).toMatchObject({ costCents: 1, issueId: null, projectId: null }); + }); + + it("preserves dismissed incidents and warning incidents during admission checks", async () => { + const f = await fixture(); const budgets = budgetService(db); + await budgets.upsertPolicy(f.company.id, { scopeType: "agent", scopeId: f.agent.id, amount: 100 }, "board"); + await costService(db).createEvent(f.company.id, { ...f.receipt, costCents: 80 }); + for (let i = 0; i < 3; i++) expect(await budgets.getInvocationBlock(f.company.id, f.agent.id)).toBeNull(); + expect((await budgets.overview(f.company.id)).activeIncidents).toHaveLength(1); + await costService(db).createEvent(f.company.id, { ...f.receipt, costCents: 20 }); + const [incident] = await db.select().from(budgetIncidents).where(and(eq(budgetIncidents.companyId, f.company.id), eq(budgetIncidents.thresholdType, "hard"))); + await budgets.resolveIncident(f.company.id, incident.id, { action: "keep_paused" }, "board"); + for (let i = 0; i < 3; i++) expect(await budgets.getInvocationBlock(f.company.id, f.agent.id)).toBeTruthy(); + expect(await db.select().from(approvals).where(eq(approvals.companyId, f.company.id))).toHaveLength(1); + }); + + it("does not revive terminated agents or archived companies during budget reconciliation", async () => { + const f = await fixture(); const budgets = budgetService(db); + await db.update(agents).set({ status: "terminated", pauseReason: "budget" }).where(eq(agents.id, f.agent.id)); + await db.update(companies).set({ status: "archived", pauseReason: "budget" }).where(eq(companies.id, f.company.id)); + for (const [scopeType, scopeId] of [["agent", f.agent.id], ["company", f.company.id]] as const) { + await budgets.upsertPolicy(f.company.id, { scopeType, scopeId, amount: 1000 }, "board"); + } + await costService(db).createEvent(f.company.id, { ...f.receipt, costCents: 2000 }); + expect((await db.select().from(agents).where(eq(agents.id, f.agent.id)))[0].status).toBe("terminated"); + expect((await db.select().from(companies).where(eq(companies.id, f.company.id)))[0].status).toBe("archived"); + }); + + it.each(["agent", "company", "project"] as const)("limits delayed %s budget cancellation to work that preceded the policy check", async (scopeType) => { + const f = await fixture(); + const { heartbeatService } = await import("../services/heartbeat.js"); + const heartbeat = heartbeatService(db); + const scopeId = scopeType === "agent" ? f.agent.id : scopeType === "company" ? f.company.id : f.project.id; + const cutoff = new Date(); + await db.update(heartbeatRuns).set({ status: "queued", createdAt: new Date(cutoff.getTime() - 1000), contextSnapshot: { projectId: f.project.id } }).where(eq(heartbeatRuns.id, f.run.id)); + const [newRun] = await db.insert(heartbeatRuns).values({ companyId: f.company.id, agentId: f.agent.id, invocationSource: "on_demand", status: "scheduled_retry", + scheduledRetryAt: new Date(cutoff.getTime() + 60000), createdAt: new Date(cutoff.getTime() + 1000), contextSnapshot: { projectId: f.project.id }, + }).returning(); + await heartbeat.cancelBudgetScopeWork({ companyId: f.company.id, scopeType, scopeId, createdBefore: cutoff }); + expect((await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, f.run.id)))[0].status).toBe("cancelled"); + expect((await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, newRun.id)))[0].status).toBe("scheduled_retry"); + }); + +}); diff --git a/server/src/__tests__/cost-accounting-routes.test.ts b/server/src/__tests__/cost-accounting-routes.test.ts new file mode 100644 index 00000000000..8b0899aa48a --- /dev/null +++ b/server/src/__tests__/cost-accounting-routes.test.ts @@ -0,0 +1,50 @@ +import express from "express"; +import request from "supertest"; +import { randomUUID } from "node:crypto"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { agents, companies, createDb } from "@paperclipai/db"; +import { costRoutes } from "../routes/costs.js"; +import { errorHandler } from "../middleware/error-handler.js"; +import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase } from "./helpers/embedded-postgres.js"; +const support = await getEmbeddedPostgresTestSupport(); +(support.supported ? describe : describe.skip)("accounting operator authorization", () => { + let database: Awaited>; + let db: ReturnType; + let companyId: string, foreignId: string, agentId: string; + beforeAll(async () => { + database = await startEmbeddedPostgresTestDatabase("accounting-authz-"); db = createDb(database.connectionString); + const rows = await db.insert(companies).values([{ name: "A", issuePrefix: "AUTH_A" }, { name: "B", issuePrefix: "AUTH_B" }]).returning(); + companyId = rows[0].id; foreignId = rows[1].id; + agentId = (await db.insert(agents).values({ companyId, name: "Worker", role: "engineer", adapterType: "process" }).returning())[0].id; + },30_000); + afterAll(async () => { await database?.cleanup(); }); + function app(actor: Record) { + const app = express(); app.use(express.json()); app.use((req,_res,next) => { req.actor = actor as typeof req.actor; next(); }); + app.use("/api", costRoutes(db)); app.use(errorHandler); return app; + } + const operations = [ + ["get", "health", undefined], ["get", "inspect", undefined], ["get", "invoices", undefined], + ["get", `invoices/${randomUUID()}`, undefined], ["get", `events/${randomUUID()}/adjustments`, undefined], + ["post", "repair", { fingerprint: "a".repeat(64), reason: "test" }], + ["post", "provider-costs/import", {}], ["post", "retry", { runId: randomUUID() }], ["post", "invoices", {}], ["post", `events/${randomUUID()}/adjustments`, {}], + ] as const; + it.each(operations)("denies agents for %s %s before reading or validating operator payloads", async (method, route, body) => { + const response = await request(app({ type: "agent", companyId, agentId }))[method](`/api/companies/${companyId}/accounting/${route}`).send(body); + expect(response.status).toBe(403); + }); + it.each(operations)("denies nonmembers for %s %s", async (method, route, body) => { + const response = await request(app({ type: "board", source: "session", userId: "operator", companyIds: [foreignId] }))[method](`/api/companies/${companyId}/accounting/${route}`).send(body); + expect(response.status).toBe(403); + }); + it("allows operator inspection and rejects viewer mutations", async () => { + const board = { type: "board", source: "session", userId: "operator", companyIds: [companyId], memberships: [{ companyId, status: "active", membershipRole: "viewer" }] }; + const response = await request(app(board)).get(`/api/companies/${companyId}/accounting/inspect`); + expect(response.status).toBe(200); expect(response.body.findings).toEqual([]); + expect((await request(app(board)).post(`/api/companies/${companyId}/accounting/repair`).send({ fingerprint: response.body.fingerprint, reason: "test" })).status).toBe(403); + }); + it("validates correction and repair payloads before mutating", async () => { + const board = app({ type: "board", source: "local_implicit", userId: "operator" }); + expect((await request(board).post(`/api/companies/${companyId}/accounting/repair`).send({ fingerprint: "old", reason: "" })).status).toBe(400); + expect((await request(board).post(`/api/companies/${companyId}/accounting/events/${randomUUID()}/adjustments`).send({ idempotencyKey: "x", expectedCents: "0", correctedCents: "-0.000000001", reason: "Invalid", pricing: { source: "operator" } })).status).toBe(400); + }); +}); diff --git a/server/src/__tests__/costs-service.test.ts b/server/src/__tests__/costs-service.test.ts index a8ea5fa2990..4c4057aa3ac 100644 --- a/server/src/__tests__/costs-service.test.ts +++ b/server/src/__tests__/costs-service.test.ts @@ -221,6 +221,21 @@ describe("cost routes", () => { }); }); + it("requires an explicit all-time request and rejects conflicting ranges", () => { + const { parseCostDateRange } = loadCostParsers(); + expect(parseCostDateRange({})).toBeUndefined(); + expect(parseCostDateRange({ period: "all" })).toEqual({ allTime: true }); + for (const input of [ + { from: "2026-09-02", to: "2026-09-01" }, + { from: ["2026-09-01"] }, + { from: "2026-02-30" }, + { from: "" }, + { from: "09/01/2026" }, + { period: "all", from: "2026-09-01" }, + { period: "unknown" }, + ]) expect(() => parseCostDateRange(input)).toThrow(); + }); + it("returns 400 for an invalid 'from' date string", async () => { const { parseCostDateRange } = loadCostParsers(); expect(() => parseCostDateRange({ from: "not-a-date" })).toThrow(/invalid 'from' date/i); @@ -270,7 +285,7 @@ describe("cost routes", () => { it("returns 400 for invalid finance event list limits", async () => { const { parseCostLimit } = loadCostParsers(); - expect(() => parseCostLimit({ limit: "0" })).toThrow(/invalid 'limit'/i); + for (const limit of ["0", "25garbage", "1.5", ["25"], "501"]) expect(() => parseCostLimit({ limit })).toThrow(/invalid 'limit'/i); }); it("accepts valid finance event list limits", async () => { @@ -352,7 +367,8 @@ describe("cost routes", () => { }); it("allows authorized board users to update an agent budget and budget policy", async () => { - mockAgentService.update.mockResolvedValueOnce({ + mockAgentService.getById.mockResolvedValueOnce({ id: "agent-1", companyId: "company-1", name: "Budget Agent", budgetMonthlyCents: 100, spentMonthlyCents: 0 }); + mockAgentService.getById.mockResolvedValueOnce({ id: "agent-1", companyId: "company-1", name: "Budget Agent", @@ -373,7 +389,9 @@ describe("cost routes", () => { .send({ budgetMonthlyCents: 2500 }); expect(res.status).toBe(200); - expect(mockAgentService.update).toHaveBeenCalledWith("agent-1", { budgetMonthlyCents: 2500 }); + expect(res.body.budgetMonthlyCents).toBe(2500); + expect(mockAgentService.update).not.toHaveBeenCalled(); + expect(mockBudgetService.upsertPolicy).toHaveBeenCalledTimes(1); expect(mockBudgetService.upsertPolicy).toHaveBeenCalledWith( "company-1", { @@ -384,19 +402,8 @@ describe("cost routes", () => { }, "board-user", ); - expect(mockLogActivity).toHaveBeenCalledWith( - expect.anything(), - expect.objectContaining({ - companyId: "company-1", - actorType: "user", - actorId: "board-user", - agentId: null, - action: "agent.budget_updated", - entityType: "agent", - entityId: "agent-1", - details: { budgetMonthlyCents: 2500 }, - }), - ); + // Policy mutation owns the atomic entity update and activity log. + expect(mockLogActivity).not.toHaveBeenCalled(); }); }); @@ -692,6 +699,7 @@ describeEmbeddedPostgres("cost and finance aggregate overflow handling", () => { issueCount: 3, includeDescendants: true, costCents: 600, + costCentsExact: "600.0000000", inputTokens: 60, cachedInputTokens: 6, outputTokens: 12, diff --git a/server/src/__tests__/fixtures/accounting-writer.ts b/server/src/__tests__/fixtures/accounting-writer.ts new file mode 100644 index 00000000000..dc4fc8a80bb --- /dev/null +++ b/server/src/__tests__/fixtures/accounting-writer.ts @@ -0,0 +1,20 @@ +// Dedicated real-process fault fixture. Only invoked with a throwaway test DB. +import { createDb } from "@paperclipai/db"; +import { costService } from "../../services/costs.js"; +import { createRunUsageRecorder } from "../../services/usage-receipts.js"; +const [mode, encoded] = process.argv.slice(2); +const input = JSON.parse(encoded); +if (!process.env.PAPERCLIP_ACCOUNTING_TEST_DATABASE) throw new Error("Explicit test database required"); +const db = createDb(process.env.PAPERCLIP_ACCOUNTING_TEST_DATABASE); +try { + if (mode === "receipt") { + const recorder = await createRunUsageRecorder(db, { companyId: input.companyId, runId: input.runId, adapterType: "process" }, input.directory); + // The receipt is fsynced before persistUsageReceipt enters a transaction. + // Freeze at this boundary, so the parent can SIGKILL us before any DB row. + db.transaction = async () => { process.stdout.write("RECEIPT_DURABLE\n"); return new Promise(() => {}); }; + await recorder.capture({ costUsdExact: "0.012345678", usage: { inputTokens: 7, cachedInputTokens: 11, outputTokens: 3 }, complete: true, billingType: "metered_api" }); + } else { + await Promise.all(Array.from({ length: 4 }, () => costService(db).createEvent(input.companyId, { ...input.receipt, occurredAt: new Date(input.receipt.occurredAt) }))); + process.stdout.write("WRITES_COMMITTED\n"); + } +} finally { await db.$client.end(); } diff --git a/server/src/__tests__/heartbeat-cost-accounting.test.ts b/server/src/__tests__/heartbeat-cost-accounting.test.ts index 32ce3a13283..69c11576197 100644 --- a/server/src/__tests__/heartbeat-cost-accounting.test.ts +++ b/server/src/__tests__/heartbeat-cost-accounting.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; import { + normalizeAdapterRunUsage, resolveCacheAdjustedCostUsd, resolveLedgerCostStatus, } from "../services/heartbeat.js"; @@ -35,6 +36,13 @@ describe("heartbeat cost accounting", () => { })).toBe("unpriced"); }); + it("distinguishes missing receipts, reported zeroes, and included subscription usage", () => { + const noTokens = { inputTokens: 0, cachedInputTokens: 0, outputTokens: 0 }; + expect(resolveLedgerCostStatus({ ...noTokens, costUsd: null })).toBe("unpriced"); + expect(resolveLedgerCostStatus({ ...noTokens, costUsd: 0 })).toBe("reported"); + expect(resolveLedgerCostStatus({ ...noTokens, costUsd: null, billingType: "subscription_included" })).toBe("reported"); + }); + it("marks reported CLI cost as priced", () => { expect(resolveLedgerCostStatus({ costUsd: 1.25, @@ -86,3 +94,17 @@ describe("heartbeat cost accounting", () => { })).toBe(1.5); }); }); + + +describe("adapter usage basis", () => { + const prior = { inputTokens: 100, cachedInputTokens: 500, outputTokens: 20 }; + it.each([undefined, null, "per_run"] as const)("keeps equal consecutive run usage when the basis is %s", (basis) => { + expect(normalizeAdapterRunUsage(prior, prior, basis)).toEqual(prior); + }); + it("subtracts session totals only when explicitly declared", () => { + expect(normalizeAdapterRunUsage({ inputTokens: 110, cachedInputTokens: 700, outputTokens: 25 }, prior, "session_cumulative")) + .toEqual({ inputTokens: 10, cachedInputTokens: 200, outputTokens: 5 }); + expect(normalizeAdapterRunUsage({ inputTokens: 5, cachedInputTokens: 10, outputTokens: 2 }, prior, "session_cumulative")) + .toEqual({ inputTokens: 5, cachedInputTokens: 10, outputTokens: 2 }); + }); +}); diff --git a/server/src/__tests__/heartbeat-list.test.ts b/server/src/__tests__/heartbeat-list.test.ts index c73d2b7b663..a36a71da1e3 100644 --- a/server/src/__tests__/heartbeat-list.test.ts +++ b/server/src/__tests__/heartbeat-list.test.ts @@ -1,6 +1,6 @@ import { randomUUID } from "node:crypto"; import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; -import { agents, companies, createDb, heartbeatRuns } from "@paperclipai/db"; +import { costEvents, agents, companies, createDb, heartbeatRuns } from "@paperclipai/db"; import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase, @@ -26,6 +26,7 @@ describeEmbeddedPostgres("heartbeat list", () => { }, 20_000); afterEach(async () => { + await db.delete(costEvents); await db.delete(heartbeatRuns); await db.delete(agents); await db.delete(companies); diff --git a/server/src/__tests__/heartbeat-process-recovery.test.ts b/server/src/__tests__/heartbeat-process-recovery.test.ts index 07bef2d6d8c..eeb8f031de4 100644 --- a/server/src/__tests__/heartbeat-process-recovery.test.ts +++ b/server/src/__tests__/heartbeat-process-recovery.test.ts @@ -31,7 +31,9 @@ import { agentWakeupRequests, approvals, authUsers, + budgetIncidents, budgetPolicies, + budgetReservations, chatActions, chatConversations, chatDeliveries, @@ -581,6 +583,7 @@ describeEmbeddedPostgres("heartbeat orphaned process recovery", () => { await db.delete(issueTreeHolds); await db.delete(agentTaskSessions); await db.delete(issueApprovals); + await db.delete(budgetIncidents); await db.delete(approvals); await db.update(issues).set({ lastStatusDecisionId: null }); await db.delete(statusDecisionEffects); @@ -7550,6 +7553,23 @@ describeEmbeddedPostgres("heartbeat orphaned process recovery", () => { }, ); + it("releases the dispatch reservation after a proven pre-provider adapter failure", async () => { + mockAdapterExecute.mockResolvedValueOnce({ + exitCode: 1, signal: null, timedOut: false, + errorMessage: "Provider startup failed", provider: "test", model: "test-model", + executionRecovery: { kind: "bootstrap", providerWorkStarted: false }, + }); + const { runId } = await seedRunFixture({ runtimeMode: "legacy", agentStatus: "idle", runStatus: "queued", includeIssue: false }); + const heartbeat = heartbeatService(db); + await heartbeat.resumeQueuedRuns(); + await heartbeat.drainActiveRunExecutions(); + const run = await heartbeat.getRun(runId); + expect(run).toMatchObject({ status: "failed", costAccountingPending: false }); + expect(run?.costAccountedAt).not.toBeNull(); + expect((await db.select().from(budgetReservations).where(eq(budgetReservations.runId, runId)))[0]).toMatchObject({ state: "released" }); + expect(await db.select().from(costEvents).where(eq(costEvents.heartbeatRunId, runId))).toEqual([]); + }); + it.each(["single Stop", "agent pause"] as const)( "fences adapter registration while an earlier no-owner %s waits to commit", async (operation) => { @@ -7674,6 +7694,10 @@ describeEmbeddedPostgres("heartbeat orphaned process recovery", () => { releaseAdapter(); await heartbeat.drainActiveRunExecutions(); const settledRun = await heartbeat.getRun(runId); + expect(settledRun?.costAccountingPending).toBe(false); + expect(settledRun?.costAccountedAt).not.toBeNull(); + expect((await db.select().from(budgetReservations).where(eq(budgetReservations.runId, runId)))[0]).toMatchObject({ state: "released" }); + expect(await db.select().from(costEvents).where(eq(costEvents.heartbeatRunId, runId))).toEqual([]); expect({ status: settledRun?.status, errorCode: settledRun?.errorCode, @@ -11065,6 +11089,9 @@ describeEmbeddedPostgres("heartbeat orphaned process recovery", () => { await waitForRunToSettle(heartbeat, sourceRun.id, 5_000); } expect(sourceRun?.id).not.toBe(runId); + // Durable accounting is bookkeeping, not evidence that the agent acted + // on its plan. A receipt must not suppress plan-only recovery. + expect(await db.select().from(costEvents).where(eq(costEvents.heartbeatRunId, String(sourceRunId)))).toHaveLength(1); expect(sourceRun?.livenessState).toBe("plan_only"); if (withLegacyReview) { expect(await db.select().from(issues).where(eq(issues.id, legacyReviewId))).toEqual(legacyReviewBefore); diff --git a/server/src/__tests__/openapi-routes.test.ts b/server/src/__tests__/openapi-routes.test.ts index 729b5f4af40..40e54ff8093 100644 --- a/server/src/__tests__/openapi-routes.test.ts +++ b/server/src/__tests__/openapi-routes.test.ts @@ -742,6 +742,36 @@ describe("openapi routes", () => { }); }); + it("documents exact money, reviewed correction inputs, and board-only accounting access", () => { + const { spec } = loadSpecRoutes(); + const base = "/api/companies/{companyId}/accounting/"; + for (const [suffix, methods] of [ + ["health", ["get"]], ["inspect", ["get"]], ["repair", ["post"]], ["retry", ["post"]], + ["invoices", ["get", "post"]], ["invoices/{invoiceId}", ["get"]], ["events/{eventId}/adjustments", ["get", "post"]], + ] as const) { + for (const method of methods) { + const operation = spec.paths[`${base}${suffix}`][method]; + expect(operation["x-paperclip-authorization"]).toEqual({ actor: "board" }); + expect(operation.security).toEqual([{ BoardSessionAuth: [] }, { BoardApiKeyAuth: [] }]); + expect(operation.responses["403"]).toBeDefined(); + } + } + const adjustment = spec.paths[`${base}events/{eventId}/adjustments`].post; + expect(adjustment.responses["201"]).toBeDefined(); + expect(adjustment.responses["409"]).toBeDefined(); + expect(adjustment.responses["422"]).toBeDefined(); + expect(adjustment.requestBody.content["application/json"].schema.required).toEqual(expect.arrayContaining([ + "idempotencyKey", "expectedCents", "correctedCents", "reason", "pricing", + ])); + for (const [endpoint, field] of [["cost-events", "costCents"], ["finance-events", "amountCents"]]) { + const operation = spec.paths[`/api/companies/{companyId}/${endpoint}`].post; + expect(operation.requestBody.content["application/json"].schema.properties[field]).toMatchObject({ oneOf: [{ type: "string" }, { type: "number" }] }); + expect(operation.responses["409"]).toBeDefined(); + } + const parameters = spec.paths["/api/companies/{companyId}/costs/summary"].get.parameters; + expect(parameters).toEqual(expect.arrayContaining([expect.objectContaining({ name: "period", in: "query", schema: expect.objectContaining({ enum: ["month", "all"] }) })])); + }); + it("documents board-only repository discovery and selection", () => { const { spec } = loadSpecRoutes(); const discovery = spec.paths["/api/companies/{companyId}/project-repositories"].get; diff --git a/server/src/__tests__/plugin-managed-agents.test.ts b/server/src/__tests__/plugin-managed-agents.test.ts index 0b06a551266..b364a866680 100644 --- a/server/src/__tests__/plugin-managed-agents.test.ts +++ b/server/src/__tests__/plugin-managed-agents.test.ts @@ -11,6 +11,7 @@ import { agentInstructionHeads, agents, approvals, + budgetPolicies, companies, createDb, pluginEntities, @@ -106,6 +107,7 @@ describeEmbeddedPostgres("plugin-managed agents", () => { await db.delete(pluginManagedResources); await db.delete(pluginCompanySettings); await db.delete(approvals); + await db.delete(budgetPolicies); await db.delete(agents); await db.delete(plugins); await db.delete(companies); @@ -497,6 +499,7 @@ describeEmbeddedPostgres("plugin-managed agents", () => { const relinked = await services.agents.managedReconcile({ companyId, agentKey: "wiki-maintainer" }); expect(relinked.status).toBe("relinked"); expect(relinked.agentId).toBe(agentId); + expect(relinked.agent?.urlKey).toBe("renamed-wiki-agent"); const [binding] = await db.select().from(pluginEntities); expect(binding?.data).toMatchObject({ agentId }); diff --git a/server/src/__tests__/provider-billing-import.test.ts b/server/src/__tests__/provider-billing-import.test.ts new file mode 100644 index 00000000000..aa590c66965 --- /dev/null +++ b/server/src/__tests__/provider-billing-import.test.ts @@ -0,0 +1,236 @@ +import { describe, expect, it, vi } from "vitest"; +import { fetchProviderDailyCosts } from "../services/provider-billing-import.js"; +import type { ImportProviderCosts } from "@paperclipai/shared"; + +const input: ImportProviderCosts = { + provider: "openai", + secretId: "00000000-0000-4000-8000-000000000001", + accountId: "org_test", + scopeIds: ["proj_test"], + from: "2026-09-01", + to: "2026-09-02", +}; +const start = Date.parse(input.from) / 1000; +function page(value = 0.06) { + return { + data: [ + { + start_time: start, + end_time: start + 86400, + results: [ + { project_id: "proj_test", amount: { currency: "usd", value } }, + ], + }, + ], + has_more: false, + next_page: null, + }; +} +const response = (body: unknown) => + new Response(JSON.stringify(body), { status: 200 }); +describe("provider cost report ingestion", () => { + it("uses fixed endpoints, admin identity and scoped daily amounts in the right currency units", async () => { + const fetcher = vi.fn(async () => response(page())); + expect( + await fetchProviderDailyCosts(input, "private-admin", fetcher), + ).toEqual([ + { day: "2026-09-01", scopeId: "proj_test", amountCents: "6.0000000" }, + ]); + const [url, options] = fetcher.mock.calls[0] as unknown as [ + URL, + RequestInit, + ]; + expect(url.origin).toBe("https://api.openai.com"); + expect(url.searchParams.get("group_by[]")).toBe("project_id"); + expect(options.redirect).toBe("error"); + expect(options.headers).toMatchObject({ + "OpenAI-Organization": "org_test", + }); + }); + it("verifies Anthropic organization identity and treats its amount as cents", async () => { + const fetcher = vi + .fn() + .mockResolvedValueOnce(response({ id: "org_test" })) + .mockResolvedValueOnce( + response({ + data: [ + { + starting_at: "2026-09-01T00:00:00Z", + ending_at: "2026-09-02T00:00:00Z", + results: [ + { + workspace_id: "proj_test", + amount: "123.78912", + currency: "USD", + }, + ], + }, + ], + has_more: false, + }), + ); + expect( + ( + await fetchProviderDailyCosts( + { ...input, provider: "anthropic" }, + "private-admin", + fetcher, + ) + )[0].amountCents, + ).toBe("123.7891200"); + await expect( + fetchProviderDailyCosts( + { ...input, provider: "anthropic" }, + "private-admin", + vi.fn(async () => response({ id: "other" })), + ), + ).rejects.toThrow("different provider organization"); + }); + it("excludes unrelated projects and supports an explicit empty daily bucket", async () => { + const body = page(); + body.data[0].results[0].project_id = "other-company-project"; + expect( + ( + await fetchProviderDailyCosts( + input, + "private", + vi.fn(async () => response(body)), + ) + )[0].amountCents, + ).toBe("0.0000000"); + }); + it.each([ + "missing-day", + "missing-group", + "overlap", + "currency", + "negative", + "period", + "cursor", + "shape", + ])( + "rejects %s rather than importing partial or invented totals", + async (kind) => { + const body: any = page(); + if (kind === "missing-day") body.data = []; + if (kind === "missing-group") delete body.data[0].results[0].project_id; + if (kind === "overlap") body.data.push(body.data[0]); + if (kind === "currency") body.data[0].results[0].amount.currency = "EUR"; + if (kind === "negative") body.data[0].results[0].amount.value = -1; + if (kind === "period") body.data[0].end_time++; + if (kind === "cursor") body.has_more = true; + if (kind === "shape") body.data[0].results[0].amount = {}; + await expect( + fetchProviderDailyCosts( + input, + "private", + vi.fn(async () => response(body)), + ), + ).rejects.toThrow(); + }, + ); + it("does not expose upstream errors or credentials", async () => { + await expect( + fetchProviderDailyCosts( + input, + "private-secret", + vi.fn( + async () => + new Response("private-secret raw diagnostics", { status: 403 }), + ), + ), + ).rejects.toThrow("Check the admin credential"); + }); + it("bounds response size and does not follow redirects", async () => { + await expect( + fetchProviderDailyCosts( + input, + "private", + vi.fn(async () => new Response("x".repeat(2_000_001))), + ), + ).rejects.toThrow("too large"); + }); + it("collects all pages before returning and rejects a repeated cursor", async () => { + const first = { ...page(), has_more: true, next_page: "next" }; + const second = page(0.01); + second.data[0].start_time += 86400; + second.data[0].end_time += 86400; + const fetcher = vi + .fn() + .mockResolvedValueOnce(response(first)) + .mockResolvedValueOnce(response(second)); + expect( + await fetchProviderDailyCosts( + { ...input, to: "2026-09-03" }, + "private", + fetcher, + ), + ).toHaveLength(2); + const empty = { data: [], has_more: true, next_page: "same" }; + await expect( + fetchProviderDailyCosts( + input, + "private", + vi.fn(async () => response(empty)), + ), + ).rejects.toThrow("cursor"); + }); + it("preserves a valid report if closing its already-consumed response stream fails", async () => { + let sent = false; + const reader = { + read: async () => + sent + ? { done: true } + : ((sent = true), + { done: false, value: Buffer.from(JSON.stringify(page())) }), + cancel: async () => { + throw new Error("closed transport"); + }, + }; + const fetcher = vi + .fn() + .mockResolvedValue({ + ok: true, + body: { getReader: () => reader }, + } as unknown as Response); + expect( + (await fetchProviderDailyCosts(input, "private", fetcher))[0].amountCents, + ).toBe("6.0000000"); + }); + it("includes unallocated costs only when the default scope was explicitly selected", async () => { + const body = page(); + (body.data[0].results[0] as { project_id: string | null }).project_id = + null; + expect( + await fetchProviderDailyCosts( + { ...input, scopeIds: ["default"] }, + "private", + vi.fn(async () => response(body)), + ), + ).toEqual([ + { day: "2026-09-01", scopeId: "default", amountCents: "6.0000000" }, + ]); + }); + it("rejects a provider amount with the other provider's unit/shape and missing period boundaries", async () => { + const wrong: any = page(); + wrong.data[0].results[0].amount = "6"; + wrong.data[0].results[0].currency = "USD"; + await expect( + fetchProviderDailyCosts( + input, + "private", + vi.fn(async () => response(wrong)), + ), + ).rejects.toThrow("invalid billing amount"); + const missing: any = page(); + delete missing.data[0].start_time; + delete missing.data[0].end_time; + await expect( + fetchProviderDailyCosts( + input, + "private", + vi.fn(async () => response(missing)), + ), + ).rejects.toThrow("unexpected billing period"); + }); +}); diff --git a/server/src/__tests__/server-startup-feedback-export.test.ts b/server/src/__tests__/server-startup-feedback-export.test.ts index f6f8e20dbb3..4de339d0354 100644 --- a/server/src/__tests__/server-startup-feedback-export.test.ts +++ b/server/src/__tests__/server-startup-feedback-export.test.ts @@ -57,6 +57,7 @@ const { reason: null, })); const heartbeatServiceMock = { + reconcileCostAccounting: vi.fn(async () => ({ scanned: 0, accounted: 0 })), resolveSchedulingSuppression: resolveHeartbeatSchedulingSuppressionMock, recoverNativeRunsAfterRestart: vi.fn(async () => ({ restartKind: "hard", @@ -559,11 +560,13 @@ describe("startServer feedback export wiring", () => { }) as typeof setInterval); try { await startServer(); + expect(heartbeatServiceMock.reconcileCostAccounting).toHaveBeenCalledTimes(1); expect(heartbeatServiceMock.sweepStaleIssueLocks).toHaveBeenCalledTimes(1); expect(intervalCallback).not.toBeNull(); intervalCallback?.(); await new Promise((resolve) => setImmediate(resolve)); expect(heartbeatServiceMock.sweepStaleIssueLocks).toHaveBeenCalledTimes(2); + expect(heartbeatServiceMock.reconcileCostAccounting).toHaveBeenCalledTimes(2); expect(retiredDetector).not.toHaveBeenCalled(); } finally { delete (runtime as Partial).reconcileProductivityReviews; @@ -660,6 +663,7 @@ describe("startServer feedback export wiring", () => { // reaped at startup and on the interval. expect(heartbeatServiceFactoryMock).toHaveBeenCalledTimes(1); expect(heartbeatServiceMock.sweepPendingCleanupLeases).toHaveBeenCalled(); + expect(heartbeatServiceMock.reconcileCostAccounting).toHaveBeenCalledTimes(1); expect(intervalCallback).not.toBeNull(); intervalCallback?.(); await Promise.resolve(); @@ -667,12 +671,33 @@ describe("startServer feedback export wiring", () => { expect(externalObjectsServiceMock.refreshDueObjectsForActiveCompanies).toHaveBeenCalledTimes(1); expect(routineServiceMock.tickScheduledTriggers).not.toHaveBeenCalled(); + expect(heartbeatServiceMock.reconcileCostAccounting).toHaveBeenCalledTimes(2); expect(environmentCustomImagesServiceMock.cleanupExpiredSetupSessions).not.toHaveBeenCalled(); } finally { setIntervalSpy.mockRestore(); } }); + it.each([false, true])("retries failed accounting recovery with heartbeat scheduling enabled: %s", async (enabled) => { + loadConfigMock.mockReturnValue(buildTestConfig({ heartbeatSchedulerEnabled: enabled })); + heartbeatServiceMock.reconcileCostAccounting.mockRejectedValueOnce(new Error("temporary ledger outage")); + let tick: (() => void) | undefined; + const timer = vi.spyOn(globalThis, "setInterval").mockImplementation(((callback: () => void) => { + tick = callback; + return 1 as unknown as ReturnType; + }) as typeof setInterval); + try { + expect((await startServer()).server).toBe(fakeServer); + expect(heartbeatServiceMock.reconcileCostAccounting).toHaveBeenCalledTimes(1); + expect(tick).toBeDefined(); + tick?.(); + await new Promise((resolve) => setImmediate(resolve)); + expect(heartbeatServiceMock.reconcileCostAccounting).toHaveBeenCalledTimes(2); + } finally { + timer.mockRestore(); + } + }); + it("does not replay hot-restart adoption when the orphan reaper retries", async () => { loadConfigMock.mockReturnValue(buildTestConfig({ heartbeatSchedulerEnabled: true, diff --git a/server/src/index.ts b/server/src/index.ts index 92c57e4b95b..c65a74b292e 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -876,6 +876,7 @@ async function startServerWithDatabaseTeardown( const heartbeat = config.heartbeatSchedulerEnabled ? heartbeatService(db as any, { pluginWorkerManager }) : null; + const accountingHeartbeat = heartbeat ?? heartbeatService(db as any, { pluginWorkerManager }); const decisionServiceOptions = { wakeOriginAgent: createDecisionWakeOriginAgent(heartbeat?.wakeup ?? null), }; @@ -1223,8 +1224,7 @@ async function startServerWithDatabaseTeardown( // so a just-failed lease does not draw a retry on every tick. The startup // sweep passes zero, so a restart retries a stranded orphan at once. const ENVIRONMENT_LEASE_CLEANUP_SWEEP_BACKOFF_MS = 5 * 60 * 1000; - const environmentLeaseCleanupHeartbeat = - heartbeat ?? heartbeatService(db as any, { pluginWorkerManager }); + const environmentLeaseCleanupHeartbeat = accountingHeartbeat; const chatCompletionDeliveries = chatCompletionDeliveryService(db as any, environmentLeaseCleanupHeartbeat); // Activity publication happens after the status transaction commits. This is // a best-effort fast path; the durable outbox and sweeps remain authoritative. @@ -1442,6 +1442,7 @@ async function startServerWithDatabaseTeardown( }, "worktree run-execution cutoff state", ); + await accountingHeartbeat.reconcileCostAccounting().catch((err) => logger.error({ err }, "Cost accounting recovery failed; pending receipts will retry")); const heartbeatSchedulingSuppression = await heartbeat.resolveSchedulingSuppression(); // Reap orphaned runs before timer ticks start so wakeups cannot coalesce @@ -1457,8 +1458,7 @@ async function startServerWithDatabaseTeardown( // workers before cleanup or retry admission, including unmanaged installs. await app.locals.bundledPluginsStartup; try { - const nativeRecovery = - await heartbeat.recoverNativeRunsAfterRestart(); + const nativeRecovery = await heartbeat.recoverNativeRunsAfterRestart(); if (nativeRecovery.dispositions.length > 0) { logger.info( { @@ -1756,6 +1756,9 @@ async function startServerWithDatabaseTeardown( })); trackHeartbeatSchedulerWork(chatCompletionDeliveries.sweepPending().catch((err) => logger.error({ err }, "chat completion delivery failed"))); + + trackHeartbeatSchedulerWork(accountingHeartbeat.reconcileCostAccounting().catch((err) => logger.error({ err }, "Cost accounting recovery failed"))); + trackHeartbeatSchedulerWork(connectionDeliveries.sweepPending().catch((err) => logger.error({ err }, "connection continuation delivery failed"))); trackHeartbeatSchedulerWork(app.locals.toolGateway.sweepActionReviews().catch((err: unknown) => logger.error({ err }, "tool review recovery failed"))); trackHeartbeatSchedulerWork(app.locals.toolGateway.cleanupExpiredSessions().catch((err: unknown) => logger.error({ err }, "gateway token cleanup failed"))); @@ -1832,8 +1835,10 @@ async function startServerWithDatabaseTeardown( // is still required. A failed acquire can leak a paid provider sandbox, so // this path retries the teardown at startup and on the interval, exactly as // the enabled path does. + await accountingHeartbeat.reconcileCostAccounting().catch((err) => logger.error({ err }, "Cost accounting recovery failed; pending receipts will retry")); await runEnvironmentLeaseCleanupSweep(0); startHeartbeatSchedulerInterval(() => { + trackHeartbeatSchedulerWork(accountingHeartbeat.reconcileCostAccounting().catch((err) => logger.error({ err }, "Cost accounting recovery failed"))); scheduleExternalObjectRefreshSweep(new Date()); scheduleEnvironmentLeaseCleanupSweep(); scheduleGitHubConnectionEventPoll(); diff --git a/server/src/routes/costs.ts b/server/src/routes/costs.ts index b66abc0fc0d..d30a4de3877 100644 --- a/server/src/routes/costs.ts +++ b/server/src/routes/costs.ts @@ -1,4 +1,10 @@ +import { importProviderDailyCosts } from "../services/provider-billing-import.js"; +import { importProviderCostsSchema } from "@paperclipai/shared"; +import { accountingIntegrityService } from "../services/accounting-integrity.js"; +import { billingReconciliationService } from "../services/billing-reconciliation.js"; +import { adjustCostSchema, importBillingInvoiceSchema, repairAccountingSchema, retryAccountingSchema } from "@paperclipai/shared"; import { Router } from "express"; +import { z } from "zod"; import type { Db } from "@paperclipai/db"; import { createCostEventSchema, @@ -18,28 +24,37 @@ import { issueService, heartbeatService, accessService, - logActivity, } from "../services/index.js"; import { assertBoard, assertCompanyAccess, getAccessibleResource, getActorInfo } from "./authz.js"; import { fetchCompanyQuotaWindows } from "../services/quota-windows.js"; import { badRequest } from "../errors.js"; import type { PluginWorkerManager } from "../services/plugin-worker-manager.js"; +const reportDateSchema = z.union([z.iso.datetime({ offset: true }), z.iso.date()]); + export function parseCostDateRange(query: Record) { + if (query.period !== undefined && query.period !== "all" && query.period !== "month") throw badRequest("invalid 'period'"); + if (query.period === "all") { + if (query.from !== undefined || query.to !== undefined) throw badRequest("all-time period cannot have date bounds"); + return { allTime: true }; + } + if (query.from !== undefined && !reportDateSchema.safeParse(query.from).success) throw badRequest("invalid 'from' date"); + if (query.to !== undefined && !reportDateSchema.safeParse(query.to).success) throw badRequest("invalid 'to' date"); const fromRaw = query.from as string | undefined; const toRaw = query.to as string | undefined; const from = fromRaw ? new Date(fromRaw) : undefined; const to = toRaw ? new Date(toRaw) : undefined; if (from && isNaN(from.getTime())) throw badRequest("invalid 'from' date"); if (to && isNaN(to.getTime())) throw badRequest("invalid 'to' date"); + if (from && to && from > to) throw badRequest("from must not be after to"); return (from || to) ? { from, to } : undefined; } export function parseCostLimit(query: Record) { - const raw = Array.isArray(query.limit) ? query.limit[0] : query.limit; + const raw = query.limit; if (raw == null || raw === "") return 100; - const limit = typeof raw === "number" ? raw : Number.parseInt(String(raw), 10); - if (!Number.isFinite(limit) || limit <= 0 || limit > 500) { + const limit = typeof raw === "number" || typeof raw === "string" ? Number(raw) : Number.NaN; + if (!Number.isInteger(limit) || limit <= 0 || limit > 500) { throw badRequest("invalid 'limit' value"); } return limit; @@ -120,22 +135,11 @@ export function costRoutes( return; } + const actor = getActorInfo(req); const event = await costs.createEvent(companyId, { ...req.body, occurredAt: new Date(req.body.occurredAt), - }); - - const actor = getActorInfo(req); - await logActivity(db, { - companyId, - actorType: actor.actorType, - actorId: actor.actorId, - agentId: actor.agentId, - action: "cost.reported", - entityType: "cost_event", - entityId: event.id, - details: { costCents: event.costCents, model: event.model }, - }); + }, { actorType: actor.actorType, actorId: actor.actorId, agentId: actor.agentId }); res.status(201).json(event); }); @@ -145,27 +149,11 @@ export function costRoutes( assertCompanyAccess(req, companyId); assertBoard(req); + const actor = getActorInfo(req); const event = await finance.createEvent(companyId, { ...req.body, occurredAt: new Date(req.body.occurredAt), - }); - - const actor = getActorInfo(req); - await logActivity(db, { - companyId, - actorType: actor.actorType, - actorId: actor.actorId, - agentId: actor.agentId, - action: "finance_event.reported", - entityType: "finance_event", - entityId: event.id, - details: { - amountCents: event.amountCents, - biller: event.biller, - eventKind: event.eventKind, - direction: event.direction, - }, - }); + }, { actorType: actor.actorType, actorId: actor.actorId, agentId: actor.agentId }); res.status(201).json(event); }); @@ -331,22 +319,6 @@ export function costRoutes( assertBoard(req); const companyId = req.params.companyId as string; assertCompanyAccess(req, companyId); - const company = await companies.update(companyId, { budgetMonthlyCents: req.body.budgetMonthlyCents }); - if (!company) { - res.status(404).json({ error: "Company not found" }); - return; - } - - await logActivity(db, { - companyId, - actorType: "user", - actorId: req.actor.userId ?? "board", - action: "company.budget_updated", - entityType: "company", - entityId: companyId, - details: { budgetMonthlyCents: req.body.budgetMonthlyCents }, - }); - await budgets.upsertPolicy( companyId, { @@ -358,7 +330,7 @@ export function costRoutes( req.actor.userId ?? "board", ); - res.json(company); + res.json(await companies.getById(companyId)); }); router.patch("/agents/:agentId/budgets", validate(updateBudgetSchema), async (req, res) => { @@ -368,37 +340,50 @@ export function costRoutes( assertBoard(req); - const updated = await agents.update(agentId, { budgetMonthlyCents: req.body.budgetMonthlyCents }); - if (!updated) { - res.status(404).json({ error: "Agent not found" }); - return; - } - - const actor = getActorInfo(req); - await logActivity(db, { - companyId: updated.companyId, - actorType: actor.actorType, - actorId: actor.actorId, - agentId: actor.agentId, - action: "agent.budget_updated", - entityType: "agent", - entityId: updated.id, - details: { budgetMonthlyCents: updated.budgetMonthlyCents }, - }); - await budgets.upsertPolicy( - updated.companyId, + agent.companyId, { scopeType: "agent", - scopeId: updated.id, - amount: updated.budgetMonthlyCents, + scopeId: agent.id, + amount: req.body.budgetMonthlyCents, windowKind: "calendar_month_utc", }, req.actor.type === "board" ? req.actor.userId ?? "board" : null, ); - res.json(updated); + res.json(await agents.getById(agentId)); }); + const integrity = accountingIntegrityService(db, budgetHooks); + const billing = billingReconciliationService(db, budgetHooks); + // Operational accounting evidence and repairs are board-only. These payloads + // can contain company-wide billing identifiers even for an agent's own run. + router.use("/companies/:companyId/accounting", (req, _res, next) => { + assertCompanyAccess(req, req.params.companyId as string); assertBoard(req); next(); + }); + router.post("/companies/:companyId/accounting/provider-costs/import", validate(importProviderCostsSchema), async (req, res) => { + const companyId = req.params.companyId as string; + assertCompanyAccess(req, companyId); + assertBoard(req); + const result = await importProviderDailyCosts(db, companyId, req.body, getActorInfo(req).actorId); + res.json(result); + }); + + router.get("/companies/:companyId/accounting/health", async (req, res) => res.json(await integrity.health(req.params.companyId as string))); + router.get("/companies/:companyId/accounting/inspect", async (req, res) => res.json(await integrity.inspect(req.params.companyId as string))); + router.post("/companies/:companyId/accounting/repair", validate(repairAccountingSchema), async (req, res) => + res.json(await integrity.repair(req.params.companyId as string, req.body.fingerprint, req.body.reason, getActorInfo(req).actorId))); + router.post("/companies/:companyId/accounting/retry", validate(retryAccountingSchema), async (req, res) => + res.json(await integrity.retry(req.params.companyId as string, req.body.runId, getActorInfo(req).actorId))); + router.get("/companies/:companyId/accounting/invoices", async (req, res) => res.json(await billing.list(req.params.companyId as string))); + router.post("/companies/:companyId/accounting/invoices", validate(importBillingInvoiceSchema), async (req, res) => + res.status(201).json(await billing.importInvoice(req.params.companyId as string, req.body, getActorInfo(req).actorId))); + router.get("/companies/:companyId/accounting/invoices/:invoiceId", async (req, res) => + res.json(await billing.reconcile(req.params.companyId as string, req.params.invoiceId as string))); + router.get("/companies/:companyId/accounting/events/:eventId/adjustments", async (req, res) => + res.json(await billing.adjustments(req.params.companyId as string, req.params.eventId as string))); + router.post("/companies/:companyId/accounting/events/:eventId/adjustments", validate(adjustCostSchema), async (req, res) => + res.status(201).json(await billing.adjust(req.params.companyId as string, req.params.eventId as string, req.body, getActorInfo(req).actorId))); + return router; } diff --git a/server/src/routes/openapi.ts b/server/src/routes/openapi.ts index f37ba2eba83..92a52920996 100644 --- a/server/src/routes/openapi.ts +++ b/server/src/routes/openapi.ts @@ -124,6 +124,11 @@ import { updateBudgetSchema, upsertBudgetPolicySchema, resolveBudgetIncidentSchema, + repairAccountingSchema, + retryAccountingSchema, + importBillingInvoiceSchema, + importProviderCostsSchema, + adjustCostSchema, // Sidebar upsertSidebarOrderPreferenceSchema, // Announcements @@ -1320,6 +1325,7 @@ const PUBLIC_OPERATIONS = new Set([ ]); const BOARD_ONLY_PREFIXES = [ + "/api/companies/{companyId}/accounting/", "/api/announcements/", "/api/auth/", "/api/admin/", @@ -5250,6 +5256,12 @@ registry.registerPath({ // ─── Costs ─────────────────────────────────────────────────────────────────── +const costReportQuerySchema = z.object({ + period: z.enum(["month", "all"]).optional().describe("Defaults to the current UTC month. All-time cannot be combined with date bounds."), + from: z.union([z.iso.datetime({ offset: true }), z.iso.date()]).optional().describe("Inclusive start bound."), + to: z.union([z.iso.datetime({ offset: true }), z.iso.date()]).optional().describe("Inclusive end bound; must not precede from."), +}); + const costSummaryPaths = [ "summary", "by-agent", @@ -5271,7 +5283,12 @@ for (const segment of costSummaryPaths) { path: `/api/companies/{companyId}/costs/${segment}`, tags: ["costs"], summary: `Cost report: ${segment}`, - request: { params: z.object({ companyId: z.string() }) }, + request: { + params: z.object({ companyId: z.string() }), + ...(!["window-spend", "quota-windows"].includes(segment) ? { + query: segment === "finance-events" ? costReportQuerySchema.extend({ limit: z.coerce.number().int().min(1).max(500).optional() }) : costReportQuerySchema, + } : {}), + }, responses: { 200: r.ok(), 401: r.unauthorized }, }); } @@ -5281,11 +5298,12 @@ registry.registerPath({ path: "/api/companies/{companyId}/cost-events", tags: ["costs"], summary: "Record a cost event", + description: "Amounts are USD cents, including fractional cents. Decimal strings preserve exact nanodollar precision. Reuse the idempotency key and original receipt on retries; conflicting reuse returns 409.", request: { params: z.object({ companyId: z.string() }), body: jsonBody(createCostEventSchema), }, - responses: { 200: r.ok(), 400: r.badRequest, 401: r.unauthorized }, + responses: { 200: r.ok(), 400: r.badRequest, 401: r.unauthorized, 409: r.conflict }, }); registry.registerPath({ @@ -5293,13 +5311,47 @@ registry.registerPath({ path: "/api/companies/{companyId}/finance-events", tags: ["costs"], summary: "Record a finance event", + description: "Amounts accept exact decimal cents in the recorded currency. Credits use a nonnegative amount with direction credit. No currency conversion is inferred. Receipt-key conflicts return 409.", request: { params: z.object({ companyId: z.string() }), body: jsonBody(createFinanceEventSchema), }, - responses: { 200: r.ok(), 400: r.badRequest, 401: r.unauthorized }, + responses: { 200: r.ok(), 400: r.badRequest, 401: r.unauthorized, 409: r.conflict }, }); +for (const [suffix, summary] of [ + ["health", "Inspect pending receipts, unpriced charges, cancellation backlog, and held reservations"], + ["inspect", "Independently compare ledger evidence and stored totals"], + ["invoices", "List imported provider invoices"], + ["invoices/{invoiceId}", "Review exact invoice matches and unresolved differences"], + ["events/{eventId}/adjustments", "Read append-only correction history and prior pricing evidence"], +] as const) { + registry.registerPath({ + method: "get", path: `/api/companies/{companyId}/accounting/${suffix}`, tags: ["costs"], summary, + description: "Company-scoped board access is required. Board viewers may inspect; agents cannot access operator accounting evidence.", + request: { params: z.object({ companyId: z.string(), + ...(suffix.includes("invoiceId") ? { invoiceId: z.string() } : {}), + ...(suffix.includes("eventId") ? { eventId: z.string() } : {}), + }) }, + responses: { 200: r.ok(), 401: r.unauthorized, 403: r.forbidden, 404: r.notFound }, + }); +} + +for (const operation of [ + { suffix: "repair", schema: repairAccountingSchema, status: 200, summary: "Repair reviewed ledger projections", description: "Requires an audit reason and the current inspection fingerprint. A stale inspection returns 409. Missing evidence is never invented." }, + { suffix: "retry", schema: retryAccountingSchema, status: 200, summary: "Retry accounting for a company-owned run", description: "Retries accounting only. Provider work is never re-executed; incomplete evidence remains pending." }, + { suffix: "provider-costs/import", schema: importProviderCostsSchema, status: 200, summary: "Import a scoped provider cost report", description: "Reads up to 31 completed UTC days from OpenAI or Anthropic with a company admin credential. Requires explicit provider account and project/workspace identifiers. All pages validate before writing. Reimports append only the difference; run costs are not repriced. Report totals remain separate from invoices." }, + { suffix: "invoices", schema: importBillingInvoiceSchema, status: 201, summary: "Import normalized invoice evidence", description: "Idempotent by company, biller, and external invoice ID. Conflicting reuse returns 409. Import atomically creates finance timeline events and does not reprice run costs." }, + { suffix: "events/{eventId}/adjustments", schema: adjustCostSchema, status: 201, summary: "Apply a reviewed cost correction", description: "Requires the expected exact cents, an idempotency key, reason, and pricing evidence. Original amounts and pricing revisions remain in history. Stale reviews return 409; ambiguous or contradictory invoice evidence returns 422." }, +] as const) { + registry.registerPath({ + method: "post", path: `/api/companies/{companyId}/accounting/${operation.suffix}`, tags: ["costs"], + summary: operation.summary, description: `${operation.description} Company-scoped board write access is required; viewers and agents cannot mutate accounting.`, + request: { params: z.object({ companyId: z.string(), ...(operation.suffix.includes("eventId") ? { eventId: z.string() } : {}) }), body: jsonBody(operation.schema) }, + responses: { [operation.status]: r.ok(), 400: r.badRequest, 401: r.unauthorized, 403: r.forbidden, 404: r.notFound, 409: r.conflict, 422: r.unprocessable }, + }); +} + registry.registerPath({ method: "post", path: "/api/companies/{companyId}/budgets/policies", diff --git a/server/src/services/accounting-integrity.ts b/server/src/services/accounting-integrity.ts new file mode 100644 index 00000000000..c5b1709e71e --- /dev/null +++ b/server/src/services/accounting-integrity.ts @@ -0,0 +1,121 @@ +import { and, eq, sql } from "drizzle-orm"; +import { agents, agentRuntimeState, companies, heartbeatRuns, type Db } from "@paperclipai/db"; +import { normalizeCents, usdToCents, type AccountingFinding, type AccountingHealth, type AccountingInspection } from "@paperclipai/shared"; +import { conflict, notFound } from "../errors.js"; +import { receiptFingerprint } from "./receipt-fingerprint.js"; +import { withAccountingTransaction } from "./accounting-transaction.js"; +import { logActivity } from "./activity-log.js"; +import { accountRunCost } from "./run-cost-accounting.js"; +import type { BudgetServiceHooks } from "./budgets.js"; + +/** Independent aggregation: deliberately does not reuse writer projections. */ +async function inspect(tx: Db, companyId: string): Promise { + const findings: AccountingFinding[] = []; + const monthly = await tx.execute<{ kind: "company_projection" | "agent_projection"; id: string; actual: string; expected: string }>(sql` + with month_events as (select agent_id, cost_cents from cost_events where company_id = ${companyId} + and occurred_at >= date_trunc('month', now() at time zone 'UTC') at time zone 'UTC' + and occurred_at < (date_trunc('month', now() at time zone 'UTC') + interval '1 month') at time zone 'UTC') + select 'company_projection' as kind, id, spent_monthly_cents::text as actual, + (select coalesce(sum(cost_cents),0)::text from month_events) as expected from companies where id = ${companyId} + union all select 'agent_projection', a.id, a.spent_monthly_cents::text, + coalesce((select sum(e.cost_cents) from month_events e where e.agent_id = a.id),0)::text from agents a where a.company_id = ${companyId}`); + for (const row of monthly) if (normalizeCents(row.actual) !== normalizeCents(row.expected)) findings.push({ kind: row.kind, entityId: row.id, repairable: true, + actual: { cents: normalizeCents(row.actual) }, expected: { cents: normalizeCents(row.expected) } }); + const runtime = await tx.execute<{ id: string; baseline: string | null; actual: Record; expected: Record }>(sql` + with totals as (select r.agent_id, sum(e.cost_cents) as cents, sum(e.input_tokens) as input, sum(e.cached_input_tokens) as cached, sum(e.output_tokens) as output + from heartbeat_runs r join cost_events e on e.heartbeat_run_id = r.id and e.company_id = r.company_id + where r.company_id = ${companyId} and r.accounting_projection_version = 'v2' and r.cost_accounted_at is not null group by r.agent_id) + select a.agent_id as id, b.agent_id as baseline, + jsonb_build_object('cents', a.total_cost_cents::text, 'input', a.total_input_tokens::text, 'cached', a.total_cached_input_tokens::text, 'output', a.total_output_tokens::text) as actual, + jsonb_build_object('cents', (coalesce(b.cost_cents,0) + coalesce(t.cents,0))::text, 'input', (coalesce(b.input_tokens,0) + coalesce(t.input,0))::text, + 'cached', (coalesce(b.cached_input_tokens,0) + coalesce(t.cached,0))::text, 'output', (coalesce(b.output_tokens,0) + coalesce(t.output,0))::text) as expected + from agent_runtime_state a left join accounting_runtime_baselines b on b.agent_id = a.agent_id and b.company_id = a.company_id + left join totals t on t.agent_id = a.agent_id where a.company_id = ${companyId} order by a.agent_id`); + for (const row of runtime) { + if (!row.baseline) { findings.push({ kind: "legacy_runtime", entityId: row.id, repairable: false, actual: row.actual, expected: {} }); continue; } + row.actual.cents = normalizeCents(row.actual.cents); row.expected.cents = normalizeCents(row.expected.cents); + if (Object.keys(row.expected).some(key => row.actual[key] !== row.expected[key])) findings.push({ kind: "runtime_projection", entityId: row.id, repairable: true, actual: row.actual, expected: row.expected }); + } + const incomplete = await tx.execute<{ id: string; acknowledged: boolean; events: number }>(sql` + select r.id, r.cost_accounted_at is not null as acknowledged, count(e.id)::int as events from heartbeat_runs r + left join cost_events e on e.heartbeat_run_id = r.id and e.company_id = r.company_id + where r.company_id = ${companyId} and r.status in ('succeeded','failed','timed_out','cancelled') + and (r.cost_accounting_pending or r.accounting_projection_version = 'v2') + and r.result_json->'executionRecovery'->>'providerWorkStarted' is distinct from 'false' + and r.usage_json->>'accountingProviderWorkStarted' is distinct from 'false' + group by r.id having r.cost_accounted_at is null or count(e.id) = 0 order by r.id`); + for (const row of incomplete) findings.push({ kind: row.acknowledged ? "missing_receipt" : "missing_acknowledgement", entityId: row.id, repairable: false, + actual: { acknowledged: String(row.acknowledged), events: String(row.events) }, expected: { acknowledged: "true", events: ">=1" } }); + // Verify new run receipts independently of the projections. Corrections + // change effective valuation, while reported_cost_cents preserves the + // provider amount this acknowledgement originally consumed. + const receipts = await tx.execute<{ id: string; usage: Record; actual: Record }>(sql` + select r.id, r.usage_json as usage, jsonb_build_object('cents', sum(coalesce(e.reported_cost_cents,e.cost_cents))::text, + 'input', sum(e.input_tokens)::text, 'cached', sum(e.cached_input_tokens)::text, 'output', sum(e.output_tokens)::text) as actual + from heartbeat_runs r join cost_events e on e.heartbeat_run_id = r.id and e.company_id = r.company_id + where r.company_id = ${companyId} and r.accounting_projection_version = 'v2' and r.cost_accounted_at is not null group by r.id order by r.id`); + for (const row of receipts) { + const usage = row.usage ?? {}; + const cost = usage.cacheAdjustedCostUsd ?? usage.costUsdExact ?? usage.costUsd ?? 0; + let cents: string; + try { cents = usage.billingType === "subscription_included" ? "0.0000000" : usdToCents(String(cost)); } + catch { cents = "invalid receipt"; } + const expected = { cents, input: String(usage.inputTokens ?? 0), cached: String(usage.cachedInputTokens ?? 0), output: String(usage.outputTokens ?? 0) }; + row.actual.cents = normalizeCents(row.actual.cents); + if (Object.entries(expected).some(([key,value]) => row.actual[key] !== value)) findings.push({ kind: "receipt_mismatch", entityId: row.id, repairable: false, actual: row.actual, expected }); + } + findings.sort((a,b) => a.kind.localeCompare(b.kind) || a.entityId.localeCompare(b.entityId)); + return { companyId, findings, fingerprint: receiptFingerprint({ companyId, findings }), checkedAt: new Date().toISOString() }; +} + +export function accountingIntegrityService(db: Db, hooks: BudgetServiceHooks = {}) { + return { + inspect: (companyId: string) => withAccountingTransaction(db, companyId, tx => inspect(tx, companyId)), + repair: (companyId: string, fingerprint: string, reason: string, actorId: string) => withAccountingTransaction(db, companyId, async (tx, publications) => { + if (!reason.trim()) throw conflict("A repair reason is required"); + const before = await inspect(tx, companyId); + if (before.fingerprint !== fingerprint) throw conflict("Accounting changed since inspection; inspect again before repairing"); + for (const finding of before.findings.filter(f => f.repairable)) { + if (finding.kind === "company_projection") await tx.update(companies).set({ spentMonthlyCents: sql`${finding.expected.cents}::numeric`, updatedAt: new Date() }).where(eq(companies.id, companyId)); + if (finding.kind === "agent_projection") await tx.update(agents).set({ spentMonthlyCents: sql`${finding.expected.cents}::numeric`, updatedAt: new Date() }).where(and(eq(agents.id, finding.entityId), eq(agents.companyId, companyId))); + if (finding.kind === "runtime_projection") await tx.update(agentRuntimeState).set({ totalCostCents: sql`${finding.expected.cents}::numeric`, + totalInputTokens: sql`${finding.expected.input}::bigint`, totalCachedInputTokens: sql`${finding.expected.cached}::bigint`, totalOutputTokens: sql`${finding.expected.output}::bigint`, updatedAt: new Date(), + }).where(and(eq(agentRuntimeState.agentId, finding.entityId), eq(agentRuntimeState.companyId, companyId))); + } + await logActivity(tx, { companyId, actorType: "user", actorId, action: "accounting.projections_repaired", entityType: "company", entityId: companyId, + details: { fingerprint, reason, repairs: before.findings.filter(f => f.repairable) } }, publications); + return inspect(tx, companyId); + }), + retry: async (companyId: string, runId: string, actorId: string) => { + const [run] = await db.select({ id: heartbeatRuns.id }).from(heartbeatRuns).where(and(eq(heartbeatRuns.companyId, companyId), eq(heartbeatRuns.id, runId))); + if (!run) throw notFound("Accounting run not found"); + await logActivity(db, { companyId, actorType: "user", actorId, action: "accounting.retry_requested", entityType: "heartbeat_run", entityId: runId }); + try { return { accounted: await accountRunCost(db, runId, hooks) }; } + catch (error) { + await db.update(heartbeatRuns).set({ accountingLastAttemptAt: new Date(), accountingAttemptCount: sql`${heartbeatRuns.accountingAttemptCount} + 1`, + accountingLastError: error instanceof Error ? error.message.slice(0,1000) : "Accounting retry failed", + }).where(and(eq(heartbeatRuns.id, runId), eq(heartbeatRuns.companyId, companyId))); + throw error; + } + }, + health: (companyId: string): Promise => withAccountingTransaction(db, companyId, async tx => { + const [counts] = await tx.execute<{ pending: number; unpriced: number; oldest: string | null; cancellations: number; reserved: string }>(sql` + select (select count(*)::int from heartbeat_runs where company_id = ${companyId} and cost_accounting_pending and status in ('succeeded','failed','timed_out','cancelled')) as pending, + (select count(*)::int from cost_events where company_id = ${companyId} and cost_status = 'unpriced' and billing_type <> 'subscription_included') as unpriced, + (select min(coalesce(finished_at,created_at))::text from heartbeat_runs where company_id = ${companyId} and cost_accounting_pending and status in ('succeeded','failed','timed_out','cancelled')) as oldest, + (select count(*)::int from budget_policies where company_id = ${companyId} and enforcement_version > enforcement_delivered_version) as cancellations, + (select coalesce(sum(amount_cents),0)::text from budget_reservations where company_id = ${companyId} and state = 'held') as reserved`); + const pending = await tx.execute<{ runId: string; agentId: string; state: "waiting_for_receipt" | "retryable"; lastError: string | null; attempts: number; since: string; lastAttemptAt: string | null }>(sql` + select id as "runId", agent_id as "agentId", case when usage_json->>'accountingReceiptReady' = 'false' + and usage_json->>'accountingProviderWorkStarted' is distinct from 'false' + and result_json->'executionRecovery'->>'providerWorkStarted' is distinct from 'false' then 'waiting_for_receipt' else 'retryable' end as state, + accounting_last_error as "lastError", accounting_attempt_count as attempts, coalesce(finished_at,created_at)::text as since, accounting_last_attempt_at::text as "lastAttemptAt" + from heartbeat_runs where company_id = ${companyId} and cost_accounting_pending and status in ('succeeded','failed','timed_out','cancelled') order by coalesce(finished_at,created_at),id limit 100`); + const unpriced = await tx.execute<{ runId: string | null; costEventId: string; agentId: string; since: string }>(sql` + select heartbeat_run_id as "runId", id as "costEventId", agent_id as "agentId", occurred_at::text as since from cost_events + where company_id = ${companyId} and cost_status = 'unpriced' and billing_type <> 'subscription_included' order by occurred_at,id limit 100`); + return { companyId, pendingRunCount: counts.pending, unpricedEventCount: counts.unpriced, oldestPendingAt: counts.oldest, pendingCancellationCount: counts.cancellations, + heldReservationCents: normalizeCents(counts.reserved), items: [...pending, ...unpriced.map(row => ({ ...row, state: "unpriced" as const, lastError: null, attempts: 0, lastAttemptAt: null }))] }; + }), + }; +} diff --git a/server/src/services/accounting-transaction.ts b/server/src/services/accounting-transaction.ts new file mode 100644 index 00000000000..3d4fec7b826 --- /dev/null +++ b/server/src/services/accounting-transaction.ts @@ -0,0 +1,29 @@ +import { eq } from "drizzle-orm"; +import { companies, type Db } from "@paperclipai/db"; +import { publishActivity, type ActivityPublication } from "./activity-log.js"; +import { notFound } from "../errors.js"; +import { logger } from "../middleware/logger.js"; + +/** Serialize ledger and budget mutations in one company. Always acquire this + * before locking a run or a policy, so concurrent writers use one lock order. + * NO KEY UPDATE permits FK KEY SHARE locks held by recovery callers. */ +export async function withAccountingTransaction(db: Db, companyId: string, work: (tx: Db, publications: ActivityPublication[]) => Promise): Promise { + const publications: ActivityPublication[] = []; + const result = await db.transaction(async (transaction) => { + const tx = transaction as unknown as Db; + const [company] = await tx.select({ id: companies.id }).from(companies) + .where(eq(companies.id, companyId)).for("no key update"); + if (!company) throw notFound("Company not found"); + return work(tx, publications); + }); + publishAccountingActivities(companyId, publications); + return result; +} + +/** Database history is durable even when the live subscriber channel fails. */ +export function publishAccountingActivities(companyId: string, publications: ActivityPublication[]) { + for (const publication of publications) { + try { publishActivity(publication); } + catch (error) { logger.warn({ err: error, companyId }, "Accounting committed; live activity publication failed"); } + } +} diff --git a/server/src/services/agents.ts b/server/src/services/agents.ts index c0dd80270d9..07a025adce3 100644 --- a/server/src/services/agents.ts +++ b/server/src/services/agents.ts @@ -1,3 +1,6 @@ +import { budgetServiceInTransaction } from "./budgets.js"; +import { withAccountingTransaction } from "./accounting-transaction.js"; +import type { ActivityPublication } from "./activity-log.js"; import { agentAppearanceSchema, randomAgentAppearance, resolveAgentAppearance, agentAvatarUrl } from "@paperclipai/shared"; import { createHash, randomBytes } from "node:crypto"; import { and, desc, eq, gte, inArray, lt, ne, or, sql } from "drizzle-orm"; @@ -787,7 +790,7 @@ export function agentService(db: Db) { const beforeConfig = shouldRecordRevision ? buildConfigSnapshot(existing) : null; type AgentUpdateResult = Awaited>; - const applyUpdate = async (txDb: Db): Promise => { + const applyUpdate = async (txDb: Db, publications: ActivityPublication[] = []): Promise => { const updated = await txDb .update(agents) .set({ ...normalizedPatch, updatedAt: new Date() }) @@ -826,6 +829,11 @@ export function agentService(db: Db) { ); } + if (normalizedPatch.budgetMonthlyCents !== undefined) { + await budgetServiceInTransaction(txDb, publications).upsertPolicy(existing.companyId, { + scopeType: "agent", scopeId: id, amount: normalizedPatch.budgetMonthlyCents, windowKind: "calendar_month_utc", + }, options?.recordRevision?.createdByUserId ?? null); + } const normalizedUpdated = await agentService(txDb).getById(updated.id); if (!normalizedUpdated) { throw notFound("Agent not found"); @@ -852,6 +860,8 @@ export function agentService(db: Db) { return normalizedUpdated; }; + if (normalizedPatch.budgetMonthlyCents !== undefined) return withAccountingTransaction(db, existing.companyId, applyUpdate); + const transaction = (db as unknown as { transaction?: (callback: (tx: unknown) => Promise) => Promise; }).transaction; diff --git a/server/src/services/billing-reconciliation.ts b/server/src/services/billing-reconciliation.ts new file mode 100644 index 00000000000..21cec4110ed --- /dev/null +++ b/server/src/services/billing-reconciliation.ts @@ -0,0 +1,140 @@ +import { createFinanceEventInTransaction } from "./finance.js"; +import { and, desc, eq, sql } from "drizzle-orm"; +import { agentRuntimeState, costEvents, costAdjustments, billingInvoices, billingInvoiceLines, heartbeatRuns, type Db } from "@paperclipai/db"; +import { adjustCostSchema, importBillingInvoiceSchema, normalizeCents, subtractCents, type AdjustCost, type ImportBillingInvoice } from "@paperclipai/shared"; +import { conflict, notFound, unprocessable } from "../errors.js"; +import { withAccountingTransaction } from "./accounting-transaction.js"; +import { receiptFingerprint } from "./receipt-fingerprint.js"; +import { logActivity } from "./activity-log.js"; +import { updateMonthlySpendProjections } from "./costs.js"; +import { budgetServiceInTransaction, deliverBudgetEnforcement, type BudgetServiceHooks } from "./budgets.js"; + +export function billingReconciliationService(db: Db, hooks: BudgetServiceHooks = {}) { + return { + importInvoice: (companyId: string, raw: ImportBillingInvoice, actorId: string) => withAccountingTransaction(db, companyId, async (tx, publications) => { + const input = importBillingInvoiceSchema.parse(raw); + // Line order in an export is not part of invoice identity. + input.lines.sort((a,b) => a.externalId.localeCompare(b.externalId)); + const receiptHash = receiptFingerprint(input); + const [existing] = await tx.select().from(billingInvoices).where(and(eq(billingInvoices.companyId, companyId), eq(billingInvoices.biller, input.biller), eq(billingInvoices.externalId, input.externalId))); + if (existing) { + if (existing.receiptHash !== receiptHash) throw conflict("Invoice identifier already has different contents"); + return existing; + } + // Validate explicit references, even on non-USD invoices that we cannot + // compare with the USD cost ledger. Never infer cross-company matches. + for (const line of input.lines) { + if (line.costEventId) { + const [event] = await tx.select({ id: costEvents.id }).from(costEvents).where(and(eq(costEvents.companyId, companyId), eq(costEvents.id, line.costEventId))); + if (!event) throw notFound("Invoice cost event not found"); + } + if (line.runId) { + const [run] = await tx.select({ id: heartbeatRuns.id }).from(heartbeatRuns).where(and(eq(heartbeatRuns.companyId, companyId), eq(heartbeatRuns.id, line.runId))); + if (!run) throw notFound("Invoice run not found"); + } + } + const [invoice] = await tx.insert(billingInvoices).values({ companyId, biller: input.biller, externalId: input.externalId, currency: input.currency, receiptHash, importedBy: actorId }).returning(); + await tx.insert(billingInvoiceLines).values(input.lines.map(line => ({ ...line, companyId, invoiceId: invoice.id, occurredAt: new Date(line.occurredAt), pricing: line.pricing ?? {} }))); + for (const line of input.lines) { + await createFinanceEventInTransaction(tx, publications, companyId, { + idempotencyKey: `invoice:${invoice.id}:${receiptFingerprint(line.externalId)}`, + biller: input.biller, externalInvoiceId: input.externalId, + eventKind: line.kind === "inference" ? "inference_charge" : line.kind === "credit" ? "credit_refund" : "platform_fee", + direction: line.kind === "credit" ? "credit" : "debit", + amountCents: line.amountCents, currency: input.currency, occurredAt: new Date(line.occurredAt), + description: `Invoice ${input.externalId} · ${line.externalId}`.slice(0, 500), + costEventId: line.costEventId, heartbeatRunId: line.runId, + metadataJson: { source: "provider_invoice", invoiceId: invoice.id, externalLineId: line.externalId }, + }, { actorType: "user", actorId }); + } + await logActivity(tx, { companyId, actorType: "user", actorId, action: "accounting.invoice_imported", entityType: "billing_invoice", entityId: invoice.id, + details: { biller: invoice.biller, externalId: invoice.externalId, currency: invoice.currency, lineCount: input.lines.length, receiptHash } }, publications); + return invoice; + }), + list: (companyId: string) => db.select().from(billingInvoices).where(eq(billingInvoices.companyId, companyId)).orderBy(desc(billingInvoices.createdAt)).limit(100), + reconcile: (companyId: string, invoiceId: string) => withAccountingTransaction(db, companyId, async tx => { + const [invoice] = await tx.select().from(billingInvoices).where(and(eq(billingInvoices.companyId, companyId), eq(billingInvoices.id, invoiceId))); + if (!invoice) throw notFound("Invoice not found"); + const lines = await tx.select().from(billingInvoiceLines).where(and(eq(billingInvoiceLines.companyId, companyId), eq(billingInvoiceLines.invoiceId, invoiceId))).orderBy(billingInvoiceLines.externalId); + const rows = []; + for (const line of lines) { + const candidates = invoice.currency !== "USD" || line.kind !== "inference" || !(line.costEventId || line.runId || line.providerRequestId) ? [] : await tx.select({ + id: costEvents.id, cents: sql`${costEvents.costCents}::text`, status: costEvents.costStatus, + }).from(costEvents).where(and(eq(costEvents.companyId, companyId), eq(costEvents.biller, invoice.biller), + line.costEventId ? eq(costEvents.id, line.costEventId) : undefined, + line.runId ? eq(costEvents.heartbeatRunId, line.runId) : undefined, + line.providerRequestId ? eq(costEvents.providerRequestId, line.providerRequestId) : undefined, + line.model ? eq(costEvents.model, line.model) : undefined)).limit(2); + const match = candidates.length === 1 ? candidates[0] : null; + const differenceCents = match ? subtractCents(line.amountCents, match.cents) : null; + const status = invoice.currency !== "USD" ? "unsupported_currency" : line.kind !== "inference" ? "non_inference" + : candidates.length > 1 ? "ambiguous" : !match ? "unmatched" : differenceCents === "0.0000000" && match.status !== "unpriced" ? "matched" : "difference"; + rows.push({ ...line, amountCents: normalizeCents(line.amountCents), status, matchedEventId: match?.id ?? null, + recordedCents: match ? normalizeCents(match.cents) : null, differenceCents }); + } + // Multiple invoice lines cannot independently revalue the same charge. + const counts = new Map(); + for (const row of rows) if (row.matchedEventId) counts.set(row.matchedEventId, (counts.get(row.matchedEventId) ?? 0) + 1); + for (const row of rows) if (row.matchedEventId && counts.get(row.matchedEventId)! > 1) row.status = "ambiguous"; + return { invoice, lines: rows }; + }), + adjustments: (companyId: string, eventId: string) => db.select().from(costAdjustments).where(and(eq(costAdjustments.companyId, companyId), eq(costAdjustments.costEventId, eventId))).orderBy(desc(costAdjustments.createdAt)), + adjust: async (companyId: string, eventId: string, raw: AdjustCost, actorId: string) => { + const input = adjustCostSchema.parse(raw); + const result = await withAccountingTransaction(db, companyId, async (tx, publications) => { + const hash = receiptFingerprint({ eventId, ...input }); + const [existing] = await tx.select().from(costAdjustments).where(and(eq(costAdjustments.companyId, companyId), eq(costAdjustments.idempotencyKey, input.idempotencyKey))); + if (existing) { + if (existing.receiptHash !== hash) throw conflict("Correction key already has different contents"); + return existing; + } + const [event] = await tx.select({ event: costEvents, exact: sql`${costEvents.costCents}::text` }).from(costEvents) + .where(and(eq(costEvents.companyId, companyId), eq(costEvents.id, eventId))).for("update"); + if (!event) throw notFound("Cost event not found"); + if (normalizeCents(event.exact) !== input.expectedCents) throw conflict("Charge changed since review; review it again"); + if (input.invoiceLineId) { + const reportLine = await tx.select({ line: billingInvoiceLines, invoice: billingInvoices }).from(billingInvoiceLines) + .innerJoin(billingInvoices, eq(billingInvoices.id, billingInvoiceLines.invoiceId)).where(and(eq(billingInvoiceLines.id, input.invoiceLineId), eq(billingInvoiceLines.companyId, companyId), eq(billingInvoices.companyId, companyId))); + const row = reportLine[0]; + if (!row) throw notFound("Invoice line not found"); + if (row.invoice.currency !== "USD" || row.line.kind !== "inference" || row.invoice.biller !== event.event.biller + || normalizeCents(row.line.amountCents) !== input.correctedCents + || row.line.costEventId && row.line.costEventId !== eventId + || row.line.runId && row.line.runId !== event.event.heartbeatRunId + || row.line.providerRequestId && row.line.providerRequestId !== event.event.providerRequestId + || row.line.model && row.line.model !== event.event.model) throw unprocessable("Invoice line does not support this correction"); + const review = await billingReconciliationService(tx).reconcile(companyId, row.invoice.id); + const reviewedLine = review.lines.find(line => line.id === input.invoiceLineId); + if (!reviewedLine || !["matched", "difference"].includes(reviewedLine.status) || reviewedLine.matchedEventId !== eventId) + throw unprocessable("Invoice match is ambiguous or unverified; review an explicit correction without attaching this line"); + const [used] = await tx.select().from(costAdjustments).where(and(eq(costAdjustments.companyId, companyId), eq(costAdjustments.invoiceLineId, input.invoiceLineId))); + if (used) throw conflict("Invoice line has already been applied"); + } + const [adjustment] = await tx.insert(costAdjustments).values({ companyId, costEventId: eventId, invoiceLineId: input.invoiceLineId, + idempotencyKey: input.idempotencyKey, receiptHash: hash, previousCents: input.expectedCents, correctedCents: input.correctedCents, + previousStatus: event.event.costStatus, previousPricing: event.event.pricingProvenance ?? {}, + reason: input.reason, actorId, pricing: input.pricing }).returning(); + // Original provider value and fingerprint remain immutable. cost_cents + // is the effective valuation, backed by this append-only correction. + const [updated] = await tx.update(costEvents).set({ costCents: sql`${input.correctedCents}::numeric`, + reportedCostCents: event.event.reportedCostCents ?? normalizeCents(event.exact), costStatus: "reported", pricingProvenance: input.pricing, + }).where(eq(costEvents.id, eventId)).returning(); + await updateMonthlySpendProjections(tx, companyId, updated.agentId, subtractCents(input.correctedCents, input.expectedCents), updated.occurredAt); + if (updated.heartbeatRunId) { + const [run] = await tx.select().from(heartbeatRuns).where(and(eq(heartbeatRuns.id, updated.heartbeatRunId), eq(heartbeatRuns.companyId, companyId))); + if (run?.costAccountedAt && run.accountingProjectionVersion === "v2") await tx.update(agentRuntimeState) + .set({ totalCostCents: sql`${agentRuntimeState.totalCostCents} + ${subtractCents(input.correctedCents, input.expectedCents)}::numeric`, updatedAt: new Date() }) + .where(and(eq(agentRuntimeState.agentId, updated.agentId), eq(agentRuntimeState.companyId, companyId))); + } + const budgets = budgetServiceInTransaction(tx, publications); + await budgets.evaluateCostEvent(updated); + await budgets.getInvocationBlock(companyId, updated.agentId, { projectId: updated.projectId }); + await logActivity(tx, { companyId, actorType: "user", actorId, action: "accounting.charge_corrected", entityType: "cost_event", entityId: eventId, + details: { adjustmentId: adjustment.id, previousCents: input.expectedCents, correctedCents: input.correctedCents, reason: input.reason, pricing: input.pricing } }, publications); + return adjustment; + }); + await deliverBudgetEnforcement(db, hooks, companyId); + return result; + }, + }; +} diff --git a/server/src/services/budget-reservations.ts b/server/src/services/budget-reservations.ts new file mode 100644 index 00000000000..5c28c5aa99d --- /dev/null +++ b/server/src/services/budget-reservations.ts @@ -0,0 +1,45 @@ +import { and, eq, sql } from "drizzle-orm"; +import { budgetPolicies, budgetReservations, heartbeatRuns, type Db } from "@paperclipai/db"; +import { centsToUnits, unitsToCents } from "@paperclipai/shared"; +import { conflict, notFound } from "../errors.js"; +import { withAccountingTransaction } from "./accounting-transaction.js"; +import { budgetServiceInTransaction, computeObservedSpend } from "./budgets.js"; + +/** Reserve before dispatch, under the same company lock as charges and policy + * changes. Estimates constrain admission; they cannot cap a provider's bill. + * A reservation survives timeouts and restarts until accounting proves closure. */ +export async function reserveRunBudget(db: Db, companyId: string, runId: string, projectId: string | null, ledgerScope: Record = {}) { + return withAccountingTransaction(db, companyId, async (tx, publications) => { + const [run] = await tx.select().from(heartbeatRuns).where(and(eq(heartbeatRuns.id, runId), eq(heartbeatRuns.companyId, companyId))).for("update"); + if (!run) throw notFound("Run not found"); + if (run.costAccountedAt || !["queued", "running"].includes(run.status)) throw conflict("Run can no longer start provider work"); + const [existing] = await tx.select().from(budgetReservations).where(and(eq(budgetReservations.companyId, companyId), eq(budgetReservations.runId, runId))); + if (existing) throw conflict("Provider dispatch has already reserved this run"); + const block = await budgetServiceInTransaction(tx, publications).getInvocationBlock(companyId, run.agentId, { projectId }); + if (block) throw conflict(block.reason); + const candidates = await tx.select().from(budgetPolicies).where(and(eq(budgetPolicies.companyId, companyId), eq(budgetPolicies.isActive, true), eq(budgetPolicies.hardStopEnabled, true))); + const policies = candidates.filter(p => p.metric === "billed_cents" && p.amount > 0 && ( + p.scopeType === "company" && p.scopeId === companyId || p.scopeType === "agent" && p.scopeId === run.agentId || p.scopeType === "project" && p.scopeId === projectId)); + const amount = policies.reduce((max, policy) => { const next = centsToUnits(policy.reservationCents); return next > max ? next : max; }, 0n); + for (const policy of policies) { + const [held] = await tx.select({ amount: sql`coalesce(sum(${budgetReservations.amountCents}), 0)::text` }).from(budgetReservations).where(and( + eq(budgetReservations.companyId, companyId), eq(budgetReservations.state, "held"), + policy.scopeType === "agent" ? eq(budgetReservations.agentId, run.agentId) : undefined, + policy.scopeType === "project" ? eq(budgetReservations.projectId, projectId!) : undefined, + )); + const observed = await computeObservedSpend(tx, policy); + // Even zero-estimate runs cannot enter a scope whose available capacity + // is completely reserved. Outstanding reservations carry across months. + const committed = centsToUnits(observed.totalExact) + centsToUnits(held.amount); + const limit = centsToUnits(policy.amount); + if (committed >= limit || committed + amount > limit) throw conflict("Available budget is reserved by unfinished runs"); + } + // A zero-valued row also fences duplicate dispatch when estimates are off. + const [reservation] = await tx.insert(budgetReservations).values({ companyId, runId, agentId: run.agentId, projectId, + amountCents: unitsToCents(amount), providerStartedAt: new Date() }).returning(); + await tx.update(heartbeatRuns).set({ costAccountingPending: true, + usageJson: sql`coalesce(${heartbeatRuns.usageJson}, '{}'::jsonb) || ${JSON.stringify({ accountingReceiptReady: false, ledgerScope: { ...(run.usageJson?.ledgerScope as object ?? {}), ...ledgerScope, projectId } })}::jsonb`, + }).where(eq(heartbeatRuns.id, runId)); + return reservation; + }); +} diff --git a/server/src/services/budgets.ts b/server/src/services/budgets.ts index e281dcb51f3..4767a99b519 100644 --- a/server/src/services/budgets.ts +++ b/server/src/services/budgets.ts @@ -1,4 +1,5 @@ -import { and, desc, eq, gte, inArray, lt, ne, sql } from "drizzle-orm"; +import { compareCents, normalizeCents } from "@paperclipai/shared"; +import { and, desc, eq, gte, inArray, lt, ne, sql, or, isNull } from "drizzle-orm"; import type { Db } from "@paperclipai/db"; import { agents, @@ -7,6 +8,7 @@ import { budgetPolicies, companies, costEvents, + heartbeatRuns, projects, } from "@paperclipai/db"; import type { @@ -23,7 +25,9 @@ import type { BudgetWindowKind, } from "@paperclipai/shared"; import { notFound, unprocessable } from "../errors.js"; -import { logActivity } from "./activity-log.js"; +import { logger } from "../middleware/logger.js"; +import { withAccountingTransaction } from "./accounting-transaction.js"; +import { logActivity, type LogActivityInput, type ActivityPublication } from "./activity-log.js"; type ScopeRecord = { companyId: string; @@ -39,6 +43,8 @@ export type BudgetEnforcementScope = { companyId: string; scopeType: BudgetScopeType; scopeId: string; + /** Snapshot taken under the company lock, before a later budget grant can admit new work. */ + createdBefore?: Date; }; export type BudgetServiceHooks = { @@ -64,13 +70,13 @@ function resolveWindow(windowKind: BudgetWindowKind, now = new Date()) { } function budgetStatusFromObserved( - observedAmount: number, + observedAmount: string, amount: number, warnPercent: number, ): BudgetPolicySummary["status"] { if (amount <= 0) return "ok"; - if (observedAmount >= amount) return "hard_stop"; - if (observedAmount >= Math.ceil((amount * warnPercent) / 100)) return "warning"; + if (compareCents(observedAmount, amount) >= 0) return "hard_stop"; + if (compareCents(observedAmount, Math.ceil((amount * warnPercent) / 100)) >= 0) return "warning"; return "ok"; } @@ -140,11 +146,11 @@ async function resolveScopeRecord(db: Db, scopeType: BudgetScopeType, scopeId: s }; } -async function computeObservedAmount( +export async function computeObservedSpend( db: Db, policy: Pick, ) { - if (policy.metric !== "billed_cents") return 0; + if (policy.metric !== "billed_cents") return { total: 0, totalExact: "0.0000000", unpricedEventCount: 0, pendingRunCount: 0 }; const conditions = [eq(costEvents.companyId, policy.companyId)]; if (policy.scopeType === "agent") conditions.push(eq(costEvents.agentId, policy.scopeId)); @@ -157,12 +163,36 @@ async function computeObservedAmount( const [row] = await db .select({ - total: sql`coalesce(sum(${costEvents.costCents}), 0)::double precision`, + unpricedEventCount: sql`count(*) filter (where ${costEvents.costStatus} = 'unpriced' and ${costEvents.billingType} <> 'subscription_included')::int`, + total: sql`coalesce(sum(${costEvents.costCents}), 0)::text`, }) .from(costEvents) .where(and(...conditions)); - return Number(row?.total ?? 0); + const pendingConditions = [eq(heartbeatRuns.companyId, policy.companyId), eq(heartbeatRuns.costAccountingPending, true), + inArray(heartbeatRuns.status, ["succeeded", "failed", "timed_out", "cancelled"])]; + if (policy.scopeType === "agent") pendingConditions.push(eq(heartbeatRuns.agentId, policy.scopeId)); + if (policy.scopeType === "project") pendingConditions.push(sql`${heartbeatRuns.usageJson}->'ledgerScope'->>'projectId' = ${policy.scopeId}`); + if (policy.windowKind === "calendar_month_utc") { + pendingConditions.push(sql`coalesce(${heartbeatRuns.finishedAt}, ${heartbeatRuns.createdAt}) >= ${start.toISOString()}::timestamptz`, sql`coalesce(${heartbeatRuns.finishedAt}, ${heartbeatRuns.createdAt}) < ${end.toISOString()}::timestamptz`); + } + const [pending] = await db.select({ count: sql`count(*)::int` }).from(heartbeatRuns).where(and(...pendingConditions)); + return { total: Number(row?.total ?? 0), totalExact: normalizeCents(row?.total ?? 0), unpricedEventCount: Number(row?.unpricedEventCount ?? 0), pendingRunCount: pending?.count ?? 0 }; +} + +async function computeObservedAmount(db: Db, policy: PolicyRow) { + return (await computeObservedSpend(db, policy)).total; +} +async function reached(db: Db, policy: PolicyRow, threshold: number) { + return compareCents((await computeObservedSpend(db, policy)).totalExact, threshold) >= 0; +} +function observedBlocks(policy: PolicyRow, observed: Awaited>) { + if (!policy.isActive || !policy.hardStopEnabled || policy.amount <= 0) return false; + return compareCents(observed.totalExact, policy.amount) >= 0 || observed.pendingRunCount > 0 || (policy.unpricedUsagePolicy !== "allow" && observed.unpricedEventCount > 0); +} +async function policyBlocks(db: Db, policy: PolicyRow) { + if (!policy.isActive || !policy.hardStopEnabled || policy.amount <= 0) return false; + return observedBlocks(policy, await computeObservedSpend(db, policy)); } function buildApprovalPayload(input: { @@ -170,6 +200,7 @@ function buildApprovalPayload(input: { scopeName: string; thresholdType: BudgetThresholdType; amountObserved: number; + amountObservedExact: string; windowStart: Date; windowEnd: Date; }) { @@ -182,11 +213,12 @@ function buildApprovalPayload(input: { thresholdType: input.thresholdType, budgetAmount: input.policy.amount, observedAmount: input.amountObserved, + observedAmountExact: input.amountObservedExact, warnPercent: input.policy.warnPercent, windowStart: input.windowStart.toISOString(), windowEnd: input.windowEnd.toISOString(), policyId: input.policy.id, - guidance: "Raise the budget and resume the scope, or keep the scope paused.", + guidance: "Resolve pending or unpriced accounting, raise the budget, or keep the scope paused.", }; } @@ -210,7 +242,9 @@ async function markApprovalStatus( .where(eq(approvals.id, approvalId)); } -export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { +/** Internal: caller must hold the company accounting transaction lock. */ +export function budgetServiceInTransaction(db: Db, publications: ActivityPublication[] = []) { + const recordActivity = (input: LogActivityInput) => logActivity(db, input, publications); async function pauseScopeForBudget(policy: PolicyRow) { const now = new Date(); if (policy.scopeType === "agent") { @@ -234,7 +268,7 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { pausedAt: now, updatedAt: now, }) - .where(eq(projects.id, policy.scopeId)); + .where(and(eq(projects.id, policy.scopeId), or(isNull(projects.pausedAt), eq(projects.pauseReason, "budget")))); return; } @@ -246,19 +280,24 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { pausedAt: now, updatedAt: now, }) - .where(eq(companies.id, policy.scopeId)); + .where(and(eq(companies.id, policy.scopeId), or(eq(companies.status, "active"), and(eq(companies.status, "paused"), eq(companies.pauseReason, "budget"))))); } async function pauseAndCancelScopeForBudget(policy: PolicyRow) { await pauseScopeForBudget(policy); - await hooks.cancelWorkForScope?.({ - companyId: policy.companyId, - scopeType: policy.scopeType as BudgetScopeType, - scopeId: policy.scopeId, - }); + await db.update(budgetPolicies).set({ + enforcementVersion: sql`${budgetPolicies.enforcementVersion} + 1`, + }).where(eq(budgetPolicies.id, policy.id)); } async function resumeScopeFromBudget(policy: PolicyRow) { + const policies = await db.select().from(budgetPolicies).where(and( + eq(budgetPolicies.companyId, policy.companyId), eq(budgetPolicies.scopeType, policy.scopeType), + eq(budgetPolicies.scopeId, policy.scopeId), eq(budgetPolicies.isActive, true), eq(budgetPolicies.hardStopEnabled, true), + )); + for (const candidate of policies) { + if (await policyBlocks(db, candidate)) return; + } const now = new Date(); if (policy.scopeType === "agent") { await db @@ -269,7 +308,7 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { pausedAt: null, updatedAt: now, }) - .where(and(eq(agents.id, policy.scopeId), eq(agents.pauseReason, "budget"))); + .where(and(eq(agents.id, policy.scopeId), eq(agents.status, "paused"), eq(agents.pauseReason, "budget"))); return; } @@ -293,7 +332,7 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { pausedAt: null, updatedAt: now, }) - .where(and(eq(companies.id, policy.scopeId), eq(companies.pauseReason, "budget"))); + .where(and(eq(companies.id, policy.scopeId), eq(companies.status, "paused"), eq(companies.pauseReason, "budget"))); } async function getPolicyRow(policyId: string) { @@ -316,7 +355,7 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { async function buildPolicySummary(policy: PolicyRow): Promise { const scope = await resolveScopeRecord(db, policy.scopeType as BudgetScopeType, policy.scopeId); - const observedAmount = await computeObservedAmount(db, policy); + const { total: observedAmount, totalExact: observedAmountExact, unpricedEventCount, pendingRunCount } = await computeObservedSpend(db, policy); const { start, end } = resolveWindow(policy.windowKind as BudgetWindowKind); const amount = policy.isActive ? policy.amount : 0; const utilizationPercent = @@ -331,14 +370,19 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { windowKind: policy.windowKind as BudgetWindowKind, amount, observedAmount, + observedAmountExact, + reservationCents: policy.reservationCents, + unpricedEventCount, + pendingRunCount, + unpricedUsagePolicy: policy.unpricedUsagePolicy as "block" | "allow", remainingAmount: amount > 0 ? Math.max(0, amount - observedAmount) : 0, utilizationPercent, warnPercent: policy.warnPercent, hardStopEnabled: policy.hardStopEnabled, notifyEnabled: policy.notifyEnabled, isActive: policy.isActive, - status: policy.isActive - ? budgetStatusFromObserved(observedAmount, amount, policy.warnPercent) + status: await policyBlocks(db, policy) ? "hard_stop" : policy.isActive + ? budgetStatusFromObserved(observedAmountExact, amount, policy.warnPercent) : "ok", paused: scope.paused, pauseReason: scope.pauseReason, @@ -361,18 +405,21 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { eq(budgetIncidents.policyId, policy.id), eq(budgetIncidents.windowStart, start), eq(budgetIncidents.thresholdType, thresholdType), - ne(budgetIncidents.status, "dismissed"), + or(eq(budgetIncidents.status, "open"), and(eq(budgetIncidents.status, "dismissed"), eq(budgetIncidents.amountLimit, policy.amount))), ), ) + .orderBy(desc(budgetIncidents.createdAt)) .then((rows) => rows[0] ?? null); if (existing) return { incident: existing, created: false }; + const amountObservedExact = (await computeObservedSpend(db, policy)).totalExact; const scope = await resolveScopeRecord(db, policy.scopeType as BudgetScopeType, policy.scopeId); const payload = buildApprovalPayload({ policy, scopeName: normalizeScopeName(policy.scopeType as BudgetScopeType, scope.name), thresholdType, amountObserved, + amountObservedExact, windowStart: start, windowEnd: end, }); @@ -405,7 +452,7 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { windowEnd: end, thresholdType, amountLimit: policy.amount, - amountObserved, + amountObserved: sql`${amountObservedExact}::numeric`, status: "open", approvalId: approval?.id ?? null, }) @@ -435,11 +482,14 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { policyId: string, approvalStatus: "approved" | "rejected" | null, decidedByUserId: string | null, + thresholdType?: BudgetThresholdType, ) { + const condition = and(eq(budgetIncidents.policyId, policyId), eq(budgetIncidents.status, "open"), + thresholdType ? eq(budgetIncidents.thresholdType, thresholdType) : undefined); const openRows = await db .select() .from(budgetIncidents) - .where(and(eq(budgetIncidents.policyId, policyId), eq(budgetIncidents.status, "open"))); + .where(condition); await db .update(budgetIncidents) @@ -448,7 +498,7 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { resolvedAt: new Date(), updatedAt: new Date(), }) - .where(and(eq(budgetIncidents.policyId, policyId), eq(budgetIncidents.status, "open"))); + .where(condition); if (!approvalStatus || !decidedByUserId) return; for (const row of openRows) { @@ -494,12 +544,64 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { ); } + async function reconcileScope(companyId: string, scopeType: BudgetScopeType, scopeId: string) { + const scope = await resolveScopeRecord(db, scopeType, scopeId); + if (scope.companyId !== companyId) throw notFound("Budget scope not found"); + const policies = await db.select().from(budgetPolicies).where(and( + eq(budgetPolicies.companyId, companyId), eq(budgetPolicies.scopeType, scopeType), eq(budgetPolicies.scopeId, scopeId), + )); + for (const policy of policies) { + const observed = await computeObservedAmount(db, policy); + if (await policyBlocks(db, policy)) { + await createIncidentIfNeeded(policy, "hard", observed); + await pauseAndCancelScopeForBudget(policy); + const title = scopeType === "company" ? "Company" : scopeType === "agent" ? "Agent" : "Project"; + if ((await computeObservedSpend(db, policy)).pendingRunCount > 0) { + return { scopeType, scopeId, scopeName: scope.name, reason: `${title} cannot start work while completed runs await accounting.` }; + } + if (policy.unpricedUsagePolicy !== "allow" && (await computeObservedSpend(db, policy)).unpricedEventCount > 0) { + return { scopeType, scopeId, scopeName: scope.name, reason: `${title} cannot start work because recorded usage has no reliable price.` }; + } + return { scopeType, scopeId, scopeName: scope.name, reason: scopeType === "project" + ? "Project cannot start work because its budget hard-stop is still exceeded." + : `${title} is paused because its budget hard-stop was reached.` }; + } + // A closed UTC window, inactive policy, or budget raise releases only + // budget-owned pauses. Manual pauses remain operator decisions. + await resolveOpenIncidentsForPolicy(policy.id, "approved", "budget_service", "hard"); + if (!policy.isActive || !policy.notifyEnabled || !(await reached(db, policy, Math.ceil(policy.amount * policy.warnPercent / 100)))) { + await resolveOpenIncidentsForPolicy(policy.id, "approved", "budget_service", "soft"); + } + } + // Agent status has its own admission gate and error code. Preserve a + // manual pause without misclassifying it as a budget failure. + if (scopeType !== "agent" && scope.paused && scope.pauseReason !== "budget") { + return { scopeType, scopeId, scopeName: scope.name, reason: `${scopeType === "company" ? "Company" : "Project"} is paused and cannot start new work.` }; + } + if (scope.paused && scope.pauseReason === "budget" && !policies.length) { + return { scopeType, scopeId, scopeName: scope.name, reason: "Budget pause requires a policy or an explicit operator resume." }; + } + if (scope.pauseReason === "budget" && policies[0]) await resumeScopeFromBudget(policies[0]); + return null; + } + return { + reconcileCompanyScopes: async (companyId: string) => { + const policies = await listPolicyRows(companyId); + const seen = new Set(); + for (const policy of policies) { + const key = `${policy.scopeType}:${policy.scopeId}`; + if (seen.has(key)) continue; + seen.add(key); + await reconcileScope(companyId, policy.scopeType as BudgetScopeType, policy.scopeId); + } + }, listPolicies: async (companyId: string): Promise => { const rows = await listPolicyRows(companyId); return rows.map((row) => ({ ...row, scopeType: row.scopeType as BudgetScopeType, + unpricedUsagePolicy: row.unpricedUsagePolicy as "block" | "allow", metric: row.metric as BudgetMetric, windowKind: row.windowKind as BudgetWindowKind, })); @@ -515,6 +617,7 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { throw unprocessable("Budget scope does not belong to company"); } + if (input.reservationCents !== undefined && compareCents(input.reservationCents, 0) < 0) throw unprocessable("Reservation cannot be negative"); const metric = input.metric ?? "billed_cents"; const windowKind = input.windowKind ?? (input.scopeType === "project" ? "lifetime" : "calendar_month_utc"); const amount = Math.max(0, Math.floor(input.amount)); @@ -539,9 +642,11 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { .update(budgetPolicies) .set({ amount, + reservationCents: normalizeCents(input.reservationCents ?? existing.reservationCents ?? 0), warnPercent: input.warnPercent ?? existing.warnPercent, hardStopEnabled: input.hardStopEnabled ?? existing.hardStopEnabled, notifyEnabled: input.notifyEnabled ?? existing.notifyEnabled, + unpricedUsagePolicy: input.unpricedUsagePolicy ?? existing.unpricedUsagePolicy, isActive: nextIsActive, updatedByUserId: actorUserId, updatedAt: now, @@ -558,9 +663,11 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { metric, windowKind, amount, + reservationCents: normalizeCents(input.reservationCents ?? 0), warnPercent: input.warnPercent ?? 80, hardStopEnabled: input.hardStopEnabled ?? true, notifyEnabled: input.notifyEnabled ?? true, + unpricedUsagePolicy: input.unpricedUsagePolicy ?? "block", isActive: nextIsActive, createdByUserId: actorUserId, updatedByUserId: actorUserId, @@ -588,17 +695,22 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { .where(eq(agents.id, input.scopeId)); } - if (amount > 0) { + if (row.isActive && amount > 0) { const observedAmount = await computeObservedAmount(db, row); - if (observedAmount < amount) { + if (!(await policyBlocks(db, row))) { await resumeScopeFromBudget(row); - await resolveOpenIncidentsForPolicy(row.id, actorUserId ? "approved" : null, actorUserId); + await resolveOpenIncidentsForPolicy(row.id, "approved", actorUserId ?? "budget_service", "hard"); + if (row.notifyEnabled && (await reached(db, row, Math.ceil(row.amount * row.warnPercent / 100)))) { + await createIncidentIfNeeded(row, "soft", observedAmount); + } else { + await resolveOpenIncidentsForPolicy(row.id, null, null, "soft"); + } } else { const softThreshold = Math.ceil((row.amount * row.warnPercent) / 100); - if (row.notifyEnabled && observedAmount >= softThreshold) { + if (row.notifyEnabled && (await reached(db, row, softThreshold)) && (!row.hardStopEnabled || !(await reached(db, row, row.amount)))) { await createIncidentIfNeeded(row, "soft", observedAmount); } - if (row.hardStopEnabled && observedAmount >= row.amount) { + if (await policyBlocks(db, row)) { await resolveOpenSoftIncidents(row.id); await createIncidentIfNeeded(row, "hard", observedAmount); await pauseAndCancelScopeForBudget(row); @@ -606,10 +718,10 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { } } else { await resumeScopeFromBudget(row); - await resolveOpenIncidentsForPolicy(row.id, actorUserId ? "approved" : null, actorUserId); + await resolveOpenIncidentsForPolicy(row.id, "approved", actorUserId ?? "budget_service"); } - await logActivity(db, { + await recordActivity({ companyId, actorType: "user", actorId: actorUserId ?? "board", @@ -640,8 +752,8 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { companyId, policies, activeIncidents, - pausedAgentCount: policies.filter((policy) => policy.scopeType === "agent" && policy.paused).length, - pausedProjectCount: policies.filter((policy) => policy.scopeType === "project" && policy.paused).length, + pausedAgentCount: new Set(policies.filter((policy) => policy.scopeType === "agent" && policy.paused).map((policy) => policy.scopeId)).size, + pausedProjectCount: new Set(policies.filter((policy) => policy.scopeType === "project" && policy.paused).map((policy) => policy.scopeId)).size, pendingApprovalCount: activeIncidents.filter((incident) => incident.approvalStatus === "pending").length, }; }, @@ -667,13 +779,14 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { for (const policy of relevantPolicies) { if (policy.metric !== "billed_cents" || policy.amount <= 0) continue; - const observedAmount = await computeObservedAmount(db, policy); + const observed = await computeObservedSpend(db, policy); + const observedAmount = observed.total; const softThreshold = Math.ceil((policy.amount * policy.warnPercent) / 100); - if (policy.notifyEnabled && observedAmount >= softThreshold) { + if (policy.notifyEnabled && compareCents(observed.totalExact, softThreshold) >= 0 && (!policy.hardStopEnabled || compareCents(observed.totalExact, policy.amount) < 0)) { const softIncident = await createIncidentIfNeeded(policy, "soft", observedAmount); if (softIncident?.created) { - await logActivity(db, { + await recordActivity({ companyId: policy.companyId, actorType: "system", actorId: "budget_service", @@ -690,12 +803,12 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { } } - if (policy.hardStopEnabled && observedAmount >= policy.amount) { + if (observedBlocks(policy, observed)) { await resolveOpenSoftIncidents(policy.id); const hardIncident = await createIncidentIfNeeded(policy, "hard", observedAmount); await pauseAndCancelScopeForBudget(policy); if (hardIncident?.created) { - await logActivity(db, { + await recordActivity({ companyId: policy.companyId, actorType: "system", actorId: "budget_service", @@ -720,147 +833,18 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { agentId: string, context?: { issueId?: string | null; projectId?: string | null }, ) => { - const agent = await db - .select({ - status: agents.status, - pauseReason: agents.pauseReason, - companyId: agents.companyId, - name: agents.name, - }) - .from(agents) - .where(eq(agents.id, agentId)) - .then((rows) => rows[0] ?? null); - if (!agent || agent.companyId !== companyId) throw notFound("Agent not found"); - - const company = await db - .select({ - status: companies.status, - pauseReason: companies.pauseReason, - name: companies.name, - }) - .from(companies) - .where(eq(companies.id, companyId)) - .then((rows) => rows[0] ?? null); - if (!company) throw notFound("Company not found"); - if (company.status === "paused") { - return { - scopeType: "company" as const, - scopeId: companyId, - scopeName: company.name, - reason: - company.pauseReason === "budget" - ? "Company is paused because its budget hard-stop was reached." - : "Company is paused and cannot start new work.", - }; + const scopes: Array<{ scopeType: BudgetScopeType; scopeId: string }> = [ + { scopeType: "company", scopeId: companyId }, { scopeType: "agent", scopeId: agentId }, + ...(context?.projectId ? [{ scopeType: "project" as const, scopeId: context.projectId }] : []), + ]; + for (const { scopeType, scopeId } of scopes) { + if ((await resolveScopeRecord(db, scopeType, scopeId)).companyId !== companyId) throw notFound("Budget scope not found"); } - - const companyPolicy = await db - .select() - .from(budgetPolicies) - .where( - and( - eq(budgetPolicies.companyId, companyId), - eq(budgetPolicies.scopeType, "company"), - eq(budgetPolicies.scopeId, companyId), - eq(budgetPolicies.isActive, true), - eq(budgetPolicies.metric, "billed_cents"), - ), - ) - .then((rows) => rows[0] ?? null); - if (companyPolicy && companyPolicy.hardStopEnabled && companyPolicy.amount > 0) { - const observed = await computeObservedAmount(db, companyPolicy); - if (observed >= companyPolicy.amount) { - return { - scopeType: "company" as const, - scopeId: companyId, - scopeName: company.name, - reason: "Company cannot start new work because its budget hard-stop is exceeded.", - }; - } + for (const { scopeType, scopeId } of scopes) { + const block = await reconcileScope(companyId, scopeType, scopeId); + if (block) return block; } - - if (agent.status === "paused" && agent.pauseReason === "budget") { - return { - scopeType: "agent" as const, - scopeId: agentId, - scopeName: agent.name, - reason: "Agent is paused because its budget hard-stop was reached.", - }; - } - - const agentPolicy = await db - .select() - .from(budgetPolicies) - .where( - and( - eq(budgetPolicies.companyId, companyId), - eq(budgetPolicies.scopeType, "agent"), - eq(budgetPolicies.scopeId, agentId), - eq(budgetPolicies.isActive, true), - eq(budgetPolicies.metric, "billed_cents"), - ), - ) - .then((rows) => rows[0] ?? null); - if (agentPolicy && agentPolicy.hardStopEnabled && agentPolicy.amount > 0) { - const observed = await computeObservedAmount(db, agentPolicy); - if (observed >= agentPolicy.amount) { - return { - scopeType: "agent" as const, - scopeId: agentId, - scopeName: agent.name, - reason: "Agent cannot start because its budget hard-stop is still exceeded.", - }; - } - } - - const candidateProjectId = context?.projectId ?? null; - if (!candidateProjectId) return null; - - const project = await db - .select({ - id: projects.id, - name: projects.name, - companyId: projects.companyId, - pauseReason: projects.pauseReason, - pausedAt: projects.pausedAt, - }) - .from(projects) - .where(eq(projects.id, candidateProjectId)) - .then((rows) => rows[0] ?? null); - - if (!project || project.companyId !== companyId) return null; - const projectPolicy = await db - .select() - .from(budgetPolicies) - .where( - and( - eq(budgetPolicies.companyId, companyId), - eq(budgetPolicies.scopeType, "project"), - eq(budgetPolicies.scopeId, project.id), - eq(budgetPolicies.isActive, true), - eq(budgetPolicies.metric, "billed_cents"), - ), - ) - .then((rows) => rows[0] ?? null); - if (projectPolicy && projectPolicy.hardStopEnabled && projectPolicy.amount > 0) { - const observed = await computeObservedAmount(db, projectPolicy); - if (observed >= projectPolicy.amount) { - return { - scopeType: "project" as const, - scopeId: project.id, - scopeName: project.name, - reason: "Project cannot start work because its budget hard-stop is still exceeded.", - }; - } - } - - if (!project.pausedAt || project.pauseReason !== "budget") return null; - return { - scopeType: "project" as const, - scopeId: project.id, - scopeName: project.name, - reason: "Project is paused because its budget hard-stop was reached.", - }; + return null; }, resolveIncident: async ( @@ -881,7 +865,11 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { if (input.action === "raise_budget_and_resume") { const nextAmount = Math.max(0, Math.floor(input.amount ?? 0)); const currentObserved = await computeObservedAmount(db, policy); - if (nextAmount <= currentObserved) { + if (policy.unpricedUsagePolicy !== "allow" && (await computeObservedSpend(db, policy)).unpricedEventCount > 0) { + throw unprocessable("Resolve unpriced usage or explicitly allow it in the budget policy before resuming"); + } + if ((await computeObservedSpend(db, policy)).pendingRunCount > 0) throw unprocessable("Completed runs must finish accounting before resuming"); + if (await reached(db, policy, nextAmount)) { throw unprocessable("New budget must exceed current observed spend"); } @@ -933,7 +921,7 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { await markApprovalStatus(db, incident.approvalId ?? null, "rejected", input.decisionNote, actorUserId); } - await logActivity(db, { + await recordActivity({ companyId: incident.companyId, actorType: "user", actorId: actorUserId, @@ -958,3 +946,52 @@ export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { }, }; } + +/** Cancellation is an at-least-once external effect. A failed delivery never + * rolls back committed spend, and its version remains pending for recovery. */ +export async function deliverBudgetEnforcement(db: Db, hooks: BudgetServiceHooks, companyId?: string) { + if (!hooks.cancelWorkForScope) return; + const pending = await db.select().from(budgetPolicies).where(and( + sql`${budgetPolicies.enforcementVersion} > ${budgetPolicies.enforcementDeliveredVersion}`, + companyId ? eq(budgetPolicies.companyId, companyId) : undefined, + )); + for (const policy of pending) { + try { + const cancellation = await withAccountingTransaction(db, policy.companyId, async (tx) => { + const [current] = await tx.select().from(budgetPolicies).where(eq(budgetPolicies.id, policy.id)); + if (!current || current.enforcementVersion !== policy.enforcementVersion || !(await policyBlocks(tx, current))) return null; + return { companyId: policy.companyId, scopeType: policy.scopeType as BudgetScopeType, scopeId: policy.scopeId, createdBefore: new Date() }; + }); + if (cancellation) await hooks.cancelWorkForScope(cancellation); + await db.update(budgetPolicies).set({ enforcementDeliveredVersion: policy.enforcementVersion }) + .where(and(eq(budgetPolicies.id, policy.id), eq(budgetPolicies.enforcementVersion, policy.enforcementVersion))); + } catch (error) { + logger.warn({ err: error, policyId: policy.id }, "Budget cancellation delivery pending; accounting is committed"); + } + } +} + +export function budgetService(db: Db, hooks: BudgetServiceHooks = {}) { + const reads = budgetServiceInTransaction(db); + async function mutate(companyId: string, work: (service: ReturnType) => Promise) { + const result = await withAccountingTransaction(db, companyId, (tx, publications) => work(budgetServiceInTransaction(tx, publications))); + await deliverBudgetEnforcement(db, hooks, companyId); + return result; + } + return { + ...reads, + deliverPendingEnforcement: (companyId?: string) => deliverBudgetEnforcement(db, hooks, companyId), + reconcilePolicies: async () => { + const rows = await db.selectDistinct({ companyId: budgetPolicies.companyId }).from(budgetPolicies); + for (const row of rows) await mutate(row.companyId, (service) => service.reconcileCompanyScopes(row.companyId)); + }, + upsertPolicy: (companyId: string, input: BudgetPolicyUpsertInput, actorUserId: string | null) => + mutate(companyId, (service) => service.upsertPolicy(companyId, input, actorUserId)), + evaluateCostEvent: (event: typeof costEvents.$inferSelect) => + mutate(event.companyId, (service) => service.evaluateCostEvent(event)), + getInvocationBlock: (companyId: string, agentId: string, context?: { issueId?: string | null; projectId?: string | null }) => + mutate(companyId, (service) => service.getInvocationBlock(companyId, agentId, context)), + resolveIncident: (companyId: string, incidentId: string, input: BudgetIncidentResolutionInput, actorUserId: string) => + mutate(companyId, (service) => service.resolveIncident(companyId, incidentId, input, actorUserId)), + }; +} diff --git a/server/src/services/codex-pricing.ts b/server/src/services/codex-pricing.ts new file mode 100644 index 00000000000..13adce53749 --- /dev/null +++ b/server/src/services/codex-pricing.ts @@ -0,0 +1,97 @@ +import { centsToUsd, unitsToCents, usdToUnits } from "@paperclipai/shared"; +import type { AdapterUsageCheckpoint } from "@paperclipai/adapter-utils"; + +// Immutable, reviewed rate card. Existing receipts retain their selected rates. +// https://developers.openai.com/api/docs/pricing (retrieved 2026-09-30) +const VERSION = "openai-standard-2026-09-30"; +const RATES: Record = { + "gpt-6-astra": ["10", "1", "12.5", "50"], + "gpt-6-sol": ["2", "0.2", "2.5", "10"], + "gpt-6.1-sol": ["2", "0.1", "2.5", "10"], + "gpt-6-luna": ["0.1", "0.01", "0.125", "0.5"], + "gpt-5.6-sol": ["4", "0.4", "5", "20"], +}; + +/** Price new, direct-API receipts only. A token-price estimate is not an invoice. + * Run totals cannot establish per-request context length or cache-write splits; + * preserve those assumptions instead of inferring a long request from a long run. */ +export function priceCodexReceipt( + receipt: AdapterUsageCheckpoint, +): AdapterUsageCheckpoint { + if ( + receipt.costUsd != null || + receipt.costUsdExact != null || + receipt.cacheAdjustedCostUsd != null || + receipt.provider !== "openai" || + receipt.biller !== "openai" || + !["api", "metered_api"].includes(receipt.billingType ?? "") || + receipt.usageBasis !== "per_run" || + !receipt.usage || + receipt.usageByModel?.length + ) + return receipt; + const model = receipt.model ?? ""; + const rates = Object.hasOwn(RATES, model) ? RATES[model] : undefined; + if (!rates) return receipt; + const usage = receipt.usage; + const counts = [ + usage.inputTokens - (usage.cacheWriteTokens ?? 0), + usage.cachedInputTokens ?? 0, + usage.cacheWriteTokens ?? 0, + usage.outputTokens, + ]; + if (counts.some((n) => !Number.isSafeInteger(n) || n < 0)) return receipt; + const context = receipt.pricingContext; + if ( + context?.serviceTier && + !["standard", "default", "priority", "fast", "flex", "batch"].includes( + context.serviceTier, + ) + ) + return receipt; + const tierNumerator = + context?.serviceTier === "priority" || context?.serviceTier === "fast" + ? 2n + : 1n; + const tierDenominator = + context?.serviceTier === "flex" || context?.serviceTier === "batch" + ? 2n + : 1n; + const units = counts.reduce((total, count, index) => { + const longNumerator = + context?.contextTier === "long" ? (index === 3 ? 3n : 4n) : 2n; + return ( + total + + BigInt(count) * usdToUnits(rates[index]) * tierNumerator * longNumerator + ); + }, 0n); + const divisor = 1_000_000n * tierDenominator * 2n; + const costCents = unitsToCents((units + divisor / 2n) / divisor); + const assumptions = [ + !context?.serviceTier + ? "standard processing assumed" + : `${context.serviceTier} processing`, + !context?.contextTier + ? "short per-request context assumed" + : `${context.contextTier} per-request context`, + usage.cacheWriteTokens === undefined + ? "cache writes not separately reported" + : "cache writes reported", + ]; + return { + ...receipt, + costUsdExact: centsToUsd(costCents), + costStatus: "estimated", + pricingProvenance: { + source: "rate_card", + version: VERSION, + evidence: `https://developers.openai.com/api/docs/pricing; ${assumptions.join("; ")}`, + inputCentsPerMillion: unitsToCents(usdToUnits(rates[0])), + cachedInputCentsPerMillion: unitsToCents(usdToUnits(rates[1])), + cacheWriteCentsPerMillion: unitsToCents(usdToUnits(rates[2])), + outputCentsPerMillion: unitsToCents(usdToUnits(rates[3])), + serviceTier: context?.serviceTier ?? "standard", + contextTier: context?.contextTier ?? "short", + }, + }; +} diff --git a/server/src/services/companies.ts b/server/src/services/companies.ts index b0838f14aca..841c4008337 100644 --- a/server/src/services/companies.ts +++ b/server/src/services/companies.ts @@ -1,3 +1,5 @@ +import { publishAccountingActivities } from "./accounting-transaction.js"; +import { budgetServiceInTransaction } from "./budgets.js"; import { and, count, eq, gte, inArray, isNull, lt, notInArray, sql } from "drizzle-orm"; import type { Db } from "@paperclipai/db"; import { @@ -47,7 +49,7 @@ import { } from "./issue-prefix.js"; import { environmentService } from "./environments.js"; import { heartbeatService } from "./heartbeat.js"; -import { logActivity } from "./activity-log.js"; +import { logActivity, type ActivityPublication } from "./activity-log.js"; import { builtInAgentService } from "./built-in-agents.js"; @@ -332,12 +334,16 @@ export function companyService(db: Db) { data: Partial & { logoAssetId?: string | null }, actor: CompanyActivityActor = SYSTEM_COMPANY_ACTOR, ) => { + const budgetPublications: ActivityPublication[] = []; const result = await db.transaction(async (tx) => { const existing = await getCompanyQuery(tx) .where(eq(companies.id, id)) .then((rows) => rows[0] ?? null); if (!existing) return null; + if (data.budgetMonthlyCents !== undefined) { + await tx.select({ id: companies.id }).from(companies).where(eq(companies.id, id)).for("no key update"); + } const { logoAssetId, ...companyPatch } = data; const willReactivate = existing.status !== "active" && companyPatch.status === "active"; const willArchive = existing.status !== "archived" && companyPatch.status === "archived"; @@ -431,6 +437,13 @@ export function companyService(db: Db) { await tx.delete(assets).where(eq(assets.id, existing.logoAssetId)); } + if (data.budgetMonthlyCents !== undefined) { + await budgetServiceInTransaction(tx as unknown as Db, budgetPublications).upsertPolicy(id, { + scopeType: "company", scopeId: id, amount: data.budgetMonthlyCents, windowKind: "calendar_month_utc", + }, actor.actorType === "user" ? actor.actorId : null); + const [budgetUpdated] = await tx.select().from(companies).where(eq(companies.id, id)); + Object.assign(updated, budgetUpdated); + } const [hydrated] = await hydrateCompanySpend([{ ...updated, logoAssetId: logoAssetId === undefined ? existing.logoAssetId : logoAssetId, @@ -448,6 +461,7 @@ export function companyService(db: Db) { }; }); if (!result) return null; + publishAccountingActivities(id, budgetPublications); // Post-commit, fire-and-forget, and BEFORE any finalization that // could throw: a Cloud-pinned primary company that crossed the // archived boundary (either direction) rings the harness so the diff --git a/server/src/services/company-skills.ts b/server/src/services/company-skills.ts index 1392c69fecb..9ed53be8512 100644 --- a/server/src/services/company-skills.ts +++ b/server/src/services/company-skills.ts @@ -6598,7 +6598,7 @@ export function companySkillService(db: Db) { const rows = await db .select({ issueId: costEvents.issueId, - costCents: sql`coalesce(sum(${costEvents.costCents}), 0)::int`, + costCents: sql`coalesce(sum(${costEvents.costCents}), 0)::double precision`, inputTokens: sql`coalesce(sum(${costEvents.inputTokens}), 0)::int`, cachedInputTokens: sql`coalesce(sum(${costEvents.cachedInputTokens}), 0)::int`, outputTokens: sql`coalesce(sum(${costEvents.outputTokens}), 0)::int`, diff --git a/server/src/services/cost-date-range.ts b/server/src/services/cost-date-range.ts new file mode 100644 index 00000000000..c7f60b9006e --- /dev/null +++ b/server/src/services/cost-date-range.ts @@ -0,0 +1,12 @@ +export interface CostDateRange { + from?: Date; + to?: Date; + allTime?: boolean; +} + +/** Default reports match the UTC month used by monetary budgets. Explicit + * bounds preserve the existing inclusive API; all-time must be requested. */ +export function resolveCostDateRange(range?: CostDateRange, now = new Date()): CostDateRange { + if (range?.allTime || range?.from || range?.to) return range; + return { from: new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)), to: now }; +} diff --git a/server/src/services/costs.ts b/server/src/services/costs.ts index 1dd0144f7d7..fe1a583900d 100644 --- a/server/src/services/costs.ts +++ b/server/src/services/costs.ts @@ -1,16 +1,17 @@ -import { agentAvatarUrl, resolveAgentAppearance } from "@paperclipai/shared"; -import { and, desc, eq, gte, isNotNull, isNull, lt, lte, sql } from "drizzle-orm"; +import { receiptFingerprint } from "./receipt-fingerprint.js"; +import { agentAvatarUrl, resolveAgentAppearance, createCostEventSchema, normalizeCents, type MoneyInput } from "@paperclipai/shared"; +import { and, desc, eq, gte, inArray, isNotNull, isNull, lt, lte, sql } from "drizzle-orm"; import { alias } from "drizzle-orm/pg-core"; import type { Db } from "@paperclipai/db"; -import { activityLog, agents, companies, costEvents, heartbeatRuns, issues, projects } from "@paperclipai/db"; -import { notFound, unprocessable } from "../errors.js"; -import { budgetService, type BudgetServiceHooks } from "./budgets.js"; +import { activityLog, agents, companies, costEvents, heartbeatRuns, issues, projects, goals } from "@paperclipai/db"; +import { notFound, unprocessable, conflict } from "../errors.js"; +import { budgetService, budgetServiceInTransaction, type BudgetServiceHooks } from "./budgets.js"; +import { logActivity, type LogActivityInput, type ActivityPublication } from "./activity-log.js"; +import { withAccountingTransaction } from "./accounting-transaction.js"; import { visibleIssueCondition } from "./issue-visibility.js"; -export interface CostDateRange { - from?: Date; - to?: Date; -} +import { resolveCostDateRange, type CostDateRange } from "./cost-date-range.js"; +export type { CostDateRange } from "./cost-date-range.js"; const METERED_BILLING_TYPE = "metered_api"; const SUBSCRIPTION_BILLING_TYPES = ["subscription_included", "subscription_overage"] as const; @@ -28,6 +29,26 @@ function currentUtcMonthWindow(now = new Date()) { }; } +/** Increment an initialized month under the company lock. On upgrade or UTC + * rollover, initialize from the ledger in the same transaction exactly once. + * Reports, budgets and the integrity checker continue reading the ledger. */ +export async function updateMonthlySpendProjections(db: Db, companyId: string, agentId: string, amountCents: MoneyInput, occurredAt: Date) { + const { start, end } = currentUtcMonthWindow(); + const month = start.toISOString().slice(0, 10); + const delta = occurredAt >= start && occurredAt < end ? normalizeCents(amountCents) : "0.0000000"; + for (const table of [companies, agents] as const) { + const id = table === companies ? companyId : agentId; + const companyCondition = table === companies ? eq(companies.id, companyId) : eq(agents.companyId, companyId); + await db.update(table).set({ + spentMonthlyCents: sql`case when ${table.spendMonthUtc} = ${month} then ${table.spentMonthlyCents} + ${delta}::numeric else ( + select coalesce(sum(${costEvents.costCents}),0) from ${costEvents} where ${costEvents.companyId} = ${companyId} + and ${costEvents.occurredAt} >= ${start.toISOString()}::timestamptz and ${costEvents.occurredAt} < ${end.toISOString()}::timestamptz + ${table === agents ? sql`and ${costEvents.agentId} = ${agentId}` : sql``}) end`, + spendMonthUtc: month, updatedAt: new Date(), + }).where(and(eq(table.id, id), companyCondition)); + } +} + export async function getMonthlySpendTotal( db: Db, scope: { companyId: string; agentId?: string | null }, @@ -50,56 +71,69 @@ export async function getMonthlySpendTotal( return Number(row?.total ?? 0); } +export async function createCostEventInTransaction(db: Db, companyId: string, data: Omit & { costCents: MoneyInput }, publications: ActivityPublication[] = [], actor?: Pick) { + const parsed = createCostEventSchema.safeParse({ ...data, occurredAt: data.occurredAt.toISOString() }); + if (!parsed.success) throw unprocessable("Invalid cost receipt", parsed.error.flatten()); + const values = { + ...parsed.data, + issueId: parsed.data.issueId ?? null, + projectId: parsed.data.projectId ?? null, + goalId: parsed.data.goalId ?? null, + heartbeatRunId: parsed.data.heartbeatRunId ?? null, + billingCode: parsed.data.billingCode ?? null, + idempotencyKey: parsed.data.idempotencyKey ?? null, + costCents: normalizeCents(parsed.data.costCents), + occurredAt: new Date(parsed.data.occurredAt), + }; + const receiptHash = receiptFingerprint(values); + // Older receipts hashed the numeric API value. Retain replay compatibility + // only when the decimal value survives that representation exactly. + let legacyHash: string | null = null; + try { + if (normalizeCents(Number(values.costCents)) === values.costCents) legacyHash = receiptFingerprint({ ...values, costCents: Number(values.costCents) }); + } catch { /* The legacy number may round beyond the decimal storage limit. */ } + if (values.idempotencyKey) { + const [existing] = await db.select().from(costEvents).where(and( + eq(costEvents.companyId, companyId), eq(costEvents.idempotencyKey, values.idempotencyKey), + )); + if (existing) { + if (existing.receiptHash !== receiptHash && existing.receiptHash !== legacyHash) throw conflict("Idempotency key already used for a different cost receipt"); + const [exact] = await db.select({ costCents: sql`${costEvents.costCents}::text` }).from(costEvents).where(eq(costEvents.id, existing.id)); + return { ...existing, costCentsExact: normalizeCents(exact.costCents) }; + } + } + const links = [ + [agents, values.agentId, "Agent"], [issues, values.issueId, "Issue"], + [projects, values.projectId, "Project"], [goals, values.goalId, "Goal"], + [heartbeatRuns, values.heartbeatRunId, "Heartbeat run"], + ] as const; + for (const [table, id, label] of links) { + if (!id) continue; + const [row] = await db.select({ companyId: table.companyId }).from(table).where(eq(table.id, id)); + if (!row) throw notFound(`${label} not found`); + if (row.companyId !== companyId) throw unprocessable(`${label} does not belong to company`); + } + if (values.heartbeatRunId) { + const [run] = await db.select({ agentId: heartbeatRuns.agentId }).from(heartbeatRuns).where(eq(heartbeatRuns.id, values.heartbeatRunId)); + if (run.agentId !== values.agentId) throw unprocessable("Heartbeat run does not belong to agent"); + } + const [event] = await db.insert(costEvents).values({ ...values, costCents: sql`${values.costCents}::numeric`, reportedCostCents: values.costCents, id: data.id, companyId, receiptHash }).returning(); + await updateMonthlySpendProjections(db, companyId, event.agentId, values.costCents, event.occurredAt); + await budgetServiceInTransaction(db, publications).evaluateCostEvent(event); + await logActivity(db, { + companyId, actorType: actor?.actorType ?? "system", actorId: actor?.actorId ?? "cost_accounting", agentId: actor?.agentId ?? event.agentId, + runId: event.heartbeatRunId, action: "cost.reported", entityType: "cost_event", entityId: event.id, + details: { costCents: event.costCents, costCentsExact: values.costCents, model: event.model, costStatus: event.costStatus }, + }, publications); + return { ...event, costCentsExact: values.costCents }; +} + export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { const budgets = budgetService(db, budgetHooks); return { - createEvent: async (companyId: string, data: Omit) => { - const agent = await db - .select() - .from(agents) - .where(eq(agents.id, data.agentId)) - .then((rows) => rows[0] ?? null); - - if (!agent) throw notFound("Agent not found"); - if (agent.companyId !== companyId) { - throw unprocessable("Agent does not belong to company"); - } - - const event = await db - .insert(costEvents) - .values({ - ...data, - companyId, - biller: data.biller ?? data.provider, - billingType: data.billingType ?? "unknown", - cachedInputTokens: data.cachedInputTokens ?? 0, - }) - .returning() - .then((rows) => rows[0]); - - const [agentMonthSpend, companyMonthSpend] = await Promise.all([ - getMonthlySpendTotal(db, { companyId, agentId: event.agentId }), - getMonthlySpendTotal(db, { companyId }), - ]); - - await db - .update(agents) - .set({ - spentMonthlyCents: agentMonthSpend, - updatedAt: new Date(), - }) - .where(eq(agents.id, event.agentId)); - - await db - .update(companies) - .set({ - spentMonthlyCents: companyMonthSpend, - updatedAt: new Date(), - }) - .where(eq(companies.id, companyId)); - - await budgets.evaluateCostEvent(event); - + createEvent: async (companyId: string, data: Omit & { costCents: MoneyInput }, actor?: Pick) => { + const event = await withAccountingTransaction(db, companyId, (tx, publications) => createCostEventInTransaction(tx, companyId, data, publications, actor)); + await budgets.deliverPendingEnforcement(companyId); return event; }, @@ -112,17 +146,28 @@ export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { if (!company) throw notFound("Company not found"); + range = resolveCostDateRange(range); const conditions: ReturnType[] = [eq(costEvents.companyId, companyId)]; if (range?.from) conditions.push(gte(costEvents.occurredAt, range.from)); if (range?.to) conditions.push(lte(costEvents.occurredAt, range.to)); - const [{ total }] = await db + const [{ total, totalExact, eventCount, unpricedEventCount, estimatedEventCount }] = await db .select({ + eventCount: sql`count(*)::int`, + estimatedEventCount: sql`count(*) filter (where ${costEvents.costStatus} = 'estimated')::int`, + unpricedEventCount: sql`count(*) filter (where ${costEvents.costStatus} = 'unpriced' and ${costEvents.billingType} <> 'subscription_included')::int`, total: sumAsNumber(costEvents.costCents), + totalExact: sql`coalesce(sum(${costEvents.costCents}), 0)::text`, }) .from(costEvents) .where(and(...conditions)); + const pendingConditions = [eq(heartbeatRuns.companyId, companyId), eq(heartbeatRuns.costAccountingPending, true), + inArray(heartbeatRuns.status, ["succeeded", "failed", "timed_out", "cancelled"])]; + if (range?.from) pendingConditions.push(sql`coalesce(${heartbeatRuns.finishedAt}, ${heartbeatRuns.createdAt}) >= ${range.from.toISOString()}::timestamptz`); + if (range?.to) pendingConditions.push(sql`coalesce(${heartbeatRuns.finishedAt}, ${heartbeatRuns.createdAt}) <= ${range.to.toISOString()}::timestamptz`); + const [pending] = await db.select({ count: sql`count(*)::int` }).from(heartbeatRuns).where(and(...pendingConditions)); + const pendingRunCount = pending?.count ?? 0; const spendCents = Number(total); const utilization = company.budgetMonthlyCents > 0 @@ -131,7 +176,13 @@ export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { return { companyId, + eventCount, + unpricedEventCount, + estimatedEventCount, + pendingRunCount, + pricingComplete: unpricedEventCount === 0 && pendingRunCount === 0, spendCents, + spendCentsExact: normalizeCents(totalExact), budgetCents: company.budgetMonthlyCents, utilizationPercent: Number(utilization.toFixed(2)), }; @@ -238,6 +289,7 @@ export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { .select({ issueCount: sql`count(distinct ${issues.id})::int`, costCents: sumAsNumber(costEvents.costCents), + costCentsExact: sql`coalesce(sum(${costEvents.costCents}), 0)::text`, inputTokens: sumAsNumber(costEvents.inputTokens), cachedInputTokens: sumAsNumber(costEvents.cachedInputTokens), outputTokens: sumAsNumber(costEvents.outputTokens), @@ -270,6 +322,7 @@ export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { issueCount: Number(costRow?.issueCount ?? 0), includeDescendants: true, costCents: Number(costRow?.costCents ?? 0), + costCentsExact: normalizeCents(costRow?.costCentsExact ?? 0), inputTokens: Number(costRow?.inputTokens ?? 0), cachedInputTokens: Number(costRow?.cachedInputTokens ?? 0), outputTokens: Number(costRow?.outputTokens ?? 0), @@ -279,6 +332,7 @@ export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { }, byAgent: async (companyId: string, range?: CostDateRange) => { + range = resolveCostDateRange(range); const conditions: ReturnType[] = [eq(costEvents.companyId, companyId)]; if (range?.from) conditions.push(gte(costEvents.occurredAt, range.from)); if (range?.to) conditions.push(lte(costEvents.occurredAt, range.to)); @@ -289,7 +343,10 @@ export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { agentName: agents.name, agentAppearance: agents.appearance, agentStatus: agents.status, + eventCount: sql`count(*)::int`, + estimatedEventCount: sql`count(*) filter (where ${costEvents.costStatus} = 'estimated')::int`, costCents: sumAsNumber(costEvents.costCents), + costCentsExact: sql`coalesce(sum(${costEvents.costCents}), 0)::text`, inputTokens: sumAsNumber(costEvents.inputTokens), cachedInputTokens: sumAsNumber(costEvents.cachedInputTokens), outputTokens: sumAsNumber(costEvents.outputTokens), @@ -316,6 +373,7 @@ export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { }, byProvider: async (companyId: string, range?: CostDateRange) => { + range = resolveCostDateRange(range); const conditions: ReturnType[] = [eq(costEvents.companyId, companyId)]; if (range?.from) conditions.push(gte(costEvents.occurredAt, range.from)); if (range?.to) conditions.push(lte(costEvents.occurredAt, range.to)); @@ -327,6 +385,7 @@ export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { billingType: costEvents.billingType, model: costEvents.model, costCents: sumAsNumber(costEvents.costCents), + costCentsExact: sql`coalesce(sum(${costEvents.costCents}), 0)::text`, inputTokens: sumAsNumber(costEvents.inputTokens), cachedInputTokens: sumAsNumber(costEvents.cachedInputTokens), outputTokens: sumAsNumber(costEvents.outputTokens), @@ -348,6 +407,7 @@ export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { }, byBiller: async (companyId: string, range?: CostDateRange) => { + range = resolveCostDateRange(range); const conditions: ReturnType[] = [eq(costEvents.companyId, companyId)]; if (range?.from) conditions.push(gte(costEvents.occurredAt, range.from)); if (range?.to) conditions.push(lte(costEvents.occurredAt, range.to)); @@ -356,6 +416,7 @@ export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { .select({ biller: costEvents.biller, costCents: sumAsNumber(costEvents.costCents), + costCentsExact: sql`coalesce(sum(${costEvents.costCents}), 0)::text`, inputTokens: sumAsNumber(costEvents.inputTokens), cachedInputTokens: sumAsNumber(costEvents.cachedInputTokens), outputTokens: sumAsNumber(costEvents.outputTokens), @@ -390,14 +451,16 @@ export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { { label: "7d", hours: 168 }, ] as const; + const now = new Date(); const results = await Promise.all( windows.map(async ({ label, hours }) => { - const since = new Date(Date.now() - hours * 60 * 60 * 1000); + const since = new Date(now.getTime() - hours * 60 * 60 * 1000); const rows = await db .select({ provider: costEvents.provider, biller: sql`case when count(distinct ${costEvents.biller}) = 1 then min(${costEvents.biller}) else 'mixed' end`, costCents: sumAsNumber(costEvents.costCents), + costCentsExact: sql`coalesce(sum(${costEvents.costCents}), 0)::text`, inputTokens: sumAsNumber(costEvents.inputTokens), cachedInputTokens: sumAsNumber(costEvents.cachedInputTokens), outputTokens: sumAsNumber(costEvents.outputTokens), @@ -407,6 +470,7 @@ export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { and( eq(costEvents.companyId, companyId), gte(costEvents.occurredAt, since), + lte(costEvents.occurredAt, now), ), ) .groupBy(costEvents.provider) @@ -418,6 +482,7 @@ export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { window: label as string, windowHours: hours, costCents: row.costCents, + costCentsExact: normalizeCents(row.costCentsExact), inputTokens: row.inputTokens, cachedInputTokens: row.cachedInputTokens, outputTokens: row.outputTokens, @@ -429,6 +494,7 @@ export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { }, byAgentModel: async (companyId: string, range?: CostDateRange) => { + range = resolveCostDateRange(range); const conditions: ReturnType[] = [eq(costEvents.companyId, companyId)]; if (range?.from) conditions.push(gte(costEvents.occurredAt, range.from)); if (range?.to) conditions.push(lte(costEvents.occurredAt, range.to)); @@ -446,7 +512,10 @@ export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { biller: costEvents.biller, billingType: costEvents.billingType, model: costEvents.model, + eventCount: sql`count(*)::int`, + estimatedEventCount: sql`count(*) filter (where ${costEvents.costStatus} = 'estimated')::int`, costCents: sumAsNumber(costEvents.costCents), + costCentsExact: sql`coalesce(sum(${costEvents.costCents}), 0)::text`, inputTokens: sumAsNumber(costEvents.inputTokens), cachedInputTokens: sumAsNumber(costEvents.cachedInputTokens), outputTokens: sumAsNumber(costEvents.outputTokens), @@ -473,9 +542,9 @@ export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { byProject: async (companyId: string, range?: CostDateRange) => { const issueIdAsText = sql`${issues.id}::text`; const runProjectLinks = db - .selectDistinctOn([activityLog.runId, issues.projectId], { + .select({ runId: activityLog.runId, - projectId: issues.projectId, + projectId: sql`min(${issues.projectId}::text)::uuid`.as("linked_project_id"), }) .from(activityLog) .innerJoin( @@ -493,10 +562,12 @@ export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { isNotNull(issues.projectId), ), ) - .orderBy(activityLog.runId, issues.projectId, desc(activityLog.createdAt)) + .groupBy(activityLog.runId) + .having(sql`count(distinct ${issues.projectId}) = 1`) .as("run_project_links"); - const effectiveProjectId = sql`coalesce(${costEvents.projectId}, ${runProjectLinks.projectId})`; + const effectiveProjectId = sql`coalesce(${costEvents.projectId}, case when ${costEvents.receiptHash} is null then ${runProjectLinks.projectId} end)`; + range = resolveCostDateRange(range); const conditions: ReturnType[] = [eq(costEvents.companyId, companyId)]; if (range?.from) conditions.push(gte(costEvents.occurredAt, range.from)); if (range?.to) conditions.push(lte(costEvents.occurredAt, range.to)); @@ -505,18 +576,19 @@ export function costService(db: Db, budgetHooks: BudgetServiceHooks = {}) { return db .select({ - projectId: effectiveProjectId, + projectId: projects.id, projectName: projects.name, costCents: costCentsExpr, + costCentsExact: sql`coalesce(sum(${costEvents.costCents}), 0)::text`, inputTokens: sumAsNumber(costEvents.inputTokens), cachedInputTokens: sumAsNumber(costEvents.cachedInputTokens), outputTokens: sumAsNumber(costEvents.outputTokens), }) .from(costEvents) .leftJoin(runProjectLinks, eq(costEvents.heartbeatRunId, runProjectLinks.runId)) - .innerJoin(projects, sql`${projects.id} = ${effectiveProjectId}`) - .where(and(...conditions, sql`${effectiveProjectId} is not null`)) - .groupBy(effectiveProjectId, projects.name) + .leftJoin(projects, and(eq(projects.companyId, companyId), sql`${projects.id} = ${effectiveProjectId}`)) + .where(and(...conditions)) + .groupBy(projects.id, projects.name) .orderBy(desc(costCentsExpr)); }, }; diff --git a/server/src/services/dashboard.ts b/server/src/services/dashboard.ts index f4d7f7e4ed9..15926b6ac29 100644 --- a/server/src/services/dashboard.ts +++ b/server/src/services/dashboard.ts @@ -1,4 +1,4 @@ -import { and, eq, gte, sql } from "drizzle-orm"; +import { and, eq, gte, lte, sql } from "drizzle-orm"; import type { Db } from "@paperclipai/db"; import { agents, approvals, companies, costEvents, heartbeatRuns, issues } from "@paperclipai/db"; import { notFound } from "../errors.js"; @@ -96,6 +96,7 @@ export function dashboardService(db: Db) { and( eq(costEvents.companyId, companyId), gte(costEvents.occurredAt, monthStart), + lte(costEvents.occurredAt, now), ), )); diff --git a/server/src/services/finance.ts b/server/src/services/finance.ts index 766bea7b1f3..7f382281735 100644 --- a/server/src/services/finance.ts +++ b/server/src/services/finance.ts @@ -1,12 +1,14 @@ -import { and, desc, eq, gte, lte, sql } from "drizzle-orm"; +import { and, desc, eq, getTableColumns, gte, lte, sql } from "drizzle-orm"; import type { Db } from "@paperclipai/db"; import { agents, costEvents, financeEvents, goals, heartbeatRuns, issues, projects } from "@paperclipai/db"; -import { notFound, unprocessable } from "../errors.js"; +import { notFound, unprocessable, conflict } from "../errors.js"; -export interface FinanceDateRange { - from?: Date; - to?: Date; -} +import { resolveCostDateRange, type CostDateRange as FinanceDateRange } from "./cost-date-range.js"; +import { createFinanceEventSchema, normalizeCents, subtractCents, type MoneyInput } from "@paperclipai/shared"; +import { receiptFingerprint } from "./receipt-fingerprint.js"; +import { withAccountingTransaction } from "./accounting-transaction.js"; +import { logActivity, type LogActivityInput, type ActivityPublication } from "./activity-log.js"; +export type { CostDateRange as FinanceDateRange } from "./cost-date-range.js"; async function assertBelongsToCompany( db: Db, @@ -28,19 +30,29 @@ async function assertBelongsToCompany( } function rangeConditions(companyId: string, range?: FinanceDateRange) { + range = resolveCostDateRange(range); const conditions: ReturnType[] = [eq(financeEvents.companyId, companyId)]; if (range?.from) conditions.push(gte(financeEvents.occurredAt, range.from)); if (range?.to) conditions.push(lte(financeEvents.occurredAt, range.to)); return conditions; } -export function financeService(db: Db) { - const debitExpr = sql`coalesce(sum(case when ${financeEvents.direction} = 'debit' then ${financeEvents.amountCents} else 0 end), 0)::double precision`; - const creditExpr = sql`coalesce(sum(case when ${financeEvents.direction} = 'credit' then ${financeEvents.amountCents} else 0 end), 0)::double precision`; - const estimatedDebitExpr = sql`coalesce(sum(case when ${financeEvents.direction} = 'debit' and ${financeEvents.estimated} = true then ${financeEvents.amountCents} else 0 end), 0)::double precision`; - - return { - createEvent: async (companyId: string, data: Omit) => { +export async function createFinanceEventInTransaction(tx: Db, publications: ActivityPublication[], companyId: string, data: Omit & { amountCents: MoneyInput }, actor?: Pick) { + const db = tx; + const parsed = createFinanceEventSchema.safeParse({ ...data, occurredAt: data.occurredAt.toISOString() }); + if (!parsed.success) throw unprocessable("Invalid finance receipt", parsed.error.flatten()); + const values = { ...parsed.data, amountCents: normalizeCents(parsed.data.amountCents), occurredAt: new Date(parsed.data.occurredAt) }; + const receiptHash = receiptFingerprint(values); + let legacyHash: string | null = null; + try { if (normalizeCents(Number(values.amountCents)) === values.amountCents) legacyHash = receiptFingerprint({ ...values, amountCents: Number(values.amountCents) }); } catch { /* Decimal exceeds the legacy number range. */ } + if (values.idempotencyKey) { + const [existing] = await db.select().from(financeEvents).where(and(eq(financeEvents.companyId, companyId), eq(financeEvents.idempotencyKey, values.idempotencyKey))); + if (existing) { + if (existing.receiptHash !== receiptHash && existing.receiptHash !== legacyHash) throw conflict("Idempotency key already used for a different finance receipt"); + const [exact] = await db.select({ amount: sql`${financeEvents.amountCents}::text` }).from(financeEvents).where(eq(financeEvents.id, existing.id)); + return { ...existing, amountCentsExact: normalizeCents(exact.amount) }; + } + } if (data.agentId) await assertBelongsToCompany(db, agents, data.agentId, companyId, "Agent"); if (data.issueId) await assertBelongsToCompany(db, issues, data.issueId, companyId, "Issue"); if (data.projectId) await assertBelongsToCompany(db, projects, data.projectId, companyId, "Project"); @@ -51,35 +63,60 @@ export function financeService(db: Db) { const event = await db .insert(financeEvents) .values({ - ...data, - companyId, - currency: data.currency ?? "USD", - direction: data.direction ?? "debit", - estimated: data.estimated ?? false, + ...values, amountCents: sql`${values.amountCents}::numeric`, companyId, receiptHash, id: data.id, }) .returning() .then((rows) => rows[0]); - return event; - }, + await logActivity(db, { + companyId, actorType: actor?.actorType ?? "system", actorId: actor?.actorId ?? "finance_accounting", agentId: actor?.agentId, + action: "finance_event.reported", entityType: "finance_event", entityId: event.id, + details: { amountCents: event.amountCents, amountCentsExact: values.amountCents, currency: event.currency, biller: event.biller, eventKind: event.eventKind, direction: event.direction }, + }, publications); + return { ...event, amountCentsExact: values.amountCents }; +} + +export function financeService(db: Db) { + const debitExpr = sql`coalesce(sum(case when ${financeEvents.direction} = 'debit' and ${financeEvents.metadataJson}->>'source' is distinct from 'provider_cost_report' then ${financeEvents.amountCents} else 0 end), 0)`; + const creditExpr = sql`coalesce(sum(case when ${financeEvents.direction} = 'credit' and ${financeEvents.metadataJson}->>'source' is distinct from 'provider_cost_report' then ${financeEvents.amountCents} else 0 end), 0)`; + const estimatedDebitExpr = sql`coalesce(sum(case when ${financeEvents.direction} = 'debit' and ${financeEvents.estimated} = true and ${financeEvents.metadataJson}->>'source' is distinct from 'provider_cost_report' then ${financeEvents.amountCents} else 0 end), 0)`; + + return { + createEvent: async (companyId: string, data: Omit & { amountCents: MoneyInput }, actor?: Pick) => withAccountingTransaction(db, companyId, (tx, publications) => createFinanceEventInTransaction(tx, publications, companyId, data, actor)), summary: async (companyId: string, range?: FinanceDateRange) => { const conditions = rangeConditions(companyId, range); - const [row] = await db + const rows = await db .select({ - debitCents: debitExpr, - creditCents: creditExpr, - estimatedDebitCents: estimatedDebitExpr, + currency: financeEvents.currency, + providerReportedCentsExact: sql`coalesce(sum(case when ${financeEvents.metadataJson}->>'source' = 'provider_cost_report' then case when ${financeEvents.direction} = 'credit' then -${financeEvents.amountCents} else ${financeEvents.amountCents} end else 0 end), 0)::text`, + debitCents: sql`${debitExpr}::double precision`, + debitCentsExact: sql`${debitExpr}::text`, + creditCents: sql`${creditExpr}::double precision`, + creditCentsExact: sql`${creditExpr}::text`, + estimatedDebitCents: sql`${estimatedDebitExpr}::double precision`, + estimatedDebitCentsExact: sql`${estimatedDebitExpr}::text`, eventCount: sql`count(*)::int`, }) .from(financeEvents) - .where(and(...conditions)); + .where(and(...conditions)) + .groupBy(financeEvents.currency) + .orderBy(financeEvents.currency); + const row = rows.find((row) => row.currency === "USD"); return { companyId, + currency: "USD" as const, + providerReportedCentsExact: normalizeCents(row?.providerReportedCentsExact ?? 0), + providerReportedCents: Number(row?.providerReportedCentsExact ?? 0), + currencies: rows.map((row) => ({ ...row, netCents: Number(subtractCents(row.debitCentsExact, row.creditCentsExact)), netCentsExact: subtractCents(row.debitCentsExact, row.creditCentsExact) })), debitCents: Number(row?.debitCents ?? 0), creditCents: Number(row?.creditCents ?? 0), - netCents: Number(row?.debitCents ?? 0) - Number(row?.creditCents ?? 0), + netCents: Number(subtractCents(row?.debitCentsExact ?? 0, row?.creditCentsExact ?? 0)), + netCentsExact: subtractCents(row?.debitCentsExact ?? 0, row?.creditCentsExact ?? 0), + debitCentsExact: normalizeCents(row?.debitCentsExact ?? 0), + creditCentsExact: normalizeCents(row?.creditCentsExact ?? 0), + estimatedDebitCentsExact: normalizeCents(row?.estimatedDebitCentsExact ?? 0), estimatedDebitCents: Number(row?.estimatedDebitCents ?? 0), eventCount: Number(row?.eventCount ?? 0), }; @@ -87,44 +124,56 @@ export function financeService(db: Db) { byBiller: async (companyId: string, range?: FinanceDateRange) => { const conditions = rangeConditions(companyId, range); + conditions.push(sql`${financeEvents.metadataJson}->>'source' is distinct from 'provider_cost_report'`); return db .select({ + currency: financeEvents.currency, biller: financeEvents.biller, - debitCents: debitExpr, - creditCents: creditExpr, - estimatedDebitCents: estimatedDebitExpr, + debitCents: sql`${debitExpr}::double precision`, + debitCentsExact: sql`${debitExpr}::text`, + creditCents: sql`${creditExpr}::double precision`, + creditCentsExact: sql`${creditExpr}::text`, + estimatedDebitCents: sql`${estimatedDebitExpr}::double precision`, + estimatedDebitCentsExact: sql`${estimatedDebitExpr}::text`, eventCount: sql`count(*)::int`, kindCount: sql`count(distinct ${financeEvents.eventKind})::int`, netCents: sql`(${debitExpr} - ${creditExpr})::double precision`, + netCentsExact: sql`(${debitExpr} - ${creditExpr})::text`, }) .from(financeEvents) .where(and(...conditions)) - .groupBy(financeEvents.biller) + .groupBy(financeEvents.biller, financeEvents.currency) .orderBy(desc(sql`(${debitExpr} - ${creditExpr})::double precision`), financeEvents.biller); }, byKind: async (companyId: string, range?: FinanceDateRange) => { const conditions = rangeConditions(companyId, range); + conditions.push(sql`${financeEvents.metadataJson}->>'source' is distinct from 'provider_cost_report'`); return db .select({ + currency: financeEvents.currency, eventKind: financeEvents.eventKind, - debitCents: debitExpr, - creditCents: creditExpr, - estimatedDebitCents: estimatedDebitExpr, + debitCents: sql`${debitExpr}::double precision`, + debitCentsExact: sql`${debitExpr}::text`, + creditCents: sql`${creditExpr}::double precision`, + creditCentsExact: sql`${creditExpr}::text`, + estimatedDebitCents: sql`${estimatedDebitExpr}::double precision`, + estimatedDebitCentsExact: sql`${estimatedDebitExpr}::text`, eventCount: sql`count(*)::int`, billerCount: sql`count(distinct ${financeEvents.biller})::int`, netCents: sql`(${debitExpr} - ${creditExpr})::double precision`, + netCentsExact: sql`(${debitExpr} - ${creditExpr})::text`, }) .from(financeEvents) .where(and(...conditions)) - .groupBy(financeEvents.eventKind) + .groupBy(financeEvents.eventKind, financeEvents.currency) .orderBy(desc(sql`(${debitExpr} - ${creditExpr})::double precision`), financeEvents.eventKind); }, list: async (companyId: string, range?: FinanceDateRange, limit: number = 100) => { const conditions = rangeConditions(companyId, range); return db - .select() + .select({ ...getTableColumns(financeEvents), amountCentsExact: sql`${financeEvents.amountCents}::text` }) .from(financeEvents) .where(and(...conditions)) .orderBy(desc(financeEvents.occurredAt), desc(financeEvents.createdAt)) diff --git a/server/src/services/heartbeat.ts b/server/src/services/heartbeat.ts index a1c57d00f21..14e3c48a2f3 100644 --- a/server/src/services/heartbeat.ts +++ b/server/src/services/heartbeat.ts @@ -14,6 +14,9 @@ import { type NativeWorkspaceFinalizationOwnership, } from "./native-runtime/native-workspace-finalization-ownership.js"; import { hasStopOnlyCleanup, settleStopOnlyCleanup } from "./sandbox-stop-and-retain.js"; +import { reserveRunBudget } from "./budget-reservations.js"; +import { accountRunCost, reconcileRunCosts } from "./run-cost-accounting.js"; +import { createRunUsageRecorder, replayUsageReceipts } from "./usage-receipts.js"; import { applyWorkspaceRestoreFailure } from "@paperclipai/adapter-utils/workspace-restore-result"; import { hasWorkspaceRestoreFailure } from "@paperclipai/shared"; import { externalConversationStateSql, nonIdleSlackIssueCondition } from "./slack-conversation-state.js"; @@ -306,7 +309,6 @@ import { appendWithByteCap, MAX_EXCERPT_BYTES, } from "../adapters/utils.js"; -import { costService } from "./costs.js"; import { authorizeChatConversationForBoundRun, isExternalChatWaitAuthorizationContention, @@ -678,6 +680,7 @@ const HEARTBEAT_MAX_CONCURRENT_RUNS_MAX = 50; const LIVENESS_BOOKKEEPING_ACTIVITY_ACTIONS = [ "environment.lease_acquired", "environment.lease_released", + "cost.reported", ]; const DEFERRED_WAKE_CONTEXT_KEY = "_paperclipWakeContext"; const EXTERNAL_ATTACHMENT_OMISSIONS_KEY = "externalAttachmentOmissions"; @@ -5309,21 +5312,23 @@ function resolveLedgerBiller(result: AdapterExecutionResult): string { ); } -function normalizeBilledCostCents( +export function normalizeBilledCostCents( costUsd: number | null | undefined, billingType: BillingType, ): number { if (billingType === "subscription_included") return 0; if (typeof costUsd !== "number" || !Number.isFinite(costUsd)) return 0; - return Math.max(0, Math.round(costUsd * 100)); + return Math.max(0, Number((costUsd * 100).toFixed(7))); } export function resolveLedgerCostStatus(input: { costUsd: number | null | undefined; + billingType?: BillingType; inputTokens: number; cachedInputTokens: number; outputTokens: number; }): CostStatus { + if (input.billingType === "subscription_included") return "reported"; // A paused turn can have neither a token receipt nor a cost receipt. Zero // normalized counters do not establish that its billed cost was zero. return typeof input.costUsd === "number" && @@ -5529,12 +5534,13 @@ function readRawUsageTotals(usageJson: unknown): UsageTotals | null { }; } -function deriveNormalizedUsageDelta( +export function normalizeAdapterRunUsage( current: UsageTotals | null, previous: UsageTotals | null, + usageBasis?: "per_run" | "session_cumulative" | null, ): UsageTotals | null { if (!current) return null; - if (!previous) return { ...current }; + if (!previous || usageBasis !== "session_cumulative") return { ...current }; const inputTokens = current.inputTokens >= previous.inputTokens @@ -12107,7 +12113,7 @@ export function heartbeatService( // Adapters that declare per-run usage (e.g. the ACPX lane reports each // turn's tokens, not session totals) must not be session-delta'd, or // consecutive runs would be undercounted. - if (!sessionId || !rawUsage || usageBasis === "per_run") { + if (!sessionId || !rawUsage || usageBasis !== "session_cumulative") { return { normalizedUsage: rawUsage, previousRawUsage: null as UsageTotals | null, @@ -12120,7 +12126,7 @@ export function heartbeatService( }); const previousRawUsage = readRawUsageTotals(previousRun?.usageJson); return { - normalizedUsage: deriveNormalizedUsageDelta(rawUsage, previousRawUsage), + normalizedUsage: normalizeAdapterRunUsage(rawUsage, previousRawUsage, usageBasis), previousRawUsage, derivedFromSessionTotals: previousRawUsage !== null, }; @@ -12880,6 +12886,10 @@ export function heartbeatService( .where(eq(heartbeatRuns.id, runId)) .then((rows) => rows[0] ?? null); + // Preserve the receipt-source fence when finalization enriches usage. A + // late spool replay must still be able to complete an unfinished receipt. + if (patch?.usageJson && previousStatus?.usageJson) patch = { ...patch, usageJson: { ...previousStatus.usageJson, ...patch.usageJson } }; + // Cancelling a queued run that never acquired provider execution is // positive bootstrap evidence. It must not hold unrelated queued messages. if (previousStatus && (status === "cancelled" || status === "interrupted")) { @@ -12973,6 +12983,10 @@ export function heartbeatService( patch = { ...patch, resultJson: cancellationResultJson(previousStatus, status, patch?.resultJson, patch?.errorCode, patch?.error) }; } + // Preserve the receipt-source fence when finalization enriches usage. A + // late spool replay must still be able to complete an unfinished receipt. + if (patch?.usageJson && previousStatus?.usageJson) patch = { ...patch, usageJson: { ...previousStatus.usageJson, ...patch.usageJson } }; + // Cancelling a queued run that never acquired provider execution is // positive bootstrap evidence. It must not hold unrelated queued messages. if ( @@ -16856,7 +16870,7 @@ export function heartbeatService( if (checkCostCap && policy.maxDailyCostCents !== null) { const [row] = await client .select({ - total: sql`coalesce(sum(${costEvents.costCents})::bigint, 0)`, + total: sql`coalesce(sum(${costEvents.costCents})::double precision, 0)`, }) .from(costEvents) .where( @@ -19924,32 +19938,7 @@ export function heartbeatService( normalizedUsage?: UsageTotals | null, ) { await ensureRuntimeState(agent); - const usage = normalizedUsage ?? normalizeUsageTotals(result.usage); - const inputTokens = usage?.inputTokens ?? 0; - const outputTokens = usage?.outputTokens ?? 0; - const cachedInputTokens = usage?.cachedInputTokens ?? 0; - const billingType = normalizeLedgerBillingType(result.billingType); - const billedCostUsd = resolveCacheAdjustedCostUsd(result); - const additionalCostCents = normalizeBilledCostCents( - billedCostUsd, - billingType, - ); - const hasTokenUsage = - inputTokens > 0 || outputTokens > 0 || cachedInputTokens > 0; - const costStatus = resolveLedgerCostStatus({ - costUsd: billedCostUsd, - inputTokens, - cachedInputTokens, - outputTokens, - }); - const provider = result.provider ?? "unknown"; - const biller = resolveLedgerBiller(result); - const ledgerScope = await resolveLedgerScopeForRun( - db, - agent.companyId, - run, - ); - + await accountRunCost(db, run.id, budgetHooks); await db .update(agentRuntimeState) .set({ @@ -19958,34 +19947,10 @@ export function heartbeatService( lastRunId: run.id, lastRunStatus: run.status, lastError: run.error ?? null, - totalInputTokens: sql`${agentRuntimeState.totalInputTokens} + ${inputTokens}`, - totalOutputTokens: sql`${agentRuntimeState.totalOutputTokens} + ${outputTokens}`, - totalCachedInputTokens: sql`${agentRuntimeState.totalCachedInputTokens} + ${cachedInputTokens}`, - totalCostCents: sql`${agentRuntimeState.totalCostCents} + ${additionalCostCents}`, updatedAt: new Date(), }) .where(eq(agentRuntimeState.agentId, agent.id)); - if (additionalCostCents > 0 || hasTokenUsage) { - const costs = costService(db, budgetHooks); - await costs.createEvent(agent.companyId, { - heartbeatRunId: run.id, - agentId: agent.id, - issueId: ledgerScope.issueId, - projectId: ledgerScope.projectId, - billingCode: ledgerScope.billingCode, - provider, - biller, - billingType, - costStatus, - model: result.model ?? "unknown", - inputTokens, - cachedInputTokens, - outputTokens, - costCents: additionalCostCents, - occurredAt: new Date(), - }); - } } // A 403 from claimQueuedRun comes from the run's own persisted identity @@ -20968,6 +20933,13 @@ export function heartbeatService( issueContext.executionWorkspacePreference, } : null; + const storedLedgerScope = parseObject(parseObject(run.usageJson).ledgerScope); + const runLedgerScope = Object.keys(storedLedgerScope).length > 0 + ? storedLedgerScope + : await resolveLedgerScopeForRun(db, agent.companyId, run); + await db.update(heartbeatRuns).set({ + usageJson: sql`coalesce(${heartbeatRuns.usageJson}, '{}'::jsonb) || ${JSON.stringify({ ledgerScope: runLedgerScope })}::jsonb`, + }).where(eq(heartbeatRuns.id, run.id)); const continuationSummary = issueRef && !isConversation(issueContext) ? await getIssueContinuationSummaryDocument(db, issueRef.id) : null; @@ -24601,6 +24573,7 @@ export function heartbeatService( adapterFinalizeOutcome = status; }; + const usageRecorder = await createRunUsageRecorder(db, { companyId: run.companyId, runId: run.id, adapterType: agent.adapterType }); let adapterResult: AdapterExecutionResult; const runGoalControlRequestId = readNonEmptyString( context.goalControlRequestId, @@ -24683,8 +24656,9 @@ export function heartbeatService( ); const guardedDispatch = await dispatchResolvedInteractionContinuationWithAtomicGate( - (markDispatchStarted) => - executePaperclipNativeSession({ + async (markDispatchStarted) => { + await reserveRunBudget(db, run.companyId, run.id, readNonEmptyString(runLedgerScope.projectId), runLedgerScope); + return executePaperclipNativeSession({ db, execution: nativeExecution, getFreshSessionHandoff: getNativeFreshSessionHandoff, @@ -24741,11 +24715,14 @@ export function heartbeatService( hasChanges: () => instructionCopies.hasChanges({ companyId: agent.companyId, runId: run.id, target: executionTarget }), collectStopped: collectStoppedInstructions, } : undefined, + + onUsage: async receipt => { await usageRecorder.capture(receipt); }, preparationSpans: nativeRunnerPreparationSpans, // Bootstrap with executable/home discovery while keeping // configured provider values and the server-selected // workspace boundary authoritative. managedGitHub: !useHostGitHub && githubSelection.configured, + billingIdentity: managedAiRuntime ? { provider: managedAiRuntime.attribution.provider, biller: managedAiRuntime.attribution.provider, billingType: managedAiRuntime.attribution.method === "subscription" ? "subscription_included" : "metered_api" } : undefined, managedAiCredentialIdentity: managedAiRuntime?.identity, managedAiCredentialHome: managedAiRuntime ? String((managedAiRuntime.config.env as Record).CODEX_HOME) : undefined, runnerEnvironment: { @@ -24817,7 +24794,8 @@ export function heartbeatService( markDispatchStarted(); await persistRunProcessMetadata(run.id, meta); }, - }), + }); + }, ); if (!guardedDispatch.dispatched) return; nativeDispatchStarted = true; @@ -24906,7 +24884,8 @@ export function heartbeatService( } const guardedDispatch = await dispatchResolvedInteractionContinuationWithAtomicGate( - (markDispatchStarted) => { + async (markDispatchStarted) => { + await reserveRunBudget(db, run.companyId, run.id, readNonEmptyString(runLedgerScope.projectId), runLedgerScope); legacyAdapterEntered = true; return adapter.execute({ getFreshSessionHandoff, @@ -24932,6 +24911,7 @@ export function heartbeatService( onLog, onMeta: onAdapterMeta, onEvent: onAdapterEvent, + onUsage: async receipt => { await usageRecorder.capture(receipt); }, startupTraceContext: getStartupTraceContext(), onRuntimeProgress: async (progress) => { await recordCurrentHeartbeatRunRuntimeProgress( @@ -24990,6 +24970,17 @@ export function heartbeatService( adapterResult = await guardedDispatch.resultPromise; } if (instructionSave) adapterResult.resultJson = { ...adapterResult.resultJson, instructionSave }; + + if (parseObject(adapterResult.executionRecovery).providerWorkStarted !== false) { + const captured = await usageRecorder.complete(adapterResult); + adapterResult = { ...adapterResult, ...captured, usageComplete: captured.complete }; + } else { + // Stop may already own the terminal result. Preserve its metadata + // while durably recording the proof needed to release admission. + await db.update(heartbeatRuns).set({ costAccountingPending: true, + usageJson: sql`coalesce(${heartbeatRuns.usageJson}, '{}'::jsonb) || '{"accountingProviderWorkStarted":false}'::jsonb`, + }).where(and(eq(heartbeatRuns.id, run.id), isNull(heartbeatRuns.costAccountedAt))); + } adapterResult = applyWorkspaceRestoreFailure(adapterResult); // A returned result can include a failed restore. Keep the workspace // barrier closed until required files have been restored. @@ -25425,64 +25416,66 @@ export function heartbeatService( ? "timed_out" : "failed"; - const cacheAdjustedCostUsd = resolveCacheAdjustedCostUsd(adapterResult); - const usageJson = - normalizedUsage || - adapterResult.costUsd != null || - cacheAdjustedCostUsd != null - ? ({ - ...(normalizedUsage ?? {}), - ...(rawUsage - ? { - rawInputTokens: rawUsage.inputTokens, - rawCachedInputTokens: rawUsage.cachedInputTokens, - rawOutputTokens: rawUsage.outputTokens, - } - : {}), - ...(sessionUsageResolution.derivedFromSessionTotals - ? { usageSource: "session_delta" } - : adapterResult.usageBasis === "per_run" - ? { usageSource: "per_run" } - : {}), - ...((nextSessionState.displayId ?? - nextSessionState.legacySessionId) - ? { - persistedSessionId: - nextSessionState.displayId ?? - nextSessionState.legacySessionId, - } - : {}), - sessionReused: - runtimeForAdapter.sessionId != null || - runtimeForAdapter.sessionDisplayId != null, - taskSessionReused: taskSessionForRun != null, - freshSession: - runtimeForAdapter.sessionId == null && - runtimeForAdapter.sessionDisplayId == null, - sessionRotated: sessionCompaction.rotate, - sessionRotationReason: sessionCompaction.reason, - configFreshness: configFreshnessResultMetadata, - provider: - readNonEmptyString(adapterResult.provider) ?? "unknown", - biller: resolveLedgerBiller(adapterResult), - model: readNonEmptyString(adapterResult.model) ?? "unknown", - ...(adapterResult.costUsd != null - ? { costUsd: adapterResult.costUsd } - : {}), - ...(cacheAdjustedCostUsd != null - ? { cacheAdjustedCostUsd } - : {}), - costStatus: resolveLedgerCostStatus({ - costUsd: cacheAdjustedCostUsd, - inputTokens: normalizedUsage?.inputTokens ?? 0, - cachedInputTokens: normalizedUsage?.cachedInputTokens ?? 0, - outputTokens: normalizedUsage?.outputTokens ?? 0, - }), - billingType: normalizeLedgerBillingType( - adapterResult.billingType, - ), - } as Record) - : null; + const cacheAdjustedCostUsd = adapterResult.costUsdExact != null && adapterResult.cacheAdjustedCostUsd == null + ? null : resolveCacheAdjustedCostUsd(adapterResult); + const usageJson: Record = { + accountingReceiptReady: adapterResult.usageComplete !== false, + costUsdExact: adapterResult.costUsdExact ?? null, + providerRequestId: adapterResult.providerRequestId ?? null, + ...(normalizedUsage ?? {}), + ...(adapterResult.usageByModel ? { usageByModel: adapterResult.usageByModel } : {}), + ...(rawUsage + ? { + rawInputTokens: rawUsage.inputTokens, + rawCachedInputTokens: rawUsage.cachedInputTokens, + rawOutputTokens: rawUsage.outputTokens, + } + : {}), + ...(sessionUsageResolution.derivedFromSessionTotals + ? { usageSource: "session_delta" } + : adapterResult.usageBasis === "per_run" + ? { usageSource: "per_run" } + : {}), + ...((nextSessionState.displayId ?? + nextSessionState.legacySessionId) + ? { + persistedSessionId: + nextSessionState.displayId ?? + nextSessionState.legacySessionId, + } + : {}), + sessionReused: + runtimeForAdapter.sessionId != null || + runtimeForAdapter.sessionDisplayId != null, + taskSessionReused: taskSessionForRun != null, + freshSession: + runtimeForAdapter.sessionId == null && + runtimeForAdapter.sessionDisplayId == null, + sessionRotated: sessionCompaction.rotate, + sessionRotationReason: sessionCompaction.reason, + configFreshness: configFreshnessResultMetadata, + provider: + readNonEmptyString(adapterResult.provider) ?? "unknown", + biller: resolveLedgerBiller(adapterResult), + model: readNonEmptyString(adapterResult.model) ?? "unknown", + ...(adapterResult.costUsd != null + ? { costUsd: adapterResult.costUsd } + : {}), + ...(cacheAdjustedCostUsd != null + ? { cacheAdjustedCostUsd } + : {}), + pricingProvenance: adapterResult.pricingProvenance, + costStatus: adapterResult.costStatus ?? resolveLedgerCostStatus({ + costUsd: cacheAdjustedCostUsd ?? (adapterResult.costUsdExact != null ? Number(adapterResult.costUsdExact) : null), + billingType: normalizeLedgerBillingType(adapterResult.billingType), + inputTokens: normalizedUsage?.inputTokens ?? 0, + cachedInputTokens: normalizedUsage?.cachedInputTokens ?? 0, + outputTokens: normalizedUsage?.outputTokens ?? 0, + }), + billingType: normalizeLedgerBillingType( + adapterResult.billingType, + ), + }; const persistedResultJson = cancellationResultJson(latestRun ?? run, outcome, mergeHeartbeatRunResultJson( mergeRunStopMetadataForAgent(agent, outcome, { @@ -25506,13 +25499,17 @@ export function heartbeatService( adapterResult.summary ?? null, ), runErrorCode, runErrorMessage); + const ledgerScope = runLedgerScope; const finalRunPatch: Partial = { + // Accounting must acknowledge even a proven pre-provider failure: + // that transaction releases the reservation without creating a charge. + costAccountingPending: true, finishedAt: new Date(), error: runErrorMessage, errorCode: runErrorCode, exitCode: adapterResult.exitCode, signal: adapterResult.signal, - usageJson, + usageJson: { ...usageJson, ledgerScope }, resultJson: persistedResultJson, sessionIdAfter: nextSessionState.displayId ?? nextSessionState.legacySessionId, @@ -25550,6 +25547,8 @@ export function heartbeatService( .set({ ...finalRunPatch, resultJson: cancellationResultJson(persistedRunWrite.run, status, finalRunPatch.resultJson, runErrorCode, runErrorMessage), + + usageJson: { ...parseObject(persistedRunWrite.run.usageJson), ...parseObject(finalRunPatch.usageJson) }, finishedAt: persistedRunWrite.run.finishedAt ?? finalRunPatch.finishedAt, updatedAt: new Date(), @@ -25564,6 +25563,9 @@ export function heartbeatService( .then((rows) => rows[0] ?? null); } if (!persistedRun) { + await accountRunCost(db, run.id, budgetHooks).catch((err) => { + logger.error({ err, runId: run.id }, "Late run accounting queued for recovery"); + }); logger.info( { runId: run.id, @@ -25576,6 +25578,10 @@ export function heartbeatService( } } if (persistedRun) { + // Accounting recovery is independent of workspace or issue finalization. + await accountRunCost(db, persistedRun.id, budgetHooks).catch((err) => { + logger.error({ err, runId: run.id }, "Run accounting queued for recovery"); + }); persistedRun = (await classifyAndPersistRunLiveness( persistedRun, @@ -29290,7 +29296,7 @@ export function heartbeatService( return { scanned: candidates.length, dispatched, recovered, deferred }; } - async function listProjectScopedRunIds(companyId: string, projectId: string) { + async function listProjectScopedRunIds(companyId: string, projectId: string, createdBefore?: Date) { const runIssueId = sql< string | null >`${heartbeatRuns.contextSnapshot} ->> 'issueId'`; @@ -29311,6 +29317,7 @@ export function heartbeatService( .where( and( eq(heartbeatRuns.companyId, companyId), + createdBefore ? lt(heartbeatRuns.createdAt, createdBefore) : undefined, inArray(heartbeatRuns.status, [ ...CANCELLABLE_HEARTBEAT_RUN_STATUSES, ]), @@ -29413,7 +29420,12 @@ export function heartbeatService( error: "Cancelled due to budget pause", updatedAt: now, }) - .where(inArray(agentWakeupRequests.id, wakeupIds)); + .where(and( + inArray(agentWakeupRequests.id, wakeupIds), + inArray(agentWakeupRequests.status, ["queued", "deferred_issue_execution"]), + isNull(agentWakeupRequests.runId), + scope.createdBefore ? lt(agentWakeupRequests.createdAt, scope.createdBefore) : undefined, + )); return wakeupIds.length; } @@ -29883,35 +29895,15 @@ export function heartbeatService( } async function cancelBudgetScopeWork(scope: BudgetEnforcementScope) { - if (scope.scopeType === "agent") { - await cancelActiveForAgentInternal( - scope.scopeId, - "Cancelled due to budget pause", - ); - await cancelPendingWakeupsForBudgetScope(scope); - return; - } - - const runIds = - scope.scopeType === "company" - ? await db - .select({ id: heartbeatRuns.id }) - .from(heartbeatRuns) - .where( - and( - eq(heartbeatRuns.companyId, scope.companyId), - inArray(heartbeatRuns.status, [ - ...CANCELLABLE_HEARTBEAT_RUN_STATUSES, - ]), - ), - ) - .then((rows) => rows.map((row) => row.id)) - : await listProjectScopedRunIds(scope.companyId, scope.scopeId); - - for (const runId of runIds) { - await cancelRunInternal(runId, "Cancelled due to budget pause"); - } - + const runIds = scope.scopeType === "project" + ? await listProjectScopedRunIds(scope.companyId, scope.scopeId, scope.createdBefore) + : await db.select({ id: heartbeatRuns.id }).from(heartbeatRuns).where(and( + eq(heartbeatRuns.companyId, scope.companyId), + scope.scopeType === "agent" ? eq(heartbeatRuns.agentId, scope.scopeId) : undefined, + scope.createdBefore ? lt(heartbeatRuns.createdAt, scope.createdBefore) : undefined, + inArray(heartbeatRuns.status, [...CANCELLABLE_HEARTBEAT_RUN_STATUSES]), + )).then((rows) => rows.map((row) => row.id)); + for (const runId of runIds) await cancelRunInternal(runId, "Cancelled due to budget pause"); await cancelPendingWakeupsForBudgetScope(scope); } @@ -30278,6 +30270,10 @@ export function heartbeatService( scanSilentActiveRuns, reconcileTaskWatchdogs, + reconcileCostAccounting: async () => { + await replayUsageReceipts(db); + return reconcileRunCosts(db, budgetHooks); + }, buildRunOutputSilence, diff --git a/server/src/services/native-runtime/native-session-executor.test.ts b/server/src/services/native-runtime/native-session-executor.test.ts index 4ea8aa1b99b..185f3416533 100644 --- a/server/src/services/native-runtime/native-session-executor.test.ts +++ b/server/src/services/native-runtime/native-session-executor.test.ts @@ -305,6 +305,7 @@ import { nativeUsageCostUsd, nativeUsageBiller, normalizeNativeUsage, + resolveNativeBilling, parseRemoteRunnerProcessIdentity, REMOTE_RUNNER_CHILD_LAUNCH_SCRIPT, verifyRemoteRunnerReattachment, @@ -985,6 +986,35 @@ describe("native provider usage normalization", () => { expect(nativeUsageCostUsd(usage, provider)).toBeUndefined(); expect(normalizeNativeUsage(usage)).toMatchObject({ inputTokens: 100, outputTokens: 12 }); }); + + it("retains cache writes and avoids counting Codex cache hits twice", () => { + expect(normalizeNativeUsage({ runDelta: { inputTokens: 120, cacheReadTokens: 100, cacheWriteTokens: 5, outputTokens: 10 } }, { inputIncludesCacheReads: true })) + .toEqual({ inputTokens: 20, cachedInputTokens: 100, cacheWriteTokens: 5, outputTokens: 10 }); + }); + it("does not treat a protocol-default price as free usage", () => { + expect(nativeUsageCostUsd({ runDelta: { inputTokens: 20, outputTokens: 10, providerCostUsd: 0 } })).toBeUndefined(); + }); + it("reports the actual runtime billing identity", () => { + expect(resolveNativeBilling({ kind: "opencode", model: "openrouter/deepseek/test", permissionMode: "allow" })) + .toEqual({ provider: "deepseek", biller: "openrouter", billingType: "unknown" }); + expect(resolveNativeBilling({ kind: "acpx", agent: "claude", model: "claude-test", permissionMode: "approve-all", + profile: { driverKind: "acpx_runtime", protocolVersion: 1, acpxVersion: "0.13.1", agent: "claude", agentProfileVersion: 1, + agentServerPackage: "@zed-industries/claude-agent-acp", agentServerVersion: "1", agentRuntimePackage: null, + agentRuntimeVersion: null, commandDigest: "fixture" }, + }, { ANTHROPIC_API_KEY: "test" })) + .toEqual({ provider: "anthropic", biller: "anthropic", billingType: "metered_api" }); + }); + + it("prefers the adjusted turn price to cumulative session prices", () => { + expect(nativeUsageCostUsd({ providerCostUsd: 10, runDelta: { inputTokens: 10, providerCostUsd: 2, cacheAdjustedCostUsd: 1 } })).toBe(1); + }); + it.each(["OPENAI_BASE_URL", "OPENAI_API_BASE", "OPENAI_API_BASE_URL"])("does not price native or managed proxy traffic as direct OpenAI using %s", key => { + const provider = { kind: "codex", model: "gpt-6-astra" } as Parameters[0]; + expect(resolveNativeBilling(provider, { OPENAI_API_KEY: "fixture", [key]: "https://proxy.example/v1" }).biller).toBe("unknown"); + expect(resolveNativeBilling(provider, { [key]: "https://proxy.example/v1" }, { provider: "openai", biller: "openai", billingType: "metered_api" }).biller).toBe("unknown"); + expect(resolveNativeBilling(provider, {}, { provider: "openai", biller: "openai", billingType: "metered_api" })).toMatchObject({ biller: "openai", billingType: "metered_api" }); + }); + it("reads remote runner run-delta tokens and provider cost", () => { const usage = { total: { diff --git a/server/src/services/native-runtime/native-session-executor.ts b/server/src/services/native-runtime/native-session-executor.ts index 11c1e5256e4..4ba162a2e29 100644 --- a/server/src/services/native-runtime/native-session-executor.ts +++ b/server/src/services/native-runtime/native-session-executor.ts @@ -3,6 +3,7 @@ import { readLocalAiCredentialFile } from "../local-ai-credential-file.js"; import { prepareGrokRunnerCredentials } from "./grok-runner-credentials.js"; import { copyBackGrokAuth } from "@paperclipai/adapter-grok-local/server"; +import { inferOpenAiCompatibleBiller, type AdapterUsageCheckpoint } from "@paperclipai/adapter-utils"; import { isSupportedRemoteCodexVersion, parseCodexCliVersion, @@ -7256,6 +7257,7 @@ export async function executePaperclipNativeSession(input: { collectStopped: () => Promise; }; /** Persist task-level continuity before a durable goal can outlive this run. */ + onUsage?: (receipt: AdapterUsageCheckpoint) => Promise; onGoalCheckpoint?: (snapshot: PersistedNativeSession) => Promise; sessionGoalControl?: NativeSessionGoalControl | null; resumeSessionGoalHeartbeat?: boolean; @@ -7267,6 +7269,7 @@ export async function executePaperclipNativeSession(input: { /** Private grant materialization; never a user-configured host path. */ managedAiCredentialHome?: string; managedAiCredentialIdentity?: string; + billingIdentity?: Pick; runnerExecutionTarget?: AdapterExecutionTarget | null; /** Resolved per-run authorization; not an independent instance setting. */ runnerIngressAuthorized?: boolean; @@ -8597,6 +8600,20 @@ async function executePaperclipNativeSessionWithinScope( }, { parentName: "task.run" }, ); + // Persist provider accounting before any workspace/issue finalization. A + // detached controller or failed finalizer must not lose a completed turn. + const accountingUsage = normalizeNativeUsage(native.usage, { inputIncludesCacheReads: input.execution.provider.kind === "codex" }); + const accountingCost = nativeUsageCostUsd(native.usage, input.execution.provider); + const billing = resolveNativeBilling(input.execution.provider, input.runnerEnvironment, input.billingIdentity); + await input.onUsage?.({ usage: accountingUsage, ...billing, complete: true, usageBasis: "per_run", + model: input.execution.provider.model ?? "unknown", costUsd: accountingCost ?? null }); + if (!input.onUsage) await input.db.update(heartbeatRuns).set({ + costAccountingPending: true, + usageJson: sql`coalesce(${heartbeatRuns.usageJson}, '{}'::jsonb) || ${JSON.stringify({ + ...accountingUsage, ...billing, accountingReceiptReady: true, model: input.execution.provider.model ?? "unknown", + costUsd: accountingCost ?? null, usageSource: "per_run", + })}::jsonb`, + }).where(and(eq(heartbeatRuns.id, input.execution.binding.runId), eq(heartbeatRuns.companyId, input.execution.binding.companyId), isNull(heartbeatRuns.costAccountedAt))); try { await completeManagedNativeCredentialTurn(managedCredentialSession); } catch { @@ -9219,9 +9236,9 @@ async function executePaperclipNativeSessionWithinScope( summary: native.result.summary, sessionId: native.normalizedSessionId, sessionDisplayId: native.providerSessionId ?? native.normalizedSessionId, - provider: nativeUsageBiller(input.execution.provider), + ...resolveNativeBilling(input.execution.provider, input.runnerEnvironment, input.billingIdentity), model: input.execution.provider.model, - usage: normalizeNativeUsage(native.usage), + usage: normalizeNativeUsage(native.usage, { inputIncludesCacheReads: input.execution.provider.kind === "codex" }), costUsd: nativeUsageCostUsd(native.usage, input.execution.provider), usageBasis: "per_run", nativeFinalization: finalization, @@ -9297,18 +9314,14 @@ export function nativeUsageCostUsd( if (provider?.kind === "acpx" && ["cursor", "copilot", "pi"].includes(provider.agent)) return undefined; if (!usage) return undefined; const measurement = nativeUsageMeasurement(usage); - const direct = - numericUsageField(usage, [ - "providerCostUsd", - "cacheAdjustedCostUsd", - "costUsd", - ]) ?? - numericUsageField(measurement, [ - "providerCostUsd", - "cacheAdjustedCostUsd", - "costUsd", - ]); - if (direct !== undefined) return direct; + const direct = numericUsageField(measurement, ["cacheAdjustedCostUsd", "providerCostUsd", "costUsd"]) + ?? numericUsageField(usage, ["cacheAdjustedCostUsd", "providerCostUsd", "costUsd"]); + // PRP currently defaults an absent providerCostUsd to zero. That zero is + // not proof of a free request when the runtime observed token consumption. + if (direct !== undefined && direct > 0) return direct; + if (direct === 0 && (numericUsageField(measurement, ["inputTokens"]) ?? 0) === 0 + && (numericUsageField(measurement, ["outputTokens"]) ?? 0) === 0 + && (numericUsageField(measurement, ["cacheReadTokens", "cachedInputTokens"]) ?? 0) === 0) return 0; const cost = record(usage.cost); const currency = typeof cost.currency === "string" ? cost.currency.toUpperCase() : "USD"; @@ -9316,7 +9329,34 @@ export function nativeUsageCostUsd( return numericUsageField(cost, ["amount", "total"]); } -export function normalizeNativeUsage(usage: Record | null) { +/** Billing identity follows the selected runtime and resolved credentials. + * Unknown authentication remains unknown; a provider name does not prove billing mode. */ +export function resolveNativeBilling(provider: NativeExecutionInput["provider"], env: NodeJS.ProcessEnv = {}, identity?: Pick): Pick { + if (identity) return identity.provider === "openai" && identity.billingType === "metered_api" + ? { ...identity, biller: inferOpenAiCompatibleBiller(env, identity.biller ?? "unknown") } + : identity; + if (provider.kind === "claude_managed") return { provider: "anthropic", biller: "anthropic", billingType: "metered_api" }; + if (provider.kind === "aws_agentcore") return { provider: "aws_agentcore", biller: "aws_agentcore", billingType: "metered_api" }; + if (provider.kind === "acpx" && ["cursor", "copilot"].includes(provider.agent)) { + const biller = nativeUsageBiller(provider); + return { provider: biller, biller, billingType: "unknown" }; + } + if (provider.kind === "acpx" && provider.agent === "claude") { + if (env.CLAUDE_CODE_USE_BEDROCK === "1") return { provider: "anthropic", biller: "aws_bedrock", billingType: "metered_api" }; + return { provider: "anthropic", biller: "anthropic", billingType: env.ANTHROPIC_API_KEY ? "metered_api" : env.CLAUDE_CODE_OAUTH_TOKEN ? "subscription_included" : "unknown" }; + } + if (provider.kind === "opencode" || (provider.kind === "acpx" && provider.agent === "pi")) { + const parts = (provider.model ?? "").split("/"); + const biller = parts.length > 1 ? parts[0] : provider.kind === "acpx" ? nativeUsageBiller(provider) : "unknown"; + const modelProvider = biller === "openrouter" && parts.length > 2 ? parts[1] : biller; + return { provider: modelProvider, biller, billingType: "unknown" }; + } + if (provider.kind !== "codex" && !(provider.kind === "acpx" && provider.agent === "codex")) + return { provider: "unknown", biller: "unknown", billingType: "unknown" }; + return { provider: "openai", biller: env.OPENAI_API_KEY ? inferOpenAiCompatibleBiller(env, "openai") : "unknown", billingType: env.OPENAI_API_KEY ? "metered_api" : "unknown" }; +} + +export function normalizeNativeUsage(usage: Record | null, options: { inputIncludesCacheReads?: boolean } = {}) { if (!usage) return undefined; const measurement = nativeUsageMeasurement(usage); const cache = record(measurement.cache); @@ -9327,13 +9367,13 @@ export function normalizeNativeUsage(usage: Record | null) { "cacheReadTokens", "cachedReadTokens", ]) ?? numericUsageField(cache, ["read"]); + const cacheWriteTokens = numericUsageField(measurement, ["cacheWriteTokens", "cacheCreationInputTokens", "cachedWriteTokens"]) ?? numericUsageField(cache, ["write"]); return { - inputTokens: - numericUsageField(measurement, [ - "inputTokens", - "input", - "promptTokens", - ]) ?? 0, + ...(cacheWriteTokens === undefined ? {} : { cacheWriteTokens }), + inputTokens: Math.max(0, + (numericUsageField(measurement, ["inputTokens", "input", "promptTokens"]) ?? 0) + - (options.inputIncludesCacheReads ? cachedInputTokens ?? 0 : 0) + ) + (options.inputIncludesCacheReads ? 0 : cacheWriteTokens ?? 0), outputTokens: numericUsageField(measurement, [ "outputTokens", diff --git a/server/src/services/plugin-managed-agents.ts b/server/src/services/plugin-managed-agents.ts index f86f7a439db..4c4e2ee1aeb 100644 --- a/server/src/services/plugin-managed-agents.ts +++ b/server/src/services/plugin-managed-agents.ts @@ -337,7 +337,7 @@ export function pluginManagedAgentService( // and its canonical history. Transfer only its proven stable-key marker to // the replacement installation, atomically with the new scoped bindings. await upsertBinding(companyId, declaration, agentId, {}, adapterType, tx); - const [relinked] = await tx.update(agents).set({ + await tx.update(agents).set({ metadata: managedMetadata(options.pluginId, options.pluginKey, declaration, agent.metadata), updatedAt: new Date(), }).where(and(eq(agents.id, agentId), eq(agents.companyId, companyId))).returning(); @@ -346,6 +346,8 @@ export function pluginManagedAgentService( action: "plugin.managed_agent.relinked", entityType: "agent", entityId: agentId, details: { sourcePluginKey: options.pluginKey, managedResourceKey: declaration.agentKey, previousPluginId }, }); + const relinked = await agentService(tx as unknown as Db).getById(agentId); + if (!relinked) throw notFound("Managed agent not found after relink"); return relinked as Agent; }); } diff --git a/server/src/services/provider-billing-import.ts b/server/src/services/provider-billing-import.ts new file mode 100644 index 00000000000..866fb7a7a9a --- /dev/null +++ b/server/src/services/provider-billing-import.ts @@ -0,0 +1,367 @@ +import { z } from "zod"; +import { and, eq, gte, inArray, lt } from "drizzle-orm"; +import { + companySecrets, + providerBillingSnapshots, + type Db, +} from "@paperclipai/db"; +import { + centsToUnits, + usdToUnits, + unitsToCents, + importProviderCostsSchema, + type ImportProviderCosts, +} from "@paperclipai/shared"; +import { secretService } from "./secrets.js"; +import { withAccountingTransaction } from "./accounting-transaction.js"; +import { createFinanceEventInTransaction } from "./finance.js"; +import { forbidden, unprocessable } from "../errors.js"; + +const pageSchema = z.object({ + data: z + .array( + z.object({ + start_time: z.number().int().optional(), + end_time: z.number().int().optional(), + starting_at: z.iso.datetime({ offset: true }).optional(), + ending_at: z.iso.datetime({ offset: true }).optional(), + results: z + .array( + z.object({ + project_id: z.string().nullable().optional(), + workspace_id: z.string().nullable().optional(), + amount: z.union([ + z.string(), + z.object({ value: z.number().finite(), currency: z.string() }), + ]), + currency: z.string().optional(), + }), + ) + .max(5000), + }), + ) + .max(31), + has_more: z.boolean(), + next_page: z.string().max(4000).nullable().optional(), +}); +export interface DailyProviderCost { + day: string; + scopeId: string; + amountCents: string; +} + +async function boundedJson(response: Response) { + if (!response.ok || !response.body) + throw unprocessable( + "Provider billing is unavailable. Check the admin credential and selected account.", + ); + const reader = response.body.getReader(); + const chunks: Uint8Array[] = []; + let size = 0; + try { + for (;;) { + const next = await reader.read(); + if (next.done) break; + size += next.value.byteLength; + if (size > 2_000_000) + throw unprocessable( + "Provider billing response is too large; use a shorter period.", + ); + chunks.push(next.value); + } + return JSON.parse(Buffer.concat(chunks).toString("utf8")); + } finally { + await reader.cancel().catch(() => undefined); + } +} + +/** Complete every page before writing. Group only by project/workspace so a + * provider's line-item changes cannot manufacture duplicate charges. */ +export async function fetchProviderDailyCosts( + input: ImportProviderCosts, + credential: string, + fetcher = fetch, +): Promise { + const parsed = importProviderCostsSchema.parse(input); + const from = Date.parse(parsed.from), + to = Date.parse(parsed.to); + const base = new URL( + parsed.provider === "openai" + ? "https://api.openai.com/v1/organization/costs" + : "https://api.anthropic.com/v1/organizations/cost_report", + ); + base.searchParams.set( + parsed.provider === "openai" ? "start_time" : "starting_at", + parsed.provider === "openai" + ? String(from / 1000) + : new Date(from).toISOString(), + ); + base.searchParams.set( + parsed.provider === "openai" ? "end_time" : "ending_at", + parsed.provider === "openai" + ? String(to / 1000) + : new Date(to).toISOString(), + ); + base.searchParams.set("bucket_width", "1d"); + base.searchParams.set("limit", "31"); + base.searchParams.append( + "group_by[]", + parsed.provider === "openai" ? "project_id" : "workspace_id", + ); + const headers: Record = + parsed.provider === "openai" + ? { + Authorization: `Bearer ${credential}`, + "OpenAI-Organization": parsed.accountId, + } + : { "x-api-key": credential, "anthropic-version": "2023-06-01" }; + headers["User-Agent"] = "Paperclip/1.0 (https://paperclip.ing)"; + const totals = new Map(); + const seen = new Set(); + const days = new Set(); + // Missing requested scopes in an otherwise complete daily report mean zero. + for (let time = from; time < to; time += 86400000) + for (const scope of parsed.scopeIds) + totals.set(`${new Date(time).toISOString().slice(0, 10)}:${scope}`, 0n); + const cursors = new Set(); + const signal = AbortSignal.timeout(30_000); + if (parsed.provider === "anthropic") { + const identity = z.object({ id: z.string() }).parse( + await boundedJson( + await fetcher("https://api.anthropic.com/v1/organizations/me", { + headers, + signal, + redirect: "error", + }), + ), + ); + if (identity.id !== parsed.accountId) + throw unprocessable( + "The admin credential belongs to a different provider organization.", + ); + } + for (let page = 0; page < 100; page++) { + const response = await fetcher(base, { + headers, + signal, + redirect: "error", + }); + const body = pageSchema.parse(await boundedJson(response)); + for (const bucket of body.data) { + const start = + parsed.provider === "openai" + ? (bucket.start_time ?? NaN) * 1000 + : Date.parse(bucket.starting_at ?? ""); + const end = + parsed.provider === "openai" + ? (bucket.end_time ?? NaN) * 1000 + : Date.parse(bucket.ending_at ?? ""); + if ( + !Number.isFinite(start) || + start < from || + end > to || + end - start !== 86400000 || + start % 86400000 !== 0 + ) + throw unprocessable("Provider returned an unexpected billing period."); + days.add(start); + for (const result of bucket.results) { + const scopeField = parsed.provider === "openai" ? "project_id" : "workspace_id"; + if (!Object.hasOwn(result, scopeField)) { + throw unprocessable("Provider report is missing the requested project/workspace grouping."); + } + const scope = + (parsed.provider === "openai" + ? result.project_id + : result.workspace_id) ?? "default"; + if (!parsed.scopeIds.includes(scope)) continue; + const identity = `${new Date(start).toISOString().slice(0, 10)}:${scope}`; + if (seen.has(identity)) + throw unprocessable("Provider returned overlapping billing buckets."); + seen.add(identity); + const currency = + typeof result.amount === "object" + ? result.amount.currency + : result.currency; + if (currency?.toUpperCase() !== "USD") + throw unprocessable("Only USD provider cost reports are supported."); + const units = + parsed.provider === "openai" && typeof result.amount === "object" + ? usdToUnits(result.amount.value) + : parsed.provider === "anthropic" && + typeof result.amount === "string" + ? centsToUnits(result.amount) + : -1n; + if (units < 0n) + throw unprocessable("Provider returned an invalid billing amount."); + totals.set(identity, units); + } + } + if (!body.has_more) { + if (days.size !== (to - from) / 86400000) + throw unprocessable( + "Provider report is incomplete; no financial events were changed.", + ); + return [...totals].map(([key, value]) => ({ + day: key.slice(0, 10), + scopeId: key.slice(11), + amountCents: unitsToCents(value), + })); + } + if (!body.next_page || cursors.has(body.next_page)) + throw unprocessable("Provider returned an invalid billing cursor."); + cursors.add(body.next_page); + base.searchParams.set("page", body.next_page); + } + throw unprocessable("Provider billing report exceeded the page limit."); +} + +export async function importProviderDailyCosts( + db: Db, + companyId: string, + raw: ImportProviderCosts, + actorId: string, + fetcher = fetch, +) { + const input = importProviderCostsSchema.parse(raw); + const [secret] = await db + .select() + .from(companySecrets) + .where( + and( + eq(companySecrets.id, input.secretId), + eq(companySecrets.companyId, companyId), + ), + ); + if (!secret || secret.scope !== "company") + throw forbidden("Select a company billing credential."); + const credential = await secretService(db).resolveSecretValue( + companyId, + secret.id, + "latest", + { + accessContext: { + consumerType: "system", + consumerId: "provider-billing-import", + actorType: "user", + actorId, + }, + }, + ); + // Compare database revisions, not application clocks: concurrent importers + // can run on different hosts or start in the same millisecond. + const snapshots = await db + .select() + .from(providerBillingSnapshots) + .where( + and( + eq(providerBillingSnapshots.companyId, companyId), + eq(providerBillingSnapshots.provider, input.provider), + eq(providerBillingSnapshots.accountId, input.accountId), + inArray(providerBillingSnapshots.scopeId, input.scopeIds), + gte(providerBillingSnapshots.day, input.from), + lt(providerBillingSnapshots.day, input.to), + ), + ); + const expectedRevisions = new Map( + snapshots.map((row) => [`${row.day}:${row.scopeId}`, row.revision]), + ); + const daily = await fetchProviderDailyCosts(input, credential, fetcher); + return applyProviderDailyCosts( + db, + companyId, + input, + daily, + actorId, + expectedRevisions, + ); +} + +export async function applyProviderDailyCosts( + db: Db, + companyId: string, + input: ImportProviderCosts, + daily: DailyProviderCost[], + actorId: string, + expectedRevisions?: ReadonlyMap, +) { + return withAccountingTransaction(db, companyId, async (tx, publications) => { + let eventsCreated = 0; + for (const day of daily) { + const where = and( + eq(providerBillingSnapshots.companyId, companyId), + eq(providerBillingSnapshots.provider, input.provider), + eq(providerBillingSnapshots.accountId, input.accountId), + eq(providerBillingSnapshots.scopeId, day.scopeId), + eq(providerBillingSnapshots.day, day.day), + ); + const [previous] = await tx + .select() + .from(providerBillingSnapshots) + .where(where); + const delta = + centsToUnits(day.amountCents) - + centsToUnits(previous?.amountCents ?? "0"); + if (previous && delta === 0n) continue; + if ( + expectedRevisions && + (previous?.revision ?? 0) !== + (expectedRevisions.get(`${day.day}:${day.scopeId}`) ?? 0) + ) + throw unprocessable( + "A newer provider report was imported. Fetch the report again.", + ); + const revision = (previous?.revision ?? 0) + 1; + const [snapshot] = previous + ? await tx + .update(providerBillingSnapshots) + .set({ + amountCents: day.amountCents, + revision, + updatedAt: new Date(), + }) + .where(where) + .returning() + : await tx + .insert(providerBillingSnapshots) + .values({ + companyId, + provider: input.provider, + accountId: input.accountId, + ...day, + revision, + }) + .returning(); + if (!delta) continue; + await createFinanceEventInTransaction( + tx, + publications, + companyId, + { + idempotencyKey: `provider-report:${snapshot.id}:${revision}`, + biller: input.provider, + eventKind: "inference_charge", + direction: delta < 0n ? "credit" : "debit", + currency: "USD", + amountCents: unitsToCents(delta < 0n ? -delta : delta), + occurredAt: new Date(`${day.day}T00:00:00.000Z`), + description: + `${input.provider} cost report · ${day.scopeId} · ${day.day}${revision > 1 ? " (correction)" : ""}`.slice( + 0, + 500, + ), + metadataJson: { + source: "provider_cost_report", + accountId: input.accountId, + scopeId: day.scopeId, + revision, + totalCents: day.amountCents, + }, + }, + { actorType: "user", actorId }, + ); + eventsCreated++; + } + return { daysRead: daily.length, eventsCreated }; + }); +} diff --git a/server/src/services/receipt-fingerprint.ts b/server/src/services/receipt-fingerprint.ts new file mode 100644 index 00000000000..d70acc6b332 --- /dev/null +++ b/server/src/services/receipt-fingerprint.ts @@ -0,0 +1,12 @@ +import { createHash } from "node:crypto"; + +function canonical(value: unknown): unknown { + if (value instanceof Date) return value.toISOString(); + if (Array.isArray(value)) return value.map(canonical); + if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, canonical(item)])); + return value; +} + +export function receiptFingerprint(value: unknown): string { + return createHash("sha256").update(JSON.stringify(canonical(value))).digest("hex"); +} diff --git a/server/src/services/run-cost-accounting.ts b/server/src/services/run-cost-accounting.ts new file mode 100644 index 00000000000..e4d643f92d5 --- /dev/null +++ b/server/src/services/run-cost-accounting.ts @@ -0,0 +1,131 @@ +import { usdToUnits, unitsToCents, centsToUnits } from "@paperclipai/shared"; +import { and, asc, eq, inArray, sql } from "drizzle-orm"; +import { accountingRuntimeBaselines, budgetReservations, agentRuntimeState, agents, heartbeatRuns, issues, projects, type Db } from "@paperclipai/db"; +import { withAccountingTransaction } from "./accounting-transaction.js"; +import { createCostEventInTransaction } from "./costs.js"; +import { budgetService, deliverBudgetEnforcement, type BudgetServiceHooks } from "./budgets.js"; +import { logger } from "../middleware/logger.js"; + +const terminalStatuses = ["succeeded", "failed", "timed_out", "cancelled"]; +const object = (value: unknown): Record => value && typeof value === "object" && !Array.isArray(value) ? value as Record : {}; +const text = (value: unknown) => typeof value === "string" && value.length > 0 ? value : null; +const amount = (value: unknown) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null; + +/** The run's persisted usage is a durable receipt. Ledger, runtime totals, and + * acknowledgement commit together, including after a restart or failed run. */ +export async function accountRunCost(db: Db, runId: string, hooks: BudgetServiceHooks = {}) { + const [identity] = await db.select({ companyId: heartbeatRuns.companyId }).from(heartbeatRuns).where(eq(heartbeatRuns.id, runId)); + if (!identity) return false; + const accounted = await withAccountingTransaction(db, identity.companyId, async (tx, publications) => { + const [run] = await tx.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, runId)).for("update"); + if (!run?.costAccountingPending || !terminalStatuses.includes(run.status)) return false; + if (run.costAccountedAt) { + await tx.update(heartbeatRuns).set({ costAccountingPending: false }).where(eq(heartbeatRuns.id, run.id)); + return false; + } + const preProviderFailure = object(object(run.resultJson).executionRecovery).providerWorkStarted === false + || object(run.usageJson).accountingProviderWorkStarted === false; + // A stop can mark the run terminal before the provider's shutdown receipt + // arrives. Keep the debt pending instead of acknowledging an invented zero + // and permanently discarding that late receipt. + if (!preProviderFailure && object(run.usageJson).accountingReceiptReady === false) return false; + // Acknowledgement is part of the same transaction. Budget checks inside + // this transaction must not mistake this receipt for missing accounting. + await tx.update(heartbeatRuns).set({ costAccountingPending: false, costAccountedAt: new Date() }).where(eq(heartbeatRuns.id, run.id)); + await tx.update(budgetReservations).set({ state: preProviderFailure ? "released" : "settled", settledAt: new Date() }) + .where(and(eq(budgetReservations.companyId, run.companyId), eq(budgetReservations.runId, run.id), eq(budgetReservations.state, "held"))); + if (preProviderFailure) return true; + const usage = object(run.usageJson); + const scope = object(usage.ledgerScope); + // Deleted attribution targets become unallocated; a deletion must never + // prevent recording provider spend. Foreign-company references still fail. + for (const [table, key] of [[issues, "issueId"], [projects, "projectId"]] as const) { + const id = text(scope[key]); + if (id) { + const [linked] = await tx.select({ id: table.id }).from(table).where(eq(table.id, id)); + if (!linked) scope[key] = null; + } + } + const costUsd = amount(usage.cacheAdjustedCostUsd) ?? text(usage.costUsdExact) ?? amount(usage.costUsd); + const billingType = text(usage.billingType) ?? "unknown"; + const costUnits = billingType === "subscription_included" ? 0n : usdToUnits(costUsd ?? 0); + const costCents = unitsToCents(costUnits); + const receipt = { + idempotencyKey: `heartbeat:${run.id}:final`, + agentId: run.agentId, heartbeatRunId: run.id, + issueId: text(scope.issueId), projectId: text(scope.projectId), billingCode: text(scope.billingCode), + provider: text(usage.provider) ?? "unknown", biller: text(usage.biller) ?? text(usage.provider) ?? "unknown", + providerRequestId: text(usage.providerRequestId), + pricingProvenance: object(usage.pricingProvenance), + model: text(usage.model) ?? "unknown", billingType, + costStatus: costUsd === null && billingType !== "subscription_included" ? "unpriced" : usage.costStatus === "estimated" ? "estimated" : "reported", + inputTokens: amount(usage.inputTokens) ?? 0, cachedInputTokens: amount(usage.cachedInputTokens) ?? 0, outputTokens: amount(usage.outputTokens) ?? 0, + costCents, occurredAt: run.finishedAt ?? run.createdAt, + }; + const parts = Array.isArray(usage.usageByModel) ? usage.usageByModel.map(object) : []; + const tokensMatch = ["inputTokens", "cachedInputTokens", "outputTokens"].every((key) => + parts.reduce((sum, part) => sum + (amount(object(part.usage)[key]) ?? 0), 0) === (amount(usage[key]) ?? 0)); + const costMatches = costUsd !== null && parts.reduce((sum, part) => sum + usdToUnits(amount(part.costUsd) ?? 0), 0n) === usdToUnits(costUsd); + const validParts = parts.length > 0 && tokensMatch && costMatches + && parts.every((part) => text(part.model) && amount(part.costUsd) !== null) + && new Set(parts.map((part) => part.model)).size === parts.length; + const events = []; + if (validParts) { + let allocated = 0n; + for (const [index, part] of parts.entries()) { + const partUsage = object(part.usage); + const remainder = costUnits - allocated; + const reported = usdToUnits(amount(part.costUsd) ?? 0); + const partUnits = billingType === "subscription_included" ? 0n : index === parts.length - 1 ? remainder : reported < remainder ? reported : remainder; + allocated += partUnits; + const partCents = unitsToCents(partUnits); + events.push(await createCostEventInTransaction(tx, run.companyId, { + ...receipt, idempotencyKey: `heartbeat:${run.id}:model:${index}`, model: text(part.model)!, + inputTokens: amount(partUsage.inputTokens) ?? 0, cachedInputTokens: amount(partUsage.cachedInputTokens) ?? 0, + outputTokens: amount(partUsage.outputTokens) ?? 0, costCents: partCents, + }, publications)); + } + } else { + events.push(await createCostEventInTransaction(tx, run.companyId, receipt, publications)); + } + const event = events.reduce((total, part) => ({ + inputTokens: total.inputTokens + part.inputTokens, cachedInputTokens: total.cachedInputTokens + part.cachedInputTokens, + outputTokens: total.outputTokens + part.outputTokens, costCents: total.costCents + centsToUnits(part.costCentsExact), + }), { inputTokens: 0, cachedInputTokens: 0, outputTokens: 0, costCents: 0n }); + const [agent] = await tx.select({ adapterType: agents.adapterType }).from(agents).where(eq(agents.id, run.agentId)); + await tx.insert(agentRuntimeState).values({ agentId: run.agentId, companyId: run.companyId, adapterType: agent.adapterType }).onConflictDoNothing(); + await tx.execute(sql`insert into accounting_runtime_baselines (agent_id,company_id,cost_cents,input_tokens,cached_input_tokens,output_tokens) + select agent_id,company_id,total_cost_cents,total_input_tokens,total_cached_input_tokens,total_output_tokens + from agent_runtime_state where agent_id = ${run.agentId} on conflict do nothing`); + await tx.update(agentRuntimeState).set({ + totalInputTokens: sql`${agentRuntimeState.totalInputTokens} + ${event.inputTokens}`, + totalCachedInputTokens: sql`${agentRuntimeState.totalCachedInputTokens} + ${event.cachedInputTokens}`, + totalOutputTokens: sql`${agentRuntimeState.totalOutputTokens} + ${event.outputTokens}`, + totalCostCents: sql`${agentRuntimeState.totalCostCents} + ${unitsToCents(event.costCents)}::numeric`, + updatedAt: new Date(), + }).where(eq(agentRuntimeState.agentId, run.agentId)); + await tx.update(heartbeatRuns).set({ costAccountingPending: false, costAccountedAt: new Date(), accountingProjectionVersion: "v2", accountingLastError: null, accountingLastAttemptAt: new Date() }).where(eq(heartbeatRuns.id, run.id)); + return true; + }); + await deliverBudgetEnforcement(db, hooks, identity.companyId); + return accounted; +} + +export async function reconcileRunCosts(db: Db, hooks: BudgetServiceHooks = {}) { + const pending = await db.select({ id: heartbeatRuns.id }).from(heartbeatRuns).where(and( + eq(heartbeatRuns.costAccountingPending, true), inArray(heartbeatRuns.status, terminalStatuses), + sql`(${heartbeatRuns.usageJson}->>'accountingReceiptReady' is distinct from 'false' or ${heartbeatRuns.resultJson}->'executionRecovery'->>'providerWorkStarted' = 'false' or ${heartbeatRuns.usageJson}->>'accountingProviderWorkStarted' = 'false')`, + )).orderBy(asc(heartbeatRuns.updatedAt), asc(heartbeatRuns.id)).limit(100); + let accounted = 0; + for (const run of pending) { + try { if (await accountRunCost(db, run.id, hooks)) accounted++; } + catch (error) { + // Move failed attempts behind other pending work so a poisoned receipt + // cannot monopolize the bounded batch on every recovery tick. + await db.update(heartbeatRuns).set({ updatedAt: new Date(), accountingLastAttemptAt: new Date(), accountingAttemptCount: sql`${heartbeatRuns.accountingAttemptCount} + 1`, accountingLastError: error instanceof Error ? error.message.slice(0, 1000) : "Accounting failed" }).where(eq(heartbeatRuns.id, run.id)); + logger.error({ err: error, runId: run.id }, "Run accounting remains pending for retry"); + } + } + await budgetService(db, hooks).reconcilePolicies(); + return { scanned: pending.length, accounted }; +} diff --git a/server/src/services/runtime-skill-cache.ts b/server/src/services/runtime-skill-cache.ts index 5bade582ab7..010fb28bf1f 100644 --- a/server/src/services/runtime-skill-cache.ts +++ b/server/src/services/runtime-skill-cache.ts @@ -165,6 +165,18 @@ async function setTreeMode(directory: string, readonly: boolean): Promise if (readonly) await fs.chmod(directory, 0o555); } +// Darwin requires a directory being renamed to be writable by its owner. +// Change only the directory itself, while the publication lock is held; the +// contents remain read-only. Restore the mode even when publication fails. +async function renameCacheDirectory(source: string, destination: string) { + const stat = await fs.lstat(source); + const directory = stat.isDirectory() && !stat.isSymbolicLink(); + if (directory) await fs.chmod(source, 0o700); + let moved = false; + try { await fs.rename(source, destination); moved = true; } + finally { if (directory) await fs.chmod(moved ? destination : source, stat.mode & 0o777); } +} + async function removeTree(directory: string): Promise { await setTreeMode(directory, false); await fs.rm(directory, { recursive: true, force: true }); @@ -202,9 +214,9 @@ export async function resolveRuntimeSkillCache( if (!await matches(spec, staging)) throw new Error("Runtime skill cache validation failed"); // Lifecycle mutations can update the DB while this builder owns the filesystem lock. if (!await stillInstalled()) throw new Error("Skill was renamed or removed during preparation"); - await fs.rename(spec.entry, path.join(spec.root, `.invalid-${spec.fingerprint}-${randomUUID()}`)) + await renameCacheDirectory(spec.entry, path.join(spec.root, `.invalid-${spec.fingerprint}-${randomUUID()}`)) .catch((error: NodeJS.ErrnoException) => { if (error.code !== "ENOENT") throw error; }); - await fs.rename(staging, spec.entry); + await renameCacheDirectory(staging, spec.entry); return path.join(spec.entry, "files"); } finally { await removeTree(staging); } }); diff --git a/server/src/services/status-cards.ts b/server/src/services/status-cards.ts index 025fd0dd188..ce109509077 100644 --- a/server/src/services/status-cards.ts +++ b/server/src/services/status-cards.ts @@ -877,7 +877,7 @@ export function statusCardService( ? await tx.select({ inputTokens: sql`coalesce(sum(${costEvents.inputTokens}), 0)::int`, outputTokens: sql`coalesce(sum(${costEvents.outputTokens}), 0)::int`, - costCents: sql`coalesce(sum(${costEvents.costCents}), 0)::int`, + costCents: sql`coalesce(sum(${costEvents.costCents}), 0)::double precision`, }).from(costEvents).where(eq(costEvents.heartbeatRunId, actor.runId)) : []; const existingUpdate = await tx.select().from(statusCardUpdates) diff --git a/server/src/services/usage-receipts.ts b/server/src/services/usage-receipts.ts new file mode 100644 index 00000000000..c629b41c567 --- /dev/null +++ b/server/src/services/usage-receipts.ts @@ -0,0 +1,185 @@ +import { priceCodexReceipt } from "./codex-pricing.js"; +import { randomUUID } from "node:crypto"; +import { promises as fs } from "node:fs"; +import path from "node:path"; +import { z } from "zod"; +import { and, eq, isNull, sql } from "drizzle-orm"; +import { heartbeatRuns, runUsageReceipts, type Db } from "@paperclipai/db"; +import type { AdapterExecutionResult, AdapterUsageCheckpoint } from "@paperclipai/adapter-utils"; +import { pricingProvenanceSchema, usdToUnits } from "@paperclipai/shared"; +import { resolvePaperclipInstanceRoot } from "../home-paths.js"; +import { conflict, notFound } from "../errors.js"; +import { logger } from "../middleware/logger.js"; +import { receiptFingerprint } from "./receipt-fingerprint.js"; +import { withAccountingTransaction } from "./accounting-transaction.js"; + +const token = z.number().int().nonnegative().max(2_147_483_647); +const price = z.number().finite().nonnegative().nullable().optional(); +const usage = z.object({ inputTokens: token, outputTokens: token, cachedInputTokens: token.optional(), cacheWriteTokens: token.optional() }); +const checkpointSchema = z.object({ + attemptId: z.string().uuid().optional(), usage: usage.optional(), + usageByModel: z.array(z.object({ model: z.string().min(1).max(250), usage, costUsd: z.number().finite().nonnegative() })).max(500).optional(), + usageBasis: z.enum(["per_run", "session_cumulative"]).nullable().optional(), + provider: z.string().max(250).nullable().optional(), biller: z.string().max(250).nullable().optional(), + model: z.string().max(250).nullable().optional(), billingType: z.string().max(100).nullable().optional(), + costUsd: price, cacheAdjustedCostUsd: price, + costStatus: z.enum(["reported", "estimated", "unpriced"]).optional(), + pricingContext: z.object({ serviceTier: z.string().max(50).optional(), contextTier: z.enum(["short", "long"]).optional() }).optional(), + pricingProvenance: pricingProvenanceSchema.optional(), + costUsdExact: z.string().max(160).refine(value => { try { return !value.startsWith("-") && usdToUnits(value) >= 0n; } catch { return false; } }, "Invalid exact USD amount").nullable().optional(), + providerRequestId: z.string().max(250).nullable().optional(), complete: z.boolean(), +}); +const envelopeSchema = z.object({ + schema: z.literal("paperclip/accounting-receipt/v1"), id: z.string().uuid(), companyId: z.string().uuid(), + runId: z.string().uuid(), sourceId: z.string().uuid(), sequence: z.number().int().positive(), receivedAt: z.iso.datetime(), + adapterType: z.string().max(100), receipt: checkpointSchema, +}); +export type UsageReceiptEnvelope = z.infer; +export const usageReceiptSpoolPath = () => path.join(resolvePaperclipInstanceRoot(), "accounting-receipts"); + +async function syncDirectory(directory: string) { + if (process.platform === "win32") return; + const handle = await fs.open(directory, "r"); + try { await handle.sync(); } finally { await handle.close(); } +} + +export async function spoolUsageReceipt(envelope: UsageReceiptEnvelope, directory = usageReceiptSpoolPath()) { + const parsed = envelopeSchema.parse(envelope); + await fs.mkdir(directory, { recursive: true, mode: 0o700 }); + const target = path.join(directory, `${parsed.id}.json`); + const temporary = `${target}.${randomUUID()}.tmp`; + const handle = await fs.open(temporary, "wx", 0o600); + try { + try { await handle.writeFile(JSON.stringify(parsed)); await handle.sync(); } + finally { await handle.close(); } + await fs.rename(temporary, target); await syncDirectory(directory); + } catch (error) { await fs.rm(temporary, { force: true }); throw error; } + return target; +} + +export async function persistUsageReceipt(db: Db, raw: UsageReceiptEnvelope) { + const envelope = envelopeSchema.parse(raw); + const hash = receiptFingerprint(envelope); + return withAccountingTransaction(db, envelope.companyId, async tx => { + const [run] = await tx.select().from(heartbeatRuns).where(and(eq(heartbeatRuns.id, envelope.runId), eq(heartbeatRuns.companyId, envelope.companyId))).for("update"); + if (!run) throw notFound("Accounting run not found"); + const [existing] = await tx.select().from(runUsageReceipts).where(and(eq(runUsageReceipts.companyId, envelope.companyId), eq(runUsageReceipts.runId, envelope.runId), eq(runUsageReceipts.sourceId, envelope.sourceId), eq(runUsageReceipts.sequence, envelope.sequence))); + if (existing && existing.receiptHash !== hash) throw conflict("Conflicting usage checkpoint replay"); + if (!existing) await tx.insert(runUsageReceipts).values({ + id: envelope.id, companyId: envelope.companyId, runId: envelope.runId, sourceId: envelope.sourceId, + sequence: envelope.sequence, receiptHash: hash, receiptJson: envelope, complete: envelope.receipt.complete, receivedAt: new Date(envelope.receivedAt), + }); + const previous = run.usageJson ?? {}; + // A restarted/replaced controller owns a fresh source. Old spool files + // remain auditable but cannot overwrite its snapshots or acknowledged work. + if (run.costAccountedAt || previous.accountingReceiptSourceId !== envelope.sourceId || Number(previous.accountingReceiptSequence ?? 0) >= envelope.sequence) return; + const receipt = envelope.receipt; + const billingType = receipt.billingType === "api" ? "metered_api" : receipt.billingType === "subscription" ? "subscription_included" : receipt.billingType ?? "unknown"; + await tx.update(heartbeatRuns).set({ + costAccountingPending: true, + accountingLastError: null, + usageJson: { ...previous, ...receipt.usage, + provider: receipt.provider ?? "unknown", biller: receipt.biller ?? receipt.provider ?? "unknown", model: receipt.model ?? "unknown", billingType, + costUsd: receipt.costUsd ?? null, costUsdExact: receipt.costUsdExact ?? null, cacheAdjustedCostUsd: receipt.cacheAdjustedCostUsd ?? null, + usageByModel: receipt.usageByModel ?? null, usageSource: receipt.usageBasis ?? "per_run", providerRequestId: receipt.providerRequestId ?? null, + accountingReceiptReady: receipt.complete && receipt.usageBasis !== "session_cumulative", accountingReceiptId: envelope.id, + accountingReceiptSequence: envelope.sequence, accountingReceiptReceivedAt: envelope.receivedAt, + costStatus: receipt.costStatus ?? null, + pricingProvenance: receipt.pricingProvenance ?? { source: receipt.costUsd != null || receipt.costUsdExact != null ? "provider_reported" : "unknown", version: "accounting-receipt/v1" }, + }, + }).where(and(eq(heartbeatRuns.id, run.id), isNull(heartbeatRuns.costAccountedAt))); + }); +} + +export async function replayUsageReceipts(db: Db, directory = usageReceiptSpoolPath(), limit = 100) { + let files: string[]; + try { files = (await fs.readdir(directory)).filter(name => name.endsWith(".json")).sort(); } + catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT") return { replayed: 0, failed: 0 }; throw error; } + let replayed = 0, failed = 0; + for (const name of files.slice(0, limit)) { + const file = path.join(directory, name); + try { + const info = await fs.lstat(file); + if (!info.isFile() || info.size > 1024 * 1024) throw new Error("Invalid accounting spool entry"); + await persistUsageReceipt(db, envelopeSchema.parse(JSON.parse(await fs.readFile(file, "utf8")))); + await fs.rm(file, { force: true }); replayed++; + } catch (error) { + failed++; + // Rotate failures behind fresh entries without discarding the evidence. + await fs.rename(file, path.join(directory, `retry-${Date.now()}-${randomUUID()}.json`)).catch(() => undefined); + logger.error({ err: error, receiptFile: name }, "Accounting spool receipt remains pending"); + } + } + return { replayed, failed }; +} + +export async function createRunUsageRecorder(db: Db, input: { companyId: string; runId: string; adapterType: string }, directory = usageReceiptSpoolPath()) { + const sourceId = randomUUID(); + // Verify durable storage before starting paid work, including a directory + // sync. The database binding fences checkpoints from replaced controllers. + await fs.mkdir(directory, { recursive: true, mode: 0o700 }); + const probe = await fs.open(path.join(directory, `${sourceId}.probe`), "wx", 0o600); + try { await probe.sync(); } finally { await probe.close(); await fs.rm(path.join(directory, `${sourceId}.probe`)); } + await syncDirectory(directory); + const bound = await db.update(heartbeatRuns).set({ usageJson: sql`coalesce(${heartbeatRuns.usageJson}, '{}'::jsonb) || ${JSON.stringify({ accountingReceiptSourceId: sourceId, accountingReceiptSequence: 0 })}::jsonb` }) + .where(and(eq(heartbeatRuns.id, input.runId), eq(heartbeatRuns.companyId, input.companyId), isNull(heartbeatRuns.costAccountedAt))).returning({ id: heartbeatRuns.id }); + if (bound.length !== 1) throw conflict("Run cannot accept a new usage recorder"); + let sequence = 0; + let currentAttempt = "default"; + const attempts = new Map(); + let chain = Promise.resolve(); + let captureFailed = false; + async function capture(raw: AdapterUsageCheckpoint) { + const parsed = checkpointSchema.parse(raw) as AdapterUsageCheckpoint; + const attempt = parsed.attemptId ?? currentAttempt; + if (!attempts.has(attempt)) currentAttempt = attempt; + const previous = attempts.get(attempt); + const priced = priceCodexReceipt({ ...parsed, usage: parsed.usage ?? previous?.usage, + complete: parsed.complete && !(previous?.usage && !parsed.usage) }); + priced.pricingProvenance ??= { + source: priced.costUsd != null || priced.costUsdExact != null || priced.cacheAdjustedCostUsd != null ? "provider_reported" : "unknown", + version: "accounting-receipt/v1", + }; + attempts.set(attempt, priced); + const parts = [...attempts.values()]; + const latest = attempts.get(currentAttempt)!; + const receipt: AdapterUsageCheckpoint = { ...latest, complete: latest.complete && !captureFailed, usage: { + inputTokens: parts.reduce((sum, item) => sum + (item.usage?.inputTokens ?? 0), 0), + cachedInputTokens: parts.reduce((sum, item) => sum + (item.usage?.cachedInputTokens ?? 0), 0), + ...(parts.some(item => item.usage?.cacheWriteTokens !== undefined) ? { cacheWriteTokens: parts.reduce((sum, item) => sum + (item.usage?.cacheWriteTokens ?? 0), 0) } : {}), + outputTokens: parts.reduce((sum, item) => sum + (item.usage?.outputTokens ?? 0), 0), + } }; + if (parts.length > 1) { + let total = 0n, priced = true; + for (const part of parts) { + const cost = part.cacheAdjustedCostUsd ?? part.costUsdExact ?? part.costUsd; + if (cost == null) priced = false; else total += usdToUnits(cost); + } + if (new Set(parts.map(part => part.billingType ?? "unknown")).size > 1) receipt.billingType = "unknown"; + const decimal = `${total / 1_000_000_000n}.${String(total % 1_000_000_000n).padStart(9, "0")}`; + receipt.costUsdExact = priced ? decimal : null; + receipt.costUsd = priced ? Number(decimal) : null; + receipt.cacheAdjustedCostUsd = null; + receipt.usageByModel = undefined; + receipt.costStatus = priced && parts.some(part => part.costStatus === "estimated") ? "estimated" : priced ? "reported" : "unpriced"; + receipt.pricingProvenance = { source: receipt.costStatus === "estimated" ? "rate_card" : "unknown", version: "per-attempt/v1", evidence: "Sum of attempted-run receipts; component prices are retained in the immutable receipt journal." }; + // Every attempted provider must supply a final receipt before the total + // can be considered complete, even if a later attempt succeeded. + receipt.complete = !captureFailed && parts.every(part => part.complete); + } + const envelope: UsageReceiptEnvelope = { schema: "paperclip/accounting-receipt/v1", id: randomUUID(), ...input, sourceId, sequence: ++sequence, receivedAt: new Date().toISOString(), receipt }; + const file = await spoolUsageReceipt(envelope, directory); + try { await persistUsageReceipt(db, envelope); await fs.rm(file, { force: true }); } + catch (error) { logger.warn({ err: error, runId: input.runId, receiptId: envelope.id }, "Usage receipt is durable on disk; database replay pending"); } + return receipt; + } + return { + capture: (receipt: AdapterUsageCheckpoint) => { + const result = chain.then(() => capture(receipt)); chain = result.then(() => undefined, () => { captureFailed = true; }); return result; + }, + complete: async (result: AdapterExecutionResult) => { + await chain; + return capture({ ...result, complete: result.usageComplete !== false }); + }, + }; +} diff --git a/server/tsconfig.accounting-tests.json b/server/tsconfig.accounting-tests.json new file mode 100644 index 00000000000..02b4f94e444 --- /dev/null +++ b/server/tsconfig.accounting-tests.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": "..", + "noEmit": true + }, + "include": [ + "src/__tests__/cost-accounting-*.test.ts", + "src/__tests__/codex-pricing.test.ts", + "src/__tests__/provider-billing-import.test.ts", + "src/__tests__/company-quota-windows.test.ts", + "src/__tests__/fixtures/accounting-writer.ts", + "src/**/*.d.ts", + "vitest.accounting-mutations.config.ts", + "vitest.accounting.config.ts", + "../tests/e2e/cost-accounting.spec.ts" + ], + "exclude": [] +} diff --git a/server/vitest.accounting-mutations.config.ts b/server/vitest.accounting-mutations.config.ts new file mode 100644 index 00000000000..1af27dce3e1 --- /dev/null +++ b/server/vitest.accounting-mutations.config.ts @@ -0,0 +1,27 @@ +import { fileURLToPath } from "node:url"; +import { defineConfig } from "vitest/config"; +import base from "./vitest.config.js"; +export const mutations = { + deduplication: { file: "/services/costs.ts", from: "if (existing) {", to: "if (false && existing) {", test: "deduplicates a retried receipt" }, + company: { file: "/services/costs.ts", from: "const conditions: ReturnType[] = [eq(costEvents.companyId, companyId)];", to: "const conditions: ReturnType[] = [sql`true`];", test: "isolates company reporting" }, + projection: { file: "/services/costs.ts", from: "+ ${delta}::numeric", to: "+ 0::numeric", test: "conserves agent projections" }, + threshold: { file: "/services/budgets.ts", from: "compareCents(observed.totalExact, policy.amount) >= 0", to: "compareCents(observed.totalExact, policy.amount) > 0", test: "stops at the exact budget boundary" }, +}; +const name = process.env.PAPERCLIP_ACCOUNTING_MUTATION; +if (name && !(name in mutations)) throw new Error(`Unknown accounting mutation: ${name}`); +const mutation = name ? mutations[name as keyof typeof mutations] : null; +export default defineConfig({ + ...base, root: fileURLToPath(new URL(".", import.meta.url)), + plugins: [...(base.plugins ?? []), { + name: "accounting-mutation-test-only", enforce: "pre", + transform(code, id) { + if (!mutation || !id.split("?")[0].endsWith(mutation.file)) return; + if (!code.includes(mutation.from)) throw new Error(`Mutation anchor missing: ${name}`); + // Replace precisely one site in Vite's in-memory module. Workspace source + // stays untouched, including while another test process is running. + console.error(`ACCOUNTING_MUTATION_APPLIED ${name}`); + return code.replace(mutation.from, mutation.to); + }, + }], + test: { ...base.test, include: ["src/__tests__/cost-accounting-mutation-targets.test.ts"], coverage: { enabled: false } }, +}); diff --git a/server/vitest.accounting.config.ts b/server/vitest.accounting.config.ts new file mode 100644 index 00000000000..36ade4c483d --- /dev/null +++ b/server/vitest.accounting.config.ts @@ -0,0 +1,43 @@ +import { fileURLToPath } from "node:url"; +import { defineConfig } from "vitest/config"; +import base from "./vitest.config.js"; + +export default defineConfig({ + ...base, + root: fileURLToPath(new URL(".", import.meta.url)), + test: { + ...base.test, + include: [ + "src/__tests__/cost-accounting-*.test.ts", + "src/__tests__/budgets-service.test.ts", + "src/__tests__/costs-service.test.ts", + "src/__tests__/codex-pricing.test.ts", + "src/__tests__/provider-billing-import.test.ts", + ], + coverage: { + enabled: true, + provider: "v8", + reportOnFailure: true, + reportsDirectory: "../coverage/accounting", + reporter: ["text", "json", "json-summary", "html"], + // Every module must retain its own floor; a well-covered helper cannot + // hide a regression in ingestion, recovery, finance, or enforcement. + thresholds: { perFile: true, lines: 98, branches: 90, functions: 96, statements: 96 }, + include: [ + "src/services/accounting-transaction.ts", + "src/services/accounting-integrity.ts", + "src/services/billing-reconciliation.ts", + "src/services/budget-reservations.ts", + "src/services/usage-receipts.ts", + "src/services/receipt-fingerprint.ts", + "src/services/cost-date-range.ts", + "src/services/run-cost-accounting.ts", + "src/services/costs.ts", + "src/services/finance.ts", + "src/services/budgets.ts", + "src/services/codex-pricing.ts", + "src/services/provider-billing-import.ts", + ], + }, + }, +}); diff --git a/server/vitest.config.ts b/server/vitest.config.ts index 239bce1bc3b..ad4721b6503 100644 --- a/server/vitest.config.ts +++ b/server/vitest.config.ts @@ -37,7 +37,6 @@ export default defineConfig({ isolate: true, maxConcurrency: 1, maxWorkers: 1, - minWorkers: 1, pool: "forks", sequence: { concurrent: false, diff --git a/ui/src/pages/AgentDetail.production.tsx b/ui/src/pages/AgentDetail.production.tsx index 91efc76433d..cfcde59e12f 100644 --- a/ui/src/pages/AgentDetail.production.tsx +++ b/ui/src/pages/AgentDetail.production.tsx @@ -380,7 +380,7 @@ function runMetrics(run: HeartbeatRun) { output, cached, cost, - totalTokens: input + output, + totalTokens: input + cached + output, provider, model, }; @@ -935,6 +935,8 @@ export function AgentDetail() { metric: "billed_cents", windowKind: "calendar_month_utc", amount: budgetMonthlyCents, + unpricedEventCount: 0, pendingRunCount: 0, + unpricedUsagePolicy: "block", observedAmount: spentMonthlyCents, remainingAmount: Math.max(0, budgetMonthlyCents - spentMonthlyCents), utilizationPercent: diff --git a/ui/src/pages/ProjectDetail.tsx b/ui/src/pages/ProjectDetail.tsx index 8588bdec6fe..8c8943411d3 100644 --- a/ui/src/pages/ProjectDetail.tsx +++ b/ui/src/pages/ProjectDetail.tsx @@ -605,6 +605,8 @@ export function ProjectDetail() { metric: "billed_cents", windowKind: "lifetime", amount: 0, + unpricedEventCount: 0, pendingRunCount: 0, + unpricedUsagePolicy: "block", observedAmount: 0, remainingAmount: 0, utilizationPercent: 0, diff --git a/ui/storybook/fixtures/paperclipData.ts b/ui/storybook/fixtures/paperclipData.ts index ea3b9054f53..23a2d8db9ca 100644 --- a/ui/storybook/fixtures/paperclipData.ts +++ b/ui/storybook/fixtures/paperclipData.ts @@ -1118,6 +1118,8 @@ export const storybookApprovals: Approval[] = [ windowKind: "calendar_month_utc", metric: "billed_cents", budgetAmount: 120_000, + unpricedEventCount: 0, pendingRunCount: 0, + unpricedUsagePolicy: "block", observedAmount: 131_400, guidance: "Raise the project budget only after current release smoke checks are green.", }, @@ -1162,6 +1164,8 @@ export const storybookBudgetSummaries: BudgetPolicySummary[] = [ metric: "billed_cents", windowKind: "calendar_month_utc", amount: 250_000, + unpricedEventCount: 0, pendingRunCount: 0, + unpricedUsagePolicy: "block", observedAmount: 67_500, remainingAmount: 182_500, utilizationPercent: 27, @@ -1184,6 +1188,8 @@ export const storybookBudgetSummaries: BudgetPolicySummary[] = [ metric: "billed_cents", windowKind: "calendar_month_utc", amount: 120_000, + unpricedEventCount: 0, pendingRunCount: 0, + unpricedUsagePolicy: "block", observedAmount: 103_100, remainingAmount: 16_900, utilizationPercent: 86, @@ -1206,6 +1212,8 @@ export const storybookBudgetSummaries: BudgetPolicySummary[] = [ metric: "billed_cents", windowKind: "calendar_month_utc", amount: 40_000, + unpricedEventCount: 0, pendingRunCount: 0, + unpricedUsagePolicy: "block", observedAmount: 43_200, remainingAmount: 0, utilizationPercent: 108, diff --git a/ui/storybook/stories/budget-finance.stories.tsx b/ui/storybook/stories/budget-finance.stories.tsx index f47ff68aa14..21cbe1df0c2 100644 --- a/ui/storybook/stories/budget-finance.stories.tsx +++ b/ui/storybook/stories/budget-finance.stories.tsx @@ -346,6 +346,7 @@ const billerSpendRows: Array<{ const financeBillerRows: FinanceByBiller[] = [ { biller: "openai", + currency: "USD", debitCents: 74_200, creditCents: 12_000, netCents: 62_200, @@ -355,6 +356,7 @@ const financeBillerRows: FinanceByBiller[] = [ }, { biller: "aws_bedrock", + currency: "USD", debitCents: 45_880, creditCents: 0, netCents: 45_880, @@ -367,6 +369,7 @@ const financeBillerRows: FinanceByBiller[] = [ const financeKindRows: FinanceByKind[] = [ { eventKind: "inference_charge", + currency: "USD", debitCents: 49_820, creditCents: 0, netCents: 49_820, @@ -376,6 +379,7 @@ const financeKindRows: FinanceByKind[] = [ }, { eventKind: "log_storage_charge", + currency: "USD", debitCents: 8_760, creditCents: 0, netCents: 8_760, @@ -385,6 +389,7 @@ const financeKindRows: FinanceByKind[] = [ }, { eventKind: "provisioned_capacity_charge", + currency: "USD", debitCents: 42_900, creditCents: 0, netCents: 42_900, @@ -394,6 +399,7 @@ const financeKindRows: FinanceByKind[] = [ }, { eventKind: "credit_refund", + currency: "USD", debitCents: 0, creditCents: 12_000, netCents: -12_000, @@ -428,6 +434,7 @@ const financeTimelineRows: FinanceEvent[] = [ amountCents: 40_000, currency: "USD", estimated: false, + idempotencyKey: null, externalInvoiceId: "INV-2026-04-OPENAI-1184", metadataJson: { paymentMethod: "corporate-card" }, occurredAt: at(1_260), @@ -457,6 +464,7 @@ const financeTimelineRows: FinanceEvent[] = [ amountCents: 42_900, currency: "USD", estimated: true, + idempotencyKey: null, externalInvoiceId: "AWS-EST-7713", metadataJson: { purchaseOrder: "PO-STORYBOOK-APR" }, occurredAt: at(420), @@ -486,6 +494,7 @@ const financeTimelineRows: FinanceEvent[] = [ amountCents: 8_760, currency: "USD", estimated: true, + idempotencyKey: null, externalInvoiceId: "CF-APR-2026-0091", metadataJson: null, occurredAt: at(210), @@ -515,6 +524,7 @@ const financeTimelineRows: FinanceEvent[] = [ amountCents: 12_000, currency: "USD", estimated: false, + idempotencyKey: null, externalInvoiceId: "CR-2026-04-OPENAI-041", metadataJson: null, occurredAt: at(64),