diff --git a/.claude/launch.json b/.claude/launch.json index 219f5be924d..416679e1caa 100644 --- a/.claude/launch.json +++ b/.claude/launch.json @@ -23,6 +23,24 @@ "cd /Users/tonio/Projects/paperclip-cloud/.worktrees/deploy-stack-master/web && npx vite --port 4173 --strictPort" ], "port": 4173 + }, + { + "name": "artifact-directions-dev", + "runtimeExecutable": "/bin/sh", + "runtimeArgs": [ + "-c", + "cd ui && npx vite --config vite.artifact-directions.config.mjs --port 5180 --strictPort" + ], + "port": 5180 + }, + { + "name": "artifact-directions", + "runtimeExecutable": "/bin/sh", + "runtimeArgs": [ + "-c", + "cd ui && export ARTIFACT_DIRECTIONS_OUT_DIR=node_modules/.artifact-directions-dist && npx vite build --config vite.artifact-directions.config.mjs && npx vite preview --config vite.artifact-directions.config.mjs --port 5190 --strictPort" + ], + "port": 5190 } ] } diff --git a/doc/DEVELOPING.md b/doc/DEVELOPING.md index bb2533042b0..53504cd79e8 100644 --- a/doc/DEVELOPING.md +++ b/doc/DEVELOPING.md @@ -2583,6 +2583,10 @@ Environment overrides: negative, empty, or non-numeric value falls back to the default. `PAPERCLIP_NATIVE_HARNESS_BACKUP_EXPIRATION_ENABLED=false` turns expiry off, and `PAPERCLIP_NATIVE_HARNESS_BACKUP_EXPIRATION_DRY_RUN=true` only reports candidates. +- `PAPERCLIP_GIT_RESTORE_POINT_RETENTION_DAYS=` sets how long a git workspace + restore point is kept after its issue tree closes (see + `doc/recoverable-disk-cleanup.md`). The default is `90` and the minimum is `1`. A + negative, empty, or non-numeric value falls back to the default. Without `PAPERCLIP_DB_BACKUP_ALERT_FILE`, health checks look for `db-backup-to-s3.failure` in the backup directory, beside the backup directory, diff --git a/doc/connections/CONNECTOR-PERMISSION-AUDIT.md b/doc/connections/CONNECTOR-PERMISSION-AUDIT.md index e5ccec2a711..9c70b98e7ca 100644 --- a/doc/connections/CONNECTOR-PERMISSION-AUDIT.md +++ b/doc/connections/CONNECTOR-PERMISSION-AUDIT.md @@ -11,7 +11,8 @@ ledger; the counts above are not restated. Later additions: [Neon](./NEON.md) (`mcp-oauth`, `mcp-api-key`; 2026-10-02), [Superagent](./SUPERAGENT.md) (`mcp-api-key`; 2026-10-06), [Gauge](./GAUGE.md) (`mcp-oauth`, `mcp-api-key`; 2026-10-08), [Ditto](./DITTO.md) -(`mcp-oauth`, `mcp-api-key`; 2026-10-10). +(`mcp-oauth`, `mcp-api-key`; 2026-10-10), [Unikraft](./UNIKRAFT.md) +(`api-token`; 2026-10-15). ## Shared credential failure diff --git a/doc/connections/README.md b/doc/connections/README.md index 5a06e5a4856..bb9022ce2e5 100644 --- a/doc/connections/README.md +++ b/doc/connections/README.md @@ -14,7 +14,7 @@ Long-term memory: [Experimental memory connectors](./MEMORY.md). Provider notes: [Google Workspace](./GOOGLE-WORKSPACE.md), [Gmail](./GMAIL.md), [Asana](./ASANA.md), [Sentry](./SENTRY.md), [PostHog](./POSTHOG.md), [Braintrust](./BRAINTRUST.md), [Neon](./NEON.md), [Superagent](./SUPERAGENT.md), [Gauge](./GAUGE.md), [Ditto](./DITTO.md), -[AgentMail](./AGENTMAIL.md), [Telem.AI](./TELEM.md), [iMessage Photon](./IMESSAGE-PHOTON.md), and +[Unikraft](./UNIKRAFT.md), [AgentMail](./AGENTMAIL.md), [Telem.AI](./TELEM.md), [iMessage Photon](./IMESSAGE-PHOTON.md), and [Enterpret](./ENTERPRET.md). Optional credential custody: [Vercel Connect](./VERCEL-CONNECT.md). diff --git a/doc/connections/UNIKRAFT.md b/doc/connections/UNIKRAFT.md new file mode 100644 index 00000000000..a8706e2e06a --- /dev/null +++ b/doc/connections/UNIKRAFT.md @@ -0,0 +1,186 @@ +# Unikraft connection + +Unikraft Cloud runs unikernel micro-VMs ("instances") across regional metros. +Its connection uses the normal Apps connection, selected personal/company +credential, agent installation, action policy, gateway, revocation and audit +paths. The API token stays in the instance vault. The connector does not borrow +`unikraft login` state, `UKC_TOKEN` environment variables, or another +connection's credential. + +Sign-in tokens are never reused as resource tokens; id.paperclip.ing never +stores resource tokens; no connections hub on the ID service. + +## Vendor + +| Item | Value | +| --- | --- | +| Product | Unikraft Cloud (formerly KraftCloud) | +| API reference | https://unikraft.com/docs/api/platform/v1 | +| Specification | https://github.com/unikraft-cloud/openapi (`platform.json`, `plugins/sandbox.json`) | +| Metros | https://unikraft.com/docs/platform/metros | +| Tokens | https://unikraft.com/docs/sdks/overview#authentication | +| Console | https://console.unikraft.com | +| Hosted MCP server | None. Unikraft's MCP guides describe hosting your own MCP server on Unikraft. | + +## Transport and auth + +| Axis | Value | +| --- | --- | +| Transport | `rest_api`, executed by Paperclip's native provider bridge | +| Authentication | `api_key`, sent as `Authorization: Bearer ` | +| OAuth client ownership | Not applicable; Unikraft offers no third-party OAuth | +| Credential source | Paperclip vault | +| Grant identity | `organization` or `user` | + +Unikraft Cloud has no global platform endpoint. Each metro serves its own API at +`https://api..unikraft.cloud/v1/...`. The reviewed specification +publishes five metros: `dal`, `fra`, `sfo`, `sin` and `was`. Every action accepts +an optional `metro` argument limited to that list; actions that omit it use the +connection's default metro (Frankfurt unless changed under **Advanced**). The +token is never sent to any other host. Legacy `*.kraft.cloud` hosts, which +Unikraft documents as nearing end of life, are not reachable through this +connection. + +The saved connection URL, `https://controlplane.unikraft.cloud/v1`, identifies +the connection only. Actions use the metro hosts above. + +## Connection flow (mandatory) + +```mermaid +sequenceDiagram + actor Operator + participant UI as Paperclip UI + participant Server as Paperclip server + participant Vault as Instance vault + participant Metro as Unikraft metro API + actor Agent + Operator->>UI: Connectors, Unikraft, Add key + UI->>Server: POST /api/companies/{companyId}/tools/apps/connect (galleryKey unikraft, token, optional default metro) + Server->>Vault: Store the token as a company or user secret + Server->>Metro: GET /v1/users/quotas with the stored token + Metro-->>Server: status success + Server-->>UI: Generated catalog of 105 actions + Operator->>UI: Choose agents and action permissions + Agent->>Server: Gateway tool call, for example unikraft_get_instances + Server->>Server: Check grant, revocation, policy and saved contract + Server->>Metro: Request to api.{metro}.unikraft.cloud with the vaulted token + Metro-->>Server: JSON result + Server-->>Agent: Result with the token redacted +``` + +Paperclip endpoints used: `GET /api/companies/{companyId}/tools/apps/unikraft/preflight`, +`POST /api/companies/{companyId}/tools/apps/connect`, +`POST /api/companies/{companyId}/tools/apps/{connectionId}/finish`, +`GET /api/tool-connections/{connectionId}/catalog`, and the run-bound tool gateway. +There is no OAuth handoff, so `GET /api/tools/oauth/callback` is not used. + +## Administrator setup (mandatory) + +1. Sign in to the [Unikraft Cloud console](https://console.unikraft.com) and + create an API token for the organization agents should manage. +2. In Paperclip choose **Connectors → Unikraft → Add key**, choose who can use + the credential, and paste the token. +3. Optional: under **Advanced**, change the default metro. +4. On the saved connection's **Permissions** screen, choose agents and set + actions to Allowed, Ask first, or Off. + +A token acts for its whole organization in every metro. Unikraft does not offer +narrower token permissions, so agent assignment and action permissions enforce +the capability boundary. Revoke a token in the Unikraft console, then remove or +reconnect the Paperclip connection. + +## Resource filters + +There are no provider-enforced resource filters. `requiredResourceFilters` +(`organization`, `metro`, `instance`) is reviewed metadata. The bridge enforces +only that requests go to a published metro and that path arguments cannot +traverse to another route. + +## Actions + +The catalog contains **105 actions** generated from Unikraft's published +specifications at commit `5dd107ea1cd8716b83cb86a83ed2a6ee46f99b5b`: +89 platform API operations and 16 sandbox plugin operations. + +| Group | Examples | Classification | +| --- | --- | --- | +| Reads, logs, metrics, history and waits | `unikraft_get_instances`, `unikraft_get_instance_logs_by_uuid`, `unikraft_wait_instances`, `unikraft_sandbox_read_file` | Read | +| Creation, updates, start, attach, clone and pinning | `unikraft_create_instance`, `unikraft_update_service_group_by_uuid`, `unikraft_start_instances`, `unikraft_sandbox_write_file` | Write | +| Deletion, stop, suspend, detach, unpin, and sandbox command execution, signals and stdin | `unikraft_delete_volume_by_uuid`, `unikraft_stop_instances`, `unikraft_sandbox_run_command` | Destructive | + +Platform actions keep Unikraft's operation names (`unikraft_`). +Sandbox plugin actions are `unikraft_sandbox_` and take an +`instance` (name or UUID); they call +`https://api..unikraft.cloud/v1/instances//plugins/sandbox`. +Many Unikraft operations select batch targets with a JSON body, including on +GET and DELETE; the bridge sends those bodies as published. +`unikraft_sandbox_wait_for_command_with_timeout` returns +`{ "status": "running", "timedOut": true }` when Unikraft reports that the +command is still running at the timeout (HTTP 408), so an agent can wait again. +The bridge lives in `server/src/services/connectors/unikraft/`. + +The audit event stream (`GET /v1/audit`) is server-sent events, not a +request/response action. It is recorded in the generated inventory and not +advertised as a tool. + +The specification sources and SHA-256 hashes are recorded in +`server/src/services/connectors/unikraft/operations.generated.json`. Regenerate with: + +```sh +node scripts/generate-unikraft-tools.mjs +node --test scripts/generate-unikraft-tools.test.mjs +``` + +The generator pins a reviewed commit because the specification repository has +no tags or releases. Updating it requires explicit regeneration and catalog +review: saved connections quarantine new and changed actions until reviewed. + +## Wizard path + +The ordinary key wizard: **Access → Connect**. Setup asks only for the token. +The default metro is an optional select under **Advanced**. Setup completes when +`GET /v1/users/quotas` on the default metro confirms the token. + +## Governance defaults + +- Risk tier S4: the normal catalog includes deletion and arbitrary command + execution inside sandboxes. +- Every discovered action is enabled when setup finishes, under the shared + connection access rules; operators restrict actions on Permissions. +- New and changed actions after a catalog update are quarantined until reviewed. +- Agents cannot export the token; generic credential export rejects Unikraft. +- Responses and request bodies are bounded at 8 MiB. Each action has one caller + deadline, capped at 30 seconds. Mutations are never retried automatically; a + timed-out write can still complete at the provider. + +## Manifest + +Defined in `scripts/app-definition-overrides/unikraft.json` and generated into +`packages/shared/src/app-definitions/unikraft.json`: slug `unikraft`, category +`developer`, one method `api-token` (`rest_api`, `api_key`, `customer`), and a +`metro` select tenant field. + +## Validation hook + +Database-backed tests exercise company and personal vault ownership, real +run-scoped gateway reads, writes, deletions and sandbox commands, default and +per-call metros, rejection of unpublished metros and path traversal, Off +policies, revocation, stale-contract blocking, credential export denial, all +generated argument schemas, and bounded/sanitized errors. HTTP boundary tests +send GET and DELETE JSON bodies through the real pinned transport to a local +server. Generator tests cover streaming endpoints, shared parameter references, +unsigned integer formats and risk classification. + +These fixtures do not qualify a real Unikraft account. Live provider proof is +pending an operator connecting a token and trying an allowed read and write +(`liveProof: not-run`). + +## Brand + +`ui/public/brands/apps/unikraft.svg` is Unikraft's gradient mark from the +official documentation site, copied from +`https://raw.githubusercontent.com/unikraft-cloud/docs/d8acfcefe8186badac84d5308d641a1c1b5b239e/public/unikraft.svg` +with editor metadata removed. One asset serves both themes. Unikraft publishes +no brand kit. + +Provider research verified 2026-10-15. diff --git a/doc/connections/tool-method-permission-reviews.json b/doc/connections/tool-method-permission-reviews.json index f5a516146b9..3d83685bbb9 100644 --- a/doc/connections/tool-method-permission-reviews.json +++ b/doc/connections/tool-method-permission-reviews.json @@ -2879,6 +2879,24 @@ "liveProof": "not-run", "evidenceLimit": "Public provider documentation and OAuth metadata only. Account-bound connect, discovery, read/write execution, run-scoped gateway, refresh/reconnect, removal, and secret handling remain to be qualified for this method.", "keyPermissions": "Create a Braintrust API key in Settings → API keys for the account and organization whose data agents should access. The key must authorize the reads and writes you intend to allow; Paperclip cannot increase or narrow its provider permissions. Evaluations and Topics processing can incur costs. Dataset edits can permanently delete rows; automation activation can enable retention or delivery." + }, + { + "app": "unikraft", + "method": "api-token", + "auth": "api_key", + "policy": "provider-key", + "requestedScopes": [], + "capability": "write", + "supportedActions": "All published Unikraft Cloud platform API operations (instances, checkpoints, templates, service groups, autoscale, volumes, images, certificates, and quotas) and sandbox plugin operations (commands and files) in every published metro. The audit event stream is not exposed.", + "evidence": [ + "https://unikraft.com/docs/sdks/overview#authentication", + "https://unikraft.com/docs/platform/metros", + "https://raw.githubusercontent.com/unikraft-cloud/openapi/5dd107ea1cd8716b83cb86a83ed2a6ee46f99b5b/platform.json", + "https://raw.githubusercontent.com/unikraft-cloud/openapi/5dd107ea1cd8716b83cb86a83ed2a6ee46f99b5b/plugins/sandbox.json" + ], + "reviewedAt": "2026-10-15", + "liveProof": "not-run", + "keyPermissions": "Create an API token in the Unikraft Cloud console. A token acts for its whole organization in every metro, including running sandbox commands and deleting resources. Unikraft does not offer narrower token permissions, so choose trusted agents and use action permissions to limit what they can do." } ] } diff --git a/doc/plans/2026-10-22-artifacts-mvp-v1.md b/doc/plans/2026-10-22-artifacts-mvp-v1.md new file mode 100644 index 00000000000..fcae7a0e209 --- /dev/null +++ b/doc/plans/2026-10-22-artifacts-mvp-v1.md @@ -0,0 +1,141 @@ +# Artifacts MVP V1 — shipping the lab's direction into the product + +Date: 2026-10-22. Status: all four slices on `claude/artifacts-mvp-v1` (draft PR #17228); a browser pass on live data is the remaining check before the PR leaves draft. + +## Role of this document + +The artifact directions lab (`ui/src/artifact-directions`, merged in #17213) settled a +presentation for artifacts on three surfaces: the Artifacts page, the right panel's +Artifacts tab and the task thread. The lab draws it from fixtures on static mocks of the +chrome. This plan turns that reference into product code, one surface at a time, behind an +experimental setting, without changing the artifact data model where the lab's numbers do +not need it. + +The lab stays the visual reference: every measurement below is the one the lab verified +against the PCLP-Core mocks (node 2129:133457, 2096:87691, 2096:86523, 2205:138266). + +## Locked decisions + +1. **Gated by `/instance/settings > Experimental > Artifacts V1`** (`enableArtifactsV1`, + default off), the same plumbing as `enableCombinedInboxTasks`: shared type, validator, + feature catalog, server defaults, managed-key list, UI hook, settings toggle. The old + presentation stays the fallback until the setting is removed in a later cleanup. +2. **Token-only.** Every colour, radius, shadow, size and font value the lab kept in its + scoped `tokens.css` becomes an `--artifact-*` token in `ui/src/index.css`, with the light + theme's values as overrides, so the token gates pass and the theme editor can reach them. + Artwork geometry (sheet 180×216, glyph at (58, 58), bars on a 26px pitch, stack centres) + lives in one constants module, not in class names. +3. **Reuse the lab's code, not its types.** The primitives move to + `ui/src/components/artifacts/v1/` and take `CompanyArtifact`, `IssueWorkProduct`, + issue documents and attachments through small adapters, the way the lab's Today kit + adapted the other direction. The lab keeps its own copies; it is a reference, not a + dependency of product code. +4. **No new API fields for the first three slices.** The Artifacts page tiles show title, + identifier, count and "Updated …"; the panel and thread rows get sizes from the issue + work-product and attachment payloads they already have. A picture's aspect ratio comes + from the loaded image (`naturalWidth / naturalHeight`), 4:3 until it loads. +5. **Reduced motion is respected**: the hover movement (page turn, 6px rise) applies only + under `prefers-reduced-motion: no-preference`; colour fades stay. + +## Slices + +### Slice 1 — foundation (done) + +- Experimental setting `enableArtifactsV1` end to end, with `useArtifactsV1Enabled()`. +- `--artifact-*` tokens in `index.css` (paper, ink, faint ink, sheet and print radii, sheet + and print shadows with light overrides, grid column minimum, frame and tile heights, tile + hover fill and radius, row fill, hover, radius and gap, play scrim, hover easing). +- Primitives under `ui/src/components/artifacts/v1/`: kind helpers (`treatmentOf`, + `labelOf`, `glyphOf`, `mediaKindOf`), `ArtifactSheet` (preview and icon faces), + `ArtifactPrint` (bordered picture, fitted to a box, play disc for video), + `ArtifactStack` (the two backings), `ArtifactFront`, `ArtifactTile` (stack and single + tiles with the caption and the Paper Mono pill), `ArtifactRow` (the 40px-icon row), + date helpers, and the hover rules. Unit tests ported from the lab. +- Nothing mounted yet. Storybook stories under `documents-artifacts/` show the primitives on + fixtures so reviewers can see them without the flag. + +### Slice 2 — Artifacts page (done) + +- With the flag on, `Artifacts.tsx` renders the V1 tiles: stacks when grouped by task + (the default), single tiles otherwise, 28px gaps, 370px cells, hover fill with the + 12px radius. +- Filters become Tasks (default; grouped by task) · All files · Images · Videos · + Documents · Text · Files. The group-by menu and search stay. +- Opened stack: the `← All stacks / ACM-3 Title` row above the single tiles. +- Page tests updated for both flag states. + +### Slice 3 — right panel Artifacts tab (done) + +What the shipped tab does that the V1 rows must keep (read 2026-10-22, +`IssuePropertiesArtifactsTab.tsx`): + +- Three sources merged: work products (`useIssueWorkProducts`), task documents + (`useIssueDocuments`, minus `artifact-review-*` proxies) and attachments not + already promoted to work products (`selectArtifactAttachments`). +- Rows are grouped by run with an agent-name header and a time; V1 flattens to + one list, newest first (the lab's decision), and drops the headers. +- A Markdown work product expands in place into the document review surface + (`MarkdownWorkProductRow`, backed by the `artifact-review-` document) with + raw open and download as secondary actions; a task document expands in place + (`DocumentRow`) or opens in its own pane tab when `onOpenDocument` is given; + a text attachment opens in a text tab (`openTextAttachment`), other files in a + new tab; `documentDeepLink` expands or opens the requested document; the + upload area (`ArtifactUploadContent`) wraps the list. +- Work products carry a status badge (`workProductStatusBadge`: in progress, + for review, done, changes requested, failed). + +Approach: the V1 row is the *face* of each item. Give `DocumentRow` and +`MarkdownWorkProductRow` a `face` render prop so, with the flag on, their +header is the V1 row (name over size · date, status badge and the open / +download controls in the row's trailing slot) while their expansion, deep-link +and review behaviour stay untouched; attachments and other work products render +the V1 row directly with the same destinations the shipped cards use. + +- `IssuePropertiesArtifactsTab` renders the V1 row list when the flag is on: newest first, + 12px apart, 40px document icon or thumbnail with a play disc, name truncating before its + extension over "size · date" with the panel's date form (Today / Yesterday / 2D ago / + July 23). Existing actions (open, download, review) move into the row's menu. + +### Slice 4 — task thread (done) + +- Work products and documents between turns render as one full-width row 12px under the + "Worked" row (`TaskChatProtocolCard.tsx`, `ResourceCard`): a registered file is named after + the file with the deliverable's state beside it; a document after its key; the destination + is the card's. A message's files render as the same rows inline under the footer, 12px + below it, 8px apart at equal widths, wrapping past three (280px minimum), in place of the + media grid and file chips (`TaskChatBubble.tsx`): pictures and video open the task's + gallery, text opens a text tab when the panel offers one, anything else a new tab. +- Not carried over: the "Images · N / Files · N" labels and the four-up image grid; the + rows name every file instead. + +### Later + +- Remove the flag and the old cards once the three surfaces have been in use. +- The lab's parked surfaces (media preview overlay, panel closeup) follow the same path if + adopted. + +## Verification per slice + +Done (slices 1–4): `cd ui && pnpm typecheck` (catalog check and `tsc -b`, clean apart from +the five pre-existing `PluginOrganizationSwitcher` errors), `pnpm check:token-gates`, +`node scripts/check-maps.mjs`, and the Vitest suites for `components/artifacts/v1`, the +Artifacts page, the panel's Artifacts tab, the task-chat bubble and protocol card (each in +both flag states), the hook, the experimental settings page, the shared feature catalog and +the server instance settings. Not yet done: a browser pass with the flag on against a dev +instance with real artifacts on all three surfaces; the artwork was measured in the lab and +the wiring is covered by tests. + +- `cd ui && pnpm typecheck` (catalog check and `tsc -b`), `pnpm check:token-gates`, + `npx vitest run src/components/artifacts/v1` plus the touched pages' tests. +- A Playwright pass on the dev instance with the flag on, measuring what the lab measured: + 58px rows, 8px/12px gaps, 370px cells, 5° turn and 6px rise, Paper Mono pills. +- Storybook stories for the primitives, catalogued under `documents-artifacts`. + +## Risks + +- The Artifacts page tests and the task-chat tests assert the current markup; the flag keeps + the default path unchanged, and the V1 path gets its own tests. +- Pictures without known dimensions render at 4:3 until they load; the fitted print then + resizes once. The stack frame's height is fixed, so nothing below it moves. +- Token count grows by about thirty `--artifact-*` entries; they are namespaced and + documented in the token block. diff --git a/doc/recoverable-disk-cleanup.md b/doc/recoverable-disk-cleanup.md index e6e450bd37d..915c404e83c 100644 --- a/doc/recoverable-disk-cleanup.md +++ b/doc/recoverable-disk-cleanup.md @@ -92,3 +92,42 @@ The rest of the session directory, including its control-plane state, is kept. Each batch that finds candidates logs them with candidate and reclaimed allocated bytes, cleared task sessions, and skip reasons. + +## Git workspace restore points + +Restore points are encrypted saves of the parts of a git worktree that its remote +cannot reproduce: unpushed commits and uncommitted files. They let a closed issue's +worktree be removed and restored later. The table `execution_workspace_restore_points` +tracks each one with its storage key, hash, size and status +(`pending`, `active`, `expiring`, `superseded`, `delete_requested`, `deleted`). The objects live in +the instance's configured file storage (`local_disk` or `s3`). + +An active restore point's `delete_after` is either its expiry time or empty while its +source issue tree is open. A database trigger on `issues` keeps this current: when an +issue's status or parent changes, or an issue is deleted, it marks every active restore +point whose source issue is that issue, its old or new parent, or any of their ancestors +as due now, in the same transaction. A retention worker on the shared delivery-work +scheduler runs at startup, when issue changes settle while restore points exist, and at +real deadlines: the earliest expiry, the end of an abandoned capture's grace period, or +a failed deletion's retry. With nothing due, it schedules nothing, and open trees cause +no recurring checks. At review it checks the source issue tree: + +- Tree still open: clear `delete_after` and wait for the next issue change. +- Tree `done` or `cancelled`: expire `PAPERCLIP_GIT_RESTORE_POINT_RETENTION_DAYS` + (default `90`, minimum `1`) after the tree's latest close, immediately if that time + has passed. Expiry first moves the row to `expiring`, runs the expiry hook, and only + then requests deletion. An `expiring` row's object is never deleted; after a restart + or a hook failure the hook is retried, after 15 minutes when it failed. +- Source issue gone: delete. + +Deleting an issue or a company marks its restore points `delete_requested` in the same +transaction and notifies the worker, so their objects are removed promptly. A +`pending` restore point older than one hour was abandoned mid-capture and is deleted. +`superseded` and `delete_requested` restore points have their stored object removed, +then become `deleted` once the storage provider confirms the object is gone. A +storage failure is recorded in `last_error` and retried after 15 minutes. Work is +processed in batches of 50; a full batch continues immediately. + +Rows have no foreign keys, so they outlive a deleted workspace, issue or company long +enough to remove the stored object. Each run that changes anything logs its counts. + diff --git a/maps/agent-access-map/app-permissions.md b/maps/agent-access-map/app-permissions.md index d3cacb64ece..7183dcb59bf 100644 --- a/maps/agent-access-map/app-permissions.md +++ b/maps/agent-access-map/app-permissions.md @@ -131,6 +131,12 @@ and stale same-name contracts before refresh; new/changed tools stay quarantined exercise this boundary through real run-bound gateway sessions with fixture provider responses. They do not qualify a live Daytona account or agent model. +Unikraft uses the same native-catalog contract: stale same-name saved entries are +rejected before refresh and new or changed actions stay quarantined. Its +[database-backed fixtures](../../server/src/__tests__/unikraft-connection.test.ts) +cover this through real run-bound gateway sessions with fixture provider +responses, not a live Unikraft account. + ## `test-action` Capability ID: `app-permissions/test-action`. diff --git a/maps/agent-access-map/connection-setup.md b/maps/agent-access-map/connection-setup.md index c800966e8f8..36b34ed7d92 100644 --- a/maps/agent-access-map/connection-setup.md +++ b/maps/agent-access-map/connection-setup.md @@ -137,6 +137,13 @@ gateway; they cannot export the key. [Database-backed fixtures](../../server/src cover selected vault ownership and gateway reads/writes with provider responses replaced by fixtures. Real provider authentication remains unqualified. +Unikraft native setup uses an organization API token in the selected personal or +company vault grant. It does not borrow `unikraft login` state or `UKC_TOKEN`. +Agents use assigned provider tools through the run-bound gateway, limited to the +published metro hosts, and cannot export the token. [Database-backed fixtures](../../server/src/__tests__/unikraft-connection.test.ts) +cover vault ownership and gateway reads, writes and sandbox commands with +fixture provider responses. Real provider authentication remains unqualified. + ## `handoff` Capability ID: `connection-setup/handoff`. diff --git a/maps/feature-map/app-permissions.md b/maps/feature-map/app-permissions.md index 025b6660d8e..02705f8c62c 100644 --- a/maps/feature-map/app-permissions.md +++ b/maps/feature-map/app-permissions.md @@ -85,6 +85,12 @@ remain in place. [Daytona regressions](../../server/src/__tests__/daytona-connec cover shared/personal catalog upgrades, failed refresh, permission preservation, run-bound file transfers, and revocation with fixture provider responses. +Unikraft actions follow the same review rules: deletion, stop/suspend/detach and +sandbox command execution are destructive, and catalog updates quarantine new or +changed actions. [Unikraft regressions](../../server/src/__tests__/unikraft-connection.test.ts) +cover classification, Off policies, revocation and stale contracts with fixture +provider responses. + ## Gotchas - Connected does not imply an agent has access or an action is allowed. diff --git a/maps/feature-map/connection-setup.md b/maps/feature-map/connection-setup.md index 679a090e769..0485d87af1a 100644 --- a/maps/feature-map/connection-setup.md +++ b/maps/feature-map/connection-setup.md @@ -137,6 +137,13 @@ account-bound proof. Live token expiry/refresh, remote OAuth revocation, and native CLI automatic capability delivery remain unqualified; public metadata and fixtures do not establish those results. +Unikraft follows the same key/access wizard. Setup asks only for the API token; +the default metro is an optional select under Advanced, and each action can name +another published metro. [Unikraft browser setup](../../tests/e2e/unikraft-connector.spec.ts) +checks the catalog, password field, access controls and the advanced metro. It +does not submit a token or qualify a real provider account. See +[Unikraft](../../doc/connections/UNIKRAFT.md) for token authority and metros. + ### `mcp-link` Automated: AppsConnect's generic MCP cases cover probe-driven auth, private or diff --git a/maps/feature-map/runs-adapters.md b/maps/feature-map/runs-adapters.md index 1767da493b0..ef9b83201dc 100644 --- a/maps/feature-map/runs-adapters.md +++ b/maps/feature-map/runs-adapters.md @@ -28,6 +28,9 @@ Open an agent’s model/adapter configuration or the instance adapter settings p ### `run-history` Open an agent run at `/agents/:agentId/runs/:runId` from the agent or task activity. +The agent header has one Run now action. Administrators can enable +Capture raw provider traces under the agent configuration’s Debugging section +for future runs. The header shows Raw tracing on while that setting is enabled. For latency diagnosis with an operator OTLP endpoint, search the hashed run ID without restricting the root span name. Compare workspace preparation with its diff --git a/packages/db/src/migrations/0372_mature_the_initiative.sql b/packages/db/src/migrations/0372_mature_the_initiative.sql new file mode 100644 index 00000000000..2add5f7a544 --- /dev/null +++ b/packages/db/src/migrations/0372_mature_the_initiative.sql @@ -0,0 +1,59 @@ +CREATE TABLE "execution_workspace_restore_points" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "company_id" uuid NOT NULL, + "execution_workspace_id" uuid NOT NULL, + "source_issue_id" uuid, + "generation" integer NOT NULL, + "object_key" text NOT NULL, + "sha256" text NOT NULL, + "byte_size" bigint NOT NULL, + "status" text DEFAULT 'pending' NOT NULL, + "activated_at" timestamp with time zone, + "tree_closed_at" timestamp with time zone, + "delete_after" timestamp with time zone, + "review_generation" integer DEFAULT 0 NOT NULL, + "deleted_at" timestamp with time zone, + "last_error" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX "execution_workspace_restore_points_object_key_uniq" ON "execution_workspace_restore_points" USING btree ("object_key");--> statement-breakpoint +CREATE UNIQUE INDEX "execution_workspace_restore_points_live_generation_uniq" ON "execution_workspace_restore_points" USING btree ("execution_workspace_id","generation") WHERE "execution_workspace_restore_points"."status" <> 'deleted';--> statement-breakpoint +CREATE INDEX "execution_workspace_restore_points_status_delete_after_idx" ON "execution_workspace_restore_points" USING btree ("status","delete_after");--> statement-breakpoint +CREATE INDEX "execution_workspace_restore_points_company_idx" ON "execution_workspace_restore_points" USING btree ("company_id");--> statement-breakpoint +CREATE INDEX "execution_workspace_restore_points_source_issue_idx" ON "execution_workspace_restore_points" USING btree ("company_id","source_issue_id");--> statement-breakpoint +CREATE INDEX "execution_workspace_restore_points_active_source_idx" ON "execution_workspace_restore_points" USING btree ("source_issue_id") WHERE "execution_workspace_restore_points"."status" = 'active';--> statement-breakpoint +CREATE OR REPLACE FUNCTION paperclip_restore_point_tree_changed() RETURNS trigger +LANGUAGE plpgsql SET search_path = public, pg_temp AS $$ +DECLARE starts uuid[]; +BEGIN + IF TG_OP = 'DELETE' THEN + starts := ARRAY[OLD.parent_id]; + ELSE + starts := ARRAY[NEW.id, OLD.parent_id, NEW.parent_id]; + END IF; + UPDATE execution_workspace_restore_points + SET delete_after = CASE WHEN delete_after IS NULL OR delete_after > now() THEN now() ELSE delete_after END, + review_generation = review_generation + 1, + updated_at = now() + WHERE status = 'active' + AND source_issue_id IN ( + WITH RECURSIVE ancestors(id, parent_id, depth) AS ( + SELECT i.id, i.parent_id, 0 FROM issues i WHERE i.id = ANY(starts) + UNION + SELECT parent.id, parent.parent_id, ancestors.depth + 1 FROM issues parent + JOIN ancestors ON parent.id = ancestors.parent_id WHERE ancestors.depth < 64 + ) + SELECT id FROM ancestors + ); + RETURN NULL; +END; +$$; +--> statement-breakpoint +CREATE TRIGGER paperclip_restore_point_tree_update AFTER UPDATE OF status, parent_id ON issues +FOR EACH ROW WHEN (OLD.status IS DISTINCT FROM NEW.status OR OLD.parent_id IS DISTINCT FROM NEW.parent_id) +EXECUTE FUNCTION paperclip_restore_point_tree_changed(); +--> statement-breakpoint +CREATE TRIGGER paperclip_restore_point_tree_delete AFTER DELETE ON issues +FOR EACH ROW EXECUTE FUNCTION paperclip_restore_point_tree_changed(); diff --git a/packages/db/src/migrations/0372_mighty_swordsman.sql b/packages/db/src/migrations/0373_wandering_mongu.sql similarity index 86% rename from packages/db/src/migrations/0372_mighty_swordsman.sql rename to packages/db/src/migrations/0373_wandering_mongu.sql index ec340b1e71b..f159a7df65c 100644 --- a/packages/db/src/migrations/0372_mighty_swordsman.sql +++ b/packages/db/src/migrations/0373_wandering_mongu.sql @@ -1,2 +1,2 @@ ALTER TABLE "issue_thread_interactions" ADD COLUMN IF NOT EXISTS "dismissed_at" timestamp with time zone;--> statement-breakpoint -ALTER TABLE "issue_thread_interactions" ADD COLUMN IF NOT EXISTS "dismissed_by_user_id" text; \ No newline at end of file +ALTER TABLE "issue_thread_interactions" ADD COLUMN IF NOT EXISTS "dismissed_by_user_id" text; diff --git a/packages/db/src/migrations/meta/0372_snapshot.json b/packages/db/src/migrations/meta/0372_snapshot.json index 3525c1f850f..e1aee913afc 100644 --- a/packages/db/src/migrations/meta/0372_snapshot.json +++ b/packages/db/src/migrations/meta/0372_snapshot.json @@ -1,5 +1,5 @@ { - "id": "5bc34ca2-f5d8-4952-a6c0-4afb96e70961", + "id": "9c1b4cac-52f1-4c6e-89b3-c9a7816016c7", "prevId": "7caf727e-0913-40b3-b01c-842e9447666f", "version": "7", "dialect": "postgresql", @@ -28473,6 +28473,237 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.execution_workspace_restore_points": { + "name": "execution_workspace_restore_points", + "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 + }, + "source_issue_id": { + "name": "source_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "generation": { + "name": "generation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "object_key": { + "name": "object_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "byte_size": { + "name": "byte_size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "activated_at": { + "name": "activated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "tree_closed_at": { + "name": "tree_closed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "delete_after": { + "name": "delete_after", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "review_generation": { + "name": "review_generation", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "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": { + "execution_workspace_restore_points_object_key_uniq": { + "name": "execution_workspace_restore_points_object_key_uniq", + "columns": [ + { + "expression": "object_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_workspace_restore_points_live_generation_uniq": { + "name": "execution_workspace_restore_points_live_generation_uniq", + "columns": [ + { + "expression": "execution_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "generation", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"execution_workspace_restore_points\".\"status\" <> 'deleted'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_workspace_restore_points_status_delete_after_idx": { + "name": "execution_workspace_restore_points_status_delete_after_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "delete_after", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_workspace_restore_points_company_idx": { + "name": "execution_workspace_restore_points_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_workspace_restore_points_source_issue_idx": { + "name": "execution_workspace_restore_points_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_workspace_restore_points_active_source_idx": { + "name": "execution_workspace_restore_points_active_source_idx", + "columns": [ + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"execution_workspace_restore_points\".\"status\" = 'active'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.execution_workspace_runtime_leases": { "name": "execution_workspace_runtime_leases", "schema": "", @@ -38627,18 +38858,6 @@ "primaryKey": false, "notNull": false }, - "dismissed_at": { - "name": "dismissed_at", - "type": "timestamp with time zone", - "primaryKey": false, - "notNull": false - }, - "dismissed_by_user_id": { - "name": "dismissed_by_user_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "created_at": { "name": "created_at", "type": "timestamp with time zone", diff --git a/packages/db/src/migrations/meta/0368_snapshot.json b/packages/db/src/migrations/meta/0373_snapshot.json similarity index 92% rename from packages/db/src/migrations/meta/0368_snapshot.json rename to packages/db/src/migrations/meta/0373_snapshot.json index a5ece8bb77e..3019f5fde87 100644 --- a/packages/db/src/migrations/meta/0368_snapshot.json +++ b/packages/db/src/migrations/meta/0373_snapshot.json @@ -1,6 +1,6 @@ { - "id": "a9c12cbc-ed4b-4e5d-9d33-36f9a7fb746c", - "prevId": "c0ed7fbb-f880-4c25-887c-913e46ec0edc", + "id": "406521f8-5352-415b-805d-786854381e03", + "prevId": "9c1b4cac-52f1-4c6e-89b3-c9a7816016c7", "version": "7", "dialect": "postgresql", "tables": { @@ -64,37 +64,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -200,9 +200,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "billing_invoice_lines_company_idx": { "name": "billing_invoice_lines_company_idx", @@ -221,50 +221,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "billing_invoices", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "cost_events", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -361,24 +361,24 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -491,9 +491,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "budget_reservations_prompt_injection_idx": { "name": "budget_reservations_prompt_injection_idx", @@ -512,9 +512,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "budget_reservations_decision_idx": { "name": "budget_reservations_decision_idx", @@ -533,9 +533,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "budget_reservations_run_idx": { "name": "budget_reservations_run_idx", @@ -554,9 +554,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "budget_reservations_active_idx": { "name": "budget_reservations_active_idx", @@ -581,51 +581,51 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"budget_reservations\".\"state\" = 'held'", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -758,9 +758,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cost_adjustments_event_idx": { "name": "cost_adjustments_event_idx", @@ -785,9 +785,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cost_adjustments_invoice_line_idx": { "name": "cost_adjustments_invoice_line_idx", @@ -806,50 +806,50 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "cost_events", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "billing_invoice_lines", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -961,24 +961,24 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -1088,9 +1088,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "run_usage_receipts_run_latest_idx": { "name": "run_usage_receipts_run_latest_idx", @@ -1115,37 +1115,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -1257,9 +1257,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "activity_log_company_agent_created_idx": { "name": "activity_log_company_agent_created_idx", @@ -1284,9 +1284,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "activity_log_company_responsible_user_created_idx": { "name": "activity_log_company_responsible_user_created_idx", @@ -1311,9 +1311,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "activity_log_run_id_idx": { "name": "activity_log_run_id_idx", @@ -1326,9 +1326,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "activity_log_entity_type_id_idx": { "name": "activity_log_entity_type_id_idx", @@ -1347,50 +1347,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -1546,9 +1546,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "adapter_auth_sessions_company_owner_adapter_active_uq": { "name": "adapter_auth_sessions_company_owner_adapter_active_uq", @@ -1573,10 +1573,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"adapter_auth_sessions\".\"status\" IN ('starting', 'waiting_for_user', 'promoting', 'awaiting_code', 'submitting')", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "adapter_auth_sessions_public_session_id_uq": { "name": "adapter_auth_sessions_public_session_id_uq", @@ -1589,9 +1589,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "adapter_auth_sessions_environment_idx": { "name": "adapter_auth_sessions_environment_idx", @@ -1604,9 +1604,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "adapter_auth_sessions_expires_idx": { "name": "adapter_auth_sessions_expires_idx", @@ -1619,9 +1619,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "adapter_auth_sessions_provider_lease_idx": { "name": "adapter_auth_sessions_provider_lease_idx", @@ -1634,37 +1634,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "environments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -1752,9 +1752,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "agent_api_keys_company_agent_idx": { "name": "agent_api_keys_company_agent_idx", @@ -1773,37 +1773,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -1903,9 +1903,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "agent_commentary_company_kind_created_idx": { "name": "agent_commentary_company_kind_created_idx", @@ -1930,63 +1930,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "agent_commentary_company_id_companies_id_fk": { "name": "agent_commentary_company_id_companies_id_fk", "tableFrom": "agent_commentary", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "agent_commentary_agent_id_agents_id_fk": { "name": "agent_commentary_agent_id_agents_id_fk", "tableFrom": "agent_commentary", + "tableTo": "agents", "columnsFrom": [ "agent_id" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "agent_commentary_run_id_heartbeat_runs_id_fk": { "name": "agent_commentary_run_id_heartbeat_runs_id_fk", "tableFrom": "agent_commentary", + "tableTo": "heartbeat_runs", "columnsFrom": [ "run_id" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "agent_commentary_issue_id_issues_id_fk": { "name": "agent_commentary_issue_id_issues_id_fk", "tableFrom": "agent_commentary", + "tableTo": "issues", "columnsFrom": [ "issue_id" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -2099,9 +2099,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "agent_config_revisions_agent_created_idx": { "name": "agent_config_revisions_agent_created_idx", @@ -2120,50 +2120,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -2225,17 +2225,17 @@ "agent_identity_keys_owner_fk": { "name": "agent_identity_keys_owner_fk", "tableFrom": "agent_identity_keys", + "tableTo": "agents", "columnsFrom": [ "company_id", "agent_id" ], - "tableTo": "agents", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -2290,33 +2290,33 @@ "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" ], - "tableTo": "agent_instruction_revisions", "columnsTo": [ "company_id", "agent_id", "entry_file", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -2462,39 +2462,39 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "agents", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -2654,9 +2654,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "agent_instruction_copies_agent_idx": { "name": "agent_instruction_copies_agent_idx", @@ -2681,39 +2681,39 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -2797,9 +2797,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "agent_memberships_company_user_starred_idx": { "name": "agent_memberships_company_user_starred_idx", @@ -2824,9 +2824,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "agent_memberships_agent_idx": { "name": "agent_memberships_agent_idx", @@ -2839,9 +2839,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "agent_memberships_company_user_agent_uq": { "name": "agent_memberships_company_user_agent_uq", @@ -2866,37 +2866,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -3019,9 +3019,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "agent_runtime_state_company_updated_idx": { "name": "agent_runtime_state_company_updated_idx", @@ -3040,37 +3040,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -3184,9 +3184,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "agent_session_goal_actions_company_status_created_idx": { "name": "agent_session_goal_actions_company_status_created_idx", @@ -3211,9 +3211,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "agent_session_goal_actions_session_created_idx": { "name": "agent_session_goal_actions_session_created_idx", @@ -3232,37 +3232,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agent_task_sessions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -3424,9 +3424,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "agent_task_sessions_company_agent_updated_idx": { "name": "agent_task_sessions_company_agent_updated_idx", @@ -3451,9 +3451,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "agent_task_sessions_company_task_updated_idx": { "name": "agent_task_sessions_company_task_updated_idx", @@ -3478,50 +3478,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -3656,6 +3656,28 @@ } }, "indexes": { + "agent_wakeup_requests_task_retry_idempotency_uq": { + "name": "agent_wakeup_requests_task_retry_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 'task-retry:%'", + "concurrently": false, + "method": "btree", + "with": {} + }, "agent_wakeup_requests_company_agent_status_idx": { "name": "agent_wakeup_requests_company_agent_status_idx", "columns": [ @@ -3679,9 +3701,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "agent_wakeup_requests_company_requested_idx": { "name": "agent_wakeup_requests_company_requested_idx", @@ -3700,9 +3722,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "agent_wakeup_requests_agent_requested_idx": { "name": "agent_wakeup_requests_agent_requested_idx", @@ -3721,9 +3743,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "agent_wakeup_requests_review_path_recovery_idempotency_uq": { "name": "agent_wakeup_requests_review_path_recovery_idempotency_uq", @@ -3742,10 +3764,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"agent_wakeup_requests\".\"idempotency_key\" LIKE 'issue_review_path_lost:%' AND \"agent_wakeup_requests\".\"status\" <> 'skipped'", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "agent_wakeup_requests_disposition_repair_idempotency_uq": { "name": "agent_wakeup_requests_disposition_repair_idempotency_uq", @@ -3764,10 +3786,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"agent_wakeup_requests\".\"idempotency_key\" LIKE 'issue_disposition_repair:%' AND \"agent_wakeup_requests\".\"status\" <> 'skipped'", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "agent_wakeup_requests_question_response_delivery_idempotency_uq": { "name": "agent_wakeup_requests_question_response_delivery_idempotency_uq", @@ -3786,10 +3808,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "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 + "concurrently": false, + "method": "btree", + "with": {} }, "agent_wakeup_requests_question_response_history_idx": { "name": "agent_wakeup_requests_question_response_history_idx", @@ -3808,10 +3830,10 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"agent_wakeup_requests\".\"idempotency_key\" LIKE 'question-response:%'", - "concurrently": true + "concurrently": true, + "method": "btree", + "with": {} }, "agent_wakeup_requests_connection_intent_delivery_idempotency_uq": { "name": "agent_wakeup_requests_connection_intent_delivery_idempotency_uq", @@ -3830,10 +3852,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"agent_wakeup_requests\".\"idempotency_key\" LIKE 'connection-intent:%' AND \"agent_wakeup_requests\".\"status\" NOT IN ('skipped', 'failed', 'cancelled')", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "agent_wakeup_requests_tool_action_delivery_uq": { "name": "agent_wakeup_requests_tool_action_delivery_uq", @@ -3852,10 +3874,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"agent_wakeup_requests\".\"idempotency_key\" LIKE 'tool-action-response:%' AND \"agent_wakeup_requests\".\"status\" NOT IN ('skipped', 'failed', 'cancelled')", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "agent_wakeup_requests_chat_completion_uq": { "name": "agent_wakeup_requests_chat_completion_uq", @@ -3874,10 +3896,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"agent_wakeup_requests\".\"idempotency_key\" LIKE 'chat-completion:%'", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "agent_wakeup_requests_company_payload_issue_idx": { "name": "agent_wakeup_requests_company_payload_issue_idx", @@ -3890,43 +3912,43 @@ }, { "expression": "(\"payload\" ->> 'issueId')", - "isExpression": true, "asc": true, + "isExpression": true, "nulls": "last" } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -4170,9 +4192,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "agents_company_status_idx": { "name": "agents_company_status_idx", @@ -4191,9 +4213,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "agents_company_reports_to_idx": { "name": "agents_company_reports_to_idx", @@ -4212,9 +4234,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "agents_company_default_environment_idx": { "name": "agents_company_default_environment_idx", @@ -4233,61 +4255,61 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "agents_company_id_companies_id_fk": { "name": "agents_company_id_companies_id_fk", "tableFrom": "agents", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "environments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, "uniqueConstraints": { "agents_company_id_uq": { "name": "agents_company_id_uq", + "nullsNotDistinct": false, "columns": [ "company_id", "id" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -4373,39 +4395,39 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "connection_grants", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -4478,54 +4500,54 @@ "ai_connection_pools_id_tool_connections_id_fk": { "name": "ai_connection_pools_id_tool_connections_id_fk", "tableFrom": "ai_connection_pools", + "tableTo": "tool_connections", "columnsFrom": [ "id" ], - "tableTo": "tool_connections", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "ai_connection_pools_company_id_companies_id_fk": { "name": "ai_connection_pools_company_id_companies_id_fk", "tableFrom": "ai_connection_pools", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "ai_connection_pools_company_connection_fk": { "name": "ai_connection_pools_company_connection_fk", "tableFrom": "ai_connection_pools", + "tableTo": "tool_connections", "columnsFrom": [ "company_id", "id" ], - "tableTo": "tool_connections", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, "uniqueConstraints": { "ai_connection_pools_company_id_uq": { "name": "ai_connection_pools_company_id_uq", + "nullsNotDistinct": false, "columns": [ "company_id", "id" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -4567,43 +4589,43 @@ "ai_connection_router_cursors_pool_id_ai_connection_pools_id_fk": { "name": "ai_connection_router_cursors_pool_id_ai_connection_pools_id_fk", "tableFrom": "ai_connection_router_cursors", + "tableTo": "ai_connection_pools", "columnsFrom": [ "pool_id" ], - "tableTo": "ai_connection_pools", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "ai_connection_router_cursors_company_id_companies_id_fk": { "name": "ai_connection_router_cursors_company_id_companies_id_fk", "tableFrom": "ai_connection_router_cursors", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "ai_connection_router_cursors_company_pool_fk": { "name": "ai_connection_router_cursors_company_pool_fk", "tableFrom": "ai_connection_router_cursors", + "tableTo": "ai_connection_pools", "columnsFrom": [ "company_id", "pool_id" ], - "tableTo": "ai_connection_pools", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -4697,80 +4719,80 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "ai_connection_task_pins_company_id_companies_id_fk": { "name": "ai_connection_task_pins_company_id_companies_id_fk", "tableFrom": "ai_connection_task_pins", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "ai_connection_task_pins_pool_id_ai_connection_pools_id_fk": { "name": "ai_connection_task_pins_pool_id_ai_connection_pools_id_fk", "tableFrom": "ai_connection_task_pins", + "tableTo": "ai_connection_pools", "columnsFrom": [ "pool_id" ], - "tableTo": "ai_connection_pools", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "ai_connection_task_pins_agent_id_agents_id_fk": { "name": "ai_connection_task_pins_agent_id_agents_id_fk", "tableFrom": "ai_connection_task_pins", + "tableTo": "agents", "columnsFrom": [ "agent_id" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "ai_connection_task_pins_company_pool_fk": { "name": "ai_connection_task_pins_company_pool_fk", "tableFrom": "ai_connection_task_pins", + "tableTo": "ai_connection_pools", "columnsFrom": [ "company_id", "pool_id" ], - "tableTo": "ai_connection_pools", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "ai_connection_task_pins_company_agent_fk": { "name": "ai_connection_task_pins_company_agent_fk", "tableFrom": "ai_connection_task_pins", + "tableTo": "agents", "columnsFrom": [ "company_id", "agent_id" ], - "tableTo": "agents", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -4846,39 +4868,39 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "connection_grants", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -4952,9 +4974,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "ai_subscription_connections_credential_idx": { "name": "ai_subscription_connections_credential_idx", @@ -4973,9 +4995,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "ai_subscription_connections_account_idx": { "name": "ai_subscription_connections_account_idx", @@ -4994,63 +5016,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "ai_subscription_connections_company_id_companies_id_fk": { "name": "ai_subscription_connections_company_id_companies_id_fk", "tableFrom": "ai_subscription_connections", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "ai_subscription_connections_connection_id_tool_connections_id_fk": { "name": "ai_subscription_connections_connection_id_tool_connections_id_fk", "tableFrom": "ai_subscription_connections", + "tableTo": "tool_connections", "columnsFrom": [ "connection_id" ], - "tableTo": "tool_connections", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "ai_subscription_connections_grant_id_connection_grants_id_fk": { "name": "ai_subscription_connections_grant_id_connection_grants_id_fk", "tableFrom": "ai_subscription_connections", + "tableTo": "connection_grants", "columnsFrom": [ "grant_id" ], - "tableTo": "connection_grants", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "ai_subscription_connections_subscription_id_ai_subscriptions_id_fk": { "name": "ai_subscription_connections_subscription_id_ai_subscriptions_id_fk", "tableFrom": "ai_subscription_connections", + "tableTo": "ai_subscriptions", "columnsFrom": [ "subscription_id" ], - "tableTo": "ai_subscriptions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -5159,9 +5181,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "ai_subscription_prices_company_idx": { "name": "ai_subscription_prices_company_idx", @@ -5174,37 +5196,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "ai_subscription_prices_company_id_companies_id_fk": { "name": "ai_subscription_prices_company_id_companies_id_fk", "tableFrom": "ai_subscription_prices", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "ai_subscription_prices_subscription_id_ai_subscriptions_id_fk": { "name": "ai_subscription_prices_subscription_id_ai_subscriptions_id_fk", "tableFrom": "ai_subscription_prices", + "tableTo": "ai_subscriptions", "columnsFrom": [ "subscription_id" ], - "tableTo": "ai_subscriptions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -5343,9 +5365,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "ai_subscriptions_company_idx": { "name": "ai_subscriptions_company_idx", @@ -5358,37 +5380,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "ai_subscriptions_company_id_companies_id_fk": { "name": "ai_subscriptions_company_id_companies_id_fk", "tableFrom": "ai_subscriptions", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "ai_subscriptions_merged_into_id_ai_subscriptions_id_fk": { "name": "ai_subscriptions_merged_into_id_ai_subscriptions_id_fk", "tableFrom": "ai_subscriptions", + "tableTo": "ai_subscriptions", "columnsFrom": [ "merged_into_id" ], - "tableTo": "ai_subscriptions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -5529,9 +5551,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "approval_comments_approval_idx": { "name": "approval_comments_approval_idx", @@ -5544,9 +5566,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "approval_comments_approval_created_idx": { "name": "approval_comments_approval_created_idx", @@ -5565,50 +5587,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "approvals", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -5722,37 +5744,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "approvals_company_id_companies_id_fk": { "name": "approvals_company_id_companies_id_fk", "tableFrom": "approvals", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -5859,9 +5881,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "assets_company_provider_idx": { "name": "assets_company_provider_idx", @@ -5880,9 +5902,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "assets_company_object_key_uq": { "name": "assets_company_object_key_uq", @@ -5901,37 +5923,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "assets_company_id_companies_id_fk": { "name": "assets_company_id_companies_id_fk", "tableFrom": "assets", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -6047,24 +6069,24 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "account_user_id_user_id_fk": { "name": "account_user_id_user_id_fk", "tableFrom": "account", + "tableTo": "user", "columnsFrom": [ "user_id" ], - "tableTo": "user", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -6131,15 +6153,15 @@ "session_user_id_user_id_fk": { "name": "session_user_id_user_id_fk", "tableFrom": "session", + "tableTo": "user", "columnsFrom": [ "user_id" ], - "tableTo": "user", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -6320,9 +6342,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "board_api_keys_user_idx": { "name": "board_api_keys_user_idx", @@ -6335,24 +6357,24 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "user", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -6423,9 +6445,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "browser_use_browsers_session_idx": { "name": "browser_use_browsers_session_idx", @@ -6438,37 +6460,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "browser_use_sessions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -6590,9 +6612,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "browser_use_runs_provider_uq": { "name": "browser_use_runs_provider_uq", @@ -6605,9 +6627,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "browser_use_runs_session_idx": { "name": "browser_use_runs_session_idx", @@ -6620,50 +6642,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "browser_use_sessions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -6796,9 +6818,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "browser_use_sessions_provider_uq": { "name": "browser_use_sessions_provider_uq", @@ -6811,76 +6833,76 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "tool_connections", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "connection_grants", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -6924,28 +6946,28 @@ "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" ], - "tableTo": "connection_grants", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -7083,9 +7105,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "budget_incidents_company_scope_idx": { "name": "budget_incidents_company_scope_idx", @@ -7116,9 +7138,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "budget_incidents_policy_window_threshold_idx": { "name": "budget_incidents_policy_window_threshold_idx", @@ -7143,51 +7165,51 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"budget_incidents\".\"status\" = 'open'", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "budget_policies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "approvals", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -7358,9 +7380,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "budget_policies_company_window_idx": { "name": "budget_policies_company_window_idx", @@ -7385,9 +7407,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "budget_policies_company_scope_metric_unique_idx": { "name": "budget_policies_company_scope_metric_unique_idx", @@ -7424,24 +7446,24 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -7542,9 +7564,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "built_in_managed_resources_resource_idx": { "name": "built_in_managed_resources_resource_idx", @@ -7563,9 +7585,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "built_in_managed_resources_company_bundle_resource_uq": { "name": "built_in_managed_resources_company_bundle_resource_uq", @@ -7596,24 +7618,24 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -7689,9 +7711,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "case_access_sources_company_case_idx": { "name": "case_access_sources_company_case_idx", @@ -7710,37 +7732,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "case_access_sources_company_id_companies_id_fk": { "name": "case_access_sources_company_id_companies_id_fk", "tableFrom": "case_access_sources", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "case_access_sources_case_id_cases_id_fk": { "name": "case_access_sources_case_id_cases_id_fk", "tableFrom": "case_access_sources", + "tableTo": "cases", "columnsFrom": [ "case_id" ], - "tableTo": "cases", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -7816,9 +7838,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "case_attachments_asset_uq": { "name": "case_attachments_asset_uq", @@ -7831,50 +7853,50 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "cases", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "assets", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -7957,9 +7979,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "case_documents_document_uq": { "name": "case_documents_document_uq", @@ -7972,9 +7994,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "case_documents_company_case_updated_idx": { "name": "case_documents_company_case_updated_idx", @@ -7999,50 +8021,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "cases", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "documents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -8144,9 +8166,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "case_events_company_case_idx": { "name": "case_events_company_case_idx", @@ -8165,50 +8187,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "cases", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -8230,6 +8252,24 @@ "name": "case_handoffs", "schema": "", "columns": { + "workflow_transition": { + "name": "workflow_transition", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "workflow_prompt": { + "name": "workflow_prompt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "base_workflow_document_revision_id": { + "name": "base_workflow_document_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, "id": { "name": "id", "type": "uuid", @@ -8459,9 +8499,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "case_handoffs_actor_idempotency_uq": { "name": "case_handoffs_actor_idempotency_uq", @@ -8492,9 +8532,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "case_handoffs_pending_case_uq": { "name": "case_handoffs_pending_case_uq", @@ -8507,10 +8547,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"case_handoffs\".\"state\" in ('awaiting_release', 'pending_dispatch', 'awaiting_completion')", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "case_handoffs_pending_issue_uq": { "name": "case_handoffs_pending_issue_uq", @@ -8523,10 +8563,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"case_handoffs\".\"state\" in ('awaiting_release', 'pending_dispatch', 'awaiting_completion')", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "case_handoffs_pending_attempt_idx": { "name": "case_handoffs_pending_attempt_idx", @@ -8539,10 +8579,10 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"case_handoffs\".\"state\" in ('awaiting_release', 'pending_dispatch')", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "case_handoffs_case_created_idx": { "name": "case_handoffs_case_created_idx", @@ -8561,9 +8601,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "case_handoffs_source_run_idx": { "name": "case_handoffs_source_run_idx", @@ -8582,10 +8622,10 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"case_handoffs\".\"source_run_id\" is not null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "case_handoffs_wakeup_request_uq": { "name": "case_handoffs_wakeup_request_uq", @@ -8598,76 +8638,76 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "case_handoffs_company_id_companies_id_fk": { "name": "case_handoffs_company_id_companies_id_fk", "tableFrom": "case_handoffs", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "case_handoffs_case_id_cases_id_fk": { "name": "case_handoffs_case_id_cases_id_fk", "tableFrom": "case_handoffs", + "tableTo": "cases", "columnsFrom": [ "case_id" ], - "tableTo": "cases", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "case_handoffs_issue_id_issues_id_fk": { "name": "case_handoffs_issue_id_issues_id_fk", "tableFrom": "case_handoffs", + "tableTo": "issues", "columnsFrom": [ "issue_id" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "restrict" + "onDelete": "restrict", + "onUpdate": "no action" }, "case_handoffs_from_agent_id_agents_id_fk": { "name": "case_handoffs_from_agent_id_agents_id_fk", "tableFrom": "case_handoffs", + "tableTo": "agents", "columnsFrom": [ "from_agent_id" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "restrict" + "onDelete": "restrict", + "onUpdate": "no action" }, "case_handoffs_to_agent_id_agents_id_fk": { "name": "case_handoffs_to_agent_id_agents_id_fk", "tableFrom": "case_handoffs", + "tableTo": "agents", "columnsFrom": [ "to_agent_id" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "restrict" + "onDelete": "restrict", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -8767,9 +8807,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "case_issue_links_company_case_idx": { "name": "case_issue_links_company_case_idx", @@ -8788,9 +8828,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "case_issue_links_issue_idx": { "name": "case_issue_links_issue_idx", @@ -8803,50 +8843,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "cases", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -8922,9 +8962,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "case_labels_company_case_idx": { "name": "case_labels_company_case_idx", @@ -8943,9 +8983,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "case_labels_label_idx": { "name": "case_labels_label_idx", @@ -8958,50 +8998,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "cases", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "labels", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -9045,6 +9085,12 @@ "primaryKey": false, "notNull": false }, + "workflow_id": { + "name": "workflow_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, "handoff_version": { "name": "handoff_version", "type": "integer", @@ -9159,9 +9205,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cases_identifier_uq": { "name": "cases_identifier_uq", @@ -9174,9 +9220,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cases_work_issue_uq": { "name": "cases_work_issue_uq", @@ -9189,10 +9235,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"cases\".\"work_issue_id\" is not null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "cases_company_type_key_uq": { "name": "cases_company_type_key_uq", @@ -9217,9 +9263,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cases_company_status_idx": { "name": "cases_company_status_idx", @@ -9238,9 +9284,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cases_company_type_idx": { "name": "cases_company_type_idx", @@ -9259,9 +9305,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cases_company_project_idx": { "name": "cases_company_project_idx", @@ -9280,9 +9326,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cases_parent_idx": { "name": "cases_parent_idx", @@ -9295,9 +9341,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cases_title_search_idx": { "name": "cases_title_search_idx", @@ -9311,9 +9357,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "gin", - "concurrently": false + "with": {} }, "cases_identifier_search_idx": { "name": "cases_identifier_search_idx", @@ -9327,9 +9373,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "gin", - "concurrently": false + "with": {} }, "cases_summary_search_idx": { "name": "cases_summary_search_idx", @@ -9343,76 +9389,89 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "gin", - "concurrently": false + "with": {} } }, "foreignKeys": { "cases_company_id_companies_id_fk": { "name": "cases_company_id_companies_id_fk", "tableFrom": "cases", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "projects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" }, "cases_work_issue_id_issues_id_fk": { "name": "cases_work_issue_id_issues_id_fk", "tableFrom": "cases", + "tableTo": "issues", "columnsFrom": [ "work_issue_id" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "restrict" + "onDelete": "restrict", + "onUpdate": "no action" + }, + "cases_workflow_id_workflows_id_fk": { + "name": "cases_workflow_id_workflows_id_fk", + "tableFrom": "cases", + "tableTo": "workflows", + "columnsFrom": [ + "workflow_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "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" ], - "tableTo": "cases", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -9532,97 +9591,97 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_deliveries", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "chat_deliveries", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "chat_conversations", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "chat_external_principals", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -9720,54 +9779,54 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -9907,9 +9966,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_conversations_thread_uq": { "name": "chat_conversations_thread_uq", @@ -9940,106 +9999,106 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_endpoint_resources", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "restrict" + "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" ], - "tableTo": "issues", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_endpoint_resources", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -10182,9 +10241,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_deliveries_event_uq": { "name": "chat_deliveries_event_uq", @@ -10203,9 +10262,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_deliveries_dedupe_uq": { "name": "chat_deliveries_dedupe_uq", @@ -10224,106 +10283,106 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_conversations", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "chat_external_principals", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_conversations", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "chat_external_principals", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -10409,9 +10468,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_endpoint_leases_expiry_idx": { "name": "chat_endpoint_leases_expiry_idx", @@ -10424,39 +10483,39 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -10584,9 +10643,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_endpoint_resources_external_uq": { "name": "chat_endpoint_resources_external_uq", @@ -10611,50 +10670,50 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -10902,9 +10961,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_endpoints_agent_idx": { "name": "chat_endpoints_agent_idx", @@ -10923,9 +10982,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_endpoints_status_idx": { "name": "chat_endpoints_status_idx", @@ -10944,9 +11003,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_endpoints_public_id_uq": { "name": "chat_endpoints_public_id_uq", @@ -10959,9 +11018,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_endpoints_agentmail_inbox_uq": { "name": "chat_endpoints_agentmail_inbox_uq", @@ -10974,10 +11033,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"chat_endpoints\".\"provider\" = 'agentmail' and \"chat_endpoints\".\"status\" != 'archived' and \"chat_endpoints\".\"bot_external_id\" is not null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "chat_endpoints_connection_uq": { "name": "chat_endpoints_connection_uq", @@ -10990,9 +11049,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_endpoints_live_bot_external_uq": { "name": "chat_endpoints_live_bot_external_uq", @@ -11017,10 +11076,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "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 + "concurrently": false, + "method": "btree", + "with": {} }, "chat_endpoints_photon_number_uq": { "name": "chat_endpoints_photon_number_uq", @@ -11033,10 +11092,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"chat_endpoints\".\"provider\" = 'imessage-photon' and \"chat_endpoints\".\"status\" <> 'archived' and \"chat_endpoints\".\"bot_external_id\" is not null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "chat_endpoints_live_discord_bot_external_uq": { "name": "chat_endpoints_live_discord_bot_external_uq", @@ -11055,10 +11114,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "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 + "concurrently": false, + "method": "btree", + "with": {} }, "chat_endpoints_live_global_app_bot_external_uq": { "name": "chat_endpoints_live_global_app_bot_external_uq", @@ -11077,10 +11136,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "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 + "concurrently": false, + "method": "btree", + "with": {} }, "chat_endpoints_live_bot_username_uq": { "name": "chat_endpoints_live_bot_username_uq", @@ -11105,79 +11164,79 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "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 + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "restrict" + "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" ], - "tableTo": "agents", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "tool_connections", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, "uniqueConstraints": { "chat_endpoints_company_id_uq": { "name": "chat_endpoints_company_id_uq", + "nullsNotDistinct": false, "columns": [ "company_id", "id" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -11313,9 +11372,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_external_principals_external_uq": { "name": "chat_external_principals_external_uq", @@ -11346,35 +11405,35 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -11489,9 +11548,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_identity_links_endpoint_principal_uq": { "name": "chat_identity_links_endpoint_principal_uq", @@ -11510,54 +11569,54 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_external_principals", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -11662,138 +11721,138 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_deliveries", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "chat_publications", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issue_comments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_deliveries", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "chat_publications", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issue_comments", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "chat_conversations", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -11937,9 +11996,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_publications_work_idx": { "name": "chat_publications_work_idx", @@ -11958,9 +12017,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_publications_idempotency_uq": { "name": "chat_publications_idempotency_uq", @@ -11979,110 +12038,110 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "restrict" + "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" ], - "tableTo": "issue_comments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issue_comments", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_conversations", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -12177,9 +12236,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_sdk_state_expiry_idx": { "name": "chat_sdk_state_expiry_idx", @@ -12192,39 +12251,39 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -12327,9 +12386,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_completion_deliveries_pending_idx": { "name": "chat_completion_deliveries_pending_idx", @@ -12348,63 +12407,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_task_handoffs", "columnsTo": [ "task_id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issue_comments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -12473,63 +12532,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -12642,17 +12701,17 @@ "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" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -12771,9 +12830,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_github_registration_endpoint_idx": { "name": "chat_github_registration_endpoint_idx", @@ -12792,26 +12851,26 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -12991,9 +13050,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_github_reviews_pull_idx": { "name": "chat_github_reviews_pull_idx", @@ -13024,9 +13083,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_github_reviews_task_idx": { "name": "chat_github_reviews_task_idx", @@ -13051,65 +13110,65 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "restrict" + "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" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -13244,9 +13303,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_slack_manager_grants_owner_idx": { "name": "chat_slack_manager_grants_owner_idx", @@ -13277,24 +13336,24 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "chat_slack_manager_grants_company_id_companies_id_fk": { "name": "chat_slack_manager_grants_company_id_companies_id_fk", "tableFrom": "chat_slack_manager_grants", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -13423,32 +13482,32 @@ "chat_slack_registrations_company_id_manager_grant_id_chat_slack_manager_grants_company_id_id_fk": { "name": "chat_slack_registrations_company_id_manager_grant_id_chat_slack_manager_grants_company_id_id_fk", "tableFrom": "chat_slack_registrations", + "tableTo": "chat_slack_manager_grants", "columnsFrom": [ "company_id", "manager_grant_id" ], - "tableTo": "chat_slack_manager_grants", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" }, "chat_slack_registrations_company_id_endpoint_id_chat_endpoints_company_id_id_fk": { "name": "chat_slack_registrations_company_id_endpoint_id_chat_endpoints_company_id_id_fk", "tableFrom": "chat_slack_registrations", + "tableTo": "chat_endpoints", "columnsFrom": [ "company_id", "endpoint_id" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -13712,9 +13771,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_teams_file_transfers_token_uq": { "name": "chat_teams_file_transfers_token_uq", @@ -13733,9 +13792,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_teams_file_transfers_work_idx": { "name": "chat_teams_file_transfers_work_idx", @@ -13760,97 +13819,97 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "restrict" + "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" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_publications", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_conversations", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_external_principals", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -13950,26 +14009,26 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "chat_voice_callbacks_endpoint_fk": { "name": "chat_voice_callbacks_endpoint_fk", "tableFrom": "chat_voice_callbacks", + "tableTo": "chat_endpoints", "columnsFrom": [ "company_id", "endpoint_id" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -14118,81 +14177,81 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "chat_voice_inbound_endpoint_fk": { "name": "chat_voice_inbound_endpoint_fk", "tableFrom": "chat_voice_inbound_calls", + "tableTo": "chat_endpoints", "columnsFrom": [ "company_id", "endpoint_id" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "chat_voice_inbound_session_fk": { "name": "chat_voice_inbound_session_fk", "tableFrom": "chat_voice_inbound_calls", + "tableTo": "chat_voice_sessions", "columnsFrom": [ "company_id", "session_id" ], - "tableTo": "chat_voice_sessions", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" }, "chat_voice_inbound_intake_fk": { "name": "chat_voice_inbound_intake_fk", "tableFrom": "chat_voice_inbound_calls", + "tableTo": "issues", "columnsFrom": [ "company_id", "intake_issue_id" ], - "tableTo": "issues", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" }, "chat_voice_inbound_requested_issue_fk": { "name": "chat_voice_inbound_requested_issue_fk", "tableFrom": "chat_voice_inbound_calls", + "tableTo": "issues", "columnsFrom": [ "company_id", "requested_issue_id" ], - "tableTo": "issues", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, "uniqueConstraints": { "chat_voice_inbound_provider_uq": { "name": "chat_voice_inbound_provider_uq", + "nullsNotDistinct": false, "columns": [ "provider_session_id" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -14277,40 +14336,40 @@ "chat_voice_phone_lines_low_trust_environment_id_environments_id_fk": { "name": "chat_voice_phone_lines_low_trust_environment_id_environments_id_fk", "tableFrom": "chat_voice_phone_lines", + "tableTo": "environments", "columnsFrom": [ "low_trust_environment_id" ], - "tableTo": "environments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" }, "chat_voice_phone_lines_endpoint_fk": { "name": "chat_voice_phone_lines_endpoint_fk", "tableFrom": "chat_voice_phone_lines", + "tableTo": "chat_endpoints", "columnsFrom": [ "company_id", "endpoint_id" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, "uniqueConstraints": { "chat_voice_phone_lines_number_uq": { "name": "chat_voice_phone_lines_number_uq", + "nullsNotDistinct": false, "columns": [ "provider_number_id" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -14410,26 +14469,26 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "chat_voice_reports_session_fk": { "name": "chat_voice_reports_session_fk", "tableFrom": "chat_voice_reports", + "tableTo": "chat_voice_sessions", "columnsFrom": [ "company_id", "session_id" ], - "tableTo": "chat_voice_sessions", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -14528,9 +14587,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_voice_replies_cursor_uq": { "name": "chat_voice_replies_cursor_uq", @@ -14549,54 +14608,54 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "chat_voice_replies_company_id_companies_id_fk": { "name": "chat_voice_replies_company_id_companies_id_fk", "tableFrom": "chat_voice_replies", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "chat_voice_replies_session_fk": { "name": "chat_voice_replies_session_fk", "tableFrom": "chat_voice_replies", + "tableTo": "chat_voice_sessions", "columnsFrom": [ "company_id", "session_id" ], - "tableTo": "chat_voice_sessions", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" }, "chat_voice_replies_publication_fk": { "name": "chat_voice_replies_publication_fk", "tableFrom": "chat_voice_replies", + "tableTo": "chat_publications", "columnsFrom": [ "company_id", "publication_id" ], - "tableTo": "chat_publications", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -14774,9 +14833,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_voice_sessions_request_uq": { "name": "chat_voice_sessions_request_uq", @@ -14801,9 +14860,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_voice_sessions_live_caller_task_uq": { "name": "chat_voice_sessions_live_caller_task_uq", @@ -14828,10 +14887,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"chat_voice_sessions\".\"state\" not in ('ended', 'failed', 'expired')", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "chat_voice_sessions_expiry_idx": { "name": "chat_voice_sessions_expiry_idx", @@ -14850,95 +14909,95 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "chat_voice_sessions_company_id_companies_id_fk": { "name": "chat_voice_sessions_company_id_companies_id_fk", "tableFrom": "chat_voice_sessions", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "chat_voice_sessions_endpoint_fk": { "name": "chat_voice_sessions_endpoint_fk", "tableFrom": "chat_voice_sessions", + "tableTo": "chat_endpoints", "columnsFrom": [ "company_id", "endpoint_id" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" }, "chat_voice_sessions_conversation_fk": { "name": "chat_voice_sessions_conversation_fk", "tableFrom": "chat_voice_sessions", + "tableTo": "chat_conversations", "columnsFrom": [ "company_id", "conversation_id" ], - "tableTo": "chat_conversations", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" }, "chat_voice_sessions_issue_fk": { "name": "chat_voice_sessions_issue_fk", "tableFrom": "chat_voice_sessions", + "tableTo": "issues", "columnsFrom": [ "company_id", "issue_id" ], - "tableTo": "issues", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" }, "chat_voice_sessions_agent_fk": { "name": "chat_voice_sessions_agent_fk", "tableFrom": "chat_voice_sessions", + "tableTo": "agents", "columnsFrom": [ "company_id", "assigned_agent_id" ], - "tableTo": "agents", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, "uniqueConstraints": { "chat_voice_sessions_company_id_uq": { "name": "chat_voice_sessions_company_id_uq", + "nullsNotDistinct": false, "columns": [ "company_id", "id" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -15055,9 +15114,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "chat_voice_tool_calls_webhook_uq": { "name": "chat_voice_tool_calls_webhook_uq", @@ -15076,54 +15135,54 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "chat_voice_tool_calls_company_id_companies_id_fk": { "name": "chat_voice_tool_calls_company_id_companies_id_fk", "tableFrom": "chat_voice_tool_calls", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "chat_voice_tool_calls_session_fk": { "name": "chat_voice_tool_calls_session_fk", "tableFrom": "chat_voice_tool_calls", + "tableTo": "chat_voice_sessions", "columnsFrom": [ "company_id", "session_id" ], - "tableTo": "chat_voice_sessions", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" }, "chat_voice_tool_calls_delivery_fk": { "name": "chat_voice_tool_calls_delivery_fk", "tableFrom": "chat_voice_tool_calls", + "tableTo": "chat_deliveries", "columnsFrom": [ "company_id", "delivery_id" ], - "tableTo": "chat_deliveries", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -15248,9 +15307,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cli_auth_challenges_approved_by_idx": { "name": "cli_auth_challenges_approved_by_idx", @@ -15263,9 +15322,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cli_auth_challenges_requested_company_idx": { "name": "cli_auth_challenges_requested_company_idx", @@ -15278,50 +15337,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "user", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "board_api_keys", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -15478,9 +15537,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": {}, @@ -15540,9 +15599,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_logos_asset_uq": { "name": "company_logos_asset_uq", @@ -15555,37 +15614,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "assets", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -15675,9 +15734,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_memberships_principal_status_idx": { "name": "company_memberships_principal_status_idx", @@ -15702,9 +15761,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_memberships_company_status_idx": { "name": "company_memberships_company_status_idx", @@ -15723,24 +15782,24 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -15823,28 +15882,28 @@ "company_observability_company_id_companies_id_fk": { "name": "company_observability_company_id_companies_id_fk", "tableFrom": "company_observability", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "company_observability_credentials_secret_id_company_secrets_id_fk": { "name": "company_observability_credentials_secret_id_company_secrets_id_fk", "tableFrom": "company_observability", + "tableTo": "company_secrets", "columnsFrom": [ "credentials_secret_id" ], - "tableTo": "company_secrets", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -15958,63 +16017,63 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "goals", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -16124,9 +16183,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_secret_bindings_secret_idx": { "name": "company_secret_bindings_secret_idx", @@ -16139,9 +16198,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_secret_bindings_target_idx": { "name": "company_secret_bindings_target_idx", @@ -16166,9 +16225,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_secret_bindings_target_path_uq": { "name": "company_secret_bindings_target_path_uq", @@ -16199,37 +16258,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "company_secrets", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -16464,9 +16523,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_secret_proposals_proposer_status_idx": { "name": "company_secret_proposals_proposer_status_idx", @@ -16485,9 +16544,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_secret_proposals_expiry_idx": { "name": "company_secret_proposals_expiry_idx", @@ -16506,9 +16565,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_secret_proposals_secret_proposal_idx": { "name": "company_secret_proposals_secret_proposal_idx", @@ -16521,9 +16580,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_secret_proposals_interaction_idx": { "name": "company_secret_proposals_interaction_idx", @@ -16536,128 +16595,128 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "company_secrets", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "company_secret_proposals", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issue_thread_interactions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "company_secrets", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -16802,9 +16861,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_secret_provider_configs_company_provider_idx": { "name": "company_secret_provider_configs_company_provider_idx", @@ -16823,9 +16882,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_secret_provider_configs_default_uq": { "name": "company_secret_provider_configs_default_uq", @@ -16844,38 +16903,38 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"company_secret_provider_configs\".\"is_default\" = true", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -16988,9 +17047,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_secret_versions_value_sha256_idx": { "name": "company_secret_versions_value_sha256_idx", @@ -17003,9 +17062,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_secret_versions_fingerprint_idx": { "name": "company_secret_versions_fingerprint_idx", @@ -17018,9 +17077,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_secret_versions_secret_version_uq": { "name": "company_secret_versions_secret_version_uq", @@ -17039,37 +17098,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "company_secrets", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -17114,6 +17173,12 @@ "primaryKey": false, "notNull": false }, + "owner_project_id": { + "name": "owner_project_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, "owner_plugin_id": { "name": "owner_plugin_id", "type": "uuid", @@ -17254,9 +17319,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_secrets_company_scope_idx": { "name": "company_secrets_company_scope_idx", @@ -17275,9 +17340,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_secrets_company_owner_idx": { "name": "company_secrets_company_owner_idx", @@ -17296,9 +17361,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_secrets_user_definition_owner_idx": { "name": "company_secrets_user_definition_owner_idx", @@ -17323,9 +17388,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_secrets_company_provider_idx": { "name": "company_secrets_company_provider_idx", @@ -17344,9 +17409,43 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} + }, + "company_secrets_project_instance_owner_key_uq": { + "name": "company_secrets_project_instance_owner_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner_project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner_plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"company_secrets\".\"scope\" = 'instance' and \"company_secrets\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} }, "company_secrets_instance_owner_key_uq": { "name": "company_secrets_instance_owner_key_uq", @@ -17377,10 +17476,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"company_secrets\".\"scope\" = 'instance' and \"company_secrets\".\"deleted_at\" is null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "company_secrets_provider_config_idx": { "name": "company_secrets_provider_config_idx", @@ -17393,9 +17492,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_secrets_company_name_uq": { "name": "company_secrets_company_name_uq", @@ -17414,10 +17513,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"company_secrets\".\"scope\" = 'company' and \"company_secrets\".\"deleted_at\" is null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "company_secrets_company_key_uq": { "name": "company_secrets_company_key_uq", @@ -17436,10 +17535,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"company_secrets\".\"scope\" = 'company' and \"company_secrets\".\"deleted_at\" is null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "company_secrets_user_definition_owner_uq": { "name": "company_secrets_user_definition_owner_uq", @@ -17464,92 +17563,107 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"company_secrets\".\"scope\" = 'user' and \"company_secrets\".\"deleted_at\" is null", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" }, "company_secrets_owner_plugin_id_plugins_id_fk": { "name": "company_secrets_owner_plugin_id_plugins_id_fk", "tableFrom": "company_secrets", + "tableTo": "plugins", "columnsFrom": [ "owner_plugin_id" ], - "tableTo": "plugins", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "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" ], - "tableTo": "user_secret_definitions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "company_secret_provider_configs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" }, "company_secrets_company_id_owner_agent_id_agents_company_id_id_fk": { "name": "company_secrets_company_id_owner_agent_id_agents_company_id_id_fk", "tableFrom": "company_secrets", + "tableTo": "agents", "columnsFrom": [ "company_id", "owner_agent_id" ], - "tableTo": "agents", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" + }, + "company_secrets_company_id_owner_project_id_projects_company_id_id_fk": { + "name": "company_secrets_company_id_owner_project_id_projects_company_id_id_fk", + "tableFrom": "company_secrets", + "tableTo": "projects", + "columnsFrom": [ + "company_id", + "owner_project_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -17558,7 +17672,7 @@ "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 and \"company_secrets\".\"owner_agent_id\" is null and \"company_secrets\".\"owner_plugin_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 and \"company_secrets\".\"owner_agent_id\" is null and \"company_secrets\".\"owner_plugin_id\" is null\n ) or (\n \"company_secrets\".\"scope\" = 'instance'\n and \"company_secrets\".\"owner_user_id\" is null and \"company_secrets\".\"user_secret_definition_id\" is null\n and \"company_secrets\".\"owner_agent_id\" is not null and \"company_secrets\".\"owner_plugin_id\" is not null\n )" + "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 and \"company_secrets\".\"owner_agent_id\" is null and \"company_secrets\".\"owner_project_id\" is null and \"company_secrets\".\"owner_plugin_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 and \"company_secrets\".\"owner_agent_id\" is null and \"company_secrets\".\"owner_project_id\" is null and \"company_secrets\".\"owner_plugin_id\" is null\n ) or (\n \"company_secrets\".\"scope\" = 'instance'\n and \"company_secrets\".\"owner_user_id\" is null and \"company_secrets\".\"user_secret_definition_id\" is null\n and (\"company_secrets\".\"owner_agent_id\" is not null) <> (\"company_secrets\".\"owner_project_id\" is not null)\n and \"company_secrets\".\"owner_plugin_id\" is not null\n )" } }, "isRLSEnabled": false @@ -17619,15 +17733,15 @@ "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -17666,15 +17780,15 @@ "company_skill_import_budgets_company_id_companies_id_fk": { "name": "company_skill_import_budgets_company_id_companies_id_fk", "tableFrom": "company_skill_import_budgets", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -17775,9 +17889,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_skill_source_entries_company_skill_idx": { "name": "company_skill_source_entries_company_skill_idx", @@ -17796,50 +17910,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "company_skill_sources", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "company_skills", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -18083,10 +18197,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"company_skill_sources\".\"removed_at\" is null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "company_skill_sources_identity_ref_idx": { "name": "company_skill_sources_identity_ref_idx", @@ -18111,25 +18225,25 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"company_skill_sources\".\"removed_at\" is null", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -18294,9 +18408,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "project_skill_sources_source_project_idx": { "name": "project_skill_sources_source_project_idx", @@ -18321,9 +18435,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "project_skill_sources_sync_idx": { "name": "project_skill_sources_sync_idx", @@ -18348,63 +18462,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "project_skill_sources_company_id_companies_id_fk": { "name": "project_skill_sources_company_id_companies_id_fk", "tableFrom": "project_skill_sources", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "project_skill_sources_project_id_projects_id_fk": { "name": "project_skill_sources_project_id_projects_id_fk", "tableFrom": "project_skill_sources", + "tableTo": "projects", "columnsFrom": [ "project_id" ], - "tableTo": "projects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "project_skill_sources_workspace_id_project_workspaces_id_fk": { "name": "project_skill_sources_workspace_id_project_workspaces_id_fk", "tableFrom": "project_skill_sources", + "tableTo": "project_workspaces", "columnsFrom": [ "workspace_id" ], - "tableTo": "project_workspaces", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" }, "project_skill_sources_source_id_company_skill_sources_id_fk": { "name": "project_skill_sources_source_id_company_skill_sources_id_fk", "tableFrom": "project_skill_sources", + "tableTo": "company_skill_sources", "columnsFrom": [ "source_id" ], - "tableTo": "company_skill_sources", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -18505,9 +18619,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_skill_comments_parent_idx": { "name": "company_skill_comments_parent_idx", @@ -18520,63 +18634,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "company_skills", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "company_skill_comments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -18640,24 +18754,24 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "company_skill_repository_snapshots_company_id_companies_id_fk": { "name": "company_skill_repository_snapshots_company_id_companies_id_fk", "tableFrom": "company_skill_repository_snapshots", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -18727,9 +18841,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_skill_stars_skill_user_idx": { "name": "company_skill_stars_skill_user_idx", @@ -18748,9 +18862,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_skill_stars_company_skill_created_idx": { "name": "company_skill_stars_company_skill_created_idx", @@ -18775,50 +18889,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "company_skills", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -18913,9 +19027,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_skill_test_inputs_company_skill_active_idx": { "name": "company_skill_test_inputs_company_skill_active_idx", @@ -18940,37 +19054,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "company_skills", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -19083,50 +19197,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -19316,9 +19430,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_skill_test_runs_company_issue_idx": { "name": "company_skill_test_runs_company_issue_idx", @@ -19337,9 +19451,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_skill_test_runs_company_input_created_idx": { "name": "company_skill_test_runs_company_input_created_idx", @@ -19364,9 +19478,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_skill_test_runs_company_status_idx": { "name": "company_skill_test_runs_company_status_idx", @@ -19385,9 +19499,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_skill_test_runs_company_harness_expires_idx": { "name": "company_skill_test_runs_company_harness_expires_idx", @@ -19406,89 +19520,89 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "company_skills", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "company_skill_test_inputs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "company_skill_versions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "restrict" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "restrict" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "restrict" + "onDelete": "restrict", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -19607,9 +19721,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_skill_versions_skill_release_idx": { "name": "company_skill_versions_skill_release_idx", @@ -19628,10 +19742,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"company_skill_versions\".\"release_id\" is not null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "company_skill_versions_company_skill_created_idx": { "name": "company_skill_versions_company_skill_created_idx", @@ -19656,63 +19770,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "company_skills", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "company_skill_versions_repository_snapshot_id_company_skill_repository_snapshots_id_fk": { "name": "company_skill_versions_repository_snapshot_id_company_skill_repository_snapshots_id_fk", "tableFrom": "company_skill_versions", + "tableTo": "company_skill_repository_snapshots", "columnsFrom": [ "repository_snapshot_id" ], - "tableTo": "company_skill_repository_snapshots", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "restrict" + "onDelete": "restrict", + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -19948,9 +20062,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_skills_company_name_idx": { "name": "company_skills_company_name_idx", @@ -19969,9 +20083,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_skills_company_folder_idx": { "name": "company_skills_company_folder_idx", @@ -19990,9 +20104,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_skills_company_categories_idx": { "name": "company_skills_company_categories_idx", @@ -20005,9 +20119,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "gin", - "concurrently": false + "with": {} }, "company_skills_company_sharing_scope_idx": { "name": "company_skills_company_sharing_scope_idx", @@ -20026,9 +20140,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_skills_company_current_version_idx": { "name": "company_skills_company_current_version_idx", @@ -20047,9 +20161,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_skills_company_forked_from_idx": { "name": "company_skills_company_forked_from_idx", @@ -20068,76 +20182,76 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "folders", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "company_skills", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "company_skill_versions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -20272,9 +20386,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_transfer_runs_idempotency_direction_idx": { "name": "company_transfer_runs_idempotency_direction_idx", @@ -20293,9 +20407,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_transfer_runs_actor_status_idx": { "name": "company_transfer_runs_actor_status_idx", @@ -20314,24 +20428,24 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -20397,9 +20511,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_user_sidebar_preferences_user_idx": { "name": "company_user_sidebar_preferences_user_idx", @@ -20412,9 +20526,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "company_user_sidebar_preferences_company_user_uq": { "name": "company_user_sidebar_preferences_company_user_uq", @@ -20433,24 +20547,24 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -20574,9 +20688,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "completion_contracts_issue_hash_uq": { "name": "completion_contracts_issue_hash_uq", @@ -20595,68 +20709,68 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "completion_contracts", "columnsTo": [ "company_id", "issue_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -20794,9 +20908,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "connection_event_deliveries_company_status_idx": { "name": "connection_event_deliveries_company_status_idx", @@ -20821,24 +20935,24 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -20902,37 +21016,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "issue_thread_interactions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -21135,9 +21249,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cost_events_provider_request_idx": { "name": "cost_events_provider_request_idx", @@ -21162,9 +21276,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cost_events_company_project_occurred_idx": { "name": "cost_events_company_project_occurred_idx", @@ -21189,9 +21303,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cost_events_unpriced_idx": { "name": "cost_events_unpriced_idx", @@ -21216,10 +21330,10 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"cost_events\".\"cost_status\" = 'unpriced' and \"cost_events\".\"billing_type\" <> 'subscription_included'", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "cost_events_data_quality_idx": { "name": "cost_events_data_quality_idx", @@ -21244,10 +21358,10 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"cost_events\".\"billing_type\" = 'unknown' or (\"cost_events\".\"cost_status\" = 'unpriced' and \"cost_events\".\"billing_type\" <> 'subscription_included')", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "cost_events_company_occurred_idx": { "name": "cost_events_company_occurred_idx", @@ -21266,9 +21380,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cost_events_company_subscription_occurred_idx": { "name": "cost_events_company_subscription_occurred_idx", @@ -21293,9 +21407,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cost_events_company_agent_occurred_idx": { "name": "cost_events_company_agent_occurred_idx", @@ -21320,9 +21434,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cost_events_company_provider_occurred_idx": { "name": "cost_events_company_provider_occurred_idx", @@ -21347,9 +21461,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cost_events_company_biller_occurred_idx": { "name": "cost_events_company_biller_occurred_idx", @@ -21374,9 +21488,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "cost_events_company_heartbeat_run_idx": { "name": "cost_events_company_heartbeat_run_idx", @@ -21395,102 +21509,102 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "projects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "goals", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" }, "cost_events_subscription_id_ai_subscriptions_id_fk": { "name": "cost_events_subscription_id_ai_subscriptions_id_fk", "tableFrom": "cost_events", + "tableTo": "ai_subscriptions", "columnsFrom": [ "subscription_id" ], - "tableTo": "ai_subscriptions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -21569,15 +21683,15 @@ "company_decision_models_company_id_companies_id_fk": { "name": "company_decision_models_company_id_companies_id_fk", "tableFrom": "company_decision_models", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -21773,9 +21887,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "decision_invocations_pending_idx": { "name": "decision_invocations_pending_idx", @@ -21788,10 +21902,10 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"decision_invocations\".\"status\" = 'running'", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "decision_invocations_cost_idx": { "name": "decision_invocations_cost_idx", @@ -21804,37 +21918,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "decision_invocations_company_id_companies_id_fk": { "name": "decision_invocations_company_id_companies_id_fk", "tableFrom": "decision_invocations", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "decision_invocations_cost_event_id_cost_events_id_fk": { "name": "decision_invocations_cost_event_id_cost_events_id_fk", "tableFrom": "decision_invocations", + "tableTo": "cost_events", "columnsFrom": [ "cost_event_id" ], - "tableTo": "cost_events", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -21972,9 +22086,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "decision_archive_notification_outbox_pending_idx": { "name": "decision_archive_notification_outbox_pending_idx", @@ -21993,37 +22107,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -22140,9 +22254,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "decision_queue_items_company_source_idx": { "name": "decision_queue_items_company_source_idx", @@ -22167,78 +22281,78 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agent_api_keys", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "decision_queues", "columnsTo": [ "id", "company_id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -22370,9 +22484,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "decision_queues_company_updated_idx": { "name": "decision_queues_company_updated_idx", @@ -22391,74 +22505,74 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agent_api_keys", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -22605,9 +22719,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "decision_retention_company_archived_idx": { "name": "decision_retention_company_archived_idx", @@ -22626,50 +22740,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -22812,9 +22926,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "decision_triage_company_decide_by_idx": { "name": "decision_triage_company_decide_by_idx", @@ -22833,63 +22947,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agent_api_keys", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -23029,9 +23143,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "decision_triage_events_queue_created_idx": { "name": "decision_triage_events_queue_created_idx", @@ -23050,76 +23164,76 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "decision_queues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agent_api_keys", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -23246,9 +23360,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "decision_training_examples_issue_idx": { "name": "decision_training_examples_issue_idx", @@ -23261,9 +23375,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "decision_training_examples_source_author_uq": { "name": "decision_training_examples_source_author_uq", @@ -23288,37 +23402,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -23400,63 +23514,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -23550,9 +23664,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "decision_effect_executions_target_issue_idx": { "name": "decision_effect_executions_target_issue_idx", @@ -23565,50 +23679,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "decisions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "activity_log", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -23652,9 +23766,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "decision_target_issues_issue_idx": { "name": "decision_target_issues_issue_idx", @@ -23667,50 +23781,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "decisions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": { @@ -23912,9 +24026,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "decisions_bundle_idx": { "name": "decisions_bundle_idx", @@ -23927,9 +24041,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "decisions_origin_issue_idx": { "name": "decisions_origin_issue_idx", @@ -23942,9 +24056,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "decisions_company_idempotency_uq": { "name": "decisions_company_idempotency_uq", @@ -23963,77 +24077,77 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"decisions\".\"idempotency_key\" IS NOT NULL", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "decision_bundles", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -24157,9 +24271,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "document_annotation_anchor_snapshots_company_document_revision_idx": { "name": "document_annotation_anchor_snapshots_company_document_revision_idx", @@ -24184,76 +24298,76 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "document_annotation_threads", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "documents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "document_revisions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "document_revisions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -24390,9 +24504,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "document_annotation_comments_company_issue_created_at_idx": { "name": "document_annotation_comments_company_issue_created_at_idx", @@ -24417,9 +24531,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "document_annotation_comments_company_routine_created_at_idx": { "name": "document_annotation_comments_company_routine_created_at_idx", @@ -24444,9 +24558,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "document_annotation_comments_company_case_created_at_idx": { "name": "document_annotation_comments_company_case_created_at_idx", @@ -24471,9 +24585,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "document_annotation_comments_company_document_created_at_idx": { "name": "document_annotation_comments_company_document_created_at_idx", @@ -24498,9 +24612,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "document_annotation_comments_issue_comment_idx": { "name": "document_annotation_comments_issue_comment_idx", @@ -24513,9 +24627,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "document_annotation_comments_body_search_idx": { "name": "document_annotation_comments_body_search_idx", @@ -24529,128 +24643,128 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "gin", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "document_annotation_threads", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "routines", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "cases", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "documents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issue_comments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -24875,9 +24989,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "document_annotation_threads_company_issue_status_idx": { "name": "document_annotation_threads_company_issue_status_idx", @@ -24902,9 +25016,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "document_annotation_threads_company_routine_status_idx": { "name": "document_annotation_threads_company_routine_status_idx", @@ -24929,9 +25043,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "document_annotation_threads_company_case_status_idx": { "name": "document_annotation_threads_company_case_status_idx", @@ -24956,9 +25070,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "document_annotation_threads_company_current_revision_open_idx": { "name": "document_annotation_threads_company_current_revision_open_idx", @@ -24989,9 +25103,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "document_annotation_threads_company_anchor_state_idx": { "name": "document_annotation_threads_company_anchor_state_idx", @@ -25010,128 +25124,128 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "routines", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "cases", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "documents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "document_revisions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "document_revisions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -25219,9 +25333,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "document_memberships_company_user_document_uq": { "name": "document_memberships_company_user_document_uq", @@ -25246,37 +25360,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "documents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -25383,9 +25497,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "document_revisions_company_document_created_idx": { "name": "document_revisions_company_document_created_idx", @@ -25410,63 +25524,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "documents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -25605,9 +25719,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "documents_company_created_idx": { "name": "documents_company_created_idx", @@ -25626,9 +25740,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "documents_title_search_idx": { "name": "documents_title_search_idx", @@ -25642,9 +25756,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "gin", - "concurrently": false + "with": {} }, "documents_latest_body_search_idx": { "name": "documents_latest_body_search_idx", @@ -25658,63 +25772,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "gin", - "concurrently": false + "with": {} } }, "foreignKeys": { "documents_company_id_companies_id_fk": { "name": "documents_company_id_companies_id_fk", "tableFrom": "documents", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -25854,10 +25968,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"dot_agent_bindings\".\"revoked_at\" IS NULL", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "dot_bindings_active_grant_uq": { "name": "dot_bindings_active_grant_uq", @@ -25870,10 +25984,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"dot_agent_bindings\".\"revoked_at\" IS NULL AND \"dot_agent_bindings\".\"grant_id\" IS NOT NULL", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "dot_bindings_pairing_code_uq": { "name": "dot_bindings_pairing_code_uq", @@ -25886,50 +26000,50 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "dot_agent_bindings_company_id_companies_id_fk": { "name": "dot_agent_bindings_company_id_companies_id_fk", "tableFrom": "dot_agent_bindings", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "dot_agent_bindings_agent_id_agents_id_fk": { "name": "dot_agent_bindings_agent_id_agents_id_fk", "tableFrom": "dot_agent_bindings", + "tableTo": "agents", "columnsFrom": [ "agent_id" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "dot_agent_bindings_grant_id_mcp_oauth_grants_id_fk": { "name": "dot_agent_bindings_grant_id_mcp_oauth_grants_id_fk", "tableFrom": "dot_agent_bindings", + "tableTo": "mcp_oauth_grants", "columnsFrom": [ "grant_id" ], - "tableTo": "mcp_oauth_grants", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -26022,9 +26136,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "dot_mailbox_binding_cursor_idx": { "name": "dot_mailbox_binding_cursor_idx", @@ -26049,50 +26163,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "dot_mailbox_items_company_id_companies_id_fk": { "name": "dot_mailbox_items_company_id_companies_id_fk", "tableFrom": "dot_mailbox_items", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "dot_mailbox_items_binding_id_dot_agent_bindings_id_fk": { "name": "dot_mailbox_items_binding_id_dot_agent_bindings_id_fk", "tableFrom": "dot_mailbox_items", + "tableTo": "dot_agent_bindings", "columnsFrom": [ "binding_id" ], - "tableTo": "dot_agent_bindings", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "dot_mailbox_items_assignment_id_dot_runner_assignments_id_fk": { "name": "dot_mailbox_items_assignment_id_dot_runner_assignments_id_fk", "tableFrom": "dot_mailbox_items", + "tableTo": "dot_runner_assignments", "columnsFrom": [ "assignment_id" ], - "tableTo": "dot_runner_assignments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -26242,9 +26356,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "dot_assignments_active_binding_uq": { "name": "dot_assignments_active_binding_uq", @@ -26257,10 +26371,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"dot_runner_assignments\".\"status\" IN ('offered', 'accepted')", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "dot_assignments_company_idx": { "name": "dot_assignments_company_idx", @@ -26273,63 +26387,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "dot_runner_assignments_company_id_companies_id_fk": { "name": "dot_runner_assignments_company_id_companies_id_fk", "tableFrom": "dot_runner_assignments", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "dot_runner_assignments_binding_id_dot_agent_bindings_id_fk": { "name": "dot_runner_assignments_binding_id_dot_agent_bindings_id_fk", "tableFrom": "dot_runner_assignments", + "tableTo": "dot_agent_bindings", "columnsFrom": [ "binding_id" ], - "tableTo": "dot_agent_bindings", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "dot_runner_assignments_run_id_heartbeat_runs_id_fk": { "name": "dot_runner_assignments_run_id_heartbeat_runs_id_fk", "tableFrom": "dot_runner_assignments", + "tableTo": "heartbeat_runs", "columnsFrom": [ "run_id" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "dot_runner_assignments_agent_id_agents_id_fk": { "name": "dot_runner_assignments_agent_id_agents_id_fk", "tableFrom": "dot_runner_assignments", + "tableTo": "agents", "columnsFrom": [ "agent_id" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -26443,37 +26557,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "dot_runner_operations_company_id_companies_id_fk": { "name": "dot_runner_operations_company_id_companies_id_fk", "tableFrom": "dot_runner_operations", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "dot_runner_operations_assignment_id_dot_runner_assignments_id_fk": { "name": "dot_runner_operations_assignment_id_dot_runner_assignments_id_fk", "tableFrom": "dot_runner_operations", + "tableTo": "dot_runner_assignments", "columnsFrom": [ "assignment_id" ], - "tableTo": "dot_runner_assignments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -26540,17 +26654,17 @@ "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" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -26663,9 +26777,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "email_messages_conversation_idx": { "name": "email_messages_conversation_idx", @@ -26690,41 +26804,41 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_conversations", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -26810,42 +26924,42 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"email_sends\".\"outcome\" in ('queued', 'uncertain')", - "concurrently": false + "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" ], - "tableTo": "chat_endpoints", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "chat_publications", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -27000,9 +27114,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "environment_custom_image_setup_sessions_environment_active_uq": { "name": "environment_custom_image_setup_sessions_environment_active_uq", @@ -27015,10 +27129,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"environment_custom_image_setup_sessions\".\"status\" IN ('starting', 'waiting_for_user', 'capturing')", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "environment_custom_image_setup_sessions_template_idx": { "name": "environment_custom_image_setup_sessions_template_idx", @@ -27031,9 +27145,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "environment_custom_image_setup_sessions_promoted_template_idx": { "name": "environment_custom_image_setup_sessions_promoted_template_idx", @@ -27046,9 +27160,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "environment_custom_image_setup_sessions_expires_idx": { "name": "environment_custom_image_setup_sessions_expires_idx", @@ -27061,9 +27175,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "environment_custom_image_setup_sessions_provider_lease_idx": { "name": "environment_custom_image_setup_sessions_provider_lease_idx", @@ -27082,76 +27196,76 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "environments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "environment_custom_image_templates", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "environment_custom_image_templates", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "environment_leases", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -27284,9 +27398,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "environment_custom_image_templates_environment_provider_status_idx": { "name": "environment_custom_image_templates_environment_provider_status_idx", @@ -27311,9 +27425,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "environment_custom_image_templates_environment_active_uq": { "name": "environment_custom_image_templates_environment_active_uq", @@ -27326,10 +27440,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"environment_custom_image_templates\".\"status\" = 'active'", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "environment_custom_image_templates_superseded_by_idx": { "name": "environment_custom_image_templates_superseded_by_idx", @@ -27342,9 +27456,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "environment_custom_image_templates_last_used_idx": { "name": "environment_custom_image_templates_last_used_idx", @@ -27357,50 +27471,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "environments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "environment_custom_image_templates", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -27536,6 +27650,22 @@ } }, "indexes": { + "environment_leases_retained_recovery_adoption_idx": { + "name": "environment_leases_retained_recovery_adoption_idx", + "columns": [ + { + "expression": "heartbeat_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"environment_leases\".\"lease_policy\" = 'retain_on_failure'\n and \"environment_leases\".\"heartbeat_run_id\" is not null\n and not (coalesce(\"environment_leases\".\"metadata\", '{}'::jsonb) ? 'systemRecoveryAdoption')", + "concurrently": true, + "method": "btree", + "with": {} + }, "environment_leases_company_environment_status_idx": { "name": "environment_leases_company_environment_status_idx", "columns": [ @@ -27559,9 +27689,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "environment_leases_company_execution_workspace_idx": { "name": "environment_leases_company_execution_workspace_idx", @@ -27580,9 +27710,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "environment_leases_company_issue_idx": { "name": "environment_leases_company_issue_idx", @@ -27601,9 +27731,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "environment_leases_heartbeat_run_idx": { "name": "environment_leases_heartbeat_run_idx", @@ -27616,9 +27746,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "environment_leases_company_last_used_idx": { "name": "environment_leases_company_last_used_idx", @@ -27637,9 +27767,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "environment_leases_provider_lease_idx": { "name": "environment_leases_provider_lease_idx", @@ -27652,76 +27782,76 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "environments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "execution_workspaces", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -27814,9 +27944,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "environments_local_driver_idx": { "name": "environments_local_driver_idx", @@ -27829,10 +27959,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"environments\".\"driver\" = 'local'", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "environments_managed_sandbox_idx": { "name": "environments_managed_sandbox_idx", @@ -27845,10 +27975,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"environments\".\"driver\" = 'sandbox' AND (\"environments\".\"metadata\" ->> 'managedByPaperclip')::boolean = true", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "environments_name_idx": { "name": "environments_name_idx", @@ -27861,9 +27991,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": {}, @@ -28004,9 +28134,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "execution_workspace_repositories_path_idx": { "name": "execution_workspace_repositories_path_idx", @@ -28025,9 +28155,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "execution_workspace_repositories_request_idx": { "name": "execution_workspace_repositories_request_idx", @@ -28046,9 +28176,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "execution_workspace_repositories_company_workspace_idx": { "name": "execution_workspace_repositories_company_workspace_idx", @@ -28067,50 +28197,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "execution_workspace_repositories_company_id_companies_id_fk": { "name": "execution_workspace_repositories_company_id_companies_id_fk", "tableFrom": "execution_workspace_repositories", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "execution_workspace_repositories_execution_workspace_id_execution_workspaces_id_fk": { "name": "execution_workspace_repositories_execution_workspace_id_execution_workspaces_id_fk", "tableFrom": "execution_workspace_repositories", + "tableTo": "execution_workspaces", "columnsFrom": [ "execution_workspace_id" ], - "tableTo": "execution_workspaces", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "execution_workspace_repositories_requested_by_issue_id_issues_id_fk": { "name": "execution_workspace_repositories_requested_by_issue_id_issues_id_fk", "tableFrom": "execution_workspace_repositories", + "tableTo": "issues", "columnsFrom": [ "requested_by_issue_id" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -28223,24 +28353,24 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "execution_workspace_repository_access_company_id_companies_id_fk": { "name": "execution_workspace_repository_access_company_id_companies_id_fk", "tableFrom": "execution_workspace_repository_access", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -28317,24 +28447,24 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "execution_workspace_repository_roots_company_id_companies_id_fk": { "name": "execution_workspace_repository_roots_company_id_companies_id_fk", "tableFrom": "execution_workspace_repository_roots", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -28343,6 +28473,237 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.execution_workspace_restore_points": { + "name": "execution_workspace_restore_points", + "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 + }, + "source_issue_id": { + "name": "source_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "generation": { + "name": "generation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "object_key": { + "name": "object_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "byte_size": { + "name": "byte_size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "activated_at": { + "name": "activated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "tree_closed_at": { + "name": "tree_closed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "delete_after": { + "name": "delete_after", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "review_generation": { + "name": "review_generation", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "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": { + "execution_workspace_restore_points_object_key_uniq": { + "name": "execution_workspace_restore_points_object_key_uniq", + "columns": [ + { + "expression": "object_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_workspace_restore_points_live_generation_uniq": { + "name": "execution_workspace_restore_points_live_generation_uniq", + "columns": [ + { + "expression": "execution_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "generation", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"execution_workspace_restore_points\".\"status\" <> 'deleted'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_workspace_restore_points_status_delete_after_idx": { + "name": "execution_workspace_restore_points_status_delete_after_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "delete_after", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_workspace_restore_points_company_idx": { + "name": "execution_workspace_restore_points_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_workspace_restore_points_source_issue_idx": { + "name": "execution_workspace_restore_points_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_workspace_restore_points_active_source_idx": { + "name": "execution_workspace_restore_points_active_source_idx", + "columns": [ + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"execution_workspace_restore_points\".\"status\" = 'active'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.execution_workspace_runtime_leases": { "name": "execution_workspace_runtime_leases", "schema": "", @@ -28455,9 +28816,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "execution_workspace_runtime_leases_company_owner_idx": { "name": "execution_workspace_runtime_leases_company_owner_idx", @@ -28476,9 +28837,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "execution_workspace_runtime_leases_expires_at_idx": { "name": "execution_workspace_runtime_leases_expires_at_idx", @@ -28491,86 +28852,86 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "execution_workspaces", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -28757,9 +29118,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "execution_workspaces_company_project_workspace_status_idx": { "name": "execution_workspaces_company_project_workspace_status_idx", @@ -28784,9 +29145,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "execution_workspaces_company_source_issue_idx": { "name": "execution_workspaces_company_source_issue_idx", @@ -28805,9 +29166,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "execution_workspaces_company_last_used_idx": { "name": "execution_workspaces_company_last_used_idx", @@ -28826,9 +29187,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "execution_workspaces_company_branch_idx": { "name": "execution_workspaces_company_branch_idx", @@ -28847,76 +29208,76 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "projects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "restrict" + "onDelete": "restrict", + "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" ], - "tableTo": "project_workspaces", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "execution_workspaces", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -29066,9 +29427,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "external_object_mentions_company_object_idx": { "name": "external_object_mentions_company_object_idx", @@ -29087,9 +29448,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "external_object_mentions_company_provider_idx": { "name": "external_object_mentions_company_provider_idx", @@ -29114,9 +29475,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "external_object_mentions_company_source_record_uq": { "name": "external_object_mentions_company_source_record_uq", @@ -29165,10 +29526,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"external_object_mentions\".\"source_record_id\" is not null and \"external_object_mentions\".\"canonical_identity_hash\" is not null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "external_object_mentions_company_source_null_record_uq": { "name": "external_object_mentions_company_source_null_record_uq", @@ -29211,64 +29572,64 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"external_object_mentions\".\"source_record_id\" is null and \"external_object_mentions\".\"canonical_identity_hash\" is not null", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "external_objects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "plugins", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -29500,9 +29861,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "external_objects_company_provider_status_idx": { "name": "external_objects_company_provider_status_idx", @@ -29527,9 +29888,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "external_objects_company_refresh_idx": { "name": "external_objects_company_refresh_idx", @@ -29548,9 +29909,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "external_objects_company_external_id_uq": { "name": "external_objects_company_external_id_uq", @@ -29581,9 +29942,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "external_objects_company_identity_uq": { "name": "external_objects_company_identity_uq", @@ -29614,37 +29975,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "plugins", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -29714,15 +30075,15 @@ "company_fast_responses_company_id_companies_id_fk": { "name": "company_fast_responses_company_id_companies_id_fk", "tableFrom": "company_fast_responses", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -29944,9 +30305,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "fast_response_requests_history_idx": { "name": "fast_response_requests_history_idx", @@ -29965,9 +30326,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "fast_response_requests_work_idx": { "name": "fast_response_requests_work_idx", @@ -29980,38 +30341,38 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"fast_response_requests\".\"status\" in ('pending', 'running')", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} } }, "foreignKeys": { "fast_response_requests_company_id_companies_id_fk": { "name": "fast_response_requests_company_id_companies_id_fk", "tableFrom": "fast_response_requests", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "fast_response_requests_cost_event_id_cost_events_id_fk": { "name": "fast_response_requests_cost_event_id_cost_events_id_fk", "tableFrom": "fast_response_requests", + "tableTo": "cost_events", "columnsFrom": [ "cost_event_id" ], - "tableTo": "cost_events", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -30206,9 +30567,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "feedback_exports_company_created_idx": { "name": "feedback_exports_company_created_idx", @@ -30227,9 +30588,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "feedback_exports_company_status_idx": { "name": "feedback_exports_company_status_idx", @@ -30254,9 +30615,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "feedback_exports_company_issue_idx": { "name": "feedback_exports_company_issue_idx", @@ -30281,9 +30642,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "feedback_exports_company_project_idx": { "name": "feedback_exports_company_project_idx", @@ -30308,9 +30669,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "feedback_exports_company_author_idx": { "name": "feedback_exports_company_author_idx", @@ -30335,63 +30696,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "feedback_votes", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "projects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -30511,9 +30872,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "feedback_votes_issue_target_idx": { "name": "feedback_votes_issue_target_idx", @@ -30538,9 +30899,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "feedback_votes_author_idx": { "name": "feedback_votes_author_idx", @@ -30559,9 +30920,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "feedback_votes_company_target_author_idx": { "name": "feedback_votes_company_target_author_idx", @@ -30592,37 +30953,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -30833,9 +31194,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "finance_events_company_occurred_idx": { "name": "finance_events_company_occurred_idx", @@ -30854,9 +31215,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "finance_events_company_biller_occurred_idx": { "name": "finance_events_company_biller_occurred_idx", @@ -30881,9 +31242,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "finance_events_company_kind_occurred_idx": { "name": "finance_events_company_kind_occurred_idx", @@ -30908,9 +31269,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "finance_events_company_direction_occurred_idx": { "name": "finance_events_company_direction_occurred_idx", @@ -30935,9 +31296,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "finance_events_company_heartbeat_run_idx": { "name": "finance_events_company_heartbeat_run_idx", @@ -30956,9 +31317,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "finance_events_company_cost_event_idx": { "name": "finance_events_company_cost_event_idx", @@ -30977,102 +31338,102 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "projects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "goals", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "cost_events", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -31186,9 +31547,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "folders_company_kind_root_slug_uq": { "name": "folders_company_kind_root_slug_uq", @@ -31213,10 +31574,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"folders\".\"parent_id\" is null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "folders_company_kind_parent_slug_uq": { "name": "folders_company_kind_parent_slug_uq", @@ -31247,10 +31608,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"folders\".\"parent_id\" is not null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "folders_company_kind_system_key_uq": { "name": "folders_company_kind_system_key_uq", @@ -31275,10 +31636,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"folders\".\"system_key\" is not null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "folders_company_kind_parent_position_idx": { "name": "folders_company_kind_parent_position_idx", @@ -31315,37 +31676,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "folders_company_id_companies_id_fk": { "name": "folders_company_id_companies_id_fk", "tableFrom": "folders", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "folders", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "restrict" + "onDelete": "restrict", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -31436,50 +31797,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "goals_company_id_companies_id_fk": { "name": "goals_company_id_companies_id_fk", "tableFrom": "goals", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "goals", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -31641,9 +32002,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "grok_dispatch_company_status_idx": { "name": "grok_dispatch_company_status_idx", @@ -31662,9 +32023,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "grok_dispatch_assignment_kind_idx": { "name": "grok_dispatch_assignment_kind_idx", @@ -31683,76 +32044,76 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "grok_bot_dispatches_company_id_companies_id_fk": { "name": "grok_bot_dispatches_company_id_companies_id_fk", "tableFrom": "grok_bot_dispatches", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "grok_bot_dispatches_routine_id_grok_bot_routines_id_fk": { "name": "grok_bot_dispatches_routine_id_grok_bot_routines_id_fk", "tableFrom": "grok_bot_dispatches", + "tableTo": "grok_bot_routines", "columnsFrom": [ "routine_id" ], - "tableTo": "grok_bot_routines", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "grok_bot_dispatches_binding_id_dot_agent_bindings_id_fk": { "name": "grok_bot_dispatches_binding_id_dot_agent_bindings_id_fk", "tableFrom": "grok_bot_dispatches", + "tableTo": "dot_agent_bindings", "columnsFrom": [ "binding_id" ], - "tableTo": "dot_agent_bindings", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "grok_bot_dispatches_grant_id_mcp_oauth_grants_id_fk": { "name": "grok_bot_dispatches_grant_id_mcp_oauth_grants_id_fk", "tableFrom": "grok_bot_dispatches", + "tableTo": "mcp_oauth_grants", "columnsFrom": [ "grant_id" ], - "tableTo": "mcp_oauth_grants", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "grok_bot_dispatches_assignment_id_dot_runner_assignments_id_fk": { "name": "grok_bot_dispatches_assignment_id_dot_runner_assignments_id_fk", "tableFrom": "grok_bot_dispatches", + "tableTo": "dot_runner_assignments", "columnsFrom": [ "assignment_id" ], - "tableTo": "dot_runner_assignments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -31878,9 +32239,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "grok_routines_company_agent_idx": { "name": "grok_routines_company_agent_idx", @@ -31899,76 +32260,76 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "grok_bot_routines_company_id_companies_id_fk": { "name": "grok_bot_routines_company_id_companies_id_fk", "tableFrom": "grok_bot_routines", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "grok_bot_routines_agent_id_agents_id_fk": { "name": "grok_bot_routines_agent_id_agents_id_fk", "tableFrom": "grok_bot_routines", + "tableTo": "agents", "columnsFrom": [ "agent_id" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "grok_bot_routines_binding_id_dot_agent_bindings_id_fk": { "name": "grok_bot_routines_binding_id_dot_agent_bindings_id_fk", "tableFrom": "grok_bot_routines", + "tableTo": "dot_agent_bindings", "columnsFrom": [ "binding_id" ], - "tableTo": "dot_agent_bindings", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "grok_bot_routines_secret_id_company_secrets_id_fk": { "name": "grok_bot_routines_secret_id_company_secrets_id_fk", "tableFrom": "grok_bot_routines", + "tableTo": "company_secrets", "columnsFrom": [ "secret_id" ], - "tableTo": "company_secrets", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" }, "grok_bot_routines_verified_grant_id_mcp_oauth_grants_id_fk": { "name": "grok_bot_routines_verified_grant_id_mcp_oauth_grants_id_fk", "tableFrom": "grok_bot_routines", + "tableTo": "mcp_oauth_grants", "columnsFrom": [ "verified_grant_id" ], - "tableTo": "mcp_oauth_grants", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -32103,9 +32464,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "heartbeat_run_events_run_source_event_uq": { "name": "heartbeat_run_events_run_source_event_uq", @@ -32124,10 +32485,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"heartbeat_run_events\".\"source_event_id\" is not null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "heartbeat_run_events_run_source_seq_uq": { "name": "heartbeat_run_events_run_source_seq_uq", @@ -32152,10 +32513,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"heartbeat_run_events\".\"source_instance_id\" is not null and \"heartbeat_run_events\".\"source_seq\" is not null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "heartbeat_run_events_company_run_idx": { "name": "heartbeat_run_events_company_run_idx", @@ -32174,9 +32535,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "heartbeat_run_events_company_created_idx": { "name": "heartbeat_run_events_company_created_idx", @@ -32195,50 +32556,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -32344,9 +32705,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "heartbeat_run_watchdog_decisions_company_run_snooze_idx": { "name": "heartbeat_run_watchdog_decisions_company_run_snooze_idx", @@ -32371,76 +32732,76 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -32939,6 +33300,22 @@ } }, "indexes": { + "heartbeat_runs_retained_recovery_adoption_idx": { + "name": "heartbeat_runs_retained_recovery_adoption_idx", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"heartbeat_runs\".\"result_json\"->'workspaceRestoreRecovery'->>'schema' = 'paperclip.workspace-restore-recovery.v1'\n and not (coalesce(\"heartbeat_runs\".\"result_json\", '{}'::jsonb) ? 'systemRecoveryAdoption')\n and \"heartbeat_runs\".\"status\" in ('failed', 'interrupted', 'timed_out', 'cancelled', 'succeeded')", + "concurrently": false, + "method": "btree", + "with": {} + }, "heartbeat_runs_cost_accounting_pending_idx": { "name": "heartbeat_runs_cost_accounting_pending_idx", "columns": [ @@ -32956,10 +33333,10 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"heartbeat_runs\".\"cost_accounting_pending\" = true", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "heartbeat_runs_cost_data_quality_idx": { "name": "heartbeat_runs_cost_data_quality_idx", @@ -32972,8 +33349,8 @@ }, { "expression": "coalesce(\"finished_at\", \"created_at\") desc", - "isExpression": true, "asc": true, + "isExpression": true, "nulls": "last" }, { @@ -32984,10 +33361,10 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"heartbeat_runs\".\"cost_accounting_pending\" = true and \"heartbeat_runs\".\"status\" in ('succeeded','failed','timed_out','cancelled','interrupted')", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "heartbeat_runs_execution_status_delivery_idx": { "name": "heartbeat_runs_execution_status_delivery_idx", @@ -33000,10 +33377,10 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"heartbeat_runs\".\"execution_status_delivery_id\" is not null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "heartbeat_runs_execution_control_deadline_idx": { "name": "heartbeat_runs_execution_control_deadline_idx", @@ -33016,10 +33393,10 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"heartbeat_runs\".\"execution_control_deadline_at\" is not null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "heartbeat_runs_native_replacement_predecessor_uq": { "name": "heartbeat_runs_native_replacement_predecessor_uq", @@ -33038,10 +33415,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"heartbeat_runs\".\"scheduled_retry_reason\" = 'native_safe_replacement'", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "heartbeat_runs_company_agent_started_idx": { "name": "heartbeat_runs_company_agent_started_idx", @@ -33066,9 +33443,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "heartbeat_runs_company_issue_created_idx": { "name": "heartbeat_runs_company_issue_created_idx", @@ -33093,9 +33470,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "heartbeat_runs_company_responsible_user_idx": { "name": "heartbeat_runs_company_responsible_user_idx", @@ -33120,9 +33497,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "heartbeat_runs_company_liveness_idx": { "name": "heartbeat_runs_company_liveness_idx", @@ -33147,9 +33524,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "heartbeat_runs_company_status_last_output_idx": { "name": "heartbeat_runs_company_status_last_output_idx", @@ -33174,9 +33551,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "heartbeat_runs_company_status_process_started_idx": { "name": "heartbeat_runs_company_status_process_started_idx", @@ -33201,9 +33578,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "heartbeat_runs_company_created_at_desc_idx": { "name": "heartbeat_runs_company_created_at_desc_idx", @@ -33222,9 +33599,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "heartbeat_runs_company_ctx_issue_created_idx": { "name": "heartbeat_runs_company_ctx_issue_created_idx", @@ -33237,8 +33614,8 @@ }, { "expression": "(\"context_snapshot\" ->> 'issueId')", - "isExpression": true, "asc": true, + "isExpression": true, "nulls": "last" }, { @@ -33249,9 +33626,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "heartbeat_runs_company_ctx_task_created_idx": { "name": "heartbeat_runs_company_ctx_task_created_idx", @@ -33264,8 +33641,8 @@ }, { "expression": "(\"context_snapshot\" ->> 'taskId')", - "isExpression": true, "asc": true, + "isExpression": true, "nulls": "last" }, { @@ -33276,9 +33653,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "heartbeat_runs_company_ctx_taskkey_created_idx": { "name": "heartbeat_runs_company_ctx_taskkey_created_idx", @@ -33291,8 +33668,8 @@ }, { "expression": "(\"context_snapshot\" ->> 'taskKey')", - "isExpression": true, "asc": true, + "isExpression": true, "nulls": "last" }, { @@ -33303,9 +33680,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "heartbeat_runs_company_ctx_paperclip_issue_created_idx": { "name": "heartbeat_runs_company_ctx_paperclip_issue_created_idx", @@ -33318,8 +33695,8 @@ }, { "expression": "(\"context_snapshot\" -> 'paperclipIssue' ->> 'id')", - "isExpression": true, "asc": true, + "isExpression": true, "nulls": "last" }, { @@ -33330,9 +33707,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": true, "method": "btree", - "concurrently": true + "with": {} }, "heartbeat_runs_company_origin_issue_idx": { "name": "heartbeat_runs_company_origin_issue_idx", @@ -33345,104 +33722,104 @@ }, { "expression": "coalesce(\n \"native_issue_id\"::text,\n nullif(\"context_snapshot\"->>'issueId', ''),\n nullif(\"context_snapshot\"->>'taskId', ''),\n nullif(\"context_snapshot\"->>'taskKey', '')\n )", - "isExpression": true, "asc": true, + "isExpression": true, "nulls": "last" } ], "isUnique": false, - "with": {}, + "concurrently": true, "method": "btree", - "concurrently": true + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" }, "heartbeat_runs_issue_id_issues_id_fk": { "name": "heartbeat_runs_issue_id_issues_id_fk", "tableFrom": "heartbeat_runs", + "tableTo": "issues", "columnsFrom": [ "issue_id" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "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" ], - "tableTo": "agent_wakeup_requests", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" - ], - "nullsNotDistinct": false + ] }, "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" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -33536,9 +33913,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "inbox_dismissals_company_item_idx": { "name": "inbox_dismissals_company_item_idx", @@ -33557,9 +33934,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "inbox_dismissals_company_user_item_idx": { "name": "inbox_dismissals_company_user_item_idx", @@ -33584,24 +33961,24 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -33671,9 +34048,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "connection_grant_delegations_grant_agent_uq": { "name": "connection_grant_delegations_grant_agent_uq", @@ -33692,52 +34069,52 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "connection_grants", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -33813,9 +34190,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "connection_grant_members_grant_subject_uq": { "name": "connection_grant_members_grant_subject_uq", @@ -33840,39 +34217,39 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "connection_grants", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -34029,9 +34406,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "connection_grants_subject_user_idx": { "name": "connection_grants_subject_user_idx", @@ -34050,9 +34427,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "connection_grants_subject_agent_idx": { "name": "connection_grants_subject_agent_idx", @@ -34071,9 +34448,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "connection_grants_user_uq": { "name": "connection_grants_user_uq", @@ -34092,9 +34469,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "connection_grants_agent_uq": { "name": "connection_grants_agent_uq", @@ -34113,9 +34490,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "connection_grants_default_uq": { "name": "connection_grants_default_uq", @@ -34128,90 +34505,90 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"connection_grants\".\"is_default\" = true and \"connection_grants\".\"kind\" = 'organization'", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_connections", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, "uniqueConstraints": { "connection_grants_company_id_uq": { "name": "connection_grants_company_id_uq", + "nullsNotDistinct": false, "columns": [ "company_id", "id" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -34381,9 +34758,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "connection_token_issuances_connection_created_idx": { "name": "connection_token_issuances_connection_created_idx", @@ -34408,9 +34785,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "connection_token_issuances_agent_connection_idx": { "name": "connection_token_issuances_agent_connection_idx", @@ -34441,9 +34818,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "connection_token_issuances_run_idx": { "name": "connection_token_issuances_run_idx", @@ -34462,102 +34839,102 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "tool_applications", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_connections", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "projects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -34631,24 +35008,24 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "environments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -34714,9 +35091,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "instance_user_roles_role_idx": { "name": "instance_user_roles_role_idx", @@ -34729,9 +35106,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": {}, @@ -34835,9 +35212,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "invites_company_invite_state_idx": { "name": "invites_company_invite_state_idx", @@ -34868,24 +35245,24 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "invites_company_id_companies_id_fk": { "name": "invites_company_id_companies_id_fk", "tableFrom": "invites", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -34985,9 +35362,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_access_grants_issue_idx": { "name": "issue_access_grants_issue_idx", @@ -35006,37 +35383,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "issue_access_grants_issue_id_issues_id_fk": { "name": "issue_access_grants_issue_id_issues_id_fk", "tableFrom": "issue_access_grants", + "tableTo": "issues", "columnsFrom": [ "issue_id" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "issue_access_grants_granted_by_agent_id_agents_id_fk": { "name": "issue_access_grants_granted_by_agent_id_agents_id_fk", "tableFrom": "issue_access_grants", + "tableTo": "agents", "columnsFrom": [ "granted_by_agent_id" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -35108,9 +35485,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_approvals_approval_idx": { "name": "issue_approvals_approval_idx", @@ -35123,9 +35500,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_approvals_company_idx": { "name": "issue_approvals_company_idx", @@ -35138,63 +35515,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "approvals", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": { @@ -35285,9 +35662,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_attachments_issue_comment_idx": { "name": "issue_attachments_issue_comment_idx", @@ -35300,9 +35677,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_attachments_originating_run_idx": { "name": "issue_attachments_originating_run_idx", @@ -35315,9 +35692,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_attachments_asset_uq": { "name": "issue_attachments_asset_uq", @@ -35330,76 +35707,76 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "assets", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issue_comments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -35585,9 +35962,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_comments_company_idx": { "name": "issue_comments_company_idx", @@ -35600,9 +35977,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_comments_company_issue_created_at_idx": { "name": "issue_comments_company_issue_created_at_idx", @@ -35627,9 +36004,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_comments_company_author_issue_created_at_idx": { "name": "issue_comments_company_author_issue_created_at_idx", @@ -35660,9 +36037,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_comments_body_search_idx": { "name": "issue_comments_body_search_idx", @@ -35676,148 +36053,148 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "gin", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "user", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" - ], - "nullsNotDistinct": false + ] }, "issue_comments_company_id_uq": { "name": "issue_comments_company_id_uq", + "nullsNotDistinct": false, "columns": [ "company_id", "id" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -35879,9 +36256,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_create_idempotency_keys_issue_idx": { "name": "issue_create_idempotency_keys_issue_idx", @@ -35894,9 +36271,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_create_idempotency_keys_company_created_at_idx": { "name": "issue_create_idempotency_keys_company_created_at_idx", @@ -35915,37 +36292,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -36028,9 +36405,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_documents_document_uq": { "name": "issue_documents_document_uq", @@ -36043,9 +36420,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_documents_company_issue_updated_idx": { "name": "issue_documents_company_issue_updated_idx", @@ -36070,50 +36447,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "documents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -36220,9 +36597,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_execution_decisions_stage_idx": { "name": "issue_execution_decisions_stage_idx", @@ -36247,63 +36624,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -36400,9 +36777,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_inbox_archives_company_user_idx": { "name": "issue_inbox_archives_company_user_idx", @@ -36421,9 +36798,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_inbox_archives_company_issue_user_idx": { "name": "issue_inbox_archives_company_issue_user_idx", @@ -36448,63 +36825,63 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -36560,9 +36937,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_labels_label_idx": { "name": "issue_labels_label_idx", @@ -36575,9 +36952,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_labels_company_idx": { "name": "issue_labels_company_idx", @@ -36590,50 +36967,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "labels", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": { @@ -36782,9 +37159,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_plan_decompositions_active_owner_idx": { "name": "issue_plan_decompositions_active_owner_idx", @@ -36803,10 +37180,10 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"issue_plan_decompositions\".\"status\" = 'in_flight'", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "issue_plan_decompositions_source_revision_uq": { "name": "issue_plan_decompositions_source_revision_uq", @@ -36831,89 +37208,89 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "document_revisions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issue_thread_interactions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -37053,9 +37430,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_question_response_deliveries_correlation_uq": { "name": "issue_question_response_deliveries_correlation_uq", @@ -37068,9 +37445,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_question_response_deliveries_pending_idx": { "name": "issue_question_response_deliveries_pending_idx", @@ -37089,9 +37466,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_question_response_deliveries_company_issue_idx": { "name": "issue_question_response_deliveries_company_issue_idx", @@ -37116,76 +37493,76 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issue_thread_interactions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -37272,9 +37649,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_read_states_company_user_idx": { "name": "issue_read_states_company_user_idx", @@ -37293,9 +37670,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_read_states_company_issue_user_idx": { "name": "issue_read_states_company_issue_user_idx", @@ -37320,37 +37697,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -37528,6 +37905,71 @@ } }, "indexes": { + "issue_recovery_actions_source_run_idx": { + "name": "issue_recovery_actions_source_run_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "(\"evidence\"->>'runId')", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_recovery_actions_system_recovery_due_idx": { + "name": "issue_recovery_actions_system_recovery_due_idx", + "columns": [ + { + "expression": "(\"evidence\"#>'{systemRecovery,nextAttemptAt}')", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"issue_recovery_actions\".\"evidence\" ? 'systemRecovery'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_recovery_actions_system_recovery_adoption_idx": { + "name": "issue_recovery_actions_system_recovery_adoption_idx", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "not (\"issue_recovery_actions\".\"evidence\" ? 'systemRecovery') and (\n \"issue_recovery_actions\".\"evidence\"->'workspaceRestoreRecovery'->>'schema' = 'paperclip.workspace-restore-recovery.v1'\n or \"issue_recovery_actions\".\"cause\" in ('native_workspace_sync_out_unrecoverable', 'native_workspace_sync_out_retry_exhausted')\n )", + "concurrently": false, + "method": "btree", + "with": {} + }, "issue_recovery_actions_retained_workspace_recovery_idx": { "name": "issue_recovery_actions_retained_workspace_recovery_idx", "columns": [ @@ -37539,10 +37981,10 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\n (\"issue_recovery_actions\".\"evidence\"->'workspaceRestoreRecovery'->>'schema' = 'paperclip.workspace-restore-recovery.v1'\n or \"issue_recovery_actions\".\"cause\" in ('native_workspace_sync_out_unrecoverable', 'native_workspace_sync_out_retry_exhausted'))\n and (\"issue_recovery_actions\".\"evidence\"->'executionReconciliation' is null\n or \"issue_recovery_actions\".\"evidence\"#>>'{workspaceRecovery,status}' = 'recovered')\n ", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "issue_recovery_actions_company_source_status_idx": { "name": "issue_recovery_actions_company_source_status_idx", @@ -37567,9 +38009,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_recovery_actions_company_owner_status_idx": { "name": "issue_recovery_actions_company_owner_status_idx", @@ -37594,9 +38036,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_recovery_actions_company_recovery_issue_idx": { "name": "issue_recovery_actions_company_recovery_issue_idx", @@ -37615,9 +38057,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_recovery_actions_active_source_uq": { "name": "issue_recovery_actions_active_source_uq", @@ -37636,10 +38078,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"issue_recovery_actions\".\"status\" in ('active', 'escalated')", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "issue_recovery_actions_active_fingerprint_uq": { "name": "issue_recovery_actions_active_fingerprint_uq", @@ -37670,90 +38112,90 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"issue_recovery_actions\".\"status\" in ('active', 'escalated')", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -37848,9 +38290,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_reference_mentions_company_target_issue_idx": { "name": "issue_reference_mentions_company_target_issue_idx", @@ -37869,9 +38311,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_reference_mentions_company_issue_pair_idx": { "name": "issue_reference_mentions_company_issue_pair_idx", @@ -37896,9 +38338,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_reference_mentions_company_source_mention_record_uq": { "name": "issue_reference_mentions_company_source_mention_record_uq", @@ -37935,10 +38377,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"issue_reference_mentions\".\"source_record_id\" is not null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "issue_reference_mentions_company_source_mention_null_record_uq": { "name": "issue_reference_mentions_company_source_mention_null_record_uq", @@ -37969,51 +38411,51 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"issue_reference_mentions\".\"source_record_id\" is null", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -38102,9 +38544,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_relations_company_related_issue_idx": { "name": "issue_relations_company_related_issue_idx", @@ -38123,9 +38565,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_relations_company_type_idx": { "name": "issue_relations_company_type_idx", @@ -38144,9 +38586,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_relations_company_edge_uq": { "name": "issue_relations_company_edge_uq", @@ -38177,63 +38619,63 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -38416,6 +38858,18 @@ "primaryKey": false, "notNull": false }, + "dismissed_at": { + "name": "dismissed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "dismissed_by_user_id": { + "name": "dismissed_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, "created_at": { "name": "created_at", "type": "timestamp with time zone", @@ -38443,9 +38897,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_thread_interactions_company_issue_created_at_idx": { "name": "issue_thread_interactions_company_issue_created_at_idx", @@ -38470,9 +38924,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_thread_interactions_company_issue_status_idx": { "name": "issue_thread_interactions_company_issue_status_idx", @@ -38497,9 +38951,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_thread_interactions_company_issue_idempotency_uq": { "name": "issue_thread_interactions_company_issue_idempotency_uq", @@ -38524,10 +38978,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"issue_thread_interactions\".\"idempotency_key\" IS NOT NULL", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "issue_thread_interactions_source_comment_idx": { "name": "issue_thread_interactions_source_comment_idx", @@ -38540,9 +38994,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_thread_interactions_addressee_agent_idx": { "name": "issue_thread_interactions_addressee_agent_idx", @@ -38555,9 +39009,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_thread_interactions_addressee_user_idx": { "name": "issue_thread_interactions_addressee_user_idx", @@ -38570,115 +39024,115 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issue_comments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -38810,9 +39264,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_tree_hold_members_company_issue_idx": { "name": "issue_tree_hold_members_company_issue_idx", @@ -38831,9 +39285,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_tree_hold_members_hold_depth_idx": { "name": "issue_tree_hold_members_hold_depth_idx", @@ -38852,89 +39306,89 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issue_tree_holds", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -39097,9 +39551,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_tree_holds_company_status_mode_idx": { "name": "issue_tree_holds_company_status_mode_idx", @@ -39124,89 +39578,89 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -39375,9 +39829,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_watchdogs_company_status_idx": { "name": "issue_watchdogs_company_status_idx", @@ -39396,9 +39850,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_watchdogs_company_agent_idx": { "name": "issue_watchdogs_company_agent_idx", @@ -39417,9 +39871,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_watchdogs_company_watchdog_issue_uq": { "name": "issue_watchdogs_company_watchdog_issue_uq", @@ -39438,116 +39892,116 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"issue_watchdogs\".\"watchdog_issue_id\" is not null", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -39717,9 +40171,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_work_products_company_execution_workspace_type_idx": { "name": "issue_work_products_company_execution_workspace_type_idx", @@ -39744,9 +40198,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_work_products_company_provider_external_id_idx": { "name": "issue_work_products_company_provider_external_id_idx", @@ -39771,9 +40225,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issue_work_products_company_updated_idx": { "name": "issue_work_products_company_updated_idx", @@ -39792,89 +40246,89 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "projects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "execution_workspaces", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "workspace_runtime_services", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -40335,9 +40789,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issues_company_status_idx": { "name": "issues_company_status_idx", @@ -40356,9 +40810,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issues_company_harness_kind_idx": { "name": "issues_company_harness_kind_idx", @@ -40377,9 +40831,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issues_company_assignee_status_idx": { "name": "issues_company_assignee_status_idx", @@ -40404,9 +40858,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issues_company_assignee_user_status_idx": { "name": "issues_company_assignee_user_status_idx", @@ -40431,9 +40885,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issues_company_responsible_user_idx": { "name": "issues_company_responsible_user_idx", @@ -40452,9 +40906,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issues_company_parent_idx": { "name": "issues_company_parent_idx", @@ -40473,9 +40927,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issues_company_privacy_parent_idx": { "name": "issues_company_privacy_parent_idx", @@ -40494,9 +40948,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issues_company_privacy_root_idx": { "name": "issues_company_privacy_root_idx", @@ -40515,9 +40969,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issues_company_project_idx": { "name": "issues_company_project_idx", @@ -40536,9 +40990,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issues_company_origin_idx": { "name": "issues_company_origin_idx", @@ -40563,9 +41017,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issues_company_project_workspace_idx": { "name": "issues_company_project_workspace_idx", @@ -40584,9 +41038,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issues_company_execution_workspace_idx": { "name": "issues_company_execution_workspace_idx", @@ -40605,9 +41059,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issues_company_monitor_due_idx": { "name": "issues_company_monitor_due_idx", @@ -40626,9 +41080,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issues_company_updated_idx": { "name": "issues_company_updated_idx", @@ -40647,9 +41101,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issues_company_created_idx": { "name": "issues_company_created_idx", @@ -40668,9 +41122,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issues_open_normalized_title_created_idx": { "name": "issues_open_normalized_title_created_idx", @@ -40689,8 +41143,8 @@ }, { "expression": "lower(regexp_replace(btrim(\"title\"), '\\s+', ' ', 'g'))", - "isExpression": true, "asc": true, + "isExpression": true, "nulls": "last" }, { @@ -40701,10 +41155,10 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"issues\".\"hidden_at\" is null and \"issues\".\"status\" not in ('done', 'cancelled')", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "issues_company_priority_idx": { "name": "issues_company_priority_idx", @@ -40723,9 +41177,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issues_identifier_idx": { "name": "issues_identifier_idx", @@ -40738,9 +41192,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "issues_title_search_idx": { "name": "issues_title_search_idx", @@ -40754,9 +41208,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "gin", - "concurrently": false + "with": {} }, "issues_identifier_search_idx": { "name": "issues_identifier_search_idx", @@ -40770,9 +41224,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "gin", - "concurrently": false + "with": {} }, "issues_description_search_idx": { "name": "issues_description_search_idx", @@ -40786,9 +41240,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "gin", - "concurrently": false + "with": {} }, "issues_open_routine_execution_uq": { "name": "issues_open_routine_execution_uq", @@ -40819,10 +41273,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "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 + "concurrently": false, + "method": "btree", + "with": {} }, "issues_active_liveness_recovery_incident_uq": { "name": "issues_active_liveness_recovery_incident_uq", @@ -40847,10 +41301,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "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 + "concurrently": false, + "method": "btree", + "with": {} }, "issues_active_liveness_recovery_leaf_uq": { "name": "issues_active_liveness_recovery_leaf_uq", @@ -40875,10 +41329,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "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 + "concurrently": false, + "method": "btree", + "with": {} }, "issues_active_stale_run_evaluation_uq": { "name": "issues_active_stale_run_evaluation_uq", @@ -40903,10 +41357,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "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 + "concurrently": false, + "method": "btree", + "with": {} }, "issues_active_task_watchdog_uq": { "name": "issues_active_task_watchdog_uq", @@ -40931,10 +41385,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "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 + "concurrently": false, + "method": "btree", + "with": {} }, "issues_active_productivity_review_uq": { "name": "issues_active_productivity_review_uq", @@ -40959,10 +41413,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "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 + "concurrently": false, + "method": "btree", + "with": {} }, "issues_active_stranded_issue_recovery_uq": { "name": "issues_active_stranded_issue_recovery_uq", @@ -40987,10 +41441,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "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 + "concurrently": false, + "method": "btree", + "with": {} }, "issues_onboarding_first_task_uq": { "name": "issues_onboarding_first_task_uq", @@ -41003,192 +41457,192 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"issues\".\"origin_kind\" = 'onboarding_first_task'", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "projects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "project_workspaces", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "goals", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" }, "issues_privacy_root_issue_id_issues_id_fk": { "name": "issues_privacy_root_issue_id_issues_id_fk", "tableFrom": "issues", + "tableTo": "issues", "columnsFrom": [ "privacy_root_issue_id" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" }, "issues_privacy_parent_issue_id_issues_id_fk": { "name": "issues_privacy_parent_issue_id_issues_id_fk", "tableFrom": "issues", + "tableTo": "issues", "columnsFrom": [ "privacy_parent_issue_id" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "execution_workspaces", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, "uniqueConstraints": { "issues_company_id_uq": { "name": "issues_company_id_uq", + "nullsNotDistinct": false, "columns": [ "company_id", "id" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -41357,9 +41811,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "join_requests_company_status_type_created_idx": { "name": "join_requests_company_status_type_created_idx", @@ -41390,9 +41844,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "join_requests_pending_human_user_uq": { "name": "join_requests_pending_human_user_uq", @@ -41411,10 +41865,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"join_requests\".\"request_type\" = 'human' AND \"join_requests\".\"status\" = 'pending_approval' AND \"join_requests\".\"requesting_user_id\" IS NOT NULL", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "join_requests_pending_human_email_uq": { "name": "join_requests_pending_human_email_uq", @@ -41427,57 +41881,57 @@ }, { "expression": "lower(\"request_email_snapshot\")", - "isExpression": true, "asc": true, + "isExpression": true, "nulls": "last" } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"join_requests\".\"request_type\" = 'human' AND \"join_requests\".\"status\" = 'pending_approval' AND \"join_requests\".\"request_email_snapshot\" IS NOT NULL", - "concurrently": false + "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" ], - "tableTo": "invites", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -41542,9 +41996,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "labels_company_name_idx": { "name": "labels_company_name_idx", @@ -41563,24 +42017,24 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "labels_company_id_companies_id_fk": { "name": "labels_company_id_companies_id_fk", "tableFrom": "labels", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -41730,9 +42184,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "managed_agent_profiles_company_key_uq": { "name": "managed_agent_profiles_company_key_uq", @@ -41751,9 +42205,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "managed_agent_profiles_company_resource_uq": { "name": "managed_agent_profiles_company_resource_uq", @@ -41784,37 +42238,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "company_secrets", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "restrict" + "onDelete": "restrict", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -42000,99 +42454,100 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"native_run_finalizations\".\"control_deadline_at\" is not null", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "company_id", "native_issue_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "native_run_results", "columnsTo": [ "company_id", "issue_id", "run_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "work_assessments", "columnsTo": [ "company_id", "issue_id", "run_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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", @@ -42100,7 +42555,6 @@ "assessment_id", "decision_id" ], - "tableTo": "status_decisions", "columnsTo": [ "company_id", "issue_id", @@ -42108,8 +42562,8 @@ "assessment_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -42236,9 +42690,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "native_run_results_run_caller_result_uq": { "name": "native_run_results_run_caller_result_uq", @@ -42257,9 +42711,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "native_run_results_run_caller_dedupe_uq": { "name": "native_run_results_run_caller_dedupe_uq", @@ -42278,88 +42732,88 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "company_id", "native_issue_id", "id", "completion_contract_id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "completion_contracts", "columnsTo": [ "company_id", "issue_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -42477,9 +42931,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_automation_executions_company_case_idx": { "name": "pipeline_automation_executions_company_case_idx", @@ -42498,9 +42952,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_automation_executions_routine_idx": { "name": "pipeline_automation_executions_routine_idx", @@ -42513,9 +42967,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_automation_executions_execution_issue_idx": { "name": "pipeline_automation_executions_execution_issue_idx", @@ -42528,9 +42982,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_automation_executions_retry_of_execution_idx": { "name": "pipeline_automation_executions_retry_of_execution_idx", @@ -42543,63 +42997,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "pipeline_cases", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "routines", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -42675,9 +43129,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_case_blockers_blocked_by_idx": { "name": "pipeline_case_blockers_blocked_by_idx", @@ -42690,9 +43144,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_case_blockers_company_case_idx": { "name": "pipeline_case_blockers_company_case_idx", @@ -42711,50 +43165,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "pipeline_cases", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "pipeline_cases", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -42842,9 +43296,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_case_documents_document_uq": { "name": "pipeline_case_documents_document_uq", @@ -42857,9 +43311,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_case_documents_company_case_updated_idx": { "name": "pipeline_case_documents_company_case_updated_idx", @@ -42884,50 +43338,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "pipeline_cases", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "documents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -43041,9 +43495,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_case_events_company_case_idx": { "name": "pipeline_case_events_company_case_idx", @@ -43062,76 +43516,76 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "pipeline_cases", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "pipeline_stages", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "pipeline_stages", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -43251,9 +43705,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_case_issue_links_issue_idx": { "name": "pipeline_case_issue_links_issue_idx", @@ -43266,9 +43720,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_case_issue_links_company_case_idx": { "name": "pipeline_case_issue_links_company_case_idx", @@ -43287,9 +43741,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_case_issue_links_automation_attempt_idx": { "name": "pipeline_case_issue_links_automation_attempt_idx", @@ -43302,50 +43756,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "pipeline_cases", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -43587,9 +44041,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_cases_parent_request_key_uq": { "name": "pipeline_cases_parent_request_key_uq", @@ -43608,10 +44062,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"pipeline_cases\".\"request_key\" is not null and \"pipeline_cases\".\"retired_at\" is null", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "pipeline_cases_company_idx": { "name": "pipeline_cases_company_idx", @@ -43624,9 +44078,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_cases_pipeline_stage_idx": { "name": "pipeline_cases_pipeline_stage_idx", @@ -43645,9 +44099,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_cases_parent_idx": { "name": "pipeline_cases_parent_idx", @@ -43660,9 +44114,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_cases_automation_attempt_idx": { "name": "pipeline_cases_automation_attempt_idx", @@ -43675,9 +44129,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_cases_retired_idx": { "name": "pipeline_cases_retired_idx", @@ -43696,9 +44150,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_cases_lease_expires_idx": { "name": "pipeline_cases_lease_expires_idx", @@ -43711,90 +44165,90 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"pipeline_cases\".\"lease_expires_at\" is not null", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "pipelines", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "pipeline_stages", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "pipeline_cases", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -43886,9 +44340,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_documents_document_uq": { "name": "pipeline_documents_document_uq", @@ -43901,9 +44355,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_documents_company_pipeline_updated_idx": { "name": "pipeline_documents_company_pipeline_updated_idx", @@ -43928,50 +44382,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "pipelines", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "documents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -44061,9 +44515,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_stages_pipeline_position_idx": { "name": "pipeline_stages_pipeline_position_idx", @@ -44082,24 +44536,24 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "pipelines", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -44187,9 +44641,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_transitions_pipeline_from_idx": { "name": "pipeline_transitions_pipeline_from_idx", @@ -44208,9 +44662,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipeline_transitions_pipeline_to_idx": { "name": "pipeline_transitions_pipeline_to_idx", @@ -44229,50 +44683,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "pipelines", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "pipeline_stages", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "pipeline_stages", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -44380,9 +44834,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipelines_company_idx": { "name": "pipelines_company_idx", @@ -44395,9 +44849,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "pipelines_company_project_idx": { "name": "pipelines_company_project_idx", @@ -44416,50 +44870,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "pipelines_company_id_companies_id_fk": { "name": "pipelines_company_id_companies_id_fk", "tableFrom": "pipelines", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "projects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -44538,9 +44992,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_company_settings_plugin_idx": { "name": "plugin_company_settings_plugin_idx", @@ -44553,9 +45007,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_company_settings_company_plugin_uq": { "name": "plugin_company_settings_company_plugin_uq", @@ -44574,37 +45028,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "plugins", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -44682,37 +45136,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "plugins", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -44791,9 +45245,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_database_namespaces_namespace_idx": { "name": "plugin_database_namespaces_namespace_idx", @@ -44806,9 +45260,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_database_namespaces_status_idx": { "name": "plugin_database_namespaces_status_idx", @@ -44821,24 +45275,24 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "plugins", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -44940,9 +45394,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_entities_company_idx": { "name": "plugin_entities_company_idx", @@ -44955,9 +45409,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_entities_type_idx": { "name": "plugin_entities_type_idx", @@ -44970,9 +45424,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_entities_scope_idx": { "name": "plugin_entities_scope_idx", @@ -44991,50 +45445,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "plugins", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" - ], - "nullsNotDistinct": true + ] } }, "policies": {}, @@ -45134,9 +45588,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_job_runs_plugin_idx": { "name": "plugin_job_runs_plugin_idx", @@ -45149,9 +45603,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_job_runs_company_idx": { "name": "plugin_job_runs_company_idx", @@ -45164,9 +45618,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_job_runs_status_idx": { "name": "plugin_job_runs_status_idx", @@ -45179,50 +45633,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "plugin_jobs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "plugins", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -45306,9 +45760,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_jobs_next_run_idx": { "name": "plugin_jobs_next_run_idx", @@ -45321,9 +45775,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_jobs_unique_idx": { "name": "plugin_jobs_unique_idx", @@ -45342,24 +45796,24 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "plugins", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -45404,37 +45858,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "plugin_lifecycle_acknowledgments_plugin_id_plugins_id_fk": { "name": "plugin_lifecycle_acknowledgments_plugin_id_plugins_id_fk", "tableFrom": "plugin_lifecycle_acknowledgments", + "tableTo": "plugins", "columnsFrom": [ "plugin_id" ], - "tableTo": "plugins", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "plugin_lifecycle_acknowledgments_event_id_resource_lifecycle_events_id_fk": { "name": "plugin_lifecycle_acknowledgments_event_id_resource_lifecycle_events_id_fk", "tableFrom": "plugin_lifecycle_acknowledgments", + "tableTo": "resource_lifecycle_events", "columnsFrom": [ "event_id" ], - "tableTo": "resource_lifecycle_events", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": { @@ -45519,9 +45973,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_logs_company_idx": { "name": "plugin_logs_company_idx", @@ -45534,9 +45988,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_logs_level_idx": { "name": "plugin_logs_level_idx", @@ -45549,37 +46003,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "plugins", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -45669,9 +46123,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_managed_resources_plugin_idx": { "name": "plugin_managed_resources_plugin_idx", @@ -45684,9 +46138,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_managed_resources_resource_idx": { "name": "plugin_managed_resources_resource_idx", @@ -45705,9 +46159,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_managed_resources_company_plugin_resource_uq": { "name": "plugin_managed_resources_company_plugin_resource_uq", @@ -45738,37 +46192,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "plugins", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -45868,9 +46322,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_migrations_plugin_idx": { "name": "plugin_migrations_plugin_idx", @@ -45883,9 +46337,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_migrations_status_idx": { "name": "plugin_migrations_status_idx", @@ -45898,24 +46352,24 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "plugins", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -45998,38 +46452,38 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "plugins", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" - ], - "nullsNotDistinct": true + ] } }, "policies": {}, @@ -46135,9 +46589,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_webhook_deliveries_company_idx": { "name": "plugin_webhook_deliveries_company_idx", @@ -46150,9 +46604,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_webhook_deliveries_status_idx": { "name": "plugin_webhook_deliveries_status_idx", @@ -46165,9 +46619,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugin_webhook_deliveries_key_idx": { "name": "plugin_webhook_deliveries_key_idx", @@ -46180,37 +46634,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "plugins", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -46320,9 +46774,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "plugins_status_idx": { "name": "plugins_status_idx", @@ -46335,9 +46789,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": {}, @@ -46439,9 +46893,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "principal_permission_grants_company_permission_idx": { "name": "principal_permission_grants_company_permission_idx", @@ -46460,24 +46914,24 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -46553,9 +47007,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "project_access_members_subject_lookup_idx": { "name": "project_access_members_subject_lookup_idx", @@ -46586,37 +47040,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "project_access_members_company_id_companies_id_fk": { "name": "project_access_members_company_id_companies_id_fk", "tableFrom": "project_access_members", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "project_access_members_project_id_projects_id_fk": { "name": "project_access_members_project_id_projects_id_fk", "tableFrom": "project_access_members", + "tableTo": "projects", "columnsFrom": [ "project_id" ], - "tableTo": "projects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -46679,9 +47133,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "project_goals_goal_idx": { "name": "project_goals_goal_idx", @@ -46694,9 +47148,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "project_goals_company_idx": { "name": "project_goals_company_idx", @@ -46709,50 +47163,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "projects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "goals", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": { @@ -46844,9 +47298,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "project_memberships_company_user_starred_idx": { "name": "project_memberships_company_user_starred_idx", @@ -46871,9 +47325,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "project_memberships_project_idx": { "name": "project_memberships_project_idx", @@ -46886,9 +47340,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "project_memberships_company_user_project_uq": { "name": "project_memberships_company_user_project_uq", @@ -46913,37 +47367,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "projects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -47095,9 +47549,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "project_workspaces_project_primary_idx": { "name": "project_workspaces_project_primary_idx", @@ -47116,9 +47570,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "project_workspaces_project_source_type_idx": { "name": "project_workspaces_project_source_type_idx", @@ -47137,9 +47591,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "project_workspaces_company_shared_key_idx": { "name": "project_workspaces_company_shared_key_idx", @@ -47158,9 +47612,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "project_workspaces_project_remote_ref_idx": { "name": "project_workspaces_project_remote_ref_idx", @@ -47185,37 +47639,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "projects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -47355,6 +47809,27 @@ } }, "indexes": { + "projects_company_id_uq": { + "name": "projects_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": {} + }, "projects_company_idx": { "name": "projects_company_idx", "columns": [ @@ -47366,9 +47841,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "projects_company_personal_owner_uq": { "name": "projects_company_personal_owner_uq", @@ -47387,51 +47862,51 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"projects\".\"personal_owner_user_id\" is not null", - "concurrently": 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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "goals", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -47558,9 +48033,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "provider_trace_records_expiry_idx": { "name": "provider_trace_records_expiry_idx", @@ -47579,9 +48054,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "provider_trace_records_company_created_idx": { "name": "provider_trace_records_company_created_idx", @@ -47600,37 +48075,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -47741,9 +48216,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "remote_agent_profiles_company_key_uq": { "name": "remote_agent_profiles_company_key_uq", @@ -47762,24 +48237,24 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -47807,15 +48282,15 @@ "primaryKey": true, "notNull": true, "identity": { + "type": "always", "name": "resource_lifecycle_events_id_seq", + "schema": "public", "increment": "1", + "startWith": "1", "minValue": "1", "maxValue": "9223372036854775807", - "startWith": "1", "cache": "1", - "cycle": false, - "schema": "public", - "type": "always" + "cycle": false } }, "company_id": { @@ -47874,10 +48349,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"resource_lifecycle_events\".\"action\" = 'create'", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "resource_lifecycle_events_company_idx": { "name": "resource_lifecycle_events_company_idx", @@ -47896,9 +48371,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "resource_lifecycle_events_resource_order_idx": { "name": "resource_lifecycle_events_resource_order_idx", @@ -47929,24 +48404,24 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "resource_lifecycle_events_company_id_companies_id_fk": { "name": "resource_lifecycle_events_company_id_companies_id_fk", "tableFrom": "resource_lifecycle_events", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -48038,9 +48513,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routine_documents_document_uq": { "name": "routine_documents_document_uq", @@ -48053,9 +48528,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routine_documents_company_routine_updated_idx": { "name": "routine_documents_company_routine_updated_idx", @@ -48080,50 +48555,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "routines", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "documents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -48241,9 +48716,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routine_revisions_company_routine_created_idx": { "name": "routine_revisions_company_routine_created_idx", @@ -48268,9 +48743,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routine_revisions_company_responsible_user_idx": { "name": "routine_revisions_company_responsible_user_idx", @@ -48295,76 +48770,76 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "routines", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "routine_revisions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -48515,9 +48990,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routine_runs_revision_idx": { "name": "routine_runs_revision_idx", @@ -48530,9 +49005,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routine_runs_company_responsible_user_idx": { "name": "routine_runs_company_responsible_user_idx", @@ -48557,9 +49032,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routine_runs_trigger_idx": { "name": "routine_runs_trigger_idx", @@ -48578,9 +49053,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routine_runs_dispatch_fingerprint_idx": { "name": "routine_runs_dispatch_fingerprint_idx", @@ -48599,9 +49074,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routine_runs_linked_issue_idx": { "name": "routine_runs_linked_issue_idx", @@ -48614,9 +49089,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routine_runs_trigger_idempotency_idx": { "name": "routine_runs_trigger_idempotency_idx", @@ -48635,76 +49110,76 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "routines", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "routine_triggers", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "routine_revisions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -48892,9 +49367,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routine_triggers_company_kind_idx": { "name": "routine_triggers_company_kind_idx", @@ -48913,9 +49388,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routine_triggers_next_run_idx": { "name": "routine_triggers_next_run_idx", @@ -48928,9 +49403,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routine_triggers_public_id_idx": { "name": "routine_triggers_public_id_idx", @@ -48943,9 +49418,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routine_triggers_public_id_uq": { "name": "routine_triggers_public_id_uq", @@ -48958,76 +49433,76 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "routines", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "company_secrets", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -49091,37 +49566,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "routine_triggers", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -49345,9 +49820,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routines_company_assignee_idx": { "name": "routines_company_assignee_idx", @@ -49366,9 +49841,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routines_company_project_idx": { "name": "routines_company_project_idx", @@ -49387,9 +49862,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routines_company_folder_idx": { "name": "routines_company_folder_idx", @@ -49408,9 +49883,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routines_company_responsible_user_idx": { "name": "routines_company_responsible_user_idx", @@ -49429,9 +49904,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "routines_company_origin_idx": { "name": "routines_company_origin_idx", @@ -49456,115 +49931,115 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "routines_company_id_companies_id_fk": { "name": "routines_company_id_companies_id_fk", "tableFrom": "routines", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "projects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "folders", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "goals", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -49677,9 +50152,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "run_identity_contexts_run_correlation_idx": { "name": "run_identity_contexts_run_correlation_idx", @@ -49698,9 +50173,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "run_identity_contexts_company_run_idx": { "name": "run_identity_contexts_company_run_idx", @@ -49719,24 +50194,24 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -49800,50 +50275,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "assets", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -50010,9 +50485,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "secret_access_events_secret_created_idx": { "name": "secret_access_events_secret_created_idx", @@ -50031,9 +50506,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "secret_access_events_user_definition_created_idx": { "name": "secret_access_events_user_definition_created_idx", @@ -50052,9 +50527,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "secret_access_events_company_credential_owner_idx": { "name": "secret_access_events_company_credential_owner_idx", @@ -50079,9 +50554,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "secret_access_events_consumer_idx": { "name": "secret_access_events_consumer_idx", @@ -50106,9 +50581,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "secret_access_events_run_idx": { "name": "secret_access_events_run_idx", @@ -50121,89 +50596,89 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "company_secrets", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "user_secret_definitions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "plugins", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -50304,9 +50779,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "smoke_run_steps_company_path_idx": { "name": "smoke_run_steps_company_path_idx", @@ -50325,37 +50800,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "smoke_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -50447,9 +50922,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "smoke_runs_company_status_idx": { "name": "smoke_runs_company_status_idx", @@ -50468,24 +50943,24 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -50625,9 +51100,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "status_card_updates_generation_issue_idx": { "name": "status_card_updates_generation_issue_idx", @@ -50640,50 +51115,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "status_cards", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -50916,9 +51391,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "status_cards_company_next_eval_idx": { "name": "status_cards_company_next_eval_idx", @@ -50937,102 +51412,102 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "documents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -51171,9 +51646,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "status_decision_effects_company_idempotency_uq": { "name": "status_decision_effects_company_idempotency_uq", @@ -51192,56 +51667,56 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "status_decisions", "columnsTo": [ "company_id", "issue_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -51378,9 +51853,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "status_decisions_company_assessment_uq": { "name": "status_decisions_company_assessment_uq", @@ -51399,9 +51874,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "status_decisions_company_issue_digest_uq": { "name": "status_decisions_company_issue_digest_uq", @@ -51426,98 +51901,98 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "work_assessments", "columnsTo": [ "company_id", "issue_id", "run_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "status_decisions", "columnsTo": [ "company_id", "issue_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" - ], - "nullsNotDistinct": false + ] }, "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" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -51629,9 +52104,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "summary_slots_company_scope_idx": { "name": "summary_slots_company_scope_idx", @@ -51656,9 +52131,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "summary_slots_company_generating_issue_idx": { "name": "summary_slots_company_generating_issue_idx", @@ -51677,9 +52152,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "summary_slots_company_updated_idx": { "name": "summary_slots_company_updated_idx", @@ -51698,76 +52173,76 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "documents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" - ], - "nullsNotDistinct": true + ] } }, "policies": {}, @@ -51897,9 +52372,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_access_audit_connection_idx": { "name": "tool_access_audit_connection_idx", @@ -51912,9 +52387,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_access_audit_gateway_idx": { "name": "tool_access_audit_gateway_idx", @@ -51933,76 +52408,76 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "tool_mcp_gateways", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_mcp_gateway_tokens", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_connections", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_catalog_entries", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -52071,63 +52546,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "tool_action_requests", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issue_thread_interactions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -52295,9 +52770,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_action_requests_invocation_idx": { "name": "tool_action_requests_invocation_idx", @@ -52310,9 +52785,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_action_requests_issue_idx": { "name": "tool_action_requests_issue_idx", @@ -52331,115 +52806,115 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "tool_invocations", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issue_thread_interactions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "approvals", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -52554,9 +53029,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_applications_company_status_idx": { "name": "tool_applications_company_status_idx", @@ -52575,9 +53050,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_applications_company_name_uq": { "name": "tool_applications_company_name_uq", @@ -52596,9 +53071,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_applications_company_key_uq": { "name": "tool_applications_company_key_uq", @@ -52617,50 +53092,50 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "plugins", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -52955,9 +53430,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_call_events_run_idx": { "name": "tool_call_events_run_idx", @@ -52976,9 +53451,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_call_events_issue_idx": { "name": "tool_call_events_issue_idx", @@ -52997,9 +53472,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_call_events_invocation_idx": { "name": "tool_call_events_invocation_idx", @@ -53012,9 +53487,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_call_events_gateway_idx": { "name": "tool_call_events_gateway_idx", @@ -53033,167 +53508,167 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_mcp_gateways", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_mcp_gateway_tokens", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_applications", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_connections", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_catalog_entries", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_invocations", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_action_requests", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_runtime_slots", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -53406,9 +53881,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_catalog_entries_application_idx": { "name": "tool_catalog_entries_application_idx", @@ -53421,9 +53896,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_catalog_entries_connection_idx": { "name": "tool_catalog_entries_connection_idx", @@ -53436,9 +53911,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_catalog_entries_company_status_idx": { "name": "tool_catalog_entries_company_status_idx", @@ -53457,9 +53932,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_catalog_entries_connection_name_uq": { "name": "tool_catalog_entries_connection_name_uq", @@ -53478,63 +53953,63 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "tool_applications", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "tool_connections", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -53641,39 +54116,39 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "tool_connection_app_snapshots_company_id_companies_id_fk": { "name": "tool_connection_app_snapshots_company_id_companies_id_fk", "tableFrom": "tool_connection_app_snapshots", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "tool_connection_app_snapshots_company_connection_fk": { "name": "tool_connection_app_snapshots_company_connection_fk", "tableFrom": "tool_connection_app_snapshots", + "tableTo": "tool_connections", "columnsFrom": [ "company_id", "connection_id" ], - "tableTo": "tool_connections", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -53794,39 +54269,39 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "tool_connection_app_syncs_company_id_companies_id_fk": { "name": "tool_connection_app_syncs_company_id_companies_id_fk", "tableFrom": "tool_connection_app_syncs", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "tool_connection_app_syncs_company_connection_fk": { "name": "tool_connection_app_syncs_company_connection_fk", "tableFrom": "tool_connection_app_syncs", + "tableTo": "tool_connections", "columnsFrom": [ "company_id", "connection_id" ], - "tableTo": "tool_connections", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -53914,9 +54389,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_connection_installs_connection_idx": { "name": "tool_connection_installs_connection_idx", @@ -53935,9 +54410,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_connection_installs_target_uq": { "name": "tool_connection_installs_target_uq", @@ -53968,50 +54443,50 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "tool_connections", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -54238,9 +54713,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_connections_application_idx": { "name": "tool_connections_application_idx", @@ -54253,9 +54728,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_connections_company_enabled_idx": { "name": "tool_connections_company_enabled_idx", @@ -54274,9 +54749,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_connections_company_uid_uq": { "name": "tool_connections_company_uid_uq", @@ -54295,61 +54770,61 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "tool_applications", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -54470,9 +54945,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_gateway_rate_limit_counters_window_uq": { "name": "tool_gateway_rate_limit_counters_window_uq", @@ -54497,24 +54972,24 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -54663,9 +55138,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_gateway_sessions_company_agent_idx": { "name": "tool_gateway_sessions_company_agent_idx", @@ -54684,9 +55159,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_gateway_sessions_company_expires_idx": { "name": "tool_gateway_sessions_company_expires_idx", @@ -54705,9 +55180,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_gateway_sessions_run_idx": { "name": "tool_gateway_sessions_run_idx", @@ -54726,9 +55201,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_gateway_sessions_issue_idx": { "name": "tool_gateway_sessions_issue_idx", @@ -54747,9 +55222,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_gateway_sessions_gateway_idx": { "name": "tool_gateway_sessions_gateway_idx", @@ -54768,102 +55243,102 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "projects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_mcp_gateways", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_mcp_gateway_tokens", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -55178,9 +55653,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_invocations_run_idx": { "name": "tool_invocations_run_idx", @@ -55199,9 +55674,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_invocations_issue_idx": { "name": "tool_invocations_issue_idx", @@ -55220,9 +55695,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_invocations_gateway_idx": { "name": "tool_invocations_gateway_idx", @@ -55241,9 +55716,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_invocations_company_idempotency_uq": { "name": "tool_invocations_company_idempotency_uq", @@ -55262,128 +55737,128 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_mcp_gateways", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_mcp_gateway_tokens", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_applications", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_connections", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_catalog_entries", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -55549,9 +56024,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_mcp_gateway_tokens_expiry_idx": { "name": "tool_mcp_gateway_tokens_expiry_idx", @@ -55570,9 +56045,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_mcp_gateway_tokens_gateway_idx": { "name": "tool_mcp_gateway_tokens_gateway_idx", @@ -55591,9 +56066,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_mcp_gateway_tokens_subject_idx": { "name": "tool_mcp_gateway_tokens_subject_idx", @@ -55618,9 +56093,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_mcp_gateway_tokens_company_expires_idx": { "name": "tool_mcp_gateway_tokens_company_expires_idx", @@ -55639,63 +56114,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "tool_mcp_gateways", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -55890,9 +56365,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_mcp_gateways_company_status_idx": { "name": "tool_mcp_gateways_company_status_idx", @@ -55911,9 +56386,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_mcp_gateways_profile_idx": { "name": "tool_mcp_gateways_profile_idx", @@ -55932,9 +56407,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_mcp_gateways_public_id_uq": { "name": "tool_mcp_gateways_public_id_uq", @@ -55947,9 +56422,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_mcp_gateways_company_slug_uq": { "name": "tool_mcp_gateways_company_slug_uq", @@ -55968,9 +56443,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_mcp_gateways_company_name_uq": { "name": "tool_mcp_gateways_company_name_uq", @@ -55989,102 +56464,102 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "tool_profiles", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "restrict" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "projects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -56201,9 +56676,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_oauth_states_connection_idx": { "name": "tool_oauth_states_connection_idx", @@ -56216,9 +56691,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_oauth_states_actor_idx": { "name": "tool_oauth_states_actor_idx", @@ -56237,9 +56712,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_oauth_states_subject_agent_idx": { "name": "tool_oauth_states_subject_agent_idx", @@ -56258,9 +56733,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_oauth_states_expires_at_idx": { "name": "tool_oauth_states_expires_at_idx", @@ -56273,50 +56748,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "tool_connections", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -56438,9 +56913,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_policies_company_type_idx": { "name": "tool_policies_company_type_idx", @@ -56459,9 +56934,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_policies_company_name_uq": { "name": "tool_policies_company_name_uq", @@ -56480,37 +56955,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -56619,9 +57094,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_profile_bindings_target_profile_uq": { "name": "tool_profile_bindings_target_profile_uq", @@ -56652,50 +57127,50 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "tool_profiles", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -56809,9 +57284,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_profile_entries_application_idx": { "name": "tool_profile_entries_application_idx", @@ -56830,9 +57305,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_profile_entries_connection_idx": { "name": "tool_profile_entries_connection_idx", @@ -56851,9 +57326,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_profile_entries_catalog_entry_idx": { "name": "tool_profile_entries_catalog_entry_idx", @@ -56872,76 +57347,76 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "tool_profiles", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "tool_applications", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "tool_connections", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "tool_catalog_entries", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -57045,9 +57520,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_profiles_company_key_uq": { "name": "tool_profiles_company_key_uq", @@ -57066,9 +57541,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_profiles_company_name_uq": { "name": "tool_profiles_company_name_uq", @@ -57087,24 +57562,24 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -57211,9 +57686,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_rate_limit_counters_window_uq": { "name": "tool_rate_limit_counters_window_uq", @@ -57250,37 +57725,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "tool_policies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -57364,9 +57839,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_runtime_metric_counters_bucket_uq": { "name": "tool_runtime_metric_counters_bucket_uq", @@ -57391,24 +57866,24 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -57634,9 +58109,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_runtime_slots_connection_idx": { "name": "tool_runtime_slots_connection_idx", @@ -57649,9 +58124,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_runtime_slots_execution_workspace_idx": { "name": "tool_runtime_slots_execution_workspace_idx", @@ -57670,9 +58145,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_runtime_slots_slot_key_uq": { "name": "tool_runtime_slots_slot_key_uq", @@ -57691,89 +58166,89 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "tool_applications", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "tool_connections", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "project_workspaces", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "execution_workspaces", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -57896,9 +58371,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_stdio_command_templates_company_status_idx": { "name": "tool_stdio_command_templates_company_status_idx", @@ -57917,9 +58392,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "tool_stdio_command_templates_company_key_uq": { "name": "tool_stdio_command_templates_company_key_uq", @@ -57938,37 +58413,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -58046,9 +58521,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "user_company_preferences_primary_agent_idx": { "name": "user_company_preferences_primary_agent_idx", @@ -58061,37 +58536,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "user_company_preferences_company_id_companies_id_fk": { "name": "user_company_preferences_company_id_companies_id_fk", "tableFrom": "user_company_preferences", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "user_company_preferences_primary_agent_id_agents_id_fk": { "name": "user_company_preferences_primary_agent_id_agents_id_fk", "tableFrom": "user_company_preferences", + "tableTo": "agents", "columnsFrom": [ "primary_agent_id" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -58170,9 +58645,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "user_inbox_agent_policies_allowed_agent_ids_idx": { "name": "user_inbox_agent_policies_allowed_agent_ids_idx", @@ -58185,24 +58660,24 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "gin", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -58317,9 +58792,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "user_secret_declarations_definition_idx": { "name": "user_secret_declarations_definition_idx", @@ -58332,9 +58807,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "user_secret_declarations_target_idx": { "name": "user_secret_declarations_target_idx", @@ -58359,9 +58834,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "user_secret_declarations_company_required_idx": { "name": "user_secret_declarations_company_required_idx", @@ -58380,9 +58855,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "user_secret_declarations_target_path_uq": { "name": "user_secret_declarations_target_path_uq", @@ -58413,9 +58888,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "user_secret_declarations_required_override_idx": { "name": "user_secret_declarations_required_override_idx", @@ -58434,38 +58909,38 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"user_secret_declarations\".\"allow_missing_override\" = true", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "user_secret_definitions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -58611,9 +59086,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "user_secret_definitions_company_provider_idx": { "name": "user_secret_definitions_company_provider_idx", @@ -58632,9 +59107,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "user_secret_definitions_provider_config_idx": { "name": "user_secret_definitions_provider_config_idx", @@ -58647,9 +59122,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "user_secret_definitions_company_key_uq": { "name": "user_secret_definitions_company_key_uq", @@ -58668,64 +59143,64 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"user_secret_definitions\".\"deleted_at\" is null", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "company_secret_provider_configs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -58785,9 +59260,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": {}, @@ -58942,137 +59417,137 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "issues", "columnsTo": [ "company_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "company_id", "native_issue_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "completion_contracts", "columnsTo": [ "company_id", "issue_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "native_run_results", "columnsTo": [ "company_id", "issue_id", "run_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "work_assessments", "columnsTo": [ "company_id", "issue_id", "run_id", "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" - ], - "nullsNotDistinct": false + ] } }, "policies": {}, @@ -59084,6 +59559,113 @@ }, "isRLSEnabled": false }, + "public.workflows": { + "name": "workflows", + "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 + }, + "prompt": { + "name": "prompt", + "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": { + "workflows_company_idx": { + "name": "workflows_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflows_company_id_companies_id_fk": { + "name": "workflows_company_id_companies_id_fk", + "tableFrom": "workflows", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflows_created_by_agent_id_agents_id_fk": { + "name": "workflows_created_by_agent_id_agents_id_fk", + "tableFrom": "workflows", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.workspace_operations": { "name": "workspace_operations", "schema": "", @@ -59251,9 +59833,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "workspace_operations_company_workspace_started_idx": { "name": "workspace_operations_company_workspace_started_idx", @@ -59278,9 +59860,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "workspace_operations_company_workspace_issue_started_idx": { "name": "workspace_operations_company_workspace_issue_started_idx", @@ -59311,63 +59893,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "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" ], - "tableTo": "execution_workspaces", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -59590,9 +60172,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "workspace_runtime_services_company_execution_workspace_status_idx": { "name": "workspace_runtime_services_company_execution_workspace_status_idx", @@ -59617,9 +60199,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "workspace_runtime_services_company_project_status_idx": { "name": "workspace_runtime_services_company_project_status_idx", @@ -59644,9 +60226,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "workspace_runtime_services_run_idx": { "name": "workspace_runtime_services_run_idx", @@ -59659,9 +60241,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "workspace_runtime_services_company_updated_idx": { "name": "workspace_runtime_services_company_updated_idx", @@ -59680,102 +60262,102 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "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" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "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" ], - "tableTo": "projects", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "project_workspaces", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "execution_workspaces", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "issues", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "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" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -59923,9 +60505,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "mcp_attachment_upload_expiry_idx": { "name": "mcp_attachment_upload_expiry_idx", @@ -59938,50 +60520,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "mcp_attachment_uploads_grant_id_mcp_oauth_grants_id_fk": { "name": "mcp_attachment_uploads_grant_id_mcp_oauth_grants_id_fk", "tableFrom": "mcp_attachment_uploads", + "tableTo": "mcp_oauth_grants", "columnsFrom": [ "grant_id" ], - "tableTo": "mcp_oauth_grants", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "mcp_attachment_uploads_company_id_companies_id_fk": { "name": "mcp_attachment_uploads_company_id_companies_id_fk", "tableFrom": "mcp_attachment_uploads", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "mcp_attachment_uploads_user_id_user_id_fk": { "name": "mcp_attachment_uploads_user_id_user_id_fk", "tableFrom": "mcp_attachment_uploads", + "tableTo": "user", "columnsFrom": [ "user_id" ], - "tableTo": "user", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -60057,9 +60639,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "mcp_event_admissions_grant_idx": { "name": "mcp_event_admissions_grant_idx", @@ -60072,9 +60654,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "mcp_event_admissions_company_idx": { "name": "mcp_event_admissions_company_idx", @@ -60087,37 +60669,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "mcp_event_admissions_company_id_companies_id_fk": { "name": "mcp_event_admissions_company_id_companies_id_fk", "tableFrom": "mcp_event_admissions", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "mcp_event_admissions_grant_id_mcp_oauth_grants_id_fk": { "name": "mcp_event_admissions_grant_id_mcp_oauth_grants_id_fk", "tableFrom": "mcp_event_admissions", + "tableTo": "mcp_oauth_grants", "columnsFrom": [ "grant_id" ], - "tableTo": "mcp_oauth_grants", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -60206,9 +60788,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "mcp_event_deliveries_activity_uq": { "name": "mcp_event_deliveries_activity_uq", @@ -60227,9 +60809,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "mcp_event_deliveries_due_idx": { "name": "mcp_event_deliveries_due_idx", @@ -60242,24 +60824,24 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "mcp_event_deliveries_subscription_id_mcp_event_subscriptions_id_fk": { "name": "mcp_event_deliveries_subscription_id_mcp_event_subscriptions_id_fk", "tableFrom": "mcp_event_deliveries", + "tableTo": "mcp_event_subscriptions", "columnsFrom": [ "subscription_id" ], - "tableTo": "mcp_event_subscriptions", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -60370,9 +60952,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "mcp_event_subscriptions_company_idx": { "name": "mcp_event_subscriptions_company_idx", @@ -60385,37 +60967,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "mcp_event_subscriptions_company_id_companies_id_fk": { "name": "mcp_event_subscriptions_company_id_companies_id_fk", "tableFrom": "mcp_event_subscriptions", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "mcp_event_subscriptions_grant_id_mcp_oauth_grants_id_fk": { "name": "mcp_event_subscriptions_grant_id_mcp_oauth_grants_id_fk", "tableFrom": "mcp_event_subscriptions", + "tableTo": "mcp_oauth_grants", "columnsFrom": [ "grant_id" ], - "tableTo": "mcp_oauth_grants", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -60495,9 +61077,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "mcp_file_ticket_grant_idx": { "name": "mcp_file_ticket_grant_idx", @@ -60510,50 +61092,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "mcp_file_tickets_company_id_companies_id_fk": { "name": "mcp_file_tickets_company_id_companies_id_fk", "tableFrom": "mcp_file_tickets", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "mcp_file_tickets_grant_id_mcp_oauth_grants_id_fk": { "name": "mcp_file_tickets_grant_id_mcp_oauth_grants_id_fk", "tableFrom": "mcp_file_tickets", + "tableTo": "mcp_oauth_grants", "columnsFrom": [ "grant_id" ], - "tableTo": "mcp_oauth_grants", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "mcp_file_tickets_upload_id_mcp_attachment_uploads_id_fk": { "name": "mcp_file_tickets_upload_id_mcp_attachment_uploads_id_fk", "tableFrom": "mcp_file_tickets", + "tableTo": "mcp_attachment_uploads", "columnsFrom": [ "upload_id" ], - "tableTo": "mcp_attachment_uploads", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -60660,9 +61242,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "mcp_mutation_receipts_company_idx": { "name": "mcp_mutation_receipts_company_idx", @@ -60675,50 +61257,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "mcp_mutation_receipts_company_id_companies_id_fk": { "name": "mcp_mutation_receipts_company_id_companies_id_fk", "tableFrom": "mcp_mutation_receipts", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "mcp_mutation_receipts_user_id_user_id_fk": { "name": "mcp_mutation_receipts_user_id_user_id_fk", "tableFrom": "mcp_mutation_receipts", + "tableTo": "user", "columnsFrom": [ "user_id" ], - "tableTo": "user", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "mcp_mutation_receipts_grant_id_mcp_oauth_grants_id_fk": { "name": "mcp_mutation_receipts_grant_id_mcp_oauth_grants_id_fk", "tableFrom": "mcp_mutation_receipts", + "tableTo": "mcp_oauth_grants", "columnsFrom": [ "grant_id" ], - "tableTo": "mcp_oauth_grants", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -60884,9 +61466,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "mcp_oauth_user_code_uq": { "name": "mcp_oauth_user_code_uq", @@ -60899,9 +61481,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "mcp_oauth_device_expiry_idx": { "name": "mcp_oauth_device_expiry_idx", @@ -60914,37 +61496,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "mcp_oauth_device_requests_client_id_mcp_oauth_clients_id_fk": { "name": "mcp_oauth_device_requests_client_id_mcp_oauth_clients_id_fk", "tableFrom": "mcp_oauth_device_requests", + "tableTo": "mcp_oauth_clients", "columnsFrom": [ "client_id" ], - "tableTo": "mcp_oauth_clients", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "mcp_oauth_device_requests_grant_id_mcp_oauth_grants_id_fk": { "name": "mcp_oauth_device_requests_grant_id_mcp_oauth_grants_id_fk", "tableFrom": "mcp_oauth_device_requests", + "tableTo": "mcp_oauth_grants", "columnsFrom": [ "grant_id" ], - "tableTo": "mcp_oauth_grants", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -61039,63 +61621,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "mcp_oauth_grants_company_id_companies_id_fk": { "name": "mcp_oauth_grants_company_id_companies_id_fk", "tableFrom": "mcp_oauth_grants", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "mcp_oauth_grants_user_id_user_id_fk": { "name": "mcp_oauth_grants_user_id_user_id_fk", "tableFrom": "mcp_oauth_grants", + "tableTo": "user", "columnsFrom": [ "user_id" ], - "tableTo": "user", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "mcp_oauth_grants_client_id_mcp_oauth_clients_id_fk": { "name": "mcp_oauth_grants_client_id_mcp_oauth_clients_id_fk", "tableFrom": "mcp_oauth_grants", + "tableTo": "mcp_oauth_clients", "columnsFrom": [ "client_id" ], - "tableTo": "mcp_oauth_clients", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "mcp_oauth_grants_agent_id_agents_id_fk": { "name": "mcp_oauth_grants_agent_id_agents_id_fk", "tableFrom": "mcp_oauth_grants", + "tableTo": "agents", "columnsFrom": [ "agent_id" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -61140,9 +61722,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": {}, @@ -61254,9 +61836,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "mcp_oauth_requests_expiry_idx": { "name": "mcp_oauth_requests_expiry_idx", @@ -61269,37 +61851,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "mcp_oauth_requests_client_id_mcp_oauth_clients_id_fk": { "name": "mcp_oauth_requests_client_id_mcp_oauth_clients_id_fk", "tableFrom": "mcp_oauth_requests", + "tableTo": "mcp_oauth_clients", "columnsFrom": [ "client_id" ], - "tableTo": "mcp_oauth_clients", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "mcp_oauth_requests_grant_id_mcp_oauth_grants_id_fk": { "name": "mcp_oauth_requests_grant_id_mcp_oauth_grants_id_fk", "tableFrom": "mcp_oauth_requests", + "tableTo": "mcp_oauth_grants", "columnsFrom": [ "grant_id" ], - "tableTo": "mcp_oauth_grants", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -61369,9 +61951,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "mcp_oauth_tokens_grant_idx": { "name": "mcp_oauth_tokens_grant_idx", @@ -61384,9 +61966,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "mcp_oauth_tokens_expiry_idx": { "name": "mcp_oauth_tokens_expiry_idx", @@ -61399,24 +61981,24 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "mcp_oauth_tokens_grant_id_mcp_oauth_grants_id_fk": { "name": "mcp_oauth_tokens_grant_id_mcp_oauth_grants_id_fk", "tableFrom": "mcp_oauth_tokens", + "tableTo": "mcp_oauth_grants", "columnsFrom": [ "grant_id" ], - "tableTo": "mcp_oauth_grants", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -61549,9 +62131,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "external_holds_agent_idx": { "name": "external_holds_agent_idx", @@ -61570,37 +62152,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "external_agent_holds_company_id_companies_id_fk": { "name": "external_agent_holds_company_id_companies_id_fk", "tableFrom": "external_agent_holds", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "external_agent_holds_agent_id_agents_id_fk": { "name": "external_agent_holds_agent_id_agents_id_fk", "tableFrom": "external_agent_holds", + "tableTo": "agents", "columnsFrom": [ "agent_id" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -61813,10 +62395,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"muse_agent_bindings\".\"revoked_at\" IS NULL", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "muse_bindings_ticket_uq": { "name": "muse_bindings_ticket_uq", @@ -61829,9 +62411,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "muse_bindings_active_grant_uq": { "name": "muse_bindings_active_grant_uq", @@ -61844,51 +62426,51 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"muse_agent_bindings\".\"revoked_at\" IS NULL AND \"muse_agent_bindings\".\"grant_id\" IS NOT NULL", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} } }, "foreignKeys": { "muse_agent_bindings_company_id_companies_id_fk": { "name": "muse_agent_bindings_company_id_companies_id_fk", "tableFrom": "muse_agent_bindings", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "muse_agent_bindings_agent_id_agents_id_fk": { "name": "muse_agent_bindings_agent_id_agents_id_fk", "tableFrom": "muse_agent_bindings", + "tableTo": "agents", "columnsFrom": [ "agent_id" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "muse_agent_bindings_grant_id_mcp_oauth_grants_id_fk": { "name": "muse_agent_bindings_grant_id_mcp_oauth_grants_id_fk", "tableFrom": "muse_agent_bindings", + "tableTo": "mcp_oauth_grants", "columnsFrom": [ "grant_id" ], - "tableTo": "mcp_oauth_grants", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "set null" + "onDelete": "set null", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -61982,9 +62564,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "muse_credentials_binding_idx": { "name": "muse_credentials_binding_idx", @@ -62003,37 +62585,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "muse_credentials_company_id_companies_id_fk": { "name": "muse_credentials_company_id_companies_id_fk", "tableFrom": "muse_credentials", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "muse_credentials_binding_id_muse_agent_bindings_id_fk": { "name": "muse_credentials_binding_id_muse_agent_bindings_id_fk", "tableFrom": "muse_credentials", + "tableTo": "muse_agent_bindings", "columnsFrom": [ "binding_id" ], - "tableTo": "muse_agent_bindings", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -62121,37 +62703,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "muse_idle_receipts_company_id_companies_id_fk": { "name": "muse_idle_receipts_company_id_companies_id_fk", "tableFrom": "muse_idle_receipts", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "muse_idle_receipts_binding_id_muse_agent_bindings_id_fk": { "name": "muse_idle_receipts_binding_id_muse_agent_bindings_id_fk", "tableFrom": "muse_idle_receipts", + "tableTo": "muse_agent_bindings", "columnsFrom": [ "binding_id" ], - "tableTo": "muse_agent_bindings", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -62251,9 +62833,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "muse_input_source_uq": { "name": "muse_input_source_uq", @@ -62272,37 +62854,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "muse_input_deliveries_company_id_companies_id_fk": { "name": "muse_input_deliveries_company_id_companies_id_fk", "tableFrom": "muse_input_deliveries", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "muse_input_deliveries_assignment_id_muse_runner_assignments_id_fk": { "name": "muse_input_deliveries_assignment_id_muse_runner_assignments_id_fk", "tableFrom": "muse_input_deliveries", + "tableTo": "muse_runner_assignments", "columnsFrom": [ "assignment_id" ], - "tableTo": "muse_runner_assignments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -62408,9 +62990,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "muse_mailbox_cursor_idx": { "name": "muse_mailbox_cursor_idx", @@ -62435,50 +63017,50 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "muse_mailbox_items_company_id_companies_id_fk": { "name": "muse_mailbox_items_company_id_companies_id_fk", "tableFrom": "muse_mailbox_items", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "muse_mailbox_items_binding_id_muse_agent_bindings_id_fk": { "name": "muse_mailbox_items_binding_id_muse_agent_bindings_id_fk", "tableFrom": "muse_mailbox_items", + "tableTo": "muse_agent_bindings", "columnsFrom": [ "binding_id" ], - "tableTo": "muse_agent_bindings", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "muse_mailbox_items_assignment_id_muse_runner_assignments_id_fk": { "name": "muse_mailbox_items_assignment_id_muse_runner_assignments_id_fk", "tableFrom": "muse_mailbox_items", + "tableTo": "muse_runner_assignments", "columnsFrom": [ "assignment_id" ], - "tableTo": "muse_runner_assignments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -62610,37 +63192,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "muse_receiver_contact_buckets_company_id_companies_id_fk": { "name": "muse_receiver_contact_buckets_company_id_companies_id_fk", "tableFrom": "muse_receiver_contact_buckets", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "muse_receiver_contact_buckets_binding_id_muse_agent_bindings_id_fk": { "name": "muse_receiver_contact_buckets_binding_id_muse_agent_bindings_id_fk", "tableFrom": "muse_receiver_contact_buckets", + "tableTo": "muse_agent_bindings", "columnsFrom": [ "binding_id" ], - "tableTo": "muse_agent_bindings", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -62809,9 +63391,9 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "muse_assignments_live_binding_uq": { "name": "muse_assignments_live_binding_uq", @@ -62824,10 +63406,10 @@ } ], "isUnique": true, - "with": {}, - "method": "btree", "where": "\"muse_runner_assignments\".\"status\" IN ('offered','claimed','accepted')", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "muse_assignments_company_idx": { "name": "muse_assignments_company_idx", @@ -62840,63 +63422,63 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "muse_runner_assignments_company_id_companies_id_fk": { "name": "muse_runner_assignments_company_id_companies_id_fk", "tableFrom": "muse_runner_assignments", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "muse_runner_assignments_binding_id_muse_agent_bindings_id_fk": { "name": "muse_runner_assignments_binding_id_muse_agent_bindings_id_fk", "tableFrom": "muse_runner_assignments", + "tableTo": "muse_agent_bindings", "columnsFrom": [ "binding_id" ], - "tableTo": "muse_agent_bindings", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "muse_runner_assignments_run_id_heartbeat_runs_id_fk": { "name": "muse_runner_assignments_run_id_heartbeat_runs_id_fk", "tableFrom": "muse_runner_assignments", + "tableTo": "heartbeat_runs", "columnsFrom": [ "run_id" ], - "tableTo": "heartbeat_runs", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "muse_runner_assignments_agent_id_agents_id_fk": { "name": "muse_runner_assignments_agent_id_agents_id_fk", "tableFrom": "muse_runner_assignments", + "tableTo": "agents", "columnsFrom": [ "agent_id" ], - "tableTo": "agents", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -63004,37 +63586,37 @@ } ], "isUnique": true, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "muse_runner_operations_company_id_companies_id_fk": { "name": "muse_runner_operations_company_id_companies_id_fk", "tableFrom": "muse_runner_operations", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "muse_runner_operations_assignment_id_muse_runner_assignments_id_fk": { "name": "muse_runner_operations_assignment_id_muse_runner_assignments_id_fk", "tableFrom": "muse_runner_operations", + "tableTo": "muse_runner_assignments", "columnsFrom": [ "assignment_id" ], - "tableTo": "muse_runner_assignments", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -63092,15 +63674,15 @@ "company_prompt_injection_prevention_company_id_companies_id_fk": { "name": "company_prompt_injection_prevention_company_id_companies_id_fk", "tableFrom": "company_prompt_injection_prevention", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -63314,9 +63896,9 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} }, "prompt_injection_invocations_pending_idx": { "name": "prompt_injection_invocations_pending_idx", @@ -63329,10 +63911,10 @@ } ], "isUnique": false, - "with": {}, - "method": "btree", "where": "\"prompt_injection_invocations\".\"status\" = 'running'", - "concurrently": false + "concurrently": false, + "method": "btree", + "with": {} }, "prompt_injection_invocations_cost_idx": { "name": "prompt_injection_invocations_cost_idx", @@ -63345,37 +63927,37 @@ } ], "isUnique": false, - "with": {}, + "concurrently": false, "method": "btree", - "concurrently": false + "with": {} } }, "foreignKeys": { "prompt_injection_invocations_company_id_companies_id_fk": { "name": "prompt_injection_invocations_company_id_companies_id_fk", "tableFrom": "prompt_injection_invocations", + "tableTo": "companies", "columnsFrom": [ "company_id" ], - "tableTo": "companies", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "cascade" + "onDelete": "cascade", + "onUpdate": "no action" }, "prompt_injection_invocations_cost_event_id_cost_events_id_fk": { "name": "prompt_injection_invocations_cost_event_id_cost_events_id_fk", "tableFrom": "prompt_injection_invocations", + "tableTo": "cost_events", "columnsFrom": [ "cost_event_id" ], - "tableTo": "cost_events", "columnsTo": [ "id" ], - "onUpdate": "no action", - "onDelete": "no action" + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -63396,21 +63978,21 @@ }, "enums": {}, "schemas": {}, - "views": {}, "sequences": { "public.chat_telegram_draft_ids": { "name": "chat_telegram_draft_ids", + "schema": "public", "increment": "1", + "startWith": "1", "minValue": "1", "maxValue": "2147483647", - "startWith": "1", "cache": "1", - "cycle": false, - "schema": "public" + "cycle": false } }, "roles": {}, "policies": {}, + "views": {}, "_meta": { "columns": {}, "schemas": {}, diff --git a/packages/db/src/migrations/meta/_journal.json b/packages/db/src/migrations/meta/_journal.json index da888b3c81d..ccd97aeb7f2 100644 --- a/packages/db/src/migrations/meta/_journal.json +++ b/packages/db/src/migrations/meta/_journal.json @@ -2595,8 +2595,15 @@ { "idx": 372, "version": "7", - "when": 1792679793575, - "tag": "0372_mighty_swordsman", + "when": 1792707356696, + "tag": "0372_mature_the_initiative", + "breakpoints": true + }, + { + "idx": 373, + "version": "7", + "when": 1792710084082, + "tag": "0373_wandering_mongu", "breakpoints": true } ] diff --git a/packages/db/src/schema/execution_workspace_restore_points.ts b/packages/db/src/schema/execution_workspace_restore_points.ts new file mode 100644 index 00000000000..80f3f55483a --- /dev/null +++ b/packages/db/src/schema/execution_workspace_restore_points.ts @@ -0,0 +1,46 @@ +import { sql } from "drizzle-orm"; +import { bigint, index, integer, pgTable, text, timestamp, uniqueIndex, uuid } from "drizzle-orm/pg-core"; + +export const EXECUTION_WORKSPACE_RESTORE_POINT_STATUSES = [ + "pending", + "active", + "expiring", + "superseded", + "delete_requested", + "deleted", +] as const; +export type ExecutionWorkspaceRestorePointStatus = (typeof EXECUTION_WORKSPACE_RESTORE_POINT_STATUSES)[number]; + +export const executionWorkspaceRestorePoints = pgTable( + "execution_workspace_restore_points", + { + id: uuid("id").primaryKey().defaultRandom(), + companyId: uuid("company_id").notNull(), + executionWorkspaceId: uuid("execution_workspace_id").notNull(), + sourceIssueId: uuid("source_issue_id"), + generation: integer("generation").notNull(), + objectKey: text("object_key").notNull(), + sha256: text("sha256").notNull(), + byteSize: bigint("byte_size", { mode: "number" }).notNull(), + status: text("status").$type().notNull().default("pending"), + activatedAt: timestamp("activated_at", { withTimezone: true }), + treeClosedAt: timestamp("tree_closed_at", { withTimezone: true }), + deleteAfter: timestamp("delete_after", { withTimezone: true }), + reviewGeneration: integer("review_generation").notNull().default(0), + deletedAt: timestamp("deleted_at", { withTimezone: true }), + lastError: text("last_error"), + createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), + updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), + }, + (table) => ({ + objectKeyUniqueIdx: uniqueIndex("execution_workspace_restore_points_object_key_uniq").on(table.objectKey), + liveGenerationUniqueIdx: uniqueIndex("execution_workspace_restore_points_live_generation_uniq") + .on(table.executionWorkspaceId, table.generation) + .where(sql`${table.status} <> 'deleted'`), + statusDeleteAfterIdx: index("execution_workspace_restore_points_status_delete_after_idx").on(table.status, table.deleteAfter), + companyIdx: index("execution_workspace_restore_points_company_idx").on(table.companyId), + sourceIssueIdx: index("execution_workspace_restore_points_source_issue_idx").on(table.companyId, table.sourceIssueId), + activeSourceIdx: index("execution_workspace_restore_points_active_source_idx").on(table.sourceIssueId) + .where(sql`${table.status} = 'active'`), + }), +); diff --git a/packages/db/src/schema/index.ts b/packages/db/src/schema/index.ts index 47615fcaec0..5be5f2d8c88 100644 --- a/packages/db/src/schema/index.ts +++ b/packages/db/src/schema/index.ts @@ -32,6 +32,11 @@ export { projectAccessMembers } from "./project_access_members.js"; export { documentMemberships } from "./document_memberships.js"; export { projectWorkspaces } from "./project_workspaces.js"; export { executionWorkspaces } from "./execution_workspaces.js"; +export { + executionWorkspaceRestorePoints, + EXECUTION_WORKSPACE_RESTORE_POINT_STATUSES, + type ExecutionWorkspaceRestorePointStatus, +} from "./execution_workspace_restore_points.js"; export { executionWorkspaceRuntimeLeases } from "./execution_workspace_runtime_leases.js"; export { environments } from "./environments.js"; export { environmentLeases } from "./environment_leases.js"; diff --git a/packages/shared/src/app-definitions.generated.ts b/packages/shared/src/app-definitions.generated.ts index 77f51bb2b8b..8d4e0208511 100644 --- a/packages/shared/src/app-definitions.generated.ts +++ b/packages/shared/src/app-definitions.generated.ts @@ -105,6 +105,7 @@ import a103 from "./app-definitions/local.json" with { type: "json" }; import a104 from "./app-definitions/daytona.json" with { type: "json" }; import a105 from "./app-definitions/monday.json" with { type: "json" }; import a106 from "./app-definitions/paperclip-notebook.json" with { type: "json" }; -import a107 from "./app-definitions/speko.json" with { type: "json" }; +import a107 from "./app-definitions/unikraft.json" with { type: "json" }; +import a108 from "./app-definitions/speko.json" with { type: "json" }; import type { AppDefinition } from "./types/app-definition.js"; -export const APP_DEFINITIONS=[a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31,a32,a33,a34,a35,a36,a37,a38,a39,a40,a41,a42,a43,a44,a45,a46,a47,a48,a49,a50,a51,a52,a53,a54,a55,a56,a57,a58,a59,a60,a61,a62,a63,a64,a65,a66,a67,a68,a69,a70,a71,a72,a73,a74,a75,a76,a77,a78,a79,a80,a81,a82,a83,a84,a85,a86,a87,a88,a89,a90,a91,a92,a93,a94,a95,a96,a97,a98,a99,a100,a101,a102,a103,a104,a105,a106,a107] as AppDefinition[]; +export const APP_DEFINITIONS=[a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31,a32,a33,a34,a35,a36,a37,a38,a39,a40,a41,a42,a43,a44,a45,a46,a47,a48,a49,a50,a51,a52,a53,a54,a55,a56,a57,a58,a59,a60,a61,a62,a63,a64,a65,a66,a67,a68,a69,a70,a71,a72,a73,a74,a75,a76,a77,a78,a79,a80,a81,a82,a83,a84,a85,a86,a87,a88,a89,a90,a91,a92,a93,a94,a95,a96,a97,a98,a99,a100,a101,a102,a103,a104,a105,a106,a107,a108] as AppDefinition[]; diff --git a/packages/shared/src/app-definitions.test.ts b/packages/shared/src/app-definitions.test.ts index 3b1febbbde9..5855c9d2c92 100644 --- a/packages/shared/src/app-definitions.test.ts +++ b/packages/shared/src/app-definitions.test.ts @@ -886,7 +886,7 @@ describe("AppDefinition catalog", () => { "ticktick", "xero", ]); - expect(APP_STORE_DEFINITIONS).toHaveLength(85); + expect(APP_STORE_DEFINITIONS).toHaveLength(86); const connectableSlugs = new Set( CONNECTABLE_APP_DEFINITIONS.map((entry) => entry.slug), ); @@ -1285,6 +1285,38 @@ describe("AppDefinition catalog", () => { expect(method.warnings).toHaveLength(2); } }); + it("connects Unikraft Cloud's published HTTP APIs with an organization API token", () => { + const unikraft = APP_DEFINITIONS.find((app) => app.slug === "unikraft")!; + expect(unikraft).toMatchObject({ + name: "Unikraft", + categories: ["developer"], + urlPatterns: ["https://console.unikraft.com/*", "https://*.unikraft.cloud/*"], + docsUrl: "https://unikraft.com/docs/api/platform/v1", + branding: { logoUrl: "/brands/apps/unikraft.svg" }, + }); + expect(APP_STORE_DEFINITIONS.some((app) => app.slug === "unikraft")).toBe(true); + expect(CONNECTABLE_APP_DEFINITIONS.some((app) => app.slug === "unikraft")).toBe(true); + expect(unikraft.methods.map((method) => method.key)).toEqual(["api-token"]); + const [method] = unikraft.methods; + expect(method).toMatchObject({ + transport: "rest_api", + auth: "api_key", + ownershipModes: ["customer"], + riskTier: "S4", + defaults: { serverUrl: "https://controlplane.unikraft.cloud/v1" }, + keyPlacement: { location: "header", name: "Authorization", prefix: "Bearer " }, + }); + expect(method!.credentialFields).toEqual([ + expect.objectContaining({ key: "authorization", type: "password", secret: true, required: true }), + ]); + // The key form renders credential helper text, not method warnings. + expect(method!.credentialFields?.[0]?.helperMd).toContain("running sandbox commands and deleting resources"); + // The default metro is optional: setup asks only for the token. + expect(method!.tenantFields).toEqual([ + expect.objectContaining({ key: "metro", type: "select", advanced: true, defaultValue: "fra" }), + ]); + expect(method!.tenantFields?.[0]?.options?.map((option) => option.value)).toEqual(["dal", "fra", "sfo", "sin", "was"]); + }); it("connects Gauge's hosted server with browser sign-in or an organization API key", () => { const gauge = APP_DEFINITIONS.find((app) => app.slug === "gauge")!; expect(gauge).toMatchObject({ diff --git a/packages/shared/src/app-definitions.ts b/packages/shared/src/app-definitions.ts index 4f325b202e3..278ec99f7eb 100644 --- a/packages/shared/src/app-definitions.ts +++ b/packages/shared/src/app-definitions.ts @@ -9,6 +9,7 @@ export const CONNECTABLE_APP_SLUGS = new Set([ "agentmail", "browser-use-cloud", "daytona", + "unikraft", "cognee", ...SELF_SERVE_MCP_CANDIDATES.map((entry) => entry.slug), "monday", diff --git a/packages/shared/src/app-definitions/unikraft.json b/packages/shared/src/app-definitions/unikraft.json new file mode 100644 index 00000000000..2a930d3e373 --- /dev/null +++ b/packages/shared/src/app-definitions/unikraft.json @@ -0,0 +1,95 @@ +{ + "schemaVersion": 1, + "slug": "unikraft", + "name": "Unikraft", + "description": "Manage Unikraft Cloud instances, services, volumes, images, certificates, autoscaling, and sandbox commands and files.", + "categories": [ + "developer" + ], + "featured": false, + "urlPatterns": [ + "https://console.unikraft.com/*", + "https://*.unikraft.cloud/*" + ], + "methods": [ + { + "key": "api-token", + "label": "Connect Unikraft", + "transport": "rest_api", + "auth": "api_key", + "ownershipModes": [ + "customer" + ], + "whenToUse": "Manage resources and run sandbox commands in your Unikraft Cloud organization.", + "defaults": { + "serverUrl": "https://controlplane.unikraft.cloud/v1" + }, + "guidanceMd": "Connect a Unikraft Cloud API token. All published platform API and sandbox plugin actions are available; your action policies control which ones agents can use. Each action can target any Unikraft Cloud metro, and uses the default metro when it does not name one.", + "riskTier": "S4", + "credentialFields": [ + { + "key": "authorization", + "label": "Unikraft API token", + "type": "password", + "required": true, + "placeholder": "Enter Unikraft API token", + "secret": true, + "helperMd": "Create an API token in the Unikraft Cloud console. A token acts for its whole organization in every metro, including running sandbox commands and deleting resources. Unikraft does not offer narrower token permissions, so choose trusted agents and use action permissions to limit what they can do." + } + ], + "keyPlacement": { + "location": "header", + "name": "Authorization", + "prefix": "Bearer " + }, + "tenantFields": [ + { + "key": "metro", + "label": "Default metro", + "type": "select", + "advanced": true, + "defaultValue": "fra", + "helperMd": "Used to verify the token and for actions that do not name a metro.", + "options": [ + { + "value": "dal", + "label": "Dallas (dal)" + }, + { + "value": "fra", + "label": "Frankfurt (fra)" + }, + { + "value": "sfo", + "label": "San Francisco (sfo)" + }, + { + "value": "sin", + "label": "Singapore (sin)" + }, + { + "value": "was", + "label": "Washington, DC (was)" + } + ] + } + ], + "requiredResourceFilters": [ + "organization", + "metro", + "instance" + ], + "consoleLinks": { + "keys": "https://console.unikraft.com", + "docs": "https://unikraft.com/docs/sdks/overview#authentication" + }, + "warnings": [ + "This connection can run commands in sandboxes, read and write sandbox files, start and stop instances, and delete instances, volumes, and other resources. Select trusted agents and set action permissions before use. Your Unikraft API token stays in the vault." + ] + } + ], + "docsUrl": "https://unikraft.com/docs/api/platform/v1", + "branding": { + "logoUrl": "/brands/apps/unikraft.svg" + } +} diff --git a/packages/shared/src/feature-catalog.ts b/packages/shared/src/feature-catalog.ts index d66bbd9cacc..48e282e7691 100644 --- a/packages/shared/src/feature-catalog.ts +++ b/packages/shared/src/feature-catalog.ts @@ -199,6 +199,14 @@ export const INSTANCE_FEATURE_CATALOG: Record value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a /** Retain only reachable input definitions, rather than copying the whole API * schema into every action. Provider response schemas are not tool inputs. */ -function inputSchema(value, schemas) { +export function inputSchema(value, schemas) { const definitions = {}; function visit(node) { if (Array.isArray(node)) return node.map(visit); diff --git a/scripts/generate-unikraft-tools.mjs b/scripts/generate-unikraft-tools.mjs new file mode 100644 index 00000000000..bf17a88e55e --- /dev/null +++ b/scripts/generate-unikraft-tools.mjs @@ -0,0 +1,135 @@ +import { createHash } from "node:crypto"; +import { readFile, writeFile, mkdir } from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; +import { inputSchema } from "./generate-daytona-tools.mjs"; + +// The official specification repository has no tags or releases. Pin a +// reviewed commit so a provider update needs explicit regeneration and review. +const commit = "5dd107ea1cd8716b83cb86a83ed2a6ee46f99b5b"; +const sources = { + platform: `https://raw.githubusercontent.com/unikraft-cloud/openapi/${commit}/platform.json`, + sandbox: `https://raw.githubusercontent.com/unikraft-cloud/openapi/${commit}/plugins/sandbox.json`, +}; +const verbs = new Set(["get", "post", "put", "patch", "delete"]); +const snake = value => value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2") + .replace(/[^a-zA-Z0-9]+/g, "_").toLowerCase(); +// Requests that wait, read, or list without changing provider state. +const readOnlyPosts = new Set(["ReadFile", "ReadRawFile", "WaitForCommand", "WaitForCommandWithTimeout"]); +// Reversible but disruptive lifecycle changes, and code execution in a sandbox. +const destructiveOperations = /^(Stop|Suspend|Detach|Unpin)|^(RunCommand|SignalCommand|WriteCommandStdin)$/; + +function parameterDefinition(parameter, spec) { + if (!parameter.$ref) return parameter; + const prefix = "#/components/parameters/"; + if (!parameter.$ref.startsWith(prefix)) throw new Error(`Unsupported parameter reference: ${parameter.$ref}`); + const resolved = spec.components?.parameters?.[parameter.$ref.slice(prefix.length)]; + if (!resolved) throw new Error(`Missing parameter: ${parameter.$ref}`); + return resolved; +} + +/** Ajv does not define OpenAPI's unsigned integer formats. Keep their sign + * constraint instead of an unknown format that would not validate anything. + * Some unsigned fields document -1 as a sentinel (for example, the longest + * drain time); keep that value valid. */ +function unsignedFormats(node) { + if (Array.isArray(node)) return node.forEach(unsignedFormats); + if (!node || typeof node !== "object") return; + if (/^uint(8|16|32|64)$/.test(node.format ?? "")) { + delete node.format; + node.minimum ??= /(^|\s)-1\b/.test(node.description ?? "") ? -1 : 0; + } + Object.values(node).forEach(unsignedFormats); +} + +function streamingOperation(operation) { + return Object.values(operation.responses ?? {}).some(response => Object.hasOwn(response.content ?? {}, "text/event-stream")); +} + +export function generateUnikraftCatalog(specs) { + const metros = (specs.platform?.servers ?? []).map(server => new URL(server.url).hostname.match(/^api\.([a-z0-9]+)\.unikraft\.cloud$/)?.[1]) + .filter(Boolean).sort(); + const tools = []; + const streaming = []; + for (const [kind, spec] of Object.entries(specs)) { + for (const [route, item] of Object.entries(spec.paths)) { + for (const [method, operation] of Object.entries(item)) { + if (!verbs.has(method)) continue; + if (!operation.operationId) throw new Error(`Missing operationId: ${kind} ${method} ${route}`); + const name = kind === "platform" ? `unikraft_${snake(operation.operationId)}` : `unikraft_${kind}_${snake(operation.operationId)}`; + // Server-sent event streams hold a connection open; they are not + // request/response actions. Keep them in the inventory so an update + // cannot silently lose an operation. + if (streamingOperation(operation)) { + streaming.push({ name, kind, path: route, method: method.toUpperCase(), transport: "server-sent-events" }); + continue; + } + const properties = {}, required = [], parameters = []; + const groups = { path: { type: "object", properties: {}, required: [], additionalProperties: false }, + query: { type: "object", properties: {}, required: [], additionalProperties: false }, + headers: { type: "object", properties: {}, required: [], additionalProperties: false } }; + for (const reference of [...(item.parameters ?? []), ...(operation.parameters ?? [])]) { + const parameter = parameterDefinition(reference, spec); + const group = parameter.in === "header" ? "headers" : parameter.in; + if (!groups[group]) throw new Error(`Unsupported parameter: ${name} ${parameter.in}`); + // Authorization comes from the selected connection, never an argument. + if (group === "headers" && /^(authorization|cookie|host)$/i.test(parameter.name)) continue; + groups[group].properties[parameter.name] = { ...(parameter.schema ?? {}), + ...(parameter.description ? { description: parameter.description } : {}) }; + if (parameter.required) groups[group].required.push(parameter.name); + parameters.push({ name: parameter.name, group, style: parameter.style ?? "form", explode: parameter.explode ?? true }); + } + for (const [group, schema] of Object.entries(groups)) { + if (!Object.keys(schema.properties).length) continue; + properties[group] = schema; + if (schema.required.length) required.push(group); + } + properties.metro = { type: "string", enum: metros, description: "Unikraft Cloud metro. Defaults to the connection's default metro." }; + if (kind === "sandbox") { + properties.instance = { type: "string", minLength: 1, maxLength: 200, description: "Name or UUID of the instance running the sandbox plugin." }; + required.push("instance"); + } + const content = operation.requestBody?.content ?? {}; + const bodyContentType = Object.hasOwn(content, "application/json") ? "application/json" : Object.keys(content)[0]; + if (bodyContentType && bodyContentType !== "application/json") throw new Error(`Unsupported request body: ${name} ${bodyContentType}`); + if (bodyContentType) { + properties.body = content[bodyContentType].schema ?? {}; + if (operation.requestBody.required) required.push("body"); + } + const readOnly = method === "get" || readOnlyPosts.has(operation.operationId); + const destructive = !readOnly && (method === "delete" || destructiveOperations.test(operation.operationId)); + const schema = inputSchema({ type: "object", additionalProperties: false, properties, required }, spec.components?.schemas ?? {}); + unsignedFormats(schema); + tools.push({ name, kind, path: route, method: method.toUpperCase(), parameters, + ...(bodyContentType ? { bodyContentType } : {}), + title: operation.summary || operation.operationId, + description: operation.description || operation.summary || operation.operationId, + inputSchema: schema, + annotations: { readOnlyHint: readOnly, destructiveHint: destructive, idempotentHint: readOnly, openWorldHint: true } }); + } + } + } + if (!metros.length) throw new Error("The platform specification lists no Unikraft Cloud metros"); + const names = tools.map(tool => tool.name); + if (new Set(names).size !== names.length) throw new Error("Duplicate Unikraft action name"); + return { metros, tools, streaming }; +} + +async function main() { + const inputIndex = process.argv.indexOf("--input-dir"); + const inputDir = inputIndex < 0 ? undefined : process.argv[inputIndex + 1]; + const specs = {}, provenance = {}; + for (const [kind, url] of Object.entries(sources)) { + const raw = inputDir ? await readFile(path.join(inputDir, `unikraft-${kind}-openapi.json`)) + : Buffer.from(await (await fetch(url)).arrayBuffer()); + specs[kind] = JSON.parse(raw.toString("utf8")); + provenance[kind] = { url, sha256: createHash("sha256").update(raw).digest("hex") }; + } + const output = { schemaVersion: 1, sources: provenance, ...generateUnikraftCatalog(specs) }; + const target = fileURLToPath(new URL("../server/src/services/connectors/unikraft/operations.generated.json", import.meta.url)); + await mkdir(path.dirname(target), { recursive: true }); + await writeFile(target, JSON.stringify(output, null, 2) + "\n"); + console.log(`Generated ${output.tools.length} Unikraft actions across ${output.metros.length} metros; recorded ${output.streaming.length} streaming endpoints.`); +} + +if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) await main(); diff --git a/scripts/generate-unikraft-tools.test.mjs b/scripts/generate-unikraft-tools.test.mjs new file mode 100644 index 00000000000..2e7cabee672 --- /dev/null +++ b/scripts/generate-unikraft-tools.test.mjs @@ -0,0 +1,61 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { generateUnikraftCatalog } from "./generate-unikraft-tools.mjs"; + +const servers = [{ url: "https://api.fra.unikraft.cloud" }, { url: "https://api.dal.unikraft.cloud" }]; + +test("accounts for every HTTP operation and server-sent event stream without dropping methods", () => { + const result = generateUnikraftCatalog({ platform: { servers, paths: { + "/v1/instances": { get: { operationId: "GetInstances" }, post: { operationId: "CreateInstance" } }, + "/v1/audit": { get: { operationId: "SubscribeAuditEvents", responses: { default: { content: { "text/event-stream": {} } } } } }, + } } }); + assert.deepEqual(result.metros, ["dal", "fra"]); + assert.deepEqual(result.tools.map(t => t.name), ["unikraft_get_instances", "unikraft_create_instance"]); + assert.deepEqual(result.streaming.map(s => s.name), ["unikraft_subscribe_audit_events"]); + assert.deepEqual(result.tools[0].inputSchema.properties.metro.enum, ["dal", "fra"]); +}); + +test("resolves shared parameters, keeps GET bodies and replaces unsigned formats", () => { + const result = generateUnikraftCatalog({ platform: { servers, paths: { "/v1/instances/log": { get: { + operationId: "GetInstanceLogs", + parameters: [{ $ref: "#/components/parameters/Uuid" }, { name: "limit", in: "query", schema: { type: "integer", format: "uint32" } }, + { name: "Authorization", in: "header", schema: { type: "string" } }], + requestBody: { required: true, content: { "application/json": { schema: { $ref: "#/components/schemas/Target" } } } }, + } } }, components: { + parameters: { Uuid: { name: "uuid", in: "query", schema: { type: "array", items: { type: "string" } } } }, + schemas: { Target: { type: "object", properties: { offset: { type: "integer", format: "uint64" }, + drain_timeout_ms: { type: "integer", format: "uint64", description: "No draining if 0. Use -1 for the largest possible value." } } } }, + } } }); + const [tool] = result.tools; + assert.deepEqual(tool.parameters.map(p => p.name), ["uuid", "limit"]); + assert.equal(tool.inputSchema.properties.headers, undefined); + assert.deepEqual(tool.inputSchema.properties.query.properties.limit, { type: "integer", minimum: 0 }); + assert.deepEqual(tool.inputSchema.definitions.Target.properties.offset, { type: "integer", minimum: 0 }); + // A documented -1 sentinel stays valid. + assert.equal(tool.inputSchema.definitions.Target.properties.drain_timeout_ms.minimum, -1); + assert.equal(tool.bodyContentType, "application/json"); + assert.ok(tool.inputSchema.required.includes("body")); + assert.equal(tool.annotations.readOnlyHint, true); +}); + +test("targets a sandbox instance and treats execution, disruption and deletion as destructive", () => { + const result = generateUnikraftCatalog({ platform: { servers, paths: { + "/v1/instances/stop": { put: { operationId: "StopInstances" } }, + "/v1/instances/start": { put: { operationId: "StartInstances" } }, + "/v1/volumes/detach": { put: { operationId: "DetachVolumes" } }, + "/v1/volumes/{uuid}": { delete: { operationId: "DeleteVolumeByUUID" } }, + } }, sandbox: { paths: { + "/commands": { post: { operationId: "RunCommand" } }, + "/fs/read": { post: { operationId: "ReadFile" } }, + "/fs/write": { post: { operationId: "WriteFile" } }, + } } }); + const risk = Object.fromEntries(result.tools.map(t => [t.name, + t.annotations.destructiveHint ? "destructive" : t.annotations.readOnlyHint ? "read" : "write"])); + assert.deepEqual(risk, { + unikraft_stop_instances: "destructive", unikraft_start_instances: "write", unikraft_detach_volumes: "destructive", + unikraft_delete_volume_by_uuid: "destructive", unikraft_sandbox_run_command: "destructive", + unikraft_sandbox_read_file: "read", unikraft_sandbox_write_file: "write", + }); + const sandbox = result.tools.find(t => t.name === "unikraft_sandbox_run_command"); + assert.ok(sandbox.inputSchema.required.includes("instance")); +}); diff --git a/server/src/__tests__/connector-tool-policy.test.ts b/server/src/__tests__/connector-tool-policy.test.ts index 9c094e3d27a..873ff3ecacc 100644 --- a/server/src/__tests__/connector-tool-policy.test.ts +++ b/server/src/__tests__/connector-tool-policy.test.ts @@ -41,7 +41,7 @@ describe("reviewed connector risk dispatch", () => { }); describe("reviewed connector catalog policies", () => { - it.each(["railway", "enterpret", "daytona", "braintrust"])("declares the initial drift policy for %s", source => { + it.each(["railway", "enterpret", "daytona", "unikraft", "braintrust"])("declares the initial drift policy for %s", source => { expect(connectorToolPolicy(source).quarantineNewEntries).toBe(true); }); diff --git a/server/src/__tests__/git-restore-point-ledger-config.test.ts b/server/src/__tests__/git-restore-point-ledger-config.test.ts new file mode 100644 index 00000000000..17690127a9b --- /dev/null +++ b/server/src/__tests__/git-restore-point-ledger-config.test.ts @@ -0,0 +1,25 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; +import { loadConfig } from "../config.ts"; + +describe("git restore point retention config parsing", () => { + afterEach(() => { + vi.unstubAllEnvs(); + }); + + it("keeps restore points 90 days after their issue tree closes by default", () => { + vi.stubEnv("PAPERCLIP_GIT_RESTORE_POINT_RETENTION_DAYS", undefined); + expect(loadConfig()).toMatchObject({ gitRestorePointRetentionDays: 90 }); + }); + + it("accepts an explicit retention and never goes below one day", () => { + vi.stubEnv("PAPERCLIP_GIT_RESTORE_POINT_RETENTION_DAYS", "30"); + expect(loadConfig()).toMatchObject({ gitRestorePointRetentionDays: 30 }); + vi.stubEnv("PAPERCLIP_GIT_RESTORE_POINT_RETENTION_DAYS", "0"); + expect(loadConfig()).toMatchObject({ gitRestorePointRetentionDays: 1 }); + }); + + it.each(["", "-1", "many"])("falls back to 90 days for %j", (value) => { + vi.stubEnv("PAPERCLIP_GIT_RESTORE_POINT_RETENTION_DAYS", value); + expect(loadConfig()).toMatchObject({ gitRestorePointRetentionDays: 90 }); + }); +}); diff --git a/server/src/__tests__/git-restore-point-ledger.test.ts b/server/src/__tests__/git-restore-point-ledger.test.ts new file mode 100644 index 00000000000..8b2e894dc44 --- /dev/null +++ b/server/src/__tests__/git-restore-point-ledger.test.ts @@ -0,0 +1,381 @@ +import { randomUUID } from "node:crypto"; +import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest"; +import { eq } from "drizzle-orm"; +import { agents, companies, createDb, executionWorkspaceRestorePoints, issues } from "@paperclipai/db"; +import { + getEmbeddedPostgresTestSupport, + startEmbeddedPostgresTestDatabase, +} from "./helpers/embedded-postgres.js"; +import { + GIT_RESTORE_POINT_RETRY_MS, + gitRestorePointRetentionService, + registerGitRestorePointWork, + requestRestorePointDeletion, +} from "../services/git-restore-point-ledger.ts"; +import { issueService } from "../services/issues.ts"; +import { deleteCompany } from "../services/company-deletion.ts"; +import type { StorageService } from "../storage/types.ts"; + +const embeddedPostgresSupport = await getEmbeddedPostgresTestSupport(); +const describeEmbeddedPostgres = embeddedPostgresSupport.supported ? describe : describe.skip; + +if (!embeddedPostgresSupport.supported) { + console.warn( + `Skipping embedded Postgres git restore point ledger tests on this host: ${embeddedPostgresSupport.reason ?? "unsupported environment"}`, + ); +} + +const DAY_MS = 24 * 60 * 60 * 1000; + +describeEmbeddedPostgres("git restore point retention", () => { + let db!: ReturnType; + let tempDb: Awaited> | null = null; + const now = new Date(); + let deleted: string[] = []; + let failDeletes = false; + let silentlyKeep = false; + const storage = { + deleteObject: async (_companyId: string, objectKey: string) => { + if (failDeletes) throw new Error("storage unavailable"); + if (!silentlyKeep) deleted.push(objectKey); + }, + headObject: async (_companyId: string, objectKey: string) => ({ exists: silentlyKeep && !deleted.includes(objectKey) }), + } as unknown as StorageService; + + beforeAll(async () => { + tempDb = await startEmbeddedPostgresTestDatabase("paperclip-git-restore-point-ledger-"); + db = createDb(tempDb.connectionString); + }, 20_000); + + afterEach(async () => { + deleted = []; + failDeletes = false; + silentlyKeep = false; + await db.delete(executionWorkspaceRestorePoints); + await db.delete(issues); + await db.delete(agents); + await db.delete(companies); + }); + + afterAll(async () => { + await tempDb?.cleanup(); + }); + + async function seed(input: { + status?: "pending" | "active" | "superseded" | "delete_requested"; + issueStatus?: string; + closedDaysAgo?: number; + openChild?: boolean; + createdHoursAgo?: number; + withoutIssue?: boolean; + deleteAfter?: Date | null; + companyId?: string; + } = {}) { + const companyId = input.companyId ?? randomUUID(); + if (!input.companyId) { + await db.insert(companies).values({ id: companyId, name: "Restore", issuePrefix: `R${companyId.slice(0, 6)}` }); + } + const issueStatus = input.issueStatus ?? "done"; + const closedAt = new Date(now.getTime() - (input.closedDaysAgo ?? 10) * DAY_MS); + const [issue] = await db.insert(issues).values({ + companyId, + title: "Work", + status: issueStatus, + completedAt: issueStatus === "done" ? closedAt : null, + cancelledAt: issueStatus === "cancelled" ? closedAt : null, + }).returning(); + if (input.openChild) { + await db.insert(issues).values({ companyId, title: "Child", status: "blocked", parentId: issue!.id }); + } + const workspaceId = randomUUID(); + const [row] = await db.insert(executionWorkspaceRestorePoints).values({ + companyId, + executionWorkspaceId: workspaceId, + sourceIssueId: input.withoutIssue ? randomUUID() : issue!.id, + generation: 1, + objectKey: `${companyId}/git-restore-points/${workspaceId}/1/${randomUUID()}.pcrp`, + sha256: "a".repeat(64), + byteSize: 1024, + status: input.status ?? "active", + deleteAfter: input.deleteAfter ?? null, + createdAt: new Date(now.getTime() - (input.createdHoursAgo ?? 0) * 60 * 60 * 1000), + }).returning(); + return row!; + } + + function service(options: { at?: Date; beforeExpire?: (row: typeof executionWorkspaceRestorePoints.$inferSelect) => Promise } = {}) { + return gitRestorePointRetentionService(db, { storage, now: () => options.at ?? now, beforeExpire: options.beforeExpire }); + } + + async function reload(id: string) { + const [row] = await db.select().from(executionWorkspaceRestorePoints).where(eq(executionWorkspaceRestorePoints.id, id)); + return row!; + } + + const due = () => new Date(now.getTime() - 1000); + + it("waits without a deadline while its tree is open", async () => { + const row = await seed({ openChild: true, deleteAfter: due() }); + expect(await service().tick()).toMatchObject({ scheduled: 1, deleted: 0 }); + expect(await reload(row.id)).toMatchObject({ status: "active", treeClosedAt: null, deleteAfter: null }); + expect(await service().nextDeadlineAt()).toBeNull(); + }); + + it("schedules deletion 90 days after the tree closes", async () => { + const row = await seed({ closedDaysAgo: 10, deleteAfter: due() }); + await service().tick(); + const reloaded = await reload(row.id); + expect(reloaded.status).toBe("active"); + expect(reloaded.deleteAfter!.getTime() - reloaded.treeClosedAt!.getTime()).toBe(90 * DAY_MS); + expect(deleted).toEqual([]); + }); + + it("expires and deletes a restore point at its deadline once the tree has been closed long enough", async () => { + const row = await seed({ closedDaysAgo: 91, deleteAfter: new Date(now.getTime() - 1000) }); + const beforeExpire = vi.fn(async () => {}); + expect(await service({ beforeExpire }).tick()).toMatchObject({ expired: 1, deleted: 1 }); + expect(beforeExpire).toHaveBeenCalledOnce(); + expect(deleted).toEqual([row.objectKey]); + expect(await reload(row.id)).toMatchObject({ status: "deleted" }); + }); + + it("keeps a restore point expiring, and its object, until the expiry hook succeeds", async () => { + const row = await seed({ closedDaysAgo: 91, deleteAfter: due() }); + expect(await service({ beforeExpire: async () => { throw new Error("recipe busy"); } }).tick()).toMatchObject({ failed: 1, expired: 0, deleted: 0 }); + expect(await reload(row.id)).toMatchObject({ status: "expiring", lastError: "recipe busy" }); + expect(await service().tick()).toMatchObject({ expired: 0, deleted: 0 }); + expect(await service().nextDeadlineAt()).toBe((await reload(row.id)).updatedAt.getTime() + GIT_RESTORE_POINT_RETRY_MS); + const beforeExpire = vi.fn(async () => {}); + const later = service({ at: new Date(now.getTime() + GIT_RESTORE_POINT_RETRY_MS + 1000), beforeExpire }); + expect(await later.tick()).toMatchObject({ expired: 1, deleted: 1 }); + expect(beforeExpire).toHaveBeenCalledOnce(); + expect(deleted).toEqual([row.objectKey]); + }); + + it("resumes the expiry hook after a restart instead of deleting the object directly", async () => { + const row = await seed({ closedDaysAgo: 91, status: "active" }); + await db.update(executionWorkspaceRestorePoints).set({ status: "expiring" }).where(eq(executionWorkspaceRestorePoints.id, row.id)); + const beforeExpire = vi.fn(async () => {}); + expect(await service().nextDeadlineAt()).toBe(now.getTime()); + expect(await service({ beforeExpire }).tick()).toMatchObject({ expired: 1, deleted: 1 }); + expect(beforeExpire).toHaveBeenCalledOnce(); + expect(await reload(row.id)).toMatchObject({ status: "deleted" }); + }); + + it("never deletes the object of an expiring restore point whose hook has not run", async () => { + const row = await seed({ closedDaysAgo: 91, status: "active" }); + await db.update(executionWorkspaceRestorePoints).set({ status: "expiring" }).where(eq(executionWorkspaceRestorePoints.id, row.id)); + await service({ beforeExpire: async () => { throw new Error("recipe busy"); } }).tick(); + expect(deleted).toEqual([]); + }); + + it("deletes restore points whose source issue no longer exists at review", async () => { + const row = await seed({ withoutIssue: true, deleteAfter: due() }); + expect(await service().tick()).toMatchObject({ orphaned: 1, deleted: 1 }); + expect(await reload(row.id)).toMatchObject({ status: "deleted" }); + }); + + it("evaluates only restore points that are due, so retained ones cause no rescans", async () => { + for (let index = 0; index < 60; index += 1) await seed({ closedDaysAgo: 10, deleteAfter: new Date(now.getTime() + DAY_MS) }); + expect(await service().tick()).toMatchObject({ scheduled: 0, expired: 0, truncated: false }); + expect(await service().nextDeadlineAt()).toBe(now.getTime() + DAY_MS); + }); + + it("works through a backlog of due restore points in bounded batches", async () => { + for (let index = 0; index < 60; index += 1) await seed({ closedDaysAgo: 10, deleteAfter: due() }); + expect(await service().tick()).toMatchObject({ scheduled: 50, truncated: true }); + expect(await service().tick()).toMatchObject({ scheduled: 10, truncated: false }); + expect(await service().tick()).toMatchObject({ scheduled: 0, truncated: false }); + }); + + it("cleans up abandoned pending restore points but leaves recent ones", async () => { + const stale = await seed({ status: "pending", createdHoursAgo: 2 }); + const recent = await seed({ status: "pending", createdHoursAgo: 0 }); + await service().tick(); + expect(await reload(stale.id)).toMatchObject({ status: "deleted" }); + expect(await reload(recent.id)).toMatchObject({ status: "pending" }); + }); + + it("deletes superseded restore points", async () => { + const row = await seed({ status: "superseded" }); + await service().tick(); + expect(await reload(row.id)).toMatchObject({ status: "deleted" }); + }); + + it("does not mark an object deleted when it is still present after deletion", async () => { + const row = await seed({ status: "delete_requested" }); + silentlyKeep = true; + expect(await service().tick()).toMatchObject({ failed: 1, deleted: 0 }); + expect(await reload(row.id)).toMatchObject({ status: "delete_requested", lastError: "restore_point_object_still_present" }); + }); + + it("retries a failed deletion only after its retry deadline", async () => { + const row = await seed({ status: "delete_requested" }); + failDeletes = true; + expect(await service().tick()).toMatchObject({ failed: 1 }); + failDeletes = false; + expect(await service().tick()).toMatchObject({ failed: 0, deleted: 0 }); + expect(await service().nextDeadlineAt()).toBe((await reload(row.id)).updatedAt.getTime() + GIT_RESTORE_POINT_RETRY_MS); + expect(await service({ at: new Date(now.getTime() + GIT_RESTORE_POINT_RETRY_MS + 1000) }).tick()).toMatchObject({ deleted: 1 }); + expect(await reload(row.id)).toMatchObject({ status: "deleted", lastError: null }); + }); + + it("reports the real next deadline, and none when idle", async () => { + expect(await service().nextDeadlineAt()).toBeNull(); + await seed({ openChild: true }); + expect(await service().nextDeadlineAt()).toBeNull(); + const reviewed = await seed({ closedDaysAgo: 10, deleteAfter: due() }); + expect(await service().nextDeadlineAt()).toBe(due().getTime()); + await service().tick(); + expect(await service().nextDeadlineAt()).toBe((await reload(reviewed.id)).deleteAfter!.getTime()); + const pending = await seed({ status: "pending", createdHoursAgo: 0 }); + expect(await service().nextDeadlineAt()).toBe((await reload(pending.id)).createdAt.getTime() + 60 * 60 * 1000); + await seed({ status: "delete_requested" }); + expect(await service().nextDeadlineAt()).toBe(now.getTime()); + }); + + it("marks only the deleted issue's restore points when an issue is removed", async () => { + const removed = await seed({ deleteAfter: new Date(now.getTime() + 80 * DAY_MS) }); + const kept = await seed({ deleteAfter: new Date(now.getTime() + 80 * DAY_MS) }); + await issueService(db).remove(removed.sourceIssueId!); + expect(await reload(removed.id)).toMatchObject({ status: "delete_requested" }); + expect(await reload(kept.id)).toMatchObject({ status: "active" }); + await service().tick(); + expect(deleted).toEqual([removed.objectKey]); + }); + + it("marks every restore point of a deleted company", async () => { + const first = await seed({ deleteAfter: new Date(now.getTime() + 80 * DAY_MS) }); + const second = await seed({ companyId: first.companyId, status: "pending" }); + const other = await seed({ deleteAfter: new Date(now.getTime() + 80 * DAY_MS) }); + await deleteCompany(db, first.companyId); + expect(await reload(first.id)).toMatchObject({ status: "delete_requested" }); + expect(await reload(second.id)).toMatchObject({ status: "delete_requested" }); + expect(await reload(other.id)).toMatchObject({ status: "active" }); + await service().tick(); + expect(deleted.sort()).toEqual([first.objectKey, second.objectKey].sort()); + }); + + it("requests deletion by issue without touching superseded rows", async () => { + const row = await seed({ status: "superseded" }); + await db.transaction(async (tx) => requestRestorePointDeletion(tx, { sourceIssueId: row.sourceIssueId! })); + expect(await reload(row.id)).toMatchObject({ status: "superseded" }); + }); + + it("schedules nothing when idle, and only the next deadline when restore points exist", async () => { + let task!: { run: (signal: AbortSignal) => Promise; nextRunAt?: () => number | null }; + const coordinator = { register: (_queue: string, registered: typeof task) => { task = registered; return { ready: Promise.resolve(), wake: vi.fn() }; } }; + registerGitRestorePointWork(coordinator as never, service(), () => {}); + await task.run(new AbortController().signal); + expect(task.nextRunAt?.()).toBeNull(); + const row = await seed({ closedDaysAgo: 10, deleteAfter: due() }); + await task.run(new AbortController().signal); + expect(task.nextRunAt?.()).toBe((await reload(row.id)).deleteAfter!.getTime()); + }); + + it("wakes on issue changes only while restore points exist", async () => { + let task!: { run: (signal: AbortSignal) => Promise; nextRunAt?: () => number | null }; + const wake = vi.fn(); + const coordinator = { register: (_queue: string, registered: typeof task) => { task = registered; return { ready: Promise.resolve(), wake }; } }; + const worker = registerGitRestorePointWork(coordinator as never, service(), () => {}); + await task.run(new AbortController().signal); + worker.issuesChanged(); + expect(wake).not.toHaveBeenCalled(); + await seed({ openChild: true }); + await task.run(new AbortController().signal); + worker.issuesChanged(); + expect(wake).toHaveBeenCalledOnce(); + }); + + describe("issue tree trigger", () => { + async function child(parentId: string, companyId: string, status = "blocked") { + const [row] = await db.insert(issues).values({ companyId, title: "Child", status, parentId }).returning(); + return row!; + } + + it("marks a parent's restore point due when its last open child closes", async () => { + const row = await seed(); + const open = await child(row.sourceIssueId!, row.companyId); + await db.update(executionWorkspaceRestorePoints).set({ deleteAfter: null }).where(eq(executionWorkspaceRestorePoints.id, row.id)); + await db.update(issues).set({ status: "done", completedAt: new Date() }).where(eq(issues.id, open.id)); + expect((await reload(row.id)).deleteAfter!.getTime()).toBeLessThanOrEqual(Date.now()); + }); + + it("marks the former parent when a child is deleted or moved", async () => { + const deletedFrom = await seed(); + const movedFrom = await seed(); + const deleting = await child(deletedFrom.sourceIssueId!, deletedFrom.companyId); + const moving = await child(movedFrom.sourceIssueId!, movedFrom.companyId); + const [elsewhere] = await db.insert(issues).values({ companyId: movedFrom.companyId, title: "Other", status: "todo" }).returning(); + await db.update(executionWorkspaceRestorePoints).set({ deleteAfter: null }); + await db.delete(issues).where(eq(issues.id, deleting.id)); + await db.update(issues).set({ parentId: elsewhere!.id }).where(eq(issues.id, moving.id)); + expect((await reload(deletedFrom.id)).deleteAfter).not.toBeNull(); + expect((await reload(movedFrom.id)).deleteAfter).not.toBeNull(); + }); + + it("marks a restore point when its own source issue changes status", async () => { + const row = await seed({ issueStatus: "in_progress" }); + await db.update(issues).set({ status: "done", completedAt: new Date() }).where(eq(issues.id, row.sourceIssueId!)); + expect((await reload(row.id)).deleteAfter).not.toBeNull(); + }); + + it("leaves unrelated, inactive and already due restore points alone", async () => { + const unrelated = await seed({ issueStatus: "in_progress" }); + const superseded = await seed({ status: "superseded", issueStatus: "in_progress" }); + const alreadyDue = await seed({ issueStatus: "in_progress", deleteAfter: new Date(now.getTime() - DAY_MS) }); + const [other] = await db.insert(issues).values({ companyId: unrelated.companyId, title: "Other", status: "todo" }).returning(); + await db.update(issues).set({ status: "done", completedAt: new Date() }).where(eq(issues.id, other!.id)); + await db.update(issues).set({ status: "done", completedAt: new Date() }).where(eq(issues.id, superseded.sourceIssueId!)); + await db.update(issues).set({ status: "done", completedAt: new Date() }).where(eq(issues.id, alreadyDue.sourceIssueId!)); + expect((await reload(unrelated.id)).deleteAfter).toBeNull(); + expect((await reload(superseded.id)).deleteAfter).toBeNull(); + expect((await reload(alreadyDue.id)).deleteAfter!.getTime()).toBe(now.getTime() - DAY_MS); + }); + + it("keeps a restore point due when its tree closes between the worker's read and write", async () => { + const row = await seed({ deleteAfter: due() }); + const open = await child(row.sourceIssueId!, row.companyId); + const racing = gitRestorePointRetentionService(db, { + storage, + afterTreeRead: async () => { + await db.update(issues).set({ status: "done", completedAt: new Date() }).where(eq(issues.id, open.id)); + }, + }); + expect(await racing.tick()).toMatchObject({ raced: 1, scheduled: 0 }); + const stillDue = await reload(row.id); + expect(stillDue.deleteAfter!.getTime()).toBeLessThanOrEqual(Date.now()); + await gitRestorePointRetentionService(db, { storage }).tick(); + const reviewed = await reload(row.id); + expect(reviewed.deleteAfter!.getTime() - reviewed.treeClosedAt!.getTime()).toBe(90 * DAY_MS); + }); + + it("does not expire a restore point whose tree reopens between the worker's read and write", async () => { + const row = await seed({ closedDaysAgo: 120, deleteAfter: due() }); + const racing = gitRestorePointRetentionService(db, { + storage, + afterTreeRead: async () => { + await db.update(issues).set({ status: "todo", completedAt: null }).where(eq(issues.id, row.sourceIssueId!)); + }, + }); + expect(await racing.tick()).toMatchObject({ raced: 1, expired: 0, deleted: 0 }); + expect(await reload(row.id)).toMatchObject({ status: "active" }); + await gitRestorePointRetentionService(db, { storage }).tick(); + expect(await reload(row.id)).toMatchObject({ status: "active", deleteAfter: null }); + expect(deleted).toEqual([]); + }); + + it("lets the worker schedule expiry once an open tree closes", async () => { + const row = await seed(); + const open = await child(row.sourceIssueId!, row.companyId); + await db.update(executionWorkspaceRestorePoints).set({ deleteAfter: due() }).where(eq(executionWorkspaceRestorePoints.id, row.id)); + const live = gitRestorePointRetentionService(db, { storage }); + await live.tick(); + expect((await reload(row.id)).deleteAfter).toBeNull(); + await db.update(issues).set({ status: "done", completedAt: new Date() }).where(eq(issues.id, open.id)); + await live.tick(); + const reviewed = await reload(row.id); + expect(reviewed.deleteAfter!.getTime() - reviewed.treeClosedAt!.getTime()).toBe(90 * DAY_MS); + }); + }); +}); diff --git a/server/src/__tests__/git-restore-point.test.ts b/server/src/__tests__/git-restore-point.test.ts new file mode 100644 index 00000000000..62135a90e83 --- /dev/null +++ b/server/src/__tests__/git-restore-point.test.ts @@ -0,0 +1,304 @@ +import { execFileSync } from "node:child_process"; +import fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { + applyRestorePoint, + captureRestorePoint, + currentRestorePointSnapshot, + importRestorePoint, + openRestorePoint, + RestorePointRestoreFailure, + RestorePointSkip, + sealRestorePoint, +} from "../services/git-restore-point.ts"; + +const identity = { companyId: "company-1", workspaceId: "workspace-1", restorePointId: "restore-1", generation: 1 }; + +function git(cwd: string, ...args: string[]) { + return execFileSync("git", ["-C", cwd, ...args], { + encoding: "utf8", + env: { ...process.env, GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: os.devNull, GIT_AUTHOR_NAME: "Test", GIT_AUTHOR_EMAIL: "t@example.com", GIT_COMMITTER_NAME: "Test", GIT_COMMITTER_EMAIL: "t@example.com" }, + }).trim(); +} + +async function write(root: string, relative: string, contents: string, mode?: number) { + await fs.mkdir(path.dirname(path.join(root, relative)), { recursive: true }); + await fs.writeFile(path.join(root, relative), contents); + if (mode !== undefined) await fs.chmod(path.join(root, relative), mode); +} + +async function read(root: string, relative: string) { + return fs.readFile(path.join(root, relative), "utf8"); +} + +describe("git restore points", () => { + let root: string; + let remote: string; + let work: string; + + beforeEach(async () => { + vi.stubEnv("PAPERCLIP_SECRETS_MASTER_KEY", "31".repeat(32)); + root = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-restore-point-test-")); + remote = path.join(root, "remote.git"); + work = path.join(root, "work"); + git(root, "init", "-q", "--bare", "-b", "master", remote); + git(root, "clone", "-q", remote, work); + await write(work, ".gitignore", "node_modules/\n.env\n"); + await write(work, "src/app.ts", "export const value = 1;\n"); + await write(work, "src/remove-me.ts", "export const gone = true;\n"); + await write(work, "README.md", "hello\n"); + git(work, "add", "-A"); + git(work, "commit", "-q", "-m", "initial"); + git(work, "push", "-q", "origin", "HEAD:master"); + }); + + afterEach(async () => { + vi.unstubAllEnvs(); + await fs.rm(root, { recursive: true, force: true }); + }); + + const upstream = () => [git(work, "rev-parse", "origin/master")]; + + async function freshClone(name: string) { + const target = path.join(root, name); + git(root, "clone", "-q", remote, target); + return target; + } + + it("returns nothing when the workspace is fully reproducible from the remote", async () => { + await write(work, "node_modules/pkg/index.js", "ignored\n"); + expect(await captureRestorePoint({ cwd: work, upstreamCommits: upstream() })).toBeNull(); + }); + + it("round-trips unpushed commits, staged and unstaged edits, untracked files, deletions and modes", async () => { + git(work, "checkout", "-q", "-b", "feature"); + await write(work, "src/app.ts", "export const value = 2;\n"); + git(work, "commit", "-q", "-am", "unpushed one"); + await write(work, "src/feature.ts", "export const feature = true;\n"); + git(work, "add", "src/feature.ts"); + git(work, "commit", "-q", "-m", "unpushed two"); + await write(work, "src/app.ts", "export const value = 3;\n"); + git(work, "add", "src/app.ts"); + await write(work, "src/app.ts", "export const value = 4;\n"); + await fs.rm(path.join(work, "src/remove-me.ts")); + await write(work, "notes/todo.md", "untracked work\n"); + await write(work, "scripts/run.sh", "#!/bin/sh\necho hi\n", 0o755); + await write(work, "private/key.txt", "secret-ish\n", 0o600); + await fs.symlink("../README.md", path.join(work, "notes/readme-link")); + await write(work, "node_modules/pkg/index.js", "ignored\n"); + await write(work, ".env", "TOKEN=ignored\n"); + await write(work, "config/.env.local", "TOKEN=untracked-env\n"); + + const captured = await captureRestorePoint({ cwd: work, upstreamCommits: upstream() }); + expect(captured).not.toBeNull(); + const { payload, manifest } = captured!; + expect(manifest.head).toBe(git(work, "rev-parse", "HEAD")); + expect(manifest.prerequisites).toEqual(upstream()); + expect(await currentRestorePointSnapshot(work)).toEqual({ head: manifest.head, index: manifest.index, worktree: manifest.worktree, modes: manifest.modes }); + + const restored = await freshClone("restored"); + expect(() => git(restored, "cat-file", "-e", `${manifest.head}^{commit}`)).toThrow(); + const imported = await importRestorePoint({ repositoryCwd: restored, payload }); + git(restored, "checkout", "-q", "-b", "feature", imported.head); + await applyRestorePoint({ cwd: restored, manifest: imported }); + + expect(await read(restored, "src/app.ts")).toBe("export const value = 4;\n"); + expect(git(restored, "show", ":src/app.ts")).toBe("export const value = 3;"); + expect(await read(restored, "src/feature.ts")).toBe("export const feature = true;\n"); + await expect(fs.access(path.join(restored, "src/remove-me.ts"))).rejects.toMatchObject({ code: "ENOENT" }); + expect(await read(restored, "notes/todo.md")).toBe("untracked work\n"); + expect(git(restored, "status", "--porcelain", "--", "notes/todo.md")).toBe("?? notes/todo.md"); + expect((await fs.stat(path.join(restored, "scripts/run.sh"))).mode & 0o777).toBe(0o755); + expect((await fs.stat(path.join(restored, "private/key.txt"))).mode & 0o777).toBe(0o600); + expect(await fs.readlink(path.join(restored, "notes/readme-link"))).toBe("../README.md"); + await expect(fs.access(path.join(restored, "node_modules"))).rejects.toMatchObject({ code: "ENOENT" }); + await expect(fs.access(path.join(restored, ".env"))).rejects.toMatchObject({ code: "ENOENT" }); + await expect(fs.access(path.join(restored, "config/.env.local"))).rejects.toMatchObject({ code: "ENOENT" }); + }); + + it("captures edits to tracked environment files", async () => { + await write(work, ".env.example", "TOKEN=\n"); + git(work, "add", ".env.example"); + git(work, "commit", "-q", "-m", "example env"); + git(work, "push", "-q", "origin", "HEAD:master"); + git(work, "fetch", "-q"); + await write(work, ".env.example", "TOKEN=\nOTHER=\n"); + const captured = await captureRestorePoint({ cwd: work, upstreamCommits: upstream() }); + expect(captured).not.toBeNull(); + const restored = await freshClone("restored"); + await importRestorePoint({ repositoryCwd: restored, payload: captured!.payload }); + await applyRestorePoint({ cwd: restored, manifest: captured!.manifest }); + expect(await read(restored, ".env.example")).toBe("TOKEN=\nOTHER=\n"); + }); + + it("applies safeguards and modes to staged files whose contents match the index", async () => { + await write(work, "private/staged.txt", "staged secret\n", 0o600); + git(work, "add", "private/staged.txt"); + await expect(captureRestorePoint({ cwd: work, upstreamCommits: upstream(), limits: { maxFiles: 0 } })) + .rejects.toMatchObject({ reason: "restore_point_too_large" }); + const captured = await captureRestorePoint({ cwd: work, upstreamCommits: upstream() }); + expect(captured!.manifest.modes).toEqual([{ path: "private/staged.txt", mode: 0o600 }]); + const restored = await freshClone("restored"); + await importRestorePoint({ repositoryCwd: restored, payload: captured!.payload }); + await applyRestorePoint({ cwd: restored, manifest: captured!.manifest }); + expect((await fs.stat(path.join(restored, "private/staged.txt"))).mode & 0o777).toBe(0o600); + expect(git(restored, "status", "--porcelain", "--", "private/staged.txt")).toBe("A private/staged.txt"); + }); + + it("detects permission-only changes after capture", async () => { + await write(work, "private/key.txt", "secret\n", 0o600); + const before = await currentRestorePointSnapshot(work); + await fs.chmod(path.join(work, "private/key.txt"), 0o644); + expect(await currentRestorePointSnapshot(work)).not.toEqual(before); + }); + + it("restores exact bytes when the repository configures a diff text conversion", async () => { + await write(work, ".gitattributes", "*.txt diff=upper\n"); + await write(work, "notes.txt", "original\n"); + git(work, "add", ".gitattributes", "notes.txt"); + git(work, "commit", "-q", "-m", "textconv"); + git(work, "push", "-q", "origin", "HEAD:master"); + git(work, "fetch", "-q"); + await write(work, "notes.txt", "changed contents\n"); + const captured = await captureRestorePoint({ cwd: work, upstreamCommits: upstream() }); + const restored = await freshClone("restored"); + git(restored, "config", "diff.upper.textconv", "tr a-z A-Z <"); + await importRestorePoint({ repositoryCwd: restored, payload: captured!.payload }); + await applyRestorePoint({ cwd: restored, manifest: captured!.manifest }); + expect(await read(restored, "notes.txt")).toBe("changed contents\n"); + }); + + it("captures a small edit despite a long ordinary history", async () => { + for (let index = 0; index < 60; index += 1) { + await write(work, "log.txt", `${index}\n`); + git(work, "commit", "-q", "-am", `history ${index}`, "--allow-empty"); + } + git(work, "add", "-A"); + git(work, "commit", "-q", "-m", "tracked log", "--allow-empty"); + git(work, "push", "-q", "origin", "HEAD:master"); + git(work, "fetch", "-q"); + await write(work, "notes/todo.md", "small edit\n"); + const captured = await captureRestorePoint({ cwd: work, upstreamCommits: upstream() }); + expect(captured!.manifest.preserved).toEqual([]); + expect(captured!.manifest.worktree).not.toBeNull(); + }); + + it("refuses to capture when history-only commits exceed the supported limit", async () => { + git(work, "checkout", "-q", "-b", "feature"); + for (let index = 0; index < 51; index += 1) { + await write(work, `scratch/${index}.txt`, `${index}\n`); + git(work, "add", "-A"); + git(work, "commit", "-q", "-m", `scratch ${index}`); + git(work, "reset", "-q", "--hard", "origin/master"); + } + await expect(captureRestorePoint({ cwd: work, upstreamCommits: upstream() })) + .rejects.toMatchObject({ reason: "restore_point_history_too_large" }); + }); + + it("keeps capture objects out of the repository", async () => { + await write(work, "notes/todo.md", "untracked work\n"); + const before = git(work, "count-objects", "-v"); + expect(await captureRestorePoint({ cwd: work, upstreamCommits: upstream() })).not.toBeNull(); + expect(git(work, "count-objects", "-v")).toBe(before); + expect(git(work, "fsck", "--no-reflogs", "--unreachable", "--no-progress")).toBe(""); + }); + + it("produces the same snapshot ids for unchanged contents and different ids after a change", async () => { + await write(work, "notes/todo.md", "one\n"); + const first = await currentRestorePointSnapshot(work); + expect(await currentRestorePointSnapshot(work)).toEqual(first); + await write(work, "notes/todo.md", "two\n"); + expect(await currentRestorePointSnapshot(work)).not.toEqual(first); + }); + + it("bundles commits only reachable from history that is not on any branch", async () => { + git(work, "checkout", "-q", "-b", "feature"); + await write(work, "src/lost.ts", "export const lost = true;\n"); + git(work, "add", "src/lost.ts"); + git(work, "commit", "-q", "-m", "rewritten away"); + const lost = git(work, "rev-parse", "HEAD"); + git(work, "reset", "-q", "--hard", "origin/master"); + const captured = await captureRestorePoint({ cwd: work, upstreamCommits: upstream() }); + expect(captured!.manifest.preserved).toEqual([lost]); + const restored = await freshClone("restored"); + await importRestorePoint({ repositoryCwd: restored, payload: captured!.payload }); + expect(git(restored, "cat-file", "-t", lost)).toBe("commit"); + expect(git(restored, "for-each-ref", "refs/paperclip-restore-import/")).toBe(""); + }); + + it("skips a nested untracked repository", async () => { + git(root, "init", "-q", path.join(work, "vendor/other")); + await write(work, "vendor/other/file.txt", "x\n"); + await expect(captureRestorePoint({ cwd: work, upstreamCommits: upstream() })) + .rejects.toMatchObject({ reason: "untracked_nested_repository" }); + }); + + it("ignores special files, which git never tracks", async () => { + execFileSync("mkfifo", [path.join(work, "pipe")]); + expect(await captureRestorePoint({ cwd: work, upstreamCommits: upstream() })).toBeNull(); + }); + + it("skips workspaces over the size limits", async () => { + await write(work, "big.bin", "x".repeat(2048)); + await expect(captureRestorePoint({ cwd: work, upstreamCommits: upstream(), limits: { maxFileBytes: 1024 } })) + .rejects.toBeInstanceOf(RestorePointSkip); + await expect(captureRestorePoint({ cwd: work, upstreamCommits: upstream(), limits: { maxFiles: 0 } })) + .rejects.toMatchObject({ reason: "restore_point_too_large" }); + }); + + it("refuses to import a bundle whose prerequisites are missing", async () => { + git(work, "checkout", "-q", "-b", "feature"); + await write(work, "src/app.ts", "export const value = 2;\n"); + git(work, "commit", "-q", "-am", "unpushed"); + const captured = await captureRestorePoint({ cwd: work, upstreamCommits: upstream() }); + const empty = path.join(root, "empty"); + git(root, "init", "-q", empty); + await expect(importRestorePoint({ repositoryCwd: empty, payload: captured!.payload })) + .rejects.toMatchObject({ reason: "bundle_prerequisites_missing" }); + }); + + it("refuses to apply onto a different commit", async () => { + await write(work, "notes/todo.md", "work\n"); + const captured = await captureRestorePoint({ cwd: work, upstreamCommits: upstream() }); + const restored = await freshClone("restored"); + await write(restored, "other.txt", "x\n"); + git(restored, "add", "other.txt"); + git(restored, "commit", "-q", "-m", "diverged"); + await expect(applyRestorePoint({ cwd: restored, manifest: captured!.manifest })) + .rejects.toMatchObject({ reason: "head_mismatch" }); + }); + + describe("encryption", () => { + const payload = Buffer.from("restore point payload"); + + it("round-trips and binds to the restore point identity", async () => { + const sealed = await sealRestorePoint(payload, identity); + expect(sealed.ciphertext.includes(payload)).toBe(false); + expect(await openRestorePoint({ ...sealed, identity })).toEqual(payload); + await expect(openRestorePoint({ ...sealed, identity: { ...identity, workspaceId: "workspace-2" } })) + .rejects.toMatchObject({ reason: "key_identity_mismatch" }); + }); + + it("rejects tampered, truncated or rehashed ciphertext", async () => { + const sealed = await sealRestorePoint(payload, identity); + const flipped = Buffer.from(sealed.ciphertext); + flipped[flipped.length - 20] ^= 1; + await expect(openRestorePoint({ ...sealed, ciphertext: flipped, identity })) + .rejects.toMatchObject({ reason: "ciphertext_hash_mismatch" }); + const { createHash } = await import("node:crypto"); + await expect(openRestorePoint({ ...sealed, ciphertext: flipped, sha256: createHash("sha256").update(flipped).digest("hex"), identity })) + .rejects.toMatchObject({ reason: "ciphertext_authentication_failed" }); + const truncated = sealed.ciphertext.subarray(0, 10); + await expect(openRestorePoint({ ...sealed, ciphertext: truncated, sha256: createHash("sha256").update(truncated).digest("hex"), identity })) + .rejects.toBeInstanceOf(RestorePointRestoreFailure); + }); + + it("cannot be opened with a different master key", async () => { + const sealed = await sealRestorePoint(payload, identity); + vi.stubEnv("PAPERCLIP_SECRETS_MASTER_KEY", "42".repeat(32)); + await expect(openRestorePoint({ ...sealed, identity })).rejects.toThrow(); + }); + }); +}); diff --git a/server/src/__tests__/instance-settings-service.test.ts b/server/src/__tests__/instance-settings-service.test.ts index 631c1389844..f6e619822f3 100644 --- a/server/src/__tests__/instance-settings-service.test.ts +++ b/server/src/__tests__/instance-settings-service.test.ts @@ -76,6 +76,7 @@ describe("instance settings service", () => { enableMuse: false, enableGrokBot: false, enableCombinedInboxTasks: false, + enableArtifactsV1: false, enableChatConnectors: false, enableGitHubReviewBots: false, enableMemoryConnectors: false, diff --git a/server/src/__tests__/unikraft-connection.test.ts b/server/src/__tests__/unikraft-connection.test.ts new file mode 100644 index 00000000000..5980682d8f2 --- /dev/null +++ b/server/src/__tests__/unikraft-connection.test.ts @@ -0,0 +1,198 @@ +import { randomUUID } from "node:crypto"; +import { and, eq } from "drizzle-orm"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +import { agents, companies, companyMemberships, companySecrets, connectionGrants, createDb, + ensurePostgresDatabase, applyPendingMigrations, heartbeatRuns, issues, toolCatalogEntries, toolConnections } from "@paperclipai/db"; +import { toolAccessService } from "../services/tool-access.js"; +import { createToolGatewayService } from "../services/tool-gateway.js"; +import { UNIKRAFT_TOOLS, executeUnikraftTool, unikraftClient } from "../services/connectors/unikraft/client.js"; +import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase } from "./helpers/embedded-postgres.js"; + +const external = process.env.PAPERCLIP_TEST_DATABASE_URL; +const support = external ? { supported: true } : await getEmbeddedPostgresTestSupport(); +const actor = { actorType: "user" as const, actorId: "unikraft-reviewer" }; +const credential = "unikraft-fixture-token"; +(support.supported ? describe : describe.skip)("Unikraft governed provider actions", () => { + let db: ReturnType; + let temp: Awaited> | undefined; + beforeAll(async () => { + if (external) { + const url = new URL(external); url.pathname = "/unikraft_" + randomUUID().replaceAll("-", ""); + await ensurePostgresDatabase(external, url.pathname.slice(1)); + await applyPendingMigrations(url.toString()); db = createDb(url.toString()); + } else { temp = await startEmbeddedPostgresTestDatabase("unikraft-connector-"); db = createDb(temp.connectionString); } + }, 30000); + afterAll(async () => { await db?.$client.end(); await temp?.cleanup(); }); + + async function fixture(options: { personal?: boolean; metro?: string } = {}) { + const [company] = await db.insert(companies).values({ name: "Unikraft fixture", issuePrefix: `U${randomUUID().slice(0, 6)}` }).returning(); + await db.insert(companyMemberships).values({ companyId: company.id, principalType: "user", principalId: actor.actorId, membershipRole: "admin", status: "active" }); + const instanceId = randomUUID(); + const request = vi.fn(async (url: string, init: RequestInit) => { + expect(new Headers(init.headers).get("Authorization")).toBe(`Bearer ${credential}`); + expect(["error", "manual"]).toContain(init.redirect); + const parsed = new URL(url); + expect(parsed.hostname).toMatch(/^api\.(dal|fra|sfo|sin|was)\.unikraft\.cloud$/); + const metro = parsed.hostname.split(".")[1]; + const body = typeof init.body === "string" ? JSON.parse(init.body) : undefined; + if (parsed.pathname === "/v1/users/quotas") return Response.json({ status: "success", data: { quotas: [{ uuid: randomUUID() }] }, op_time_us: 1 }); + if (parsed.pathname === "/v1/instances" && init.method === "GET") { + return Response.json({ status: "success", data: { instances: [{ uuid: instanceId, name: "web", metro, selector: body ?? null }] }, op_time_us: 1 }); + } + if (parsed.pathname === "/v1/instances" && init.method === "POST") return Response.json({ status: "success", data: { instances: [{ uuid: instanceId, ...body }] }, op_time_us: 1 }); + if (parsed.pathname === `/v1/instances/${instanceId}` && init.method === "DELETE") return Response.json({ status: "success", data: { instances: [{ uuid: instanceId, state: "deleted" }] }, op_time_us: 1 }); + if (parsed.pathname === "/v1/instances/web/plugins/sandbox/commands" && init.method === "POST") { + return Response.json({ uuid: randomUUID(), cmd: body.cmd, stdout: `ran with ${credential}` }); + } + throw new Error(`Unexpected provider request ${init.method} ${url}`); + }); + const service = toolAccessService(db, { remoteHttpRequest: request, remoteHttpEndpointLookup: async () => [{ address: "8.8.8.8", family: 4 }] }); + const connected = await service.connectGalleryApp(company.id, { galleryKey: "unikraft", methodKey: "api-token", + ...(options.personal ? { grantKind: "user" as const } : {}), ...(options.metro ? { configValues: { metro: options.metro } } : {}), + credentialValues: { "credentials.authorization": credential } }, actor); + const [agent] = await db.insert(agents).values({ companyId: company.id, name: "Operator", role: "engineer", adapterType: "process" }).returning(); + await service.finishGalleryAppConnection(company.id, connected.connectionId, { enabledCatalogEntryIds: connected.catalog.map(c => c.id), + askFirstCatalogEntryIds: [], access: { agentIds: [agent.id] } }, actor); + const [issue] = await db.insert(issues).values({ companyId: company.id, title: "Provider work", assigneeAgentId: agent.id }).returning(); + const [run] = await db.insert(heartbeatRuns).values({ companyId: company.id, agentId: agent.id, issueId: issue.id, + scopeKind: "issue", invocationSource: "on_demand", status: "running", responsibleUserId: actor.actorId, contextSnapshot: { issueId: issue.id } }).returning(); + const gateway = createToolGatewayService(db, { remoteHttpRequest: request }); + const session = await gateway.createSession({ companyId: company.id, agentId: agent.id, runId: run.id, issueId: issue.id }); + const tools = await gateway.listToolsForSession(session.token); + const call = (name: string, parameters: unknown) => { + const tool = tools.find(t => t.upstreamToolName === name); + expect(tool, name).toBeTruthy(); + return gateway.executeTool({ sessionToken: session.token, tool: tool!.name, parameters, idempotencyKey: randomUUID() }); + }; + return { company, service, connected, agent, request, call, gateway, session, instanceId }; + } + + it.each([false, true])("uses the selected vault identity for real gateway reads, writes and sandbox commands (personal=%s)", async personal => { + const f = await fixture({ personal }); + const [grant] = await db.select().from(connectionGrants).where(eq(connectionGrants.connectionId, f.connected.connectionId)); + const [secret] = await db.select().from(companySecrets).where(eq(companySecrets.id, grant.credentialSecretRefs[0].secretId)); + expect(grant.kind).toBe(personal ? "user" : "organization"); + expect(secret.scope).toBe(personal ? "user" : "company"); + expect(secret.ownerUserId).toBe(personal ? actor.actorId : null); + expect(f.connected.catalog).toHaveLength(UNIKRAFT_TOOLS.length); + expect(f.connected.catalog.find(c => c.toolName === "unikraft_get_instances")).toMatchObject({ isReadOnly: true }); + expect(f.connected.catalog.find(c => c.toolName === "unikraft_create_instance")).toMatchObject({ isReadOnly: false, isDestructive: false }); + for (const name of ["unikraft_delete_instance_by_uuid", "unikraft_stop_instances", "unikraft_sandbox_run_command"]) { + expect(f.connected.catalog.find(c => c.toolName === name), name).toMatchObject({ isDestructive: true, isReadOnly: false }); + } + expect(JSON.stringify(await f.service.getConnection(f.connected.connectionId))).not.toContain(credential); + // The token is verified against the default metro. + expect(f.request.mock.calls.some(([url]) => url === "https://api.fra.unikraft.cloud/v1/users/quotas")).toBe(true); + // Batch selectors travel as a JSON body even on GET. + const listed = JSON.stringify(await f.call("unikraft_get_instances", { body: [{ name: "web" }] })); + expect(listed).toContain(f.instanceId); expect(listed).toContain('"metro":"fra"'); expect(listed).toContain('"name":"web"'); + expect(JSON.stringify(await f.call("unikraft_get_instances", { metro: "sin" }))).toContain('"metro":"sin"'); + expect(JSON.stringify(await f.call("unikraft_create_instance", { body: { image: "nginx:latest" } }))).toContain("nginx:latest"); + expect(JSON.stringify(await f.call("unikraft_delete_instance_by_uuid", { path: { uuid: f.instanceId } }))).toContain("deleted"); + const ran = JSON.stringify(await f.call("unikraft_sandbox_run_command", { instance: "web", body: { cmd: "echo done" } })); + expect(ran).toContain("echo done"); expect(ran).not.toContain(credential); + }, 30000); + + it("uses the connection's default metro for verification and actions that do not name one", async () => { + const f = await fixture({ metro: "was" }); + expect(f.request.mock.calls.some(([url]) => url === "https://api.was.unikraft.cloud/v1/users/quotas")).toBe(true); + expect(JSON.stringify(await f.call("unikraft_get_instances", {}))).toContain('"metro":"was"'); + }); + + it("rejects unpublished metros, unknown arguments, path traversal and foreign bindings before forwarding the token", async () => { + const f = await fixture(); f.request.mockClear(); + await expect(f.call("unikraft_get_instances", { metro: "attacker.example" })).rejects.toThrow(); + await expect(f.call("unikraft_get_instances", { command: "printenv" })).rejects.toThrow(); + await expect(f.call("unikraft_sandbox_run_command", { instance: "../../users/quotas", body: { cmd: "true" } })).rejects.toThrow("Invalid Unikraft path"); + expect(f.request).not.toHaveBeenCalled(); + await expect(executeUnikraftTool({ binding: { companyId: randomUUID(), agentId: f.agent.id }, + connection: { companyId: f.company.id, config: {} }, headers: { Authorization: `Bearer ${credential}` }, request: f.request, + toolName: "unikraft_get_instances", parameters: {} })).rejects.toThrow("Connection not found"); + await expect(executeUnikraftTool({ binding: { companyId: f.company.id, agentId: f.agent.id }, + connection: { companyId: f.company.id, config: { methodConfig: { metro: "evil" } } }, headers: { Authorization: `Bearer ${credential}` }, + request: f.request, toolName: "unikraft_get_instances", parameters: {} })).rejects.toThrow("Choose a Unikraft Cloud metro"); + expect(f.request).not.toHaveBeenCalled(); + }); + + it("rechecks revocation and Off policies for a previously created gateway session", async () => { + const f = await fixture(); + await f.service.finishGalleryAppConnection(f.company.id, f.connected.connectionId, { + enabledCatalogEntryIds: f.connected.catalog.filter(c => c.toolName !== "unikraft_sandbox_run_command").map(c => c.id), + askFirstCatalogEntryIds: [], access: { agentIds: [f.agent.id] }, + }, actor); + f.request.mockClear(); + await expect(f.call("unikraft_sandbox_run_command", { instance: "web", body: { cmd: "true" } })).rejects.toThrow(); + expect(f.request).not.toHaveBeenCalled(); + await db.update(toolConnections).set({ enabled: false }).where(and(eq(toolConnections.id, f.connected.connectionId), eq(toolConnections.companyId, f.company.id))); + await expect(f.call("unikraft_get_instances", {})).rejects.toThrow(); + expect(f.request).not.toHaveBeenCalled(); + }); + + it("blocks a stale same-name contract and quarantines it on refresh", async () => { + const f = await fixture(); + const changed = f.connected.catalog.find(entry => entry.toolName === "unikraft_sandbox_run_command")!; + await db.update(toolCatalogEntries).set({ versionHash: "earlier-provider-contract", schemaHash: "earlier-schema", riskLevel: "read" }) + .where(eq(toolCatalogEntries.id, changed.id)); + f.request.mockClear(); + expect((await f.gateway.listToolsForSession(f.session.token)).some(tool => tool.upstreamToolName === changed.toolName)).toBe(false); + await expect(f.call(changed.toolName, { instance: "web", body: { cmd: "true" } })).rejects.toThrow(); + expect(f.request).not.toHaveBeenCalled(); + const refreshed = await f.service.refreshCatalog(f.connected.connectionId, actor, { enableAllByDefault: true }); + expect(refreshed.catalog.find(entry => entry.toolName === changed.toolName)?.status).toBe("quarantined"); + }); + + it("does not export the token to agents", async () => { + const f = await fixture(); + const [run] = await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.agentId, f.agent.id)); + await expect(f.service.mintConnectionTokenForAgent({ connectionId: f.connected.connectionId, companyId: f.company.id, + agentId: f.agent.id, runId: run.id, body: {} as never })).rejects.toThrow("Unikraft credentials stay in the governed tool gateway"); + }); +}); + +describe("Unikraft provider client", () => { + it("validates every published action schema before provider dispatch", async () => { + const request = vi.fn(async () => { throw new Error("Unexpected provider request"); }); + expect(UNIKRAFT_TOOLS).toHaveLength(105); + for (const tool of UNIKRAFT_TOOLS) { + await expect(executeUnikraftTool({ binding: { companyId: "company", agentId: "agent" }, + connection: { companyId: "company", config: {} }, headers: { Authorization: `Bearer ${credential}` }, + request, toolName: tool.name, parameters: null }), tool.name).rejects.toThrow("Invalid Unikraft action arguments"); + } + expect(request).not.toHaveBeenCalled(); + }); + it("sanitizes provider denials and rejects an unconfirmed token", async () => { + await expect(unikraftClient({ Authorization: `Bearer ${credential}` }, async () => new Response(credential, { status: 401 })).probe()) + .rejects.toThrow("Unikraft denied access"); + await expect(unikraftClient({ Authorization: `Bearer ${credential}` }, async () => Response.json({ status: "error" })).probe()) + .rejects.toThrow("did not confirm"); + }); + it("accepts the documented -1 drain timeout for the longest drain", async () => { + const request = vi.fn(async (_url: string, init: RequestInit) => Response.json({ status: "success", echo: JSON.parse(init.body as string) })); + for (const [toolName, parameters] of [ + ["unikraft_stop_instances", { body: [{ name: "web", drain_timeout_ms: -1 }] }], + ["unikraft_suspend_instance_by_uuid", { path: { uuid: randomUUID() }, body: { drain_timeout_ms: -1 } }], + ] as const) { + await executeUnikraftTool({ binding: { companyId: "company", agentId: "agent" }, connection: { companyId: "company", config: {} }, + headers: { Authorization: `Bearer ${credential}` }, request, toolName, parameters }); + } + expect(request).toHaveBeenCalledTimes(2); + await expect(executeUnikraftTool({ binding: { companyId: "company", agentId: "agent" }, connection: { companyId: "company", config: {} }, + headers: { Authorization: `Bearer ${credential}` }, request, toolName: "unikraft_stop_instances", + parameters: { body: [{ name: "web", drain_timeout_ms: -2 }] } })).rejects.toThrow("Invalid Unikraft action arguments"); + }); + it("reports a timed-out sandbox wait as a still-running command, not a provider failure", async () => { + const commandId = randomUUID(); + const request = vi.fn(async () => new Response(null, { status: 408 })); + const call = (toolName: string) => executeUnikraftTool({ binding: { companyId: "company", agentId: "agent" }, + connection: { companyId: "company", config: {} }, headers: { Authorization: `Bearer ${credential}` }, request, toolName, + parameters: { instance: "web", path: { uuid: commandId }, ...(toolName.endsWith("with_timeout") ? { body: { timeout_s: 0.01 } } : {}) } }); + await expect(call("unikraft_sandbox_wait_for_command_with_timeout")).resolves.toMatchObject({ status: "running", timedOut: true }); + expect(request.mock.calls[0]![0]).toBe(`https://api.fra.unikraft.cloud/v1/instances/web/plugins/sandbox/commands/${commandId}/wait_timeout`); + // Other operations keep treating 408 as a failure. + await expect(call("unikraft_sandbox_get_command_by_uuid")).rejects.toThrow("Unikraft could not complete this operation"); + }); + it("bounds response bytes", async () => { + await expect(unikraftClient({ Authorization: `Bearer ${credential}` }, async () => new Response("x".repeat(8 * 1024 * 1024 + 1))).probe()) + .rejects.toThrow("exceeded the limit"); + }); +}); diff --git a/server/src/__tests__/unikraft-http-boundary.test.ts b/server/src/__tests__/unikraft-http-boundary.test.ts new file mode 100644 index 00000000000..f004fde48dd --- /dev/null +++ b/server/src/__tests__/unikraft-http-boundary.test.ts @@ -0,0 +1,69 @@ +import { createServer, type Server } from "node:http"; +import { connect, type AddressInfo, type Socket } from "node:net"; +import { afterEach, describe, expect, it } from "vitest"; +import { executeUnikraftTool } from "../services/connectors/unikraft/client.js"; +import { guardedRemoteHttpFetch } from "../services/remote-http-fetch.js"; + +const credential = "unikraft-http-fixture-token"; +const servers: Server[] = [], sockets: Socket[] = []; +afterEach(async () => { + sockets.splice(0).forEach(socket => socket.destroy()); + await Promise.all(servers.splice(0).map(server => new Promise(resolve => server.close(() => resolve())))); +}); + +async function provider() { + const received: { method: string; url: string; host: string; contentType?: string; body: string }[] = []; + const server = createServer((req, res) => { + const chunks: Buffer[] = []; + req.on("data", chunk => chunks.push(chunk)); + req.on("end", () => { + received.push({ method: req.method!, url: req.url!, host: req.headers.host!, contentType: req.headers["content-type"], + body: Buffer.concat(chunks).toString("utf8") }); + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify({ status: "success", data: { instances: [] }, op_time_us: 1 })); + }); + }); + servers.push(server); + server.on("connection", socket => sockets.push(socket)); + await new Promise(resolve => server.listen(0, "127.0.0.1", resolve)); + const port = (server.address() as AddressInfo).port; + // Preserve the real pinned node:http path, including its body serializer. + // Only the network/TLS leg is a controlled local stand-in. + const request = (url: string, init: RequestInit) => { + const endpoint = new URL(url); + endpoint.protocol = "http:"; endpoint.port = String(port); + return guardedRemoteHttpFetch(endpoint, init, { + allowPrivateNetwork: false, lookup: async () => [{ address: "93.184.216.34", family: 4 }], + socketFactory: target => { + const socket = connect({ host: "127.0.0.1", port }); + sockets.push(socket); + Object.defineProperty(socket, "remoteAddress", { get: () => target.address, configurable: true }); + return socket; + }, + responseTimeoutMs: 5000, error: (message, code) => Object.assign(new Error(message), { code }), + }); + }; + const call = (toolName: string, parameters: unknown) => executeUnikraftTool({ + binding: { companyId: "company", agentId: "agent" }, connection: { companyId: "company", config: {} }, + headers: { Authorization: `Bearer ${credential}` }, request, toolName, parameters, + signal: AbortSignal.timeout(3000), deadlineAt: Date.now() + 3000, + }); + return { call, received }; +} + +describe("Unikraft production HTTP boundary", () => { + it.each([ + ["unikraft_get_instance_logs", "GET", { metro: "dal", query: { name: ["web"] }, body: [{ name: "web" }] }], + ["unikraft_delete_instances", "DELETE", { body: [{ name: "web" }] }], + ] as const)("sends %s JSON selectors as a %s request body through the pinned transport", async (toolName, method, parameters) => { + const p = await provider(); + await p.call(toolName, parameters); + expect(p.received).toHaveLength(1); + const [received] = p.received; + expect(received.method).toBe(method); + expect(received.host).toMatch(new RegExp(`^api\\.${parameters.metro ?? "fra"}\\.unikraft\\.cloud:`)); + expect(received.contentType).toBe("application/json"); + expect(JSON.parse(received.body)).toEqual([{ name: "web" }]); + if (method === "GET") expect(received.url).toBe("/v1/instances/log?name=web"); + }); +}); diff --git a/server/src/config.ts b/server/src/config.ts index 62c40bbbcf1..23b1ee84c51 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -91,6 +91,7 @@ export interface Config { nativeHarnessBackupExpirationEnabled: boolean; nativeHarnessBackupExpirationDryRun: boolean; nativeHarnessBackupExpirationDays: number; + gitRestorePointRetentionDays: number; serveUi: boolean; uiDevMiddleware: boolean; secretsProvider: SecretProvider; @@ -337,6 +338,7 @@ export function loadConfig(): Config { const skillVersionRetentionKeepNewest = nonNegativeIntegerEnv("PAPERCLIP_SKILL_VERSION_RETENTION_KEEP", 0); const skillVersionRetentionGraceMinutes = nonNegativeIntegerEnv("PAPERCLIP_SKILL_VERSION_RETENTION_GRACE_MINUTES", 60); const nativeHarnessBackupExpirationDays = Math.max(1, nonNegativeIntegerEnv("PAPERCLIP_NATIVE_HARNESS_BACKUP_EXPIRATION_DAYS", 7)); + const gitRestorePointRetentionDays = Math.max(1, nonNegativeIntegerEnv("PAPERCLIP_GIT_RESTORE_POINT_RETENTION_DAYS", 90)); const bindValidationErrors = validateConfiguredBindMode({ deploymentMode, deploymentExposure, @@ -394,6 +396,7 @@ export function loadConfig(): Config { nativeHarnessBackupExpirationEnabled: process.env.PAPERCLIP_NATIVE_HARNESS_BACKUP_EXPIRATION_ENABLED !== "false", nativeHarnessBackupExpirationDryRun: process.env.PAPERCLIP_NATIVE_HARNESS_BACKUP_EXPIRATION_DRY_RUN === "true", nativeHarnessBackupExpirationDays, + gitRestorePointRetentionDays, serveUi: process.env.SERVE_UI !== undefined ? process.env.SERVE_UI === "true" diff --git a/server/src/index.ts b/server/src/index.ts index ed7417f4915..6b6de3cb71b 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -12,6 +12,7 @@ import { NATIVE_HARNESS_BACKUP_EXPIRATION_STARTUP_DELAY_MS, nativeHarnessBackupExpirationService, } from "./services/native-runtime/native-harness-backup-expiration.js"; +import { gitRestorePointRetentionService, registerGitRestorePointWork } from "./services/git-restore-point-ledger.js"; import { subscribeAllCompanyLiveEvents } from "./services/live-events.js"; import { DELIVERY_QUEUES } from "./services/delivery-work-notifications.js"; import { registerBudgetEnforcementWork, registerCostAccountingWork } from "./services/budget-enforcement-worker.js"; @@ -1497,9 +1498,21 @@ async function startServerWithDatabaseTeardown( logger.error({ err }, "merged pull-request confirmation sweep failed"); })); }; + const gitRestorePointWorker = registerGitRestorePointWork(deliveryWork, gitRestorePointRetentionService(db as any, { + storage: storageService, + retentionDays: config.gitRestorePointRetentionDays, + }), (result) => { + if (result.abandoned > 0 || result.expired > 0 || result.orphaned > 0 || result.deleted > 0 || result.failed > 0) { + logger.info(result, "git restore point retention"); + } + }); let lastTerminalWorkspaceSkipLogAt = 0; const terminalWorkspaceWorker = registerTerminalWorkspaceWork(deliveryWork, { ...terminalWorkspaces, + terminalWorkspaceWorkSettled: (hints) => { + terminalWorkspaces.terminalWorkspaceWorkSettled?.(hints); + gitRestorePointWorker.issuesChanged(); + }, sweepTerminalWorkspaces: async (limit, signal, options) => { const result = await terminalWorkspaces.sweepTerminalWorkspaces(limit, signal, options); if (result.archived > 0 || result.cleanupFailed > 0 || result.compacted > 0 || result.compactionFailed > 0) { diff --git a/server/src/services/cloud-instance.ts b/server/src/services/cloud-instance.ts index 5cde4dd8ddc..afddae33956 100644 --- a/server/src/services/cloud-instance.ts +++ b/server/src/services/cloud-instance.ts @@ -7,6 +7,7 @@ const CLOUD_STAGING_EXPERIMENTAL_KEYS: ReadonlySet = new Set([ "enableBetaSkills", "enableBuiltInAgents", "enableCases", + "enableArtifactsV1", "enableChatConnectors", "enableClassicTaskInterface", "enableCombinedInboxTasks", diff --git a/server/src/services/company-deletion.ts b/server/src/services/company-deletion.ts index 30c524db9c2..432cad76e7a 100644 --- a/server/src/services/company-deletion.ts +++ b/server/src/services/company-deletion.ts @@ -4,6 +4,7 @@ import { withAccountHomeSecretMutationLock } from "@paperclipai/adapter-codex-lo import type { Db } from "@paperclipai/db"; import { eq, inArray } from "drizzle-orm"; import { createAgentLifecycle, processAgentLifecycle } from "./agent-lifecycle.js"; +import { requestRestorePointDeletion } from "./git-restore-point-ledger.js"; import { agentLifecycleCompanyDeletion } from "../modules/agent-lifecycle/company-deletion.js"; import { companies, @@ -109,6 +110,7 @@ export async function deleteCompany(db: Db, id: string) { await tx.delete(companyLogos).where(eq(companyLogos.companyId, id)); await tx.delete(assets).where(eq(assets.companyId, id)); await tx.delete(goals).where(eq(goals.companyId, id)); + await requestRestorePointDeletion(tx, { companyId: id }); await tx.delete(executionWorkspaces).where(eq(executionWorkspaces.companyId, id)); await tx.delete(projects).where(eq(projects.companyId, id)); await agentLifecycleCompanyDeletion.deleteCompanyData(tx, id); diff --git a/server/src/services/connectors/tool-policy.ts b/server/src/services/connectors/tool-policy.ts index 6b5cfe63d93..c8444474943 100644 --- a/server/src/services/connectors/tool-policy.ts +++ b/server/src/services/connectors/tool-policy.ts @@ -1,5 +1,5 @@ import type { ToolRiskLevel } from "@paperclipai/shared"; -import { daytonaRisk, isDaytonaConnection } from "../daytona.js"; +import { NATIVE_PROVIDER_CATALOGS } from "../native-provider-catalogs.js"; import { classifyRailwayToolRisk, isRailwayEndpoint } from "../railway.js"; import { braintrustRisk } from "./braintrust/risk.js"; import { enterpretRisk } from "./enterpret/risk.js"; @@ -34,12 +34,13 @@ const reviewedPolicies: ReviewedToolPolicy[] = [ // review and drift handling, without enabling the native API bridge. matchesEndpoint: connection => isRailwayEndpoint(connection.config.url), }, - { - sourceTemplateKey: "daytona", + // Native REST providers (Daytona, Unikraft) classify from their generated contract. + ...NATIVE_PROVIDER_CATALOGS.map((provider): ReviewedToolPolicy => ({ + sourceTemplateKey: provider.slug, quarantineNewEntries: true, - classifyRisk: tool => daytonaRisk(tool.name), - matchesConnection: isDaytonaConnection, - }, + classifyRisk: tool => provider.risk(tool.name), + matchesConnection: provider.isConnection, + })), { sourceTemplateKey: "enterpret", quarantineNewEntries: true, classifyRisk: enterpretRisk }, { sourceTemplateKey: "braintrust", quarantineNewEntries: true, classifyRisk: braintrustRisk }, ]; diff --git a/server/src/services/connectors/unikraft/client.ts b/server/src/services/connectors/unikraft/client.ts new file mode 100644 index 00000000000..edeea4e2f93 --- /dev/null +++ b/server/src/services/connectors/unikraft/client.ts @@ -0,0 +1,108 @@ +import { NATIVE_REST_MAX_BYTES, nativeCatalog, nativeRestFailure as failure, nativeRestPathValue, nativeRestRecord as record, + nativeRestSender, type NativeCatalogEntry, type NativeRestRequest } from "../../native-rest-client.js"; +import catalog from "./operations.generated.json" with { type: "json" }; + +export const UNIKRAFT_METHOD = "api-token"; +// Unikraft Cloud has no global platform endpoint; each metro serves its own API. +// The global control-plane URL identifies the connection; actions use the +// reviewed metro hosts below. +export const UNIKRAFT_CONNECTION_URL = "https://controlplane.unikraft.cloud/v1"; +export const UNIKRAFT_METROS: readonly string[] = catalog.metros; +export const UNIKRAFT_DEFAULT_METRO = "fra"; +type Operation = typeof catalog.tools[number]; +const contract = nativeCatalog(catalog.tools); + +export const UNIKRAFT_TOOLS = contract.tools; +export const isUnikraftToolName = contract.isToolName; +export const unikraftRisk = contract.risk; +export const isCurrentUnikraftCatalogEntry = (entry: NativeCatalogEntry) => contract.isCurrentEntry(entry); + +export function isUnikraftConnection(connection: { transport: string; config?: Record }) { + return connection.transport === "rest_api" && connection.config?.sourceTemplateKey === "unikraft" + && connection.config.connectionMethodKey === UNIKRAFT_METHOD && connection.config.url === UNIKRAFT_CONNECTION_URL; +} + +/** Only metros published in the reviewed specification receive the token. */ +export function unikraftApiUrl(metro: unknown) { + if (typeof metro !== "string" || !UNIKRAFT_METROS.includes(metro)) { + throw failure(422, "unikraft_metro_invalid", `Choose a Unikraft Cloud metro: ${UNIKRAFT_METROS.join(", ")}.`); + } + return `https://api.${metro}.unikraft.cloud`; +} + +function pathValue(value: unknown) { + return nativeRestPathValue(value, () => failure(422, "unikraft_arguments_invalid", "Invalid Unikraft path argument.")); +} + +/** A provider-focused REST bridge. It never reads host files, calls host shell + * commands, borrows CLI login state, or looks up Paperclip tasks. */ +export function unikraftClient(headers: Record, request: NativeRestRequest, defaultMetro: unknown = UNIKRAFT_DEFAULT_METRO, + signal: AbortSignal = AbortSignal.timeout(30_000), deadlineAt: number = Date.now() + 30_000) { + const authorization = headers.Authorization; + if (!authorization || !/^Bearer [^\r\n]+$/.test(authorization)) { + throw failure(422, "unikraft_credential_missing", "Reconnect Unikraft to restore its API token."); + } + const { send } = nativeRestSender({ provider: "Unikraft", code: "unikraft", authorization, request, signal, deadlineAt, + deniedMessage: "Unikraft denied access. Check that this token belongs to the intended organization and has not been revoked." }); + + async function probe() { + const value = record(await send(`${unikraftApiUrl(defaultMetro)}/v1/users/quotas`, { method: "GET" })); + if (value.status !== "success") throw failure(502, "unikraft_invalid_response", "Unikraft did not confirm this API token."); + return { metro: defaultMetro as string }; + } + + async function execute(operation: Operation, parameters: Record) { + let base = unikraftApiUrl(parameters.metro ?? defaultMetro); + if (operation.kind === "sandbox") base += `/v1/instances/${pathValue(parameters.instance)}/plugins/sandbox`; + const route = operation.path.replace(/\{([^}]+)\}/g, (_match, key: string) => pathValue(record(parameters.path)[key])); + const url = new URL(base + route); + const projectedHeaders: Record = {}; + for (const parameter of operation.parameters) { + if (parameter.group === "path") continue; + const value = record(parameters[parameter.group])[parameter.name]; + if (value === undefined || value === null) continue; + if (parameter.group === "headers") { + const text = String(value); + if (/[\r\n]/.test(text)) throw failure(422, "unikraft_arguments_invalid", "Invalid Unikraft header argument."); + projectedHeaders[parameter.name] = text; + } else if (Array.isArray(value)) { + if (parameter.explode) value.forEach(item => url.searchParams.append(parameter.name, String(item))); + else url.searchParams.append(parameter.name, value.join(",")); + } else url.searchParams.append(parameter.name, String(value)); + } + let body: string | undefined; + // Unikraft selects batch targets with JSON bodies, including on GET and + // DELETE. The pinned transport sends them as ordinary request bodies. + if (parameters.body !== undefined) { + body = JSON.stringify(parameters.body); + if (Buffer.byteLength(body) > NATIVE_REST_MAX_BYTES) throw failure(422, "unikraft_input_limit", "The request body exceeds the limit."); + projectedHeaders["Content-Type"] = "application/json"; + } + // The sandbox plugin documents 408 as "the command is still running when + // the wait timed out", an ordinary outcome rather than a failure. + const expected = operation.kind === "sandbox" && operation.path === "/commands/{uuid}/wait_timeout" + ? { 408: { status: "running", timedOut: true, message: "The command is still running. Wait again or check its logs." } } : {}; + // No automatic retry. A transport failure cannot prove a write did not run. + return send(url.toString(), { method: operation.method, headers: projectedHeaders, body }, {}, expected); + } + return { probe, execute }; +} + +type Binding = { companyId: string; agentId: string | null }; +type Connection = { companyId: string; config: Record }; +export async function executeUnikraftTool(input: { + binding: Binding; connection: Connection; headers: Record; + request: NativeRestRequest; toolName: string; parameters: unknown; + signal?: AbortSignal; + deadlineAt?: number; +}) { + if (!input.binding.agentId || input.connection.companyId !== input.binding.companyId) { + throw failure(404, "unikraft_connection_not_found", "Connection not found."); + } + const operation = contract.operation(input.toolName); + if (!operation) throw failure(404, "unikraft_tool_not_found", "Unikraft action not found."); + if (!contract.validate(operation.name, input.parameters)) throw failure(422, "unikraft_arguments_invalid", "Invalid Unikraft action arguments."); + const config = record(input.connection.config.methodConfig); + return unikraftClient(input.headers, input.request, config.metro ?? UNIKRAFT_DEFAULT_METRO, input.signal, input.deadlineAt) + .execute(operation, record(input.parameters)); +} diff --git a/server/src/services/connectors/unikraft/operations.generated.json b/server/src/services/connectors/unikraft/operations.generated.json new file mode 100644 index 00000000000..3a572def95e --- /dev/null +++ b/server/src/services/connectors/unikraft/operations.generated.json @@ -0,0 +1,11837 @@ +{ + "schemaVersion": 1, + "sources": { + "platform": { + "url": "https://raw.githubusercontent.com/unikraft-cloud/openapi/5dd107ea1cd8716b83cb86a83ed2a6ee46f99b5b/platform.json", + "sha256": "b9e3d4f304bdaf93e36089a13d44196ad0977d91f8e69fd010c5ddaf7d7a760b" + }, + "sandbox": { + "url": "https://raw.githubusercontent.com/unikraft-cloud/openapi/5dd107ea1cd8716b83cb86a83ed2a6ee46f99b5b/plugins/sandbox.json", + "sha256": "ac687a0f04d0fa286aa41fa577fe2e8870b55de6b9b60f83f5d6958ff012a2cb" + } + }, + "metros": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "tools": [ + { + "name": "unikraft_create_certificate", + "kind": "platform", + "path": "/v1/certificates", + "method": "POST", + "parameters": [], + "bodyContentType": "application/json", + "title": "Create Certificate", + "description": "Upload a new certificate.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Certificates.CreateCertificateRequest" + } + }, + "required": [ + "body" + ], + "definitions": { + "Certificates.CreateCertificateRequest": { + "type": "object", + "required": [ + "chain", + "pkey" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the certificate.\n\nThis is a human-readable name that can be used to identify the certificate.\nThe name must be unique within the context of your account. If no name is\nspecified, a random name is generated for you. The name can also be used\nto identify the certificate in API calls." + }, + "cn": { + "type": "string", + "description": "The common name (CN) of the certificate.\n\nDeprecated: Use `common_name` instead.", + "deprecated": true + }, + "common_name": { + "type": "string", + "description": "The common name (CN) of the certificate.\n\nThis must be a fully-qualified domain name (FQDN). Exactly one of `cn`\nor `common_name` must be specified." + }, + "chain": { + "type": "string", + "description": "The certificate chain in PEM format. Required for user-uploaded certificates." + }, + "pkey": { + "type": "string", + "description": "The private key in PEM format. Required for user-uploaded certificates." + } + }, + "description": "The request message for creating/uploading a new certificate." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_certificates", + "kind": "platform", + "path": "/v1/certificates", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "name", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "details", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "count", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "from", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "order", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "sortby", + "group": "query", + "style": "form", + "explode": false + } + ], + "bodyContentType": "application/json", + "title": "List Certificates", + "description": "List certificates.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "query": { + "type": "object", + "properties": { + "uuid": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The UUID of the resource." + }, + "name": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The name of the resource." + }, + "details": { + "type": "boolean" + }, + "count": { + "type": "integer", + "minimum": 0 + }, + "from": { + "type": "string" + }, + "order": { + "$ref": "#/definitions/Common.PaginationOrder" + }, + "sortby": { + "$ref": "#/definitions/Common.PaginationSortBy" + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [], + "definitions": { + "Common.PaginationOrder": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "description": "The sort order used by list endpoints." + }, + "Common.PaginationSortBy": { + "type": "string", + "enum": [ + "create_time" + ], + "description": "The sort field used by list endpoints." + }, + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_delete_certificates", + "kind": "platform", + "path": "/v1/certificates", + "method": "DELETE", + "parameters": [], + "bodyContentType": "application/json", + "title": "Delete Certificates", + "description": "Delete certificates by ID(s).", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_update_certificates", + "kind": "platform", + "path": "/v1/certificates", + "method": "PUT", + "parameters": [], + "bodyContentType": "application/json", + "title": "Update Certificates", + "description": "Update certificates.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Certificates.UpdateCertificatesRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Certificates.UpdateCertificatesRequestItem": { + "type": "object", + "required": [ + "chain", + "pkey" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "chain": { + "type": "string", + "description": "The new certificate chain. This is the public chain of the certificate in\nPEM format. The chain should include the certificate and any intermediate\ncertificates." + }, + "pkey": { + "type": "string", + "description": "The new private key. This is the private key of the certificate in PEM\nformat. The private key must match the public key in the certificate\nchain." + } + }, + "description": "A single update operation to be applied to a certificate." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_certificate_by_uuid", + "kind": "platform", + "path": "/v1/certificates/{uuid}", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Get Certificate by UUID", + "description": "Get a certificate by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_delete_certificate_by_uuid", + "kind": "platform", + "path": "/v1/certificates/{uuid}", + "method": "DELETE", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Delete Certificate by UUID", + "description": "Delete a certificate by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_update_certificate_by_uuid", + "kind": "platform", + "path": "/v1/certificates/{uuid}", + "method": "PUT", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Update Certificate by UUID", + "description": "Update a certificate by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Certificates.UpdateCertificateByUUIDRequestBody" + } + }, + "required": [ + "path", + "body" + ], + "definitions": { + "Certificates.UpdateCertificateByUUIDRequestBody": { + "type": "object", + "required": [ + "chain", + "pkey" + ], + "properties": { + "chain": { + "type": "string", + "description": "The new certificate chain. This is the public chain of the certificate in\nPEM format. The chain should include the certificate and any intermediate\ncertificates." + }, + "pkey": { + "type": "string", + "description": "The new private key. This is the private key of the certificate in PEM\nformat. The private key must match the public key in the certificate\nchain." + } + }, + "description": "The request body for updating a certificate by its UUID or name." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_healthz", + "kind": "platform", + "path": "/v1/healthz", + "method": "GET", + "parameters": [], + "title": "Health Check", + "description": "Return the status of a full-system health check.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [] + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_image_store", + "kind": "platform", + "path": "/v1/image-store", + "method": "GET", + "parameters": [ + { + "name": "digest", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "tag", + "group": "query", + "style": "form", + "explode": false + } + ], + "bodyContentType": "application/json", + "title": "Get Images in Store", + "description": "Retrieve all images in store.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "query": { + "type": "object", + "properties": { + "digest": { + "type": "string" + }, + "tag": { + "type": "string" + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Images.GetImagesRequestTagOrDigest" + } + } + }, + "required": [], + "definitions": { + "Images.GetImagesRequestTagOrDigest": { + "type": "object", + "properties": { + "digest": { + "type": "string" + }, + "tag": { + "type": "string" + } + }, + "oneOf": [ + { + "required": [ + "digest" + ] + }, + { + "required": [ + "tag" + ] + } + ] + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_images", + "kind": "platform", + "path": "/v1/images", + "method": "GET", + "parameters": [ + { + "name": "digest", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "tag", + "group": "query", + "style": "form", + "explode": false + } + ], + "bodyContentType": "application/json", + "title": "Get Images", + "description": "Retrieve all images.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "query": { + "type": "object", + "properties": { + "digest": { + "type": "string" + }, + "tag": { + "type": "string" + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Images.GetImagesRequestTagOrDigest" + } + } + }, + "required": [], + "definitions": { + "Images.GetImagesRequestTagOrDigest": { + "type": "object", + "properties": { + "digest": { + "type": "string" + }, + "tag": { + "type": "string" + } + }, + "oneOf": [ + { + "required": [ + "digest" + ] + }, + { + "required": [ + "tag" + ] + } + ] + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_pin_images", + "kind": "platform", + "path": "/v1/images", + "method": "POST", + "parameters": [], + "bodyContentType": "application/json", + "title": "Pin Images", + "description": "Pull and pin one or more images so they stay cached and are never\nevicted, without relying on an on-demand pull to succeed at instance\nstart. If a pull fails, the agent's error is returned in the entry's\n`message` field.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Images.PinImageRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Images.PinImageRequestItem": { + "type": "object", + "required": [ + "url", + "timeout_s" + ], + "properties": { + "url": { + "type": "string", + "description": "The image URL to pull and pin." + }, + "credentials": { + "type": "string", + "description": "Optional credentials for authenticating to an OCI registry.\nOnly valid for OCI registry URLs; the platform rejects this\nfield for non-OCI schemes." + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Optional HTTP headers to send when fetching the image." + }, + "pull_policy": { + "allOf": [ + { + "$ref": "#/definitions/Instances.PullPolicy" + } + ], + "description": "Controls when the image is pulled relative to what is already cached on\nthe node. If unset, this is inferred from the URL." + }, + "timeout_s": { + "type": "integer", + "description": "Number of seconds to wait for the pull to complete. Required and must\nbe non-zero; `-1` waits up to the platform's maximum timeout." + }, + "merge_requests": { + "type": "boolean", + "description": "Avoid duplicate pulls by merging with any in-flight request for the\nsame image. Defaults to `true`." + }, + "autokill": { + "allOf": [ + { + "$ref": "#/definitions/Images.PinImageRequestItemAutokill" + } + ], + "description": "Automatically unpin the image after a period of inactivity." + } + }, + "description": "The request item for pinning a single image." + }, + "Instances.PullPolicy": { + "type": "string", + "enum": [ + "always", + "if_not_present", + "never" + ], + "description": "PullPolicy defines when an image should be pulled." + }, + "Images.PinImageRequestItemAutokill": { + "type": "object", + "required": [ + "time_ms" + ], + "properties": { + "time_ms": { + "type": "integer", + "description": "Automatically unpin the image after this many milliseconds of\ninactivity. `0` (the default) disables this.", + "minimum": 0 + } + }, + "description": "Automatically unpin the image after a period of inactivity." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_unpin_images", + "kind": "platform", + "path": "/v1/images", + "method": "DELETE", + "parameters": [], + "bodyContentType": "application/json", + "title": "Unpin Images", + "description": "Unpin one or more images by UUID, making them eligible for normal\ncache eviction again. This does not delete the image.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Images.UnpinImageRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Images.UnpinImageRequestItem": { + "type": "object", + "required": [ + "uuid" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the image to unpin. Only UUID is supported; name, URL,\ntag, and digest are not." + } + }, + "description": "The request item for unpinning a single image." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_create_instance", + "kind": "platform", + "path": "/v1/instances", + "method": "POST", + "parameters": [], + "bodyContentType": "application/json", + "title": "Create Instance", + "description": "Create an instance in Unikraft Cloud.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Instances.CreateInstanceRequest" + } + }, + "required": [ + "body" + ], + "definitions": { + "Instances.CreateInstanceRequest": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the instance.\n\nIf not provided, a random name will be generated. The name must be\nunique." + }, + "image": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/Instances.ImageSpec" + } + ], + "description": "The image to use for the instance.\n\nEither an image or a template must be specified. Accepts either a plain\nimage reference string (`\"nginx:latest\"`) or an object carrying additional\npull configuration (`{\"url\": \"nginx:latest\", \"pull_policy\": \"always\"}`)." + }, + "args": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The arguments to pass to the instance when it starts." + }, + "env": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Environment variables to set for the instance." + }, + "memory_mb": { + "type": "integer", + "description": "Memory in MB to allocate for the instance. Default is 128." + }, + "service_group": { + "allOf": [ + { + "$ref": "#/definitions/Instances.CreateInstanceRequestServiceGroup" + } + ], + "description": "The service group configuration when creating an instance.\n\nWhen creating an instance, either a previously created (persistent)\nservice group can be referenced (either through its name or UUID), or a\nnew (ephemeral) service group can be created for the instance by\nspecifying the list of services it should expose and optionally the\ndomains it should use. Not used by template instances." + }, + "volumes": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.CreateInstanceRequestVolume" + }, + "description": "Volumes to attach to the instance.\n\nThis list can contain both existing and new volumes to create as part of\nthe instance creation. Existing volumes can be referenced by their name or\nUUID. New volumes can be created by specifying a name, size in MiB, and\nmount point in the instance. The mount point is the directory in the\ninstance where the volume will be mounted." + }, + "autostart": { + "type": "boolean", + "description": "Whether the instance should start automatically on creation.\nMust be set to true when `timeout_s` is specified." + }, + "replicas": { + "type": "integer", + "description": "Number of additional replicas to create. The total number of instances\ncreated is `replicas + 1`. Defaults to 0." + }, + "restart_policy": { + "allOf": [ + { + "$ref": "#/definitions/Instances.InstanceRestartPolicy" + } + ], + "description": "Restart policy for the instance. This defines how the instance\nshould behave when it stops or crashes. Cannot be combined with\nthe `delete-on-stop` feature." + }, + "scale_to_zero": { + "allOf": [ + { + "$ref": "#/definitions/Instances.CreateInstanceScaleToZero" + } + ], + "description": "Scale-to-zero configuration for the instance. Requires\n`service_group` to be set. Cannot be combined with the\n`delete-on-stop` feature." + }, + "vcpus": { + "type": "integer", + "description": "Number of vCPUs to allocate for the instance. Defaults to 1." + }, + "wait_timeout_ms": { + "type": "integer", + "description": "Deprecated: Use `timeout_s` instead. Timeout in milliseconds to\nwait for all new instances to reach running state. Requires\n`autostart` to be set. If `timeout_s` is not set, this value is\nconverted by rounding up to the next full second. No wait\nperformed for a value of 0.", + "deprecated": true + }, + "features": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.InstanceFeature" + }, + "description": "Features to enable for the instance. Features are specific\nconfigurations or capabilities that can be enabled for the\ninstance. The `scale-to-zero` and `delete-on-stop` features are\nmutually exclusive." + }, + "timeout_s": { + "type": "integer", + "description": "Timeout in seconds to wait for all new instances to reach running\nstate. Requires `autostart` to be set. If you autostart your\nnew instance, you can wait for it to finish starting with a\nblocking API call if you specify a wait timeout greater than\nzero. No wait performed for a value of 0." + }, + "roms": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.CreateInstanceRequestRom" + }, + "description": "Read-Only Memory (ROM) blobs to attach to the instance.\nUnikraft Cloud supports the ability to attach Read-Only Memory (ROM) blobs\nto instances. It allows you to create a general-purpose base image and\nthen customize individual instances by attaching code or data as separate\nROM blobs." + }, + "plugins": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.CreateInstanceRequestPlugin" + }, + "description": "Plugins to attach to the instance. Plugins let you attach small helper\nprograms to an instance and reach each one over a direct, authenticated\nHTTP endpoint. Each plugin loads from its own ROM image, mounts at\n`/uk/plugins/`, and is reachable at\n`.../v1/instances//plugins//`. At most 8 plugins\nmay be attached to an instance." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Tags to associate with the instance." + }, + "annotations": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Annotations to associate with the instance.\n\nUnlike tags, annotations also reach the guest: they are included in the\ninstance's startdata, and selected keys can be injected into the console\nlog output.\n\nKeys follow the Kubernetes annotation key syntax, `[/]`:\nthe optional prefix is a non-wildcard DNS subdomain of at most 253\ncharacters, and the name is at most 63 characters of `[-_.a-zA-Z0-9]`\nstarting and ending with an alphanumeric. Values are unconstrained apart\nfrom ASCII control characters. An instance holds at most 256 annotations.\n\nWhen the instance inherits annotations from a template, branch, or\ncheckpoint, the given annotations are merged into them rather than\nreplacing them. On a key clash the value given here wins." + }, + "template": { + "allOf": [ + { + "$ref": "#/definitions/Instances.CreateInstanceRequestTemplate" + } + ], + "description": "Template instances.\nAn existing instance can be saved as a template. This template is then\nused to create new instances that inherit the exact configuration and\nstate the original instance had when the template was created." + }, + "sched_priority": { + "allOf": [ + { + "$ref": "#/definitions/Common.SchedPriority" + } + ], + "description": "The scheduling priority for the instance. Only settable by\nusers with scheduling priority override permissions." + }, + "schedules": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.Schedule" + }, + "description": "Schedules for the instance. Scheduled operations let you automatically\nstart, stop, delete, or exec a command in the instance on a calendar-\nbased schedule. For `exec` schedules, set the `args` field to the command\nand its arguments. Each instance stores its own schedules, and cloning\npreserves them." + }, + "autokill": { + "allOf": [ + { + "$ref": "#/definitions/Instances.CreateInstanceRequestAutokill" + } + ], + "description": "Automatic delete-on-idle/request-limit configuration. Not used for\ntemplate instances." + }, + "hostname": { + "type": "string", + "description": "The hostname of the instance.\n\nIf not provided, the hostname will be set to the instance name. The\nhostname must be a valid DNS label (e.g., \"my-instance\") and is used for\ninternal DNS resolution within the Unikraft Cloud network." + }, + "dependencies": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + }, + "description": "Dependencies of the instance.\n\nA list of instance identifiers (name or UUID) that this instance depends\non. Dependencies define startup ordering and can be used to ensure that\nprerequisite instances are running before this instance starts." + }, + "branch_from": { + "allOf": [ + { + "$ref": "#/definitions/Common.NameOrUUID" + } + ], + "description": "Reference to an existing instance to branch from. The instance can be\nrunning or stopped. If the source instance is running, a snapshot will be\ntaken asynchronously and the new instance will wait for it to complete\nbefore starting. Mutually exclusive with `image` and `template`." + }, + "checkpoint": { + "allOf": [ + { + "$ref": "#/definitions/Common.NameOrUUID" + } + ], + "description": "Reference to an existing checkpoint to create the instance from. The\ncheckpoint must be in the `checkpoint` state. The new instance will be\ncreated with the same configuration and state as the checkpoint. Mutually\nexclusive with `image`, `template`, and `branch_from`." + }, + "gateway": { + "type": "string", + "description": "The default gateway to configure inside the guest." + }, + "nameserver": { + "type": "string", + "description": "The DNS resolver to configure inside the guest." + }, + "network_interfaces": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.CreateInstanceRequestNetworkInterface" + }, + "description": "Network interfaces to attach to the instance." + }, + "type": { + "allOf": [ + { + "$ref": "#/definitions/Instances.InstanceType" + } + ], + "description": "The type of virtual machine to use for the instance. Defaults to `micro`,\nwhich runs on Firecracker. `full` runs on QEMU instead and is required\nfor GPU passthrough (see `gpus`) and, in the future, Windows VMs. QEMU-\nbacked instances currently do not support scale-to-zero, templates,\nbranching, or checkpointing, and only support block-based volumes (no\nvirtiofs). Requires a plan with full VM support and cannot be combined\nwith `template`, `branch_from`, or `checkpoint`." + }, + "gpus": { + "type": "integer", + "minimum": 0, + "maximum": 1, + "description": "Number of GPUs to attach to the instance. Currently restricted to at most\n1. Requires `type` to be `full` and a plan with GPU support. A GPU stays\nassigned to the instance, even while stopped, until the instance is\ndeleted. Cannot be combined with `template`, `branch_from`, or\n`checkpoint`." + } + }, + "description": "The request message for creating a new instance." + }, + "Instances.ImageSpec": { + "type": "object", + "required": [ + "url" + ], + "properties": { + "url": { + "type": "string", + "description": "The image URL" + }, + "credentials": { + "type": "string", + "description": "Optional credentials for authenticating to an OCI registry.\nOnly valid for OCI registry URLs; the platform rejects this\nfield for non-OCI schemes." + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Optional HTTP headers to send when fetching the image." + }, + "pull_policy": { + "allOf": [ + { + "$ref": "#/definitions/Instances.PullPolicy" + } + ], + "description": "Controls when the image is pulled relative to what is already cached on\nthe node." + } + }, + "description": "ImageSpec describes an image to use for an instance." + }, + "Instances.PullPolicy": { + "type": "string", + "enum": [ + "always", + "if_not_present", + "never" + ], + "description": "PullPolicy defines when an image should be pulled." + }, + "Instances.CreateInstanceRequestServiceGroup": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "domains": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.CreateInstanceRequestDomain" + } + }, + "services": { + "type": "array", + "items": { + "$ref": "#/definitions/Services.Service" + } + }, + "soft_limit": { + "type": "integer", + "minimum": 0 + }, + "hard_limit": { + "type": "integer", + "minimum": 0 + } + }, + "description": "The service group configuration when creating an instance. If no existing\n(persistent) service group is specified via its identifier, a new\n(ephemeral) service group can be created by specifying the services it\nshould expose. A service defines the configuration settings of an exposed\nport by the instance. A service is a combination of a public port, an\ninternal port, and a set of handlers that define how the service will handle\nincoming connections.", + "oneOf": [ + { + "required": [ + "uuid" + ] + }, + { + "required": [ + "name" + ] + } + ] + }, + "Instances.CreateInstanceRequestDomain": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "Publicly accessible domain name.\n\nIf this name ends in a period `.` it must be a valid Full Qualified\nDomain Name (FQDN), e.g. `example.com.`; otherwise it will become a\nsubdomain of the target metro, e.g. `example` becomes\n`example.fra0.unikraft.app`." + }, + "certificate": { + "allOf": [ + { + "$ref": "#/definitions/Common.NameOrUUID" + } + ], + "description": "A reference to an existing certificate which can be used for the\nspecified domain. If unspecified, Unikraft Cloud will\nautomatically generate a new certificate for the domain based on Let's\nEncrypt and seek to accomplish a DNS-01 challenge." + } + }, + "description": "The domain configuration for the service group. A domain defines a publicly\naccessible domain name for the instance. If the domain name ends with a\nperiod `.`, it must be a valid Fully Qualified Domain Name (FQDN), otherwise\nit will become a subdomain of the target metro. The domain can be associated\nwith an existing certificate by specifying the certificate's name or UUID.\nIf no certificate is specified and a FQDN is provided, Unikraft Cloud will\nautomatically generate a new certificate for the domain based on Let's\nEncrypt and seek to accomplish a DNS-01 challenge." + }, + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + }, + "Services.Service": { + "type": "object", + "required": [ + "port" + ], + "properties": { + "port": { + "type": "integer", + "description": "This is the public-facing port that the service will be accessible from\non the Internet.", + "minimum": 0 + }, + "destination_port": { + "type": "integer", + "description": "The port number that the instance is listening on. This is the internal\nport which Unikraft Cloud will forward traffic to.", + "minimum": 0 + }, + "protocol": { + "$ref": "#/definitions/Services.ServiceProtocol" + }, + "ip": { + "type": "string" + }, + "handlers": { + "type": "array", + "items": { + "$ref": "#/definitions/Services.ConnectionHandler" + }, + "description": "Connection handlers to use for the service. Handlers define how the\nservice will handle incoming connections and forward traffic from the\nInternet to your application. For example, a service can be configured\nto terminate TLS connections, redirect HTTP traffic, or enable HTTP mode\nfor load balancing. You configure the handlers for every published\nservice port individually." + } + }, + "description": "A service connects a public-facing port to an internal destination port on\nwhich an application instance listens on. Additional handlers can be\ndefined for each published port in order to define how the service will\nhandle incoming connections and forward traffic from the Internet to your\napplication. For example, a service can be configured to terminate TLS\nconnections, redirect HTTP traffic, or enable HTTP mode for load balancing." + }, + "Services.ServiceProtocol": { + "type": "string", + "enum": [ + "tcp", + "udp" + ], + "description": "Protocol for a service." + }, + "Services.ConnectionHandler": { + "type": "string", + "enum": [ + "tls", + "http", + "redirect" + ], + "description": "Connection handlers to use for the service.\n\nHandlers define how the service will handle incoming connections and\nforward traffic from the Internet to your application. For example, a\nservice can be configured to terminate TLS connections, redirect HTTP\ntraffic, or enable HTTP mode for load balancing. You configure the handlers\nfor every published service port individually.\n\nThere are currently 3 supported handlers:\n\n| Handler | Description |\n|------------|-------------|\n| `tls` | Terminate the TLS connection at the Unikraft Cloud gateway using our wildcard certificate issued for the kraft.cloud domain. The gateway forwards the unencrypted traffic to your application. |\n| `http` | Enable HTTP mode on the load balancer to load balance on the level of individual HTTP requests. In this mode, only HTTP connections are accepted. If this option is not set the load balancer works in TCP mode and distributes TCP connections. |\n| `redirect` | Redirect traffic from the source port to the destination port. |\n\nNote that there is a set of constraints when publishing ports:\n\n- Port 80: MUST have \"http\" and MUST not have \"tls\" set;\n- Port 443: MUST have http and tls set;\n- The `redirect` handler can only be set on port 80 (HTTP) to redirect to\nport 443 (HTTPS);\n- All other ports MUST have tls and MUST not have http set." + }, + "Instances.CreateInstanceRequestVolume": { + "type": "object", + "required": [ + "at" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "size_mb": { + "type": "integer", + "minimum": 0 + }, + "host_path": { + "type": "string" + }, + "at": { + "type": "string" + }, + "readonly": { + "type": "boolean" + }, + "quota_policy": { + "type": "string" + }, + "filesystem": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "uid": { + "type": "integer", + "minimum": 0 + }, + "gid": { + "type": "integer", + "minimum": 0 + }, + "access_mode": { + "$ref": "#/definitions/Volumes.VolumeAccessMode" + }, + "args": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "description": "A volume defines a storage volume that can be attached to the instance.", + "oneOf": [ + { + "required": [ + "size_mb" + ] + }, + { + "required": [ + "host_path" + ] + } + ] + }, + "Volumes.VolumeAccessMode": { + "type": "string", + "enum": [ + "rwo", + "rox", + "rwx" + ], + "description": "VolumeAccessMode defines the access mode of a volume, which controls sharing\nbehavior and caching strategy." + }, + "Instances.InstanceRestartPolicy": { + "type": "string", + "enum": [ + "never", + "always", + "on-failure" + ], + "description": "The restart policy of an instance.\n\nWhen an instance stops either because the application exits or the instance\ncrashes, Unikraft Cloud can auto-restart your instance. Auto-restarts are\nperformed according to the restart policy configured for a particular\ninstance. The policy can have the following values:\n\n| Policy | Description |\n|--------------|-------------|\n| `never` | Never restart the instance (default). |\n| `always` | Always restart the instance when the stop is initiated from within the instance (i.e., the application exits or the instance crashes). |\n| `on-failure` | Only restart the instance if it crashes. |\n\nWhen an instance stops, the stop reason and the configured restart policy\nare evaluated to decide if a restart should be performed. Unikraft Cloud\nuses an exponential back-off delay (immediate, 5s, 10s, 20s, 40s, ..., 5m)\nto slow down restarts in tight crash loops. If an instance runs without\nproblems for 10s the back-off delay is reset and the restart sequence ends.\nThe `restart.attempt` attribute reported in counts the number of restarts\nperformed in the current sequence. The `restart.next_at` field indicates\nwhen the next restart will take place if a back-off delay is in effect. A\nmanual start or stop of the instance aborts the restart sequence and resets\nthe back-off delay." + }, + "Instances.CreateInstanceScaleToZero": { + "type": "object", + "properties": { + "policy": { + "allOf": [ + { + "$ref": "#/definitions/Instances.InstanceScaleToZeroPolicy" + } + ], + "description": "The specific policy to use for scaling the instance to zero." + }, + "stateful": { + "type": "boolean", + "description": "Whether the instance should be stateful when scaled to zero. If set to\ntrue, the instance will retain its state (e.g., RAM contents) when scaled\nto zero. This is useful for instances that need to maintain their state\nacross scale-to-zero operations. If set to false, the instance will lose\nits state when scaled to zero, and it will be restarted from scratch when\nscaled back up." + }, + "cooldown_time_ms": { + "type": "integer", + "description": "The cooldown time in milliseconds before the instance can be scaled to\nzero again. This is useful to prevent rapid scaling to zero and back up,\nwhich can lead to performance issues or resource exhaustion." + }, + "notify_time_ms": { + "type": "integer", + "description": "The notification time in milliseconds before the instance is scaled to\nzero. This allows the instance to perform any necessary cleanup or state\nsaving before being scaled down." + } + }, + "description": "Scale-to-zero configuration when creating an instance." + }, + "Instances.InstanceScaleToZeroPolicy": { + "type": "string", + "enum": [ + "off", + "on", + "idle" + ], + "description": "Scale-to-zero policy types to use when creating an instance.\n\nThe policy can be one of the following values:\n\n| Policy | Description |\n|--------|-------------|\n| `on` | The instance should be scaled-to-zero (when possible). |\n| `off` | The instance should never be scaled-to-zero. |\n| `idle` | The instance should be scaled-to-zero even with established but idle TCP connections. |" + }, + "Instances.InstanceFeature": { + "type": "string", + "enum": [ + "delete-on-stop", + "nested-virt" + ], + "description": "Features are specific configurations or capabilities that can be enabled for\nthe instance.\n\nThe list of available features to enable for the instance:\n\n| Feature | Description |\n|--------------------|-------------|\n| `delete-on-stop` | The instance will be deleted when it is stopped. This is useful for instances that are not needed after they are stopped, such as temporary or ephemeral instances. Cannot be combined with a `restart_policy` other than `never`. |\n| `nested-virt` | Expose virtualization extensions to the guest, so that it can run virtual machines of its own. Requires the `nested_virt` permission. |" + }, + "Instances.CreateInstanceRequestRom": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the ROM to use for the instance configuration." + }, + "image": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/Instances.ImageSpec" + } + ], + "description": "The image of the ROM to use for the instance configuration. Mutually\nexclusive with `files`. Accepts either a plain image reference string\n(`\"nginx:latest\"`) or an object carrying additional pull configuration\n(`{\"url\": \"nginx:latest\", \"pull_policy\": \"always\"}`)." + }, + "at": { + "type": "string", + "description": "The path at which the ROM should be automatically mounted inside the\ninstance. When set, the platform mounts the ROM device at the specified\npath so the guest does not need to mount it manually. When omitted, the\nROM is exposed as a raw block device and the guest is responsible for\nmounting it." + }, + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.InlineFile" + }, + "description": "Inline files to use as the ROM content. When specified, the platform\ncreates an EROFS image from the provided files. Mutually exclusive with\n`image`." + } + }, + "description": "A ROM to attach when creating an instance." + }, + "Common.InlineFile": { + "type": "object", + "required": [ + "path", + "data" + ], + "properties": { + "path": { + "type": "string", + "description": "The file path within the image." + }, + "encoding": { + "allOf": [ + { + "$ref": "#/definitions/Common.InlineDataEncoding" + } + ], + "description": "The encoding of the data field. Defaults to \"text\"." + }, + "data": { + "type": "string", + "description": "The file data, encoded according to the encoding field." + } + }, + "description": "An inline file entry represents a single file within an image." + }, + "Common.InlineDataEncoding": { + "type": "string", + "enum": [ + "text", + "base64" + ], + "description": "Encoding type for inline file data." + }, + "Instances.CreateInstanceRequestPlugin": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "The plugin name. It becomes the `` segment in the plugin\nendpoint (`.../plugins//`). A plugin name has a\nmaximum length of 63 characters and contains only letters (`a`-`z`,\n`A`-`Z`), digits (`0`-`9`), hyphen (`-`), and underscore (`_`)." + }, + "image": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/Instances.ImageSpec" + } + ], + "description": "The plugin's image. The platform loads the image, mounts it at\n`/uk/plugins/`, and runs its `init` program when the plugin\nstarts. Accepts either a plain image reference string\n(`\"user/myplugin:latest\"`) or an object carrying additional pull\nconfiguration (`{\"url\": \"user/myplugin:latest\", \"pull_policy\": \"always\"}`).\nExactly one of `image` and `rom` must be set." + }, + "rom": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/Instances.ImageSpec" + } + ], + "description": "The plugin's image, under its former name. The platform still accepts\nit and adds a deprecation warning to the response. Exactly one of\n`image` and `rom` must be set.\n\nDeprecated: Use `image` instead.", + "deprecated": true + }, + "config": { + "description": "Arbitrary JSON configuration that the platform passes to the plugin's\n`init` program on `STDIN`. Any JSON value works, including a string, a\nnumber, or an object." + } + }, + "description": "A helper program attached to the instance and reachable over a direct,\nauthenticated HTTP endpoint. A plugin runs inside the instance next to\nthe main application, loads from its own ROM image, and answers requests\nthat the Unikraft Cloud API forwards to it.", + "oneOf": [ + { + "required": [ + "image" + ] + }, + { + "required": [ + "rom" + ] + } + ] + }, + "Instances.CreateInstanceRequestTemplate": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "prepare": { + "type": "boolean", + "description": "Whether the instance needs to run in order to reach template state" + }, + "create_args": { + "allOf": [ + { + "$ref": "#/definitions/Instances.Instance" + } + ], + "description": "Configuration parameters to apply when building the new instance from the\nsource template." + }, + "prepare_timeout_s": { + "type": "integer", + "description": "Timeout in seconds for preparing the template before the preparation is\naborted. Only applies when `prepare` is set. A value of 0 means no\ntimeout." + }, + "autokill": { + "allOf": [ + { + "$ref": "#/definitions/Instances.TemplateAutokill" + } + ], + "description": "Automatic delete-on-idle configuration for the template. Only applies\nwhen `prepare` is set." + } + }, + "description": "Template configuration when creating an instance." + }, + "Instances.Instance": { + "type": "object", + "required": [ + "uuid", + "name", + "created_at", + "state", + "image", + "memory_mb", + "vcpus", + "restart_policy", + "type" + ], + "properties": { + "status": { + "allOf": [ + { + "$ref": "#/definitions/Common.ResponseStatus" + } + ], + "description": "Indicates whether the operation was successful for this item." + }, + "message": { + "type": "string", + "description": "An optional message providing additional information." + }, + "error": { + "type": "integer", + "description": "An optional error code." + }, + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The human-readable name of the resource." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "The time the instance was created." + }, + "state": { + "allOf": [ + { + "$ref": "#/definitions/Instances.InstanceState" + } + ], + "description": "The state of the instance. This indicates the current state of the\ninstance, such as whether it is running, stopped, or in an error state." + }, + "private_fqdn": { + "type": "string", + "description": "The internal hostname of the instance. This address can be used privately\nwithin the Unikraft Cloud network to access the instance. It is not\naccessible from the public Internet." + }, + "image": { + "type": "string", + "description": "The image used to create the instance. This is a reference to the\nUnikraft image that was used to create the instance." + }, + "memory_mb": { + "type": "integer", + "description": "The amount of memory in megabytes allocated for the instance. This is the\ntotal amount of memory that is available to the instance for its\noperations.", + "minimum": 0 + }, + "vcpus": { + "type": "integer", + "description": "The number of vCPUs allocated for the instance. This is the total\nnumber of virtual CPUs that are available to the instance for its\noperations.", + "minimum": 0 + }, + "args": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The arguments passed to the instance when it was started. This is a\nlist of command-line arguments that were provided to the instance at\nstartup. These arguments can be used to configure the behavior of the\ninstance and its applications." + }, + "env": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Environment variables set for the instance." + }, + "start_count": { + "type": "integer", + "description": "The total number of times the instance has been started. This is a counter\nthat increments each time the instance is started, regardless of whether it\nwas manually stopped or restarted. This can be useful for tracking the\nusage of the instance over time and/or for debugging purposes.\n\nNot used for template instances.", + "minimum": 0 + }, + "restart_count": { + "type": "integer", + "description": "The total number of times the instance has been restarted. This is a counter\nthat increments each time the instance has been restarted. This can be\nuseful for tracking the usage of the instance over time and/or for\ndebugging purposes.\nNot used for template instances.", + "minimum": 0 + }, + "started_at": { + "type": "string", + "format": "date-time", + "description": "The time the instance was started. This is the timestamp when the\ninstance was last started.\nNot used for template instances." + }, + "stopped_at": { + "type": "string", + "format": "date-time", + "description": "The time the instance was stopped. This is the timestamp when the\ninstance was last stopped. If the instance is currently running, this\nfield will be empty.\nNot used for template instances." + }, + "uptime_ms": { + "type": "integer", + "description": "The total amount of time the instance has been running in milliseconds.\nNot used for template instances.", + "minimum": 0 + }, + "retained_until": { + "type": "string", + "format": "date-time", + "description": "Time when the instance will be permanently deleted (for deleted instances)." + }, + "vmm_start_time_us": { + "type": "integer", + "description": "(Developer-only). The time taken between the main controller and the\nbeginning of execution of the VMM (Virtual Machine Monitor) measured in\nmicroseconds. This field is primarily used for debugging and performance\nanalysis purposes.\nNot used for template instances.", + "minimum": 0 + }, + "vmm_load_time_us": { + "type": "integer", + "description": "(Developer-only). The time it took the VMM (Virtual Machine Monitor) to\nload the instance's kernel and initramfs into VM memory measured in\nmicroseconds. This field is primarily used for debugging and performance\nanalysis purposes.\nNot used for template instances.", + "minimum": 0 + }, + "vmm_ready_time_us": { + "type": "integer", + "description": "(Developer-only). The time taken for the VMM (Virtual Machine Monitor) to\nbecome ready to execute the instance measured in microseconds. This is the\ntime from when the VMM started until it was ready to execute the instance's\ncode. This field is primarily used for debugging and performance analysis\npurposes.\nNot used for template instances.", + "minimum": 0 + }, + "boot_time_us": { + "type": "integer", + "description": "The boot time of the instance in microseconds. We take a pragmatic\napproach is to define the boot time. We calculate this as the difference\nin time between the moment the virtualization toolstack is invoked to\nrespond to a VM boot request and the moment the OS starts executing user\ncode (i.e., the end of the guest OS boot process). This is essentially the\ntime that a user would experience in a deployment, minus the application\ninitialization time, which we leave out since it is independent from the\nOS.\nNot used for template instances.", + "minimum": 0 + }, + "net_time_us": { + "type": "integer", + "description": "This is the time it took for the user-level application to start listening\non a non-localhost port measured in microseconds. This is the time from\nwhen the instance started until it reasonably ready to start responding to\nnetwork requests. This is useful for measuring the time it takes for the\ninstance to become operationally ready.\nNot used for template instances.", + "minimum": 0 + }, + "template_time_us": { + "type": "integer", + "description": "Template creation time in microseconds.", + "minimum": 0 + }, + "stop_reason": { + "type": "integer", + "description": "The instance stop reason.\n\nProvides reason as to why an instance is stopped or in the process of\nshutting down. The stop reason is a bitmask that tells you the origin of\nthe shutdown:\n\n| Bit | 4 | 3 | 2 | 1 | 0 (LSB) |\n|---------|------------|------------|------------|------------|--------------|\n| Purpose | [F]orced | [U]ser | [P]latform | [A]pp | [K]ernel |\n\n- **Forced**: This was a force stop. A forced stop does not give the\ninstance a chance to perform a clean shutdown. Bits 0\n(Kernel) and 1 (App) can thus never be set for forced\nshutdowns. Consequently, there won't be an `exit_code` or\n`stop_code`.\n- **User**: Stop initiated by user, e.g. via an API call.\n- **Platform**: Stop initiated by platform, e.g. an autoscale policy.\n- **App**: The Application exited. The `exit_code` field will be set.\n- **Kernel**: The kernel exited. The `stop_code` field will be set.\n\nFor example, the stop reason will contain the following values in the given\nscenarios:\n\n| Value | Bitmask | Aliases | Scenario |\n|-------|---------|---------|----------|\n| 28 | `11100` | `FUP--` | Forced user-initiated shutdown. |\n| 15 | `01111` | `-UPAK` | Regular user-initiated shutdown. The application and kernel have exited. The exit_code and stop_code indicate if the application and kernel shut down cleanly. |\n| 13 | `01101` | `-UP-K` | The user initiated a shutdown but the application was forcefully killed by the kernel during shutdown. This can be the case if the image does not support a clean application exit or the application crashed after receiving a termination signal. The exit_code won’t be present in this scenario. |\n| 7 | `00111` | `--PAK` | Unikraft Cloud initiated the shutdown, for example, due to scale-to-zero. The application and kernel have exited. The exit_code and stop_code indicate if the application and kernel shut down cleanly. |\n| 3 | `00011` | `---AK` | The application exited. The exit_code and stop_code indicate if the application and kernel shut down cleanly. |\n| 1 | `00001` | `----K` | The instance likely expierenced a fatal crash and the stop_code contains more information about the cause of the crash. |\n| 0 | `00000` | `-----` | The stop reason is unknown. |\nNot used for template instances.", + "minimum": 0 + }, + "exit_code": { + "type": "integer", + "description": "The application exit code.\n\nThis is the code which the application returns upon leaving its main entry\npoint. The encoding of the exit code is application specific. See the\ndocumentation of the application for more details. Usually, an exit code\nof `0` indicates success / no failure.\nNot used for template instances.", + "minimum": 0 + }, + "stop_code": { + "type": "integer", + "description": "The kernel stop code.\n\nThis value encodes multiple details about the stop irrespective of the\napplication.\n\n```\nMSB LSB\n┌──────────────┬──────────┬──────────┬───────────┬────────┐\n│ 31 ────── 24 │ 23 ── 16 │ 15 │ 14 ──── 8 │ 7 ── 0 │\n├──────────────┼──────────┼──────────┼───────────┼────────┤\n│ reserved[^1] │ errno │ shutdown │ initlevel │ reason │\n└──────────────┴──────────┴──────────┴───────────┴────────┘\n```\n\n- **errno**: The application errno, using Linux's errno.h values.\n(Optional, can be 0.)\n- **shutdown**: Whether the shutdown originated from the inittable (0) or\nfrom the termtable (1).\n- **initlevel**: The initlevel at the time of the stop.\n- **reason**: The reason for the stop. See `StopCodeReason`.\n\n[^1]: Reserved for future use.\nNot used for template instances.", + "minimum": 0 + }, + "restart_policy": { + "allOf": [ + { + "$ref": "#/definitions/Instances.InstanceRestartPolicy" + } + ], + "description": "The restart configuration for the instance.\n\nWhen an instance stops either because the application exits or the instance\ncrashes, Unikraft Cloud can auto-restart your instance. Auto-restarts are\nperformed according to the restart policy configured for a particular\ninstance.\n\nThe policy can have the following values:\n\n| Policy | Description |\n|--------------|-------------|\n| `never` | Never restart the instance (default). |\n| `always` | Always restart the instance when the stop is initiated from within the instance (i.e., the application exits or the instance crashes). |\n| `on-failure` | Only restart the instance if it crashes. |\n\nWhen an instance stops, the stop reason and the configured restart policy\nare evaluated to decide if a restart should be performed. Unikraft Cloud\nuses an exponential back-off delay (immediate, 5s, 10s, 20s, 40s, ..., 5m)\nto slow down restarts in tight crash loops. If an instance runs without\nproblems for 10s the back-off delay is reset and the restart sequence ends.\n\nThe `restart.attempt` attribute reported in counts the number of restarts\nperformed in the current sequence. The `restart.next_at` field indicates\nwhen the next restart will take place if a back-off delay is in effect.\n\nA manual start or stop of the instance aborts the restart sequence and\nresets the back-off delay." + }, + "scale_to_zero": { + "allOf": [ + { + "$ref": "#/definitions/Instances.InstanceScaleToZero" + } + ], + "description": "The scale-to-zero configuration for the instance.\n\nWith conventional cloud platforms you need to keep at least one instance\nrunning at all times to be able to respond to incoming requests. Performing\na just-in-time cold boot is simply too time-consuming and would create a\nresponse latency of multiple seconds. This is not the case with Unikraft\nCloud. Instances on Unikraft Cloud are able to cold boot within\nmilliseconds, which allows us to perform low-latency scale-to-zero.\n\nTo enable scale-to-zero for an instance it is sufficient to add a\n`scale_to_zero` configuration block. Unikraft Cloud will then put the\ninstance into standby if there is no traffic to your service within the\nwindow of a cooldown period. When there is new traffic coming in, it is\nautomatically started again.\n\nIf you have a heavyweight application that takes long to cold boot or has\nbad first request latency (e.g., with JIT compilation) consider to enable\nstateful scale-to-zero." + }, + "service_group": { + "allOf": [ + { + "$ref": "#/definitions/Instances.InstanceServiceGroup" + } + ], + "description": "The service group configuration for the instance." + }, + "services": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Services exposed by this instance (format: \"protocol:port\")." + }, + "volumes": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.InstanceVolume" + }, + "description": "The list of volumes attached to the instance." + }, + "network_interfaces": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.InstanceNetworkInterface" + }, + "description": "The network interfaces of the instance.\nNot used for template instances." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The tags associated with the instance." + }, + "annotations": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "The annotations associated with the instance.\n\nKeys follow the Kubernetes annotation key syntax, `[/]`: the\noptional prefix is a non-wildcard DNS subdomain of at most 253 characters,\nand the name is at most 63 characters of `[-_.a-zA-Z0-9]` starting and\nending with an alphanumeric. Values are unconstrained apart from ASCII\ncontrol characters, which are rejected because they would corrupt the\nconsole log output annotations can be forwarded to.\n\nAn instance holds at most 256 annotations." + }, + "snapshot": { + "allOf": [ + { + "$ref": "#/definitions/Instances.InstanceSnapshot" + } + ], + "description": "The snapshot of the instance, if exists." + }, + "delete_lock": { + "type": "boolean", + "description": "If set to true, the instance cannot be deleted until the lock is removed." + }, + "features": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.InstanceFeature" + } + }, + "restart": { + "allOf": [ + { + "$ref": "#/definitions/Instances.InstanceRestartAttempt" + } + ], + "description": "The current restart attempt for the instance.\nNot used for template instances." + }, + "roms": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.InstanceRom" + }, + "description": "Read-Only Memory (ROM) blobs to attach to the instance.\nUnikraft Cloud supports the ability to attach Read-Only Memory (ROM) blobs\nto instances. It allows you to create a general-purpose base image and\nthen customize individual instances by attaching code or data as separate\nROM blobs." + }, + "plugins": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.InstancePlugin" + }, + "description": "Plugins attached to the instance. Plugins let you attach small helper\nprograms to an instance and reach each one over a direct, authenticated\nHTTP endpoint. Each plugin loads from its own ROM image, mounts at\n`/uk/plugins/`, and is reachable at\n`.../v1/instances//plugins//`. At most 8\nplugins may be attached to an instance." + }, + "schedules": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.Schedule" + }, + "description": "Scheduled operations for this instance.\n\nEach schedule defines a calendar expression and an action (`start`,\n`stop`, `delete`, or `exec`) to perform at matching times. When the\naction is `exec`, the `args` field of the schedule specifies the command\nto run inside the instance." + }, + "autokill": { + "allOf": [ + { + "$ref": "#/definitions/Instances.InstanceAutokill" + } + ], + "description": "Automatic delete-on-idle/request-limit configuration.\nNot used for template instances." + }, + "updates": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.InstancePendingUpdate" + }, + "description": "Queued property changes awaiting application." + }, + "sched_priority": { + "allOf": [ + { + "$ref": "#/definitions/Common.SchedPriority" + } + ], + "description": "The scheduling priority for the instance. Only present for\nusers with scheduling priority override permissions." + }, + "template_autokill": { + "allOf": [ + { + "$ref": "#/definitions/Instances.InstanceTemplateAutokill" + } + ], + "description": "Template-specific automatic delete-on-idle configuration.\nNot used for non-template instances." + }, + "checkpoint_autokill": { + "allOf": [ + { + "$ref": "#/definitions/Instances.InstanceTemplateAutokill" + } + ], + "description": "Checkpoint-specific automatic delete-on-idle configuration.\nOnly used for checkpoint instances." + }, + "private_ip": { + "type": "string", + "description": "The private IP address of the instance." + }, + "gateway": { + "type": "string", + "description": "The default gateway configured inside the guest." + }, + "nameserver": { + "type": "string", + "description": "The DNS resolver configured inside the guest." + }, + "type": { + "allOf": [ + { + "$ref": "#/definitions/Instances.InstanceType" + } + ], + "description": "The type of virtual machine used to run the instance." + }, + "gpus": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.InstanceGpu" + }, + "description": "GPUs attached to the instance. Only present for instances of type\n`full`." + } + }, + "description": "Instance with per-item response envelope fields merged in." + }, + "Common.ResponseStatus": { + "type": "string", + "enum": [ + "success", + "error", + "partial_success" + ], + "description": "The response status of an API request." + }, + "Instances.InstanceState": { + "type": "string", + "enum": [ + "stopped", + "starting", + "running", + "draining", + "stopping", + "template", + "standby", + "deleted", + "checkpoint" + ], + "description": "The current state of an instance." + }, + "Instances.InstanceScaleToZero": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Indicates whether scale-to-zero is enabled for the instance." + }, + "policy": { + "allOf": [ + { + "$ref": "#/definitions/Instances.InstanceScaleToZeroPolicy" + } + ], + "description": "The specific policy to use for scaling the instance to zero." + }, + "stateful": { + "type": "boolean", + "description": "Whether the instance should be stateful when scaled to zero. If set to\ntrue, the instance will retain its state (e.g., RAM contents) when scaled\nto zero. This is useful for instances that need to maintain their state\nacross scale-to-zero operations. If set to false, the instance will lose\nits state when scaled to zero, and it will be restarted from scratch when\nscaled back up." + }, + "cooldown_time_ms": { + "type": "integer", + "description": "The cooldown time in milliseconds before the instance can be scaled to\nzero again. This is useful to prevent rapid scaling to zero and back up,\nwhich can lead to performance issues or resource exhaustion." + }, + "notify_time_ms": { + "type": "integer", + "description": "The notification time in milliseconds before the instance is scaled to\nzero. This allows the instance to perform any necessary cleanup or state\nsaving before being scaled down." + } + }, + "description": "Scale-to-zero defines the configuration for scaling the instance to zero.\nWhen an instance is scaled-to-zero it can be either stopped (and fully\nshutdown) or paused wherein the state of the instance is preserved (e.g.,\nRAM contents) and the instance can be resumed later without losing its\nstate, i.e. \"stateful\"." + }, + "Instances.InstanceServiceGroup": { + "type": "object", + "required": [ + "uuid", + "name" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The human-readable name of the resource." + }, + "domains": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.ServiceGroupInstanceDomain" + }, + "description": "The domain configuration for the service group." + } + }, + "description": "The service group configuration for the instance. This is a reference to the\nservice group that the instance is part of. The service group defines the\nservices (e.g. ports, connection handling) that the instance exposes and how\nthey are configured." + }, + "Instances.ServiceGroupInstanceDomain": { + "type": "object", + "required": [ + "fqdn" + ], + "properties": { + "fqdn": { + "type": "string", + "description": "Publicly accessible domain name.\n\nIf this name ends in a period `.` it must be a valid Full Qualified\nDomain Name (FQDN), otherwise it will become a subdomain of the target\nmetro." + }, + "certificate": { + "allOf": [ + { + "$ref": "#/definitions/Common.NameOrUUID" + } + ], + "description": "The certificate associated with the domain.\n\nThe certificate is used to secure the domain with TLS/SSL. If no\ncertificate is specified, Unikraft Cloud will automatically generate a\nnew certificate for the domain based on Let's Encrypt and seek to\naccomplish a DNS-01 challenge." + } + }, + "description": "The domain configuration for the service group. Domain names are completely\nspecified with all labels in the hierarchy of the DNS, having no parts\nomitted. The domain can be associated with an existing certificate by\nspecifying the certificate's name or UUID. If no certificate is specified\nand a FQDN is provided, Unikraft Cloud will automatically generate a new\ncertificate for the domain based on Let's Encrypt and seek to accomplish a\nDNS-01 challenge." + }, + "Instances.InstanceVolume": { + "type": "object", + "required": [ + "uuid", + "name", + "at", + "readonly" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The human-readable name of the resource." + }, + "at": { + "type": "string", + "description": "The mount point of the volume in the instance. This is the directory in\nthe instance where the volume will be mounted." + }, + "readonly": { + "type": "boolean", + "description": "Whether the volume is read-only or not." + } + }, + "description": "A volume defines a storage which can be attached to the instance. Volumes\ncan be used to store persistent data which should remain available even if\nthe instance is stopped or restarted." + }, + "Instances.InstanceNetworkInterface": { + "type": "object", + "required": [ + "uuid", + "private_ip", + "mac" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "private_ip": { + "type": "string", + "description": "The private IP address of the network interface. This is the internal IP\naddress that is used for communication between instances within the same\nnetwork." + }, + "mac": { + "type": "string", + "description": "The MAC address of the network interface." + }, + "name": { + "type": "string", + "description": "The interface name. If omitted, Unikraft Cloud generates one as\n-ethX, falling back to eth- when the\ninstance name is too long." + }, + "tap_name": { + "type": "string", + "description": "The TAP device to attach the interface. Provide it together with ip." + }, + "autoconfig": { + "type": "boolean", + "description": "Whether the interface is automatically configured inside the guest\n(IP address, routes, etc.). When absent or true, autoconfiguration\nis enabled. Present and false when the guest is expected to\nconfigure the interface manually." + }, + "relay": { + "allOf": [ + { + "$ref": "#/definitions/Instances.InstanceNetworkInterfaceRelay" + } + ], + "description": "Relay configuration for this interface." + } + }, + "description": "A network interface attached to an instance." + }, + "Instances.InstanceNetworkInterfaceRelay": { + "type": "object", + "required": [ + "uuid", + "name", + "relay_dns" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "UUID of the relay interface." + }, + "name": { + "type": "string", + "description": "Name of the relay interface." + }, + "relay_dns": { + "type": "boolean", + "description": "Whether DNS traffic is relayed through this interface.\nDefaults to true." + } + }, + "description": "An object that routes all traffic through another interface." + }, + "Instances.InstanceSnapshot": { + "type": "object", + "required": [ + "uuid" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "description": "A snapshot reference for an instance." + }, + "Instances.InstanceRestartAttempt": { + "type": "object", + "required": [ + "attempt" + ], + "properties": { + "attempt": { + "type": "integer", + "description": "Current restart attempt number. This is incremented each time the instance\nis restarted automatically by the platform.", + "minimum": 0 + }, + "next_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp of the next scheduled restart attempt." + } + }, + "description": "Restart attempt information for an instance." + }, + "Instances.InstanceRom": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the ROM to use for the instance configuration." + }, + "image": { + "type": "string", + "description": "The image of the ROM to use for the instance configuration. Mutually\nexclusive with `files`." + }, + "at": { + "type": "string", + "description": "The path at which the ROM should be automatically mounted inside the\ninstance. When set, the platform mounts the ROM device at the specified\npath so the guest does not need to mount it manually. When omitted, the\nROM is exposed as a raw block device and the guest is responsible for\nmounting it." + }, + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.InlineFile" + }, + "description": "Inline files to use as the ROM content. When specified, the platform\ncreates an EROFS image from the provided files. Mutually exclusive with\n`image`." + } + }, + "description": "A ROM attached to an instance." + }, + "Instances.InstancePlugin": { + "type": "object", + "required": [ + "name", + "image" + ], + "properties": { + "name": { + "type": "string", + "description": "The plugin name. It becomes the `` segment in the plugin\nendpoint (`.../plugins//`). A plugin name has a\nmaximum length of 63 characters and contains only letters (`a`-`z`,\n`A`-`Z`), digits (`0`-`9`), hyphen (`-`), and underscore (`_`)." + }, + "image": { + "type": "string", + "description": "The plugin's image, given as an image reference string such as\n`user/myplugin:latest`. The platform loads the image, mounts it at\n`/uk/plugins/`, and runs its `init` program when the\nplugin starts." + }, + "config": { + "description": "Arbitrary JSON configuration that the platform passes to the plugin's\n`init` program on `STDIN`. Any JSON value works, including a string, a\nnumber, or an object." + } + }, + "description": "A helper program attached to the instance and reachable over a direct,\nauthenticated HTTP endpoint. A plugin runs inside the instance next to\nthe main application, loads from its own ROM image, and answers requests\nthat the Unikraft Cloud API forwards to it." + }, + "Instances.Schedule": { + "type": "object", + "required": [ + "name", + "when", + "action" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the schedule.\n\nMust be unique within an instance." + }, + "when": { + "type": "string", + "description": "The calendar expression specifying when the action should be performed.\n\nUses systemd calendar event syntax.\nSee https://www.man7.org/linux/man-pages/man7/systemd.time.7.html" + }, + "action": { + "allOf": [ + { + "$ref": "#/definitions/Instances.ScheduleAction" + } + ], + "description": "The action to perform at the scheduled time." + }, + "next_at": { + "type": "string", + "format": "date-time", + "description": "The timestamp of when the next scheduled action will occur.\n\nThis field is populated only in responses (not settable in requests).\nOmitted if no next execution is scheduled.", + "readOnly": true + }, + "args": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The command to execute when the action is `exec`.\n\nRequired when `action` is `SCHEDULE_ACTION_EXEC`, ignored otherwise.\nEach element is a separate argument; the first element is the executable." + } + }, + "description": "A schedule defines when an action should be performed on an instance.\n\nEach schedule specifies a name, a calendar expression following systemd\ncalendar event syntax, and an action (start, stop, delete, or exec).\n\nCalendar expressions format:\n\n[weekday] [[year-]month-day] [hour:minute[:second]]\n\nSupported syntax:\n\n- `*` - Any value\n- `5` - Exact value\n- `1..5` - Range\n- `1..5/2` - Range with step\n- `1,2,5` - Comma-separated list\n\nExample: `*-*-* 09:00:00` - Every day at 09:00 UTC\nExample: `Sat,Sun *-*-* 20:00:00` - Every Saturday and Sunday at 20:00 UTC" + }, + "Instances.ScheduleAction": { + "type": "string", + "enum": [ + "start", + "stop", + "delete", + "exec" + ], + "description": "The action to perform on a scheduled operation." + }, + "Instances.InstanceAutokill": { + "type": "object", + "properties": { + "time_ms": { + "type": "integer", + "description": "Time in milliseconds after the instance was stopped before it is deleted.\nA value of 0 disables time-based autokill.", + "minimum": 0 + }, + "num_requests": { + "type": "integer", + "description": "Maximum number of requests/connections the instance serves before it is\ndeleted. A value of 0 disables request-based autokill.", + "minimum": 0 + } + }, + "description": "Automatic delete-on-idle/request-limit configuration for non-template\ninstances. Not used for template instances." + }, + "Instances.InstancePendingUpdate": { + "type": "object", + "required": [ + "prop", + "op", + "value", + "status" + ], + "properties": { + "prop": { + "allOf": [ + { + "$ref": "#/definitions/Instances.MutableInstanceProperty" + } + ], + "description": "The property being updated." + }, + "op": { + "allOf": [ + { + "$ref": "#/definitions/Instances.MutableInstanceOperation" + } + ], + "description": "The patch operation type." + }, + "value": { + "description": "The new value for the property. Type depends on the property being updated." + }, + "status": { + "type": "string", + "enum": [ + "pending", + "failed" + ], + "description": "The status of this update." + }, + "error": { + "type": "string", + "description": "Error message. Only present when status is \"failed\"." + } + }, + "description": "A queued property change awaiting application on an instance." + }, + "Instances.MutableInstanceProperty": { + "type": "string", + "enum": [ + "image", + "args", + "env", + "memory_mb", + "vcpus", + "scale_to_zero", + "tags", + "delete_lock", + "schedules", + "autokill", + "hostname", + "roms", + "dependencies", + "sched_priority", + "plugins", + "annotations" + ], + "description": "The mutable properties of an instance that can be updated." + }, + "Instances.MutableInstanceOperation": { + "type": "string", + "enum": [ + "set", + "add", + "del" + ], + "description": "Mutable instance operations." + }, + "Common.SchedPriority": { + "type": "string", + "enum": [ + "normal", + "medium", + "high", + "admin" + ], + "description": "SchedPriority defines the scheduling priority for an instance.\nUser requires the `override_vm_priority` permission to change it.\n\nThe list of available scheduling priorities:\n\n| Priority | Description |\n|----------|-------------|\n| `normal` | Default scheduling priority. |\n| `medium` | Medium scheduling priority. |\n| `high` | High scheduling priority. |\n| `admin` | Admin scheduling priority. |" + }, + "Instances.InstanceTemplateAutokill": { + "type": "object", + "properties": { + "time_ms": { + "type": "integer", + "description": "Time in milliseconds after the template was last used for cloning before\nit is deleted. A value of 0 disables template autokill.", + "minimum": 0 + } + }, + "description": "Autokill configuration for a template instance." + }, + "Instances.InstanceType": { + "type": "string", + "enum": [ + "micro", + "full" + ], + "description": "The type of virtual machine used to run an instance.\n\n| Type | Description |\n|---------|-------------|\n| `micro` | A lightweight microVM (default). Boots in milliseconds and is suitable for most workloads. |\n| `full` | A full virtual machine with broader hardware support, such as GPU passthrough. Requires a plan with full VM support. |" + }, + "Instances.InstanceGpu": { + "type": "object", + "required": [ + "uuid", + "model" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the GPU." + }, + "model": { + "type": "string", + "description": "The GPU model, given as its PCI vendor and device ID in the form\n`:`." + } + }, + "description": "A GPU attached to the instance." + }, + "Instances.TemplateAutokill": { + "type": "object", + "properties": { + "time_ms": { + "type": "integer", + "description": "Time in milliseconds after the template was last used for cloning before\nit is deleted. A value of 0 disables template autokill.", + "minimum": 0 + } + }, + "description": "Automatic delete-on-idle configuration for the template instance. Only\napplies when `prepare` is set." + }, + "Instances.CreateInstanceRequestAutokill": { + "type": "object", + "properties": { + "time_ms": { + "type": "integer", + "description": "Time in milliseconds after the instance was stopped before it is deleted.\nA value of 0 disables time-based autokill.", + "minimum": 0 + }, + "num_requests": { + "type": "integer", + "description": "Maximum number of requests/connections the instance serves before it is\ndeleted. A value of 0 disables request-based autokill.", + "minimum": 0 + } + }, + "description": "Automatic delete-on-idle/request-limit configuration for non-template instances." + }, + "Instances.CreateInstanceRequestNetworkInterface": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The interface name. If omitted, Unikraft Cloud generates one as\n-ethX, falling back to eth- when the\ninstance name is too long." + }, + "tap_name": { + "type": "string", + "description": "The TAP device to attach the interface to. Provide it together\nwith ip." + }, + "ip": { + "type": "string", + "description": "The interface IP address in CIDR notation. Provide it together\nwith tap_name to bring your own interface." + }, + "mac": { + "type": "string", + "description": "The interface MAC address. Provide it together with tap_name. Must be\na unicast address outside the platform's address pool." + }, + "autoconfig": { + "type": "boolean", + "description": "Whether the guest configures the interface itself. Defaults to true." + }, + "relay": { + "allOf": [ + { + "$ref": "#/definitions/Instances.NetworkInterfaceRelay" + } + ], + "description": "Relay configuration for this interface." + } + }, + "description": "Network interface configuration for a new instance." + }, + "Instances.NetworkInterfaceRelay": { + "type": "object", + "properties": { + "relay_dns": { + "type": "boolean", + "description": "Whether the relay forwards DNS requests. Set to false\nto let the default DNS server handle them instead.\nDefaults to true." + }, + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An object that routes all traffic through another interface.", + "oneOf": [ + { + "required": [ + "uuid" + ] + }, + { + "required": [ + "name" + ] + } + ] + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_instances", + "kind": "platform", + "path": "/v1/instances", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "name", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "details", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "count", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "from", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "order", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "sortby", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "tags", + "group": "query", + "style": "form", + "explode": false + } + ], + "bodyContentType": "application/json", + "title": "List Instances", + "description": "Get one or many instances with their current status and configuration.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "query": { + "type": "object", + "properties": { + "uuid": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The UUID of the resource." + }, + "name": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The name of the resource." + }, + "details": { + "type": "boolean" + }, + "count": { + "type": "integer", + "minimum": 0 + }, + "from": { + "type": "string" + }, + "order": { + "$ref": "#/definitions/Common.PaginationOrder" + }, + "sortby": { + "$ref": "#/definitions/Common.PaginationSortBy" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [], + "definitions": { + "Common.PaginationOrder": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "description": "The sort order used by list endpoints." + }, + "Common.PaginationSortBy": { + "type": "string", + "enum": [ + "create_time" + ], + "description": "The sort field used by list endpoints." + }, + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_delete_instances", + "kind": "platform", + "path": "/v1/instances", + "method": "DELETE", + "parameters": [], + "bodyContentType": "application/json", + "title": "Delete Instances", + "description": "Delete instances by ID(s).", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.DeleteInstanceRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Instances.DeleteInstanceRequestItem": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "timeout_s": { + "type": "integer", + "description": "Timeout in seconds to wait for the instance to be deleted. No wait\nperformed for a value of 0." + }, + "dont_retain": { + "type": "boolean", + "description": "Delete immediately without retention. If the instance is already\nbeing retained, this will force its deletion. Ignored if retention\nfor instances is not configured." + } + }, + "description": "The request item for deleting an instance by its UUID or name." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_update_instances", + "kind": "platform", + "path": "/v1/instances", + "method": "PATCH", + "parameters": [], + "bodyContentType": "application/json", + "title": "Update Instances", + "description": "Update instances by ID(s).", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.UpdateInstancesRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Instances.UpdateInstancesRequestItem": { + "type": "object", + "required": [ + "prop", + "op" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "id": { + "type": "string", + "description": "A client-provided identifier for tracking this operation in the response." + }, + "prop": { + "allOf": [ + { + "$ref": "#/definitions/Instances.MutableInstanceProperty" + } + ], + "description": "The property to modify." + }, + "op": { + "allOf": [ + { + "$ref": "#/definitions/Instances.MutableInstanceOperation" + } + ], + "description": "The operation to perform on the property." + }, + "value": { + "description": "The value for the update operation. The type depends on the property and operation:\n- For \"image\": image reference string, or object with image url,\ncredentials, headers and pull policy\n- For \"args\": string or array of strings\n- For \"env\": object (for SET/ADD) or string/array of strings (for DEL)\n- For \"memory_mb\": integer\n- For \"vcpus\": integer\n- For \"scale_to_zero\": object with cooldown_time_ms, policy, and stateful fields\n- For \"tags\": array of strings\n- For \"delete_lock\": boolean\n- For \"schedules\": array of schedule objects (with name, when, action, and optional args fields)\nfor SET/ADD, or array of schedule names for DEL. Use action \"exec\" together with args to\nexecute a command at the scheduled time.\n- For \"autokill\": object with time_ms and num_requests fields\n- For \"hostname\": string (valid DNS label)\n- For \"roms\": array of ROM objects (with name and image fields) for SET/ADD, or array of ROM names for DEL\n- For \"plugins\": array of plugin objects (with name, image, and optional config fields)\nfor SET/ADD. The deprecated `rom` field is accepted in place of `image`.\n- For \"dependencies\": array of instance identifiers (name or UUID)\n- For \"sched_priority\": SchedPriority enum value (\"normal\", \"medium\", \"high\", \"admin\")\n- For \"annotations\": object (for SET/ADD) or string/array of strings (for DEL)" + } + }, + "description": "A single update operation to be applied to an instance." + }, + "Instances.MutableInstanceProperty": { + "type": "string", + "enum": [ + "image", + "args", + "env", + "memory_mb", + "vcpus", + "scale_to_zero", + "tags", + "delete_lock", + "schedules", + "autokill", + "hostname", + "roms", + "dependencies", + "sched_priority", + "plugins", + "annotations" + ], + "description": "The mutable properties of an instance that can be updated." + }, + "Instances.MutableInstanceOperation": { + "type": "string", + "enum": [ + "set", + "add", + "del" + ], + "description": "Mutable instance operations." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_checkpoint_instances", + "kind": "platform", + "path": "/v1/instances/checkpoints", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "name", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "details", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "count", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "from", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "order", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "sortby", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "tags", + "group": "query", + "style": "form", + "explode": false + } + ], + "bodyContentType": "application/json", + "title": "List Checkpoint Instances", + "description": "Get one or more checkpoint instances by their UUID(s) or name(s).", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "query": { + "type": "object", + "properties": { + "uuid": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The UUID of the resource." + }, + "name": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The name of the resource." + }, + "details": { + "type": "boolean" + }, + "count": { + "type": "integer", + "minimum": 0 + }, + "from": { + "type": "string" + }, + "order": { + "$ref": "#/definitions/Common.PaginationOrder" + }, + "sortby": { + "$ref": "#/definitions/Common.PaginationSortBy" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Common.PaginationOrder": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "description": "The sort order used by list endpoints." + }, + "Common.PaginationSortBy": { + "type": "string", + "enum": [ + "create_time" + ], + "description": "The sort field used by list endpoints." + }, + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_create_checkpoint_instances", + "kind": "platform", + "path": "/v1/instances/checkpoints", + "method": "POST", + "parameters": [], + "bodyContentType": "application/json", + "title": "Create Checkpoint Instances", + "description": "Create a checkpoint from an existing instance. A checkpoint captures the\nstate of an instance at a specific point in time. Checkpoints can be\ncreated from running, stopped, or standby instances.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.CreateCheckpointInstancesRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Instances.CreateCheckpointInstancesRequestItem": { + "type": "object", + "required": [ + "from" + ], + "properties": { + "from": { + "allOf": [ + { + "$ref": "#/definitions/Common.NameOrUUID" + } + ], + "description": "The source instance to create a checkpoint from (by name or UUID)." + }, + "name": { + "type": "string", + "description": "The name of the checkpoint. If not provided, a name will be generated." + }, + "timeout_s": { + "type": "integer", + "description": "Timeout in seconds to wait for the checkpoint to be created.\nNo wait performed for a value of 0." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Tags to associate with the checkpoint." + }, + "autokill": { + "allOf": [ + { + "$ref": "#/definitions/Instances.CheckpointAutokill" + } + ], + "description": "Automatic delete-on-idle configuration for the new checkpoint." + } + }, + "description": "A single checkpoint creation request." + }, + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + }, + "Instances.CheckpointAutokill": { + "type": "object", + "properties": { + "time_ms": { + "type": "integer", + "description": "Time in milliseconds after the checkpoint was last used for restoring\nbefore it is deleted. A value of 0 disables checkpoint autokill.", + "minimum": 0 + } + }, + "description": "Automatic delete-on-idle configuration for the checkpoint instance." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_delete_checkpoint_instances", + "kind": "platform", + "path": "/v1/instances/checkpoints", + "method": "DELETE", + "parameters": [], + "bodyContentType": "application/json", + "title": "Delete Checkpoint Instances", + "description": "Delete the specified checkpoint instance(s) by ID(s) (name or UUID).\nAfter this call the IDs of the checkpoint instances are no longer valid.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_update_checkpoint_instances", + "kind": "platform", + "path": "/v1/instances/checkpoints", + "method": "PATCH", + "parameters": [], + "bodyContentType": "application/json", + "title": "Update Checkpoint Instances", + "description": "Update (modify) one or more checkpoint instances by ID(s) (name or UUID).", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.UpdateCheckpointInstancesRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Instances.UpdateCheckpointInstancesRequestItem": { + "type": "object", + "required": [ + "prop", + "op" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "id": { + "type": "string", + "description": "A client-provided identifier for tracking this operation in the response." + }, + "prop": { + "allOf": [ + { + "$ref": "#/definitions/Instances.MutableCheckpointInstanceProperty" + } + ], + "description": "The property to modify." + }, + "op": { + "allOf": [ + { + "$ref": "#/definitions/Instances.MutableCheckpointInstanceOperation" + } + ], + "description": "The operation to perform on the property." + }, + "value": { + "description": "The value for the update operation. The type depends on the property and operation:\n- For \"tags\": array of strings\n- For \"delete_lock\": boolean\n- For \"autokill\": object with time_ms field" + } + }, + "description": "A single update operation to be applied to a checkpoint instance." + }, + "Instances.MutableCheckpointInstanceProperty": { + "type": "string", + "enum": [ + "tags", + "delete_lock", + "autokill" + ], + "description": "The mutable properties of a checkpoint instance that can be updated." + }, + "Instances.MutableCheckpointInstanceOperation": { + "type": "string", + "enum": [ + "set", + "add", + "del" + ], + "description": "The operations available on a checkpoint instance's properties." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_checkpoint_history", + "kind": "platform", + "path": "/v1/instances/checkpoints/history", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "name", + "group": "query", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Get Checkpoint History", + "description": "Get the checkpoint history of one or more checkpoint instances.\nReturns the ordered list of checkpoints in each checkpoint's history.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "query": { + "type": "object", + "properties": { + "uuid": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The UUID of the resource." + }, + "name": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The name of the resource." + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_checkpoint_instance_by_uuid", + "kind": "platform", + "path": "/v1/instances/checkpoints/{uuid}", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + }, + { + "name": "details", + "group": "query", + "style": "form", + "explode": false + } + ], + "title": "Get Checkpoint Instance by UUID", + "description": "Get a single checkpoint instance by its UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "query": { + "type": "object", + "properties": { + "details": { + "type": "boolean" + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_delete_checkpoint_instance_by_uuid", + "kind": "platform", + "path": "/v1/instances/checkpoints/{uuid}", + "method": "DELETE", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Delete Checkpoint Instance by UUID", + "description": "Delete a specified checkpoint instance by its UUID. After this call the\nUUID of the checkpoint instance is no longer valid.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_update_checkpoint_instance_by_uuid", + "kind": "platform", + "path": "/v1/instances/checkpoints/{uuid}", + "method": "PATCH", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Update Checkpoint Instance by UUID", + "description": "Update (modify) a checkpoint instance by its UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Instances.UpdateCheckpointInstanceByUUIDRequestBody" + } + }, + "required": [ + "path", + "body" + ], + "definitions": { + "Instances.UpdateCheckpointInstanceByUUIDRequestBody": { + "type": "object", + "required": [ + "prop", + "op" + ], + "properties": { + "id": { + "type": "string", + "description": "A client-provided identifier for tracking this operation in the response." + }, + "prop": { + "allOf": [ + { + "$ref": "#/definitions/Instances.MutableCheckpointInstanceProperty" + } + ], + "description": "The property to modify." + }, + "op": { + "allOf": [ + { + "$ref": "#/definitions/Instances.MutableCheckpointInstanceOperation" + } + ], + "description": "The operation to perform on the property." + }, + "value": { + "description": "The value for the update operation. The type depends on the property and operation:\n- For \"tags\": array of strings\n- For \"delete_lock\": boolean\n- For \"autokill\": object with time_ms field" + } + }, + "description": "Request body for updating a single checkpoint instance by UUID." + }, + "Instances.MutableCheckpointInstanceProperty": { + "type": "string", + "enum": [ + "tags", + "delete_lock", + "autokill" + ], + "description": "The mutable properties of a checkpoint instance that can be updated." + }, + "Instances.MutableCheckpointInstanceOperation": { + "type": "string", + "enum": [ + "set", + "add", + "del" + ], + "description": "The operations available on a checkpoint instance's properties." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_checkpoint_history_by_uuid", + "kind": "platform", + "path": "/v1/instances/checkpoints/{uuid}/history", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Get Checkpoint History by UUID", + "description": "Get the checkpoint history of a checkpoint instance by its UUID.\nReturns the ordered list of checkpoints in the checkpoint's history.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_instance_history", + "kind": "platform", + "path": "/v1/instances/history", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "name", + "group": "query", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Get Instances Checkpoint History", + "description": "Get the checkpoint history of one or more instances.\nReturns the ordered list of checkpoints associated with each instance.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "query": { + "type": "object", + "properties": { + "uuid": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The UUID of the resource." + }, + "name": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The name of the resource." + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_instance_logs", + "kind": "platform", + "path": "/v1/instances/log", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "name", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "offset", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "limit", + "group": "query", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Get Instances Logs", + "description": "Get instances logs.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "query": { + "type": "object", + "properties": { + "uuid": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The UUID of the resource." + }, + "name": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The name of the resource." + }, + "offset": { + "type": "array", + "items": { + "type": "integer" + }, + "description": "The byte offset of the log output to receive. A negative sign makes the\noffset relative to the end of the log." + }, + "limit": { + "type": "array", + "items": { + "type": "integer" + }, + "description": "The amount of bytes to return at most." + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.GetInstancesLogsRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Instances.GetInstancesLogsRequestItem": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "offset": { + "type": "integer", + "description": "The byte offset of the log output to receive. A negative sign makes the\noffset relative to the end of the log." + }, + "limit": { + "type": "integer", + "description": "The amount of bytes to return at most." + } + }, + "description": "A single request item to get an instance's logs." + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_instance_metrics", + "kind": "platform", + "path": "/v1/instances/metrics", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "name", + "group": "query", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Get Instances Metrics", + "description": "Get instances metrics.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "query": { + "type": "object", + "properties": { + "uuid": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The UUID of the resource." + }, + "name": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The name of the resource." + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [], + "definitions": { + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_start_instances", + "kind": "platform", + "path": "/v1/instances/start", + "method": "PUT", + "parameters": [], + "bodyContentType": "application/json", + "title": "Start Instances", + "description": "Start instances by ID(s).", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.StartInstancesRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Instances.StartInstancesRequestItem": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "wait_timeout_ms": { + "type": "integer", + "description": "Deprecated: Use `timeout_s` instead. Timeout in milliseconds to\nwait for the instance to reach running state. If `timeout_s` is\nnot set, this value is converted by rounding up to the next full\nsecond. No wait performed for a value of 0.", + "deprecated": true + }, + "timeout_s": { + "type": "integer", + "description": "Timeout in seconds to wait for the instance to reach running\nstate. If you start your instance, you can wait for it to\nfinish starting with a blocking API call if you specify a wait\ntimeout greater than zero. No wait performed for a value of 0." + } + }, + "description": "A single request item to start an instance." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_stop_instances", + "kind": "platform", + "path": "/v1/instances/stop", + "method": "PUT", + "parameters": [], + "bodyContentType": "application/json", + "title": "Stop Instances", + "description": "Stop instances by ID(s).", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.StopInstancesRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Instances.StopInstancesRequestItem": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "force": { + "type": "boolean", + "description": "Whether to immediately force stop the instance." + }, + "drain_timeout_ms": { + "type": "integer", + "description": "Timeout for draining connections in milliseconds.\nNo draining will occur if set to 0. The instance\ndoes not receive new connections in the draining\nphase. The instance is stopped when the last\nconnection has been closed or the timeout expired.\nThe maximum timeout may vary. Use -1 for the\nlargest possible value. Ignored if force is set.\n\nNote: This endpoint does not block. Use the wait\nendpoint for the instance to reach the stopped\nstate.", + "minimum": -1 + }, + "quick": { + "type": "boolean", + "description": "Whether to perform a quick shutdown. This flag is\noverridden by force." + }, + "ifstate": { + "type": "string", + "description": "Only stop the instance if it is in this state." + }, + "dump": { + "type": "boolean", + "description": "If set, forces the VMM to shutdown immediately and generate a coredump.\nCan only be used in conjunction with force." + } + }, + "description": "A single request item to stop an instance." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_suspend_instances", + "kind": "platform", + "path": "/v1/instances/suspend", + "method": "PUT", + "parameters": [], + "bodyContentType": "application/json", + "title": "Suspend Instances", + "description": "Suspend instances by ID(s).", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.SuspendInstancesRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Instances.SuspendInstancesRequestItem": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "drain_timeout_ms": { + "type": "integer", + "description": "Timeout for draining connections in milliseconds. No draining\nwill occur if set to 0. Use -1 for the largest possible value.", + "minimum": -1 + } + }, + "description": "A single request item to suspend an instance." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_create_template_instances", + "kind": "platform", + "path": "/v1/instances/templates", + "method": "POST", + "parameters": [], + "bodyContentType": "application/json", + "title": "Create template instances from Instances", + "description": "Convert instances to template instances.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.CreateTemplateInstancesRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Instances.CreateTemplateInstancesRequestItem": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "timeout_s": { + "type": "integer", + "description": "Timeout in seconds to wait for the template instances to be created.\nA value of -1 means to wait indefinitely until the instance reaches the\ndesired state. No wait performed for a value of 0." + }, + "autokill": { + "allOf": [ + { + "$ref": "#/definitions/Instances.ItemAutokill" + } + ], + "description": "Automatic delete-on-idle configuration for the new template." + } + }, + "description": "A single template instance to be created." + }, + "Instances.ItemAutokill": { + "type": "object", + "properties": { + "time_ms": { + "type": "integer", + "description": "Time in milliseconds after the template was last used for cloning before\nit is deleted. A value of 0 disables template autokill.", + "minimum": 0 + } + }, + "description": "Automatic delete-on-idle configuration for the template instance." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_template_instances", + "kind": "platform", + "path": "/v1/instances/templates", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "name", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "details", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "count", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "from", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "order", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "sortby", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "tags", + "group": "query", + "style": "form", + "explode": false + } + ], + "bodyContentType": "application/json", + "title": "List template instances", + "description": "Get one or more template instances.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "query": { + "type": "object", + "properties": { + "uuid": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The UUID of the resource." + }, + "name": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The name of the resource." + }, + "details": { + "type": "boolean" + }, + "count": { + "type": "integer", + "minimum": 0 + }, + "from": { + "type": "string" + }, + "order": { + "$ref": "#/definitions/Common.PaginationOrder" + }, + "sortby": { + "$ref": "#/definitions/Common.PaginationSortBy" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [], + "definitions": { + "Common.PaginationOrder": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "description": "The sort order used by list endpoints." + }, + "Common.PaginationSortBy": { + "type": "string", + "enum": [ + "create_time" + ], + "description": "The sort field used by list endpoints." + }, + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_update_template_instances", + "kind": "platform", + "path": "/v1/instances/templates", + "method": "PATCH", + "parameters": [], + "bodyContentType": "application/json", + "title": "Update template instances", + "description": "Update template instances by ID(s).", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.UpdateTemplateInstancesRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Instances.UpdateTemplateInstancesRequestItem": { + "type": "object", + "required": [ + "prop", + "op" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "id": { + "type": "string", + "description": "A client-provided identifier for tracking this operation in the response." + }, + "prop": { + "allOf": [ + { + "$ref": "#/definitions/Instances.MutableTemplateInstanceProperty" + } + ], + "description": "The property to modify." + }, + "op": { + "allOf": [ + { + "$ref": "#/definitions/Instances.MutableTemplateInstanceOperation" + } + ], + "description": "The operation to perform." + }, + "value": { + "description": "The value for the update operation. The type depends on the property and\noperation:\n- For \"tags\": array of strings\n- For \"delete_lock\": boolean\n- For \"autokill\": object with time_ms field" + } + }, + "description": "A single template instance update request item." + }, + "Instances.MutableTemplateInstanceProperty": { + "type": "string", + "enum": [ + "tags", + "delete_lock", + "autokill" + ], + "description": "The mutable properties of a template instance that can be updated." + }, + "Instances.MutableTemplateInstanceOperation": { + "type": "string", + "enum": [ + "set", + "add", + "del" + ], + "description": "Mutable template instance operations." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_delete_template_instances", + "kind": "platform", + "path": "/v1/instances/templates", + "method": "DELETE", + "parameters": [], + "bodyContentType": "application/json", + "title": "Delete template instances", + "description": "Delete template instances by ID(s).", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_template_instance_by_uuid", + "kind": "platform", + "path": "/v1/instances/templates/{uuid}", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + }, + { + "name": "details", + "group": "query", + "style": "form", + "explode": false + } + ], + "title": "Get template instance by UUID", + "description": "Get a single template instance by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "query": { + "type": "object", + "properties": { + "details": { + "type": "boolean" + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_update_template_instance_by_uuid", + "kind": "platform", + "path": "/v1/instances/templates/{uuid}", + "method": "PATCH", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Update template instance by UUID", + "description": "Update a template instance by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Instances.UpdateTemplateInstanceByUUIDRequestBody" + } + }, + "required": [ + "path", + "body" + ], + "definitions": { + "Instances.UpdateTemplateInstanceByUUIDRequestBody": { + "type": "object", + "required": [ + "prop", + "op" + ], + "properties": { + "id": { + "type": "string", + "description": "A client-provided identifier for tracking this operation in the response." + }, + "prop": { + "allOf": [ + { + "$ref": "#/definitions/Instances.MutableTemplateInstanceProperty" + } + ], + "description": "The property to modify." + }, + "op": { + "allOf": [ + { + "$ref": "#/definitions/Instances.MutableTemplateInstanceOperation" + } + ], + "description": "The operation to perform." + }, + "value": { + "description": "The value for the update operation. The type depends on the property and\noperation:\n- For \"tags\": array of strings\n- For \"delete_lock\": boolean\n- For \"autokill\": object with time_ms field" + } + } + }, + "Instances.MutableTemplateInstanceProperty": { + "type": "string", + "enum": [ + "tags", + "delete_lock", + "autokill" + ], + "description": "The mutable properties of a template instance that can be updated." + }, + "Instances.MutableTemplateInstanceOperation": { + "type": "string", + "enum": [ + "set", + "add", + "del" + ], + "description": "Mutable template instance operations." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_delete_template_instance_by_uuid", + "kind": "platform", + "path": "/v1/instances/templates/{uuid}", + "method": "DELETE", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Delete template instance by UUID", + "description": "Delete a template instance by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_wait_instances", + "kind": "platform", + "path": "/v1/instances/wait", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "name", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "state", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "timeout_ms", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "timeout_s", + "group": "query", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Wait for Instances States", + "description": "Wait for instances to reach states.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "query": { + "type": "object", + "properties": { + "uuid": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The UUID of the resource." + }, + "name": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The name of the resource." + }, + "state": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.InstanceState" + }, + "description": "The desired state to wait for. Default is `running`." + }, + "timeout_ms": { + "type": "array", + "items": { + "type": "integer" + } + }, + "timeout_s": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Instances.WaitInstancesRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Instances.InstanceState": { + "type": "string", + "enum": [ + "stopped", + "starting", + "running", + "draining", + "stopping", + "template", + "standby", + "deleted", + "checkpoint" + ], + "description": "The current state of an instance." + }, + "Instances.WaitInstancesRequestItem": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "state": { + "allOf": [ + { + "$ref": "#/definitions/Instances.InstanceState" + } + ], + "description": "The desired state to wait for. Default is `running`." + }, + "timeout_ms": { + "type": "integer", + "description": "Deprecated: Use `timeout_s` instead. Timeout in milliseconds to\nwait for the instance to reach the desired state. If `timeout_s` is\nnot set, this value is converted by rounding up to the next full\nsecond. A value of -1 means to wait indefinitely.", + "deprecated": true + }, + "timeout_s": { + "type": "integer", + "description": "Timeout in seconds to wait for the instance to reach the desired\nstate. If the timeout is reached, the request will fail with an\nerror. A value of -1 means to wait indefinitely until the instance\nreaches the desired state. No wait performed for a value of 0." + } + }, + "description": "A single request item to wait for an instance's state." + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_instance_by_uuid", + "kind": "platform", + "path": "/v1/instances/{uuid}", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + }, + { + "name": "details", + "group": "query", + "style": "form", + "explode": false + } + ], + "title": "Get Instance by UUID", + "description": "Get a single instance by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "query": { + "type": "object", + "properties": { + "details": { + "type": "boolean" + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_delete_instance_by_uuid", + "kind": "platform", + "path": "/v1/instances/{uuid}", + "method": "DELETE", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Delete Instance by UUID", + "description": "Delete instance by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Instances.DeleteInstanceByUUIDRequestBody" + } + }, + "required": [ + "path" + ], + "definitions": { + "Instances.DeleteInstanceByUUIDRequestBody": { + "type": "object", + "properties": { + "timeout_s": { + "type": "integer", + "description": "Timeout in seconds to wait for the instance to be deleted. No wait\nperformed for a value of 0." + }, + "dont_retain": { + "type": "boolean", + "description": "Delete immediately without retention. If the instance is already\nbeing retained, this will force its deletion. Ignored if retention\nfor instances is not configured." + } + }, + "description": "Parameters for deleting the instance by UUID." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_update_instance_by_uuid", + "kind": "platform", + "path": "/v1/instances/{uuid}", + "method": "PATCH", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Update Instance by UUID", + "description": "Update instance by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Instances.UpdateInstanceByUUIDRequestBody" + } + }, + "required": [ + "path", + "body" + ], + "definitions": { + "Instances.UpdateInstanceByUUIDRequestBody": { + "type": "object", + "required": [ + "prop", + "op" + ], + "properties": { + "id": { + "type": "string", + "description": "A client-provided identifier for tracking this operation in the response." + }, + "prop": { + "allOf": [ + { + "$ref": "#/definitions/Instances.MutableInstanceProperty" + } + ], + "description": "The property to modify." + }, + "op": { + "allOf": [ + { + "$ref": "#/definitions/Instances.MutableInstanceOperation" + } + ], + "description": "The operation to perform on the property." + }, + "value": { + "description": "The value for the update operation. The type depends on the property and operation:\n- For \"image\": image reference string, or object with image url,\ncredentials, headers and pull policy\n- For \"args\": string or array of strings\n- For \"env\": object (for SET/ADD) or string/array of strings (for DEL)\n- For \"memory_mb\": integer\n- For \"vcpus\": integer\n- For \"scale_to_zero\": object with cooldown_time_ms, policy, and stateful fields\n- For \"tags\": array of strings\n- For \"delete_lock\": boolean\n- For \"schedules\": array of schedule objects (with name, when, action, and optional args fields)\nfor SET/ADD, or array of schedule names for DEL. Use action \"exec\" together with args to\nexecute a command at the scheduled time.\n- For \"autokill\": object with time_ms and num_requests fields\n- For \"hostname\": string (valid DNS label)\n- For \"roms\": array of ROM objects (with name and image fields) for SET/ADD, or array of ROM names for DEL\n- For \"plugins\": array of plugin objects (with name, image, and optional config fields)\nfor SET/ADD. The deprecated `rom` field is accepted in place of `image`.\n- For \"dependencies\": array of instance identifiers (name or UUID)\n- For \"sched_priority\": SchedPriority enum value (\"normal\", \"medium\", \"high\", \"admin\")\n- For \"annotations\": object (for SET/ADD) or string/array of strings (for DEL)" + } + } + }, + "Instances.MutableInstanceProperty": { + "type": "string", + "enum": [ + "image", + "args", + "env", + "memory_mb", + "vcpus", + "scale_to_zero", + "tags", + "delete_lock", + "schedules", + "autokill", + "hostname", + "roms", + "dependencies", + "sched_priority", + "plugins", + "annotations" + ], + "description": "The mutable properties of an instance that can be updated." + }, + "Instances.MutableInstanceOperation": { + "type": "string", + "enum": [ + "set", + "add", + "del" + ], + "description": "Mutable instance operations." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_instance_history_by_uuid", + "kind": "platform", + "path": "/v1/instances/{uuid}/history", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Get Instance Checkpoint History by UUID", + "description": "Get the checkpoint history of an instance by its UUID.\nReturns the ordered list of checkpoints associated with the instance.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_instance_logs_by_uuid", + "kind": "platform", + "path": "/v1/instances/{uuid}/log", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Get Instance Logs by UUID", + "description": "Get instance logs by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Instances.GetInstanceLogsByUUIDRequestBody" + } + }, + "required": [ + "path", + "body" + ], + "definitions": { + "Instances.GetInstanceLogsByUUIDRequestBody": { + "type": "object", + "properties": { + "offset": { + "type": "integer", + "description": "The byte offset of the log output to receive. A negative sign makes the\noffset relative to the end of the log." + }, + "limit": { + "type": "integer", + "description": "The amount of bytes to return at most." + } + } + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_instance_metrics_by_uuid", + "kind": "platform", + "path": "/v1/instances/{uuid}/metrics", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Get Instance Metrics by UUID", + "description": "Get instance metrics by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_start_instance_by_uuid", + "kind": "platform", + "path": "/v1/instances/{uuid}/start", + "method": "PUT", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Start Instance by UUID", + "description": "Start instance by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Instances.StartInstanceByUUIDRequestBody" + } + }, + "required": [ + "path", + "body" + ], + "definitions": { + "Instances.StartInstanceByUUIDRequestBody": { + "type": "object", + "properties": { + "wait_timeout_ms": { + "type": "integer", + "description": "Deprecated: Use `timeout_s` instead. Timeout in milliseconds to\nwait for the instance to reach running state. If `timeout_s` is\nnot set, this value is converted by rounding up to the next full\nsecond. No wait performed for a value of 0.", + "deprecated": true + }, + "timeout_s": { + "type": "integer", + "description": "Timeout in seconds to wait for the instance to reach running\nstate. If you start your instance, you can wait for it to\nfinish starting with a blocking API call if you specify a wait\ntimeout greater than zero. No wait performed for a value of 0." + } + }, + "description": "Parameters for starting the instance." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_stop_instance_by_uuid", + "kind": "platform", + "path": "/v1/instances/{uuid}/stop", + "method": "PUT", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Stop Instance by UUID", + "description": "Stop instance by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Instances.StopInstanceByUUIDRequestBody" + } + }, + "required": [ + "path", + "body" + ], + "definitions": { + "Instances.StopInstanceByUUIDRequestBody": { + "type": "object", + "properties": { + "force": { + "type": "boolean", + "description": "Whether to immediately force stop the instance." + }, + "drain_timeout_ms": { + "type": "integer", + "description": "Timeout for draining connections in milliseconds.\nNo draining will occur if set to 0. The instance\ndoes not receive new connections in the draining\nphase. The instance is stopped when the last\nconnection has been closed or the timeout expired.\nThe maximum timeout may vary. Use -1 for the\nlargest possible value. Ignored if force is set.\n\nNote: This endpoint does not block. Use the wait\nendpoint for the instance to reach the stopped\nstate.", + "minimum": -1 + }, + "quick": { + "type": "boolean", + "description": "Whether to perform a quick shutdown. This flag is\noverridden by force." + }, + "ifstate": { + "type": "string", + "description": "Only stop the instance if it is in this state." + }, + "dump": { + "type": "boolean", + "description": "If set, forces the VMM to shutdown immediately and generate a coredump.\nCan only be used in conjunction with force." + } + }, + "description": "Parameters for stopping the instance." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_suspend_instance_by_uuid", + "kind": "platform", + "path": "/v1/instances/{uuid}/suspend", + "method": "PUT", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Suspend Instance by UUID", + "description": "Suspend instance by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Instances.SuspendInstanceByUUIDRequestBody" + } + }, + "required": [ + "path", + "body" + ], + "definitions": { + "Instances.SuspendInstanceByUUIDRequestBody": { + "type": "object", + "properties": { + "drain_timeout_ms": { + "type": "integer", + "description": "Timeout for draining connections in milliseconds. No draining\nwill occur if set to 0. Use -1 for the largest possible value.", + "minimum": -1 + } + }, + "description": "Parameters for suspending the instance." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_wait_instance_by_uuid", + "kind": "platform", + "path": "/v1/instances/{uuid}/wait", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Wait for Instance State by UUID", + "description": "Wait for instance state by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Instances.WaitInstanceByUUIDRequestBody" + } + }, + "required": [ + "path", + "body" + ], + "definitions": { + "Instances.WaitInstanceByUUIDRequestBody": { + "type": "object", + "required": [ + "state" + ], + "properties": { + "state": { + "allOf": [ + { + "$ref": "#/definitions/Instances.InstanceState" + } + ], + "description": "The desired state to wait for. Default is `running`." + }, + "timeout_ms": { + "type": "integer", + "description": "Deprecated: Use `timeout_s` instead. Timeout in milliseconds to\nwait for the instance to reach the desired state. If `timeout_s` is\nnot set, this value is converted by rounding up to the next full\nsecond. A value of -1 means to wait indefinitely.", + "deprecated": true + }, + "timeout_s": { + "type": "integer", + "description": "Timeout in seconds to wait for the instance to reach the desired\nstate. If the timeout is reached, the request will fail with an\nerror. A value of -1 means to wait indefinitely until the instance\nreaches the desired state. No wait performed for a value of 0." + } + }, + "description": "Wait parameters." + }, + "Instances.InstanceState": { + "type": "string", + "enum": [ + "stopped", + "starting", + "running", + "draining", + "stopping", + "template", + "standby", + "deleted", + "checkpoint" + ], + "description": "The current state of an instance." + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_create_service_group", + "kind": "platform", + "path": "/v1/services", + "method": "POST", + "parameters": [], + "bodyContentType": "application/json", + "title": "Create Service Group", + "description": "Create a new service group.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Services.CreateServiceGroupRequest" + } + }, + "required": [ + "body" + ], + "definitions": { + "Services.CreateServiceGroupRequest": { + "type": "object", + "required": [ + "services" + ], + "properties": { + "name": { + "type": "string", + "description": "Name of the service group. This is a human-readable name that can be used\nto identify the service group. The name must be unique within the context\nof your account. If no name is specified, a random name is generated for\nyou. The name can also be used to identify the service group in API calls." + }, + "services": { + "type": "array", + "items": { + "$ref": "#/definitions/Services.Service" + }, + "description": "Services to expose. At least one service is required." + }, + "domains": { + "type": "array", + "items": { + "$ref": "#/definitions/Services.CreateServiceGroupRequestDomain" + }, + "description": "Description of domains associated with the service group." + }, + "soft_limit": { + "type": "integer", + "description": "The soft limit is used by the Unikraft Cloud load balancer to decide when\nto wake up another standby instance.\n\nFor example, if the soft limit is set to 5 and the service consists of 2\nstandby instances, one of the instances receives up to 5 concurrent\nrequests. The 6th parallel requests wakes up the second instance. If\nthere are no more standby instances to wake up, the number of requests\nassigned to each instance will exceed the soft limit. The load balancer\nmakes sure that when the number of in-flight requests goes down again,\ninstances are put into standby as fast as possible.", + "minimum": 0 + }, + "hard_limit": { + "type": "integer", + "description": "The hard limit defines the maximum number of concurrent requests that an\ninstance assigned to the this service can handle.\n\nThe load balancer will never assign more requests to a single instance. In\ncase there are no other instances available, excess requests fail (i.e.,\nthey are blocked and not queued).", + "minimum": 0 + }, + "autokill": { + "allOf": [ + { + "$ref": "#/definitions/Services.CreateServiceGroupRequestAutokill" + } + ], + "description": "Automatic delete-on-idle configuration." + } + }, + "description": "The request message for creating a new service group." + }, + "Services.Service": { + "type": "object", + "required": [ + "port" + ], + "properties": { + "port": { + "type": "integer", + "description": "This is the public-facing port that the service will be accessible from\non the Internet.", + "minimum": 0 + }, + "destination_port": { + "type": "integer", + "description": "The port number that the instance is listening on. This is the internal\nport which Unikraft Cloud will forward traffic to.", + "minimum": 0 + }, + "protocol": { + "$ref": "#/definitions/Services.ServiceProtocol" + }, + "ip": { + "type": "string" + }, + "handlers": { + "type": "array", + "items": { + "$ref": "#/definitions/Services.ConnectionHandler" + }, + "description": "Connection handlers to use for the service. Handlers define how the\nservice will handle incoming connections and forward traffic from the\nInternet to your application. For example, a service can be configured\nto terminate TLS connections, redirect HTTP traffic, or enable HTTP mode\nfor load balancing. You configure the handlers for every published\nservice port individually." + } + }, + "description": "A service connects a public-facing port to an internal destination port on\nwhich an application instance listens on. Additional handlers can be\ndefined for each published port in order to define how the service will\nhandle incoming connections and forward traffic from the Internet to your\napplication. For example, a service can be configured to terminate TLS\nconnections, redirect HTTP traffic, or enable HTTP mode for load balancing." + }, + "Services.ServiceProtocol": { + "type": "string", + "enum": [ + "tcp", + "udp" + ], + "description": "Protocol for a service." + }, + "Services.ConnectionHandler": { + "type": "string", + "enum": [ + "tls", + "http", + "redirect" + ], + "description": "Connection handlers to use for the service.\n\nHandlers define how the service will handle incoming connections and\nforward traffic from the Internet to your application. For example, a\nservice can be configured to terminate TLS connections, redirect HTTP\ntraffic, or enable HTTP mode for load balancing. You configure the handlers\nfor every published service port individually.\n\nThere are currently 3 supported handlers:\n\n| Handler | Description |\n|------------|-------------|\n| `tls` | Terminate the TLS connection at the Unikraft Cloud gateway using our wildcard certificate issued for the kraft.cloud domain. The gateway forwards the unencrypted traffic to your application. |\n| `http` | Enable HTTP mode on the load balancer to load balance on the level of individual HTTP requests. In this mode, only HTTP connections are accepted. If this option is not set the load balancer works in TCP mode and distributes TCP connections. |\n| `redirect` | Redirect traffic from the source port to the destination port. |\n\nNote that there is a set of constraints when publishing ports:\n\n- Port 80: MUST have \"http\" and MUST not have \"tls\" set;\n- Port 443: MUST have http and tls set;\n- The `redirect` handler can only be set on port 80 (HTTP) to redirect to\nport 443 (HTTPS);\n- All other ports MUST have tls and MUST not have http set." + }, + "Services.CreateServiceGroupRequestDomain": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "Publicly accessible domain name. If this name ends in a period `.` it must\nbe a valid Full Qualified Domain Name (FQDN), otherwise it will become a\nsubdomain of the target metro." + }, + "certificate": { + "allOf": [ + { + "$ref": "#/definitions/Common.NameOrUUID" + } + ], + "description": "Use an existing certificate for the domain. If this field is\nspecified, the domain must be associated with a valid certificate." + } + }, + "description": "A domain to attach when creating a service group." + }, + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + }, + "Services.CreateServiceGroupRequestAutokill": { + "type": "object", + "properties": { + "time_ms": { + "type": "integer", + "description": "Time in milliseconds after the service group becomes empty before it is\ndeleted. A value of 0 disables autokill.", + "minimum": 0 + } + }, + "description": "Autokill configuration when creating a service group." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_service_groups", + "kind": "platform", + "path": "/v1/services", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "name", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "details", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "count", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "from", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "order", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "sortby", + "group": "query", + "style": "form", + "explode": false + } + ], + "bodyContentType": "application/json", + "title": "List Service Groups", + "description": "List service groups.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "query": { + "type": "object", + "properties": { + "uuid": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The UUID of the resource." + }, + "name": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The name of the resource." + }, + "details": { + "type": "boolean" + }, + "count": { + "type": "integer", + "minimum": 0 + }, + "from": { + "type": "string" + }, + "order": { + "$ref": "#/definitions/Common.PaginationOrder" + }, + "sortby": { + "$ref": "#/definitions/Common.PaginationSortBy" + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [], + "definitions": { + "Common.PaginationOrder": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "description": "The sort order used by list endpoints." + }, + "Common.PaginationSortBy": { + "type": "string", + "enum": [ + "create_time" + ], + "description": "The sort field used by list endpoints." + }, + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_update_service_groups", + "kind": "platform", + "path": "/v1/services", + "method": "PATCH", + "parameters": [], + "bodyContentType": "application/json", + "title": "Update Service Groups", + "description": "Update service groups.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Services.UpdateServiceGroupsRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Services.UpdateServiceGroupsRequestItem": { + "type": "object", + "required": [ + "prop", + "op" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "id": { + "type": "string", + "description": "A client-provided identifier for tracking this operation in the response." + }, + "prop": { + "allOf": [ + { + "$ref": "#/definitions/Services.MutableServiceGroupProperty" + } + ], + "description": "The property to modify." + }, + "op": { + "allOf": [ + { + "$ref": "#/definitions/Services.MutableServiceGroupOperation" + } + ], + "description": "The operation to perform." + }, + "value": { + "description": "The value for the update operation. The type depends on the property and operation:\n- For \"image\": string\n- For \"args\": string or array of strings\n- For \"env\": object (for SET/ADD) or string/array of strings (for DEL)\n- For \"memory_mb\": integer\n- For \"vcpus\": integer\n- For \"scale_to_zero\": object with cooldown_time_ms, policy, and stateful fields\n- For \"tags\": array of strings\n- For \"delete_lock\": boolean\n- For \"schedules\": array of schedule objects (with name, when, action, and optional args fields).\nUse action \"exec\" together with args to execute a command at the scheduled time.\n- For \"autokill\": object with time_ms and num_requests fields\n- For \"hostname\": string (valid DNS label)\n- For \"roms\": array of ROM objects (with name and image fields) for SET/ADD, or array of ROM names for DEL\n- For \"dependencies\": array of instance identifiers (name or UUID)\n- For \"sched_priority\": SchedPriority enum value (\"normal\", \"medium\", \"high\", \"admin\")" + } + }, + "description": "A single update operation to be applied to a service group." + }, + "Services.MutableServiceGroupProperty": { + "type": "string", + "enum": [ + "services", + "domains", + "soft_limit", + "hard_limit", + "autokill" + ], + "description": "Mutable service group properties." + }, + "Services.MutableServiceGroupOperation": { + "type": "string", + "enum": [ + "set", + "add", + "del" + ], + "description": "The mutable operations available on a service group's properties." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_delete_service_groups", + "kind": "platform", + "path": "/v1/services", + "method": "DELETE", + "parameters": [], + "bodyContentType": "application/json", + "title": "Delete Service Groups", + "description": "Delete service groups by ID(s).", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_create_autoscale_configurations", + "kind": "platform", + "path": "/v1/services/autoscale", + "method": "POST", + "parameters": [], + "bodyContentType": "application/json", + "title": "Create Autoscale Configurations", + "description": "Create one or more autoscale configurations for the specified service groups.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Autoscale.CreateAutoscaleConfigurationsRequestConfiguration" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Autoscale.CreateAutoscaleConfigurationsRequestConfiguration": { + "type": "object", + "required": [ + "create_args" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "min_size": { + "type": "integer", + "description": "The minimum number of instances to keep running." + }, + "max_size": { + "type": "integer", + "description": "The maximum number of instances to keep running." + }, + "warmup_time_ms": { + "type": "integer", + "description": "The warmup time in milliseconds for new instances." + }, + "cooldown_time_ms": { + "type": "integer", + "description": "The cooldown time in milliseconds for the autoscale configuration." + }, + "create_args": { + "allOf": [ + { + "$ref": "#/definitions/Autoscale.ConfigurationInstanceCreateArgs" + } + ], + "description": "The arguments to use when creating instances." + }, + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/Autoscale.AutoscalePolicy" + }, + "description": "The policies to apply to the autoscale configuration." + } + }, + "oneOf": [ + { + "required": [ + "uuid" + ] + }, + { + "required": [ + "name" + ] + } + ] + }, + "Autoscale.ConfigurationInstanceCreateArgs": { + "type": "object", + "properties": { + "roms": { + "allOf": [ + { + "$ref": "#/definitions/Autoscale.InstanceCreateArgsInstanceCreateRequestRoms" + } + ], + "description": "The ROM to use for the autoscale configuration." + }, + "template": { + "allOf": [ + { + "$ref": "#/definitions/Common.NameOrUUID" + } + ], + "description": "The template to use for the autoscale configuration." + } + }, + "oneOf": [ + { + "required": [ + "roms" + ] + }, + { + "required": [ + "template" + ] + } + ] + }, + "Autoscale.InstanceCreateArgsInstanceCreateRequestRoms": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the ROM." + }, + "image": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/Instances.ImageSpec" + } + ], + "description": "The image of the ROM to use for the autoscale configuration. Mutually\nexclusive with `files`. Accepts either a plain image reference string\n(`\"nginx:latest\"`) or an object carrying additional pull configuration\n(`{\"url\": \"nginx:latest\", \"pull_policy\": \"always\"}`)." + }, + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.InlineFile" + }, + "description": "Inline files to use as the ROM content. When specified, the platform\ncreates an EROFS image from the provided files. Mutually exclusive with\n`image`." + } + }, + "description": "A ROM to use for an autoscale instance create configuration." + }, + "Instances.ImageSpec": { + "type": "object", + "required": [ + "url" + ], + "properties": { + "url": { + "type": "string", + "description": "The image URL" + }, + "credentials": { + "type": "string", + "description": "Optional credentials for authenticating to an OCI registry.\nOnly valid for OCI registry URLs; the platform rejects this\nfield for non-OCI schemes." + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Optional HTTP headers to send when fetching the image." + }, + "pull_policy": { + "allOf": [ + { + "$ref": "#/definitions/Instances.PullPolicy" + } + ], + "description": "Controls when the image is pulled relative to what is already cached on\nthe node." + } + }, + "description": "ImageSpec describes an image to use for an instance." + }, + "Instances.PullPolicy": { + "type": "string", + "enum": [ + "always", + "if_not_present", + "never" + ], + "description": "PullPolicy defines when an image should be pulled." + }, + "Common.InlineFile": { + "type": "object", + "required": [ + "path", + "data" + ], + "properties": { + "path": { + "type": "string", + "description": "The file path within the image." + }, + "encoding": { + "allOf": [ + { + "$ref": "#/definitions/Common.InlineDataEncoding" + } + ], + "description": "The encoding of the data field. Defaults to \"text\"." + }, + "data": { + "type": "string", + "description": "The file data, encoded according to the encoding field." + } + }, + "description": "An inline file entry represents a single file within an image." + }, + "Common.InlineDataEncoding": { + "type": "string", + "enum": [ + "text", + "base64" + ], + "description": "Encoding type for inline file data." + }, + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + }, + "Autoscale.AutoscalePolicy": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the policy." + }, + "enabled": { + "type": "boolean", + "description": "Whether the policy is enabled." + }, + "metric": { + "allOf": [ + { + "$ref": "#/definitions/Autoscale.StepPolicyMetric" + } + ], + "description": "Metric to use for the step policy (only for step policies)." + }, + "adjustment_type": { + "allOf": [ + { + "$ref": "#/definitions/Autoscale.AdjustmentType" + } + ], + "description": "The type of adjustment to be made in the step policy (only for step policies)." + }, + "steps": { + "type": "array", + "items": { + "$ref": "#/definitions/Autoscale.AutoscalePolicyStep" + }, + "description": "The steps for the step policy. Each step defines an adjustment value and\noptional bounds." + } + }, + "description": "AutoscalePolicy defines the autoscale policy for a service. Right now it\ncontains fields from both the `ondemand` and `step` policies. They are\nmarked both as optional, so only one of them should be set at a time. This\nis a current limitation of the API design." + }, + "Autoscale.StepPolicyMetric": { + "type": "string", + "enum": [ + "cpu" + ], + "description": "The step policy is a type of autoscaling policy that scales the number of\ninstances in a service by a fixed number of instances at each step. It uses\na metric to determine when to scale up or down." + }, + "Autoscale.AdjustmentType": { + "type": "string", + "enum": [ + "change", + "exact", + "percentage" + ], + "description": "AdjustmentType defines the type of adjustment to be made in an autoscaling\nstep policy." + }, + "Autoscale.AutoscalePolicyStep": { + "type": "object", + "required": [ + "adjustment" + ], + "properties": { + "adjustment": { + "type": "integer", + "description": "The adjustment value for the step." + }, + "lower_bound": { + "type": "integer", + "description": "Lower bound for the step." + }, + "upper_bound": { + "type": "integer", + "description": "Upper bound for the step." + } + }, + "description": "A single step in a step autoscaling policy." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_autoscale_configurations", + "kind": "platform", + "path": "/v1/services/autoscale", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "name", + "group": "query", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "List Autoscale Configurations", + "description": "List autoscale configurations for the specified service groups.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "query": { + "type": "object", + "properties": { + "uuid": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The UUID of the resource." + }, + "name": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The name of the resource." + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_delete_autoscale_configurations", + "kind": "platform", + "path": "/v1/services/autoscale", + "method": "DELETE", + "parameters": [], + "bodyContentType": "application/json", + "title": "Delete Autoscale Configurations", + "description": "Delete autoscale configurations for the specified service groups.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_service_group_by_uuid", + "kind": "platform", + "path": "/v1/services/{uuid}", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + }, + { + "name": "details", + "group": "query", + "style": "form", + "explode": false + } + ], + "title": "Get Service Group by UUID", + "description": "Get a service group by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "query": { + "type": "object", + "properties": { + "details": { + "type": "boolean" + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_update_service_group_by_uuid", + "kind": "platform", + "path": "/v1/services/{uuid}", + "method": "PATCH", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Update Service Group by UUID", + "description": "Update a service group by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Services.UpdateServiceGroupByUUIDRequestBody" + } + }, + "required": [ + "path", + "body" + ], + "definitions": { + "Services.UpdateServiceGroupByUUIDRequestBody": { + "type": "object", + "required": [ + "prop", + "op" + ], + "properties": { + "id": { + "type": "string", + "description": "A client-provided identifier for tracking this operation in the response." + }, + "prop": { + "allOf": [ + { + "$ref": "#/definitions/Services.MutableServiceGroupProperty" + } + ], + "description": "The property to modify." + }, + "op": { + "allOf": [ + { + "$ref": "#/definitions/Services.MutableServiceGroupOperation" + } + ], + "description": "The operation to perform." + }, + "value": { + "description": "The value for the update operation. The type depends on the property and operation:\n- For \"image\": string\n- For \"args\": string or array of strings\n- For \"env\": object (for SET/ADD) or string/array of strings (for DEL)\n- For \"memory_mb\": integer\n- For \"vcpus\": integer\n- For \"scale_to_zero\": object with cooldown_time_ms, policy, and stateful fields\n- For \"tags\": array of strings\n- For \"delete_lock\": boolean\n- For \"schedules\": array of schedule objects (with name, when, action, and optional args fields).\nUse action \"exec\" together with args to execute a command at the scheduled time.\n- For \"autokill\": object with time_ms and num_requests fields\n- For \"hostname\": string (valid DNS label)\n- For \"roms\": array of ROM objects (with name and image fields) for SET/ADD, or array of ROM names for DEL\n- For \"dependencies\": array of instance identifiers (name or UUID)\n- For \"sched_priority\": SchedPriority enum value (\"normal\", \"medium\", \"high\", \"admin\")" + } + } + }, + "Services.MutableServiceGroupProperty": { + "type": "string", + "enum": [ + "services", + "domains", + "soft_limit", + "hard_limit", + "autokill" + ], + "description": "Mutable service group properties." + }, + "Services.MutableServiceGroupOperation": { + "type": "string", + "enum": [ + "set", + "add", + "del" + ], + "description": "The mutable operations available on a service group's properties." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_delete_service_group_by_uuid", + "kind": "platform", + "path": "/v1/services/{uuid}", + "method": "DELETE", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Delete Service Group by UUID", + "description": "Delete a service group by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_create_autoscale_configuration_by_service_group_uuid", + "kind": "platform", + "path": "/v1/services/{uuid}/autoscale", + "method": "POST", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Create Autoscale Configuration by Service Group UUID", + "description": "Create an autoscale configuration for a service group by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Autoscale.CreateAutoscaleConfigurationByServiceGroupUUIDRequest" + } + }, + "required": [ + "path", + "body" + ], + "definitions": { + "Autoscale.CreateAutoscaleConfigurationByServiceGroupUUIDRequest": { + "type": "object", + "required": [ + "uuid", + "create_args" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the service to create a configuration for." + }, + "min_size": { + "type": "integer", + "description": "The minimum number of instances to keep running." + }, + "max_size": { + "type": "integer", + "description": "The maximum number of instances to keep running." + }, + "warmup_time_ms": { + "type": "integer", + "description": "The warmup time in milliseconds for new instances." + }, + "cooldown_time_ms": { + "type": "integer", + "description": "The cooldown time in milliseconds for the autoscale configuration." + }, + "create_args": { + "allOf": [ + { + "$ref": "#/definitions/Autoscale.CreateAutoscaleConfigurationByServiceGroupUUIDRequestInstanceCreateArgs" + } + ], + "description": "The arguments to use when creating instances." + }, + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/Autoscale.AutoscalePolicy" + }, + "description": "The policies to apply to the autoscale configuration." + } + }, + "description": "The request message to create an autoscale configuration for a service group\nbased on its UUID." + }, + "Autoscale.CreateAutoscaleConfigurationByServiceGroupUUIDRequestInstanceCreateArgs": { + "type": "object", + "properties": { + "roms": { + "allOf": [ + { + "$ref": "#/definitions/Autoscale.InstanceCreateArgsInstanceCreateRequestRoms" + } + ], + "description": "The ROM to use for the autoscale configuration." + }, + "template": { + "allOf": [ + { + "$ref": "#/definitions/Common.NameOrUUID" + } + ], + "description": "The template to use for the autoscale configuration." + } + }, + "oneOf": [ + { + "required": [ + "roms" + ] + }, + { + "required": [ + "template" + ] + } + ] + }, + "Autoscale.InstanceCreateArgsInstanceCreateRequestRoms": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the ROM." + }, + "image": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/Instances.ImageSpec" + } + ], + "description": "The image of the ROM to use for the autoscale configuration. Mutually\nexclusive with `files`. Accepts either a plain image reference string\n(`\"nginx:latest\"`) or an object carrying additional pull configuration\n(`{\"url\": \"nginx:latest\", \"pull_policy\": \"always\"}`)." + }, + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.InlineFile" + }, + "description": "Inline files to use as the ROM content. When specified, the platform\ncreates an EROFS image from the provided files. Mutually exclusive with\n`image`." + } + }, + "description": "A ROM to use for an autoscale instance create configuration." + }, + "Instances.ImageSpec": { + "type": "object", + "required": [ + "url" + ], + "properties": { + "url": { + "type": "string", + "description": "The image URL" + }, + "credentials": { + "type": "string", + "description": "Optional credentials for authenticating to an OCI registry.\nOnly valid for OCI registry URLs; the platform rejects this\nfield for non-OCI schemes." + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Optional HTTP headers to send when fetching the image." + }, + "pull_policy": { + "allOf": [ + { + "$ref": "#/definitions/Instances.PullPolicy" + } + ], + "description": "Controls when the image is pulled relative to what is already cached on\nthe node." + } + }, + "description": "ImageSpec describes an image to use for an instance." + }, + "Instances.PullPolicy": { + "type": "string", + "enum": [ + "always", + "if_not_present", + "never" + ], + "description": "PullPolicy defines when an image should be pulled." + }, + "Common.InlineFile": { + "type": "object", + "required": [ + "path", + "data" + ], + "properties": { + "path": { + "type": "string", + "description": "The file path within the image." + }, + "encoding": { + "allOf": [ + { + "$ref": "#/definitions/Common.InlineDataEncoding" + } + ], + "description": "The encoding of the data field. Defaults to \"text\"." + }, + "data": { + "type": "string", + "description": "The file data, encoded according to the encoding field." + } + }, + "description": "An inline file entry represents a single file within an image." + }, + "Common.InlineDataEncoding": { + "type": "string", + "enum": [ + "text", + "base64" + ], + "description": "Encoding type for inline file data." + }, + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + }, + "Autoscale.AutoscalePolicy": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the policy." + }, + "enabled": { + "type": "boolean", + "description": "Whether the policy is enabled." + }, + "metric": { + "allOf": [ + { + "$ref": "#/definitions/Autoscale.StepPolicyMetric" + } + ], + "description": "Metric to use for the step policy (only for step policies)." + }, + "adjustment_type": { + "allOf": [ + { + "$ref": "#/definitions/Autoscale.AdjustmentType" + } + ], + "description": "The type of adjustment to be made in the step policy (only for step policies)." + }, + "steps": { + "type": "array", + "items": { + "$ref": "#/definitions/Autoscale.AutoscalePolicyStep" + }, + "description": "The steps for the step policy. Each step defines an adjustment value and\noptional bounds." + } + }, + "description": "AutoscalePolicy defines the autoscale policy for a service. Right now it\ncontains fields from both the `ondemand` and `step` policies. They are\nmarked both as optional, so only one of them should be set at a time. This\nis a current limitation of the API design." + }, + "Autoscale.StepPolicyMetric": { + "type": "string", + "enum": [ + "cpu" + ], + "description": "The step policy is a type of autoscaling policy that scales the number of\ninstances in a service by a fixed number of instances at each step. It uses\na metric to determine when to scale up or down." + }, + "Autoscale.AdjustmentType": { + "type": "string", + "enum": [ + "change", + "exact", + "percentage" + ], + "description": "AdjustmentType defines the type of adjustment to be made in an autoscaling\nstep policy." + }, + "Autoscale.AutoscalePolicyStep": { + "type": "object", + "required": [ + "adjustment" + ], + "properties": { + "adjustment": { + "type": "integer", + "description": "The adjustment value for the step." + }, + "lower_bound": { + "type": "integer", + "description": "Lower bound for the step." + }, + "upper_bound": { + "type": "integer", + "description": "Upper bound for the step." + } + }, + "description": "A single step in a step autoscaling policy." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_autoscale_configurations_by_service_group_uuid", + "kind": "platform", + "path": "/v1/services/{uuid}/autoscale", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Get Autoscale Configurations by Service Group UUID", + "description": "Get autoscale configurations for a service group by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_delete_autoscale_configurations_by_service_group_uuid", + "kind": "platform", + "path": "/v1/services/{uuid}/autoscale", + "method": "DELETE", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Delete Autoscale Configurations by Service Group UUID", + "description": "Delete the autoscale configuration for a service group by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_create_autoscale_configuration_policy", + "kind": "platform", + "path": "/v1/services/{uuid}/autoscale/policies", + "method": "POST", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Create Autoscale Configuration Policy", + "description": "Add a new autoscale policy to a service group by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Autoscale.CreateAutoscaleConfigurationPolicyRequest" + } + }, + "required": [ + "path", + "body" + ], + "definitions": { + "Autoscale.CreateAutoscaleConfigurationPolicyRequest": { + "type": "object", + "required": [ + "name", + "type" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the policy." + }, + "type": { + "allOf": [ + { + "$ref": "#/definitions/Autoscale.AutoscalePolicy" + } + ], + "description": "The policy type to add to the autoscale configuration." + } + }, + "description": "The request message to create an autoscale configuration policy for a\nservice." + }, + "Autoscale.AutoscalePolicy": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the policy." + }, + "enabled": { + "type": "boolean", + "description": "Whether the policy is enabled." + }, + "metric": { + "allOf": [ + { + "$ref": "#/definitions/Autoscale.StepPolicyMetric" + } + ], + "description": "Metric to use for the step policy (only for step policies)." + }, + "adjustment_type": { + "allOf": [ + { + "$ref": "#/definitions/Autoscale.AdjustmentType" + } + ], + "description": "The type of adjustment to be made in the step policy (only for step policies)." + }, + "steps": { + "type": "array", + "items": { + "$ref": "#/definitions/Autoscale.AutoscalePolicyStep" + }, + "description": "The steps for the step policy. Each step defines an adjustment value and\noptional bounds." + } + }, + "description": "AutoscalePolicy defines the autoscale policy for a service. Right now it\ncontains fields from both the `ondemand` and `step` policies. They are\nmarked both as optional, so only one of them should be set at a time. This\nis a current limitation of the API design." + }, + "Autoscale.StepPolicyMetric": { + "type": "string", + "enum": [ + "cpu" + ], + "description": "The step policy is a type of autoscaling policy that scales the number of\ninstances in a service by a fixed number of instances at each step. It uses\na metric to determine when to scale up or down." + }, + "Autoscale.AdjustmentType": { + "type": "string", + "enum": [ + "change", + "exact", + "percentage" + ], + "description": "AdjustmentType defines the type of adjustment to be made in an autoscaling\nstep policy." + }, + "Autoscale.AutoscalePolicyStep": { + "type": "object", + "required": [ + "adjustment" + ], + "properties": { + "adjustment": { + "type": "integer", + "description": "The adjustment value for the step." + }, + "lower_bound": { + "type": "integer", + "description": "Lower bound for the step." + }, + "upper_bound": { + "type": "integer", + "description": "Upper bound for the step." + } + }, + "description": "A single step in a step autoscaling policy." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_autoscale_configuration_policies", + "kind": "platform", + "path": "/v1/services/{uuid}/autoscale/policies", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "List Autoscale Configuration Policies", + "description": "List autoscale policies for a service group by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Autoscale.GetAutoscaleConfigurationPolicyRequest" + } + }, + "required": [ + "path" + ], + "definitions": { + "Autoscale.GetAutoscaleConfigurationPolicyRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "The Name of the policy to get." + } + }, + "description": "The request message to get an autoscale configuration policy by name." + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_delete_autoscale_configuration_policies", + "kind": "platform", + "path": "/v1/services/{uuid}/autoscale/policies", + "method": "DELETE", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Delete Autoscale Configuration Policies", + "description": "Delete autoscale policies for a service group by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Autoscale.DeletePolicyRequest" + } + }, + "required": [ + "path", + "body" + ], + "definitions": { + "Autoscale.DeletePolicyRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the policy to delete." + } + }, + "description": "The request message to delete an autoscale configuration policy by name." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_autoscale_configuration_policy_by_name", + "kind": "platform", + "path": "/v1/services/{uuid}/autoscale/policies/{name}", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + }, + { + "name": "name", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Get Autoscale Configuration Policy by Name", + "description": "Get an autoscale policy by name for a service group by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + } + }, + "required": [ + "uuid", + "name" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_delete_autoscale_configuration_policy_by_name", + "kind": "platform", + "path": "/v1/services/{uuid}/autoscale/policies/{name}", + "method": "DELETE", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + }, + { + "name": "name", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Delete Autoscale Configuration Policy by Name", + "description": "Delete an autoscale policy by name for a service group by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + } + }, + "required": [ + "uuid", + "name" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_user", + "kind": "platform", + "path": "/v1/users/quotas", + "method": "GET", + "parameters": [], + "title": "Get Current User Quotas", + "description": "List quota usage and limits of your user account.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [] + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_user_by_uuid", + "kind": "platform", + "path": "/v1/users/{uuid}/quotas", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Get User Quotas by UUID", + "description": "List quota usage and limits of a user account by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_create_volume", + "kind": "platform", + "path": "/v1/volumes", + "method": "POST", + "parameters": [], + "bodyContentType": "application/json", + "title": "Create Volume", + "description": "Create a new volume.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Volumes.CreateVolumeRequest" + } + }, + "required": [ + "body" + ], + "definitions": { + "Volumes.CreateVolumeRequest": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "size_mb": { + "type": "integer", + "minimum": 0 + }, + "host_path": { + "type": "string" + }, + "template": { + "$ref": "#/definitions/Common.NameOrUUID" + }, + "quota_policy": { + "$ref": "#/definitions/Volumes.VolumeQuotaPolicy" + }, + "filesystem": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "uid": { + "type": "integer", + "minimum": 0 + }, + "gid": { + "type": "integer", + "minimum": 0 + }, + "access_mode": { + "$ref": "#/definitions/Volumes.VolumeAccessMode" + }, + "args": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "description": "The request message for creating a volume.", + "oneOf": [ + { + "required": [ + "size_mb" + ] + }, + { + "required": [ + "host_path" + ] + }, + { + "required": [ + "template" + ] + } + ] + }, + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + }, + "Volumes.VolumeQuotaPolicy": { + "type": "string", + "enum": [ + "static", + "dynamic" + ], + "description": "Quota policy for a volume." + }, + "Volumes.VolumeAccessMode": { + "type": "string", + "enum": [ + "rwo", + "rox", + "rwx" + ], + "description": "VolumeAccessMode defines the access mode of a volume, which controls sharing\nbehavior and caching strategy." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_volumes", + "kind": "platform", + "path": "/v1/volumes", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "name", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "details", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "count", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "from", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "order", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "sortby", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "tags", + "group": "query", + "style": "form", + "explode": false + } + ], + "bodyContentType": "application/json", + "title": "List Volumes", + "description": "List volumes.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "query": { + "type": "object", + "properties": { + "uuid": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The UUID of the resource." + }, + "name": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The name of the resource." + }, + "details": { + "type": "boolean" + }, + "count": { + "type": "integer", + "minimum": 0 + }, + "from": { + "type": "string" + }, + "order": { + "$ref": "#/definitions/Common.PaginationOrder" + }, + "sortby": { + "$ref": "#/definitions/Common.PaginationSortBy" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [], + "definitions": { + "Common.PaginationOrder": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "description": "The sort order used by list endpoints." + }, + "Common.PaginationSortBy": { + "type": "string", + "enum": [ + "create_time" + ], + "description": "The sort field used by list endpoints." + }, + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_update_volumes", + "kind": "platform", + "path": "/v1/volumes", + "method": "PATCH", + "parameters": [], + "bodyContentType": "application/json", + "title": "Update Volumes", + "description": "Update volumes.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Volumes.UpdateVolumesRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Volumes.UpdateVolumesRequestItem": { + "type": "object", + "required": [ + "prop", + "op" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "id": { + "type": "string", + "description": "A client-provided identifier for tracking this operation in the response." + }, + "prop": { + "allOf": [ + { + "$ref": "#/definitions/Volumes.MutableVolumeProperty" + } + ], + "description": "The property to modify." + }, + "op": { + "allOf": [ + { + "$ref": "#/definitions/Volumes.MutableVolumeOperation" + } + ], + "description": "The operation to perform." + }, + "value": { + "description": "The value for the update operation. The type depends on the property and operation:\n- For \"image\": string\n- For \"args\": string or array of strings\n- For \"env\": object (for SET/ADD) or string/array of strings (for DEL)\n- For \"memory_mb\": integer\n- For \"vcpus\": integer\n- For \"scale_to_zero\": object with cooldown_time_ms, policy, and stateful fields\n- For \"tags\": array of strings\n- For \"delete_lock\": boolean\n- For \"schedules\": array of schedule objects (with name, when, action, and optional args fields).\nUse action \"exec\" together with args to execute a command at the scheduled time.\n- For \"autokill\": object with time_ms and num_requests fields\n- For \"hostname\": string (valid DNS label)\n- For \"roms\": array of ROM objects (with name and image fields) for SET/ADD, or array of ROM names for DEL\n- For \"dependencies\": array of instance identifiers (name or UUID)\n- For \"sched_priority\": SchedPriority enum value (\"normal\", \"medium\", \"high\", \"admin\")" + } + }, + "description": "A single update operation to be applied to a volume." + }, + "Volumes.MutableVolumeProperty": { + "type": "string", + "enum": [ + "size_mb", + "tags", + "quota_policy", + "delete_lock" + ], + "description": "Mutable volume properties." + }, + "Volumes.MutableVolumeOperation": { + "type": "string", + "enum": [ + "set", + "add", + "del" + ], + "description": "Mutable volume operations." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_delete_volumes", + "kind": "platform", + "path": "/v1/volumes", + "method": "DELETE", + "parameters": [], + "bodyContentType": "application/json", + "title": "Delete Volumes", + "description": "Delete volumes by ID(s).", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_attach_volumes", + "kind": "platform", + "path": "/v1/volumes/attach", + "method": "PUT", + "parameters": [], + "bodyContentType": "application/json", + "title": "Attach Volumes", + "description": "Attach volumes to instances.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Volumes.AttachVolumesRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Volumes.AttachVolumesRequestItem": { + "type": "object", + "required": [ + "attach_to", + "at" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "attach_to": { + "allOf": [ + { + "$ref": "#/definitions/Common.NameOrUUID" + } + ], + "description": "UUID or name of the instance to attach the volume to." + }, + "at": { + "type": "string", + "description": "Path of the mountpoint.\n\nThe path must be absolute, not contain `.` and `..` components, and not\ncontain colons (`:`). The path must point to an empty directory. If the\ndirectory does not exist, it is created." + }, + "readonly": { + "type": "boolean", + "description": "Whether the volume should be mounted read-only." + } + }, + "description": "A single request item to attach a volume to an instance." + }, + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_clone_volumes", + "kind": "platform", + "path": "/v1/volumes/clone", + "method": "POST", + "parameters": [], + "bodyContentType": "application/json", + "title": "Clone Volumes", + "description": "Clone volumes.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Volumes.CloneVolumesRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Volumes.CloneVolumesRequestItem": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "vol_name": { + "type": "string", + "description": "The name of the new cloned volume. If not provided, a random name\nof the form `vol-X` is generated for you, where `X` is a 5 character\nlong random alphanumeric suffix." + }, + "quota_policy": { + "allOf": [ + { + "$ref": "#/definitions/Volumes.VolumeQuotaPolicy" + } + ], + "description": "The quota policy for the new cloned volume. If not provided, the quota\npolicy of the source volume is used." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "A list of tags to assign to the new cloned volume." + }, + "access_mode": { + "$ref": "#/definitions/Volumes.VolumeAccessMode" + } + }, + "description": "A single request item to clone a volume." + }, + "Volumes.VolumeQuotaPolicy": { + "type": "string", + "enum": [ + "static", + "dynamic" + ], + "description": "Quota policy for a volume." + }, + "Volumes.VolumeAccessMode": { + "type": "string", + "enum": [ + "rwo", + "rox", + "rwx" + ], + "description": "VolumeAccessMode defines the access mode of a volume, which controls sharing\nbehavior and caching strategy." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_detach_volumes", + "kind": "platform", + "path": "/v1/volumes/detach", + "method": "PUT", + "parameters": [], + "bodyContentType": "application/json", + "title": "Detach Volumes", + "description": "Detach volumes from instances.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Volumes.DetachVolumesRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Volumes.DetachVolumesRequestItem": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "from": { + "allOf": [ + { + "$ref": "#/definitions/Common.NameOrUUID" + } + ], + "description": "UUID or name of the instance to detach the volume from. If not specified,\nthe volume is detached from all instances." + } + }, + "description": "A single request item to detach a volume from an instance." + }, + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_create_template_volume", + "kind": "platform", + "path": "/v1/volumes/templates", + "method": "POST", + "parameters": [], + "bodyContentType": "application/json", + "title": "Create Template Volume", + "description": "Create a template volume.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_template_volumes", + "kind": "platform", + "path": "/v1/volumes/templates", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "name", + "group": "query", + "style": "form", + "explode": true + }, + { + "name": "details", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "count", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "from", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "order", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "sortby", + "group": "query", + "style": "form", + "explode": false + }, + { + "name": "tags", + "group": "query", + "style": "form", + "explode": false + } + ], + "bodyContentType": "application/json", + "title": "List Template Volumes", + "description": "List template volumes.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "query": { + "type": "object", + "properties": { + "uuid": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The UUID of the resource." + }, + "name": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The name of the resource." + }, + "details": { + "type": "boolean" + }, + "count": { + "type": "integer", + "minimum": 0 + }, + "from": { + "type": "string" + }, + "order": { + "$ref": "#/definitions/Common.PaginationOrder" + }, + "sortby": { + "$ref": "#/definitions/Common.PaginationSortBy" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [], + "definitions": { + "Common.PaginationOrder": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "description": "The sort order used by list endpoints." + }, + "Common.PaginationSortBy": { + "type": "string", + "enum": [ + "create_time" + ], + "description": "The sort field used by list endpoints." + }, + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_update_template_volumes", + "kind": "platform", + "path": "/v1/volumes/templates", + "method": "PATCH", + "parameters": [], + "bodyContentType": "application/json", + "title": "Update Template Volumes", + "description": "Update template volumes.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Volumes.UpdateTemplateVolumesRequestItem" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Volumes.UpdateTemplateVolumesRequestItem": { + "type": "object", + "required": [ + "prop", + "op" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + }, + "id": { + "type": "string", + "description": "A client-provided identifier for tracking this operation in the response." + }, + "prop": { + "allOf": [ + { + "$ref": "#/definitions/Volumes.MutableTemplateVolumeProperty" + } + ], + "description": "The property to modify." + }, + "op": { + "allOf": [ + { + "$ref": "#/definitions/Volumes.MutableTemplateVolumeOperation" + } + ], + "description": "The operation to perform." + }, + "value": { + "description": "The value for the update operation. The type depends on the property and operation:\n- For \"image\": string\n- For \"args\": string or array of strings\n- For \"env\": object (for SET/ADD) or string/array of strings (for DEL)\n- For \"memory_mb\": integer\n- For \"vcpus\": integer\n- For \"scale_to_zero\": object with cooldown_time_ms, policy, and stateful fields\n- For \"tags\": array of strings\n- For \"delete_lock\": boolean\n- For \"schedules\": array of schedule objects (with name, when, action, and optional args fields).\nUse action \"exec\" together with args to execute a command at the scheduled time.\n- For \"autokill\": object with time_ms and num_requests fields\n- For \"hostname\": string (valid DNS label)\n- For \"roms\": array of ROM objects (with name and image fields) for SET/ADD, or array of ROM names for DEL\n- For \"dependencies\": array of instance identifiers (name or UUID)\n- For \"sched_priority\": SchedPriority enum value (\"normal\", \"medium\", \"high\", \"admin\")" + } + }, + "description": "A single update operation to be applied to a template volume." + }, + "Volumes.MutableTemplateVolumeProperty": { + "type": "string", + "enum": [ + "tags", + "delete_lock" + ], + "description": "Mutable template volume properties." + }, + "Volumes.MutableTemplateVolumeOperation": { + "type": "string", + "enum": [ + "set", + "add", + "del" + ], + "description": "Mutable template volume operations." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_delete_template_volumes", + "kind": "platform", + "path": "/v1/volumes/templates", + "method": "DELETE", + "parameters": [], + "bodyContentType": "application/json", + "title": "Delete Template Volumes", + "description": "Delete template volumes by ID(s).", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "type": "array", + "items": { + "$ref": "#/definitions/Common.NameOrUUID" + } + } + }, + "required": [ + "body" + ], + "definitions": { + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_template_volume_by_uuid", + "kind": "platform", + "path": "/v1/volumes/templates/{uuid}", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + }, + { + "name": "details", + "group": "query", + "style": "form", + "explode": false + } + ], + "title": "Get Template Volume by UUID", + "description": "Get a template volume by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "query": { + "type": "object", + "properties": { + "details": { + "type": "boolean" + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_update_template_volume_by_uuid", + "kind": "platform", + "path": "/v1/volumes/templates/{uuid}", + "method": "PATCH", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Update Template Volume by UUID", + "description": "Update a template volume by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Volumes.UpdateTemplateVolumeByUUIDRequestBody" + } + }, + "required": [ + "path", + "body" + ], + "definitions": { + "Volumes.UpdateTemplateVolumeByUUIDRequestBody": { + "type": "object", + "required": [ + "prop", + "op" + ], + "properties": { + "id": { + "type": "string", + "description": "A client-provided identifier for tracking this operation in the response." + }, + "prop": { + "allOf": [ + { + "$ref": "#/definitions/Volumes.MutableTemplateVolumeProperty" + } + ], + "description": "The property to modify." + }, + "op": { + "allOf": [ + { + "$ref": "#/definitions/Volumes.MutableTemplateVolumeOperation" + } + ], + "description": "The operation to perform." + }, + "value": { + "description": "The value for the update operation. The type depends on the property and operation:\n- For \"image\": string\n- For \"args\": string or array of strings\n- For \"env\": object (for SET/ADD) or string/array of strings (for DEL)\n- For \"memory_mb\": integer\n- For \"vcpus\": integer\n- For \"scale_to_zero\": object with cooldown_time_ms, policy, and stateful fields\n- For \"tags\": array of strings\n- For \"delete_lock\": boolean\n- For \"schedules\": array of schedule objects (with name, when, action, and optional args fields).\nUse action \"exec\" together with args to execute a command at the scheduled time.\n- For \"autokill\": object with time_ms and num_requests fields\n- For \"hostname\": string (valid DNS label)\n- For \"roms\": array of ROM objects (with name and image fields) for SET/ADD, or array of ROM names for DEL\n- For \"dependencies\": array of instance identifiers (name or UUID)\n- For \"sched_priority\": SchedPriority enum value (\"normal\", \"medium\", \"high\", \"admin\")" + } + } + }, + "Volumes.MutableTemplateVolumeProperty": { + "type": "string", + "enum": [ + "tags", + "delete_lock" + ], + "description": "Mutable template volume properties." + }, + "Volumes.MutableTemplateVolumeOperation": { + "type": "string", + "enum": [ + "set", + "add", + "del" + ], + "description": "Mutable template volume operations." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_delete_template_volume_by_uuid", + "kind": "platform", + "path": "/v1/volumes/templates/{uuid}", + "method": "DELETE", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Delete Template Volume by UUID", + "description": "Delete a template volume by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_get_volume_by_uuid", + "kind": "platform", + "path": "/v1/volumes/{uuid}", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + }, + { + "name": "details", + "group": "query", + "style": "form", + "explode": false + } + ], + "title": "Get Volume by UUID", + "description": "Get a volume by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "query": { + "type": "object", + "properties": { + "details": { + "type": "boolean" + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_update_volume_by_uuid", + "kind": "platform", + "path": "/v1/volumes/{uuid}", + "method": "PATCH", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Update Volume by UUID", + "description": "Update a volume by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Volumes.UpdateVolumeByUUIDRequestBody" + } + }, + "required": [ + "path", + "body" + ], + "definitions": { + "Volumes.UpdateVolumeByUUIDRequestBody": { + "type": "object", + "required": [ + "prop", + "op" + ], + "properties": { + "id": { + "type": "string", + "description": "A client-provided identifier for tracking this operation in the response." + }, + "prop": { + "allOf": [ + { + "$ref": "#/definitions/Volumes.MutableVolumeProperty" + } + ], + "description": "The property to modify." + }, + "op": { + "allOf": [ + { + "$ref": "#/definitions/Volumes.MutableVolumeOperation" + } + ], + "description": "The operation to perform." + }, + "value": { + "description": "The value for the update operation. The type depends on the property and operation:\n- For \"image\": string\n- For \"args\": string or array of strings\n- For \"env\": object (for SET/ADD) or string/array of strings (for DEL)\n- For \"memory_mb\": integer\n- For \"vcpus\": integer\n- For \"scale_to_zero\": object with cooldown_time_ms, policy, and stateful fields\n- For \"tags\": array of strings\n- For \"delete_lock\": boolean\n- For \"schedules\": array of schedule objects (with name, when, action, and optional args fields).\nUse action \"exec\" together with args to execute a command at the scheduled time.\n- For \"autokill\": object with time_ms and num_requests fields\n- For \"hostname\": string (valid DNS label)\n- For \"roms\": array of ROM objects (with name and image fields) for SET/ADD, or array of ROM names for DEL\n- For \"dependencies\": array of instance identifiers (name or UUID)\n- For \"sched_priority\": SchedPriority enum value (\"normal\", \"medium\", \"high\", \"admin\")" + } + } + }, + "Volumes.MutableVolumeProperty": { + "type": "string", + "enum": [ + "size_mb", + "tags", + "quota_policy", + "delete_lock" + ], + "description": "Mutable volume properties." + }, + "Volumes.MutableVolumeOperation": { + "type": "string", + "enum": [ + "set", + "add", + "del" + ], + "description": "Mutable volume operations." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_delete_volume_by_uuid", + "kind": "platform", + "path": "/v1/volumes/{uuid}", + "method": "DELETE", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Delete Volume by UUID", + "description": "Delete a volume by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + } + }, + "required": [ + "path" + ] + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_attach_volume_by_uuid", + "kind": "platform", + "path": "/v1/volumes/{uuid}/attach", + "method": "PUT", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Attach Volume by UUID", + "description": "Attach a volume by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Volumes.AttachVolumeByUUIDRequestBody" + } + }, + "required": [ + "path", + "body" + ], + "definitions": { + "Volumes.AttachVolumeByUUIDRequestBody": { + "type": "object", + "required": [ + "attach_to", + "at" + ], + "properties": { + "attach_to": { + "allOf": [ + { + "$ref": "#/definitions/Common.NameOrUUID" + } + ], + "description": "UUID or name of the instance to attach the volume to." + }, + "at": { + "type": "string", + "description": "Path of the mountpoint.\n\nThe path must be absolute, not contain `.` and `..` components, and not\ncontain colons (`:`). The path must point to an empty directory. If the\ndirectory does not exist, it is created." + }, + "readonly": { + "type": "boolean", + "description": "Whether the volume should be mounted read-only." + } + } + }, + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_clone_volume_by_uuid", + "kind": "platform", + "path": "/v1/volumes/{uuid}/clone", + "method": "POST", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Clone Volume by UUID", + "description": "Clone a volume by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Volumes.CloneVolumeByUUIDRequestBody" + } + }, + "required": [ + "path", + "body" + ], + "definitions": { + "Volumes.CloneVolumeByUUIDRequestBody": { + "type": "object", + "properties": { + "vol_name": { + "type": "string", + "description": "The name of the new cloned volume. If not provided, a random name\nof the form `vol-X` is generated for you, where `X` is a 5 character\nlong random alphanumeric suffix." + }, + "quota_policy": { + "allOf": [ + { + "$ref": "#/definitions/Volumes.VolumeQuotaPolicy" + } + ], + "description": "The quota policy for the new cloned volume. If not provided, the quota\npolicy of the source volume is used." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "A list of tags to assign to the new cloned volume." + }, + "access_mode": { + "$ref": "#/definitions/Volumes.VolumeAccessMode" + } + } + }, + "Volumes.VolumeQuotaPolicy": { + "type": "string", + "enum": [ + "static", + "dynamic" + ], + "description": "Quota policy for a volume." + }, + "Volumes.VolumeAccessMode": { + "type": "string", + "enum": [ + "rwo", + "rox", + "rwx" + ], + "description": "VolumeAccessMode defines the access mode of a volume, which controls sharing\nbehavior and caching strategy." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_detach_volume_by_uuid", + "kind": "platform", + "path": "/v1/volumes/{uuid}/detach", + "method": "PUT", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Detach Volume by UUID", + "description": "Detach a volume by UUID.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "body": { + "$ref": "#/definitions/Volumes.DetachVolumeByUUIDRequestBody" + } + }, + "required": [ + "path", + "body" + ], + "definitions": { + "Volumes.DetachVolumeByUUIDRequestBody": { + "type": "object", + "properties": { + "from": { + "allOf": [ + { + "$ref": "#/definitions/Common.NameOrUUID" + } + ], + "description": "UUID or name of the instance to detach the volume from. If not specified,\nthe volume is detached from all instances." + } + } + }, + "Common.NameOrUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "The UUID of the resource." + }, + "name": { + "type": "string", + "description": "The name of the resource." + } + }, + "description": "An identifier for a resource — either a name or a UUID, but not both.", + "oneOf": [ + { + "required": [ + "uuid" + ], + "not": { + "required": [ + "name" + ] + } + }, + { + "required": [ + "name" + ], + "not": { + "required": [ + "uuid" + ] + } + } + ] + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_sandbox_list_commands", + "kind": "sandbox", + "path": "/commands", + "method": "GET", + "parameters": [], + "title": "List Commands", + "description": "List the UUIDs of all commands, in the order they were started.\n\nThis operation is also the plugin's readiness probe: it takes no\nparameters, changes nothing, and answers on an empty sandbox, so a client\ncan call it to establish that the plugin is serving before it sends any\nreal work. Hence `x-unikraft-plugin-readiness`, which the SDK generators\nread to render their readiness helpers against this operation.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "instance": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "description": "Name or UUID of the instance running the sandbox plugin." + } + }, + "required": [ + "instance" + ] + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_sandbox_run_command", + "kind": "sandbox", + "path": "/commands", + "method": "POST", + "parameters": [], + "bodyContentType": "application/json", + "title": "Start Command", + "description": "Start a command in the sandbox. `500` is returned when the command could\nnot be spawned.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "instance": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "description": "Name or UUID of the instance running the sandbox plugin." + }, + "body": { + "$ref": "#/definitions/RunCommandRequest" + } + }, + "required": [ + "instance", + "body" + ], + "definitions": { + "RunCommandRequest": { + "type": "object", + "required": [ + "cmd" + ], + "properties": { + "cmd": { + "anyOf": [ + { + "$ref": "#/definitions/CommandLineShell" + }, + { + "$ref": "#/definitions/CommandLineArgs" + } + ], + "description": "The command to run: a command line interpreted by the shell, or an argument\nvector executed directly. A bare program name in a vector is looked up in\nthe `PATH` of `env`, falling back to the sandbox's own `PATH`. `400` is\nreturned if the vector is empty or the program cannot be resolved." + }, + "cwd": { + "type": "string", + "description": "(OPTIONAL) Current working directory" + }, + "env": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "(OPTIONAL) Environment variables" + } + }, + "description": "The request to start a command in the sandbox." + }, + "CommandLineShell": { + "type": "string", + "description": "The command line, interpreted by the shell." + }, + "CommandLineArgs": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The command as an argument vector, executed directly without a shell." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_sandbox_get_command_by_uuid", + "kind": "sandbox", + "path": "/commands/{uuid}", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Get Command by UUID", + "description": "Inspect a command by UUID. `404` is returned, with an empty body, when no\nsuch command exists.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "instance": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "description": "Name or UUID of the instance running the sandbox plugin." + } + }, + "required": [ + "path", + "instance" + ] + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_sandbox_delete_command_by_uuid", + "kind": "sandbox", + "path": "/commands/{uuid}", + "method": "DELETE", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Delete Command by UUID", + "description": "Forget a command by UUID. `404` is returned, with an empty body, when no\nsuch command exists, and `409` when the command has not finished\nexecuting: commands which are still running cannot be forgotten.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "instance": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "description": "Name or UUID of the instance running the sandbox plugin." + } + }, + "required": [ + "path", + "instance" + ] + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_sandbox_delete_command_logs_by_uuid", + "kind": "sandbox", + "path": "/commands/{uuid}/logs", + "method": "DELETE", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Delete Command Logs", + "description": "Forget the output logs of an existing command, freeing up memory.\nThe stdin/out/err pipes of running commands will be closed. Idempotent.\n`404` is returned, with an empty body, when no such command exists.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "instance": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "description": "Name or UUID of the instance running the sandbox plugin." + } + }, + "required": [ + "path", + "instance" + ] + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_sandbox_get_command_logs", + "kind": "sandbox", + "path": "/commands/{uuid}/logs", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Get Command Logs", + "description": "Fetch the collected output of a command. Both streams are base64-encoded;\nuse the raw log endpoint for binary output. `404` is returned, with an\nempty body, when no such command exists. `410` is returned, with an empty body,\nif the command's logs have been deleted.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "instance": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "description": "Name or UUID of the instance running the sandbox plugin." + }, + "body": { + "$ref": "#/definitions/CommandLogsRequest" + } + }, + "required": [ + "path", + "instance" + ], + "definitions": { + "CommandLogsRequest": { + "type": "object", + "required": [ + "stdout", + "stderr" + ], + "properties": { + "stdout": { + "allOf": [ + { + "$ref": "#/definitions/CommandLogsRange" + } + ], + "description": "The part of the standard output stream to return." + }, + "stderr": { + "allOf": [ + { + "$ref": "#/definitions/CommandLogsRange" + } + ], + "description": "The part of the standard error stream to return." + } + }, + "description": "The (optional) request body limiting which parts of the output streams are\nreturned. When omitted, both streams are returned in their entirety." + }, + "CommandLogsRange": { + "type": "object", + "required": [ + "offset" + ], + "properties": { + "offset": { + "type": "integer", + "description": "The first byte of the stream to return.", + "default": 0, + "minimum": 0 + }, + "limit": { + "type": "integer", + "description": "The maximum number of bytes to return. When omitted, everything from\n`offset` to the end of the stream is returned.", + "minimum": 0 + } + }, + "description": "The byte range of a single output stream to return." + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_sandbox_get_raw_command_log", + "kind": "sandbox", + "path": "/commands/{uuid}/logs/raw/{stream}", + "method": "GET", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + }, + { + "name": "stream", + "group": "path", + "style": "form", + "explode": true + }, + { + "name": "Range", + "group": "headers", + "style": "form", + "explode": true + } + ], + "title": "Get Raw Command Log", + "description": "Fetch an output stream of a command as raw bytes. A single contiguous\n`Range` header is honoured, as described in RFC 9110.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "stream": { + "$ref": "#/definitions/CommandLogStream" + } + }, + "required": [ + "uuid", + "stream" + ], + "additionalProperties": false + }, + "headers": { + "type": "object", + "properties": { + "Range": { + "type": "string", + "description": "A single contiguous byte range, e.g. `bytes=0-1023`." + } + }, + "required": [], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "instance": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "description": "Name or UUID of the instance running the sandbox plugin." + } + }, + "required": [ + "path", + "instance" + ], + "definitions": { + "CommandLogStream": { + "type": "string", + "enum": [ + "stdout", + "stderr" + ], + "description": "The output stream to read raw bytes from." + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_sandbox_signal_command", + "kind": "sandbox", + "path": "/commands/{uuid}/signal", + "method": "POST", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Signal Command", + "description": "Send a signal to a command. `404` is returned, with an empty body, when no\nsuch command exists, and `400` when the signal is not a known signal name.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "instance": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "description": "Name or UUID of the instance running the sandbox plugin." + }, + "body": { + "$ref": "#/definitions/CommandSignalRequest" + } + }, + "required": [ + "path", + "instance", + "body" + ], + "definitions": { + "CommandSignalRequest": { + "type": "object", + "required": [ + "signal" + ], + "properties": { + "signal": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "description": "The signal to send, either as a number or as a name, with or without the\n`SIG` prefix, e.g. `15`, `TERM` or `SIGTERM`." + } + }, + "description": "The request to send a signal to a command." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_sandbox_write_command_stdin", + "kind": "sandbox", + "path": "/commands/{uuid}/stdin", + "method": "POST", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Write Command Stdin", + "description": "Feed data into the standard input of a command. `404` is returned, with an\nempty body, when no such command exists, `410` if the command's logs have been\ndeleted, and `400` when `data` is not valid base64.\nFeeding data into a command whose standard input has been closed is an error.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "instance": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "description": "Name or UUID of the instance running the sandbox plugin." + }, + "body": { + "$ref": "#/definitions/CommandStdinRequest" + } + }, + "required": [ + "path", + "instance", + "body" + ], + "definitions": { + "CommandStdinRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "string", + "description": "The bytes to feed into standard input, base64-encoded." + }, + "eof": { + "type": "boolean", + "description": "Whether to close standard input after injecting `data`." + } + }, + "description": "The request to feed data into a command's standard input." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": true, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_sandbox_wait_for_command", + "kind": "sandbox", + "path": "/commands/{uuid}/wait", + "method": "POST", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "title": "Wait for Command", + "description": "Wait for a command to finish, without a timeout. `404` is returned, with an\nempty body, when no such command exists.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "instance": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "description": "Name or UUID of the instance running the sandbox plugin." + } + }, + "required": [ + "path", + "instance" + ] + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_sandbox_wait_for_command_with_timeout", + "kind": "sandbox", + "path": "/commands/{uuid}/wait_timeout", + "method": "POST", + "parameters": [ + { + "name": "uuid", + "group": "path", + "style": "form", + "explode": true + } + ], + "bodyContentType": "application/json", + "title": "Wait for Command with Timeout", + "description": "Wait for a command to finish, up to a timeout. Commands which are still\nrunning when the timeout elapses return `408`. `404` is returned, with an\nempty body, when no such command exists, and `400` when the timeout is not\na representable duration.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "uuid" + ], + "additionalProperties": false + }, + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "instance": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "description": "Name or UUID of the instance running the sandbox plugin." + }, + "body": { + "$ref": "#/definitions/CommandWaitTimeoutRequest" + } + }, + "required": [ + "path", + "instance", + "body" + ], + "definitions": { + "CommandWaitTimeoutRequest": { + "type": "object", + "required": [ + "timeout_s" + ], + "properties": { + "timeout_s": { + "type": "number", + "description": "The maximum time to wait, in seconds. Decimals are accepted." + } + }, + "description": "The request to wait for a command to finish, up to a timeout." + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_sandbox_create_directory", + "kind": "sandbox", + "path": "/fs/mkdir", + "method": "POST", + "parameters": [], + "bodyContentType": "application/json", + "title": "Create Directory", + "description": "Create a directory in the sandbox filesystem. `500` is returned when the\ndirectory could not be created.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "instance": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "description": "Name or UUID of the instance running the sandbox plugin." + }, + "body": { + "$ref": "#/definitions/MkdirRequest" + } + }, + "required": [ + "instance", + "body" + ], + "definitions": { + "MkdirRequest": { + "type": "object", + "required": [ + "path", + "parents" + ], + "properties": { + "path": { + "type": "string", + "description": "The absolute path of the directory to create." + }, + "parents": { + "type": "boolean", + "description": "Whether to create missing parent directories." + } + }, + "description": "The request to create a directory in the sandbox filesystem." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_sandbox_read_file", + "kind": "sandbox", + "path": "/fs/read", + "method": "POST", + "parameters": [], + "bodyContentType": "application/json", + "title": "Read File", + "description": "Read the contents of a file, base64-encoded. `500` is returned when the\nfile could not be read.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "instance": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "description": "Name or UUID of the instance running the sandbox plugin." + }, + "body": { + "$ref": "#/definitions/ReadFileRequest" + } + }, + "required": [ + "instance", + "body" + ], + "definitions": { + "ReadFileRequest": { + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string", + "description": "The absolute path of the file to read." + } + }, + "description": "The request to read a file from the sandbox filesystem." + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_sandbox_read_raw_file", + "kind": "sandbox", + "path": "/fs/read_raw", + "method": "POST", + "parameters": [], + "bodyContentType": "application/json", + "title": "Read Raw File", + "description": "Download the contents of a file verbatim.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "instance": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "description": "Name or UUID of the instance running the sandbox plugin." + }, + "body": { + "$ref": "#/definitions/ReadFileRequest" + } + }, + "required": [ + "instance", + "body" + ], + "definitions": { + "ReadFileRequest": { + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string", + "description": "The absolute path of the file to read." + } + }, + "description": "The request to read a file from the sandbox filesystem." + } + } + }, + "annotations": { + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": true + } + }, + { + "name": "unikraft_sandbox_upload_file", + "kind": "sandbox", + "path": "/fs/upload", + "method": "POST", + "parameters": [], + "bodyContentType": "application/json", + "title": "Upload File", + "description": "Upload a file to the sandbox at a given path. A new file will be created at\n- if `path` ends with '/' or exists and is a directory: path/filename\n- else: path\n`400` is returned when `encoding` is not a known encoding or `data` is not\nvalid base64, and `500` when the file could not be created.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "instance": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "description": "Name or UUID of the instance running the sandbox plugin." + }, + "body": { + "$ref": "#/definitions/UploadFileRequest" + } + }, + "required": [ + "instance", + "body" + ], + "definitions": { + "UploadFileRequest": { + "type": "object", + "required": [ + "path", + "filename", + "parents", + "encoding", + "data" + ], + "properties": { + "path": { + "type": "string", + "description": "Path where to upload the file." + }, + "filename": { + "type": "string", + "description": "Name of uploaded file; will be used if `path` is a directory." + }, + "parents": { + "type": "boolean", + "description": "Whether to create parent directories of `path`." + }, + "encoding": { + "allOf": [ + { + "$ref": "#/definitions/FileEncoding" + } + ], + "description": "The encoding of `data`." + }, + "data": { + "type": "string", + "description": "The data to write, in the given encoding" + } + }, + "description": "The request to upload a file to the sandbox filesystem." + }, + "FileEncoding": { + "type": "string", + "enum": [ + "utf-8", + "base64" + ], + "description": "The encoding of the data written to a file." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + }, + { + "name": "unikraft_sandbox_write_file", + "kind": "sandbox", + "path": "/fs/write", + "method": "POST", + "parameters": [], + "bodyContentType": "application/json", + "title": "Write File", + "description": "Write data to a file, creating it if it does not exist. `400` is returned\nwhen `encoding` is not a known encoding or `data` is not valid base64, and\n`500` when the file could not be written.", + "inputSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "metro": { + "type": "string", + "enum": [ + "dal", + "fra", + "sfo", + "sin", + "was" + ], + "description": "Unikraft Cloud metro. Defaults to the connection's default metro." + }, + "instance": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "description": "Name or UUID of the instance running the sandbox plugin." + }, + "body": { + "$ref": "#/definitions/WriteFileRequest" + } + }, + "required": [ + "instance", + "body" + ], + "definitions": { + "WriteFileRequest": { + "type": "object", + "required": [ + "path", + "append", + "encoding", + "data" + ], + "properties": { + "path": { + "type": "string", + "description": "The absolute path of the file to write. It is created if missing." + }, + "append": { + "type": "boolean", + "description": "Whether to append to the file instead of truncating it." + }, + "encoding": { + "allOf": [ + { + "$ref": "#/definitions/FileEncoding" + } + ], + "description": "The encoding of `data`." + }, + "data": { + "type": "string", + "description": "The data to write, in the given encoding." + } + }, + "description": "The request to write a file in the sandbox filesystem." + }, + "FileEncoding": { + "type": "string", + "enum": [ + "utf-8", + "base64" + ], + "description": "The encoding of the data written to a file." + } + } + }, + "annotations": { + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true + } + } + ], + "streaming": [ + { + "name": "unikraft_subscribe_audit_events", + "kind": "platform", + "path": "/v1/audit", + "method": "GET", + "transport": "server-sent-events" + } + ] +} diff --git a/server/src/services/daytona.ts b/server/src/services/daytona.ts index 81529e5b867..76a78b63c0c 100644 --- a/server/src/services/daytona.ts +++ b/server/src/services/daytona.ts @@ -1,62 +1,27 @@ -import Ajv, { type ValidateFunction } from "ajv"; -import addFormats from "ajv-formats"; import { z } from "zod"; -import { HttpError } from "../errors.js"; -import { redactSensitiveText } from "../redaction.js"; -import { descriptorHash, stableHash } from "./tool-catalog-hash.js"; +import { NATIVE_REST_MAX_BYTES as MAX_BYTES, nativeCatalog, nativeRestFailure as failure, nativeRestPathValue, nativeRestRecord as record, + nativeRestSender, type NativeCatalogEntry, type NativeRestRequest } from "./native-rest-client.js"; import catalog from "./daytona/operations.generated.json" with { type: "json" }; export const DAYTONA_API_URL = "https://app.daytona.io/api"; // Preserve the saved method identity when upgrading the earlier small catalog. export const DAYTONA_METHOD = "workspace-diagnostics"; -const MAX_BYTES = 8 * 1024 * 1024; -const record = (value: unknown): Record => - value && typeof value === "object" && !Array.isArray(value) ? value as Record : {}; -const failure = (status: number, code: string, message: string) => new HttpError(status, message, { code }); -export type DaytonaRequest = (url: string, init: RequestInit) => Promise; +export type DaytonaRequest = NativeRestRequest; type Operation = typeof catalog.tools[number]; -const operations = new Map(catalog.tools.map(operation => [operation.name, operation])); -const validators = new Map(); -// Match the interop used by the existing plugin configuration validator. -const AjvCtor = (Ajv as any).default ?? Ajv; -const ajv = new AjvCtor({ strict: false, allErrors: true, validateFormats: true }); -((addFormats as any).default ?? addFormats)(ajv); +const contract = nativeCatalog(catalog.tools); -export const DAYTONA_TOOLS = catalog.tools.map(({ name, title, description, inputSchema, annotations }) => ({ - name, title, description, inputSchema: inputSchema as Record, annotations, -})); - -export function isDaytonaToolName(name: string): boolean { - return operations.has(name); -} - -const currentContracts = new Map(DAYTONA_TOOLS.map(tool => [tool.name, { - versionHash: descriptorHash(tool, daytonaRisk(tool.name)), schemaHash: stableHash(tool.inputSchema), riskLevel: daytonaRisk(tool.name), -}])); - -export function isCurrentDaytonaCatalogEntry(entry: { toolName: string; versionHash: string; schemaHash: string | null; riskLevel: string }): boolean { - const current = currentContracts.get(entry.toolName); - return Boolean(current && current.versionHash === entry.versionHash && current.schemaHash === entry.schemaHash && current.riskLevel === entry.riskLevel); -} +export const DAYTONA_TOOLS = contract.tools; +export const isDaytonaToolName = contract.isToolName; +export const daytonaRisk = contract.risk; +export const isCurrentDaytonaCatalogEntry = (entry: NativeCatalogEntry) => contract.isCurrentEntry(entry); export function isDaytonaConnection(connection: { transport: string; config?: Record }) { return connection.transport === "rest_api" && connection.config?.sourceTemplateKey === "daytona" && connection.config.connectionMethodKey === DAYTONA_METHOD && connection.config.url === DAYTONA_API_URL; } -export function daytonaRisk(name: string): "read" | "write" | "destructive" { - const operation = operations.get(name); - if (!operation || operation.annotations.destructiveHint) return "destructive"; - return operation.annotations.readOnlyHint ? "read" : "write"; -} - function pathValue(value: unknown) { - const text = String(value); - if (!text || text.length > 8192 || /[\u0000-\u001f\\]/.test(text) - || text.split("/").some(segment => segment === "." || segment === "..")) { - throw failure(422, "daytona_arguments_invalid", "Invalid Daytona path argument."); - } - return encodeURIComponent(text); + return nativeRestPathValue(value, () => failure(422, "daytona_arguments_invalid", "Invalid Daytona path argument.")); } function decodeFile(value: unknown) { @@ -77,78 +42,11 @@ export function daytonaClient(headers: Record, request: DaytonaR if (!authorization || !/^Bearer [^\r\n]+$/.test(authorization)) { throw failure(422, "daytona_credential_missing", "Reconnect Daytona to restore its API key."); } - const secret = authorization.slice(7); - const expired = () => failure(504, "daytona_deadline_exceeded", "The Daytona action deadline expired. An operation already sent may still be running; inspect it before retrying."); - // Bound the caller even if a transport or body reader is slow to acknowledge - // cancellation. A late prerequisite result never advances to the next call. - async function withinDeadline(work: () => Promise): Promise { - if (signal.aborted || Date.now() >= deadlineAt) throw expired(); - let abort!: () => void; - try { - return await Promise.race([ - Promise.resolve().then(() => { if (signal.aborted || Date.now() >= deadlineAt) throw expired(); return work(); }), - new Promise((_resolve, reject) => { - abort = () => reject(expired()); - signal.addEventListener("abort", abort, { once: true }); - }), - ]); - } finally { signal.removeEventListener("abort", abort); } - } - function sanitize(value: unknown): unknown { - if (typeof value === "string") return redactSensitiveText(value.split(secret).join("[REDACTED]")); - if (Array.isArray(value)) return value.map(sanitize); - if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, sanitize(item)])); - return value; - } - - async function send(url: string, init: RequestInit, organizationId?: string): Promise { - const projected = new Headers(init.headers); - projected.set("Authorization", authorization!); - projected.set("X-Daytona-Source", "paperclip-connector"); - if (organizationId) projected.set("X-Daytona-Organization-ID", organizationId); - let response: Response; - try { - response = await withinDeadline(() => request(url, { ...init, redirect: "error", headers: Object.fromEntries(projected), signal })); - } catch { - if (signal.aborted || Date.now() >= deadlineAt) throw expired(); - throw failure(502, "daytona_outcome_unknown", init.method === "GET" - ? "Daytona could not complete the request." - : "Daytona did not return a result. The operation may have started; inspect its state before retrying."); - } - if (!response.ok) { - void response.body?.cancel().catch(() => {}); - const status = [400, 401, 403, 404, 409, 422, 429].includes(response.status) ? response.status : 502; - throw failure(status, "daytona_request_failed", status === 401 || status === 403 - ? "Daytona denied access. Check this key's organization and permissions. Some account operations require provider user authentication." - : status === 404 ? "Daytona could not find this resource." : "Daytona could not complete this operation."); - } - if (response.status === 204 || !response.body) return null; - const reader = response.body.getReader(); - const chunks: Uint8Array[] = []; - let length = 0; - try { - for (;;) { - const part = await withinDeadline(() => reader.read()); - if (part.done) break; - length += part.value.byteLength; - if (length > MAX_BYTES) throw failure(502, "daytona_output_limit", "Daytona's response exceeded the limit. Request a smaller page or file range."); - chunks.push(part.value); - } - } catch (error) { - if (error instanceof HttpError) throw error; - throw failure(502, "daytona_outcome_unknown", "Daytona's response was interrupted. Inspect the operation before retrying."); - } finally { void reader.cancel().catch(() => {}); } - const bytes = Buffer.concat(chunks); - if (!bytes.length) return null; - const contentType = response.headers.get("Content-Type") ?? "application/octet-stream"; - if (contentType.includes("json")) { - try { return sanitize(JSON.parse(bytes.toString("utf8"))); } - catch { throw failure(502, "daytona_invalid_response", "Daytona returned an invalid response."); } - } - if (contentType.startsWith("text/")) return sanitize(bytes.toString("utf8")); - if (bytes.includes(Buffer.from(secret))) throw failure(403, "daytona_credential_export_denied", "The response contains connection credential material."); - return { contentType, contentBase64: bytes.toString("base64"), byteLength: bytes.length }; - } + const { send: sendRequest, withinDeadline } = nativeRestSender({ provider: "Daytona", code: "daytona", authorization, request, signal, deadlineAt, + deniedMessage: "Daytona denied access. Check this key's organization and permissions. Some account operations require provider user authentication.", + fixedHeaders: { "X-Daytona-Source": "paperclip-connector" } }); + const send = (url: string, init: RequestInit, organizationId?: string) => + sendRequest(url, init, organizationId ? { "X-Daytona-Organization-ID": organizationId } : {}); async function identity() { const value = record(await send(`${DAYTONA_API_URL}/api-keys/current`, { method: "GET" })); @@ -248,11 +146,9 @@ export async function executeDaytonaTool(input: { if (!input.binding.agentId || input.connection.companyId !== input.binding.companyId) { throw failure(404, "daytona_connection_not_found", "Connection not found."); } - const operation = operations.get(input.toolName); + const operation = contract.operation(input.toolName); if (!operation) throw failure(404, "daytona_tool_not_found", "Daytona action not found."); - let validate = validators.get(operation.name); - if (!validate) { validate = ajv.compile(operation.inputSchema); validators.set(operation.name, validate!); } - if (!validate!(input.parameters)) throw failure(422, "daytona_arguments_invalid", "Invalid Daytona action arguments."); + if (!contract.validate(operation.name, input.parameters)) throw failure(422, "daytona_arguments_invalid", "Invalid Daytona action arguments."); const config = record(input.connection.config.methodConfig); return daytonaClient(input.headers, input.request, config.organizationId, input.signal, input.deadlineAt).execute(operation, record(input.parameters)); } diff --git a/server/src/services/delivery-work-notifications.ts b/server/src/services/delivery-work-notifications.ts index f91df0318f1..2f3cb3d7a4d 100644 --- a/server/src/services/delivery-work-notifications.ts +++ b/server/src/services/delivery-work-notifications.ts @@ -38,6 +38,7 @@ export const DELIVERY_QUEUES = { feedback: "feedback-exports", chatCompletion: "chat-completions", connection: "connection-continuations", + gitRestorePoints: "git-restore-points", question: "question-responses", pluginLifecycle: "plugin-resource-lifecycle", toolAction: "tool-action-receipts", diff --git a/server/src/services/git-restore-point-ledger.ts b/server/src/services/git-restore-point-ledger.ts new file mode 100644 index 00000000000..2070e2f1b1e --- /dev/null +++ b/server/src/services/git-restore-point-ledger.ts @@ -0,0 +1,238 @@ +import { and, asc, eq, inArray, isNull, lt, lte, or, sql } from "drizzle-orm"; +import { executionWorkspaceRestorePoints, type Db } from "@paperclipai/db"; +import type { StorageService } from "../storage/types.js"; +import type { createDeliveryWorkCoordinator } from "./delivery-work-coordinator.js"; +import { DELIVERY_QUEUES, notifyDeliveryWork } from "./delivery-work-notifications.js"; + +export const GIT_RESTORE_POINT_RETRY_MS = 15 * 60 * 1000; +export const GIT_RESTORE_POINT_DEFAULT_RETENTION_DAYS = 90; +const DAY_MS = 24 * 60 * 60 * 1000; +const DEFAULT_PENDING_GRACE_MS = 60 * 60 * 1000; +const DEFAULT_BATCH = 50; + +type RestorePoint = typeof executionWorkspaceRestorePoints.$inferSelect; + +export type GitRestorePointRetentionResult = { + abandoned: number; + scheduled: number; + expired: number; + orphaned: number; + deleted: number; + failed: number; + raced: number; + truncated: boolean; +}; + +export type GitRestorePointRetentionOptions = { + storage: StorageService; + retentionDays?: number; + pendingGraceMs?: number; + batchSize?: number; + now?: () => Date; + beforeExpire?: (restorePoint: RestorePoint) => Promise; + afterTreeRead?: (restorePoint: RestorePoint) => Promise; +}; + +type TreeState = { exists: boolean; open: boolean; closedAt: Date | null }; + +export function gitRestorePointRetentionService(db: Db, options: GitRestorePointRetentionOptions) { + const retentionMs = Math.max(1, options.retentionDays ?? GIT_RESTORE_POINT_DEFAULT_RETENTION_DAYS) * DAY_MS; + const pendingGraceMs = options.pendingGraceMs ?? DEFAULT_PENDING_GRACE_MS; + const batchSize = options.batchSize ?? DEFAULT_BATCH; + const now = options.now ?? (() => new Date()); + + async function issueTreeState(row: RestorePoint): Promise { + if (!row.sourceIssueId) return { exists: false, open: false, closedAt: null }; + const result = await db.execute<{ total: number; open: number; closed_at: string | null }>(sql` + WITH RECURSIVE tree AS ( + SELECT id, status, completed_at, cancelled_at, updated_at FROM issues + WHERE company_id = ${row.companyId} AND id = ${row.sourceIssueId} + UNION + SELECT c.id, c.status, c.completed_at, c.cancelled_at, c.updated_at FROM issues c + JOIN tree p ON c.parent_id = p.id WHERE c.company_id = ${row.companyId} + ) + SELECT count(*)::int AS total, + count(*) FILTER (WHERE status NOT IN ('done', 'cancelled'))::int AS open, + max(CASE WHEN status = 'done' THEN coalesce(completed_at, updated_at) + WHEN status = 'cancelled' THEN coalesce(cancelled_at, updated_at) END) AS closed_at + FROM tree`); + const state = result[0]; + if (!state || state.total === 0) return { exists: false, open: false, closedAt: null }; + return { exists: true, open: state.open > 0, closedAt: state.closed_at ? new Date(state.closed_at) : null }; + } + + async function requestDeletion(ids: string[], at: Date) { + if (ids.length === 0) return; + await db.update(executionWorkspaceRestorePoints) + .set({ status: "delete_requested", updatedAt: at }) + .where(and(inArray(executionWorkspaceRestorePoints.id, ids), + inArray(executionWorkspaceRestorePoints.status, ["pending", "active"]))); + } + + async function evaluateActive(row: RestorePoint, at: Date, result: GitRestorePointRetentionResult) { + const tree = await issueTreeState(row); + await options.afterTreeRead?.(row); + const unchanged = and( + eq(executionWorkspaceRestorePoints.id, row.id), + eq(executionWorkspaceRestorePoints.status, "active"), + eq(executionWorkspaceRestorePoints.reviewGeneration, row.reviewGeneration), + ); + const deleteAfter = !tree.exists || tree.open || !tree.closedAt ? null : new Date(tree.closedAt.getTime() + retentionMs); + if (!tree.exists || (deleteAfter && deleteAfter.getTime() <= at.getTime())) { + const claimed = await db.update(executionWorkspaceRestorePoints) + .set({ status: tree.exists ? "expiring" : "delete_requested", lastError: null, updatedAt: at }) + .where(unchanged) + .returning(); + if (claimed.length === 0) { + result.raced += 1; + return; + } + if (!tree.exists) { + result.orphaned += 1; + return; + } + await finishExpiry(claimed[0]!, at, result); + return; + } + const updated = await db.update(executionWorkspaceRestorePoints) + .set({ treeClosedAt: tree.open ? null : tree.closedAt, deleteAfter, updatedAt: at }) + .where(unchanged) + .returning({ id: executionWorkspaceRestorePoints.id }); + if (updated.length === 0) result.raced += 1; + else result.scheduled += 1; + } + + async function finishExpiry(row: RestorePoint, at: Date, result: GitRestorePointRetentionResult) { + try { + await options.beforeExpire?.(row); + } catch (error) { + await db.update(executionWorkspaceRestorePoints) + .set({ lastError: error instanceof Error ? error.message.slice(0, 500) : "expire_failed", updatedAt: at }) + .where(and(eq(executionWorkspaceRestorePoints.id, row.id), eq(executionWorkspaceRestorePoints.status, "expiring"))); + result.failed += 1; + return; + } + await db.update(executionWorkspaceRestorePoints) + .set({ status: "delete_requested", lastError: null, updatedAt: at }) + .where(and(eq(executionWorkspaceRestorePoints.id, row.id), eq(executionWorkspaceRestorePoints.status, "expiring"))); + result.expired += 1; + } + + function retryable(status: "expiring" | "superseded" | "delete_requested" | Array<"superseded" | "delete_requested">, at: Date) { + return and( + Array.isArray(status) ? inArray(executionWorkspaceRestorePoints.status, status) : eq(executionWorkspaceRestorePoints.status, status), + or(isNull(executionWorkspaceRestorePoints.lastError), + lte(executionWorkspaceRestorePoints.updatedAt, new Date(at.getTime() - GIT_RESTORE_POINT_RETRY_MS))), + ); + } + + async function tick() { + const at = now(); + const result: GitRestorePointRetentionResult = { abandoned: 0, scheduled: 0, expired: 0, orphaned: 0, deleted: 0, failed: 0, raced: 0, truncated: false }; + + const abandoned = await db.select({ id: executionWorkspaceRestorePoints.id }).from(executionWorkspaceRestorePoints) + .where(and(eq(executionWorkspaceRestorePoints.status, "pending"), + lt(executionWorkspaceRestorePoints.createdAt, new Date(at.getTime() - pendingGraceMs)))) + .orderBy(asc(executionWorkspaceRestorePoints.createdAt)).limit(batchSize); + await requestDeletion(abandoned.map((row) => row.id), at); + result.abandoned = abandoned.length; + + const due = await db.select().from(executionWorkspaceRestorePoints) + .where(and(eq(executionWorkspaceRestorePoints.status, "active"), + sql`${executionWorkspaceRestorePoints.deleteAfter} <= greatest(${at.toISOString()}::timestamptz, clock_timestamp())`)) + .orderBy(asc(executionWorkspaceRestorePoints.deleteAfter), asc(executionWorkspaceRestorePoints.id)).limit(batchSize); + for (const row of due) await evaluateActive(row, at, result); + + const expiring = await db.select().from(executionWorkspaceRestorePoints) + .where(retryable("expiring", at)) + .orderBy(asc(executionWorkspaceRestorePoints.updatedAt), asc(executionWorkspaceRestorePoints.id)).limit(batchSize); + for (const row of expiring) await finishExpiry(row, at, result); + + const doomed = await db.select().from(executionWorkspaceRestorePoints) + .where(retryable(["superseded", "delete_requested"], at)) + .orderBy(asc(executionWorkspaceRestorePoints.updatedAt), asc(executionWorkspaceRestorePoints.id)).limit(batchSize); + for (const row of doomed) { + try { + await options.storage.deleteObject(row.companyId, row.objectKey); + if ((await options.storage.headObject(row.companyId, row.objectKey)).exists) throw new Error("restore_point_object_still_present"); + await db.update(executionWorkspaceRestorePoints) + .set({ status: "deleted", deletedAt: at, lastError: null, updatedAt: at }) + .where(eq(executionWorkspaceRestorePoints.id, row.id)); + result.deleted += 1; + } catch (error) { + await db.update(executionWorkspaceRestorePoints) + .set({ lastError: error instanceof Error ? error.message.slice(0, 500) : "delete_failed", updatedAt: at }) + .where(eq(executionWorkspaceRestorePoints.id, row.id)); + result.failed += 1; + } + } + result.truncated = abandoned.length === batchSize || due.length === batchSize + || expiring.length === batchSize || doomed.length === batchSize; + return result; + } + + async function nextDeadlineAt(): Promise { + const [row] = await db.select({ + deleteNow: sql`count(*) FILTER (WHERE ${executionWorkspaceRestorePoints.status} IN ('expiring', 'superseded', 'delete_requested') AND ${executionWorkspaceRestorePoints.lastError} IS NULL)::int`, + retryFrom: sql`min(${executionWorkspaceRestorePoints.updatedAt}) FILTER (WHERE ${executionWorkspaceRestorePoints.status} IN ('expiring', 'superseded', 'delete_requested') AND ${executionWorkspaceRestorePoints.lastError} IS NOT NULL)`, + pendingFrom: sql`min(${executionWorkspaceRestorePoints.createdAt}) FILTER (WHERE ${executionWorkspaceRestorePoints.status} = 'pending')`, + expiresAt: sql`min(${executionWorkspaceRestorePoints.deleteAfter}) FILTER (WHERE ${executionWorkspaceRestorePoints.status} = 'active')`, + }).from(executionWorkspaceRestorePoints); + const at = now().getTime(); + const deadlines: number[] = []; + if (row && row.deleteNow > 0) deadlines.push(at); + if (row?.retryFrom) deadlines.push(new Date(row.retryFrom).getTime() + GIT_RESTORE_POINT_RETRY_MS); + if (row?.pendingFrom) deadlines.push(new Date(row.pendingFrom).getTime() + pendingGraceMs); + if (row?.expiresAt) deadlines.push(new Date(row.expiresAt).getTime()); + return deadlines.length > 0 ? Math.min(...deadlines) : null; + } + + async function hasActive(): Promise { + const [row] = await db.select({ id: executionWorkspaceRestorePoints.id }).from(executionWorkspaceRestorePoints) + .where(eq(executionWorkspaceRestorePoints.status, "active")).limit(1); + return Boolean(row); + } + + return { tick, nextDeadlineAt, hasActive }; +} + +export function notifyGitRestorePointWork(transaction: object, hint?: string) { + return notifyDeliveryWork(transaction, DELIVERY_QUEUES.gitRestorePoints, hint); +} + +export async function requestRestorePointDeletion( + tx: Pick, + scope: { sourceIssueId: string } | { companyId: string }, +) { + await notifyGitRestorePointWork(tx, "sourceIssueId" in scope ? `issue:${scope.sourceIssueId}` : `company:${scope.companyId}`); + await tx.update(executionWorkspaceRestorePoints) + .set({ status: "delete_requested", updatedAt: new Date() }) + .where(and( + "sourceIssueId" in scope + ? eq(executionWorkspaceRestorePoints.sourceIssueId, scope.sourceIssueId) + : eq(executionWorkspaceRestorePoints.companyId, scope.companyId), + inArray(executionWorkspaceRestorePoints.status, ["pending", "active", "expiring"]), + )); +} + +export function registerGitRestorePointWork( + coordinator: ReturnType, + service: ReturnType, + onResult: (result: GitRestorePointRetentionResult) => void, +) { + let next: number | null = null; + let tracksActive = true; + const worker = coordinator.register(DELIVERY_QUEUES.gitRestorePoints, { + retryMs: GIT_RESTORE_POINT_RETRY_MS, + run: async () => { + next = null; + const result = await service.tick(); + onResult(result); + next = result.truncated ? Date.now() + 1000 : await service.nextDeadlineAt(); + tracksActive = await service.hasActive(); + }, + hasPending: async () => false, + nextRunAt: () => next, + }); + return { ...worker, issuesChanged: () => { if (tracksActive) worker.wake(); } }; +} diff --git a/server/src/services/git-restore-point.ts b/server/src/services/git-restore-point.ts new file mode 100644 index 00000000000..709c55e77d4 --- /dev/null +++ b/server/src/services/git-restore-point.ts @@ -0,0 +1,448 @@ +import { execFile as execFileCallback } from "node:child_process"; +import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto"; +import fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { promisify } from "node:util"; +import { localEncryptedProvider } from "../secrets/local-encrypted-provider.js"; +import type { StoredSecretVersionMaterial } from "../secrets/types.js"; + +const execFile = promisify(execFileCallback); + +export const RESTORE_POINT_PAYLOAD_SCHEMA = "paperclip.git-restore-point-payload.v1"; +const PAYLOAD_MAGIC = Buffer.from("PCRP-PLAIN1\n"); +const SEALED_MAGIC = Buffer.from("PCRP1"); +const KEY_SCHEMA = "paperclip.git-restore-point-key.v1"; +const SHA = /^[0-9a-f]{40}$/; +const SNAPSHOT_IDENTITY = { + GIT_AUTHOR_NAME: "Paperclip", + GIT_AUTHOR_EMAIL: "restore-point@paperclip.invalid", + GIT_AUTHOR_DATE: "@0 +0000", + GIT_COMMITTER_NAME: "Paperclip", + GIT_COMMITTER_EMAIL: "restore-point@paperclip.invalid", + GIT_COMMITTER_DATE: "@0 +0000", +}; +const EXCLUDED_UNTRACKED = [":(exclude,glob)**/.env", ":(exclude,glob)**/.env.*"]; +const MAX_PRESERVED_COMMITS = 50; + +export type RestorePointLimits = { + maxFiles: number; + maxFileBytes: number; + maxTotalBytes: number; + maxCommits: number; + maxBundleBytes: number; +}; + +export const DEFAULT_RESTORE_POINT_LIMITS: RestorePointLimits = { + maxFiles: 20_000, + maxFileBytes: 64 * 1024 * 1024, + maxTotalBytes: 256 * 1024 * 1024, + maxCommits: 1_000, + maxBundleBytes: 256 * 1024 * 1024, +}; + +export type RestorePointManifest = { + schema: typeof RESTORE_POINT_PAYLOAD_SCHEMA; + head: string; + index: string | null; + worktree: string | null; + preserved: string[]; + prerequisites: string[]; + modes: Array<{ path: string; mode: number }>; +}; + +export type RestorePointSnapshot = { head: string; index: string | null; worktree: string | null; modes: Array<{ path: string; mode: number }> }; + +export type RestorePointIdentity = { companyId: string; workspaceId: string; restorePointId: string; generation: number }; + +export type SealedRestorePoint = { + ciphertext: Buffer; + sha256: string; + key: StoredSecretVersionMaterial; +}; + +export class RestorePointSkip extends Error { + constructor(readonly reason: string) { + super(reason); + this.name = "RestorePointSkip"; + } +} + +export class RestorePointRestoreFailure extends Error { + constructor(readonly reason: string) { + super(`The workspace restore point could not be applied: ${reason}`); + this.name = "RestorePointRestoreFailure"; + } +} + +type GitOptions = { env?: Record; input?: Buffer | string; signal?: AbortSignal; maxBuffer?: number }; + +function isolatedEnv(extra: Record = {}) { + return { + ...process.env, + GIT_CONFIG_NOSYSTEM: "1", + GIT_CONFIG_GLOBAL: os.devNull, + GIT_TERMINAL_PROMPT: "0", + GIT_OPTIONAL_LOCKS: "0", + ...extra, + }; +} + +async function git(cwd: string, args: string[], options: GitOptions = {}): Promise { + const child = execFile("git", [ + "-c", "core.fsmonitor=false", "-c", "core.autocrlf=false", "-c", "core.safecrlf=false", + "-c", `core.hooksPath=${os.devNull}`, "-C", cwd, ...args, + ], { + env: isolatedEnv(options.env), + encoding: "buffer", + maxBuffer: options.maxBuffer ?? 64 * 1024 * 1024, + timeout: 120_000, + signal: options.signal, + }); + if (options.input !== undefined) child.child.stdin?.end(options.input); + const { stdout } = await child; + return stdout as Buffer; +} + +async function gitText(cwd: string, args: string[], options: GitOptions = {}) { + return (await git(cwd, args, options)).toString("utf8").trim(); +} + +function nulList(output: Buffer) { + return output.toString("utf8").split("\0").filter(Boolean); +} + +async function objectsDirectory(cwd: string) { + const common = await gitText(cwd, ["rev-parse", "--git-common-dir"]); + return path.resolve(cwd, common, "objects"); +} + +async function snapshotStore(cwd: string, temporary: string) { + const store = path.join(temporary, "store.git"); + await execFile("git", ["init", "-q", "--bare", store], { env: isolatedEnv() }); + const repositoryObjects = await objectsDirectory(cwd); + await fs.writeFile(path.join(store, "objects", "info", "alternates"), `${repositoryObjects}\n`); + return { + store, + env: { + GIT_OBJECT_DIRECTORY: path.join(store, "objects"), + GIT_ALTERNATE_OBJECT_DIRECTORIES: repositoryObjects, + ...SNAPSHOT_IDENTITY, + }, + }; +} + +async function copyIndex(cwd: string, target: string) { + const indexPath = path.resolve(cwd, await gitText(cwd, ["rev-parse", "--git-path", "index"])); + await fs.copyFile(indexPath, target).catch((error: NodeJS.ErrnoException) => { + if (error.code !== "ENOENT") throw error; + }); +} + +async function scanChangedPaths(cwd: string, limits: RestorePointLimits, signal?: AbortSignal) { + const changed = new Set([ + ...nulList(await git(cwd, ["ls-files", "-z", "--modified"], { signal })), + ...nulList(await git(cwd, ["diff", "--cached", "--name-only", "--no-renames", "-z", "HEAD"], { signal })), + ...nulList(await git(cwd, ["ls-files", "-z", "--others", "--exclude-standard", "--", ".", ...EXCLUDED_UNTRACKED], { signal })), + ]); + if (changed.size > limits.maxFiles) throw new RestorePointSkip("restore_point_too_large"); + let totalBytes = 0; + const modes: Array<{ path: string; mode: number }> = []; + for (const relative of changed) { + if (relative.endsWith("/")) throw new RestorePointSkip("untracked_nested_repository"); + const stat = await fs.lstat(path.join(cwd, relative)).catch((error: NodeJS.ErrnoException) => { + if (error.code === "ENOENT") return null; + throw error; + }); + if (!stat) continue; + if (stat.isDirectory()) throw new RestorePointSkip("untracked_nested_repository"); + if (!stat.isFile() && !stat.isSymbolicLink()) throw new RestorePointSkip("unsupported_special_file"); + if (stat.isFile()) { + if (stat.size > limits.maxFileBytes) throw new RestorePointSkip("restore_point_too_large"); + totalBytes += stat.size; + const mode = stat.mode & 0o777; + if (mode !== 0o644 && mode !== 0o755) modes.push({ path: relative, mode }); + } + if (totalBytes > limits.maxTotalBytes) throw new RestorePointSkip("restore_point_too_large"); + } + if (changed.size > 0) { + const attributes = nulList(await git(cwd, ["check-attr", "-z", "--stdin", "filter"], { input: [...changed].join("\0"), signal })); + for (let index = 2; index < attributes.length; index += 3) { + if (attributes[index] !== "unspecified" && attributes[index] !== "unset") throw new RestorePointSkip("filtered_paths"); + } + } + return { modes: modes.sort((a, b) => a.path.localeCompare(b.path)) }; +} + +async function snapshotTrees(cwd: string, env: Record, temporary: string, signal?: AbortSignal) { + const indexCopy = path.join(temporary, "index"); + await copyIndex(cwd, indexCopy); + const indexTree = await gitText(cwd, ["write-tree"], { env: { ...env, GIT_INDEX_FILE: indexCopy }, signal }) + .catch(() => { throw new RestorePointSkip("unmerged_index"); }); + const worktreeIndex = path.join(temporary, "worktree-index"); + const worktreeEnv = { ...env, GIT_INDEX_FILE: worktreeIndex }; + await git(cwd, ["read-tree", "HEAD"], { env: worktreeEnv, signal }); + await git(cwd, ["add", "-u", "--", "."], { env: worktreeEnv, signal }); + await git(cwd, ["add", "-A", "--", ".", ...EXCLUDED_UNTRACKED], { env: worktreeEnv, signal }); + const worktreeTree = await gitText(cwd, ["write-tree"], { env: worktreeEnv, signal }); + const gitlinks = await gitText(cwd, ["ls-tree", "-r", worktreeTree], { env, signal }); + if (/^160000 /m.test(gitlinks)) throw new RestorePointSkip("submodules_unsupported"); + return { indexTree, worktreeTree }; +} + +async function snapshotCommits(cwd: string, env: Record, temporary: string, modes: RestorePointSnapshot["modes"], signal?: AbortSignal): Promise { + const head = await gitText(cwd, ["rev-parse", "--verify", "HEAD^{commit}"], { signal }); + const headTree = await gitText(cwd, ["rev-parse", "HEAD^{tree}"], { signal }); + const { indexTree, worktreeTree } = await snapshotTrees(cwd, env, temporary, signal); + if (indexTree === headTree && worktreeTree === headTree) return { head, index: null, worktree: null, modes: [] }; + const index = await gitText(cwd, ["commit-tree", indexTree, "-p", head, "-m", "paperclip restore point index"], { env, signal }); + const worktree = await gitText(cwd, ["commit-tree", worktreeTree, "-p", head, "-p", index, "-m", "paperclip restore point worktree"], { env, signal }); + return { head, index, worktree, modes }; +} + +const UNBOUNDED_LIMITS: RestorePointLimits = { + maxFiles: Number.POSITIVE_INFINITY, + maxFileBytes: Number.POSITIVE_INFINITY, + maxTotalBytes: Number.POSITIVE_INFINITY, + maxCommits: Number.POSITIVE_INFINITY, + maxBundleBytes: Number.POSITIVE_INFINITY, +}; + +export async function currentRestorePointSnapshot(input: string, signal?: AbortSignal): Promise { + const cwd = await fs.realpath(input); + const { modes } = await scanChangedPaths(cwd, UNBOUNDED_LIMITS, signal); + const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-restore-point-")); + try { + const { env } = await snapshotStore(cwd, temporary); + return await snapshotCommits(cwd, env, temporary, modes, signal); + } finally { + await fs.rm(temporary, { recursive: true, force: true }); + } +} + +async function existingCommits(cwd: string, commits: readonly string[], signal?: AbortSignal) { + const present: string[] = []; + for (const commit of commits) { + if (!SHA.test(commit)) continue; + if (await git(cwd, ["cat-file", "-e", `${commit}^{commit}`], { signal }).then(() => true, () => false)) present.push(commit); + } + return present; +} + +async function unpreservedHistory(cwd: string, head: string, boundary: readonly string[], signal?: AbortSignal) { + const candidates = new Set(); + const reflog = await gitText(cwd, ["reflog", "show", "--format=%H", "HEAD"], { signal }).catch(() => ""); + for (const line of reflog.split("\n")) if (SHA.test(line)) candidates.add(line); + const original = await gitText(cwd, ["rev-parse", "--verify", "-q", "ORIG_HEAD^{commit}"], { signal }).catch(() => ""); + if (SHA.test(original)) candidates.add(original); + candidates.delete(head); + if (candidates.size === 0) return []; + const unreachable = new Set((await gitText(cwd, ["rev-list", "--stdin"], { + signal, input: [...candidates, `^${head}`, ...boundary.map((commit) => `^${commit}`)].join("\n") + "\n", + })).split("\n").filter((line) => SHA.test(line))); + const preserved = [...candidates].filter((commit) => unreachable.has(commit)); + if (preserved.length > MAX_PRESERVED_COMMITS) throw new RestorePointSkip("restore_point_history_too_large"); + return preserved.sort(); +} + +function bundlePrerequisites(bundle: Buffer) { + const headerEnd = bundle.indexOf("\n\n"); + if (headerEnd < 0) throw new RestorePointSkip("invalid_bundle"); + return bundle.subarray(0, headerEnd).toString("utf8").split("\n").slice(1) + .filter((line) => line.startsWith("-")).map((line) => line.slice(1, 41)).filter((sha) => SHA.test(sha)).sort(); +} + +function encodePayload(manifest: RestorePointManifest, bundle: Buffer) { + const manifestBytes = Buffer.from(JSON.stringify(manifest)); + const length = Buffer.alloc(4); + length.writeUInt32BE(manifestBytes.length); + return Buffer.concat([PAYLOAD_MAGIC, length, manifestBytes, bundle]); +} + +export function decodeRestorePointPayload(payload: Buffer): { manifest: RestorePointManifest; bundle: Buffer } { + if (payload.length < PAYLOAD_MAGIC.length + 4 || !payload.subarray(0, PAYLOAD_MAGIC.length).equals(PAYLOAD_MAGIC)) { + throw new RestorePointRestoreFailure("invalid_payload"); + } + const length = payload.readUInt32BE(PAYLOAD_MAGIC.length); + const start = PAYLOAD_MAGIC.length + 4; + if (start + length > payload.length) throw new RestorePointRestoreFailure("invalid_payload"); + const manifest = JSON.parse(payload.subarray(start, start + length).toString("utf8")) as RestorePointManifest; + const shas = [manifest.head, ...(manifest.index ? [manifest.index] : []), ...(manifest.worktree ? [manifest.worktree] : []), + ...manifest.preserved, ...manifest.prerequisites]; + if (manifest.schema !== RESTORE_POINT_PAYLOAD_SCHEMA || shas.some((sha) => !SHA.test(sha)) + || (manifest.index === null) !== (manifest.worktree === null) + || manifest.modes.some((entry) => typeof entry.path !== "string" || path.isAbsolute(entry.path) + || entry.path.split("/").includes("..") || !Number.isInteger(entry.mode) || entry.mode < 0 || entry.mode > 0o777)) { + throw new RestorePointRestoreFailure("invalid_payload"); + } + return { manifest, bundle: payload.subarray(start + length) }; +} + +export async function captureRestorePoint(input: { + cwd: string; + upstreamCommits: readonly string[]; + limits?: Partial; + signal?: AbortSignal; +}): Promise<{ payload: Buffer; manifest: RestorePointManifest } | null> { + const limits = { ...DEFAULT_RESTORE_POINT_LIMITS, ...input.limits }; + const cwd = await fs.realpath(input.cwd); + const { modes } = await scanChangedPaths(cwd, limits, input.signal); + const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-restore-point-")); + try { + const { store, env } = await snapshotStore(cwd, temporary); + const snapshot = await snapshotCommits(cwd, env, temporary, modes, input.signal); + const boundary = await existingCommits(cwd, input.upstreamCommits, input.signal); + const preserved = await unpreservedHistory(cwd, snapshot.head, boundary, input.signal); + const headOnUpstream = await (async () => { + for (const tip of boundary) { + if (await git(cwd, ["merge-base", "--is-ancestor", snapshot.head, tip], { signal: input.signal }).then(() => true, () => false)) return true; + } + return false; + })(); + if (headOnUpstream && snapshot.worktree === null && preserved.length === 0) return null; + const refs: Array<[string, string]> = [["refs/restore/head", snapshot.head]]; + if (snapshot.index && snapshot.worktree) refs.push(["refs/restore/index", snapshot.index], ["refs/restore/worktree", snapshot.worktree]); + preserved.forEach((commit, index) => refs.push([`refs/restore/preserved/${index}`, commit])); + for (const [ref, commit] of refs) await git(store, ["update-ref", ref, commit], { env, signal: input.signal }); + const exclusions = boundary.map((commit) => `^${commit}`); + const commitCount = Number(await gitText(store, ["rev-list", "--count", ...refs.map(([ref]) => ref), ...exclusions], { env, signal: input.signal })); + if (commitCount > limits.maxCommits) throw new RestorePointSkip("restore_point_too_large"); + if (commitCount === 0) return null; + const bundlePath = path.join(temporary, "restore.bundle"); + await git(store, ["bundle", "create", "-q", bundlePath, "--stdin"], { + env, signal: input.signal, input: [...refs.map(([ref]) => ref), ...exclusions].join("\n") + "\n", + }); + const bundle = await fs.readFile(bundlePath); + if (bundle.length > limits.maxBundleBytes) throw new RestorePointSkip("restore_point_too_large"); + const prerequisites = bundlePrerequisites(bundle); + const manifest: RestorePointManifest = { + schema: RESTORE_POINT_PAYLOAD_SCHEMA, + head: snapshot.head, + index: snapshot.index, + worktree: snapshot.worktree, + preserved, + prerequisites, + modes: snapshot.modes, + }; + return { payload: encodePayload(manifest, bundle), manifest }; + } finally { + await fs.rm(temporary, { recursive: true, force: true }); + } +} + +export async function importRestorePoint(input: { repositoryCwd: string; payload: Buffer; signal?: AbortSignal }) { + const { manifest, bundle } = decodeRestorePointPayload(input.payload); + const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-restore-point-")); + try { + const bundlePath = path.join(temporary, "restore.bundle"); + await fs.writeFile(bundlePath, bundle, { mode: 0o600 }); + await git(input.repositoryCwd, ["bundle", "verify", "-q", bundlePath], { signal: input.signal }) + .catch(() => { throw new RestorePointRestoreFailure("bundle_prerequisites_missing"); }); + const heads = (await gitText(input.repositoryCwd, ["bundle", "list-heads", bundlePath], { signal: input.signal })) + .split("\n").filter(Boolean).map((line) => line.split(" ")); + const expected = new Map([["refs/restore/head", manifest.head]]); + if (manifest.index && manifest.worktree) { + expected.set("refs/restore/index", manifest.index); + expected.set("refs/restore/worktree", manifest.worktree); + } + manifest.preserved.forEach((commit, index) => expected.set(`refs/restore/preserved/${index}`, commit)); + if (heads.some(([sha, ref]) => expected.get(ref ?? "") !== sha)) throw new RestorePointRestoreFailure("bundle_manifest_mismatch"); + const listed = new Set(heads.map(([, ref]) => ref)); + for (const [ref, commit] of expected) { + if (listed.has(ref)) continue; + const present = await git(input.repositoryCwd, ["cat-file", "-e", `${commit}^{commit}`], { signal: input.signal }).then(() => true, () => false); + if (!present) throw new RestorePointRestoreFailure("bundle_manifest_mismatch"); + } + if (listed.size > 0) { + await git(input.repositoryCwd, ["fetch", "-q", "--no-tags", "--no-write-fetch-head", bundlePath, + ...[...listed].map((ref) => `${ref}:refs/paperclip-restore-import/${ref!.slice("refs/restore/".length)}`)], { signal: input.signal }); + } + await git(input.repositoryCwd, ["for-each-ref", "--format=delete %(refname)", "refs/paperclip-restore-import/"], { signal: input.signal }) + .then((output) => git(input.repositoryCwd, ["update-ref", "--stdin"], { input: output, signal: input.signal })); + return manifest; + } finally { + await fs.rm(temporary, { recursive: true, force: true }); + } +} + +export async function applyRestorePoint(input: { cwd: string; manifest: RestorePointManifest; signal?: AbortSignal }) { + const { manifest } = input; + const cwd = await fs.realpath(input.cwd); + const head = await gitText(cwd, ["rev-parse", "--verify", "HEAD^{commit}"], { signal: input.signal }); + if (head !== manifest.head) throw new RestorePointRestoreFailure("head_mismatch"); + if (manifest.index && manifest.worktree) { + const diff = await git(cwd, ["diff", "--binary", "--no-color", "--no-ext-diff", "--no-textconv", "--no-renames", "HEAD", manifest.worktree], { signal: input.signal }); + if (diff.length > 0) await git(cwd, ["apply", "--binary", "--whitespace=nowarn", "-"], { input: diff, signal: input.signal }); + await git(cwd, ["read-tree", `${manifest.index}^{tree}`], { signal: input.signal }); + } + for (const entry of manifest.modes) { + const target = path.join(cwd, entry.path); + if (path.relative(cwd, target).startsWith("..")) throw new RestorePointRestoreFailure("invalid_payload"); + const stat = await fs.lstat(target).catch(() => null); + if (stat?.isFile()) await fs.chmod(target, entry.mode); + } + const restored = await currentRestorePointSnapshot(cwd, input.signal); + if (restored.head !== manifest.head || restored.index !== manifest.index || restored.worktree !== manifest.worktree + || JSON.stringify(restored.modes) !== JSON.stringify(manifest.modes)) { + throw new RestorePointRestoreFailure("restored_contents_mismatch"); + } +} + +function additionalData(identity: RestorePointIdentity) { + return Buffer.from(JSON.stringify([identity.companyId, identity.workspaceId, identity.restorePointId, identity.generation])); +} + +export async function sealRestorePoint(payload: Buffer, identity: RestorePointIdentity): Promise { + const dataKey = randomBytes(32); + const iv = randomBytes(12); + const cipher = createCipheriv("aes-256-gcm", dataKey, iv); + cipher.setAAD(additionalData(identity)); + const ciphertext = Buffer.concat([SEALED_MAGIC, iv, cipher.update(payload), cipher.final(), cipher.getAuthTag()]); + const { material } = await localEncryptedProvider.createVersion({ + value: JSON.stringify({ + schema: KEY_SCHEMA, + ...identity, + dataKey: dataKey.toString("base64"), + plaintextSha256: createHash("sha256").update(payload).digest("hex"), + plaintextBytes: payload.length, + }), + } as Parameters[0]); + return { ciphertext, sha256: createHash("sha256").update(ciphertext).digest("hex"), key: material }; +} + +export async function openRestorePoint(input: { + ciphertext: Buffer; + sha256: string; + key: StoredSecretVersionMaterial; + identity: RestorePointIdentity; +}): Promise { + if (createHash("sha256").update(input.ciphertext).digest("hex") !== input.sha256) { + throw new RestorePointRestoreFailure("ciphertext_hash_mismatch"); + } + const minimum = SEALED_MAGIC.length + 12 + 16; + if (input.ciphertext.length < minimum || !input.ciphertext.subarray(0, SEALED_MAGIC.length).equals(SEALED_MAGIC)) { + throw new RestorePointRestoreFailure("invalid_ciphertext"); + } + const unwrapped = JSON.parse(await localEncryptedProvider.resolveVersion({ material: input.key, externalRef: null } as Parameters[0])); + const { identity } = input; + if (unwrapped.schema !== KEY_SCHEMA || unwrapped.companyId !== identity.companyId || unwrapped.workspaceId !== identity.workspaceId + || unwrapped.restorePointId !== identity.restorePointId || unwrapped.generation !== identity.generation) { + throw new RestorePointRestoreFailure("key_identity_mismatch"); + } + const iv = input.ciphertext.subarray(SEALED_MAGIC.length, SEALED_MAGIC.length + 12); + const tag = input.ciphertext.subarray(input.ciphertext.length - 16); + const body = input.ciphertext.subarray(SEALED_MAGIC.length + 12, input.ciphertext.length - 16); + const decipher = createDecipheriv("aes-256-gcm", Buffer.from(unwrapped.dataKey, "base64"), iv); + decipher.setAAD(additionalData(identity)); + decipher.setAuthTag(tag); + let payload: Buffer; + try { + payload = Buffer.concat([decipher.update(body), decipher.final()]); + } catch { + throw new RestorePointRestoreFailure("ciphertext_authentication_failed"); + } + if (payload.length !== unwrapped.plaintextBytes || createHash("sha256").update(payload).digest("hex") !== unwrapped.plaintextSha256) { + throw new RestorePointRestoreFailure("plaintext_hash_mismatch"); + } + return payload; +} diff --git a/server/src/services/instance-settings.ts b/server/src/services/instance-settings.ts index 23c66dd35ac..1d2395ffebe 100644 --- a/server/src/services/instance-settings.ts +++ b/server/src/services/instance-settings.ts @@ -247,6 +247,7 @@ export function normalizeExperimentalSettings(raw: unknown): InstanceExperimenta enableCases: parsed.data.enableCases ?? false, enableAgentChat: parsed.data.enableAgentChat ?? false, enableCombinedInboxTasks: parsed.data.enableCombinedInboxTasks ?? false, + enableArtifactsV1: parsed.data.enableArtifactsV1 ?? false, enableConferenceRoomChat: parsed.data.enableConferenceRoomChat ?? false, enableClassicTaskInterface: parsed.data.enableClassicTaskInterface ?? false, enableIssuePlanDecompositions: parsed.data.enableIssuePlanDecompositions ?? false, @@ -298,6 +299,7 @@ export function normalizeExperimentalSettings(raw: unknown): InstanceExperimenta enableCases: false, enableAgentChat: false, enableCombinedInboxTasks: false, + enableArtifactsV1: false, enableConferenceRoomChat: false, enableClassicTaskInterface: false, enableIssuePlanDecompositions: false, diff --git a/server/src/services/issues.ts b/server/src/services/issues.ts index 9acfdf86074..b95d7058b04 100644 --- a/server/src/services/issues.ts +++ b/server/src/services/issues.ts @@ -12,6 +12,7 @@ import { type TaskWorkspaceSelection, type TaskWorkspaceIntent } from "@papercli import { executionWorkspaceService } from "./execution-workspaces.js"; import { notifyChatPublicationWork } from "./chat-work-notifications.js"; import { notifyTerminalWorkspaceWork } from "./terminal-workspace-work.js"; +import { requestRestorePointDeletion } from "./git-restore-point-ledger.js"; import { DELIVERY_QUEUES, notifyDeliveryWork } from "./delivery-work-notifications.js"; import { recordChatHandoff, recordChatCompletion, existingChatCompletionReply, acknowledgeChatCompletionReply } from "./chat-completion-delivery.js"; import { mirrorSlackBoardComment, slackBoardReplyBindings } from "./slack-board-messages.js"; @@ -11696,6 +11697,7 @@ export function issueService(db: Db) { let removedIssue; try { await notifyTerminalWorkspaceWork(tx, `issue:${id}`); + await requestRestorePointDeletion(tx, { sourceIssueId: id }); removedIssue = await tx .delete(issues) .where(eq(issues.id, id)) diff --git a/server/src/services/native-provider-catalogs.ts b/server/src/services/native-provider-catalogs.ts new file mode 100644 index 00000000000..a37681c4168 --- /dev/null +++ b/server/src/services/native-provider-catalogs.ts @@ -0,0 +1,50 @@ +import { DAYTONA_TOOLS, daytonaClient, daytonaRisk, executeDaytonaTool, isCurrentDaytonaCatalogEntry, isDaytonaConnection, isDaytonaToolName } from "./daytona.js"; +import type { NativeCatalogEntry, NativeRestRequest } from "./native-rest-client.js"; +import { UNIKRAFT_DEFAULT_METRO, UNIKRAFT_TOOLS, executeUnikraftTool, isCurrentUnikraftCatalogEntry, isUnikraftConnection, + isUnikraftToolName, unikraftClient, unikraftRisk } from "./connectors/unikraft/client.js"; + +type ConnectionShape = { transport: string; config?: Record }; +const methodConfig = (connection: ConnectionShape) => { + const value = connection.config?.methodConfig; + return value && typeof value === "object" && !Array.isArray(value) ? value as Record : {}; +}; + +/** A provider whose action catalog is generated from its published HTTP API + * and executed by a Paperclip bridge rather than a remote MCP server. Shared + * connection code consumes this contract instead of naming providers. */ +export type NativeProviderCatalog = { + slug: string; + name: string; + tools: typeof DAYTONA_TOOLS; + isConnection(connection: ConnectionShape): boolean; + isToolName(name: string): boolean; + risk(name: string): "read" | "write" | "destructive"; + isCurrentEntry(entry: NativeCatalogEntry): boolean; + probe(connection: ConnectionShape, headers: Record, request: NativeRestRequest): Promise; + execute(input: Parameters[0]): Promise; +}; + +export const NATIVE_PROVIDER_CATALOGS: readonly NativeProviderCatalog[] = [ + { + slug: "daytona", name: "Daytona", tools: DAYTONA_TOOLS, + isConnection: isDaytonaConnection, isToolName: isDaytonaToolName, risk: daytonaRisk, isCurrentEntry: isCurrentDaytonaCatalogEntry, + probe: (connection, headers, request) => daytonaClient(headers, request, methodConfig(connection).organizationId).probe(), + execute: executeDaytonaTool, + }, + { + slug: "unikraft", name: "Unikraft", tools: UNIKRAFT_TOOLS, + isConnection: isUnikraftConnection, isToolName: isUnikraftToolName, risk: unikraftRisk, isCurrentEntry: isCurrentUnikraftCatalogEntry, + probe: (connection, headers, request) => unikraftClient(headers, request, methodConfig(connection).metro ?? UNIKRAFT_DEFAULT_METRO).probe(), + execute: executeUnikraftTool, + }, +]; + +export const NATIVE_PROVIDER_SLUGS: readonly string[] = NATIVE_PROVIDER_CATALOGS.map(provider => provider.slug); + +export function nativeProviderCatalogFor(connection: ConnectionShape): NativeProviderCatalog | null { + return NATIVE_PROVIDER_CATALOGS.find(provider => provider.isConnection(connection)) ?? null; +} + +export function nativeProviderCatalogForSlug(slug: unknown): NativeProviderCatalog | null { + return NATIVE_PROVIDER_CATALOGS.find(provider => provider.slug === slug) ?? null; +} diff --git a/server/src/services/native-rest-client.ts b/server/src/services/native-rest-client.ts new file mode 100644 index 00000000000..2b78e0baa77 --- /dev/null +++ b/server/src/services/native-rest-client.ts @@ -0,0 +1,168 @@ +import Ajv, { type ValidateFunction } from "ajv"; +import addFormats from "ajv-formats"; +import { HttpError } from "../errors.js"; +import { redactSensitiveText } from "../redaction.js"; +import { descriptorHash, stableHash } from "./tool-catalog-hash.js"; + +/** Shared transport for native provider catalogs (Daytona, Unikraft). Each + * provider owns its endpoints and target checks; this owns the bounded, + * credential-redacting request/response handling they must all apply. */ +export const NATIVE_REST_MAX_BYTES = 8 * 1024 * 1024; +export type NativeRestRequest = (url: string, init: RequestInit) => Promise; + +export const nativeRestFailure = (status: number, code: string, message: string) => new HttpError(status, message, { code }); + +export function nativeRestRecord(value: unknown): Record { + return value && typeof value === "object" && !Array.isArray(value) ? value as Record : {}; +} + +export function nativeRestPathValue(value: unknown, invalid: () => Error) { + const text = String(value); + if (!text || text.length > 8192 || /[\u0000-\u001f\\]/.test(text) + || text.split("/").some(segment => segment === "." || segment === "..")) { + throw invalid(); + } + return encodeURIComponent(text); +} + +export function nativeRestSender(options: { + /** Provider display name used in caller-facing messages. */ + provider: string; + /** Error code prefix, such as `daytona`. */ + code: string; + authorization: string; + request: NativeRestRequest; + signal: AbortSignal; + deadlineAt: number; + deniedMessage: string; + /** Provider-owned headers set after caller headers so they cannot be replaced. */ + fixedHeaders?: Record; +}) { + const { provider, code, authorization, request, signal, deadlineAt } = options; + const secret = authorization.replace(/^Bearer /, ""); + const expired = () => nativeRestFailure(504, `${code}_deadline_exceeded`, `The ${provider} action deadline expired. An operation already sent may still be running; inspect it before retrying.`); + // Bound the caller even if a transport or body reader is slow to acknowledge + // cancellation. A late prerequisite result never advances to the next call. + async function withinDeadline(work: () => Promise): Promise { + if (signal.aborted || Date.now() >= deadlineAt) throw expired(); + let abort!: () => void; + try { + return await Promise.race([ + Promise.resolve().then(() => { if (signal.aborted || Date.now() >= deadlineAt) throw expired(); return work(); }), + new Promise((_resolve, reject) => { + abort = () => reject(expired()); + signal.addEventListener("abort", abort, { once: true }); + }), + ]); + } finally { signal.removeEventListener("abort", abort); } + } + function sanitize(value: unknown): unknown { + if (typeof value === "string") return redactSensitiveText(value.split(secret).join("[REDACTED]")); + if (Array.isArray(value)) return value.map(sanitize); + if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, sanitize(item)])); + return value; + } + + /** `expectedStatuses` maps a provider-documented non-2xx outcome (for + * example, a wait that timed out) to the result returned in its place. */ + async function send(url: string, init: RequestInit, extraHeaders: Record = {}, + expectedStatuses: Record = {}): Promise { + const projected = new Headers(init.headers); + projected.set("Authorization", authorization); + for (const [name, value] of Object.entries({ ...options.fixedHeaders, ...extraHeaders })) projected.set(name, value); + let response: Response; + try { + response = await withinDeadline(() => request(url, { ...init, redirect: "error", headers: Object.fromEntries(projected), signal })); + } catch { + if (signal.aborted || Date.now() >= deadlineAt) throw expired(); + throw nativeRestFailure(502, `${code}_outcome_unknown`, init.method === "GET" + ? `${provider} could not complete the request.` + : `${provider} did not return a result. The operation may have started; inspect its state before retrying.`); + } + if (Object.hasOwn(expectedStatuses, response.status)) { + void response.body?.cancel().catch(() => {}); + return expectedStatuses[response.status]; + } + if (!response.ok) { + void response.body?.cancel().catch(() => {}); + const status = [400, 401, 403, 404, 409, 422, 429].includes(response.status) ? response.status : 502; + throw nativeRestFailure(status, `${code}_request_failed`, status === 401 || status === 403 + ? options.deniedMessage + : status === 404 ? `${provider} could not find this resource.` : `${provider} could not complete this operation.`); + } + if (response.status === 204 || !response.body) return null; + const reader = response.body.getReader(); + const chunks: Uint8Array[] = []; + let length = 0; + try { + for (;;) { + const part = await withinDeadline(() => reader.read()); + if (part.done) break; + length += part.value.byteLength; + if (length > NATIVE_REST_MAX_BYTES) throw nativeRestFailure(502, `${code}_output_limit`, `${provider}'s response exceeded the limit. Request a smaller page or file range.`); + chunks.push(part.value); + } + } catch (error) { + if (error instanceof HttpError) throw error; + throw nativeRestFailure(502, `${code}_outcome_unknown`, `${provider}'s response was interrupted. Inspect the operation before retrying.`); + } finally { void reader.cancel().catch(() => {}); } + const bytes = Buffer.concat(chunks); + if (!bytes.length) return null; + const contentType = response.headers.get("Content-Type") ?? "application/octet-stream"; + if (contentType.includes("json")) { + try { return sanitize(JSON.parse(bytes.toString("utf8"))); } + catch { throw nativeRestFailure(502, `${code}_invalid_response`, `${provider} returned an invalid response.`); } + } + if (contentType.startsWith("text/")) return sanitize(bytes.toString("utf8")); + if (bytes.includes(Buffer.from(secret))) throw nativeRestFailure(403, `${code}_credential_export_denied`, "The response contains connection credential material."); + return { contentType, contentBase64: bytes.toString("base64"), byteLength: bytes.length }; + } + + return { send, withinDeadline }; +} + +type NativeOperation = { + name: string; title: string; description: string; inputSchema: unknown; + annotations: { readOnlyHint: boolean; destructiveHint: boolean; idempotentHint?: boolean; openWorldHint?: boolean }; +}; +export type NativeCatalogEntry = { toolName: string; versionHash: string; schemaHash: string | null; riskLevel: string }; + +// Match the interop used by the existing plugin configuration validator. +const AjvCtor = (Ajv as any).default ?? Ajv; +const ajv = new AjvCtor({ strict: false, allErrors: true, validateFormats: true }); +((addFormats as any).default ?? addFormats)(ajv); + +/** The deployed contract of a generated provider catalog: agent-visible tools, + * reviewed risk, saved-entry staleness and argument validation. */ +export function nativeCatalog(operationsList: T[]) { + const operations = new Map(operationsList.map(operation => [operation.name, operation])); + const validators = new Map(); + const risk = (name: string): "read" | "write" | "destructive" => { + const operation = operations.get(name); + if (!operation || operation.annotations.destructiveHint) return "destructive"; + return operation.annotations.readOnlyHint ? "read" : "write"; + }; + const tools = operationsList.map(({ name, title, description, inputSchema, annotations }) => ({ + name, title, description, inputSchema: inputSchema as Record, annotations, + })); + const contracts = new Map(tools.map(tool => [tool.name, { + versionHash: descriptorHash(tool, risk(tool.name)), schemaHash: stableHash(tool.inputSchema), riskLevel: risk(tool.name), + }])); + return { + tools, + operation: (name: string) => operations.get(name), + isToolName: (name: string) => operations.has(name), + risk, + isCurrentEntry(entry: NativeCatalogEntry): boolean { + const current = contracts.get(entry.toolName); + return Boolean(current && current.versionHash === entry.versionHash && current.schemaHash === entry.schemaHash && current.riskLevel === entry.riskLevel); + }, + validate(name: string, parameters: unknown): boolean { + const operation = operations.get(name); + if (!operation) return false; + let validate = validators.get(name); + if (!validate) { validate = ajv.compile(operation.inputSchema as object) as ValidateFunction; validators.set(name, validate); } + return validate(parameters) as boolean; + }, + }; +} diff --git a/server/src/services/tool-access.ts b/server/src/services/tool-access.ts index 7bee173fc66..03b7255ebbd 100644 --- a/server/src/services/tool-access.ts +++ b/server/src/services/tool-access.ts @@ -72,7 +72,7 @@ export { loadGitHubGrantMetadata, loadGitHubTokenRepositories } from "./tool-acc import { stableHash } from "./tool-catalog-hash.js"; -import { isDaytonaConnection } from "./daytona.js"; +import { NATIVE_PROVIDER_SLUGS, nativeProviderCatalogFor } from "./native-provider-catalogs.js"; import { connectorToolPolicy } from "./connectors/tool-policy.js"; import { notifyChatEndpointWork } from "./chat-work-notifications.js"; @@ -2412,7 +2412,7 @@ export function toolAccessService( applicationKey: `app-gallery:${galleryEntry?.slug ?? "link"}:${randomUUID()}`, name: applicationName, description: safeApplicationDescription, - type: transport === "rest_api" && ["browser-use-cloud", "daytona"].includes(galleryEntry?.slug ?? "") ? "rest_api" : transport === "mcp_remote" ? "mcp_http" : "mcp_stdio", + type: transport === "rest_api" && ["browser-use-cloud", ...NATIVE_PROVIDER_SLUGS].includes(galleryEntry?.slug ?? "") ? "rest_api" : transport === "mcp_remote" ? "mcp_http" : "mcp_stdio", status: "draft", metadata: galleryEntry ? { @@ -4488,7 +4488,7 @@ export function toolAccessService( companyId, applicationKey: normalizeKey(input.applicationName ?? input.name), name: input.applicationName ?? input.name, - type: isBrowserUseConnection({ transport, config }) || isDaytonaConnection({ transport, config }) ? "rest_api" : transport === "mcp_remote" ? "mcp_http" : "mcp_stdio", + type: isBrowserUseConnection({ transport, config }) || nativeProviderCatalogFor({ transport, config }) ? "rest_api" : transport === "mcp_remote" ? "mcp_http" : "mcp_stdio", status: "active", metadata: {}, }) diff --git a/server/src/services/tool-access/catalog/descriptors.ts b/server/src/services/tool-access/catalog/descriptors.ts index 7644a700019..f30c518ea6c 100644 --- a/server/src/services/tool-access/catalog/descriptors.ts +++ b/server/src/services/tool-access/catalog/descriptors.ts @@ -1,4 +1,4 @@ -import { isDaytonaConnection } from "../../daytona.js"; +import { nativeProviderCatalogFor } from "../../native-provider-catalogs.js"; import { googleWorkspaceToolTarget } from "@paperclipai/shared"; import { BROWSER_USE_TOOLS } from "@paperclipai/shared"; import { isMemoryConnectorId } from "@paperclipai/shared"; @@ -291,5 +291,5 @@ export function isGoogleWorkspaceToolAllowed( export function shouldQuarantineNewEntries( connection: typeof toolConnections.$inferSelect, ): boolean { - return isDaytonaConnection(connection) || asRecord(connection.config).quarantineNewEntries === true; + return Boolean(nativeProviderCatalogFor(connection)) || asRecord(connection.config).quarantineNewEntries === true; } diff --git a/server/src/services/tool-access/catalog/discovery.ts b/server/src/services/tool-access/catalog/discovery.ts index deb7343a5ad..8dfd90c0c19 100644 --- a/server/src/services/tool-access/catalog/discovery.ts +++ b/server/src/services/tool-access/catalog/discovery.ts @@ -5,7 +5,7 @@ import type { ActorInfo } from "../types.js"; import { createOAuthDiscovery } from "../oauth/discovery.js"; import { oauthConfig } from "../oauth/metadata.js"; import { projectedConnectionHeaders } from "../connection-methods.js"; -import { DAYTONA_TOOLS, daytonaClient, isDaytonaConnection } from "../../daytona.js"; +import { nativeProviderCatalogFor } from "../../native-provider-catalogs.js"; import { GOOGLE_WORKSPACE_SERVICES, googleWorkspaceServiceGranted, googleWorkspaceToolName, googleWorkspaceGrantedScopes, isGoogleWorkspaceToolGranted, type GoogleWorkspaceService } from "@paperclipai/shared"; import { isInsufficientConnectionScope, INSUFFICIENT_CONNECTION_SCOPE_MESSAGE } from "../../connection-permission-errors.js"; import { mcpDiscoveryHttpFailure, withMcpConnectionFailure } from "../../mcp-connection-failure.js"; @@ -426,10 +426,11 @@ export function createToolCatalogDiscovery(db: Db, { now, audit, secrets, creden assertSupportedConnection(connection); await assertConnectorConnectionSetupEnabled(connection); if (connection.connectionPurpose === "ai") throw unprocessable("AI connections provide runtime authentication, not tool actions"); - if (isDaytonaConnection(connection)) { + const nativeProvider = nativeProviderCatalogFor(connection); + if (nativeProvider) { const headers = credentialHeaders ?? await resolveCredentialHeaders(connection, actor); - await daytonaClient(headers, (url, init) => requestRemoteHttpEndpoint(new URL(url), init), asRecord(connection.config.methodConfig).organizationId).probe(); - return DAYTONA_TOOLS; + await nativeProvider.probe(connection, headers, (url, init) => requestRemoteHttpEndpoint(new URL(url), init)); + return nativeProvider.tools; } if (isBrowserUseConnection(connection)) { const headers = credentialHeaders ?? await resolveCredentialHeaders(connection, actor); diff --git a/server/src/services/tool-access/catalog/health.ts b/server/src/services/tool-access/catalog/health.ts index e05be18a92b..e67f21ea6ad 100644 --- a/server/src/services/tool-access/catalog/health.ts +++ b/server/src/services/tool-access/catalog/health.ts @@ -3,7 +3,7 @@ import { connectionSetupUrl, connectionReconnectUrl } from "../identifiers.js"; import { createGitHubGrantContinuity } from "../github-grant-continuity.js"; import { createToolRuntimeSlots } from "../runtime/slots.js"; import type { ActorInfo } from "../types.js"; -import { isDaytonaConnection } from "../../daytona.js"; +import { nativeProviderCatalogFor } from "../../native-provider-catalogs.js"; import { retainMcpConnectionFailure } from "../../mcp-connection-failure.js"; import { isBrowserUseConnection } from "../../browser-use-client.js"; import { and, asc, eq, isNull, lte, ne, or, sql } from "drizzle-orm"; @@ -121,7 +121,7 @@ export function createConnectionHealth(db: Db, { now, audit, getConnectionRow, d }); for (const grant of grantsToCheck) await refreshManagedGitHubGrantAccess(connection, grant, actor); - } else if (isBrowserUseConnection(connection) || isDaytonaConnection(connection)) { + } else if (isBrowserUseConnection(connection) || nativeProviderCatalogFor(connection)) { await discoverTools(connection, undefined, actor); } else if (isAgentMailConnection(connection)) { await validateAgentMailConnection(connection); @@ -157,8 +157,8 @@ export function createConnectionHealth(db: Db, { now, audit, getConnectionRow, d : config.sourceTemplateKey === "github" && oauth.connectorProfile === "github.code" ? "GitHub account, installation, and repository access are available." - : isDaytonaConnection(connection) - ? "Daytona API access is connected." + : nativeProviderCatalogFor(connection) + ? `${nativeProviderCatalogFor(connection)!.name} API access is connected.` : isBrowserUseConnection(connection) ? "Browser Use API key is connected." : isAgentMailConnection(connection) diff --git a/server/src/services/tool-access/catalog/publication.ts b/server/src/services/tool-access/catalog/publication.ts index f60d00f213e..f8236f5c58c 100644 --- a/server/src/services/tool-access/catalog/publication.ts +++ b/server/src/services/tool-access/catalog/publication.ts @@ -4,7 +4,7 @@ import { createToolRuntimeSlots } from "../runtime/slots.js"; import type { ActorInfo, ToolAccessMutationDb } from "../types.js"; import { connectionMethodForConnection } from "../connection-methods.js"; import { stableHash, descriptorHash } from "../../tool-catalog-hash.js"; -import { isDaytonaConnection } from "../../daytona.js"; +import { nativeProviderCatalogFor } from "../../native-provider-catalogs.js"; import { googleWorkspaceToolTarget, googleWorkspaceGrantedScopes, isGoogleWorkspaceToolGranted } from "@paperclipai/shared"; import { retainMcpConnectionFailure } from "../../mcp-connection-failure.js"; import { isRemoteMcpConnectorMethod } from "@paperclipai/shared"; @@ -115,9 +115,9 @@ export function createToolCatalogPublication(db: Db, { now, audit, getConnection const existingByName = new Map( existingRows.map((entry) => [entry.toolName, entry]), ); - const nativeDaytona = isDaytonaConnection(connection); + const nativeProvider = Boolean(nativeProviderCatalogFor(connection)); if (connection.config.sourceTemplateKey === "google-workspace" - || nativeDaytona || isRemoteMcpConnectorMethod(connection.config.sourceTemplateKey, connection.config.connectionMethodKey)) { + || nativeProvider || isRemoteMcpConnectorMethod(connection.config.sourceTemplateKey, connection.config.connectionMethodKey)) { const discoveredNames = new Set(descriptors.map((descriptor) => descriptor.name)); // The catalog is shared, but discovery uses only the caller's grant. A // narrower personal consent must not remove actions another active grant @@ -135,13 +135,13 @@ export function createToolCatalogPublication(db: Db, { now, audit, getConnection } // Preserve only consent-filtered actions, not failed-service catalogs or // tools a successful, sufficiently authorized discovery actually removed. - const removedStatus = nativeDaytona ? "removed" : "disabled"; + const removedStatus = nativeProvider ? "removed" : "disabled"; const removedIds = existingRows.filter((entry) => !discoveredNames.has(entry.toolName) && entry.status !== removedStatus && !(otherWorkspaceGrants.some((scopes) => isGoogleWorkspaceToolGranted(entry.toolName, scopes)) && !isGoogleWorkspaceToolGranted(entry.toolName, selectedWorkspaceScopes) && !unavailableServices.includes(googleWorkspaceToolTarget(entry.toolName)?.service ?? ""))).map((entry) => entry.id); if (removedIds.length) await publish(tx => tx.update(toolCatalogEntries) - .set({ status: removedStatus, quarantineReason: nativeDaytona ? "provider_tool_removed" : "mcp_tool_removed", updatedAt: refreshedAt }) + .set({ status: removedStatus, quarantineReason: nativeProvider ? "provider_tool_removed" : "mcp_tool_removed", updatedAt: refreshedAt }) .where(and(eq(toolCatalogEntries.companyId, connection.companyId), eq(toolCatalogEntries.connectionId, connection.id), inArray(toolCatalogEntries.id, removedIds)))); } // Retired native actions are absent from discovery, but old catalog rows @@ -200,7 +200,7 @@ export function createToolCatalogPublication(db: Db, { now, audit, getConnection quarantineOnRefresh && (!existing || changed) && existing?.status !== "disabled" && - (!safeDefault || riskLevel !== "read" || nativeDaytona); + (!safeDefault || riskLevel !== "read" || nativeProvider); const providerPermanentlyBlocked = Boolean( googleProfile && !isGoogleWorkspaceToolAllowed(googleProfile, descriptor), diff --git a/server/src/services/tool-access/catalog/reader.ts b/server/src/services/tool-access/catalog/reader.ts index b7c1d300125..938f5042d87 100644 --- a/server/src/services/tool-access/catalog/reader.ts +++ b/server/src/services/tool-access/catalog/reader.ts @@ -1,7 +1,7 @@ import { singleFlight } from "../single-flight.js"; import type { ActorInfo } from "../types.js"; import { descriptorHash } from "../../tool-catalog-hash.js"; -import { DAYTONA_TOOLS, daytonaRisk, isDaytonaConnection, isDaytonaToolName } from "../../daytona.js"; +import { nativeProviderCatalogFor } from "../../native-provider-catalogs.js"; import { desc, eq } from "drizzle-orm"; import type { Db } from "@paperclipai/db"; import { toolCatalogEntries, toolConnections } from "@paperclipai/db"; @@ -27,18 +27,18 @@ export function createToolCatalogReader(db: Db, options: { catalogCacheTtlMs?: n .where(eq(toolCatalogEntries.connectionId, connection.id)) .orderBy(desc(toolCatalogEntries.updatedAt)); // Native provider catalogs change with the deployed code. Upgrade saved - // Daytona connections too; their first small catalog must not survive - // merely because REST discovery has no remote MCP cache timer. - const nativeDaytona = isDaytonaConnection(connection); + // connections too; an earlier catalog must not survive merely because + // REST discovery has no remote MCP cache timer. + const nativeProvider = nativeProviderCatalogFor(connection); const rowsByName = new Map(rows.map((row) => [row.toolName, row])); - const daytonaCatalogChanged = nativeDaytona && connection.status === "active" && ( - rows.some((row) => row.status !== "removed" && !isDaytonaToolName(row.toolName)) - || DAYTONA_TOOLS.some((tool) => { + const nativeCatalogChanged = Boolean(nativeProvider) && connection.status === "active" && ( + rows.some((row) => row.status !== "removed" && !nativeProvider!.isToolName(row.toolName)) + || nativeProvider!.tools.some((tool) => { const existing = rowsByName.get(tool.name); - return !existing || existing.versionHash !== descriptorHash(tool, daytonaRisk(tool.name)); + return !existing || existing.versionHash !== descriptorHash(tool, nativeProvider!.risk(tool.name)); }) ); - const cacheExpired = daytonaCatalogChanged || ( + const cacheExpired = nativeCatalogChanged || ( connection.transport === "mcp_remote" && connection.status !== "archived" && (rows.length === 0 || @@ -48,7 +48,7 @@ export function createToolCatalogReader(db: Db, options: { catalogCacheTtlMs?: n if (cacheExpired) { try { await singleFlight(catalogRefreshFlights, connection.id, () => - refreshCatalog(connection.id, nativeDaytona && actor ? actor : { + refreshCatalog(connection.id, nativeProvider && actor ? actor : { actorType: "system", actorId: "tool_catalog_cache", }), @@ -68,7 +68,7 @@ export function createToolCatalogReader(db: Db, options: { catalogCacheTtlMs?: n // Even failed discovery must not keep retired native tools visible. // Their database rows remain available to historical invocation records. return rows - .filter((row) => !nativeDaytona || (row.status !== "removed" && isDaytonaToolName(row.toolName))) + .filter((row) => !nativeProvider || (row.status !== "removed" && nativeProvider.isToolName(row.toolName))) .map((row) => toCatalogEntryForConnection(row, connection)); } }; diff --git a/server/src/services/tool-access/token-broker/service.ts b/server/src/services/tool-access/token-broker/service.ts index f6757e383bc..2d914ae2030 100644 --- a/server/src/services/tool-access/token-broker/service.ts +++ b/server/src/services/tool-access/token-broker/service.ts @@ -1,4 +1,4 @@ -import { isDaytonaConnection } from "../../daytona.js"; +import { nativeProviderCatalogFor } from "../../native-provider-catalogs.js"; import { isBrowserUseConnection } from "../../browser-use-client.js"; import { and, eq, sql } from "drizzle-orm"; import type { Db } from "@paperclipai/db"; @@ -58,7 +58,8 @@ export function createConnectionTokenBroker(db: Db, { now, secrets, policySvc, a input.connectionId, input.companyId, ); - if (isDaytonaConnection(connection)) throw forbidden("Daytona credentials stay in the governed tool gateway and cannot be exported."); + const nativeProvider = nativeProviderCatalogFor(connection); + if (nativeProvider) throw forbidden(`${nativeProvider.name} credentials stay in the governed tool gateway and cannot be exported.`); if (isBrowserUseConnection(connection)) throw forbidden("Browser Use credentials stay in the governed tool gateway and cannot be exported."); if (connection.connectionPurpose === "ai") throw unprocessable("AI credentials are available only through the runtime resolver"); const application = await getConnectionApplication(connection); diff --git a/server/src/services/tool-gateway.ts b/server/src/services/tool-gateway.ts index 3e97a9fd7cc..7bfde3b1b85 100644 --- a/server/src/services/tool-gateway.ts +++ b/server/src/services/tool-gateway.ts @@ -5,7 +5,7 @@ import type { BudgetServiceHooks } from "./budgets.js"; import type { PromptInjectionDetectorMetadata } from "@paperclipai/shared"; import { notifyDeliveryWork, DELIVERY_QUEUES } from "./delivery-work-notifications.js"; import { measureToolDiscovery, measureRunPhase } from "./execution-preparation-timing.js"; -import { executeDaytonaTool, isDaytonaConnection, isCurrentDaytonaCatalogEntry } from "./daytona.js"; +import { nativeProviderCatalogFor } from "./native-provider-catalogs.js"; import { composeConnectionInstructions } from "./connection-instructions.js"; import { googleWorkspaceToolTarget, googleWorkspaceGrantedScopes, isGoogleWorkspaceToolGranted } from "@paperclipai/shared"; import { isInsufficientConnectionScope, INSUFFICIENT_CONNECTION_SCOPE_MESSAGE } from "./connection-permission-errors.js"; @@ -1445,9 +1445,9 @@ export function createToolGatewayService( ({ catalogEntry, connection, application }) => (!featureSettings || isConnectorFeatureEnabled(getAppDefinitionForApplicationKey(application.applicationKey), featureSettings)) && !isRetiredComposioConnection(connection) && - !(isDaytonaConnection(connection) && !isCurrentDaytonaCatalogEntry(catalogEntry)) && + !(nativeProviderCatalogFor(connection) && !nativeProviderCatalogFor(connection)!.isCurrentEntry(catalogEntry)) && !(isRailwayEndpoint(connection.config.url) && (isRailwayToolBlocked(catalogEntry.toolName) || (normalizeRailwayToolName(catalogEntry.toolName).startsWith(RAILWAY_TOOL_PREFIX) && connection.config.railwayApiStatus !== "available"))) && - (((isBrowserUseConnection(connection) || isDaytonaConnection(connection)) && application.type === "rest_api") || + (((isBrowserUseConnection(connection) || nativeProviderCatalogFor(connection)) && application.type === "rest_api") || (connection.transport === "mcp_remote" && application.type === "mcp_http") || (connection.transport === "local_stdio" && @@ -1473,7 +1473,7 @@ export function createToolGatewayService( ({ catalogEntry, connection, application }, index) => { if ( connection.transport !== "mcp_remote" && - connection.transport !== "local_stdio" && !isBrowserUseConnection(connection) && !isDaytonaConnection(connection) + connection.transport !== "local_stdio" && !isBrowserUseConnection(connection) && !nativeProviderCatalogFor(connection) ) { throw new Error( `Non-MCP connection ${connection.id} cannot be exposed through the MCP gateway`, @@ -4903,7 +4903,7 @@ export function createToolGatewayService( ), ) .limit(1); - if (!connection || (connection.transport !== "mcp_remote" && !isBrowserUseConnection(connection) && !isDaytonaConnection(connection))) { + if (!connection || (connection.transport !== "mcp_remote" && !isBrowserUseConnection(connection) && !nativeProviderCatalogFor(connection))) { throw new ToolGatewayHttpError( 404, `Tool "${tool.name}" not found`, @@ -4926,8 +4926,9 @@ export function createToolGatewayService( if (isRailwayEndpoint(connection.config.url) && isRailwayToolBlocked(entry.toolName)) { throw new ToolGatewayHttpError(403, "This Railway action cannot bind its effects to an approved target. Use redeploy, restart, or rollback for an existing deployment.", "railway_action_blocked"); } - if (isDaytonaConnection(connection) && !isCurrentDaytonaCatalogEntry(entry)) { - throw new ToolGatewayHttpError(409, "Daytona's action catalog changed. Review the updated actions in Permissions.", "daytona_catalog_changed"); + const nativeProvider = nativeProviderCatalogFor(connection); + if (nativeProvider && !nativeProvider.isCurrentEntry(entry)) { + throw new ToolGatewayHttpError(409, `${nativeProvider.name}'s action catalog changed. Review the updated actions in Permissions.`, `${nativeProvider.slug}_catalog_changed`); } return { entry, connection }; } @@ -6069,11 +6070,12 @@ export function createToolGatewayService( ms = railwayCommandBudgetMs(parameters); } const grant = await resolveConnectionGrant(session, connection); - if (isDaytonaConnection(connection)) { + const nativeProvider = nativeProviderCatalogFor(connection); + if (nativeProvider) { const budgetMs = Math.min(ms, 30_000); const deadlineAt = Date.now() + budgetMs; const signal = AbortSignal.timeout(budgetMs); - const data = await executeDaytonaTool({ binding: session, connection, + const data = await nativeProvider.execute({ binding: session, connection, headers: await resolveCredentialHeaders(session, connection, grant), request: (url, init) => options.remoteHttpRequest ? options.remoteHttpRequest(url, init) : guardedRemoteHttpFetch(url, init, remoteHttpFetchOptions()), diff --git a/tests/e2e/unikraft-connector.spec.ts b/tests/e2e/unikraft-connector.spec.ts new file mode 100644 index 00000000000..eba676cf29e --- /dev/null +++ b/tests/e2e/unikraft-connector.spec.ts @@ -0,0 +1,26 @@ +import { expect, test } from "@playwright/test"; + +test("Unikraft uses the ordinary access and credential wizard", async ({ page, request }) => { + const response = await request.post("/api/companies", { data: { name: `Unikraft setup ${Date.now()}` } }); + expect(response.ok()).toBe(true); + const company = await response.json(); + await page.goto(`/${company.issuePrefix}/apps`); + await expect(page.getByRole("heading", { name: "Connectors", exact: true })).toBeVisible(); + await page.getByRole("searchbox", { name: "Search connectors" }).fill("Unikraft"); + const card = page.getByRole("list", { name: "Connector list" }).getByRole("listitem").filter({ hasText: "Unikraft" }); + await expect(card).toHaveCount(1); + // App marks are decorative (empty alt), so they have no accessible img role. + await expect(card.locator('img[src="/brands/apps/unikraft.svg"]')).toBeVisible(); + await card.getByRole("button", { name: "Add key Unikraft", exact: true }).click(); + await expect(page.getByRole("heading", { name: "Connect Unikraft", exact: true })).toBeVisible(); + await expect(page.getByLabel("Your Unikraft key", { exact: true })).toBeVisible(); + await expect(page.getByLabel("Your Unikraft key", { exact: true })).toHaveAttribute("type", "password"); + await expect(page.getByText("Default metro", { exact: true })).not.toBeVisible(); + await expect(page.getByText("Connects for everyone in your organization, available to all agents.", { exact: true })).toBeVisible(); + await page.getByRole("button", { name: "Change", exact: true }).click(); + await expect(page.getByText("Which humans can use this credential?", { exact: true })).toBeVisible(); + await expect(page.getByText("Default metro", { exact: true })).toBeVisible(); + await expect(page.locator("select").filter({ has: page.locator('option[value="fra"]') })).toHaveValue("fra"); + // No token is submitted and no real provider connection is created. Gateway + // lifecycle and provider reads are exercised in the database-backed suite. +}); diff --git a/ui/artifact-directions.html b/ui/artifact-directions.html new file mode 100644 index 00000000000..ef159c5bad3 --- /dev/null +++ b/ui/artifact-directions.html @@ -0,0 +1,38 @@ + + + + + + + Artifact directions — lab + + + + +
+ + + diff --git a/ui/public/brands/apps/manifest.json b/ui/public/brands/apps/manifest.json index e77518449c9..110ab305261 100644 --- a/ui/public/brands/apps/manifest.json +++ b/ui/public/brands/apps/manifest.json @@ -702,6 +702,12 @@ "catalogVisible": true, "localAsset": "/brands/apps/paperclip-notebook.svg", "darkAsset": "/brands/apps/paperclip-notebook-dark.svg" + }, + { + "slug": "unikraft", + "provider": "Unikraft", + "catalogVisible": true, + "localAsset": "/brands/apps/unikraft.svg" } ] } diff --git a/ui/public/brands/apps/unikraft.svg b/ui/public/brands/apps/unikraft.svg new file mode 100644 index 00000000000..834d2ff3a13 --- /dev/null +++ b/ui/public/brands/apps/unikraft.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/artifact-directions/api-stub.test.ts b/ui/src/artifact-directions/api-stub.test.ts new file mode 100644 index 00000000000..c909a3a2988 --- /dev/null +++ b/ui/src/artifact-directions/api-stub.test.ts @@ -0,0 +1,45 @@ +// @vitest-environment jsdom + +import { afterEach, describe, expect, it } from "vitest"; +import { installApiStub, registerApiRoute } from "./api-stub"; +import { COMPANY } from "./fixtures"; + +describe("installApiStub", () => { + const original = window.fetch; + const restores: Array<() => void> = []; + + afterEach(() => { + while (restores.length) restores.pop()?.(); + window.fetch = original; + }); + + it("answers /api/ from the fixtures and puts fetch back when restored", async () => { + restores.push(installApiStub()); + expect(window.fetch).not.toBe(original); + const companies = await (await window.fetch("/api/companies")).json(); + expect(companies).toEqual([COMPANY]); + restores.pop()?.(); + expect(window.fetch).toBe(original); + }); + + it("serves a registered route and answers anything else under /api/ with an empty object", async () => { + registerApiRoute((pathname) => (pathname === "/api/lab/ping" ? { pong: true } : undefined)); + restores.push(installApiStub()); + expect(await (await window.fetch("/api/lab/ping")).json()).toEqual({ pong: true }); + expect(await (await window.fetch("/api/lab/nothing", { method: "POST" })).json()).toEqual({}); + }); + + it("keeps one stub across nested installs and restores on the last release", () => { + const first = installApiStub(); + const stub = window.fetch; + const second = installApiStub(); + restores.push(first, second); + expect(window.fetch).toBe(stub); + first(); + expect(window.fetch).toBe(stub); + first(); // a second call of the same restore is a no-op + expect(window.fetch).toBe(stub); + second(); + expect(window.fetch).toBe(original); + }); +}); diff --git a/ui/src/artifact-directions/api-stub.ts b/ui/src/artifact-directions/api-stub.ts new file mode 100644 index 00000000000..143582d7de8 --- /dev/null +++ b/ui/src/artifact-directions/api-stub.ts @@ -0,0 +1,71 @@ +import { COMPANY } from "./fixtures"; + +/** + * The lab's only "server". Every API call in the app goes through `fetch`, so + * standing in for `/api/` here is enough to render real data-fetching + * components with no backend and no module mocks. Nothing under `/api/` ever + * reaches the network; everything else (fonts, assets) passes through. + * + * A direction that renders a real component registers the routes it needs. + * `installApiStub` returns a restore, so a story can scope the stub to itself. + */ + +/** Returns the JSON body for a path it owns, or `undefined` to pass. */ +export type ApiRoute = (pathname: string, method: string) => unknown | undefined; + +const routes: ApiRoute[] = []; + +export function registerApiRoute(route: ApiRoute): void { + routes.push(route); +} + +const json = (body: unknown) => + new Response(JSON.stringify(body), { status: 200, headers: { "content-type": "application/json" } }); + +function respond(pathname: string, method: string): Response { + for (const route of routes) { + const body = route(pathname, method); + if (body !== undefined) return json(body); + } + if (pathname.endsWith("/auth/get-session")) { + return json({ session: { id: "lab-session", userId: "lab-user" }, user: { id: "lab-user", name: "Lab", email: "lab@acmehats.example" } }); + } + if (pathname.endsWith("/companies")) return json([COMPANY]); + // An unhandled call answers empty rather than failing, so it is never the + // reason a frame looks broken; the debug line says what was asked for. + console.debug("[artifact-directions] unhandled", method, pathname); + return json({}); +} + +let holders = 0; +let realFetch: typeof window.fetch | null = null; +let stubFetch: typeof window.fetch | null = null; + +/** + * Stands in for `/api/` until the returned function is called. The lab page + * installs once for its lifetime; a Storybook story installs on mount and + * restores on unmount. Nested installs share one stub, and the last restore + * puts the real `fetch` back — unless something else replaced it meanwhile. + */ +export function installApiStub(): () => void { + if (holders === 0) { + const original = window.fetch; + const real = original.bind(window); + realFetch = original; + stubFetch = (async (input: RequestInfo | URL, init?: RequestInit) => { + const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url; + if (!url.includes("/api/")) return real(input as RequestInfo, init); + const { pathname } = new URL(url, window.location.origin); + return respond(pathname, (init?.method ?? "GET").toUpperCase()); + }) as typeof window.fetch; + window.fetch = stubFetch; + } + holders += 1; + let released = false; + return () => { + if (released) return; + released = true; + holders -= 1; + if (holders === 0 && realFetch && window.fetch === stubFetch) window.fetch = realFetch; + }; +} diff --git a/ui/src/artifact-directions/assets/acmehats-logo-lockup.svg b/ui/src/artifact-directions/assets/acmehats-logo-lockup.svg new file mode 100644 index 00000000000..516c93529bf --- /dev/null +++ b/ui/src/artifact-directions/assets/acmehats-logo-lockup.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + acmehats + CUSTOM CAPS · EST. 2026 \ No newline at end of file diff --git a/ui/src/artifact-directions/assets/avatar-ceo.svg b/ui/src/artifact-directions/assets/avatar-ceo.svg new file mode 100644 index 00000000000..36fc3cc1ca6 --- /dev/null +++ b/ui/src/artifact-directions/assets/avatar-ceo.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/ui/src/artifact-directions/assets/avatar-designer.svg b/ui/src/artifact-directions/assets/avatar-designer.svg new file mode 100644 index 00000000000..4309569f34d --- /dev/null +++ b/ui/src/artifact-directions/assets/avatar-designer.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/ui/src/artifact-directions/assets/cap-reel-poster.svg b/ui/src/artifact-directions/assets/cap-reel-poster.svg new file mode 100644 index 00000000000..25c4373d142 --- /dev/null +++ b/ui/src/artifact-directions/assets/cap-reel-poster.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + ACMEHATS — SIGNAL ORANGE, 360° \ No newline at end of file diff --git a/ui/src/artifact-directions/assets/cap-reel.webm b/ui/src/artifact-directions/assets/cap-reel.webm new file mode 100644 index 00000000000..92335775b9b Binary files /dev/null and b/ui/src/artifact-directions/assets/cap-reel.webm differ diff --git a/ui/src/artifact-directions/assets/cap-shot-black.svg b/ui/src/artifact-directions/assets/cap-shot-black.svg new file mode 100644 index 00000000000..8e413878bba --- /dev/null +++ b/ui/src/artifact-directions/assets/cap-shot-black.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Your place. Your cap. \ No newline at end of file diff --git a/ui/src/artifact-directions/assets/cap-shot-cobalt.svg b/ui/src/artifact-directions/assets/cap-shot-cobalt.svg new file mode 100644 index 00000000000..fc0464add97 --- /dev/null +++ b/ui/src/artifact-directions/assets/cap-shot-cobalt.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + THE SHARED MARK \ No newline at end of file diff --git a/ui/src/artifact-directions/assets/cap-shot-forest.svg b/ui/src/artifact-directions/assets/cap-shot-forest.svg new file mode 100644 index 00000000000..f9a58eed085 --- /dev/null +++ b/ui/src/artifact-directions/assets/cap-shot-forest.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + a \ No newline at end of file diff --git a/ui/src/artifact-directions/assets/contact-sheet-first-draft.svg b/ui/src/artifact-directions/assets/contact-sheet-first-draft.svg new file mode 100644 index 00000000000..8c4cc0b97a3 --- /dev/null +++ b/ui/src/artifact-directions/assets/contact-sheet-first-draft.svg @@ -0,0 +1,56 @@ +Your place.Your people.Your cap. + a + +Build an identity before making promises. + OpeningHypothesisStill open + +Specificity is the creative opening. + CustomersCategoryEvidenceAdvantage + +Start with the people behind the counter. + Independent cafés& hospitality + Ownership prideCorporate teamsClubs and makers +Make the place recognizable. + OpeningRecognitionPracticalityBelonging +Belonging is the ambition. Proof comes next. + For independent places,acmehats imagines custom capsthat carry the character of theplace and its people. + +Start with the character of a place. + The name. The people. The familiar details.A cap can become a small expressionof that shared identity. + +The Local Signature + acmehats + Familiar. Assured.Observant. + +Let the customer's name lead. + + + + + + + + + + + Your place.Your people.Your cap. +The Shared Mark + acmehats + Warm. Bold.Participatory. + +A shared identity with more energy. + + + + + + + + + + + + Your place.Your people.Your cap. +Choose The Local Signature to develop. + A / Recommended + B / Alternative \ No newline at end of file diff --git a/ui/src/artifact-directions/assets/contact-sheet-interface-craft.svg b/ui/src/artifact-directions/assets/contact-sheet-interface-craft.svg new file mode 100644 index 00000000000..02612696198 --- /dev/null +++ b/ui/src/artifact-directions/assets/contact-sheet-interface-craft.svg @@ -0,0 +1,65 @@ +Your place.Your people.Your cap. + + + + + + + + + + + +Build an identity before making promises. + OpeningHypothesisStill open + +Specificity is the creative opening. + CustomersCategoryEvidenceAdvantage + +Start with the people behind the counter. + Independent cafés& hospitality + Ownership prideCorporate teamsClubs and makers +Make the place recognizable. + OpeningRecognitionPracticalityBelonging +Belonging is the ambition. Proof comes next. + For independent places,acmehats imagines custom capsthat carry the character of theplace and its people. + +Start with the character of a place. + The name. The people. The familiar details.A cap can become a small expressionof that shared identity. + +The Local Signature + acme hats + Familiar. Assured.Observant. + +Let the customer's name lead. + + + + + + + + + + + Your place.Your people.Your cap. +The Shared Mark + acmehats + Warm. Bold.Participatory. + +A shared identity with more energy. + + + + + + + + + + + + Your place.Your people.Your cap. +Choose The Local Signature to develop. + A / Recommended + B / Alternative \ No newline at end of file diff --git a/ui/src/artifact-directions/assets/contact-sheet-revised.svg b/ui/src/artifact-directions/assets/contact-sheet-revised.svg new file mode 100644 index 00000000000..eae06750c9b --- /dev/null +++ b/ui/src/artifact-directions/assets/contact-sheet-revised.svg @@ -0,0 +1,65 @@ +Your place.Your people.Your cap. + + + + + + + + + + + +Build an identity before making promises. + OpeningHypothesisStill open + +Specificity is the creative opening. + CustomersCategoryEvidenceAdvantage + +Start with the people behind the counter. + Independent cafés& hospitality + Ownership prideCorporate teamsClubs and makers +Make the place recognizable. + OpeningRecognitionPracticalityBelonging +Belonging is the ambition. Proof comes next. + For independent places,acmehats imagines custom capsthat carry the character of theplace and its people. + +Start with the character of a place. + The name. The people. The familiar details.A cap can become a small expressionof that shared identity. + +The Local Signature + acmehats + Familiar. Assured.Observant. + +Let the customer's name lead. + + + + + + + + + + + Your place.Your people.Your cap. +The Shared Mark + acmehats + Warm. Bold.Participatory. + +A shared identity with more energy. + + + + + + + + + + + + Your place.Your people.Your cap. +Choose The Local Signature to develop. + A / Recommended + B / Alternative \ No newline at end of file diff --git a/ui/src/artifact-directions/assets/storefront-preview.svg b/ui/src/artifact-directions/assets/storefront-preview.svg new file mode 100644 index 00000000000..24da8345c5c --- /dev/null +++ b/ui/src/artifact-directions/assets/storefront-preview.svg @@ -0,0 +1,15 @@ +acmehats + + Caps that carryyour place. + Design yours + + + + + + + + + + + \ No newline at end of file diff --git a/ui/src/artifact-directions/directions/d1-solid/Counts.tsx b/ui/src/artifact-directions/directions/d1-solid/Counts.tsx new file mode 100644 index 00000000000..56911f04ca8 --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/Counts.tsx @@ -0,0 +1,65 @@ +import { Fragment } from "react"; +import { cn } from "@/lib/utils"; +import type { LabArtifact } from "../../kit"; +import { MetaDot } from "./MetaLine"; +import { StateDot } from "./StateMarker"; + +/** + * The mono count label, one grammar on every surface: a total in foreground, + * then any breakdown, then what is waiting — each waiting count behind its + * state dot, which is its separator. The caller sets the mono recipe + * (META_TEXT) and the muted colour for the fill it sits on. + * + * thread 5 attachments · 1 image · 4 files ● 5 to review + * panel 21 artifacts ● 9 to review + * gallery 6 stacks · 43 artifacts ● 12 to review ● 1 change requested + * + * Every item is unbreakable and the label wraps between items, so in a narrow + * tile or pane it takes a second line instead of running out of its slab. + */ + +const reviewCount = (items: LabArtifact[]) => items.filter((a) => a.review === "needs_review").length; +const changesCount = (items: LabArtifact[]) => items.filter((a) => a.review === "changes_requested").length; + +export function Counts({ + lead, + parts = [], + of, + leadClassName = "text-foreground", +}: { + /** "21 artifacts" */ + lead: string; + /** Plain breakdown after the lead: "1 image", "4 files". */ + parts?: string[]; + /** The artifacts counted: what waits among them is appended behind its state dot. */ + of?: LabArtifact[]; + leadClassName?: string; +}) { + const review = of ? reviewCount(of) : 0; + const changes = of ? changesCount(of) : 0; + return ( + + + {lead} + {parts.map((part) => ( + + + {part} + + ))} + + {review > 0 ? ( + + + {review} to review + + ) : null} + {changes > 0 ? ( + + + {changes} {changes === 1 ? "change" : "changes"} requested + + ) : null} + + ); +} diff --git a/ui/src/artifact-directions/directions/d1-solid/ExcerptSheet.tsx b/ui/src/artifact-directions/directions/d1-solid/ExcerptSheet.tsx new file mode 100644 index 00000000000..27938a62f6d --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/ExcerptSheet.tsx @@ -0,0 +1,204 @@ +import { Fragment } from "react"; +import { cn } from "@/lib/utils"; +import type { LabArtifact } from "../../kit"; +import { KindTag } from "./KindBlock"; + +/** + * The preview for kinds whose preview is TEXT — task documents, Markdown, code + * and sheets — at panel and gallery scale: the artifact's own words set as + * real text on a SHEET, the lightest fill short of ink in both themes (paper + * on a mat), so content is the filled thing and never a recess. + * + * prose sans; the opening line and every heading in foreground medium, + * body in the sheet's text tone + * code the mono token, whitespace kept, a long line ends in an ellipsis + * sheet a table in the mono token, header row in foreground; whole columns + * only, as many as the sheet is wide enough for, the rest counted + * ("+2 cols") + * + * Three shapes: + * plate (default) a gallery preview: the kind tab in the bottom-left + * corner, prose at 13px, as many WHOLE lines as the plate's height + * has room for above the tab (--solid-sheet-fit) — at any column + * width, never a fixed count. + * cell a small cell of a stack mosaic: the same at 11px, 8px inset. + * drawer a panel card's preview: no tab (the card's kind block is right + * above it), height set by `lines`, 12px inset like the slab. + * + * The thread never uses this: at 72×44 a text kind is an ink plate. + * No fade at the bottom edge (no gradients in this direction). + */ + +const CLAMP = { 3: "line-clamp-3", 6: "line-clamp-6" } as const; + +/** Whether an artifact's preview is its text (a picture always wins). */ +export const hasExcerpt = (a: LabArtifact) => Boolean(a.excerpt) && !a.thumbnailUrl; + +const words = (text: string) => text.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim(); +const paragraphs = (a: LabArtifact) => (a.body ?? a.excerpt ?? "").trim().split(/\n{2,}/); +const HEADING = /^#{1,6}\s+/; + +/** Markdown to the text a sheet sets: bullets kept as bullets, link and emphasis syntax dropped. */ +const inline = (markdown: string) => + markdown + .replace(/^\s*[-*]\s+/gm, "• ") + .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1") + .replace(/[*_`]/g, ""); + +/** Whether the text opens with the artifact's own title, which the surface already prints beside the sheet. */ +const opensWithTitle = (a: LabArtifact) => words(paragraphs(a)[0].replace(HEADING, "")) === words(a.title); + +interface Run { + text: string; + strong: boolean; + /** What separates it from the run before: a new line, or a space within the same paragraph. */ + lead: "\n" | " "; +} + +/** + * The prose as runs. The repeated title is left out; headings are strong; and + * the sheet always opens on a strong line — a short opening paragraph whole, + * a long one by its first sentence. + */ +function runs(a: LabArtifact): Run[] { + const all = paragraphs(a); + const blocks = (opensWithTitle(a) && all.length > 1 ? all.slice(1) : all).slice(0, 10); + const out: Run[] = blocks.map((block) => ({ text: inline(block.replace(HEADING, "")), strong: HEADING.test(block), lead: "\n" })); + const first = out[0]; + if (!first || first.strong) return out; + const sentence = first.text.length > 90 ? /^(.+?[.!?])\s+([\s\S]+)$/.exec(first.text) : null; + if (!sentence) return [{ ...first, strong: true }, ...out.slice(1)]; + return [{ text: sentence[1], strong: true, lead: "\n" }, { text: sentence[2], strong: false, lead: " " }, ...out.slice(1)]; +} + +const SHEET_TEXT = "text-(--solid-sheet-text)"; +/** + * Code and tables: 11px on 16px in a drawer or a mosaic cell; a size up on a + * tile's plate, which is read from further away (as prose is) — from 16rem of + * sheet, so the large cell of a mosaic keeps the smaller size and its columns. + */ +const MONO = "font-mono text-(length:--text-micro) leading-4"; +const MONO_PLATE = "font-mono text-(length:--text-micro) leading-4 @3xs:text-xs @3xs:leading-5"; + +/** + * Columns are shown whole or not at all. How many is decided by the width of + * the sheet itself (its own container, inside its inset), so a table never + * runs out of its sheet: two, a third from 18rem, a fourth from 24rem, a fifth + * from 32rem. A mosaic cell shows the first column only. What is left out is + * counted in the header row. + */ +const COLUMN = ["", "", "hidden @2xs:table-cell", "hidden @sm:table-cell", "hidden @lg:table-cell"]; +const SHOWN = [ + { columns: 2, when: "@2xs:hidden" }, + { columns: 3, when: "hidden @2xs:table-cell @sm:hidden" }, + { columns: 4, when: "hidden @sm:table-cell @lg:hidden" }, + { columns: 5, when: "hidden @lg:table-cell" }, +]; + +function Table({ csv, rows, single, mono, className }: { csv: string; rows: number; single: boolean; mono: string; className?: string }) { + const [head, ...body] = csv + .trim() + .split("\n") + .slice(0, rows) + .map((line) => line.split(",").slice(0, single ? 1 : COLUMN.length)); + const total = csv.trim().split("\n")[0].split(",").length; + const notes = single ? [{ columns: 1, when: "" }] : SHOWN; + return ( +
+ + + + {head.map((cell, index) => ( + + ))} + {notes.map((note) => + total > note.columns ? ( + + ) : null, + )} + + + + {body.map((row, index) => ( + + {row.map((cell, column) => ( + + ))} + + ))} + +
+ {cell} + + +{total - note.columns} + {total - note.columns === 1 ? "col" : "cols"} +
+ {cell} +
+
+ ); +} + +export function ExcerptSheet({ + artifact, + shape = "plate", + lines = 3, + className, +}: { + artifact: LabArtifact; + shape?: "plate" | "cell" | "drawer"; + /** A drawer's depth in whole lines of prose (code and tables get one more row: their lines are shorter). */ + lines?: keyof typeof CLAMP; + /** Sizing. A plate or a cell fills what it is given; a drawer is as tall as its lines. */ + className?: string; +}) { + const text = artifact.excerpt ?? ""; + const drawer = shape === "drawer"; + const cell = shape === "cell"; + const fit = drawer ? null : "max-h-(--solid-sheet-fit) overflow-hidden"; + // A drawer shows a set number of rows; a fitted sheet is given more than it can hold and keeps whole ones. + const rows = drawer ? lines + 1 : 14; + const mono = shape === "plate" ? MONO_PLATE : MONO; + return ( +
+ {artifact.kind === "sheet" ? ( + + ) : artifact.kind === "code" ? ( +
+ {text + .split("\n") + .slice(0, rows) + .map((line, index) => ( + + {line || " "} + + ))} +
+ ) : ( + // The size comes first: tailwind-merge drops a `leading-*` that precedes a font size. +
+ {runs(artifact).map((run, index) => ( + + {index > 0 ? run.lead : null} + {run.strong ? {run.text} : run.text} + + ))} +
+ )} + {drawer ? null : } + + ); +} diff --git a/ui/src/artifact-directions/directions/d1-solid/Gallery.tsx b/ui/src/artifact-directions/directions/d1-solid/Gallery.tsx new file mode 100644 index 00000000000..8408ab99677 --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/Gallery.tsx @@ -0,0 +1,114 @@ +import { cn } from "@/lib/utils"; +import type { LabGroup } from "../../kit"; +import { Counts } from "./Counts"; +import { daySections, type DaySection } from "./gallery-model"; +import { GalleryTile } from "./GalleryTile"; +import { plural } from "./kinds"; +import { META_TEXT } from "./MetaLine"; +import { StackTile } from "./StackTile"; + +/** + * GALLERY — the company Artifacts page, below its toolbar. + * + * The product's grid (1 / 2 / 3 columns at <768 / 768 / 1280px, 24px gutters + * both ways, inside the page's 1152px column), with the direction's mono + * count label above it — the line that heads the thread's attachments and the + * panel's shelf. + * + * stacks 6 stacks · 43 artifacts ● 12 to review ● 1 change requested + * [stack] [stack] [stack] … + * + * stack 21 artifacts ● 9 to review + * + * Monday, September 28 ← a day: the page's one large line + * 17 artifacts ● 9 to review + * [tile] [tile] [tile] … + * + * Saturday, September 26 + * 2 artifacts + * [tile] [tile] + * + * (`?days=off`: the same tiles in the product's flat grid, without the days.) + */ + +const GRID = "grid grid-cols-1 gap-6 md:grid-cols-2 xl:grid-cols-3"; +const COUNT_LINE = cn(META_TEXT, "px-1 text-muted-foreground"); + +export function GalleryStacks({ groups }: { groups: LabGroup[] }) { + if (groups.length === 0) return

No artifact stacks yet.

; + const all = groups.flatMap((group) => group.artifacts); + const total = groups.reduce((sum, group) => sum + group.count, 0); + return ( +
+

+ +

+ {/* Each tile keeps 28px above its face for the pile; the 24px row gap leaves a risen pile clear of the row above. */} +
    + {groups.map((group) => ( +
  • + +
  • + ))} +
+
+ ); +} + +function Day({ id, section }: { id: string; section: DaySection }) { + return ( +
+
+

+ {section.label} +

+

+ +

+
+
    + {section.items.map((artifact) => ( +
  • + +
  • + ))} +
+
+ ); +} + +/** + * The lab's one switch. Sectioning an opened stack by day is a change of + * structure riding on a visual direction; `?days=off` lists the stack as the + * product does today — one flat grid in the page's order — so the tiles can be + * judged like for like. + */ +const flat = () => typeof window !== "undefined" && new URLSearchParams(window.location.search).get("days") === "off"; + +export function GalleryStack({ group }: { group: LabGroup }) { + if (group.artifacts.length === 0) { + return

No artifacts in this stack match the current filters.

; + } + return ( +
+

+ +

+ {flat() ? ( +
    + {group.artifacts.map((artifact) => ( +
  • + +
  • + ))} +
+ ) : ( +
+ {daySections(group.artifacts).map((section) => ( + + ))} +
+ )} +
+ ); +} diff --git a/ui/src/artifact-directions/directions/d1-solid/GalleryPreview.tsx b/ui/src/artifact-directions/directions/d1-solid/GalleryPreview.tsx new file mode 100644 index 00000000000..64d9fddd77b --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/GalleryPreview.tsx @@ -0,0 +1,98 @@ +import { useState } from "react"; +import { Play } from "lucide-react"; +import { cn } from "@/lib/utils"; +import type { LabArtifact } from "../../kit"; +import { ExcerptSheet } from "./ExcerptSheet"; +import { previewKind } from "./gallery-model"; +import { extent, fileName, metaLabel } from "./kinds"; +import { CornerTag, KindDock, KindTag, PlatePicture } from "./KindBlock"; +import { META_TEXT } from "./MetaLine"; +import { StateMarker } from "./StateMarker"; + +/** + * GALLERY — the preview of one artifact. Every kind gets a preview that says + * something, and what fills it is the artifact's CONTENT, never its extension: + * + * picture the picture, full-bleed ink kind tab bottom-left + * text its own words on a sheet (ExcerptSheet) ink kind tab bottom-left + * facts a sheet that says what is known of a file the kind block docked bottom-left: + * with nothing to show: how much of it there the tab grown to a block, because + * is, at display size, and its filename here the kind is most of the story + * + * So ink is always the mark in the bottom-left corner and never the whole + * plate; a row of previews keeps one baseline of ink, and three decks in a row + * read as three decks. The other corner is the state: a bite the slab takes + * out of the bottom-right (StateMarker). A picture that fails to load becomes + * the facts sheet and says "No preview". + * + * `plate` is a tile's preview (18:11 at the width of its container) and the + * large cell of a stack mosaic; `cell` is a mosaic's small cell. + */ + +function Facts({ artifact, cell, note }: { artifact: LabArtifact; cell: boolean; note?: string }) { + const fact = note ?? extent(artifact) ?? artifact.sizeLabel ?? metaLabel(artifact); + if (cell) { + return ( +
+

{fact}

+ +
+ ); + } + return ( +
+

{fact}

+ {/* One line where the dock leaves room for one; two from 20rem of tile. */} +

+ {fileName(artifact)} +

+ +
+ ); +} + +export function GalleryPreview({ + artifact, + scale = "plate", + state = true, + className, +}: { + artifact: LabArtifact; + scale?: "plate" | "cell"; + /** Show the state bite. Off inside a stack mosaic, where the stack's count line carries state. */ + state?: boolean; + /** Sizing; the plate's 18:11 at the width of its container by default. */ + className?: string; +}) { + const [failed, setFailed] = useState(false); + const kind = failed ? "facts" : previewKind(artifact); + const cell = scale === "cell"; + return ( +
+ {kind === "text" ? ( + + ) : kind === "picture" ? ( + + setFailed(true)} /> + {/* In a mosaic the tab is on the large cell only: three tabs on three photographs spend the signature where it says least. */} + {cell ? ( + artifact.kind === "video" ? ( + + + + ) : null + ) : ( + + )} + + ) : ( + + )} + {state ? : null} +
+ ); +} diff --git a/ui/src/artifact-directions/directions/d1-solid/GalleryTile.tsx b/ui/src/artifact-directions/directions/d1-solid/GalleryTile.tsx new file mode 100644 index 00000000000..1e9d41de1da --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/GalleryTile.tsx @@ -0,0 +1,85 @@ +import { cn } from "@/lib/utils"; +import type { LabArtifact } from "../../kit"; +import { actionName, externalProps, type SolidAction } from "./actions"; +import { galleryActions } from "./gallery-model"; +import { GalleryPreview } from "./GalleryPreview"; +import { META_TEXT, MetaDot, MetaLine } from "./MetaLine"; +import { shortStamp } from "./panel-model"; +import { BUTTON, BUTTON_PRIMARY, COVER, ICON_BUTTON, SLAB, SLAB_HOVER } from "./slab"; + +/** + * GALLERY — one artifact in an opened stack (or the ungrouped grid): a slab + * tile, preview first. + * + * ┌ preview, 18:11 ───────────────────────────────┐ + * │ picture · words on a sheet · a file's facts │ + * │ [KIND] ● Review │ ink bottom-left, state bite bottom-right + * └────────────────────────────────────────────────┘ + * Title, the full width of the tile, up to two lines + * kind · size · rev · extent (mono) + * Sep 28, 11:07 AM · Author [⧉] [↓] [→] (stamp mono, author sans) + * + * The thread row's grammar turned on its end: block, title, mono meta, and + * the actions at the far end of the foot — where the thread row and the panel + * card end theirs. The whole tile is the [→] action's target (the owning + * task, at this artifact), which turns to ink when the tile is hovered. Open + * file in new tab and Download file are ghosts beside it, always visible, so + * they are reachable by touch and by keyboard and sit in no other link; each + * has its own glyph (a box with an arrow leaves the app, a tray downloads, + * the arrow goes to the task). In a tile under 18rem the actions take a row of + * their own. + */ + +function Ghost({ action, artifact }: { action: SolidAction; artifact: LabArtifact }) { + const Icon = action.icon; + return ( + + + + ); +} + +export function GalleryTile({ artifact }: { artifact: LabArtifact }) { + const { primary, secondary } = galleryActions(artifact); + const PrimaryIcon = primary.icon; + return ( +
+ +
+

{artifact.title}

+
+

+ + + + + {artifact.author} + +

+
+ {/* First in the DOM so it is first in the tab order; last in the row. */} + + + + {secondary.map((action) => ( + + ))} +
+
+
+
+ ); +} diff --git a/ui/src/artifact-directions/directions/d1-solid/KindBlock.tsx b/ui/src/artifact-directions/directions/d1-solid/KindBlock.tsx new file mode 100644 index 00000000000..2869452f0a2 --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/KindBlock.tsx @@ -0,0 +1,166 @@ +import { useState, type ReactNode } from "react"; +import { Play } from "lucide-react"; +import { cn } from "@/lib/utils"; +import type { LabArtifact } from "../../kit"; +import { blockLabel, hasThumbnail, kindGlyph } from "./kinds"; +import { SLAB_RING } from "./slab"; + +/** + * SIGNATURE 1 — the kind block: the one inverted (INK) fill in the direction. + * + * One landscape plate (18:11) with the kind's mono-caps label in its + * bottom-left corner and the kind's glyph in the opposite, top-right corner, + * so both corners of the plate are used and kinds differ by shape as well as + * by letters. It comes in three forms: + * + * KindBlock free-standing, the anchor of a row or a card: + * xs 56×36 attachments (label only) sm 72×44 thread row, panel card + * lg 128×96 a picture in the attachments block (4:3: a picture's plate, beside a narrow column of text) + * Where the artifact has a picture, the picture fills the plate + * and nothing is laid over it (a video keeps a play mark); a + * picture that fails to load falls back to the ink plate, so a + * broken thumbnail is never an empty box. + * KindTag the label alone, on an ink tab flush in the bottom-left corner + * of a preview that has content of its own (a picture, a sheet). + * KindDock the block docked in that same corner for a preview that has + * none — the tab grown to a block: 72×44 in a mosaic cell, 131×80 + * on a tile's plate (72×44 while the tile is under 18rem). + * + * Tabs and docks size themselves against the gallery unit they sit in (an + * `@container/unit`: a tile, or a stack's face), never against the viewport. + */ + +export type KindBlockSize = "xs" | "sm" | "lg"; +type FaceSize = KindBlockSize | "plate"; + +const BOX: Record = { + xs: "h-9 w-14", + sm: "h-11 w-18", + lg: "h-24 w-32", + plate: "h-11 w-18 @2xs/unit:h-20 @2xs/unit:w-auto @2xs/unit:aspect-(--solid-plate-ratio)", +}; +const PAD: Record = { xs: "p-1.5", sm: "px-2 py-1.5", lg: "p-3", plate: "px-2 py-1.5 @2xs/unit:p-3" }; +/** The glyph sits in the top-right corner, at the plate's own inset. xs has no room for one. */ +const GLYPH: Record = { + xs: null, + sm: "top-1.5 right-2 size-5", + lg: "top-3 right-3 size-7", + plate: "top-1.5 right-2 size-5 @2xs/unit:top-3 @2xs/unit:right-3 @2xs/unit:size-7", +}; +const LABEL: Record = { xs: "text-xs", sm: "text-xs", lg: "text-lg", plate: "text-xs @2xs/unit:text-2xl" }; + +/** Mono caps. Always followed by a size and then `leading-none`: tailwind-merge drops a leading that precedes a font size. */ +const MONO_CAPS = "font-mono font-semibold uppercase tracking-(--tracking-label)"; + +const INK = "bg-(--solid-ink) text-(--solid-ink-foreground)"; +/** Ink laid over a preview is ringed with the slab fill (it follows the slab's steps), so it reads on a picture of any value. */ +const RING = SLAB_RING; + +function InkFace({ artifact, size }: { artifact: LabArtifact; size: FaceSize }) { + const Glyph = kindGlyph(artifact); + return ( + <> + {GLYPH[size] ? : null} + {blockLabel(artifact)} + + ); +} + +/** A picture filling its (relative, overflow-hidden) parent. A page starts at its top-left; a photograph is about its centre. */ +export function PlatePicture({ artifact, onError }: { artifact: LabArtifact; onError: () => void }) { + return ( + + ); +} + +const TAG: Record<"sm" | "md", string> = { sm: "px-1.5 pb-1.5 pt-1", md: "px-2 pb-2 pt-1.5" }; +const TAG_TEXT: Record<"sm" | "md", string> = { sm: "text-(length:--text-nano)", md: "text-(length:--text-micro)" }; + +/** An ink tab in the bottom-left corner of a preview. The parent must be `relative overflow-hidden`. */ +export function CornerTag({ size = "md", className, children }: { size?: "sm" | "md"; className?: string; children: ReactNode }) { + return ( + + {children} + + ); +} + +/** + * The kind's label on a corner tab; a video's carries a play mark. + * `compact`: in a mosaic cell of a stack face narrower than 16rem, where there + * is no room for letters, the tab shows the kind's glyph instead. + */ +export function KindTag({ artifact, size = "md", compact = false }: { artifact: LabArtifact; size?: "sm" | "md"; compact?: boolean }) { + const Glyph = kindGlyph(artifact); + return ( + + {artifact.kind === "video" ? : null} + {compact ? : null} + {blockLabel(artifact)} + + ); +} + +/** + * The kind block docked in the bottom-left corner of a sheet that has no + * content of its own to show. `compact` as for the tab: a glyph-only square + * where a mosaic cell has no room for the block. + */ +export function KindDock({ artifact, size = "plate", compact = false }: { artifact: LabArtifact; size?: "sm" | "plate"; compact?: boolean }) { + const Glyph = kindGlyph(artifact); + const dock = cn("absolute bottom-0 left-0 rounded-tr-md", INK, RING); + return ( + <> + {compact ? ( + + + + ) : null} + + + + + ); +} + +export function KindBlock({ + artifact, + size = "sm", + ink = false, + className, +}: { + artifact: LabArtifact; + size?: KindBlockSize; + /** Always the ink plate, even when the artifact has a picture (a head that sits under the picture itself). */ + ink?: boolean; + className?: string; +}) { + const [failed, setFailed] = useState(false); + const box = cn("relative shrink-0 overflow-hidden rounded-md", BOX[size], className); + + if (hasThumbnail(artifact) && !failed && !ink) { + return ( + + setFailed(true)} /> + {/* No kind tab on a picture this small: the meta line beside it says what it is. A video keeps its play mark. */} + {artifact.kind === "video" ? ( + + + + ) : null} + + ); + } + + return ( + + + + ); +} diff --git a/ui/src/artifact-directions/directions/d1-solid/MetaLine.tsx b/ui/src/artifact-directions/directions/d1-solid/MetaLine.tsx new file mode 100644 index 00000000000..6c7152a6473 --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/MetaLine.tsx @@ -0,0 +1,70 @@ +import { Fragment } from "react"; +import { cn } from "@/lib/utils"; +import type { LabArtifact } from "../../kit"; +import { metaParts } from "./kinds"; + +/** + * SIGNATURE 2 — the mono meta line: kind · size · rev · extent, in the + * monospace token, in that order on every surface. + */ + +/** The mono recipe for every machine value in the set: meta lines, filenames, stamps, counts, identifiers. */ +export const META_TEXT = "font-mono text-(length:--text-micro) leading-4"; + +/** A set-width separator: a mono " · " is three cells wide and leaves the line gappy. */ +export function MetaDot() { + return ·; +} + +/** A host and path may break after a slash; nothing else in a part may break. */ +function breakable(part: string) { + if (!part.includes("/")) return part; + const pieces = part.split("/"); + return pieces.map((piece, index) => ( + + {index > 0 ? : null} + {index < pieces.length - 1 ? `${piece}/` : piece} + + )); +} + +export function MetaLine({ + artifact, + max, + wrap = false, + className, +}: { + artifact: LabArtifact; + /** Keep only the first `max` parts when the surface is short of room. */ + max?: number; + /** + * Let the line wrap (a panel card, a picture slab). It then breaks only + * between parts — never inside "90.1 KB" — and after a slash in a path. + * Off, the surface truncates it. + */ + wrap?: boolean; + className?: string; +}) { + const parts = max ? metaParts(artifact).slice(0, max) : metaParts(artifact); + return ( + + {parts.map((part, index) => + wrap ? ( + // The separator travels with the part before it, so a wrapped line never starts with a dot. + + + {breakable(part)} + {index < parts.length - 1 ? : null} + + {index < parts.length - 1 ? : null} + + ) : ( + + {index > 0 ? : null} + {part} + + ), + )} + + ); +} diff --git a/ui/src/artifact-directions/directions/d1-solid/PanelBody.tsx b/ui/src/artifact-directions/directions/d1-solid/PanelBody.tsx new file mode 100644 index 00000000000..766fcc5bfcc --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/PanelBody.tsx @@ -0,0 +1,77 @@ +import type { ReactNode } from "react"; +import { cn } from "@/lib/utils"; +import type { LabArtifact } from "../../kit"; +import { Counts } from "./Counts"; +import { plural } from "./kinds"; +import { META_TEXT } from "./MetaLine"; +import { PanelCard } from "./PanelCard"; +import { panelSections, shortStamp, type PanelSection } from "./panel-model"; + +/** + * PANEL — the scroll body of the task's Artifacts tab (434px pane, 402px of + * content). + * + * 21 artifacts ● 9 to review ← the shelf, counted + * + * Task documents ← a section: who or what + * 7 documents + * [card] [card] … + * + * Brand and Presentation Designer + * 5 artifacts ● 5 to review Sep 28, 10:52 AM ← counts · run start (mono) + * [card] [card] … + * + * Sections and their order are the product's — one per producing run, task + * documents on their own, as listed (panel-model.ts). The mono count label is + * the thread attachments label again: the count in foreground, the breakdown + * after it, a state dot where a state is counted. + * + * One column in the docked pane. A maximized pane sets the cards in 22rem + * columns, filled row by row so the listed order still reads across. + */ + +function Section({ section, children }: { section: PanelSection; children: ReactNode }) { + const headingId = `solid-panel-section-${section.id}`; + return ( +
+
+

+ {section.title} +

+

+ + {section.startedLabel ? ( + + ) : null} +

+
+ {children} +
+ ); +} + +export function PanelBody({ artifacts }: { artifacts: LabArtifact[] }) { + if (artifacts.length === 0) { + return

No artifacts yet. Work products, documents, and agent-produced files will appear here.

; + } + return ( +
+

+ +

+ {panelSections(artifacts).map((section) => ( +
+
    + {section.items.map((artifact) => ( +
  • + +
  • + ))} +
+
+ ))} +
+ ); +} diff --git a/ui/src/artifact-directions/directions/d1-solid/PanelCard.tsx b/ui/src/artifact-directions/directions/d1-solid/PanelCard.tsx new file mode 100644 index 00000000000..0f29bb5855b --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/PanelCard.tsx @@ -0,0 +1,135 @@ +import { useState } from "react"; +import { MessageSquare } from "lucide-react"; +import { cn } from "@/lib/utils"; +import type { LabArtifact } from "../../kit"; +import { actionName, externalProps } from "./actions"; +import { ExcerptSheet, hasExcerpt } from "./ExcerptSheet"; +import { hasThumbnail, isWaiting, plural } from "./kinds"; +import { KindBlock } from "./KindBlock"; +import { META_TEXT, MetaDot, MetaLine } from "./MetaLine"; +import { panelActions } from "./panel-actions"; +import { stampUnder, type PanelSection } from "./panel-model"; +import { PanelLead } from "./PanelLead"; +import { PanelMenu } from "./PanelMenu"; +import { StateMarker } from "./StateMarker"; +import { BUTTON, BUTTON_HOVER, COUNT_BUTTON, FOCUS, primaryRest, SLAB } from "./slab"; + +/** + * PANEL — one artifact on the task's shelf: a thread row with its drawer open. + * + * [kind block 72×44] Title, wraps a picture leads its card instead: + * Summary, wraps ┌ picture 16:9, kind tab ┐ + * kind · size · rev · extent └────────────────────────┘ + * filename.ext / content type Title … + * ┌ sheet: the excerpt as text ───────────────────┐ (only for text kinds) + * └────────────────────────────────────────────────┘ + * ● Review 11:07 AM [💬 0] [⋯] [Read document ⌄] + * + * The head is the thread row at the same size. The foot is ONE row with the + * same three places on every card: the state (or nothing); who and when in + * mono (the author only where the section does not name them; the time alone + * where the section carries the day; it breaks between the two when short of + * room, never inside either); and at the far end the comments count (Markdown + * files), "More actions" (where there are any) and the primary — the thread's + * 136px block, in ink when the artifact waits for review. + * + * This is the reading surface, so the card is NOT a click target: text can be + * selected, tooltips show, and nothing downloads from a stray click. Only the + * controls act. + * + * Nothing in the head is clamped: titles, summaries, filenames and content + * types wrap. Under 20rem of card (a pane dragged narrow) the foot's two ends + * stack. + */ + +/** Kinds whose card is the product's file card, which carries the content type. */ +const FILE_KINDS = new Set(["pdf", "slides", "sheet", "archive", "code", "file"]); + +export function PanelCard({ artifact, section }: { artifact: LabArtifact; section: PanelSection }) { + const [pictureFailed, setPictureFailed] = useState(false); + const { primary, more } = panelActions(artifact); + const PrimaryIcon = primary.icon; + const waiting = isWaiting(artifact); + const leads = hasThumbnail(artifact) && !pictureFailed; + const showAuthor = artifact.author !== section.agent; + const mono = cn(META_TEXT, "wrap-anywhere text-(--solid-meta)"); + + const button = { + "aria-label": actionName(primary, artifact), + // First in the DOM so it is first in the tab order; last in the row. + className: cn(BUTTON, primaryRest(waiting), BUTTON_HOVER, FOCUS, "order-last min-w-34 cursor-pointer justify-between px-2.5"), + }; + const face = ( + <> + {primary.label} + + + ); + + return ( +
+ {leads ? setPictureFailed(true)} /> : null} + +
+ {leads ? null : } +
+

{artifact.title}

+ {artifact.summary ?

{artifact.summary}

: null} +

+ + {artifact.filename ? {artifact.filename} : null} + {FILE_KINDS.has(artifact.kind) && artifact.contentType ? {artifact.contentType} : null} + {hasThumbnail(artifact) && pictureFailed ? No preview available : null} +

+
+
+ + {/* What waits for review is given room to be read here; the rest shows enough to be told apart. */} + {hasExcerpt(artifact) ? : null} + +
+ {/* State, then who and when. The line breaks between the two when the row is short of room, never inside either. */} +
+ +

+ {showAuthor ? ( + + {artifact.author} + + + ) : ( + {artifact.author}, + )} + + +

+
+
+ {primary.href ? ( + + {face} + + ) : ( + + )} + {artifact.commentCount != null ? ( + + ) : null} + +
+
+
+ ); +} diff --git a/ui/src/artifact-directions/directions/d1-solid/PanelLead.tsx b/ui/src/artifact-directions/directions/d1-solid/PanelLead.tsx new file mode 100644 index 00000000000..64efb44de94 --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/PanelLead.tsx @@ -0,0 +1,59 @@ +import { useRef, useState } from "react"; +import type { LabArtifact } from "../../kit"; +import { KindTag, PlatePicture } from "./KindBlock"; + +/** + * PANEL — what leads a card whose artifact is a picture: the picture itself, + * at 16:9 across the card, with the ink kind tab in its bottom-left corner — + * the same rule as the thread and the gallery (the picture is the plate and + * carries the tab), so nothing is represented twice. + * + * A video plays in place: its poster is the play button, and the product's + * controls take over once it has started. A picture that fails to load reports + * it, and the card falls back to the ink kind block. + */ + +const LEAD = "relative aspect-(--solid-lead-ratio) w-full overflow-hidden rounded-md bg-(--solid-sheet)"; + +function VideoLead({ artifact }: { artifact: LabArtifact }) { + const video = useRef(null); + const [started, setStarted] = useState(false); + return ( +
+
+ ); +} + +export function PanelLead({ artifact, onPictureError }: { artifact: LabArtifact; onPictureError: () => void }) { + if (artifact.kind === "video" && artifact.videoUrl) return ; + return ( +
+ + +
+ ); +} diff --git a/ui/src/artifact-directions/directions/d1-solid/PanelMenu.tsx b/ui/src/artifact-directions/directions/d1-solid/PanelMenu.tsx new file mode 100644 index 00000000000..6fe8df278b2 --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/PanelMenu.tsx @@ -0,0 +1,47 @@ +import { Ellipsis } from "lucide-react"; +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; +import type { LabArtifact } from "../../kit"; +import { externalProps, type SolidAction } from "./actions"; +import { ICON_BUTTON } from "./slab"; + +/** + * PANEL — a card's "More actions": every way out that is not the primary, in + * one named control, so the foot is the same single row on every card. The + * list is the product's own menu (it opens in a portal, outside the kit's + * wrapper, in the app's popover chrome); each item is the action by name, not + * an unlabelled icon. + */ +export function PanelMenu({ actions, artifact }: { actions: SolidAction[]; artifact: LabArtifact }) { + if (actions.length === 0) return null; + return ( + + + + + + {actions.map((action) => { + const Icon = action.icon; + const face = ( + <> + + {action.label} + + ); + return ( + + {action.href ? ( + + {face} + + ) : ( + + )} + + ); + })} + + + ); +} diff --git a/ui/src/artifact-directions/directions/d1-solid/StackTile.tsx b/ui/src/artifact-directions/directions/d1-solid/StackTile.tsx new file mode 100644 index 00000000000..f943cf35fe6 --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/StackTile.tsx @@ -0,0 +1,129 @@ +import { ArrowRight } from "lucide-react"; +import { cn } from "@/lib/utils"; +import type { LabGroup } from "../../kit"; +import { Counts } from "./Counts"; +import { kindTally, pileDepth, stackPreviews } from "./gallery-model"; +import { GalleryPreview } from "./GalleryPreview"; +import { plural } from "./kinds"; +import { META_TEXT, MetaDot } from "./MetaLine"; +import { CORNER_NOTCH } from "./StateMarker"; +import { BUTTON, BUTTON_PRIMARY, COVER, SLAB, SLAB_HOVER } from "./slab"; + +/** + * GALLERY — one task's stack. + * + * ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ pile: the slabs behind the face, each narrower + * ▁ MD 7 · PDF 4 · PPTX 4 · PNG 3 ▁▁▁ and one step nearer the canvas; the nearer one + * ┌──────────────────────┬─────────┐ says what is in the pile, on its edge + * │ │ 12 slides│ + * │ picture ├─────────┤ mosaic: the newest three, each as its own preview; + * │ [PNG] │ [ZIP] +18│ the one with most to show is large + * └──────────────────────┴─────────┘ + * ACM-3 Create visual directions and first acmehats brand deck + * 21 artifacts ● 9 to review [→] + * Updated Sep 28, 2026 + * + * The pile is made of flat fills only. Its depth says how much is in it: no + * slab behind one artifact, one behind two or three, two behind more. A stack + * of one is therefore an artifact tile with a task's name on it — one preview + * at full size, a flat top — and cannot be mistaken for a pile. + * + * The whole object — face and pile — is the [→] action's target ("Open + * stack"). Hover or keyboard focus steps the face, turns the action to ink, + * and the pile rises 6px a slab (not under reduced motion). + * + * Pile geometry: 28px is kept above every face; the nearer slab shows 16px of + * itself 12px in from each side, the farther one 12px more, 32px in. + */ + +const LAYER = + "pointer-events-none absolute h-10 overflow-hidden rounded-md transition-transform duration-(--motion-duration-fast) ease-(--motion-ease-standard) motion-reduce:transition-none"; +const RISE_1 = "motion-safe:group-hover/pile:-translate-y-1.5 motion-safe:group-has-[[data-primary]:focus-visible]/pile:-translate-y-1.5"; +const RISE_2 = "motion-safe:group-hover/pile:-translate-y-3 motion-safe:group-has-[[data-primary]:focus-visible]/pile:-translate-y-3"; + +function Pile({ group, depth }: { group: LabGroup; depth: 0 | 1 | 2 }) { + if (depth === 0) return null; + return ( + <> + {depth > 1 ? : null} + + {/* The slab's visible edge is 16px: one line of mono caps, cut clean where the pile runs out of room. */} + + {kindTally(group.artifacts).map((entry, index) => ( + + {index > 0 ? : null} + {entry} + + ))} + + + + ); +} + +/** The corner bite of the mosaic: how many artifacts it leaves out, in mono. */ +function MoreTab({ count }: { count: number }) { + return ( + + +{count} + + ); +} + +function Mosaic({ group }: { group: LabGroup }) { + const [lead, ...rest] = stackPreviews(group); + if (!lead) return null; + if (rest.length === 0) return ; + const more = group.count - 1 - rest.length; + return ( +
+
+ +
+ {rest.map((artifact) => ( +
+ +
+ ))} + {more > 0 ? : null} +
+ ); +} + +export function StackTile({ group }: { group: LabGroup }) { + const depth = pileDepth(group.count); + const count = plural(group.count, "artifact"); + return ( + // 28px above every face, so faces line up along a row whether or not a pile sits behind them. +
+ +
+ +
+ {/* h2: on the stacks page a stack sits directly under the page title. */} +

+ {group.taskIdentifier} + {group.title} +

+
+ {/* Always two lines — what is in it, then when — so the feet of a row line up; a long count wraps inside its own line. */} +

+ + Updated {group.updatedLabel} +

+ + + +
+
+
+
+ ); +} diff --git a/ui/src/artifact-directions/directions/d1-solid/StateMarker.tsx b/ui/src/artifact-directions/directions/d1-solid/StateMarker.tsx new file mode 100644 index 00000000000..51927c7aa1f --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/StateMarker.tsx @@ -0,0 +1,75 @@ +import { cn } from "@/lib/utils"; +import type { LabArtifact } from "../../kit"; +import { MOTION, SLAB_FILL, SLAB_JOINTS } from "./slab"; + +/** + * The state marker: the only chroma in the direction, and only a 6px dot of + * it, followed by the production label in foreground text. + * + * On a slab it has no fill of its own — dot and label sit straight on the + * slab, so it can never be taken for the button next to it. On a gallery + * preview it is written in a BITE the slab takes out of the preview's + * bottom-right corner, the mirror of the ink kind tab in the bottom-left: what + * it is on the left, what it needs on the right. + * + * Nothing renders for an artifact with no pending state, as in the product. + */ + +/** The one state vocabulary: label and dot fill per state. Counts.tsx counts in the same dots. */ +export const STATE = { + needs_review: { label: "Review", dot: "bg-(--solid-state-review)" }, + changes_requested: { label: "Changes requested", dot: "bg-(--solid-state-changes)" }, +} as const; + +export function StateDot({ state, className }: { state: keyof typeof STATE; className?: string }) { + return ; +} + +/** + * A bite out of a preview's bottom-right corner, in the slab's own fill (it + * follows the slab's hover). The state marker uses it in the gallery; so does + * a stack mosaic's "+18". The parent must be `relative overflow-hidden`. + * + * It is cut, not laid on: where the bite meets the preview's right and bottom + * edges, two 6px joints (its pseudo-elements) round the preview's new corners + * to the set's one radius — the slab painted into the corner outside a + * quarter circle (--solid-joint). + */ +export const CORNER_NOTCH = cn( + "absolute right-0 bottom-0 rounded-tl-md", + SLAB_FILL, + MOTION, + // The joint above the bite, on the preview's right edge; and the one beside it, on the bottom edge. + "before:absolute before:right-0 before:bottom-full before:size-1.5 before:rounded-br-md before:shadow-(--solid-joint)", + "after:absolute after:right-full after:bottom-0 after:size-1.5 after:rounded-br-md after:shadow-(--solid-joint)", + SLAB_JOINTS, +); + +export function StateMarker({ + review, + on = "slab", + collapse = false, + className, +}: { + review: LabArtifact["review"]; + /** `slab`: dot and label on the slab. `corner`: in a bite out of a preview's bottom-right corner. */ + on?: "slab" | "corner"; + /** Inside an `@container` narrower than 36rem, show only the dot (the label stays for screen readers). */ + collapse?: boolean; + className?: string; +}) { + if (!review) return null; + return ( + + + {STATE[review].label} + + ); +} diff --git a/ui/src/artifact-directions/directions/d1-solid/ThreadAttachments.tsx b/ui/src/artifact-directions/directions/d1-solid/ThreadAttachments.tsx new file mode 100644 index 00000000000..3dc884b4223 --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/ThreadAttachments.tsx @@ -0,0 +1,102 @@ +import { cn } from "@/lib/utils"; +import type { LabArtifact } from "../../kit"; +import { Counts } from "./Counts"; +import { fileName, hasThumbnail, plural } from "./kinds"; +import { KindBlock } from "./KindBlock"; +import { META_TEXT, MetaLine } from "./MetaLine"; +import { STATE, StateMarker } from "./StateMarker"; +import { FOCUS, SLAB, SLAB_HOVER } from "./slab"; + +/** + * THREAD — the attachments block under an agent message. + * + * Today this is two unrelated blocks (an "Images · 1" thumbnail grid and a + * scrolling "Files · 4" strip of square-cornered tiles). Here it is one grid + * under one mono label that keeps both counts and says what waits: + * + * 5 attachments · 1 image · 4 files ● 5 to review + * ┌ picture 128×96 ┐ contact-sheet.png [PPTX] name.pptx [PDF] name.pdf + * │ │ Image · 422.9 KB Slides · 90.1 KB ● Review … + * │ │ [MD] deck-guide.md [ZIP] editing-source.zip + * └────────────────┘ ● Review Markdown · 5.9 KB ● Review … + * + * A file is a 52px slab: kind block, filename, mono meta, state. Anything with + * a picture takes two rows (112px) so the picture is shown at a size it can be + * read at, with nothing laid over it. The grid wraps, so no file is ever + * scrolled out of sight. + */ + +function AttachmentSlab({ artifact }: { artifact: LabArtifact }) { + const name = fileName(artifact); + const picture = hasThumbnail(artifact); + const opensGallery = artifact.kind === "image" || artifact.kind === "video"; + const shared = { + // The name replaces the slab's content for assistive tech, so the state is part of it. + "aria-label": [`Open ${name}`, artifact.review ? STATE[artifact.review].label : null].filter(Boolean).join(", "), + title: [name, artifact.title === name ? null : artifact.title].filter(Boolean).join(" — "), + className: cn( + "group/slab flex size-full min-w-0 cursor-pointer gap-2.5 p-2 pr-3 text-left focus-visible:bg-(--solid-slab-hover)", + picture ? "items-stretch" : "items-center", + SLAB, + SLAB_HOVER, + FOCUS, + ), + }; + const face = picture ? ( + <> + + + {name} + + {/* The one place a state label may take two lines: the column beside a picture is narrow. */} + + + + ) : ( + <> + + + {name} + + {/* A long state takes the size's place; the kind always stays. */} + + + + + + ); + return opensGallery ? ( + + ) : ( + + {face} + + ); +} + +export function ThreadAttachments({ artifacts }: { artifacts: LabArtifact[] }) { + if (artifacts.length === 0) return null; + const images = artifacts.filter((a) => a.kind === "image"); + const files = artifacts.filter((a) => a.kind !== "image"); + const counts = [images.length > 0 ? plural(images.length, "image") : null, files.length > 0 ? plural(files.length, "file") : null].filter( + (count): count is string => Boolean(count), + ); + // Whatever has a picture goes first, so the two-row slabs start the grid and the files pack beside them. + const ordered = [...artifacts.filter(hasThumbnail), ...artifacts.filter((a) => !hasThumbnail(a))]; + return ( +
+

+ +

+
    + {ordered.map((artifact) => ( +
  • + +
  • + ))} +
+
+ ); +} diff --git a/ui/src/artifact-directions/directions/d1-solid/ThreadWorkProduct.tsx b/ui/src/artifact-directions/directions/d1-solid/ThreadWorkProduct.tsx new file mode 100644 index 00000000000..1b306462216 --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/ThreadWorkProduct.tsx @@ -0,0 +1,100 @@ +import { cn } from "@/lib/utils"; +import type { LabArtifact } from "../../kit"; +import { actionName, downloadAction, externalProps, threadAction } from "./actions"; +import { extent, isWaiting, metaParts } from "./kinds"; +import { KindBlock } from "./KindBlock"; +import { MetaLine } from "./MetaLine"; +import { StateMarker } from "./StateMarker"; +import { BUTTON, BUTTON_PRIMARY, COVER, ICON_BUTTON, primaryRest, SLAB, SLAB_HOVER } from "./slab"; + +/** + * THREAD — one work product between turns: a 60px slab. + * + * [kind block 72×44] Title ● Review [Open gallery ⤢] + * kind · size · rev · extent — summary + * + * One grammar for all ten kinds (today: three). The whole slab is the primary + * action's target; hovering it steps the fill and turns the action to ink. An + * artifact that waits for review has its action in ink already. + * + * The row gives way in order of what matters least (by its own width, inside its inset): + * from 48rem one-line title, summary on the meta line after a dash + * under 48rem two-line title, summary on its own line under the meta, two lines at most + * (a row with a summary grows to 76px; nothing is left to a tooltip) + * from 42rem every action is the same 136px block, as every row starts with the same 72px one + * under 36rem the state is its dot alone + * under 28rem the action is its icon alone + * and the text column never goes under 8rem, so the title is the last thing to yield. + */ +export function ThreadWorkProduct({ artifact }: { artifact: LabArtifact }) { + const action = threadAction(artifact); + const Icon = action.icon; + // A text-like file opens in the side panel, so Download becomes its own small target. + const download = action.id === "tab" ? downloadAction(artifact) : null; + const DownloadIcon = download?.icon; + // "Slides · 90.1 KB · 12 slides — 12 editable slides…" stutters: the summary keeps the figure, the meta lets it go. + const figure = extent(artifact)?.split(" ")[0]; + const echoed = Boolean(figure && artifact.summary?.startsWith(`${figure} `)); + + const primary = { + "data-primary": true, + "aria-label": actionName(action, artifact), + title: actionName(action, artifact), + // First in the DOM so it is first in the tab order; last in the row. + className: cn( + BUTTON, + primaryRest(isWaiting(artifact)), + BUTTON_PRIMARY, + COVER, + "order-last cursor-pointer after:cursor-pointer @2xl:min-w-34 @2xl:justify-between @2xl:px-2.5", + ), + }; + const face = ( + <> + {action.label} + + + ); + + return ( +
+ +
+

{artifact.title}

+

+ + {artifact.summary ? ( + + + — + + {artifact.summary} + + ) : null} +

+ {artifact.summary ?

{artifact.summary}

: null} +
+
+ {action.href ? ( + + {face} + + ) : ( + + )} + + {download && DownloadIcon ? ( + + + + ) : null} +
+
+ ); +} diff --git a/ui/src/artifact-directions/directions/d1-solid/actions.ts b/ui/src/artifact-directions/directions/d1-solid/actions.ts new file mode 100644 index 00000000000..aa3b981c3e2 --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/actions.ts @@ -0,0 +1,42 @@ +import { Download, ExternalLink, Maximize2, PanelRightOpen, type LucideIcon } from "lucide-react"; +import type { LabArtifact } from "../../kit"; +import { isTaskDocument, isTextLike, slug } from "./kinds"; + +/** + * The one thing you do with an artifact from the thread, in production + * wording. The lab's targets are inert hashes; `gallery` is a button because + * the product opens a modal from it. + * + * One glyph per destination on every surface, so no two controls on a unit + * look alike: ExternalLink leaves for a new browser tab, PanelRightOpen opens + * the side panel, Maximize2 opens the gallery, Download downloads, and + * ArrowRight (the gallery only) goes to the owning task. + */ + +export interface SolidAction { + id: "document" | "gallery" | "preview" | "tab" | "download"; + label: string; + icon: LucideIcon; + /** Absent for `gallery`, which is a button. */ + href?: string; + external?: boolean; +} + +export function downloadAction(a: LabArtifact): SolidAction { + return { id: "download", label: "Download", icon: Download, href: `#download-${a.id}` }; +} + +export function threadAction(a: LabArtifact): SolidAction { + if (isTaskDocument(a)) return { id: "document", label: "Open document", icon: PanelRightOpen, href: `#document-${slug(a)}` }; + if (a.kind === "link") return { id: "preview", label: "Open preview", icon: ExternalLink, href: a.url ?? "#", external: true }; + if (a.kind === "image" || a.kind === "video") return { id: "gallery", label: "Open gallery", icon: Maximize2 }; + // Text-like files open in the side panel; Download stays reachable as its own target. + if (isTextLike(a)) return { id: "tab", label: "Open in tab", icon: PanelRightOpen, href: `#attachment-${a.id}` }; + return downloadAction(a); +} + +/** The accessible name of an action on a given artifact: "Download: Interface Craft revision — PDF". */ +export const actionName = (action: SolidAction, a: LabArtifact) => `${action.label}: ${a.title}`; + +/** Link attributes for an action that leaves for a new browser tab. */ +export const externalProps = (action: SolidAction) => (action.external ? ({ target: "_blank", rel: "noreferrer" } as const) : null); diff --git a/ui/src/artifact-directions/directions/d1-solid/gallery-model.ts b/ui/src/artifact-directions/directions/d1-solid/gallery-model.ts new file mode 100644 index 00000000000..313eaaca50b --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/gallery-model.ts @@ -0,0 +1,107 @@ +import { ArrowRight, Download, ExternalLink } from "lucide-react"; +import type { LabArtifact, LabGroup } from "../../kit"; +import type { SolidAction } from "./actions"; +import { blockLabel, hasThumbnail, isTaskDocument, slug } from "./kinds"; + +/** + * How the gallery reads its data: what a tile's preview is, which previews a + * stack shows, how an opened stack is sectioned, and what a tile offers. + */ + +/** + * The three previews of the direction, by what the artifact has to show: + * picture image, video poster, link preview → the picture, kind tab in the corner + * text task document, Markdown, code, sheet → its own words on a sheet, kind tab in the corner + * facts PDF, slides, archive, any other file → a sheet that says what is known of it, the kind block docked in the corner + */ +export type PreviewKind = "picture" | "text" | "facts"; + +export function previewKind(a: LabArtifact): PreviewKind { + if (hasThumbnail(a)) return "picture"; + return a.excerpt ? "text" : "facts"; +} + +const VISUAL: Record = { picture: 0, text: 1, facts: 2 }; + +/** + * A stack's mosaic: its three newest artifacts (what the API already sends as + * `previewArtifacts`), with the one that has most to show in the large cell — + * a picture before text before bare facts. + */ +export function stackPreviews(group: LabGroup): LabArtifact[] { + return group.artifacts + .slice(0, 3) + .map((artifact, index) => ({ artifact, index })) + .sort((a, b) => VISUAL[previewKind(a.artifact)] - VISUAL[previewKind(b.artifact)] || a.index - b.index) + .map((entry) => entry.artifact); +} + +/** How many slabs sit behind a stack's face: none for one artifact, one for two or three, two for a real pile. */ +export const pileDepth = (count: number): 0 | 1 | 2 => (count <= 1 ? 0 : count <= 3 ? 1 : 2); + +/** + * What is in the pile, for the edge of the slab behind a stack's face: kinds + * by their block label, most numerous first ("PDF 4 · PPTX 4 · PNG 3"). + * Task documents are counted together as DOCS — their block label is a revision. + */ +export function kindTally(artifacts: LabArtifact[]): string[] { + const counts = new Map(); + for (const artifact of artifacts) { + const label = isTaskDocument(artifact) ? "DOCS" : blockLabel(artifact); + counts.set(label, (counts.get(label) ?? 0) + 1); + } + return [...counts.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([label, count]) => `${label} ${count}`); +} + +export interface DaySection { + /** "2026-09-28" */ + id: string; + /** "Monday, September 28" */ + label: string; + items: LabArtifact[]; +} + +/** An opened stack by the day of the last edit, newest day first; inside a day the page's own order (newest first) is kept. */ +export function daySections(artifacts: LabArtifact[]): DaySection[] { + const byDay = new Map(); + for (const artifact of artifacts) { + const id = artifact.updatedAt.slice(0, 10); + const section = byDay.get(id) ?? { id, label: artifact.dayLabel, items: [] }; + section.items.push(artifact); + byDay.set(id, section); + } + return [...byDay.values()].sort((a, b) => (a.id < b.id ? 1 : a.id > b.id ? -1 : 0)); +} + +export interface GalleryActions { + /** The tile itself: the owning task, at this artifact. */ + primary: SolidAction; + /** The product's two ways straight to the file, where it has them. */ + secondary: SolidAction[]; +} + +/** + * What a gallery tile offers, in the product's wording. The tile is a link to + * the owning task at the artifact's anchor; "Open file in new tab" and + * "Download file" exist for anything file-backed (a task document has no file, + * a link only opens). + */ +export function galleryActions(a: LabArtifact): GalleryActions { + const primary: SolidAction = { + id: "document", + label: `Open in ${a.taskIdentifier}`, + icon: ArrowRight, + href: isTaskDocument(a) ? `#document-${slug(a)}` : `#work-product-${a.id}`, + }; + if (isTaskDocument(a)) return { primary, secondary: [] }; + if (a.kind === "link") { + return { primary, secondary: [{ id: "preview", label: "Open link in new tab", icon: ExternalLink, href: a.url ?? "#", external: true }] }; + } + return { + primary, + secondary: [ + { id: "preview", label: "Open file in new tab", icon: ExternalLink, href: `#file-${a.id}`, external: true }, + { id: "download", label: "Download file", icon: Download, href: `#download-${a.id}` }, + ], + }; +} diff --git a/ui/src/artifact-directions/directions/d1-solid/index.tsx b/ui/src/artifact-directions/directions/d1-solid/index.tsx new file mode 100644 index 00000000000..26e4e76e9e2 --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/index.tsx @@ -0,0 +1,142 @@ +import type { DirectionKit } from "../../kit"; +import { GalleryStack, GalleryStacks } from "./Gallery"; +import { PanelBody } from "./PanelBody"; +import { ThreadAttachments } from "./ThreadAttachments"; +import { ThreadWorkProduct } from "./ThreadWorkProduct"; +import "./tokens.css"; + +/** + * DIRECTION 1 — SOLID. Flat, filled, mostly monochrome. + * + * The fix is fill, not line. Today an artifact is a 1px outline around the + * canvas colour; here it is a SLAB — a filled block a clear value step off the + * canvas, with no stroke, no shadow and no gradient — and what fills an + * artifact's preview is its CONTENT, never its file extension. + * + * FILLS (tokens.css; OKLCH lightness, light / dark; contrast against what it sits on) + * canvas 1.000 / 0.205 --background, untouched + * slab 0.900 / 0.325 the artifact (1.35 / 1.44 on the canvas; today's outline colour is 1.26 / 1.32) + * slab-hover 0.865 / 0.365 a slab that is a target, hovered or focused; slab-press 0.830 / 0.400 while pressed + * button 0.800 / 0.400 a resting button on a slab (1.39 / 1.35 on the slab) + * sheet 1.000 / 0.430 the artifact's own content — an excerpt, a file's facts: the lightest fill short of ink + * in both themes (paper on a mat); body text in --solid-sheet-text, lead lines in --foreground + * ink 0.205 / 0.800 the one inverted fill: the kind block and tab, and a primary action that is hovered or due + * pile-1/-2 0.920 / 0.290 and 0.945 / 0.265 the slabs behind a gallery stack: each one step NEARER the canvas, both themes + * Text on a slab: --foreground, and --solid-meta (0.44 / 0.78) for secondary; every pair ≥ 4.5:1 at rest and on hover. + * + * SIGNATURES — the same idea on thread, panel and gallery + * 1. KIND BLOCK (KindBlock.tsx, kinds.ts) — an INK plate: mono-caps label + * bottom-left, the kind's glyph top-right (20px in a thread row, so kinds + * differ by shape as well as letters). A file is labelled by its extension + * (PDF, PPTX, MD, PNG, ZIP, XLSX, WEBM, HTML, OTF; URL for a link); a task + * document, which has none, by its revision (REV 3) under a notebook glyph. + * KindBlock free-standing: xs 56×36 attachments (label only) · sm 72×44 + * thread row and panel card · lg 128×96 a picture attachment. + * An artifact with a picture shows the picture in the plate, + * with nothing over it (a video keeps a play mark). + * KindTag the label on an ink tab in the bottom-left corner of a + * preview that has content of its own (a picture, a sheet). + * KindDock the block docked in that corner of a preview that has none + * (131×80 on a tile, 72×44 in a mosaic cell): the tab grown + * to a block. Ink is always the mark in the bottom-left + * corner — never the whole plate. + * 2. MONO META LINE (MetaLine.tsx, kinds.ts) — kind · size · rev · extent in + * font-mono 11px, never reordered: "PDF · 57.7 KB · 12 pages", + * "Document · rev 3", "Video · 3.4 MB · 0:06". Production wording; a + * surface may take a prefix (`max`), never reorder. Every other machine + * value (filenames, stamps, counts, content types) is the same META_TEXT. + * 3. ONE RADIUS, ONE RHYTHM (slab.ts) — rounded-md on every slab, plate, + * sheet, button, tab and bite; 8px inset around the kind block in the + * thread, 12px in the panel and the gallery; 12px from block to text; 8px + * between slabs, 24px gutters in the gallery. + * 4. MONO COUNT LABEL (Counts.tsx) — heads the thread's attachments, the + * panel's shelf and sections, and the gallery's grids, days and stacks: + * "21 artifacts ● 9 to review". Items are unbreakable; the label wraps + * between them. + * + * STATE (StateMarker.tsx) — the only chroma, and only a 6px dot of it: the + * product's status hue + the production label ("Review", "Changes requested") + * in foreground text, straight on the slab with no fill of its own, so it is + * never taken for a button. What makes "this needs you" unmistakable is the + * action beside it: the primary of an artifact that waits for review is INK AT + * REST (thread and panel). On a gallery preview the state is written in a BITE + * the slab takes out of the bottom-right corner (inverse radii at its joints), + * the mirror of the ink kind tab bottom-left; a mosaic's "+18" is the same bite. + * + * INTERACTION (slab.ts) — in the thread and the gallery the whole slab is its + * primary action's target (COVER). Hover or keyboard focus steps the slab and + * turns that action to ink; pressing steps it once more. A secondary control + * is an icon-only ghost that takes the ink itself and hands the primary back + * to the button fill while hovered. The panel card is a reading surface and is + * NOT a target: only its controls act. The primary is first in the tab order + * everywhere; icon-only controls carry a tooltip naming the action. One glyph + * per destination: a box-with-arrow leaves for a new tab, a tray downloads, a + * side-panel glyph opens the pane, the plain arrow goes to the owning task. + * focus-visible is a 2px ink outline 2px off the slab or the control. Colour + * transitions only (and the pile's rise); none under reduced motion. + * + * THREAD — compact and calm (ThreadWorkProduct, ThreadAttachments) + * Row 60px: [block 72×44] title / meta — summary … ● state [action 136px]. + * Gives way by priority, by its own width: from 48rem a one-line title with + * the summary on the meta line; under that a two-line title and the summary + * on its own line (76px); under 36rem the state is its dot; under 28rem the + * action is its icon; the text column never goes under 8rem. + * Attachments: one count label ("5 attachments · 1 image · 4 files ● 5 to + * review") over a wrapping grid: a file is a 52px slab (block, filename, meta, + * state); anything with a picture takes two rows, the picture at 128×96. + * + * PANEL — a thread row with its drawer open (PanelBody, PanelCard, PanelLead, + * PanelMenu, panel-model.ts, panel-actions.ts) + * Shelf one count line, then the product's sections in the product's + * order: one per producing run (name, count line, run start), task + * documents on their own. Nothing is reordered. + * Card slab, 12px inset. A picture or a video LEADS its card at 16:9 + * with the kind tab on it (a video plays in place); every other + * kind opens with [block 72×44] beside the text. + * head title, summary, meta line, filename, and the content type on + * file cards — all wrap, nothing is clamped. + * sheet text kinds only: the excerpt on a sheet, opening line and + * headings strong; six lines when it waits for review, three otherwise. + * foot one row, the same three places on every card: ● state · who and + * when (the time alone under a section dated the same day) · and + * at the far end [comments] [⋯ More actions] [primary 136px]. All + * secondaries are in the one menu, by name. + * Widths under 20rem of card the foot's two ends stack; a maximized pane + * sets the cards in 22rem columns, row by row. + * + * GALLERY — browsing: preview first (Gallery, StackTile, GalleryTile, + * GalleryPreview, ExcerptSheet, gallery-model.ts) + * Grid the product's: 1 / 2 / 3 columns at <768 / 768 / 1280px, 24px + * gutters, under one count line. An opened stack is sectioned by + * day of last edit: the day name is the page's one large line + * (text-lg semibold), its count line beneath. `?days=off` lists it + * as the product does today, in one flat grid. + * Preview one 18:11 plate per artifact, filled by what the artifact is: + * picture image, video poster, link the picture + ink kind tab + * text document, Markdown, code, its own words on a sheet, as many whole + * sheet lines as the plate holds + ink kind tab + * facts PDF, slides, archive, file a sheet: how much of it there is at display + * size ("12 slides"), its filename, and the + * kind block docked bottom-left + * Tile slab: preview / title across the full width (2 lines) / meta + * line and stamp · author, with [⧉] [↓] [→] at the far end of the + * foot. The tile is the [→] action's target (the owning task). + * Stack a PILE of flat fills behind the face: 0 / 1 / 2 slabs for 1 / + * 2–3 / more artifacts; the nearer one says what is in the pile on + * its edge ("DOCS 7 · PDF 3 · PPTX 3"); they rise 6px a slab on + * hover. On the face a MOSAIC of the three newest, each as its own + * preview, the one with most to show large, "+18" bitten into the + * corner. Then ACM-3 + title, and a two-line foot (counts, then + * Updated) with [→]. Face and pile are one target. A stack of one + * has no pile and one full preview. + */ +export const kit: DirectionKit = { + id: "d1-solid", + name: "1.Solid", + tagline: "Flat, filled, mostly monochrome", + ThreadWorkProduct, + ThreadAttachments, + PanelBody, + GalleryStacks, + GalleryStack, +}; diff --git a/ui/src/artifact-directions/directions/d1-solid/kinds.ts b/ui/src/artifact-directions/directions/d1-solid/kinds.ts new file mode 100644 index 00000000000..faae6384955 --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/kinds.ts @@ -0,0 +1,112 @@ +import { Archive, CodeXml, File, FilePenLine, FileText, Film, Globe, Image, NotebookText, Presentation, Table2, type LucideIcon } from "lucide-react"; +import { fileKindForName } from "@/components/task-chat/task-chat-attachments"; +import type { LabArtifact, LabKind } from "../../kit"; + +/** + * The Solid vocabulary: for each of the ten kinds, the glyph that goes in the + * kind block, the mono-caps label under it, and the wording of the meta line. + * Every surface reads kinds from here so a PDF is the same PDF in the thread, + * the panel and the gallery. + * + * Glyphs are chosen to differ in shape at 20px, not only in their letters: a + * notebook (task document — kept and revised in the task), a page with a pen + * (Markdown — a source you edit), a lined page (PDF — the finished page), an + * easel, a grid, a box, brackets, a blank page, a globe, a framed picture, a + * film strip. + */ + +interface KindSpec { + glyph: LucideIcon; + /** Kind-block label when the artifact has no extension to show. */ + block: string; + /** Meta-line label when the product's extension mapper only says "File". */ + meta: string; +} + +export const KINDS: Record = { + document: { glyph: FilePenLine, block: "MD", meta: "Markdown" }, + image: { glyph: Image, block: "IMG", meta: "Image" }, + video: { glyph: Film, block: "VID", meta: "Video" }, + pdf: { glyph: FileText, block: "PDF", meta: "PDF" }, + slides: { glyph: Presentation, block: "PPT", meta: "Slides" }, + sheet: { glyph: Table2, block: "XLS", meta: "Sheet" }, + archive: { glyph: Archive, block: "ZIP", meta: "Archive" }, + code: { glyph: CodeXml, block: "CODE", meta: "Code" }, + file: { glyph: File, block: "FILE", meta: "File" }, + link: { glyph: Globe, block: "URL", meta: "Link" }, +}; + +/** A task document ("Document · rev N"), as opposed to a Markdown file an agent attached. */ +export const isTaskDocument = (a: LabArtifact) => a.kind === "document" && a.revision != null; + +/** A task document is a notebook: it is kept and revised in the task, not a file on disk. */ +export const kindGlyph = (a: LabArtifact): LucideIcon => (isTaskDocument(a) ? NotebookText : KINDS[a.kind].glyph); + +/** + * The mono-caps label in the kind block. A file is labelled by its extension + * (PDF, PPTX, MD, PNG, ZIP, CSV, WEBM); a task document has none, and is + * labelled by what tells one from the next — its revision (REV 3). + */ +export function blockLabel(a: LabArtifact): string { + if (a.kind === "link") return KINDS.link.block; + if (isTaskDocument(a)) return `REV ${a.revision}`; + const ext = a.ext?.replace(/[^a-z0-9]/gi, ""); + return ext ? ext.slice(0, 4).toUpperCase() : KINDS[a.kind].block; +} + +/** The first word of the meta line, in production wording where production has one. */ +export function metaLabel(a: LabArtifact): string { + if (isTaskDocument(a)) return "Document"; + if (a.kind === "link") return KINDS.link.meta; + if (a.kind === "image" || a.kind === "video") return KINDS[a.kind].meta; + const shipped = a.filename ? fileKindForName(a.filename).label : "File"; + return shipped === "File" ? KINDS[a.kind].meta : shipped; +} + +export const plural = (n: number, one: string) => `${n} ${one}${n === 1 ? "" : "s"}`; + +/** The key a document or file is addressed by in the lab's inert hashes. */ +export const slug = (a: LabArtifact) => (a.filename ?? a.id).replace(/\.md$/, ""); + +function host(url: string | undefined): string | null { + if (!url) return null; + try { + const parsed = new URL(url); + return `${parsed.host}${parsed.pathname === "/" ? "" : parsed.pathname}`; + } catch { + return url; + } +} + +/** How much of it there is: pages, slides, a duration, or where a link points. */ +export function extent(a: LabArtifact): string | null { + if (a.kind === "video") return a.durationLabel ?? null; + if (a.kind === "link") return host(a.url); + if (!a.pageCount) return null; + return plural(a.pageCount, a.kind === "slides" ? "slide" : "page"); +} + +/** + * The meta line's parts, in one grammar everywhere: kind · size · rev · extent + * (pages, slides, duration or host). A surface may take a prefix of this list + * when it is short of room, never reorder it. + */ +export function metaParts(a: LabArtifact): string[] { + return [metaLabel(a), a.sizeLabel, a.revision != null ? `rev ${a.revision}` : null, extent(a)].filter((part): part is string => Boolean(part)); +} + +/** What the product can open in the side panel instead of downloading (`isTextAttachment`). */ +export function isTextLike(a: LabArtifact): boolean { + if (a.kind === "link" || isTaskDocument(a)) return false; + const type = a.contentType ?? ""; + return type.startsWith("text/") || type === "application/json" || /\.(md|txt|log|csv|json|ya?ml|html|css)$/i.test(a.filename ?? ""); +} + +/** Whether the artifact has a picture to put where the kind block would go. */ +export const hasThumbnail = (a: LabArtifact) => Boolean(a.thumbnailUrl); + +/** The name a file is listed under when the surface shows files rather than titles. A link has no file, so it keeps its title. */ +export const fileName = (a: LabArtifact) => a.filename ?? a.title; + +/** Whether anything waits on the reader: the artifact's primary action is then ink at rest. */ +export const isWaiting = (a: LabArtifact) => a.review === "needs_review"; diff --git a/ui/src/artifact-directions/directions/d1-solid/panel-actions.ts b/ui/src/artifact-directions/directions/d1-solid/panel-actions.ts new file mode 100644 index 00000000000..46d55a2f66e --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/panel-actions.ts @@ -0,0 +1,54 @@ +import { ChevronDown, Download, ExternalLink, Maximize2, PanelRightOpen, Table2 } from "lucide-react"; +import type { LabArtifact } from "../../kit"; +import type { SolidAction } from "./actions"; +import { isTaskDocument, isTextLike, slug } from "./kinds"; + +/** + * What a panel card offers, in the panel's production wording. The primary is + * the same deed the thread row offers for that artifact, under the name the + * panel gives it: + * + * thread panel + * Open document Read document task documents (a side-panel tab) and Markdown files (expands in place) + * Open gallery View image / Open video + * Open preview Open link + * Open in tab Open in tab other text-like files + * Download Download file everything else + * + * Everything else the product puts on the card is listed, by name, in the + * card's one "More actions" menu: Open in tab, Open raw, Open file, Download, + * Preview data. The comments count of a Markdown file is the card's own chip, + * not listed here. + */ + +export interface PanelActions { + primary: SolidAction; + more: SolidAction[]; +} + +export function panelActions(a: LabArtifact): PanelActions { + const download = (label: string): SolidAction => ({ id: "download", label, icon: Download, href: `#download-${a.id}` }); + const openFile: SolidAction = { id: "preview", label: "Open file", icon: ExternalLink, href: `#file-${a.id}`, external: true }; + const openTab: SolidAction = { id: "tab", label: "Open in tab", icon: PanelRightOpen, href: `#attachment-${a.id}` }; + + if (isTaskDocument(a)) { + return { primary: { id: "document", label: "Read document", icon: PanelRightOpen, href: `#document-${slug(a)}` }, more: [] }; + } + if (a.kind === "link") { + return { primary: { id: "preview", label: "Open link", icon: ExternalLink, href: a.url ?? "#", external: true }, more: [] }; + } + if (a.kind === "image") return { primary: { id: "gallery", label: "View image", icon: Maximize2 }, more: [] }; + if (a.kind === "video") return { primary: { id: "gallery", label: "Open video", icon: Maximize2 }, more: [] }; + if (a.kind === "document") { + // A Markdown file an agent attached: reads in place, with the product's three ways out. + return { + primary: { id: "document", label: "Read document", icon: ChevronDown }, + more: [openTab, { id: "preview", label: "Open raw", icon: ExternalLink, href: `#raw-${a.id}`, external: true }, download("Download")], + }; + } + if (isTextLike(a)) { + const data: SolidAction[] = /\.csv$/i.test(a.filename ?? "") ? [{ id: "tab", label: "Preview data", icon: Table2 }] : []; + return { primary: openTab, more: [...data, openFile, download("Download file")] }; + } + return { primary: download("Download file"), more: [openFile] }; +} diff --git a/ui/src/artifact-directions/directions/d1-solid/panel-model.ts b/ui/src/artifact-directions/directions/d1-solid/panel-model.ts new file mode 100644 index 00000000000..5f21c57174a --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/panel-model.ts @@ -0,0 +1,63 @@ +import type { LabArtifact } from "../../kit"; +import { isTaskDocument } from "./kinds"; + +/** + * How the panel is sectioned and ordered: exactly as the product does it. + * + * Work products are grouped by the run that produced them (agent + run start); + * task documents, which no run produced, are a section of their own. Sections + * and the cards inside them keep the order the product lists them in — Solid + * changes how an artifact looks, not where it is found. + */ + +export interface PanelSection { + id: string; + /** The producing agent, or a name for what was not produced by a run. */ + title: string; + /** The agent every artifact in the section shares, when it is a run. A card repeats an author only when it differs. */ + agent?: string; + /** "Sep 28, 2026, 10:52 AM" — a run's start. */ + startedLabel?: string; + startedAt?: string; + /** "artifact", or "document" when the section holds task documents only. */ + noun: string; + items: LabArtifact[]; +} + +export function panelSections(artifacts: LabArtifact[]): PanelSection[] { + // A Map keeps insertion order: sections appear where their first artifact does, cards as listed. + const byRun = new Map(); + for (const artifact of artifacts) { + const key = artifact.run?.id ?? ""; + byRun.set(key, [...(byRun.get(key) ?? []), artifact]); + } + return [...byRun.entries()].map(([runId, items]): PanelSection => { + const run = items[0].run; + const documentsOnly = items.every(isTaskDocument); + return { + id: runId || "loose", + title: run ? run.agent : documentsOnly ? "Task documents" : "Other artifacts", + agent: run?.agent, + startedLabel: run?.startedLabel, + startedAt: run?.startedAt, + noun: !run && documentsOnly ? "document" : "artifact", + items, + }; + }); +} + +/** + * A timestamp for a narrow row: the product's label without the year when it + * is this year ("Sep 28, 11:07 AM"). The full label stays in the tooltip. + */ +export const shortStamp = (label: string) => label.replace(`, ${new Date().getFullYear()}`, ""); + +const day = (label: string) => label.slice(0, label.lastIndexOf(", ")); + +/** + * A card's stamp under a section header that already carries a date: the time + * alone when the day is the section's ("11:07 AM"), the short stamp otherwise. + */ +export function stampUnder(label: string, sectionLabel: string | undefined): string { + return sectionLabel && day(label) === day(sectionLabel) ? label.slice(label.lastIndexOf(", ") + 2) : shortStamp(label); +} diff --git a/ui/src/artifact-directions/directions/d1-solid/slab.ts b/ui/src/artifact-directions/directions/d1-solid/slab.ts new file mode 100644 index 00000000000..b4b8cfb084c --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/slab.ts @@ -0,0 +1,73 @@ +/** + * SIGNATURE 3 — the slab: one clear fill step off the canvas, one radius + * (`rounded-md`, used by every slab, block, sheet, button and notch in the + * set), no stroke, no shadow. + * + * Fill logic, shared by all three surfaces (values in tokens.css): + * slab the artifact itself + * slab-hover a slab that is a target, hovered or holding keyboard focus; slab-press while it is pressed + * button a resting button on a slab: a step past the slab, away from the canvas + * sheet the artifact's own content (an excerpt, a file's facts): the lightest fill short of ink + * ink the kind block and tab; the primary action of a hovered slab, and of an artifact that waits for review + */ + +export const MOTION = "transition-colors duration-(--motion-duration-fast) ease-(--motion-ease-standard) motion-reduce:transition-none"; + +/** A resting slab. Add `group/slab` + SLAB_HOVER when the whole slab is a target. */ +export const SLAB = `rounded-md bg-(--solid-slab) ${MOTION}`; +/** A slab that is a target steps one fill when hovered or when its primary holds keyboard focus, and one more while pressed. */ +export const SLAB_HOVER = + "hover:bg-(--solid-slab-hover) has-[[data-primary]:focus-visible]:bg-(--solid-slab-hover) active:bg-(--solid-slab-press)"; + +/** + * For anything laid over a preview that must stay the colour of the slab under + * it (a corner bite, the ring round an ink tab): it takes the slab's fill and + * follows the same three steps. + */ +export const SLAB_FILL = + "bg-(--solid-slab) group-hover/slab:bg-(--solid-slab-hover) group-has-[[data-primary]:focus-visible]/slab:bg-(--solid-slab-hover) group-active/slab:bg-(--solid-slab-press)"; +export const SLAB_RING = + "ring-2 ring-(--solid-slab) group-hover/slab:ring-(--solid-slab-hover) group-has-[[data-primary]:focus-visible]/slab:ring-(--solid-slab-hover) group-active/slab:ring-(--solid-slab-press)"; +/** The same for `currentColor` on the two pseudo-elements of a corner bite (its joints, StateMarker.tsx). */ +export const SLAB_JOINTS = + "before:text-(--solid-slab) after:text-(--solid-slab) group-hover/slab:before:text-(--solid-slab-hover) group-hover/slab:after:text-(--solid-slab-hover) group-has-[[data-primary]:focus-visible]/slab:before:text-(--solid-slab-hover) group-has-[[data-primary]:focus-visible]/slab:after:text-(--solid-slab-hover) group-active/slab:before:text-(--solid-slab-press) group-active/slab:after:text-(--solid-slab-press)"; + +/** Keyboard focus: a 2px ink outline, 2px off the slab so it reads against both the slab and the canvas. */ +export const FOCUS = "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--solid-ink)"; + +/** + * For the one element that makes a whole slab clickable: its `::after` covers + * the slab (which must be `relative`) and carries the focus outline. + */ +export const COVER = + "after:absolute after:inset-0 after:rounded-md focus-visible:outline-none focus-visible:after:outline-2 focus-visible:after:outline-offset-2 focus-visible:after:outline-(--solid-ink)"; + +/** A filled button on a slab. */ +export const BUTTON = `inline-flex h-7 shrink-0 items-center justify-center gap-1.5 rounded-md bg-(--solid-button) px-2 text-xs font-medium whitespace-nowrap text-foreground ${MOTION}`; + +/** Ink at rest: the primary of an artifact that waits for review, so "this needs you" reads without a hover. */ +const BUTTON_INK = "bg-(--solid-ink) text-(--solid-ink-foreground)"; + +/** The button's own hover and press, for a button that is not a slab's cover. */ +export const BUTTON_HOVER = "hover:bg-(--solid-ink) hover:text-(--solid-ink-foreground) active:bg-(--solid-ink) active:text-(--solid-ink-foreground)"; + +/** + * The primary action of a `group/slab` (mark it `data-primary`): turns to ink + * when the slab is hovered or the action holds keyboard focus, and goes back + * to the button fill while the pointer is on a secondary control + * (`data-secondary`), which takes the ink instead. + */ +export const BUTTON_PRIMARY = + "group-hover/slab:bg-(--solid-ink) group-hover/slab:text-(--solid-ink-foreground) focus-visible:bg-(--solid-ink) focus-visible:text-(--solid-ink-foreground) group-has-[[data-secondary]:hover]/slab:bg-(--solid-button) group-has-[[data-secondary]:hover]/slab:text-foreground"; + +/** The primary's two looks at rest. */ +export const primaryRest = (waiting: boolean) => (waiting ? BUTTON_INK : null); + +/** A secondary control on a slab: no fill at rest, ink on hover. Sits above the cover. Mark it `data-secondary`. */ +const GHOST = `relative z-10 inline-flex h-7 shrink-0 cursor-pointer items-center justify-center rounded-md text-(--solid-meta) ${MOTION} ${BUTTON_HOVER} ${FOCUS}`; + +/** The icon-only ghost: 28px square. */ +export const ICON_BUTTON = `${GHOST} w-7`; + +/** A ghost that carries a short mono value beside its icon (the panel's comments count). */ +export const COUNT_BUTTON = `${GHOST} gap-1 px-1.5 font-mono text-(length:--text-micro) leading-none`; diff --git a/ui/src/artifact-directions/directions/d1-solid/tokens.css b/ui/src/artifact-directions/directions/d1-solid/tokens.css new file mode 100644 index 00000000000..2f96ef2a9b8 --- /dev/null +++ b/ui/src/artifact-directions/directions/d1-solid/tokens.css @@ -0,0 +1,111 @@ +/* + Direction 1 — SOLID. Custom properties, scoped to the wrapper every frame puts + around a kit slot. Everything here is achromatic: the only chroma in the + direction is the product's own status vocabulary (see StateMarker.tsx). + + The fill ladder, by value only (OKLCH lightness; canvas is --background). + Each step is named for what it is, and each is a visible step from the one + it sits on (contrast in brackets, Y = L³ for greys): + + light canvas 1.000 → slab 0.900 [1.35] → slab-hover 0.865 → slab-press 0.830 + button 0.800 [1.39 on slab] sheet 1.000 [1.35 on slab] + dark canvas 0.205 → slab 0.325 [1.44] → slab-hover 0.365 → slab-press 0.400 + button 0.400 [1.35 on slab] sheet 0.430 [1.54 on slab] + + SHEET is where an artifact's own content is set (an excerpt, a file's facts): + the lightest fill short of ink in both themes — paper on a mat. + + A gallery pile steps the other way: what is behind a stack's face is nearer + the canvas, in both themes. + + light slab 0.900 → pile-1 0.920 → pile-2 0.945 + dark slab 0.325 → pile-1 0.290 → pile-2 0.265 + + and one inverted fill, INK, for the kind block, the kind tab, and the primary + action of a hovered slab or of an artifact that waits for review: + + light ink 0.205 / on-ink 0.985 + dark ink 0.800 / on-ink 0.205 (silver, not white: it must not out-shout the prose) + + Text contrast, all ≥ 4.5:1: + --solid-meta on slab light 5.8 dark 6.2 + --solid-meta on slab-hover light 5.2 dark 5.3 + --solid-meta on pile-1 light 6.1 dark 7.1 + --solid-sheet-text on sheet light 10.4 dark 6.4 + --foreground on sheet light 19.3 dark 7.8 + --foreground on button light 10.3 dark 8.8 + --solid-ink-foreground on ink light 17.2 dark 9.6 +*/ + +[data-direction="d1-solid"] { + /* Fills */ + --solid-slab: oklch(0.9 0 0); + --solid-slab-hover: oklch(0.865 0 0); + --solid-slab-press: oklch(0.83 0 0); + --solid-button: oklch(0.8 0 0); + --solid-sheet: oklch(1 0 0); + --solid-ink: oklch(0.205 0 0); + /* A gallery pile: the slabs behind a stack's face, each one step nearer the canvas. */ + --solid-pile-1: oklch(0.92 0 0); + --solid-pile-2: oklch(0.945 0 0); + + /* Text on those fills */ + --solid-ink-foreground: oklch(0.985 0 0); + /* Secondary text on a slab or a pile. --muted-foreground is 3.4:1 on the light slab. */ + --solid-meta: oklch(0.44 0 0); + /* Body text on a sheet: a step under --foreground, well over the meta tone. */ + --solid-sheet-text: oklch(0.37 0 0); + + /* State dots: the product's status hues. */ + --solid-state-review: var(--status-task-icon-in_review); + --solid-state-changes: var(--status-task-icon-blocked); + + /* Geometry */ + /* The kind block and every gallery preview: one landscape plate (72×44 in a thread row). */ + --solid-plate-ratio: 18 / 11; + /* A picture or a video leading a panel card. */ + --solid-lead-ratio: 16 / 9; + /* The attachments block: as many equal slabs as fit, never narrower than 16rem; a file is one 52px row, a picture two. */ + --solid-attach-cols: repeat(auto-fill, minmax(16rem, 1fr)); + --solid-attach-row: 3.25rem; + /* The panel's cards: one column in the docked pane; a maximized pane sets as many 22rem columns as fit, row by row. */ + --solid-panel-cols: repeat(auto-fill, minmax(min(22rem, 100%), 1fr)); + /* + Text on a gallery sheet: as many WHOLE lines as the sheet has room for above + the band kept clear for the kind tab. Resolved where it is used, against + the sheet as a size container: 100cqh is the sheet inside its own inset, so + taking 1rem more off leaves a band of that inset + 1rem under the text + (32px on a plate, 24px in a mosaic cell). + */ + --solid-sheet-fit: round(down, calc(100cqh - 1rem), 1lh); + /* + A joint of a corner bite: a 6px box with one rounded corner, whose shadow + (its own shape grown by 3px and moved 3px right and down, so it starts at + the box's own top-left) paints only what lies outside the quarter circle. + The colour is the box's currentColor — the slab's fill, at any step. + */ + --solid-joint: 0.1875rem 0.1875rem 0 0.1875rem currentColor; +} + +.dark [data-direction="d1-solid"] { + --solid-slab: oklch(0.325 0 0); + --solid-slab-hover: oklch(0.365 0 0); + --solid-slab-press: oklch(0.4 0 0); + --solid-button: oklch(0.4 0 0); + --solid-sheet: oklch(0.43 0 0); + --solid-ink: oklch(0.8 0 0); + --solid-pile-1: oklch(0.29 0 0); + --solid-pile-2: oklch(0.265 0 0); + + --solid-ink-foreground: oklch(0.205 0 0); + --solid-meta: oklch(0.78 0 0); + --solid-sheet-text: oklch(0.92 0 0); + + /* The product's blocked red (#dc2626) is 2.6:1 on the dark slab; a 6px dot that stands for a state needs 3:1. */ + --solid-state-changes: oklch(0.704 0.191 22.216); +} + +/* A gallery sheet is a size container, so its text can be fitted to whole lines of its own height (--solid-sheet-fit). */ +[data-direction="d1-solid"] [data-solid-sheet="fit"] { + container-type: size; +} diff --git a/ui/src/artifact-directions/directions/d2-tint/Content.tsx b/ui/src/artifact-directions/directions/d2-tint/Content.tsx new file mode 100644 index 00000000000..6b5ae59fabb --- /dev/null +++ b/ui/src/artifact-directions/directions/d2-tint/Content.tsx @@ -0,0 +1,138 @@ +import { Fragment, useLayoutEffect, useRef, useState } from "react"; +import { cn } from "@/lib/utils"; +import type { LabArtifact } from "../../kit"; +import { documentExcerpt, sheetPreview } from "./excerpt"; + +/** + * What is INSIDE a text-like artifact, as bare content: the caller supplies + * the sheet it sits on (`FoldedSheet`) and the type size. The panel, the + * gallery tile and a folder's face set the same three things at three sizes. + * + * ProseExcerpt a document's opening in two tiers: at most one heading on + * its own line (foreground, medium), body lines in secondary + * ink, list items each on a line behind a dash. One clamped + * block, so it always ends on a whole line with an ellipsis. + * SheetTable header + first rows, monospace; the first column truncates, + * value columns keep their width; columns that do not fit the + * measured width are counted, never clipped + * CodeLines the first lines, monospace, each cut with an ellipsis + */ + +export function ProseExcerpt({ artifact, maxChars, className }: { artifact: LabArtifact; maxChars?: number; className?: string }) { + const lines = documentExcerpt(artifact, maxChars); + if (lines.length === 0) return null; + return ( + // Inline content with explicit breaks, so the line clamp counts every line of every tier. +

+ {lines.map((line, index) => ( + + {index > 0 ?
: null} + {line.heading ? {line.text} : line.item ? <>– {line.text} : line.text} +
+ ))} +

+ ); +} + +/** How many monospace cells fit across an element, re-measured when it resizes. */ +function useCells() { + const ref = useRef(null); + const [fit, setFit] = useState<{ cells: number; width: number } | null>(null); + useLayoutEffect(() => { + const el = ref.current; + if (!el) return; + const measure = () => { + const probe = document.createElement("span"); + probe.textContent = "0000000000"; + probe.className = "invisible absolute whitespace-pre"; + el.append(probe); + const cell = probe.getBoundingClientRect().width / 10; + probe.remove(); + if (cell > 0) setFit({ cells: Math.floor(el.clientWidth / cell), width: el.clientWidth }); + }; + measure(); + const observer = new ResizeObserver(measure); + observer.observe(el); + return () => observer.disconnect(); + }, []); + return [ref, fit] as const; +} + +const FIRST_CELL = "w-full max-w-0 truncate pr-2"; +const VALUE_CELL = "whitespace-nowrap pl-3 text-right"; + +export function SheetTable({ + artifact, + rows, + className, +}: { + artifact: LabArtifact; + /** Rows to show: a number, or a function of the measured width in px (a fixed-height sheet shows fewer when it is narrow). */ + rows: number | ((width: number) => number); + /** Type size and leading of the table. */ + className?: string; +}) { + const [ref, fit] = useCells(); + const maxRows = typeof rows === "number" ? rows : rows(fit?.width ?? 0); + // Before the first measurement only the name column is laid out, so nothing can spill for a frame. + const preview = sheetPreview(artifact, maxRows, fit?.cells ?? 0); + if (!preview) return null; + const more = [ + preview.more > 0 ? `+${preview.more} more row${preview.more === 1 ? "" : "s"}` : null, + preview.moreColumns > 0 ? `+${preview.moreColumns} more column${preview.moreColumns === 1 ? "" : "s"}` : null, + ].filter(Boolean); + return ( +
+
+ + + {preview.header.map((cell, index) => ( + + ))} + + + + {preview.rows.map((row, r) => ( + + {row.map((cell, index) => ( + + ))} + + ))} + +
+ {cell} +
+ {cell} +
+ {more.length > 0 ?

{more.join(" · ")}

: null} +
+ ); +} + +export function CodeLines({ + artifact, + lines, + step, + className, +}: { + artifact: LabArtifact; + lines: number; + /** Per-line classes, by index — a fixed-height sheet hides its last lines in a narrow unit instead of slicing one. */ + step?: readonly string[]; + className?: string; +}) { + if (!artifact.excerpt) return null; + return ( +
+      {artifact.excerpt
+        .split("\n")
+        .slice(0, lines)
+        .map((line, index) => (
+          
+            {line || " "}
+          
+        ))}
+    
+ ); +} diff --git a/ui/src/artifact-directions/directions/d2-tint/FileName.tsx b/ui/src/artifact-directions/directions/d2-tint/FileName.tsx new file mode 100644 index 00000000000..51f55071ae8 --- /dev/null +++ b/ui/src/artifact-directions/directions/d2-tint/FileName.tsx @@ -0,0 +1,19 @@ +import { cn } from "@/lib/utils"; + +/** + * A filename is a machine value: monospace. When it does not fit, the stem + * truncates and the extension never does — two files that share a long stem + * (`acmehats-interface-craft.pptx` / `.pdf`) stay tellable apart. + * + * `mono={false}` is for a human title standing in where a file has no name. + */ +export function FileName({ name, mono = true, className }: { name: string; mono?: boolean; className?: string }) { + const dot = mono ? name.lastIndexOf(".") : -1; + const [stem, ext] = dot > 0 ? [name.slice(0, dot), name.slice(dot)] : [name, ""]; + return ( + + {stem} + {ext ? {ext} : null} + + ); +} diff --git a/ui/src/artifact-directions/directions/d2-tint/GalleryPreview.tsx b/ui/src/artifact-directions/directions/d2-tint/GalleryPreview.tsx new file mode 100644 index 00000000000..f85455df36e --- /dev/null +++ b/ui/src/artifact-directions/directions/d2-tint/GalleryPreview.tsx @@ -0,0 +1,125 @@ +import { useState } from "react"; +import { cn } from "@/lib/utils"; +import type { LabArtifact } from "../../kit"; +import { CodeLines, ProseExcerpt, SheetTable } from "./Content"; +import { FileName } from "./FileName"; +import { FoldedPreview, FoldedSheet, KindWell } from "./Lead"; +import { hasTextPreview, headlineFact, linkLabel } from "./kinds"; + +/** + * GALLERY — the preview of one artifact, the largest object a tile has, by + * the one rule (Lead.tsx). Never an empty box: + * + * picture image, video poster, link screenshot — fold lg, extension tab, + * play mark and duration on a video + * sheet document, Markdown, sheet, code — its own text at full width on + * a neutral sheet in the same frame (fold lg, tab). Clamped to + * whole lines for the tile's width, so nothing is sliced. + * named PDF, slides, archive, generic file, or a picture that failed — + * the lg glyph on its kind well with what is known beside it: the + * countable fact as a headline ("12 slides", else the size), what + * the agent said is inside, and the filename. This replaces + * today's empty box that says FILE. + * + * The box is 16:9 and the tile is its `@container`. `depth="short"` is the + * shallower box of a single-artifact stack, whose height the caller sets. + */ + +type PreviewKind = "picture" | "sheet" | "named"; +type PreviewDepth = "tile" | "short"; + +/** Which preview an artifact gets, and the handler that turns a failed picture into a named one. */ +export function usePreviewKind(artifact: LabArtifact): [PreviewKind, () => void] { + const [failed, setFailed] = useState(false); + const kind: PreviewKind = artifact.thumbnailUrl && !failed ? "picture" : hasTextPreview(artifact) ? "sheet" : "named"; + return [kind, () => setFailed(true)]; +} + +const BOX = "aspect-video w-full"; +const TEXT = "text-(length:--text-compact) leading-5"; +const MONO = "text-(length:--text-micro) leading-5"; +// Whole lines that fit a 16:9 sheet, by the tile's width: 20px lines under a 24px head, above the tab. +const CLAMP: Record = { tile: "line-clamp-3 @3xs:line-clamp-4 @2xs:line-clamp-5 @xs:line-clamp-6 @sm:line-clamp-8", short: "line-clamp-4" }; +const CODE_STEP = ["", "", "", "hidden @3xs:block", "hidden @2xs:block", "hidden @xs:block", "hidden @sm:block", "hidden @sm:block"]; +const CODE_LINES: Record = { tile: CODE_STEP.length, short: 4 }; + +/** Data rows that fit a 16:9 sheet whose text column is `width` wide: its lines, less the header and the "+N more" note. */ +function rowsThatFit(width: number): number { + const SIDE = 28; // the sheet's two side insets + const HEAD_AND_TAB = 44; // top inset + the tab's height + const lines = Math.floor((((width + SIDE) * 9) / 16 - HEAD_AND_TAB) / 20); + return Math.max(1, lines - 2); +} + +function Sheet({ artifact, depth, className }: { artifact: LabArtifact; depth: PreviewDepth; className?: string }) { + return ( + + {artifact.kind === "document" ? ( + + ) : artifact.kind === "sheet" ? ( + + ) : ( + + )} + + ); +} + +function Named({ artifact, depth, className }: { artifact: LabArtifact; depth: PreviewDepth; className?: string }) { + const headline = headlineFact(artifact); + const address = linkLabel(artifact.url); + const words = Boolean(headline ?? artifact.summary ?? artifact.filename ?? address); + const small = "relative z-10 mt-1.5 cursor-text select-text text-(length:--text-micro) leading-4 text-(--tint-ink-2)"; + return ( + + {words ? ( +
+ {/* A count is a machine value: the mono voice, at headline size. */} + {headline ?

{headline}

: null} + {artifact.summary ? ( +

+ {artifact.summary} +

+ ) : null} + {artifact.filename ? ( + + ) : address ? ( + + {address} + + ) : null} +
+ ) : null} +
+ ); +} + +const DURATION = "absolute bottom-2 right-2 rounded-(--rad-4) bg-(--tint-scrim) px-1.5 py-1 font-mono text-(length:--text-micro) leading-none text-(--tint-on-kind)"; + +export function GalleryPreview({ + artifact, + kind, + onError, + depth = "tile", + className, +}: { + artifact: LabArtifact; + kind: PreviewKind; + onError: () => void; + depth?: PreviewDepth; + className?: string; +}) { + if (kind === "sheet") return ; + if (kind === "named") return ; + return ( + + {artifact.kind === "video" && artifact.durationLabel ? {artifact.durationLabel} : null} + + ); +} diff --git a/ui/src/artifact-directions/directions/d2-tint/GalleryStack.tsx b/ui/src/artifact-directions/directions/d2-tint/GalleryStack.tsx new file mode 100644 index 00000000000..22dc179ebfa --- /dev/null +++ b/ui/src/artifact-directions/directions/d2-tint/GalleryStack.tsx @@ -0,0 +1,66 @@ +import { useId } from "react"; +import type { LabGroup } from "../../kit"; +import { GalleryTile } from "./GalleryTile"; +import { KindTally } from "./KindTally"; +import { MetaLine } from "./MetaLine"; +import { ReviewTally } from "./StateMark"; +import { artifactCount, stackSections, type StackSection } from "./gallery-model"; +import { countReview } from "./panel-model"; + +/** + * GALLERY — one opened stack: everything below the "← All stacks" row. + * + * caption what the folder's pocket said, as one line of type on the + * page: the kinds spelled out with their mini glyphs, how much + * is waiting, the count and the updated date. A caption, not a + * control — it has no fill, so it cannot be mistaken for a second + * row of the page's own filter tabs right above it. + * sections the artifacts by the day of their last edit, newest first; a + * day that cannot fill a row shares its heading with its quiet + * neighbours (gallery-model.ts), so headings do not strand tiles + * tiles GalleryTile.tsx, three to a row on the page's grid + */ + +function Caption({ group }: { group: LabGroup }) { + return ( +
+ + + +
+ ); +} + +function Section({ section }: { section: StackSection }) { + const headingId = useId(); + return ( +
+
+

+ {section.label} +

+ {artifactCount(section.artifacts.length)} +
+
    + {section.artifacts.map((artifact) => ( + + ))} +
+
+ ); +} + +export function GalleryStack({ group }: { group: LabGroup }) { + if (group.artifacts.length === 0) { + // The shipped empty state's copy. + return

No artifacts in this stack match the current filters.

; + } + return ( +
+ + {stackSections(group.artifacts).map((section) => ( +
+ ))} +
+ ); +} diff --git a/ui/src/artifact-directions/directions/d2-tint/GalleryStacks.tsx b/ui/src/artifact-directions/directions/d2-tint/GalleryStacks.tsx new file mode 100644 index 00000000000..c7dac4b47ca --- /dev/null +++ b/ui/src/artifact-directions/directions/d2-tint/GalleryStacks.tsx @@ -0,0 +1,22 @@ +import type { LabGroup } from "../../kit"; +import { StackFolder } from "./StackFolder"; +import { StackSingle } from "./StackSingle"; + +/** + * GALLERY — the grid of task stacks under the page toolbar. + * + * The page's own grid (1 / 2 / 3 columns, 24px gap). A stack of two or more + * is a folder; a stack of one is the artifact itself. Every unit in a row + * takes the row's height, so the pockets and the single tiles end level. + */ +export function GalleryStacks({ groups }: { groups: LabGroup[] }) { + if (groups.length === 0) { + // The shipped empty state's copy. + return

No artifact stacks yet.

; + } + return ( +
    + {groups.map((group) => (group.count > 1 ? : ))} +
+ ); +} diff --git a/ui/src/artifact-directions/directions/d2-tint/GalleryTile.tsx b/ui/src/artifact-directions/directions/d2-tint/GalleryTile.tsx new file mode 100644 index 00000000000..d5121fb1485 --- /dev/null +++ b/ui/src/artifact-directions/directions/d2-tint/GalleryTile.tsx @@ -0,0 +1,70 @@ +import { cn } from "@/lib/utils"; +import type { LabArtifact } from "../../kit"; +import { GalleryPreview, usePreviewKind } from "./GalleryPreview"; +import { MetaLine } from "./MetaLine"; +import { StateMark } from "./StateMark"; +import { SLAB, SecondaryAction, TitleAction } from "./UnitAction"; +import { galleryAction, gallerySecondary } from "./actions"; +import { headlineFact, metaParts, shortDateTime, timeOnly } from "./kinds"; + +/** + * GALLERY — one artifact in an opened stack. + * + * The panel card made preview-first for every kind: the same filled slab with + * the same 8px inset, the lead across the whole width, then three lines — + * + * ┌───────────── preview, 16:9 ──────────────┐ + * │ folded picture · text sheet · kind well │ + * └──────────────────────────────────────────┘ + * Title one line + * kind · size · rev · fact ○ Review the mono meta line + * Author · 11:07 AM ↗ ↓ icon actions, always visible + * + * The title is the link (stretched over the tile); it goes to the artifact in + * its task, as today's whole-card link does. "Open file in new tab" and + * "Download file" are their own targets, visible at rest and reachable by + * keyboard — today they exist only on hover. + * + * A fact the preview already states (a video's duration chip, a kind well's + * headline) is not repeated on the meta line. The author truncates before the + * time does. Under a day heading the tile shows the time; under a heading + * that spans days (`dated`) it shows the date too. + */ +export function GalleryTile({ artifact, dated = false }: { artifact: LabArtifact; dated?: boolean }) { + const [kind, onError] = usePreviewKind(artifact); + const secondary = gallerySecondary(artifact); + const stated = kind === "named" ? headlineFact(artifact) : kind === "picture" ? artifact.durationLabel : undefined; + return ( +
  • + +
    +

    + + {artifact.title} + +

    + {/* Every tile keeps the height of a state mark here, so a row of tiles stays level whether or not one is waiting. */} +
    + + +
    +
    +

    + {artifact.author} + · + +

    + {secondary.length > 0 ? ( +
    + {secondary.map((action, index) => ( + + ))} +
    + ) : null} +
    +
    +
  • + ); +} diff --git a/ui/src/artifact-directions/directions/d2-tint/KindGlyph.tsx b/ui/src/artifact-directions/directions/d2-tint/KindGlyph.tsx new file mode 100644 index 00000000000..3975c8f9bcb --- /dev/null +++ b/ui/src/artifact-directions/directions/d2-tint/KindGlyph.tsx @@ -0,0 +1,127 @@ +import { cn } from "@/lib/utils"; +import type { LabKind } from "../../kit"; +import { Pictogram } from "./Pictogram"; + +/** + * SIGNATURE ELEMENT 1 — the kind glyph. + * + * A rounded tile with a folded top-right corner (a flat, lighter facet of the + * same hue — never a shadow), a pictogram top-left and the real extension in + * bold along the bottom. Solid `--kind` fill, flat. It is the one place a + * unit carries its hue at full strength. + * + * mini 16×18 pictogram only — index chips, kind tallies + * xs 34×38 thread: attachment slabs, and row leads on their tinted well + * sm 40×46 spare (specimen) + * md 56×64 panel leads on their tinted well; narrow gallery tiles + * lg 96×110 gallery tiles with nothing to show + * tuck 96×190 standing in a folder (StackFolder.tsx): the same top — fold, + * pictogram, label — on a tile tall enough that its bottom + * edge is always behind the pocket + * + * Without a `label` (a task document: it has no extension) the pictogram + * stands alone, larger and centred — the mini glyph's composition at size. + * + * The ancestor that sets `data-kind` supplies `--kind`, `--kind-facet` and + * `--kind-ink` (tokens.css); pass `kind` here only for the pictogram. + */ + +export type GlyphSize = "mini" | "xs" | "sm" | "md" | "lg" | "tuck"; + +interface Placement { + x: number; + y: number; + size: number; +} + +interface Spec { + w: number; + h: number; + /** Corner radius and fold size, in the tile's own px. */ + r: number; + fold: number; + picto: Placement; + /** The pictogram when there is no label to share the tile with. */ + solo: Placement; + label: { size: number; baseline: number; inset: number } | null; + box: string; +} + +const MINI: Placement = { x: 3.2, y: 5.6, size: 9.6 }; +const SPEC: Record = { + mini: { w: 16, h: 18, r: 3.5, fold: 5, picto: MINI, solo: MINI, label: null, box: "w-4 h-4.5" }, + xs: { w: 34, h: 38, r: 6, fold: 9.5, picto: { x: 4.5, y: 4.5, size: 10 }, solo: { x: 7.5, y: 12, size: 19 }, label: { size: 10, baseline: 32.2, inset: 3 }, box: "w-8.5 h-9.5" }, + sm: { w: 40, h: 46, r: 7, fold: 11.5, picto: { x: 5.5, y: 5.5, size: 14 }, solo: { x: 9, y: 14.5, size: 22 }, label: { size: 10.5, baseline: 39.2, inset: 4 }, box: "w-10 h-11.5" }, + md: { w: 56, h: 64, r: 9.5, fold: 16, picto: { x: 8, y: 8, size: 18 }, solo: { x: 12.5, y: 20, size: 31 }, label: { size: 14, baseline: 54.4, inset: 6 }, box: "w-14 h-16" }, + lg: { w: 96, h: 110, r: 16, fold: 27, picto: { x: 14, y: 14, size: 30 }, solo: { x: 22, y: 35, size: 52 }, label: { size: 23.5, baseline: 93.5, inset: 10 }, box: "w-24 h-27.5" }, + tuck: { w: 96, h: 190, r: 16, fold: 27, picto: { x: 13, y: 13, size: 26 }, solo: { x: 24, y: 30, size: 48 }, label: { size: 22, baseline: 68, inset: 10 }, box: "w-24 h-47.5" }, +}; + +/** The tile with its top-right corner cut on the diagonal. */ +function tilePath({ w, h, r, fold }: Spec): string { + const k = r * 0.45; // softens the two ends of the cut + return [ + `M${r} 0`, + `H${w - fold - k}`, + `Q${w - fold} 0 ${w - fold + k * 0.7} ${k * 0.7}`, + `L${w - k * 0.7} ${fold - k * 0.7}`, + `Q${w} ${fold} ${w} ${fold + k}`, + `V${h - r}`, + `Q${w} ${h} ${w - r} ${h}`, + `H${r}`, + `Q0 ${h} 0 ${h - r}`, + `V${r}`, + `Q0 0 ${r} 0Z`, + ].join(""); +} + +/** The folded flap: the cut-off triangle laid back onto the tile, inner corner rounded. */ +function facetPath({ w, r, fold }: Spec): string { + const k = r * 0.45; + const c = Math.min(r * 0.7, fold * 0.4); + return [ + `M${w - fold} ${k * 0.6}`, + `Q${w - fold} 0 ${w - fold + k * 0.7} ${k * 0.7}`, + `L${w - k * 0.7} ${fold - k * 0.7}`, + `Q${w} ${fold} ${w - k * 0.6} ${fold}`, + `H${w - fold + c}`, + `Q${w - fold} ${fold} ${w - fold} ${fold - c}Z`, + ].join(""); +} + +// Inter Bold capitals, as a fraction of the font size — only to decide when a label must be squeezed. +const WIDE: Record = { W: 0.99, M: 0.9, O: 0.78, Q: 0.78, G: 0.76, C: 0.73, D: 0.74, H: 0.74, N: 0.75, U: 0.73, I: 0.29, J: 0.56, L: 0.59, F: 0.61, E: 0.63, T: 0.64 }; +const labelWidth = (label: string, size: number) => [...label].reduce((sum, ch) => sum + (WIDE[ch] ?? 0.68), 0) * size; + +export function KindGlyph({ kind, label, size = "sm", className }: { kind: LabKind; label?: string; size?: GlyphSize; className?: string }) { + const spec = SPEC[size]; + const text = spec.label && label ? label : null; + const room = spec.label ? spec.w - spec.label.inset * 2 : 0; + const squeeze = text && spec.label && labelWidth(text, spec.label.size) > room; + return ( + + + + + {text && spec.label ? ( + + {text} + + ) : null} + + ); +} diff --git a/ui/src/artifact-directions/directions/d2-tint/KindTally.tsx b/ui/src/artifact-directions/directions/d2-tint/KindTally.tsx new file mode 100644 index 00000000000..b3c3a894a36 --- /dev/null +++ b/ui/src/artifact-directions/directions/d2-tint/KindTally.tsx @@ -0,0 +1,31 @@ +import { cn } from "@/lib/utils"; +import type { LabArtifact } from "../../kit"; +import { KindGlyph } from "./KindGlyph"; +import { kindNoun, kindNounInline, tallyKinds } from "./panel-model"; + +/** + * What is in a pile, as a CAPTION: one entry per kind present, in the + * vocabulary's order — the mini glyph and a mono count. Never a control: it + * has no fill and no button shape (the panel's index, which filters, is built + * from buttons — PanelIndex.tsx). + * + * `named` spells each kind out ("10 documents"), where there is a line to + * itself — the caption of an opened stack. + */ +export function KindTally({ artifacts, named = false, className }: { artifacts: LabArtifact[]; named?: boolean; className?: string }) { + const kinds = tallyKinds(artifacts); + if (kinds.length === 0) return null; + return ( +
      + {kinds.map(({ kind, count }) => ( +
    • + + + {count} + {named ? {kindNounInline(kind, count)} : {kindNoun(kind, count)}} + +
    • + ))} +
    + ); +} diff --git a/ui/src/artifact-directions/directions/d2-tint/Lead.tsx b/ui/src/artifact-directions/directions/d2-tint/Lead.tsx new file mode 100644 index 00000000000..bb1d3cd962d --- /dev/null +++ b/ui/src/artifact-directions/directions/d2-tint/Lead.tsx @@ -0,0 +1,146 @@ +import { useState, type ReactNode } from "react"; +import { cn } from "@/lib/utils"; +import type { LabArtifact } from "../../kit"; +import { KindGlyph, type GlyphSize } from "./KindGlyph"; +import { glyphLabel } from "./kinds"; + +/** + * THE ONE RULE — the lead of every unit, on every surface, is one of two things. + * + * SHOWN the artifact's own content wearing the kind's two marks: the top- + * right corner folded back as a solid kind-hue flap, and the + * extension as a hue tab on the bottom-left edge. A picture + * (`FoldedPreview`) or its text on a neutral sheet (`FoldedSheet`). + * NAMED when there is nothing to show, or no room to show it: the kind + * glyph centred on a well filled with the kind's quiet hue (`KindWell`). + * + * So colour is the fold and tab on real content, or the glyph and its well — + * never both, and nowhere else on the unit at rest. A missing or failed + * picture falls back to NAMED, so the placeholder is never an empty box. + */ + +type FoldSize = "sm" | "md" | "lg"; +/** Where the extension tab sits. `top` is for a folder, where the bottom edge is tucked behind the pocket. */ +type TabEdge = "bottom" | "top" | "none"; + +// The line-height sits AFTER the size in `tab`: tailwind-merge drops a leading that precedes a font size. +// `sheet` pads a text sheet so its first line clears the fold; `room` is the extra a tab needs on its edge. +const FOLD: Record }> = { + sm: { clip: "var(--tint-clip-fold-sm)", flap: "size-(--tint-fold-sm)", tab: "px-1 py-0.5 text-(length:--text-nano) leading-none", play: "top-(--tint-play-y-sm) size-5", sheet: "px-2 pt-2.5 pb-2", room: { bottom: "pb-5", top: "pt-5" } }, + md: { clip: "var(--tint-clip-fold-md)", flap: "size-(--tint-fold-md)", tab: "px-1.5 py-1 text-(length:--text-micro) leading-none", play: "top-1/2 size-9", sheet: "px-3 pt-4 pb-3", room: { bottom: "pb-7", top: "pt-7" } }, + lg: { clip: "var(--tint-clip-fold-lg)", flap: "size-(--tint-fold-lg)", tab: "px-2 py-1 text-xs leading-none", play: "top-1/2 size-11", sheet: "px-3.5 pt-6 pb-3.5", room: { bottom: "pb-8", top: "pt-8" } }, +}; + +const TAB_EDGE = { bottom: "bottom-0 rounded-tr-(--rad-4)", top: "top-0 rounded-br-(--rad-4)" } as const; + +/** The folded flap: the cut-off corner laid back, inner corner rounded. Square, scales with its box. */ +function FoldFlap({ className }: { className?: string }) { + return ( + + + + ); +} + +function Tab({ artifact, fold, edge }: { artifact: LabArtifact; fold: FoldSize; edge: TabEdge }) { + const label = glyphLabel(artifact); + if (!label || edge === "none") return null; + return {label}; +} + +/** The play mark on a video poster: white on a neutral scrim, never the kind hue. */ +export function PlayMark({ fold, className }: { fold: FoldSize; className?: string }) { + return ( + + + + + + ); +} + +interface FrameProps { + artifact: LabArtifact; + fold: FoldSize; + tab?: TabEdge; + className?: string; + children?: ReactNode; +} + +/** + * SHOWN, a picture. The caller sets the box (`className`) and picks the fold; + * the image fills the box and can never set its height. `fit="contain"` shows + * the whole picture on the neutral well (the panel, where it is judged). + * Children render over the image (a duration, a play button). + */ +export function FoldedPreview({ artifact, fold, tab = "bottom", fit = "cover", play = true, className, onError, children }: FrameProps & { fit?: "cover" | "contain"; play?: boolean; onError?: () => void }) { + // A cropped photograph keeps its middle; a cropped screenshot keeps its top-left, where a page starts. + const crop = artifact.kind === "link" ? "object-cover object-left-top" : "object-cover"; + return ( + + + {artifact.kind === "video" && play ? : null} + {children} + + + + ); +} + +/** + * SHOWN, text. A neutral sheet (a hole back to the page fill) holding the + * artifact's own words at full width, in the frame a picture wears. Children + * are the content (Content.tsx); anything that does not fit is clipped by the + * sheet, so the caller clamps to whole lines. + */ +export function FoldedSheet({ artifact, fold, tab = "bottom", className, children }: FrameProps) { + const spec = FOLD[fold]; + const tabbed = tab !== "none" && glyphLabel(artifact) ? spec.room[tab] : null; + return ( +
    + {children} + + +
    + ); +} + +/** NAMED. The glyph centred on its kind well; children sit beside it (a headline, a summary). */ +export function KindWell({ + artifact, + size = "md", + className, + glyphClassName, + children, +}: { + artifact: LabArtifact; + size?: GlyphSize; + className?: string; + /** Overrides the glyph's box (the SVG scales), e.g. a container-query step down in a narrow unit. */ + glyphClassName?: string; + children?: ReactNode; +}) { + return ( +
    + + {children} +
    + ); +} + +// 72×46: the one lead box of a thread row, so every title starts on the same edge. +const ROW_BOX = "h-11.5 w-18 shrink-0"; + +/** The lead of a thread row: a picture when there is one; otherwise the xs glyph on its well (no room to show text here). */ +export function Lead({ artifact }: { artifact: LabArtifact }) { + const [failed, setFailed] = useState(false); + if (!artifact.thumbnailUrl || failed) return ; + return setFailed(true)} />; +} diff --git a/ui/src/artifact-directions/directions/d2-tint/MetaLine.tsx b/ui/src/artifact-directions/directions/d2-tint/MetaLine.tsx new file mode 100644 index 00000000000..69c150591f8 --- /dev/null +++ b/ui/src/artifact-directions/directions/d2-tint/MetaLine.tsx @@ -0,0 +1,29 @@ +import { Fragment } from "react"; +import { cn } from "@/lib/utils"; + +/** + * SIGNATURE ELEMENT 2 — the mono meta line. + * + * One grammar on every surface — kind · size · rev — set in the monospace + * token because every part of it is a machine value. Never coloured: the hue + * belongs to the lead. + * + * One line with an ellipsis by default. `wrap` lets it break between parts + * instead, where the product's rule is that facts wrap rather than disappear + * (a narrow pane, a narrow folder). + */ +export function MetaLine({ parts, wrap = false, className }: { parts: string[]; wrap?: boolean; className?: string }) { + return ( + + {parts.map((part, index) => ( + + {/* A full monospace " · " is three cells wide; the dot gets half-cell margins instead. */} + {index > 0 ? · : null} + {/* When wrapping, a part never breaks inside itself; the line breaks after a dot. */} + {index > 0 && wrap ? : null} + {part} + + ))} + + ); +} diff --git a/ui/src/artifact-directions/directions/d2-tint/PanelBody.tsx b/ui/src/artifact-directions/directions/d2-tint/PanelBody.tsx new file mode 100644 index 00000000000..4cdcdd35181 --- /dev/null +++ b/ui/src/artifact-directions/directions/d2-tint/PanelBody.tsx @@ -0,0 +1,41 @@ +import { useMemo, useState } from "react"; +import type { LabArtifact } from "../../kit"; +import { PanelIndex } from "./PanelIndex"; +import { PanelSection } from "./PanelSection"; +import { buildSections, type PanelFilter } from "./panel-model"; + +/** + * PANEL — the whole scroll body of the task pane's Artifacts tab. + * + * index one row of filter buttons: All · Review · the kinds with two or + * more · "+N" (PanelIndex.tsx) + * sections producing runs and the task's documents, in the product's + * order — newest first — each saying how many of its items are + * waiting for review (panel-model.ts) + * cards one filled slab per artifact (PanelCard.tsx) + * + * A card is as tall as what it has to show: 136–208px for a file (the tall + * ones carry a three-line MIME type), 226–264 for a document, about 435 for a + * 4:3 picture — against today's 390–500 for every kind. The 21-artifact task + * is 5,400px of cards against today's 8,300. + * The filter is local state; in the product the index would stay pinned while + * the list scrolls. + */ +export function PanelBody({ artifacts }: { artifacts: LabArtifact[] }) { + const [filter, setFilter] = useState("all"); + const sections = useMemo(() => buildSections(artifacts, filter), [artifacts, filter]); + + if (artifacts.length === 0) { + // The shipped empty state, word for word. + return

    No artifacts yet. Work products, documents, and agent-produced files will appear here.

    ; + } + + return ( +
    + + {sections.map((section) => ( + + ))} +
    + ); +} diff --git a/ui/src/artifact-directions/directions/d2-tint/PanelCard.tsx b/ui/src/artifact-directions/directions/d2-tint/PanelCard.tsx new file mode 100644 index 00000000000..52c02a12ab2 --- /dev/null +++ b/ui/src/artifact-directions/directions/d2-tint/PanelCard.tsx @@ -0,0 +1,120 @@ +import { Fragment, useState } from "react"; +import { cn } from "@/lib/utils"; +import type { LabArtifact } from "../../kit"; +import { FileName } from "./FileName"; +import { MetaLine } from "./MetaLine"; +import { PanelNamed, PanelPicture, PanelSheet, panelLeadKind } from "./PanelLead"; +import { StateMark } from "./StateMark"; +import { ButtonAction, SLAB, SecondaryAction } from "./UnitAction"; +import { panelAction, panelSecondary } from "./actions"; +import { kindLabel, linkLabel, metaParts, shortDateTime } from "./kinds"; + +/** + * PANEL — one artifact on the task's shelf. One skeleton for every kind; the + * lead follows the one rule (PanelLead.tsx): + * + * SHOWN (picture, or text on a sheet) NAMED (nothing to show) + * ┌──────── lead, full width ────────┐ ┌ well ┐ title + * └──────────────────────────────────┘ │ glyph│ kind · size · fact ○ Review + * title │ │ summary + * kind · size · rev · fact ○ Review └──────┘ filename · MIME type + * summary · filename + * author · updated icon actions [Primary] (the same footer on both) + * + * Everything the shipped card prints is printed here — nothing lives only in + * a tooltip. Filename, address and MIME type sit above the card's stretched + * action so they can be selected and copied. Hover is neutral (the hue is + * spent on the lead): the slab steps up and the primary button inverts. + */ + +// Machine values a reviewer may want to copy: above the stretched link, selectable. +const COPYABLE = "relative z-10 cursor-text select-text"; +const SMALL = "mt-1 text-(length:--text-micro) leading-4 text-(--tint-ink-2)"; + +/** The product prints the MIME type on file cards — not on documents, pictures or links. */ +const showsMime = (a: LabArtifact) => Boolean(a.contentType) && a.kind !== "document" && a.kind !== "image" && a.kind !== "video" && a.kind !== "link"; + +/** A long MIME type wraps after its own separators, not in the middle of a word — and still copies as the plain string. */ +function breakable(value: string) { + return value.split(/(?<=[/.+-])/).map((part, index) => ( + + {index > 0 ? : null} + {part} + + )); +} + +function Byline({ artifact, grouped }: { artifact: LabArtifact; grouped: boolean }) { + return ( +

    + {/* Inside a run section the agent is the section's heading; only the edit time is the card's own. */} + {grouped ? null : {artifact.author}} + +

    + ); +} + +export function PanelCard({ artifact, grouped }: { artifact: LabArtifact; grouped: boolean }) { + const [failed, setFailed] = useState(false); + const lead = panelLeadKind(artifact, failed); + const shown = lead !== "named"; + const primary = panelAction(artifact); + const secondary = panelSecondary(artifact); + // A link's address is too long for the meta line at this width; it takes the filename's place. + const address = artifact.kind === "link" ? linkLabel(artifact.url) : undefined; + const meta = address ? [kindLabel(artifact)] : metaParts(artifact, { facts: true }); + const { filename } = artifact; + return ( +
  • + {lead === "picture" ? setFailed(true)} /> : lead === "sheet" ? : null} +
    + {lead === "named" ? : null} +
    + {/* Long titles wrap rather than truncate, as the shipped card's do; a third line in a narrow pane. */} +

    {artifact.title}

    + {/* The state mark shares the meta line when both fit and drops under it when they do not; neither truncates the other. */} +
    + + +
    + {artifact.summary ? ( +

    + {artifact.summary} +

    + ) : null} + {filename ? ( + + ) : address ? ( + + {address} + + ) : null} + {showsMime(artifact) ? ( + + {breakable(artifact.contentType ?? "")} + + ) : null} +
    +
    + {/* `mt-auto`: in a wide pane the cards sit two or three to a row and share its height. */} +
    + + {/* If the byline and the actions do not fit one row (a narrow pane), the actions wrap under it, still at the right. */} +
    + {secondary.map((action) => ( + + ))} + 0 ? "ml-1" : undefined} /> +
    +
    +
  • + ); +} diff --git a/ui/src/artifact-directions/directions/d2-tint/PanelIndex.tsx b/ui/src/artifact-directions/directions/d2-tint/PanelIndex.tsx new file mode 100644 index 00000000000..6e62dfbf1ec --- /dev/null +++ b/ui/src/artifact-directions/directions/d2-tint/PanelIndex.tsx @@ -0,0 +1,99 @@ +import { useState, type ReactNode } from "react"; +import { cn } from "@/lib/utils"; +import type { LabArtifact, LabKind } from "../../kit"; +import { KindGlyph } from "./KindGlyph"; +import { StateDot } from "./StateMark"; +import { FOCUS_RING, MOTION } from "./UnitAction"; +import { countReview, kindNoun, tallyKinds, type KindTally, type PanelFilter } from "./panel-model"; + +/** + * PANEL — the shelf's index: a FILTER, and built to look like one. A row of + * buttons — All, then what is waiting for review (the ring the cards use), + * then the kinds that hold two or more artifacts (mini glyph + count), then + * one "+N" button that opens the rest. Held to one row at the pane's width, + * pressed or not; in a pane dragged narrow every kind folds into the "+N". + * + * One button is pressed at a time, and pressed is unmistakable without hue: + * the foreground inversion the primary button uses, with the kind's name + * spelled out ("Images 3"). The only colour in the row is the glyphs. + * + * (The same mini glyph and count, without the button, is a CAPTION on the + * Artifacts page — KindTally.tsx.) + */ + +const CHIP = cn("inline-flex h-7 shrink-0 cursor-pointer items-center gap-1.5 rounded-md px-2 text-xs font-medium leading-none", MOTION, FOCUS_RING); +const REST = "bg-(--tint-slab) text-(--tint-ink-2) hover:bg-(--tint-slab-raised) hover:text-foreground active:bg-(--tint-button)"; +const PRESSED = "bg-foreground text-background active:bg-foreground/85"; + +function Chip({ pressed, onPress, name, kind, className, children }: { pressed?: boolean; onPress: () => void; name: string; kind?: LabKind; className?: string; children: ReactNode }) { + return ( + // The accessible name starts with the words on the button, so speech input can find it. + + ); +} + +// What one row holds at the pane's 434px: five kind chips at rest, three while one of them is spelled out. +const FRONT = 5; +const FRONT_PRESSED = 3; + +/** + * The kinds that get a chip in the row. Kinds with two or more lead; singles + * wait behind "+N", and so does the tail of the row while a pressed chip needs + * the room for its name. The pressed kind is always in the row. + */ +function frontKinds(kinds: KindTally[], filter: PanelFilter, open: boolean): KindTally[] { + if (open) return kinds; + const pressed = kinds.find((t) => t.kind === filter); + const room = pressed ? FRONT_PRESSED : FRONT; + const lead = kinds.filter((t) => t.count > 1 && t !== pressed).slice(0, pressed ? room - 1 : room); + return kinds.filter((t) => t === pressed || lead.includes(t)); +} + +const Count = ({ value }: { value: number }) => {value}; + +export function PanelIndex({ artifacts, filter, onFilter }: { artifacts: LabArtifact[]; filter: PanelFilter; onFilter: (filter: PanelFilter) => void }) { + const [open, setOpen] = useState(false); + const review = countReview(artifacts); + const kinds = tallyKinds(artifacts); + if (kinds.length < 2 && review === 0) return null; + const toggle = (next: PanelFilter) => onFilter(filter === next ? "all" : next); + const shown = frontKinds(kinds, filter, open); + const behind = kinds.length - shown.length; + return ( +
    + onFilter("all")} name={`All ${artifacts.length} artifacts`}> + All + + {review > 0 ? ( + toggle("review")} name={`Review ${review} waiting`}> + + Review + + ) : null} + {shown.map(({ kind, count }) => { + const pressed = filter === kind; + return ( + // In a narrow pane only the pressed kind and an opened row keep their chips. + toggle(kind)} name={`${kindNoun(kind, count)} ${count}`} className={cn("gap-1 px-1.5", pressed || open ? null : "@max-xs/panel:hidden")}> + + {pressed ? kindNoun(kind, count) : null} + + ); + })} + {open ? null : ( + <> + {behind > 0 ? ( + setOpen(true)} name={`+${behind} more kinds`} className="@max-xs/panel:hidden"> + +{behind} + + ) : null} + setOpen(true)} name={`+${kinds.length} kinds`} className="hidden @max-xs/panel:inline-flex"> + +{kinds.length} kinds + + + )} +
    + ); +} diff --git a/ui/src/artifact-directions/directions/d2-tint/PanelLead.tsx b/ui/src/artifact-directions/directions/d2-tint/PanelLead.tsx new file mode 100644 index 00000000000..6fa386d0780 --- /dev/null +++ b/ui/src/artifact-directions/directions/d2-tint/PanelLead.tsx @@ -0,0 +1,79 @@ +import { useState } from "react"; +import { cn } from "@/lib/utils"; +import type { LabArtifact } from "../../kit"; +import { CodeLines, ProseExcerpt, SheetTable } from "./Content"; +import { FoldedPreview, FoldedSheet, KindWell, PlayMark } from "./Lead"; +import { MOTION } from "./UnitAction"; +import { hasTextPreview } from "./kinds"; + +/** + * PANEL — the lead of a card, by the one rule (Lead.tsx): + * + * picture image, video poster, link screenshot — the full width of the + * card, the whole picture (never cropped: this is where it is + * judged), fold lg and tab. A video plays in place: the poster is + * a real button, and pressing it swaps in the player with controls. + * sheet document, Markdown, sheet, code — the artifact's own text at full + * width on a neutral sheet, fold md and tab: five lines of a + * document, header + three rows of a sheet, five lines of code. + * named PDF, slides, archive, generic file, a picture that failed — the md + * glyph on its kind well, 112px wide, as tall as the words beside it. + */ + +type PanelLeadKind = "picture" | "sheet" | "named"; + +export function panelLeadKind(a: LabArtifact, failed: boolean): PanelLeadKind { + if (a.thumbnailUrl && !failed) return "picture"; + return hasTextPreview(a) ? "sheet" : "named"; +} + +/** The picture's own shape, held between 4:3 and 16:9; anything outside that is shown whole on the neutral well. */ +function pictureBox(aspect: number | undefined): string { + if (aspect == null || aspect >= 1.7) return "aspect-video"; + return aspect >= 1.45 ? "aspect-3/2" : "aspect-4/3"; +} + +const PLAY_RING = "focus-visible:outline-2 focus-visible:-outline-offset-4 focus-visible:outline-(--tint-on-kind)"; +const DURATION = "absolute bottom-2 right-2 rounded-(--rad-4) bg-(--tint-scrim) px-1.5 py-1 font-mono text-(length:--text-micro) leading-none text-(--tint-on-kind)"; + +export function PanelPicture({ artifact, onError }: { artifact: LabArtifact; onError: () => void }) { + const [playing, setPlaying] = useState(false); + const box = cn("w-full", pictureBox(artifact.aspect)); + const video = artifact.kind === "video"; + if (video && playing && artifact.videoUrl) { + // Above the card's stretched action, so the player's own controls work. + return