diff --git a/cli/src/__tests__/company.test.ts b/cli/src/__tests__/company.test.ts
index 268d1266..922e95c7 100644
--- a/cli/src/__tests__/company.test.ts
+++ b/cli/src/__tests__/company.test.ts
@@ -220,6 +220,7 @@ describe("renderCompanyImportPreview", () => {
status: null,
executionWorkspacePolicy: null,
workspaces: [],
+ env: null,
metadata: null,
},
],
@@ -250,6 +251,7 @@ describe("renderCompanyImportPreview", () => {
key: "OPENAI_API_KEY",
description: null,
agentSlug: "ceo",
+ projectSlug: null,
kind: "secret",
requirement: "required",
defaultValue: null,
@@ -265,6 +267,7 @@ describe("renderCompanyImportPreview", () => {
key: "OPENAI_API_KEY",
description: null,
agentSlug: "ceo",
+ projectSlug: null,
kind: "secret",
requirement: "required",
defaultValue: null,
@@ -432,6 +435,7 @@ describe("import selection catalog", () => {
status: null,
executionWorkspacePolicy: null,
workspaces: [],
+ env: null,
metadata: null,
},
],
diff --git a/doc/SPEC-implementation.md b/doc/SPEC-implementation.md
index 7838de5e..964684dd 100644
--- a/doc/SPEC-implementation.md
+++ b/doc/SPEC-implementation.md
@@ -184,6 +184,11 @@ Invariant: at least one root `company` level goal per company.
- `status` enum: `backlog | planned | in_progress | completed | cancelled`
- `lead_agent_id` uuid fk `agents.id` null
- `target_date` date null
+- `env` jsonb null (same secret-aware env binding format used by agent config)
+
+Invariant:
+
+- project env is merged into run environment for issues in that project and overrides conflicting agent env keys before Paperclip runtime-owned keys are injected
## 7.6 `issues` (core task entity)
diff --git a/doc/plans/2026-04-06-smart-model-routing.md b/doc/plans/2026-04-06-smart-model-routing.md
new file mode 100644
index 00000000..cf26913c
--- /dev/null
+++ b/doc/plans/2026-04-06-smart-model-routing.md
@@ -0,0 +1,362 @@
+# 2026-04-06 Smart Model Routing
+
+Status: Proposed
+Date: 2026-04-06
+Audience: Product and engineering
+Related:
+- `doc/SPEC-implementation.md`
+- `doc/PRODUCT.md`
+- `doc/plans/2026-03-14-adapter-skill-sync-rollout.md`
+
+## 1. Purpose
+
+This document defines a V1 plan for "smart model routing" in Paperclip.
+
+The goal is not to build a generic cross-provider router in the server. The goal is:
+
+- let supported adapters use a cheaper model for lightweight heartbeat orchestration work
+- keep the main task execution on the adapter's normal primary model
+- preserve Paperclip's existing task, session, and audit invariants
+- report cost and model usage truthfully when more than one model participates in a single heartbeat
+
+The motivating use case is a local coding adapter where a cheap model can handle the first fast pass:
+
+- read the wake context
+- orient to the task and workspace
+- leave an immediate progress comment when appropriate
+- perform bounded lightweight triage
+
+Then the primary model does the substantive work.
+
+## 2. Hermes Findings
+
+Hermes does have a real "smart model routing" feature, but it is narrower than the name suggests.
+
+Observed behavior:
+
+- `agent/smart_model_routing.py` implements a conservative classifier for "simple" turns
+- the cheap path only triggers for short, single-line, non-code, non-URL, non-tool-heavy messages
+- complexity is detected with hardcoded thresholds plus a keyword denylist like `debug`, `implement`, `test`, `plan`, `tool`, `docker`, and similar terms
+- if the cheap route cannot be resolved, Hermes silently falls back to the primary model
+
+Important architectural detail:
+
+- Hermes applies this routing before constructing the agent for that turn
+- the route is resolved in `cron/scheduler.py` and passed into agent creation as the active provider/model/runtime
+
+More useful than the routing heuristic itself is Hermes' broader model-slot design:
+
+- main conversational model
+- fallback model for failover
+- auxiliary model slots for side tasks like compression and classification
+
+That separation is a better fit for Paperclip than copying Hermes' exact keyword heuristic.
+
+## 3. Current Paperclip State
+
+Paperclip already has the right execution shape for adapter-specific routing, but it currently assumes one model per heartbeat run.
+
+Current implementation facts:
+
+- `server/src/services/heartbeat.ts` builds rich run context, including `paperclipWake`, workspace metadata, and session handoff context
+- each adapter receives a single resolved `config` object and executes once
+- built-in local adapters read one `config.model` and pass it directly to the underlying CLI
+- UI config today exposes one main `model` field plus adapter-specific thinking-effort controls
+- cost accounting currently records one provider/model tuple per run via `AdapterExecutionResult`
+
+What this means:
+
+- there is no shared routing layer in the server today
+- model choice already lives at the adapter boundary, which is good
+- multi-model execution in a single heartbeat needs explicit contract work or cost reporting will become misleading
+
+## 4. Product Decision
+
+Paperclip should implement smart model routing as an adapter-local, opt-in execution pattern.
+
+V1 decision:
+
+1. Do not add a global server-side router that tries to understand every adapter.
+2. Do not copy Hermes' prompt-keyword classifier as Paperclip's default routing policy.
+3. Add an adapter-specific "cheap preflight" phase for supported adapters.
+4. Keep the primary model as the canonical work model.
+5. Persist only the primary session unless an adapter can prove that cross-model session resume is safe.
+
+Rationale:
+
+- Paperclip heartbeats are structured, issue-scoped, and already include wake metadata
+- routing by execution phase is more reliable than routing by free-text prompt complexity
+- session semantics differ by adapter, so resume behavior must stay adapter-owned
+
+## 5. Proposed V1 Behavior
+
+## 5.1 Config shape
+
+Supported adapters should add an optional routing block to `adapterConfig`.
+
+Proposed shape:
+
+```ts
+smartModelRouting?: {
+ enabled: boolean;
+ cheapModel: string;
+ cheapThinkingEffort?: string;
+ maxPreflightTurns?: number;
+ allowInitialProgressComment?: boolean;
+}
+```
+
+Notes:
+
+- keep existing `model` as the primary model
+- `cheapModel` is adapter-specific, not global
+- adapters that cannot safely support this block simply ignore it
+
+For adapters with provider-specific model fields later, the shape can expand to include provider/base-url overrides. V1 should start simple.
+
+## 5.2 Routing policy
+
+Supported adapters should run cheap preflight only when all are true:
+
+- `smartModelRouting.enabled` is true
+- `cheapModel` is configured
+- the run is issue-scoped
+- the adapter is starting a fresh session, not resuming a persisted one
+- the run is expected to do real task work rather than just resume an existing thread
+
+Supported adapters should skip cheap preflight when any are true:
+
+- a persisted task session already exists
+- the adapter cannot safely isolate preflight from the primary session
+- the issue or wake type implies the task is already mid-flight and continuity matters more than first-response speed
+
+This is intentionally phase-based, not text-heuristic-based.
+
+## 5.3 Cheap preflight responsibilities
+
+The cheap phase should be narrow and bounded.
+
+Allowed responsibilities:
+
+- ingest wake context and issue summary
+- inspect the workspace at a shallow level
+- leave a short "starting investigation" style comment when appropriate
+- collect a compact handoff summary for the primary phase
+
+Not allowed in V1:
+
+- long tool loops
+- risky file mutations
+- being the canonical persisted task session
+- deciding final completion without either explicit adapter support or a trivial success case
+
+Implementation detail:
+
+- the adapter should inject an explicit preflight prompt telling the model this is a bounded orchestration pass
+- preflight should use a very small turn budget, for example 1-2 turns
+
+## 5.4 Primary execution responsibilities
+
+After preflight, the adapter launches the normal primary execution using the existing prompt and primary model.
+
+The primary phase should receive:
+
+- the normal Paperclip prompt
+- any preflight-generated handoff summary
+- normal workspace and wake context
+
+The primary phase remains the source of truth for:
+
+- persisted session state
+- final task completion
+- most file changes
+- most cost
+
+## 6. Required Contract Changes
+
+The current `AdapterExecutionResult` is too narrow for truthful multi-model accounting.
+
+Add an optional segmented execution report, for example:
+
+```ts
+executionSegments?: Array<{
+ phase: "cheap_preflight" | "primary";
+ provider?: string | null;
+ biller?: string | null;
+ model?: string | null;
+ billingType?: AdapterBillingType | null;
+ usage?: UsageSummary;
+ costUsd?: number | null;
+ summary?: string | null;
+}>
+```
+
+V1 server behavior:
+
+- if `executionSegments` is absent, keep current single-result behavior unchanged
+- if present, write one `cost_events` row per segment that has cost or token usage
+- store the segment array in run usage/result metadata for later UI inspection
+- keep the existing top-level `provider` / `model` fields as a summary, preferably the primary phase when present
+
+This avoids breaking existing adapters while giving routed adapters truthful reporting.
+
+## 7. Adapter Rollout Plan
+
+## 7.1 Phase 1: contract and server plumbing
+
+Work:
+
+1. Extend adapter result types with segmented execution metadata.
+2. Update heartbeat cost recording to emit multiple cost events when segments are present.
+3. Include segment summaries in run metadata for transcript/debug views.
+
+Success criteria:
+
+- existing adapters behave exactly as before
+- a routed adapter can report cheap plus primary usage without collapsing them into one fake model
+
+## 7.2 Phase 2: `codex_local`
+
+Why first:
+
+- Codex already has rich prompt/handoff handling
+- the adapter already injects Paperclip skills and workspace metadata cleanly
+- the current implementation already distinguishes bootstrap, wake delta, and handoff prompt sections
+
+Implementation work:
+
+1. Add config support for `smartModelRouting`.
+2. Add a cheap-preflight prompt builder.
+3. Run cheap preflight only on fresh sessions.
+4. Pass a compact preflight handoff note into the primary prompt.
+5. Report segmented usage and model metadata.
+
+Important guardrail:
+
+- do not resume the cheap-model session as the primary session in V1
+
+## 7.3 Phase 3: `claude_local`
+
+Implementation work is similar, but the session model-switch risk is even less attractive.
+
+Same rule:
+
+- cheap preflight is ephemeral
+- primary Claude session remains canonical
+
+## 7.4 Phase 4: other adapters
+
+Candidates:
+
+- `cursor`
+- `gemini_local`
+- `opencode_local`
+- external plugin adapters through `createServerAdapter()`
+
+These should come later because each runtime has different session and model-switch semantics.
+
+## 8. UI and Config Changes
+
+For supported built-in adapters, the agent config UI should expose:
+
+- `model` as the primary model
+- `smart model routing` toggle
+- `cheap model`
+- optional cheap thinking effort
+- optional `allow initial progress comment` toggle
+
+The run detail UI should also show when routing occurred, for example:
+
+- cheap preflight model
+- primary model
+- token/cost split
+
+This matters because Paperclip's board UI is supposed to make cost and behavior legible.
+
+## 9. Why Not Copy Hermes Exactly
+
+Hermes' cheap-route heuristic is useful precedent, but Paperclip should not start there.
+
+Reasons:
+
+- Hermes is optimizing free-form conversational turns
+- Paperclip agents run structured, issue-scoped heartbeats with explicit task and workspace context
+- Paperclip already knows whether a run is fresh vs resumed, issue-scoped vs approval follow-up, and what workspace/session exists
+- those execution facts are stronger routing signals than prompt keyword matching
+
+If Paperclip later wants a cheap-only completion path for trivial runs, that can be a second-stage feature built on observed run data, not the first implementation.
+
+## 10. Risks
+
+## 10.1 Duplicate or noisy comments
+
+If the cheap phase posts an update and the primary phase posts another near-identical update, the issue thread gets worse.
+
+Mitigation:
+
+- keep cheap comments optional
+- make the preflight prompt explicitly avoid repeating status if a useful comment was already posted
+
+## 10.2 Misleading cost reporting
+
+If we only record the primary model, the board loses visibility into the routing cost tradeoff.
+
+Mitigation:
+
+- add segmented execution reporting before shipping adapter behavior
+
+## 10.3 Session corruption
+
+Cross-model session reuse may fail or degrade context quality.
+
+Mitigation:
+
+- V1 does not persist or resume cheap preflight sessions
+
+## 10.4 Cheap model overreach
+
+A cheap model with full tools and permissions may do too much low-quality work.
+
+Mitigation:
+
+- hard cap preflight turns
+- use an explicit orchestration-only prompt
+- start with supported adapters where we can test the behavior well
+
+## 11. Verification Plan
+
+Required tests:
+
+- adapter unit tests for route eligibility
+- adapter unit tests for "fresh session -> cheap preflight + primary"
+- adapter unit tests for "resumed session -> primary only"
+- heartbeat tests for segmented cost-event creation
+- UI tests for config save/load of cheap-model fields
+
+Manual checks:
+
+- create a fresh issue for a routed Codex or Claude agent
+- verify the run metadata shows both phases
+- verify only the primary session is persisted
+- verify cost rows reflect both models
+- verify the issue thread does not get duplicate kickoff comments
+
+## 12. Recommended Sequence
+
+1. Add segmented execution reporting to the adapter/server contract.
+2. Implement `codex_local` cheap preflight.
+3. Validate cost visibility and transcript UX.
+4. Implement `claude_local` cheap preflight.
+5. Decide later whether any adapters need Hermes-style text heuristics in addition to phase-based routing.
+
+## 13. Recommendation
+
+Paperclip should ship smart model routing as:
+
+- adapter-specific
+- opt-in
+- phase-based
+- session-safe
+- cost-truthful
+
+The right V1 is not "choose the cheapest model for simple prompts." The right V1 is "use a cheap model for bounded orchestration work on fresh runs, then hand off to the primary model for the real task."
diff --git a/packages/db/src/backup-lib.test.ts b/packages/db/src/backup-lib.test.ts
index 4c59216b..dcdc87c5 100644
--- a/packages/db/src/backup-lib.test.ts
+++ b/packages/db/src/backup-lib.test.ts
@@ -176,4 +176,49 @@ describeEmbeddedPostgres("runDatabaseBackup", () => {
},
60_000,
);
+
+ it(
+ "restores statements incrementally when backup comments precede the first breakpoint",
+ async () => {
+ const restoreConnectionString = await createTempDatabase();
+ const restoreSql = postgres(restoreConnectionString, { max: 1, onnotice: () => {} });
+ const backupDir = createTempDir("paperclip-db-restore-manual-");
+ const backupFile = path.join(backupDir, "manual.sql");
+
+ try {
+ await fs.promises.writeFile(
+ backupFile,
+ [
+ "-- Paperclip database backup",
+ "-- Created: 2026-04-06T00:00:00.000Z",
+ "",
+ "BEGIN;",
+ "-- paperclip statement breakpoint 69f6f3f1-42fd-46a6-bf17-d1d85f8f3900",
+ "CREATE TABLE public.restore_stream_test (id integer primary key, payload text not null);",
+ "-- paperclip statement breakpoint 69f6f3f1-42fd-46a6-bf17-d1d85f8f3900",
+ "INSERT INTO public.restore_stream_test (id, payload)",
+ "VALUES (1, 'hello');",
+ "-- paperclip statement breakpoint 69f6f3f1-42fd-46a6-bf17-d1d85f8f3900",
+ "COMMIT;",
+ "-- paperclip statement breakpoint 69f6f3f1-42fd-46a6-bf17-d1d85f8f3900",
+ ].join("\n"),
+ "utf8",
+ );
+
+ await runDatabaseRestore({
+ connectionString: restoreConnectionString,
+ backupFile,
+ });
+
+ const rows = await restoreSql.unsafe<{ payload: string }[]>(`
+ SELECT payload
+ FROM public.restore_stream_test
+ `);
+ expect(rows).toEqual([{ payload: "hello" }]);
+ } finally {
+ await restoreSql.end();
+ }
+ },
+ 20_000,
+ );
});
diff --git a/packages/db/src/backup-lib.ts b/packages/db/src/backup-lib.ts
index c9415299..ea76a2b6 100644
--- a/packages/db/src/backup-lib.ts
+++ b/packages/db/src/backup-lib.ts
@@ -1,6 +1,6 @@
-import { createWriteStream, existsSync, mkdirSync, readdirSync, statSync, unlinkSync } from "node:fs";
-import { readFile } from "node:fs/promises";
+import { createReadStream, createWriteStream, existsSync, mkdirSync, readdirSync, statSync, unlinkSync } from "node:fs";
import { basename, resolve } from "node:path";
+import { createInterface } from "node:readline";
import postgres from "postgres";
export type RunDatabaseBackupOptions = {
@@ -147,6 +147,42 @@ function tableKey(schemaName: string, tableName: string): string {
return `${schemaName}.${tableName}`;
}
+async function* readRestoreStatements(backupFile: string): AsyncGenerator {
+ const stream = createReadStream(backupFile, { encoding: "utf8" });
+ const reader = createInterface({
+ input: stream,
+ crlfDelay: Infinity,
+ });
+ let statementLines: string[] = [];
+
+ const flushStatement = () => {
+ const statement = statementLines.join("\n").trim();
+ statementLines = [];
+ return statement;
+ };
+
+ try {
+ for await (const line of reader) {
+ if (line === STATEMENT_BREAKPOINT) {
+ const statement = flushStatement();
+ if (statement.length > 0) {
+ yield statement;
+ }
+ continue;
+ }
+ statementLines.push(line);
+ }
+
+ const trailingStatement = flushStatement();
+ if (trailingStatement.length > 0) {
+ yield trailingStatement;
+ }
+ } finally {
+ reader.close();
+ stream.destroy();
+ }
+}
+
export function createBufferedTextFileWriter(filePath: string, maxBufferedBytes = DEFAULT_BACKUP_WRITE_BUFFER_BYTES) {
const stream = createWriteStream(filePath, { encoding: "utf8" });
const flushThreshold = Math.max(1, Math.trunc(maxBufferedBytes));
@@ -650,13 +686,7 @@ export async function runDatabaseRestore(opts: RunDatabaseRestoreOptions): Promi
try {
await sql`SELECT 1`;
- const contents = await readFile(opts.backupFile, "utf8");
- const statements = contents
- .split(STATEMENT_BREAKPOINT)
- .map((statement) => statement.trim())
- .filter((statement) => statement.length > 0);
-
- for (const statement of statements) {
+ for await (const statement of readRestoreStatements(opts.backupFile)) {
await sql.unsafe(statement).execute();
}
} catch (error) {
diff --git a/packages/db/src/client.test.ts b/packages/db/src/client.test.ts
index 64b1caf1..81cc2ace 100644
--- a/packages/db/src/client.test.ts
+++ b/packages/db/src/client.test.ts
@@ -401,4 +401,70 @@ describeEmbeddedPostgres("applyPendingMigrations", () => {
},
20_000,
);
+
+ it(
+ "replays migration 0050 safely when projects.env already exists",
+ async () => {
+ const connectionString = await createTempDatabase();
+
+ await applyPendingMigrations(connectionString);
+
+ const sql = postgres(connectionString, { max: 1, onnotice: () => {} });
+ try {
+ const stiffLuckmanHash = await migrationHash("0050_stiff_luckman.sql");
+
+ await sql.unsafe(
+ `DELETE FROM "drizzle"."__drizzle_migrations" WHERE hash = '${stiffLuckmanHash}'`,
+ );
+
+ const columns = await sql.unsafe<{ column_name: string }[]>(
+ `
+ SELECT column_name
+ FROM information_schema.columns
+ WHERE table_schema = 'public'
+ AND table_name = 'projects'
+ AND column_name = 'env'
+ `,
+ );
+ expect(columns).toHaveLength(1);
+ } finally {
+ await sql.end();
+ }
+
+ const pendingState = await inspectMigrations(connectionString);
+ expect(pendingState).toMatchObject({
+ status: "needsMigrations",
+ pendingMigrations: ["0050_stiff_luckman.sql"],
+ reason: "pending-migrations",
+ });
+
+ await applyPendingMigrations(connectionString);
+
+ const finalState = await inspectMigrations(connectionString);
+ expect(finalState.status).toBe("upToDate");
+
+ const verifySql = postgres(connectionString, { max: 1, onnotice: () => {} });
+ try {
+ const columns = await verifySql.unsafe<{ column_name: string; is_nullable: string; data_type: string }[]>(
+ `
+ SELECT column_name, is_nullable, data_type
+ FROM information_schema.columns
+ WHERE table_schema = 'public'
+ AND table_name = 'projects'
+ AND column_name = 'env'
+ `,
+ );
+ expect(columns).toEqual([
+ expect.objectContaining({
+ column_name: "env",
+ is_nullable: "YES",
+ data_type: "jsonb",
+ }),
+ ]);
+ } finally {
+ await verifySql.end();
+ }
+ },
+ 20_000,
+ );
});
diff --git a/packages/db/src/migrations/0050_stiff_luckman.sql b/packages/db/src/migrations/0050_stiff_luckman.sql
new file mode 100644
index 00000000..25b0741f
--- /dev/null
+++ b/packages/db/src/migrations/0050_stiff_luckman.sql
@@ -0,0 +1 @@
+ALTER TABLE "projects" ADD COLUMN IF NOT EXISTS "env" jsonb;
diff --git a/packages/db/src/migrations/meta/0050_snapshot.json b/packages/db/src/migrations/meta/0050_snapshot.json
new file mode 100644
index 00000000..9c16c2c1
--- /dev/null
+++ b/packages/db/src/migrations/meta/0050_snapshot.json
@@ -0,0 +1,12772 @@
+{
+ "id": "536b076f-aa3e-4c85-bca3-0685df608c6d",
+ "prevId": "08a85437-3008-49a3-af78-d7a1c10a0437",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.activity_log": {
+ "name": "activity_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "actor_type": {
+ "name": "actor_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'system'"
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "entity_type": {
+ "name": "entity_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "entity_id": {
+ "name": "entity_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "run_id": {
+ "name": "run_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "details": {
+ "name": "details",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "activity_log_company_created_idx": {
+ "name": "activity_log_company_created_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "activity_log_run_id_idx": {
+ "name": "activity_log_run_id_idx",
+ "columns": [
+ {
+ "expression": "run_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "activity_log_entity_type_id_idx": {
+ "name": "activity_log_entity_type_id_idx",
+ "columns": [
+ {
+ "expression": "entity_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "entity_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "activity_log_company_id_companies_id_fk": {
+ "name": "activity_log_company_id_companies_id_fk",
+ "tableFrom": "activity_log",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "activity_log_agent_id_agents_id_fk": {
+ "name": "activity_log_agent_id_agents_id_fk",
+ "tableFrom": "activity_log",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "activity_log_run_id_heartbeat_runs_id_fk": {
+ "name": "activity_log_run_id_heartbeat_runs_id_fk",
+ "tableFrom": "activity_log",
+ "tableTo": "heartbeat_runs",
+ "columnsFrom": [
+ "run_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agent_api_keys": {
+ "name": "agent_api_keys",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key_hash": {
+ "name": "key_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "agent_api_keys_key_hash_idx": {
+ "name": "agent_api_keys_key_hash_idx",
+ "columns": [
+ {
+ "expression": "key_hash",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "agent_api_keys_company_agent_idx": {
+ "name": "agent_api_keys_company_agent_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "agent_api_keys_agent_id_agents_id_fk": {
+ "name": "agent_api_keys_agent_id_agents_id_fk",
+ "tableFrom": "agent_api_keys",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "agent_api_keys_company_id_companies_id_fk": {
+ "name": "agent_api_keys_company_id_companies_id_fk",
+ "tableFrom": "agent_api_keys",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agent_config_revisions": {
+ "name": "agent_config_revisions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_agent_id": {
+ "name": "created_by_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'patch'"
+ },
+ "rolled_back_from_revision_id": {
+ "name": "rolled_back_from_revision_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "changed_keys": {
+ "name": "changed_keys",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "before_config": {
+ "name": "before_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "after_config": {
+ "name": "after_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "agent_config_revisions_company_agent_created_idx": {
+ "name": "agent_config_revisions_company_agent_created_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "agent_config_revisions_agent_created_idx": {
+ "name": "agent_config_revisions_agent_created_idx",
+ "columns": [
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "agent_config_revisions_company_id_companies_id_fk": {
+ "name": "agent_config_revisions_company_id_companies_id_fk",
+ "tableFrom": "agent_config_revisions",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "agent_config_revisions_agent_id_agents_id_fk": {
+ "name": "agent_config_revisions_agent_id_agents_id_fk",
+ "tableFrom": "agent_config_revisions",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "agent_config_revisions_created_by_agent_id_agents_id_fk": {
+ "name": "agent_config_revisions_created_by_agent_id_agents_id_fk",
+ "tableFrom": "agent_config_revisions",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "created_by_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agent_runtime_state": {
+ "name": "agent_runtime_state",
+ "schema": "",
+ "columns": {
+ "agent_id": {
+ "name": "agent_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "adapter_type": {
+ "name": "adapter_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "session_id": {
+ "name": "session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "state_json": {
+ "name": "state_json",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "last_run_id": {
+ "name": "last_run_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_run_status": {
+ "name": "last_run_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_input_tokens": {
+ "name": "total_input_tokens",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "total_output_tokens": {
+ "name": "total_output_tokens",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "total_cached_input_tokens": {
+ "name": "total_cached_input_tokens",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "total_cost_cents": {
+ "name": "total_cost_cents",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "agent_runtime_state_company_agent_idx": {
+ "name": "agent_runtime_state_company_agent_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "agent_runtime_state_company_updated_idx": {
+ "name": "agent_runtime_state_company_updated_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "agent_runtime_state_agent_id_agents_id_fk": {
+ "name": "agent_runtime_state_agent_id_agents_id_fk",
+ "tableFrom": "agent_runtime_state",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "agent_runtime_state_company_id_companies_id_fk": {
+ "name": "agent_runtime_state_company_id_companies_id_fk",
+ "tableFrom": "agent_runtime_state",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agent_task_sessions": {
+ "name": "agent_task_sessions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "adapter_type": {
+ "name": "adapter_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "task_key": {
+ "name": "task_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "session_params_json": {
+ "name": "session_params_json",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "session_display_id": {
+ "name": "session_display_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_run_id": {
+ "name": "last_run_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "agent_task_sessions_company_agent_adapter_task_uniq": {
+ "name": "agent_task_sessions_company_agent_adapter_task_uniq",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "adapter_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "task_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "agent_task_sessions_company_agent_updated_idx": {
+ "name": "agent_task_sessions_company_agent_updated_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "agent_task_sessions_company_task_updated_idx": {
+ "name": "agent_task_sessions_company_task_updated_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "task_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "agent_task_sessions_company_id_companies_id_fk": {
+ "name": "agent_task_sessions_company_id_companies_id_fk",
+ "tableFrom": "agent_task_sessions",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "agent_task_sessions_agent_id_agents_id_fk": {
+ "name": "agent_task_sessions_agent_id_agents_id_fk",
+ "tableFrom": "agent_task_sessions",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "agent_task_sessions_last_run_id_heartbeat_runs_id_fk": {
+ "name": "agent_task_sessions_last_run_id_heartbeat_runs_id_fk",
+ "tableFrom": "agent_task_sessions",
+ "tableTo": "heartbeat_runs",
+ "columnsFrom": [
+ "last_run_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agent_wakeup_requests": {
+ "name": "agent_wakeup_requests",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "trigger_detail": {
+ "name": "trigger_detail",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "reason": {
+ "name": "reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'queued'"
+ },
+ "coalesced_count": {
+ "name": "coalesced_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "requested_by_actor_type": {
+ "name": "requested_by_actor_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "requested_by_actor_id": {
+ "name": "requested_by_actor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "idempotency_key": {
+ "name": "idempotency_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "run_id": {
+ "name": "run_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "requested_at": {
+ "name": "requested_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "claimed_at": {
+ "name": "claimed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "finished_at": {
+ "name": "finished_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error": {
+ "name": "error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "agent_wakeup_requests_company_agent_status_idx": {
+ "name": "agent_wakeup_requests_company_agent_status_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "agent_wakeup_requests_company_requested_idx": {
+ "name": "agent_wakeup_requests_company_requested_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "requested_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "agent_wakeup_requests_agent_requested_idx": {
+ "name": "agent_wakeup_requests_agent_requested_idx",
+ "columns": [
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "requested_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "agent_wakeup_requests_company_id_companies_id_fk": {
+ "name": "agent_wakeup_requests_company_id_companies_id_fk",
+ "tableFrom": "agent_wakeup_requests",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "agent_wakeup_requests_agent_id_agents_id_fk": {
+ "name": "agent_wakeup_requests_agent_id_agents_id_fk",
+ "tableFrom": "agent_wakeup_requests",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agents": {
+ "name": "agents",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'general'"
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "icon": {
+ "name": "icon",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'idle'"
+ },
+ "reports_to": {
+ "name": "reports_to",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "capabilities": {
+ "name": "capabilities",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "adapter_type": {
+ "name": "adapter_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'process'"
+ },
+ "adapter_config": {
+ "name": "adapter_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "runtime_config": {
+ "name": "runtime_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "budget_monthly_cents": {
+ "name": "budget_monthly_cents",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "spent_monthly_cents": {
+ "name": "spent_monthly_cents",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "pause_reason": {
+ "name": "pause_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "paused_at": {
+ "name": "paused_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "permissions": {
+ "name": "permissions",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "last_heartbeat_at": {
+ "name": "last_heartbeat_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "agents_company_status_idx": {
+ "name": "agents_company_status_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "agents_company_reports_to_idx": {
+ "name": "agents_company_reports_to_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "reports_to",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "agents_company_id_companies_id_fk": {
+ "name": "agents_company_id_companies_id_fk",
+ "tableFrom": "agents",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "agents_reports_to_agents_id_fk": {
+ "name": "agents_reports_to_agents_id_fk",
+ "tableFrom": "agents",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "reports_to"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.approval_comments": {
+ "name": "approval_comments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "approval_id": {
+ "name": "approval_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "author_agent_id": {
+ "name": "author_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "author_user_id": {
+ "name": "author_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "approval_comments_company_idx": {
+ "name": "approval_comments_company_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "approval_comments_approval_idx": {
+ "name": "approval_comments_approval_idx",
+ "columns": [
+ {
+ "expression": "approval_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "approval_comments_approval_created_idx": {
+ "name": "approval_comments_approval_created_idx",
+ "columns": [
+ {
+ "expression": "approval_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "approval_comments_company_id_companies_id_fk": {
+ "name": "approval_comments_company_id_companies_id_fk",
+ "tableFrom": "approval_comments",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "approval_comments_approval_id_approvals_id_fk": {
+ "name": "approval_comments_approval_id_approvals_id_fk",
+ "tableFrom": "approval_comments",
+ "tableTo": "approvals",
+ "columnsFrom": [
+ "approval_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "approval_comments_author_agent_id_agents_id_fk": {
+ "name": "approval_comments_author_agent_id_agents_id_fk",
+ "tableFrom": "approval_comments",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "author_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.approvals": {
+ "name": "approvals",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "requested_by_agent_id": {
+ "name": "requested_by_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "requested_by_user_id": {
+ "name": "requested_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "decision_note": {
+ "name": "decision_note",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "decided_by_user_id": {
+ "name": "decided_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "decided_at": {
+ "name": "decided_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "approvals_company_status_type_idx": {
+ "name": "approvals_company_status_type_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "approvals_company_id_companies_id_fk": {
+ "name": "approvals_company_id_companies_id_fk",
+ "tableFrom": "approvals",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "approvals_requested_by_agent_id_agents_id_fk": {
+ "name": "approvals_requested_by_agent_id_agents_id_fk",
+ "tableFrom": "approvals",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "requested_by_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.assets": {
+ "name": "assets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "object_key": {
+ "name": "object_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content_type": {
+ "name": "content_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "byte_size": {
+ "name": "byte_size",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sha256": {
+ "name": "sha256",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "original_filename": {
+ "name": "original_filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_agent_id": {
+ "name": "created_by_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "assets_company_created_idx": {
+ "name": "assets_company_created_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "assets_company_provider_idx": {
+ "name": "assets_company_provider_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "assets_company_object_key_uq": {
+ "name": "assets_company_object_key_uq",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "object_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "assets_company_id_companies_id_fk": {
+ "name": "assets_company_id_companies_id_fk",
+ "tableFrom": "assets",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "assets_created_by_agent_id_agents_id_fk": {
+ "name": "assets_created_by_agent_id_agents_id_fk",
+ "tableFrom": "assets",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "created_by_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.account": {
+ "name": "account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token_expires_at": {
+ "name": "access_token_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token_expires_at": {
+ "name": "refresh_token_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "account_user_id_user_id_fk": {
+ "name": "account_user_id_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.session": {
+ "name": "session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "session_user_id_user_id_fk": {
+ "name": "session_user_id_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.verification": {
+ "name": "verification",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.board_api_keys": {
+ "name": "board_api_keys",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key_hash": {
+ "name": "key_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "board_api_keys_key_hash_idx": {
+ "name": "board_api_keys_key_hash_idx",
+ "columns": [
+ {
+ "expression": "key_hash",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "board_api_keys_user_idx": {
+ "name": "board_api_keys_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "board_api_keys_user_id_user_id_fk": {
+ "name": "board_api_keys_user_id_user_id_fk",
+ "tableFrom": "board_api_keys",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.budget_incidents": {
+ "name": "budget_incidents",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "policy_id": {
+ "name": "policy_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope_type": {
+ "name": "scope_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope_id": {
+ "name": "scope_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "metric": {
+ "name": "metric",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "window_kind": {
+ "name": "window_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "window_start": {
+ "name": "window_start",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "window_end": {
+ "name": "window_end",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "threshold_type": {
+ "name": "threshold_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "amount_limit": {
+ "name": "amount_limit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "amount_observed": {
+ "name": "amount_observed",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'open'"
+ },
+ "approval_id": {
+ "name": "approval_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "budget_incidents_company_status_idx": {
+ "name": "budget_incidents_company_status_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "budget_incidents_company_scope_idx": {
+ "name": "budget_incidents_company_scope_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "scope_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "scope_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "budget_incidents_policy_window_threshold_idx": {
+ "name": "budget_incidents_policy_window_threshold_idx",
+ "columns": [
+ {
+ "expression": "policy_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "window_start",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "threshold_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"budget_incidents\".\"status\" <> 'dismissed'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "budget_incidents_company_id_companies_id_fk": {
+ "name": "budget_incidents_company_id_companies_id_fk",
+ "tableFrom": "budget_incidents",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "budget_incidents_policy_id_budget_policies_id_fk": {
+ "name": "budget_incidents_policy_id_budget_policies_id_fk",
+ "tableFrom": "budget_incidents",
+ "tableTo": "budget_policies",
+ "columnsFrom": [
+ "policy_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "budget_incidents_approval_id_approvals_id_fk": {
+ "name": "budget_incidents_approval_id_approvals_id_fk",
+ "tableFrom": "budget_incidents",
+ "tableTo": "approvals",
+ "columnsFrom": [
+ "approval_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.budget_policies": {
+ "name": "budget_policies",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope_type": {
+ "name": "scope_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope_id": {
+ "name": "scope_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "metric": {
+ "name": "metric",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'billed_cents'"
+ },
+ "window_kind": {
+ "name": "window_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "amount": {
+ "name": "amount",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "warn_percent": {
+ "name": "warn_percent",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 80
+ },
+ "hard_stop_enabled": {
+ "name": "hard_stop_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "notify_enabled": {
+ "name": "notify_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_by_user_id": {
+ "name": "updated_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "budget_policies_company_scope_active_idx": {
+ "name": "budget_policies_company_scope_active_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "scope_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "scope_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "is_active",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "budget_policies_company_window_idx": {
+ "name": "budget_policies_company_window_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "window_kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "metric",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "budget_policies_company_scope_metric_unique_idx": {
+ "name": "budget_policies_company_scope_metric_unique_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "scope_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "scope_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "metric",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "window_kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "budget_policies_company_id_companies_id_fk": {
+ "name": "budget_policies_company_id_companies_id_fk",
+ "tableFrom": "budget_policies",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.cli_auth_challenges": {
+ "name": "cli_auth_challenges",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "secret_hash": {
+ "name": "secret_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "command": {
+ "name": "command",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "client_name": {
+ "name": "client_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "requested_access": {
+ "name": "requested_access",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'board'"
+ },
+ "requested_company_id": {
+ "name": "requested_company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pending_key_hash": {
+ "name": "pending_key_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "pending_key_name": {
+ "name": "pending_key_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "approved_by_user_id": {
+ "name": "approved_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "board_api_key_id": {
+ "name": "board_api_key_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "approved_at": {
+ "name": "approved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cancelled_at": {
+ "name": "cancelled_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "cli_auth_challenges_secret_hash_idx": {
+ "name": "cli_auth_challenges_secret_hash_idx",
+ "columns": [
+ {
+ "expression": "secret_hash",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "cli_auth_challenges_approved_by_idx": {
+ "name": "cli_auth_challenges_approved_by_idx",
+ "columns": [
+ {
+ "expression": "approved_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "cli_auth_challenges_requested_company_idx": {
+ "name": "cli_auth_challenges_requested_company_idx",
+ "columns": [
+ {
+ "expression": "requested_company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "cli_auth_challenges_requested_company_id_companies_id_fk": {
+ "name": "cli_auth_challenges_requested_company_id_companies_id_fk",
+ "tableFrom": "cli_auth_challenges",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "requested_company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "cli_auth_challenges_approved_by_user_id_user_id_fk": {
+ "name": "cli_auth_challenges_approved_by_user_id_user_id_fk",
+ "tableFrom": "cli_auth_challenges",
+ "tableTo": "user",
+ "columnsFrom": [
+ "approved_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "cli_auth_challenges_board_api_key_id_board_api_keys_id_fk": {
+ "name": "cli_auth_challenges_board_api_key_id_board_api_keys_id_fk",
+ "tableFrom": "cli_auth_challenges",
+ "tableTo": "board_api_keys",
+ "columnsFrom": [
+ "board_api_key_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.companies": {
+ "name": "companies",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "pause_reason": {
+ "name": "pause_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "paused_at": {
+ "name": "paused_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "issue_prefix": {
+ "name": "issue_prefix",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'PAP'"
+ },
+ "issue_counter": {
+ "name": "issue_counter",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "budget_monthly_cents": {
+ "name": "budget_monthly_cents",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "spent_monthly_cents": {
+ "name": "spent_monthly_cents",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "require_board_approval_for_new_agents": {
+ "name": "require_board_approval_for_new_agents",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "feedback_data_sharing_enabled": {
+ "name": "feedback_data_sharing_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "feedback_data_sharing_consent_at": {
+ "name": "feedback_data_sharing_consent_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "feedback_data_sharing_consent_by_user_id": {
+ "name": "feedback_data_sharing_consent_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "feedback_data_sharing_terms_version": {
+ "name": "feedback_data_sharing_terms_version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "brand_color": {
+ "name": "brand_color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "companies_issue_prefix_idx": {
+ "name": "companies_issue_prefix_idx",
+ "columns": [
+ {
+ "expression": "issue_prefix",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.company_logos": {
+ "name": "company_logos",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "asset_id": {
+ "name": "asset_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "company_logos_company_uq": {
+ "name": "company_logos_company_uq",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "company_logos_asset_uq": {
+ "name": "company_logos_asset_uq",
+ "columns": [
+ {
+ "expression": "asset_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "company_logos_company_id_companies_id_fk": {
+ "name": "company_logos_company_id_companies_id_fk",
+ "tableFrom": "company_logos",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "company_logos_asset_id_assets_id_fk": {
+ "name": "company_logos_asset_id_assets_id_fk",
+ "tableFrom": "company_logos",
+ "tableTo": "assets",
+ "columnsFrom": [
+ "asset_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.company_memberships": {
+ "name": "company_memberships",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "principal_type": {
+ "name": "principal_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "principal_id": {
+ "name": "principal_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "membership_role": {
+ "name": "membership_role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "company_memberships_company_principal_unique_idx": {
+ "name": "company_memberships_company_principal_unique_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "principal_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "principal_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "company_memberships_principal_status_idx": {
+ "name": "company_memberships_principal_status_idx",
+ "columns": [
+ {
+ "expression": "principal_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "principal_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "company_memberships_company_status_idx": {
+ "name": "company_memberships_company_status_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "company_memberships_company_id_companies_id_fk": {
+ "name": "company_memberships_company_id_companies_id_fk",
+ "tableFrom": "company_memberships",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.company_secret_versions": {
+ "name": "company_secret_versions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "secret_id": {
+ "name": "secret_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "version": {
+ "name": "version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "material": {
+ "name": "material",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value_sha256": {
+ "name": "value_sha256",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_agent_id": {
+ "name": "created_by_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "company_secret_versions_secret_idx": {
+ "name": "company_secret_versions_secret_idx",
+ "columns": [
+ {
+ "expression": "secret_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "company_secret_versions_value_sha256_idx": {
+ "name": "company_secret_versions_value_sha256_idx",
+ "columns": [
+ {
+ "expression": "value_sha256",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "company_secret_versions_secret_version_uq": {
+ "name": "company_secret_versions_secret_version_uq",
+ "columns": [
+ {
+ "expression": "secret_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "version",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "company_secret_versions_secret_id_company_secrets_id_fk": {
+ "name": "company_secret_versions_secret_id_company_secrets_id_fk",
+ "tableFrom": "company_secret_versions",
+ "tableTo": "company_secrets",
+ "columnsFrom": [
+ "secret_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "company_secret_versions_created_by_agent_id_agents_id_fk": {
+ "name": "company_secret_versions_created_by_agent_id_agents_id_fk",
+ "tableFrom": "company_secret_versions",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "created_by_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.company_secrets": {
+ "name": "company_secrets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'local_encrypted'"
+ },
+ "external_ref": {
+ "name": "external_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "latest_version": {
+ "name": "latest_version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_agent_id": {
+ "name": "created_by_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "company_secrets_company_idx": {
+ "name": "company_secrets_company_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "company_secrets_company_provider_idx": {
+ "name": "company_secrets_company_provider_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "company_secrets_company_name_uq": {
+ "name": "company_secrets_company_name_uq",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "company_secrets_company_id_companies_id_fk": {
+ "name": "company_secrets_company_id_companies_id_fk",
+ "tableFrom": "company_secrets",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "company_secrets_created_by_agent_id_agents_id_fk": {
+ "name": "company_secrets_created_by_agent_id_agents_id_fk",
+ "tableFrom": "company_secrets",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "created_by_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.company_skills": {
+ "name": "company_skills",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "markdown": {
+ "name": "markdown",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_type": {
+ "name": "source_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'local_path'"
+ },
+ "source_locator": {
+ "name": "source_locator",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_ref": {
+ "name": "source_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "trust_level": {
+ "name": "trust_level",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'markdown_only'"
+ },
+ "compatibility": {
+ "name": "compatibility",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'compatible'"
+ },
+ "file_inventory": {
+ "name": "file_inventory",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "company_skills_company_key_idx": {
+ "name": "company_skills_company_key_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "company_skills_company_name_idx": {
+ "name": "company_skills_company_name_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "company_skills_company_id_companies_id_fk": {
+ "name": "company_skills_company_id_companies_id_fk",
+ "tableFrom": "company_skills",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.cost_events": {
+ "name": "cost_events",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "goal_id": {
+ "name": "goal_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "heartbeat_run_id": {
+ "name": "heartbeat_run_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "billing_code": {
+ "name": "billing_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "biller": {
+ "name": "biller",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'unknown'"
+ },
+ "billing_type": {
+ "name": "billing_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'unknown'"
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "input_tokens": {
+ "name": "input_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "cached_input_tokens": {
+ "name": "cached_input_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "output_tokens": {
+ "name": "output_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "cost_cents": {
+ "name": "cost_cents",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "occurred_at": {
+ "name": "occurred_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "cost_events_company_occurred_idx": {
+ "name": "cost_events_company_occurred_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "cost_events_company_agent_occurred_idx": {
+ "name": "cost_events_company_agent_occurred_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "cost_events_company_provider_occurred_idx": {
+ "name": "cost_events_company_provider_occurred_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "cost_events_company_biller_occurred_idx": {
+ "name": "cost_events_company_biller_occurred_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "biller",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "cost_events_company_heartbeat_run_idx": {
+ "name": "cost_events_company_heartbeat_run_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "heartbeat_run_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "cost_events_company_id_companies_id_fk": {
+ "name": "cost_events_company_id_companies_id_fk",
+ "tableFrom": "cost_events",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "cost_events_agent_id_agents_id_fk": {
+ "name": "cost_events_agent_id_agents_id_fk",
+ "tableFrom": "cost_events",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "cost_events_issue_id_issues_id_fk": {
+ "name": "cost_events_issue_id_issues_id_fk",
+ "tableFrom": "cost_events",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "issue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "cost_events_project_id_projects_id_fk": {
+ "name": "cost_events_project_id_projects_id_fk",
+ "tableFrom": "cost_events",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "cost_events_goal_id_goals_id_fk": {
+ "name": "cost_events_goal_id_goals_id_fk",
+ "tableFrom": "cost_events",
+ "tableTo": "goals",
+ "columnsFrom": [
+ "goal_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "cost_events_heartbeat_run_id_heartbeat_runs_id_fk": {
+ "name": "cost_events_heartbeat_run_id_heartbeat_runs_id_fk",
+ "tableFrom": "cost_events",
+ "tableTo": "heartbeat_runs",
+ "columnsFrom": [
+ "heartbeat_run_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.document_revisions": {
+ "name": "document_revisions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "document_id": {
+ "name": "document_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "revision_number": {
+ "name": "revision_number",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "format": {
+ "name": "format",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'markdown'"
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "change_summary": {
+ "name": "change_summary",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_agent_id": {
+ "name": "created_by_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_run_id": {
+ "name": "created_by_run_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "document_revisions_document_revision_uq": {
+ "name": "document_revisions_document_revision_uq",
+ "columns": [
+ {
+ "expression": "document_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "revision_number",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "document_revisions_company_document_created_idx": {
+ "name": "document_revisions_company_document_created_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "document_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "document_revisions_company_id_companies_id_fk": {
+ "name": "document_revisions_company_id_companies_id_fk",
+ "tableFrom": "document_revisions",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "document_revisions_document_id_documents_id_fk": {
+ "name": "document_revisions_document_id_documents_id_fk",
+ "tableFrom": "document_revisions",
+ "tableTo": "documents",
+ "columnsFrom": [
+ "document_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "document_revisions_created_by_agent_id_agents_id_fk": {
+ "name": "document_revisions_created_by_agent_id_agents_id_fk",
+ "tableFrom": "document_revisions",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "created_by_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "document_revisions_created_by_run_id_heartbeat_runs_id_fk": {
+ "name": "document_revisions_created_by_run_id_heartbeat_runs_id_fk",
+ "tableFrom": "document_revisions",
+ "tableTo": "heartbeat_runs",
+ "columnsFrom": [
+ "created_by_run_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.documents": {
+ "name": "documents",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "format": {
+ "name": "format",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'markdown'"
+ },
+ "latest_body": {
+ "name": "latest_body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "latest_revision_id": {
+ "name": "latest_revision_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "latest_revision_number": {
+ "name": "latest_revision_number",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "created_by_agent_id": {
+ "name": "created_by_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_by_agent_id": {
+ "name": "updated_by_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_by_user_id": {
+ "name": "updated_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "documents_company_updated_idx": {
+ "name": "documents_company_updated_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "documents_company_created_idx": {
+ "name": "documents_company_created_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "documents_company_id_companies_id_fk": {
+ "name": "documents_company_id_companies_id_fk",
+ "tableFrom": "documents",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "documents_created_by_agent_id_agents_id_fk": {
+ "name": "documents_created_by_agent_id_agents_id_fk",
+ "tableFrom": "documents",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "created_by_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "documents_updated_by_agent_id_agents_id_fk": {
+ "name": "documents_updated_by_agent_id_agents_id_fk",
+ "tableFrom": "documents",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "updated_by_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.execution_workspaces": {
+ "name": "execution_workspaces",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_workspace_id": {
+ "name": "project_workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_issue_id": {
+ "name": "source_issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "mode": {
+ "name": "mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "strategy_type": {
+ "name": "strategy_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "cwd": {
+ "name": "cwd",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "repo_url": {
+ "name": "repo_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "base_ref": {
+ "name": "base_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "branch_name": {
+ "name": "branch_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_type": {
+ "name": "provider_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'local_fs'"
+ },
+ "provider_ref": {
+ "name": "provider_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "derived_from_execution_workspace_id": {
+ "name": "derived_from_execution_workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "opened_at": {
+ "name": "opened_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "closed_at": {
+ "name": "closed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cleanup_eligible_at": {
+ "name": "cleanup_eligible_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cleanup_reason": {
+ "name": "cleanup_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "execution_workspaces_company_project_status_idx": {
+ "name": "execution_workspaces_company_project_status_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "execution_workspaces_company_project_workspace_status_idx": {
+ "name": "execution_workspaces_company_project_workspace_status_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "project_workspace_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "execution_workspaces_company_source_issue_idx": {
+ "name": "execution_workspaces_company_source_issue_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "source_issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "execution_workspaces_company_last_used_idx": {
+ "name": "execution_workspaces_company_last_used_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "last_used_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "execution_workspaces_company_branch_idx": {
+ "name": "execution_workspaces_company_branch_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "branch_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "execution_workspaces_company_id_companies_id_fk": {
+ "name": "execution_workspaces_company_id_companies_id_fk",
+ "tableFrom": "execution_workspaces",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "execution_workspaces_project_id_projects_id_fk": {
+ "name": "execution_workspaces_project_id_projects_id_fk",
+ "tableFrom": "execution_workspaces",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "execution_workspaces_project_workspace_id_project_workspaces_id_fk": {
+ "name": "execution_workspaces_project_workspace_id_project_workspaces_id_fk",
+ "tableFrom": "execution_workspaces",
+ "tableTo": "project_workspaces",
+ "columnsFrom": [
+ "project_workspace_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "execution_workspaces_source_issue_id_issues_id_fk": {
+ "name": "execution_workspaces_source_issue_id_issues_id_fk",
+ "tableFrom": "execution_workspaces",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "source_issue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "execution_workspaces_derived_from_execution_workspace_id_execution_workspaces_id_fk": {
+ "name": "execution_workspaces_derived_from_execution_workspace_id_execution_workspaces_id_fk",
+ "tableFrom": "execution_workspaces",
+ "tableTo": "execution_workspaces",
+ "columnsFrom": [
+ "derived_from_execution_workspace_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.feedback_exports": {
+ "name": "feedback_exports",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "feedback_vote_id": {
+ "name": "feedback_vote_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "author_user_id": {
+ "name": "author_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_type": {
+ "name": "target_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_id": {
+ "name": "target_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "vote": {
+ "name": "vote",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'local_only'"
+ },
+ "destination": {
+ "name": "destination",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "export_id": {
+ "name": "export_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "consent_version": {
+ "name": "consent_version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "schema_version": {
+ "name": "schema_version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'paperclip-feedback-envelope-v2'"
+ },
+ "bundle_version": {
+ "name": "bundle_version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'paperclip-feedback-bundle-v2'"
+ },
+ "payload_version": {
+ "name": "payload_version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'paperclip-feedback-v1'"
+ },
+ "payload_digest": {
+ "name": "payload_digest",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "payload_snapshot": {
+ "name": "payload_snapshot",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "target_summary": {
+ "name": "target_summary",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "redaction_summary": {
+ "name": "redaction_summary",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_attempted_at": {
+ "name": "last_attempted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "exported_at": {
+ "name": "exported_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failure_reason": {
+ "name": "failure_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "feedback_exports_feedback_vote_idx": {
+ "name": "feedback_exports_feedback_vote_idx",
+ "columns": [
+ {
+ "expression": "feedback_vote_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "feedback_exports_company_created_idx": {
+ "name": "feedback_exports_company_created_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "feedback_exports_company_status_idx": {
+ "name": "feedback_exports_company_status_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "feedback_exports_company_issue_idx": {
+ "name": "feedback_exports_company_issue_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "feedback_exports_company_project_idx": {
+ "name": "feedback_exports_company_project_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "feedback_exports_company_author_idx": {
+ "name": "feedback_exports_company_author_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "author_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "feedback_exports_company_id_companies_id_fk": {
+ "name": "feedback_exports_company_id_companies_id_fk",
+ "tableFrom": "feedback_exports",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "feedback_exports_feedback_vote_id_feedback_votes_id_fk": {
+ "name": "feedback_exports_feedback_vote_id_feedback_votes_id_fk",
+ "tableFrom": "feedback_exports",
+ "tableTo": "feedback_votes",
+ "columnsFrom": [
+ "feedback_vote_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "feedback_exports_issue_id_issues_id_fk": {
+ "name": "feedback_exports_issue_id_issues_id_fk",
+ "tableFrom": "feedback_exports",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "issue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "feedback_exports_project_id_projects_id_fk": {
+ "name": "feedback_exports_project_id_projects_id_fk",
+ "tableFrom": "feedback_exports",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.feedback_votes": {
+ "name": "feedback_votes",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_type": {
+ "name": "target_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_id": {
+ "name": "target_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "author_user_id": {
+ "name": "author_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "vote": {
+ "name": "vote",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reason": {
+ "name": "reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "shared_with_labs": {
+ "name": "shared_with_labs",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "shared_at": {
+ "name": "shared_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "consent_version": {
+ "name": "consent_version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "redaction_summary": {
+ "name": "redaction_summary",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "feedback_votes_company_issue_idx": {
+ "name": "feedback_votes_company_issue_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "feedback_votes_issue_target_idx": {
+ "name": "feedback_votes_issue_target_idx",
+ "columns": [
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "target_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "target_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "feedback_votes_author_idx": {
+ "name": "feedback_votes_author_idx",
+ "columns": [
+ {
+ "expression": "author_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "feedback_votes_company_target_author_idx": {
+ "name": "feedback_votes_company_target_author_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "target_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "target_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "author_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "feedback_votes_company_id_companies_id_fk": {
+ "name": "feedback_votes_company_id_companies_id_fk",
+ "tableFrom": "feedback_votes",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "feedback_votes_issue_id_issues_id_fk": {
+ "name": "feedback_votes_issue_id_issues_id_fk",
+ "tableFrom": "feedback_votes",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "issue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.finance_events": {
+ "name": "finance_events",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "goal_id": {
+ "name": "goal_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "heartbeat_run_id": {
+ "name": "heartbeat_run_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cost_event_id": {
+ "name": "cost_event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "billing_code": {
+ "name": "billing_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "event_kind": {
+ "name": "event_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "direction": {
+ "name": "direction",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'debit'"
+ },
+ "biller": {
+ "name": "biller",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "execution_adapter_type": {
+ "name": "execution_adapter_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pricing_tier": {
+ "name": "pricing_tier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "region": {
+ "name": "region",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "quantity": {
+ "name": "quantity",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "unit": {
+ "name": "unit",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "amount_cents": {
+ "name": "amount_cents",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "currency": {
+ "name": "currency",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'USD'"
+ },
+ "estimated": {
+ "name": "estimated",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "external_invoice_id": {
+ "name": "external_invoice_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata_json": {
+ "name": "metadata_json",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "occurred_at": {
+ "name": "occurred_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "finance_events_company_occurred_idx": {
+ "name": "finance_events_company_occurred_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "finance_events_company_biller_occurred_idx": {
+ "name": "finance_events_company_biller_occurred_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "biller",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "finance_events_company_kind_occurred_idx": {
+ "name": "finance_events_company_kind_occurred_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "event_kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "finance_events_company_direction_occurred_idx": {
+ "name": "finance_events_company_direction_occurred_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "direction",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "finance_events_company_heartbeat_run_idx": {
+ "name": "finance_events_company_heartbeat_run_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "heartbeat_run_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "finance_events_company_cost_event_idx": {
+ "name": "finance_events_company_cost_event_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "cost_event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "finance_events_company_id_companies_id_fk": {
+ "name": "finance_events_company_id_companies_id_fk",
+ "tableFrom": "finance_events",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "finance_events_agent_id_agents_id_fk": {
+ "name": "finance_events_agent_id_agents_id_fk",
+ "tableFrom": "finance_events",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "finance_events_issue_id_issues_id_fk": {
+ "name": "finance_events_issue_id_issues_id_fk",
+ "tableFrom": "finance_events",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "issue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "finance_events_project_id_projects_id_fk": {
+ "name": "finance_events_project_id_projects_id_fk",
+ "tableFrom": "finance_events",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "finance_events_goal_id_goals_id_fk": {
+ "name": "finance_events_goal_id_goals_id_fk",
+ "tableFrom": "finance_events",
+ "tableTo": "goals",
+ "columnsFrom": [
+ "goal_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "finance_events_heartbeat_run_id_heartbeat_runs_id_fk": {
+ "name": "finance_events_heartbeat_run_id_heartbeat_runs_id_fk",
+ "tableFrom": "finance_events",
+ "tableTo": "heartbeat_runs",
+ "columnsFrom": [
+ "heartbeat_run_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "finance_events_cost_event_id_cost_events_id_fk": {
+ "name": "finance_events_cost_event_id_cost_events_id_fk",
+ "tableFrom": "finance_events",
+ "tableTo": "cost_events",
+ "columnsFrom": [
+ "cost_event_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.goals": {
+ "name": "goals",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "level": {
+ "name": "level",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'task'"
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'planned'"
+ },
+ "parent_id": {
+ "name": "parent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owner_agent_id": {
+ "name": "owner_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "goals_company_idx": {
+ "name": "goals_company_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "goals_company_id_companies_id_fk": {
+ "name": "goals_company_id_companies_id_fk",
+ "tableFrom": "goals",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "goals_parent_id_goals_id_fk": {
+ "name": "goals_parent_id_goals_id_fk",
+ "tableFrom": "goals",
+ "tableTo": "goals",
+ "columnsFrom": [
+ "parent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "goals_owner_agent_id_agents_id_fk": {
+ "name": "goals_owner_agent_id_agents_id_fk",
+ "tableFrom": "goals",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "owner_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.heartbeat_run_events": {
+ "name": "heartbeat_run_events",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "run_id": {
+ "name": "run_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "seq": {
+ "name": "seq",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_type": {
+ "name": "event_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stream": {
+ "name": "stream",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "level": {
+ "name": "level",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "color": {
+ "name": "color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "message": {
+ "name": "message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "heartbeat_run_events_run_seq_idx": {
+ "name": "heartbeat_run_events_run_seq_idx",
+ "columns": [
+ {
+ "expression": "run_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "seq",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "heartbeat_run_events_company_run_idx": {
+ "name": "heartbeat_run_events_company_run_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "run_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "heartbeat_run_events_company_created_idx": {
+ "name": "heartbeat_run_events_company_created_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "heartbeat_run_events_company_id_companies_id_fk": {
+ "name": "heartbeat_run_events_company_id_companies_id_fk",
+ "tableFrom": "heartbeat_run_events",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "heartbeat_run_events_run_id_heartbeat_runs_id_fk": {
+ "name": "heartbeat_run_events_run_id_heartbeat_runs_id_fk",
+ "tableFrom": "heartbeat_run_events",
+ "tableTo": "heartbeat_runs",
+ "columnsFrom": [
+ "run_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "heartbeat_run_events_agent_id_agents_id_fk": {
+ "name": "heartbeat_run_events_agent_id_agents_id_fk",
+ "tableFrom": "heartbeat_run_events",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.heartbeat_runs": {
+ "name": "heartbeat_runs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "invocation_source": {
+ "name": "invocation_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'on_demand'"
+ },
+ "trigger_detail": {
+ "name": "trigger_detail",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'queued'"
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "finished_at": {
+ "name": "finished_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error": {
+ "name": "error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "wakeup_request_id": {
+ "name": "wakeup_request_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "exit_code": {
+ "name": "exit_code",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "signal": {
+ "name": "signal",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "usage_json": {
+ "name": "usage_json",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "result_json": {
+ "name": "result_json",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "session_id_before": {
+ "name": "session_id_before",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "session_id_after": {
+ "name": "session_id_after",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "log_store": {
+ "name": "log_store",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "log_ref": {
+ "name": "log_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "log_bytes": {
+ "name": "log_bytes",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "log_sha256": {
+ "name": "log_sha256",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "log_compressed": {
+ "name": "log_compressed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "stdout_excerpt": {
+ "name": "stdout_excerpt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stderr_excerpt": {
+ "name": "stderr_excerpt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_code": {
+ "name": "error_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "external_run_id": {
+ "name": "external_run_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "process_pid": {
+ "name": "process_pid",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "process_started_at": {
+ "name": "process_started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "retry_of_run_id": {
+ "name": "retry_of_run_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "process_loss_retry_count": {
+ "name": "process_loss_retry_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "context_snapshot": {
+ "name": "context_snapshot",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "heartbeat_runs_company_agent_started_idx": {
+ "name": "heartbeat_runs_company_agent_started_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "started_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "heartbeat_runs_company_id_companies_id_fk": {
+ "name": "heartbeat_runs_company_id_companies_id_fk",
+ "tableFrom": "heartbeat_runs",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "heartbeat_runs_agent_id_agents_id_fk": {
+ "name": "heartbeat_runs_agent_id_agents_id_fk",
+ "tableFrom": "heartbeat_runs",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "heartbeat_runs_wakeup_request_id_agent_wakeup_requests_id_fk": {
+ "name": "heartbeat_runs_wakeup_request_id_agent_wakeup_requests_id_fk",
+ "tableFrom": "heartbeat_runs",
+ "tableTo": "agent_wakeup_requests",
+ "columnsFrom": [
+ "wakeup_request_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "heartbeat_runs_retry_of_run_id_heartbeat_runs_id_fk": {
+ "name": "heartbeat_runs_retry_of_run_id_heartbeat_runs_id_fk",
+ "tableFrom": "heartbeat_runs",
+ "tableTo": "heartbeat_runs",
+ "columnsFrom": [
+ "retry_of_run_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.instance_settings": {
+ "name": "instance_settings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "singleton_key": {
+ "name": "singleton_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'default'"
+ },
+ "general": {
+ "name": "general",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "experimental": {
+ "name": "experimental",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "instance_settings_singleton_key_idx": {
+ "name": "instance_settings_singleton_key_idx",
+ "columns": [
+ {
+ "expression": "singleton_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.instance_user_roles": {
+ "name": "instance_user_roles",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'instance_admin'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "instance_user_roles_user_role_unique_idx": {
+ "name": "instance_user_roles_user_role_unique_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "role",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "instance_user_roles_role_idx": {
+ "name": "instance_user_roles_role_idx",
+ "columns": [
+ {
+ "expression": "role",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.invites": {
+ "name": "invites",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "invite_type": {
+ "name": "invite_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'company_join'"
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "allowed_join_types": {
+ "name": "allowed_join_types",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'both'"
+ },
+ "defaults_payload": {
+ "name": "defaults_payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "invited_by_user_id": {
+ "name": "invited_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "accepted_at": {
+ "name": "accepted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "invites_token_hash_unique_idx": {
+ "name": "invites_token_hash_unique_idx",
+ "columns": [
+ {
+ "expression": "token_hash",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "invites_company_invite_state_idx": {
+ "name": "invites_company_invite_state_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "invite_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "revoked_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "invites_company_id_companies_id_fk": {
+ "name": "invites_company_id_companies_id_fk",
+ "tableFrom": "invites",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.issue_approvals": {
+ "name": "issue_approvals",
+ "schema": "",
+ "columns": {
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "approval_id": {
+ "name": "approval_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "linked_by_agent_id": {
+ "name": "linked_by_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "linked_by_user_id": {
+ "name": "linked_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "issue_approvals_issue_idx": {
+ "name": "issue_approvals_issue_idx",
+ "columns": [
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_approvals_approval_idx": {
+ "name": "issue_approvals_approval_idx",
+ "columns": [
+ {
+ "expression": "approval_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_approvals_company_idx": {
+ "name": "issue_approvals_company_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "issue_approvals_company_id_companies_id_fk": {
+ "name": "issue_approvals_company_id_companies_id_fk",
+ "tableFrom": "issue_approvals",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "issue_approvals_issue_id_issues_id_fk": {
+ "name": "issue_approvals_issue_id_issues_id_fk",
+ "tableFrom": "issue_approvals",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "issue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "issue_approvals_approval_id_approvals_id_fk": {
+ "name": "issue_approvals_approval_id_approvals_id_fk",
+ "tableFrom": "issue_approvals",
+ "tableTo": "approvals",
+ "columnsFrom": [
+ "approval_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "issue_approvals_linked_by_agent_id_agents_id_fk": {
+ "name": "issue_approvals_linked_by_agent_id_agents_id_fk",
+ "tableFrom": "issue_approvals",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "linked_by_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "issue_approvals_pk": {
+ "name": "issue_approvals_pk",
+ "columns": [
+ "issue_id",
+ "approval_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.issue_attachments": {
+ "name": "issue_attachments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "asset_id": {
+ "name": "asset_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_comment_id": {
+ "name": "issue_comment_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "issue_attachments_company_issue_idx": {
+ "name": "issue_attachments_company_issue_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_attachments_issue_comment_idx": {
+ "name": "issue_attachments_issue_comment_idx",
+ "columns": [
+ {
+ "expression": "issue_comment_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_attachments_asset_uq": {
+ "name": "issue_attachments_asset_uq",
+ "columns": [
+ {
+ "expression": "asset_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "issue_attachments_company_id_companies_id_fk": {
+ "name": "issue_attachments_company_id_companies_id_fk",
+ "tableFrom": "issue_attachments",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "issue_attachments_issue_id_issues_id_fk": {
+ "name": "issue_attachments_issue_id_issues_id_fk",
+ "tableFrom": "issue_attachments",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "issue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "issue_attachments_asset_id_assets_id_fk": {
+ "name": "issue_attachments_asset_id_assets_id_fk",
+ "tableFrom": "issue_attachments",
+ "tableTo": "assets",
+ "columnsFrom": [
+ "asset_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "issue_attachments_issue_comment_id_issue_comments_id_fk": {
+ "name": "issue_attachments_issue_comment_id_issue_comments_id_fk",
+ "tableFrom": "issue_attachments",
+ "tableTo": "issue_comments",
+ "columnsFrom": [
+ "issue_comment_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.issue_comments": {
+ "name": "issue_comments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "author_agent_id": {
+ "name": "author_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "author_user_id": {
+ "name": "author_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_run_id": {
+ "name": "created_by_run_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "issue_comments_issue_idx": {
+ "name": "issue_comments_issue_idx",
+ "columns": [
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_comments_company_idx": {
+ "name": "issue_comments_company_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_comments_company_issue_created_at_idx": {
+ "name": "issue_comments_company_issue_created_at_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_comments_company_author_issue_created_at_idx": {
+ "name": "issue_comments_company_author_issue_created_at_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "author_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "issue_comments_company_id_companies_id_fk": {
+ "name": "issue_comments_company_id_companies_id_fk",
+ "tableFrom": "issue_comments",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "issue_comments_issue_id_issues_id_fk": {
+ "name": "issue_comments_issue_id_issues_id_fk",
+ "tableFrom": "issue_comments",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "issue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "issue_comments_author_agent_id_agents_id_fk": {
+ "name": "issue_comments_author_agent_id_agents_id_fk",
+ "tableFrom": "issue_comments",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "author_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "issue_comments_created_by_run_id_heartbeat_runs_id_fk": {
+ "name": "issue_comments_created_by_run_id_heartbeat_runs_id_fk",
+ "tableFrom": "issue_comments",
+ "tableTo": "heartbeat_runs",
+ "columnsFrom": [
+ "created_by_run_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.issue_documents": {
+ "name": "issue_documents",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "document_id": {
+ "name": "document_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "issue_documents_company_issue_key_uq": {
+ "name": "issue_documents_company_issue_key_uq",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_documents_document_uq": {
+ "name": "issue_documents_document_uq",
+ "columns": [
+ {
+ "expression": "document_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_documents_company_issue_updated_idx": {
+ "name": "issue_documents_company_issue_updated_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "issue_documents_company_id_companies_id_fk": {
+ "name": "issue_documents_company_id_companies_id_fk",
+ "tableFrom": "issue_documents",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "issue_documents_issue_id_issues_id_fk": {
+ "name": "issue_documents_issue_id_issues_id_fk",
+ "tableFrom": "issue_documents",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "issue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "issue_documents_document_id_documents_id_fk": {
+ "name": "issue_documents_document_id_documents_id_fk",
+ "tableFrom": "issue_documents",
+ "tableTo": "documents",
+ "columnsFrom": [
+ "document_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.issue_inbox_archives": {
+ "name": "issue_inbox_archives",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "archived_at": {
+ "name": "archived_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "issue_inbox_archives_company_issue_idx": {
+ "name": "issue_inbox_archives_company_issue_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_inbox_archives_company_user_idx": {
+ "name": "issue_inbox_archives_company_user_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_inbox_archives_company_issue_user_idx": {
+ "name": "issue_inbox_archives_company_issue_user_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "issue_inbox_archives_company_id_companies_id_fk": {
+ "name": "issue_inbox_archives_company_id_companies_id_fk",
+ "tableFrom": "issue_inbox_archives",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "issue_inbox_archives_issue_id_issues_id_fk": {
+ "name": "issue_inbox_archives_issue_id_issues_id_fk",
+ "tableFrom": "issue_inbox_archives",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "issue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.issue_labels": {
+ "name": "issue_labels",
+ "schema": "",
+ "columns": {
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label_id": {
+ "name": "label_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "issue_labels_issue_idx": {
+ "name": "issue_labels_issue_idx",
+ "columns": [
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_labels_label_idx": {
+ "name": "issue_labels_label_idx",
+ "columns": [
+ {
+ "expression": "label_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_labels_company_idx": {
+ "name": "issue_labels_company_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "issue_labels_issue_id_issues_id_fk": {
+ "name": "issue_labels_issue_id_issues_id_fk",
+ "tableFrom": "issue_labels",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "issue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "issue_labels_label_id_labels_id_fk": {
+ "name": "issue_labels_label_id_labels_id_fk",
+ "tableFrom": "issue_labels",
+ "tableTo": "labels",
+ "columnsFrom": [
+ "label_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "issue_labels_company_id_companies_id_fk": {
+ "name": "issue_labels_company_id_companies_id_fk",
+ "tableFrom": "issue_labels",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "issue_labels_pk": {
+ "name": "issue_labels_pk",
+ "columns": [
+ "issue_id",
+ "label_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.issue_read_states": {
+ "name": "issue_read_states",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_read_at": {
+ "name": "last_read_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "issue_read_states_company_issue_idx": {
+ "name": "issue_read_states_company_issue_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_read_states_company_user_idx": {
+ "name": "issue_read_states_company_user_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_read_states_company_issue_user_idx": {
+ "name": "issue_read_states_company_issue_user_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "issue_read_states_company_id_companies_id_fk": {
+ "name": "issue_read_states_company_id_companies_id_fk",
+ "tableFrom": "issue_read_states",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "issue_read_states_issue_id_issues_id_fk": {
+ "name": "issue_read_states_issue_id_issues_id_fk",
+ "tableFrom": "issue_read_states",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "issue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.issue_relations": {
+ "name": "issue_relations",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "related_issue_id": {
+ "name": "related_issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_agent_id": {
+ "name": "created_by_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "issue_relations_company_issue_idx": {
+ "name": "issue_relations_company_issue_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_relations_company_related_issue_idx": {
+ "name": "issue_relations_company_related_issue_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "related_issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_relations_company_type_idx": {
+ "name": "issue_relations_company_type_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_relations_company_edge_uq": {
+ "name": "issue_relations_company_edge_uq",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "related_issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "issue_relations_company_id_companies_id_fk": {
+ "name": "issue_relations_company_id_companies_id_fk",
+ "tableFrom": "issue_relations",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "issue_relations_issue_id_issues_id_fk": {
+ "name": "issue_relations_issue_id_issues_id_fk",
+ "tableFrom": "issue_relations",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "issue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "issue_relations_related_issue_id_issues_id_fk": {
+ "name": "issue_relations_related_issue_id_issues_id_fk",
+ "tableFrom": "issue_relations",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "related_issue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "issue_relations_created_by_agent_id_agents_id_fk": {
+ "name": "issue_relations_created_by_agent_id_agents_id_fk",
+ "tableFrom": "issue_relations",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "created_by_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.issue_work_products": {
+ "name": "issue_work_products",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "execution_workspace_id": {
+ "name": "execution_workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "runtime_service_id": {
+ "name": "runtime_service_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "url": {
+ "name": "url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "review_state": {
+ "name": "review_state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'none'"
+ },
+ "is_primary": {
+ "name": "is_primary",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "health_status": {
+ "name": "health_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'unknown'"
+ },
+ "summary": {
+ "name": "summary",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_run_id": {
+ "name": "created_by_run_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "issue_work_products_company_issue_type_idx": {
+ "name": "issue_work_products_company_issue_type_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_work_products_company_execution_workspace_type_idx": {
+ "name": "issue_work_products_company_execution_workspace_type_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "execution_workspace_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_work_products_company_provider_external_id_idx": {
+ "name": "issue_work_products_company_provider_external_id_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "external_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_work_products_company_updated_idx": {
+ "name": "issue_work_products_company_updated_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "issue_work_products_company_id_companies_id_fk": {
+ "name": "issue_work_products_company_id_companies_id_fk",
+ "tableFrom": "issue_work_products",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "issue_work_products_project_id_projects_id_fk": {
+ "name": "issue_work_products_project_id_projects_id_fk",
+ "tableFrom": "issue_work_products",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "issue_work_products_issue_id_issues_id_fk": {
+ "name": "issue_work_products_issue_id_issues_id_fk",
+ "tableFrom": "issue_work_products",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "issue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "issue_work_products_execution_workspace_id_execution_workspaces_id_fk": {
+ "name": "issue_work_products_execution_workspace_id_execution_workspaces_id_fk",
+ "tableFrom": "issue_work_products",
+ "tableTo": "execution_workspaces",
+ "columnsFrom": [
+ "execution_workspace_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "issue_work_products_runtime_service_id_workspace_runtime_services_id_fk": {
+ "name": "issue_work_products_runtime_service_id_workspace_runtime_services_id_fk",
+ "tableFrom": "issue_work_products",
+ "tableTo": "workspace_runtime_services",
+ "columnsFrom": [
+ "runtime_service_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "issue_work_products_created_by_run_id_heartbeat_runs_id_fk": {
+ "name": "issue_work_products_created_by_run_id_heartbeat_runs_id_fk",
+ "tableFrom": "issue_work_products",
+ "tableTo": "heartbeat_runs",
+ "columnsFrom": [
+ "created_by_run_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.issues": {
+ "name": "issues",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "project_workspace_id": {
+ "name": "project_workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "goal_id": {
+ "name": "goal_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "parent_id": {
+ "name": "parent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'backlog'"
+ },
+ "priority": {
+ "name": "priority",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'medium'"
+ },
+ "assignee_agent_id": {
+ "name": "assignee_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "assignee_user_id": {
+ "name": "assignee_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "checkout_run_id": {
+ "name": "checkout_run_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "execution_run_id": {
+ "name": "execution_run_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "execution_agent_name_key": {
+ "name": "execution_agent_name_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "execution_locked_at": {
+ "name": "execution_locked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_agent_id": {
+ "name": "created_by_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "issue_number": {
+ "name": "issue_number",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "origin_kind": {
+ "name": "origin_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'manual'"
+ },
+ "origin_id": {
+ "name": "origin_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "origin_run_id": {
+ "name": "origin_run_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "request_depth": {
+ "name": "request_depth",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "billing_code": {
+ "name": "billing_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "assignee_adapter_overrides": {
+ "name": "assignee_adapter_overrides",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "execution_workspace_id": {
+ "name": "execution_workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "execution_workspace_preference": {
+ "name": "execution_workspace_preference",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "execution_workspace_settings": {
+ "name": "execution_workspace_settings",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cancelled_at": {
+ "name": "cancelled_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "hidden_at": {
+ "name": "hidden_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "issues_company_status_idx": {
+ "name": "issues_company_status_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issues_company_assignee_status_idx": {
+ "name": "issues_company_assignee_status_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "assignee_agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issues_company_assignee_user_status_idx": {
+ "name": "issues_company_assignee_user_status_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "assignee_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issues_company_parent_idx": {
+ "name": "issues_company_parent_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "parent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issues_company_project_idx": {
+ "name": "issues_company_project_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issues_company_origin_idx": {
+ "name": "issues_company_origin_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "origin_kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "origin_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issues_company_project_workspace_idx": {
+ "name": "issues_company_project_workspace_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "project_workspace_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issues_company_execution_workspace_idx": {
+ "name": "issues_company_execution_workspace_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "execution_workspace_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issues_identifier_idx": {
+ "name": "issues_identifier_idx",
+ "columns": [
+ {
+ "expression": "identifier",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issues_open_routine_execution_uq": {
+ "name": "issues_open_routine_execution_uq",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "origin_kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "origin_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"issues\".\"origin_kind\" = 'routine_execution'\n and \"issues\".\"origin_id\" is not null\n and \"issues\".\"hidden_at\" is null\n and \"issues\".\"execution_run_id\" is not null\n and \"issues\".\"status\" in ('backlog', 'todo', 'in_progress', 'in_review', 'blocked')",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "issues_company_id_companies_id_fk": {
+ "name": "issues_company_id_companies_id_fk",
+ "tableFrom": "issues",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "issues_project_id_projects_id_fk": {
+ "name": "issues_project_id_projects_id_fk",
+ "tableFrom": "issues",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "issues_project_workspace_id_project_workspaces_id_fk": {
+ "name": "issues_project_workspace_id_project_workspaces_id_fk",
+ "tableFrom": "issues",
+ "tableTo": "project_workspaces",
+ "columnsFrom": [
+ "project_workspace_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "issues_goal_id_goals_id_fk": {
+ "name": "issues_goal_id_goals_id_fk",
+ "tableFrom": "issues",
+ "tableTo": "goals",
+ "columnsFrom": [
+ "goal_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "issues_parent_id_issues_id_fk": {
+ "name": "issues_parent_id_issues_id_fk",
+ "tableFrom": "issues",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "parent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "issues_assignee_agent_id_agents_id_fk": {
+ "name": "issues_assignee_agent_id_agents_id_fk",
+ "tableFrom": "issues",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "assignee_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "issues_checkout_run_id_heartbeat_runs_id_fk": {
+ "name": "issues_checkout_run_id_heartbeat_runs_id_fk",
+ "tableFrom": "issues",
+ "tableTo": "heartbeat_runs",
+ "columnsFrom": [
+ "checkout_run_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "issues_execution_run_id_heartbeat_runs_id_fk": {
+ "name": "issues_execution_run_id_heartbeat_runs_id_fk",
+ "tableFrom": "issues",
+ "tableTo": "heartbeat_runs",
+ "columnsFrom": [
+ "execution_run_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "issues_created_by_agent_id_agents_id_fk": {
+ "name": "issues_created_by_agent_id_agents_id_fk",
+ "tableFrom": "issues",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "created_by_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "issues_execution_workspace_id_execution_workspaces_id_fk": {
+ "name": "issues_execution_workspace_id_execution_workspaces_id_fk",
+ "tableFrom": "issues",
+ "tableTo": "execution_workspaces",
+ "columnsFrom": [
+ "execution_workspace_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.join_requests": {
+ "name": "join_requests",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "invite_id": {
+ "name": "invite_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "request_type": {
+ "name": "request_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending_approval'"
+ },
+ "request_ip": {
+ "name": "request_ip",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "requesting_user_id": {
+ "name": "requesting_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "request_email_snapshot": {
+ "name": "request_email_snapshot",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "agent_name": {
+ "name": "agent_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "adapter_type": {
+ "name": "adapter_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "capabilities": {
+ "name": "capabilities",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "agent_defaults_payload": {
+ "name": "agent_defaults_payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "claim_secret_hash": {
+ "name": "claim_secret_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "claim_secret_expires_at": {
+ "name": "claim_secret_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "claim_secret_consumed_at": {
+ "name": "claim_secret_consumed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_agent_id": {
+ "name": "created_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "approved_by_user_id": {
+ "name": "approved_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "approved_at": {
+ "name": "approved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "rejected_by_user_id": {
+ "name": "rejected_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "rejected_at": {
+ "name": "rejected_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "join_requests_invite_unique_idx": {
+ "name": "join_requests_invite_unique_idx",
+ "columns": [
+ {
+ "expression": "invite_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "join_requests_company_status_type_created_idx": {
+ "name": "join_requests_company_status_type_created_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "request_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "join_requests_invite_id_invites_id_fk": {
+ "name": "join_requests_invite_id_invites_id_fk",
+ "tableFrom": "join_requests",
+ "tableTo": "invites",
+ "columnsFrom": [
+ "invite_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "join_requests_company_id_companies_id_fk": {
+ "name": "join_requests_company_id_companies_id_fk",
+ "tableFrom": "join_requests",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "join_requests_created_agent_id_agents_id_fk": {
+ "name": "join_requests_created_agent_id_agents_id_fk",
+ "tableFrom": "join_requests",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "created_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.labels": {
+ "name": "labels",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "color": {
+ "name": "color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "labels_company_idx": {
+ "name": "labels_company_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "labels_company_name_idx": {
+ "name": "labels_company_name_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "labels_company_id_companies_id_fk": {
+ "name": "labels_company_id_companies_id_fk",
+ "tableFrom": "labels",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.plugin_company_settings": {
+ "name": "plugin_company_settings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "plugin_id": {
+ "name": "plugin_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "settings_json": {
+ "name": "settings_json",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "plugin_company_settings_company_idx": {
+ "name": "plugin_company_settings_company_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "plugin_company_settings_plugin_idx": {
+ "name": "plugin_company_settings_plugin_idx",
+ "columns": [
+ {
+ "expression": "plugin_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "plugin_company_settings_company_plugin_uq": {
+ "name": "plugin_company_settings_company_plugin_uq",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "plugin_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "plugin_company_settings_company_id_companies_id_fk": {
+ "name": "plugin_company_settings_company_id_companies_id_fk",
+ "tableFrom": "plugin_company_settings",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "plugin_company_settings_plugin_id_plugins_id_fk": {
+ "name": "plugin_company_settings_plugin_id_plugins_id_fk",
+ "tableFrom": "plugin_company_settings",
+ "tableTo": "plugins",
+ "columnsFrom": [
+ "plugin_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.plugin_config": {
+ "name": "plugin_config",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "plugin_id": {
+ "name": "plugin_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config_json": {
+ "name": "config_json",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "plugin_config_plugin_id_idx": {
+ "name": "plugin_config_plugin_id_idx",
+ "columns": [
+ {
+ "expression": "plugin_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "plugin_config_plugin_id_plugins_id_fk": {
+ "name": "plugin_config_plugin_id_plugins_id_fk",
+ "tableFrom": "plugin_config",
+ "tableTo": "plugins",
+ "columnsFrom": [
+ "plugin_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.plugin_entities": {
+ "name": "plugin_entities",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "plugin_id": {
+ "name": "plugin_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "entity_type": {
+ "name": "entity_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope_kind": {
+ "name": "scope_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope_id": {
+ "name": "scope_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "data": {
+ "name": "data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "plugin_entities_plugin_idx": {
+ "name": "plugin_entities_plugin_idx",
+ "columns": [
+ {
+ "expression": "plugin_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "plugin_entities_type_idx": {
+ "name": "plugin_entities_type_idx",
+ "columns": [
+ {
+ "expression": "entity_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "plugin_entities_scope_idx": {
+ "name": "plugin_entities_scope_idx",
+ "columns": [
+ {
+ "expression": "scope_kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "scope_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "plugin_entities_external_idx": {
+ "name": "plugin_entities_external_idx",
+ "columns": [
+ {
+ "expression": "plugin_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "entity_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "external_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "plugin_entities_plugin_id_plugins_id_fk": {
+ "name": "plugin_entities_plugin_id_plugins_id_fk",
+ "tableFrom": "plugin_entities",
+ "tableTo": "plugins",
+ "columnsFrom": [
+ "plugin_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.plugin_job_runs": {
+ "name": "plugin_job_runs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "plugin_id": {
+ "name": "plugin_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "trigger": {
+ "name": "trigger",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "duration_ms": {
+ "name": "duration_ms",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error": {
+ "name": "error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "logs": {
+ "name": "logs",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "finished_at": {
+ "name": "finished_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "plugin_job_runs_job_idx": {
+ "name": "plugin_job_runs_job_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "plugin_job_runs_plugin_idx": {
+ "name": "plugin_job_runs_plugin_idx",
+ "columns": [
+ {
+ "expression": "plugin_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "plugin_job_runs_status_idx": {
+ "name": "plugin_job_runs_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "plugin_job_runs_job_id_plugin_jobs_id_fk": {
+ "name": "plugin_job_runs_job_id_plugin_jobs_id_fk",
+ "tableFrom": "plugin_job_runs",
+ "tableTo": "plugin_jobs",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "plugin_job_runs_plugin_id_plugins_id_fk": {
+ "name": "plugin_job_runs_plugin_id_plugins_id_fk",
+ "tableFrom": "plugin_job_runs",
+ "tableTo": "plugins",
+ "columnsFrom": [
+ "plugin_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.plugin_jobs": {
+ "name": "plugin_jobs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "plugin_id": {
+ "name": "plugin_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_key": {
+ "name": "job_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "schedule": {
+ "name": "schedule",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "last_run_at": {
+ "name": "last_run_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "next_run_at": {
+ "name": "next_run_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "plugin_jobs_plugin_idx": {
+ "name": "plugin_jobs_plugin_idx",
+ "columns": [
+ {
+ "expression": "plugin_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "plugin_jobs_next_run_idx": {
+ "name": "plugin_jobs_next_run_idx",
+ "columns": [
+ {
+ "expression": "next_run_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "plugin_jobs_unique_idx": {
+ "name": "plugin_jobs_unique_idx",
+ "columns": [
+ {
+ "expression": "plugin_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "job_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "plugin_jobs_plugin_id_plugins_id_fk": {
+ "name": "plugin_jobs_plugin_id_plugins_id_fk",
+ "tableFrom": "plugin_jobs",
+ "tableTo": "plugins",
+ "columnsFrom": [
+ "plugin_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.plugin_logs": {
+ "name": "plugin_logs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "plugin_id": {
+ "name": "plugin_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "level": {
+ "name": "level",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'info'"
+ },
+ "message": {
+ "name": "message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "meta": {
+ "name": "meta",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "plugin_logs_plugin_time_idx": {
+ "name": "plugin_logs_plugin_time_idx",
+ "columns": [
+ {
+ "expression": "plugin_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "plugin_logs_level_idx": {
+ "name": "plugin_logs_level_idx",
+ "columns": [
+ {
+ "expression": "level",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "plugin_logs_plugin_id_plugins_id_fk": {
+ "name": "plugin_logs_plugin_id_plugins_id_fk",
+ "tableFrom": "plugin_logs",
+ "tableTo": "plugins",
+ "columnsFrom": [
+ "plugin_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.plugin_state": {
+ "name": "plugin_state",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "plugin_id": {
+ "name": "plugin_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope_kind": {
+ "name": "scope_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope_id": {
+ "name": "scope_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "namespace": {
+ "name": "namespace",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'default'"
+ },
+ "state_key": {
+ "name": "state_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value_json": {
+ "name": "value_json",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "plugin_state_plugin_scope_idx": {
+ "name": "plugin_state_plugin_scope_idx",
+ "columns": [
+ {
+ "expression": "plugin_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "scope_kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "plugin_state_plugin_id_plugins_id_fk": {
+ "name": "plugin_state_plugin_id_plugins_id_fk",
+ "tableFrom": "plugin_state",
+ "tableTo": "plugins",
+ "columnsFrom": [
+ "plugin_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "plugin_state_unique_entry_idx": {
+ "name": "plugin_state_unique_entry_idx",
+ "nullsNotDistinct": true,
+ "columns": [
+ "plugin_id",
+ "scope_kind",
+ "scope_id",
+ "namespace",
+ "state_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.plugin_webhook_deliveries": {
+ "name": "plugin_webhook_deliveries",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "plugin_id": {
+ "name": "plugin_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "webhook_key": {
+ "name": "webhook_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "duration_ms": {
+ "name": "duration_ms",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error": {
+ "name": "error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "headers": {
+ "name": "headers",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "finished_at": {
+ "name": "finished_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "plugin_webhook_deliveries_plugin_idx": {
+ "name": "plugin_webhook_deliveries_plugin_idx",
+ "columns": [
+ {
+ "expression": "plugin_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "plugin_webhook_deliveries_status_idx": {
+ "name": "plugin_webhook_deliveries_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "plugin_webhook_deliveries_key_idx": {
+ "name": "plugin_webhook_deliveries_key_idx",
+ "columns": [
+ {
+ "expression": "webhook_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "plugin_webhook_deliveries_plugin_id_plugins_id_fk": {
+ "name": "plugin_webhook_deliveries_plugin_id_plugins_id_fk",
+ "tableFrom": "plugin_webhook_deliveries",
+ "tableTo": "plugins",
+ "columnsFrom": [
+ "plugin_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.plugins": {
+ "name": "plugins",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "plugin_key": {
+ "name": "plugin_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "package_name": {
+ "name": "package_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "version": {
+ "name": "version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "api_version": {
+ "name": "api_version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "categories": {
+ "name": "categories",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "manifest_json": {
+ "name": "manifest_json",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'installed'"
+ },
+ "install_order": {
+ "name": "install_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "package_path": {
+ "name": "package_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "installed_at": {
+ "name": "installed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "plugins_plugin_key_idx": {
+ "name": "plugins_plugin_key_idx",
+ "columns": [
+ {
+ "expression": "plugin_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "plugins_status_idx": {
+ "name": "plugins_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.principal_permission_grants": {
+ "name": "principal_permission_grants",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "principal_type": {
+ "name": "principal_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "principal_id": {
+ "name": "principal_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "permission_key": {
+ "name": "permission_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope": {
+ "name": "scope",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "granted_by_user_id": {
+ "name": "granted_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "principal_permission_grants_unique_idx": {
+ "name": "principal_permission_grants_unique_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "principal_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "principal_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "permission_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "principal_permission_grants_company_permission_idx": {
+ "name": "principal_permission_grants_company_permission_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "permission_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "principal_permission_grants_company_id_companies_id_fk": {
+ "name": "principal_permission_grants_company_id_companies_id_fk",
+ "tableFrom": "principal_permission_grants",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.project_goals": {
+ "name": "project_goals",
+ "schema": "",
+ "columns": {
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "goal_id": {
+ "name": "goal_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "project_goals_project_idx": {
+ "name": "project_goals_project_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "project_goals_goal_idx": {
+ "name": "project_goals_goal_idx",
+ "columns": [
+ {
+ "expression": "goal_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "project_goals_company_idx": {
+ "name": "project_goals_company_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "project_goals_project_id_projects_id_fk": {
+ "name": "project_goals_project_id_projects_id_fk",
+ "tableFrom": "project_goals",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "project_goals_goal_id_goals_id_fk": {
+ "name": "project_goals_goal_id_goals_id_fk",
+ "tableFrom": "project_goals",
+ "tableTo": "goals",
+ "columnsFrom": [
+ "goal_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "project_goals_company_id_companies_id_fk": {
+ "name": "project_goals_company_id_companies_id_fk",
+ "tableFrom": "project_goals",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "project_goals_project_id_goal_id_pk": {
+ "name": "project_goals_project_id_goal_id_pk",
+ "columns": [
+ "project_id",
+ "goal_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.project_workspaces": {
+ "name": "project_workspaces",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_type": {
+ "name": "source_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'local_path'"
+ },
+ "cwd": {
+ "name": "cwd",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "repo_url": {
+ "name": "repo_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "repo_ref": {
+ "name": "repo_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "default_ref": {
+ "name": "default_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "visibility": {
+ "name": "visibility",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'default'"
+ },
+ "setup_command": {
+ "name": "setup_command",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cleanup_command": {
+ "name": "cleanup_command",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "remote_provider": {
+ "name": "remote_provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "remote_workspace_ref": {
+ "name": "remote_workspace_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "shared_workspace_key": {
+ "name": "shared_workspace_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_primary": {
+ "name": "is_primary",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "project_workspaces_company_project_idx": {
+ "name": "project_workspaces_company_project_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "project_workspaces_project_primary_idx": {
+ "name": "project_workspaces_project_primary_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "is_primary",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "project_workspaces_project_source_type_idx": {
+ "name": "project_workspaces_project_source_type_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "source_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "project_workspaces_company_shared_key_idx": {
+ "name": "project_workspaces_company_shared_key_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "shared_workspace_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "project_workspaces_project_remote_ref_idx": {
+ "name": "project_workspaces_project_remote_ref_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "remote_provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "remote_workspace_ref",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "project_workspaces_company_id_companies_id_fk": {
+ "name": "project_workspaces_company_id_companies_id_fk",
+ "tableFrom": "project_workspaces",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "project_workspaces_project_id_projects_id_fk": {
+ "name": "project_workspaces_project_id_projects_id_fk",
+ "tableFrom": "project_workspaces",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.projects": {
+ "name": "projects",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "goal_id": {
+ "name": "goal_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'backlog'"
+ },
+ "lead_agent_id": {
+ "name": "lead_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "target_date": {
+ "name": "target_date",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "color": {
+ "name": "color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "env": {
+ "name": "env",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pause_reason": {
+ "name": "pause_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "paused_at": {
+ "name": "paused_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "execution_workspace_policy": {
+ "name": "execution_workspace_policy",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "archived_at": {
+ "name": "archived_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "projects_company_idx": {
+ "name": "projects_company_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "projects_company_id_companies_id_fk": {
+ "name": "projects_company_id_companies_id_fk",
+ "tableFrom": "projects",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "projects_goal_id_goals_id_fk": {
+ "name": "projects_goal_id_goals_id_fk",
+ "tableFrom": "projects",
+ "tableTo": "goals",
+ "columnsFrom": [
+ "goal_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "projects_lead_agent_id_agents_id_fk": {
+ "name": "projects_lead_agent_id_agents_id_fk",
+ "tableFrom": "projects",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "lead_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.routine_runs": {
+ "name": "routine_runs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "routine_id": {
+ "name": "routine_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "trigger_id": {
+ "name": "trigger_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'received'"
+ },
+ "triggered_at": {
+ "name": "triggered_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "idempotency_key": {
+ "name": "idempotency_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "trigger_payload": {
+ "name": "trigger_payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "linked_issue_id": {
+ "name": "linked_issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "coalesced_into_run_id": {
+ "name": "coalesced_into_run_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failure_reason": {
+ "name": "failure_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "routine_runs_company_routine_idx": {
+ "name": "routine_runs_company_routine_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "routine_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "routine_runs_trigger_idx": {
+ "name": "routine_runs_trigger_idx",
+ "columns": [
+ {
+ "expression": "trigger_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "routine_runs_linked_issue_idx": {
+ "name": "routine_runs_linked_issue_idx",
+ "columns": [
+ {
+ "expression": "linked_issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "routine_runs_trigger_idempotency_idx": {
+ "name": "routine_runs_trigger_idempotency_idx",
+ "columns": [
+ {
+ "expression": "trigger_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "idempotency_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "routine_runs_company_id_companies_id_fk": {
+ "name": "routine_runs_company_id_companies_id_fk",
+ "tableFrom": "routine_runs",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "routine_runs_routine_id_routines_id_fk": {
+ "name": "routine_runs_routine_id_routines_id_fk",
+ "tableFrom": "routine_runs",
+ "tableTo": "routines",
+ "columnsFrom": [
+ "routine_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "routine_runs_trigger_id_routine_triggers_id_fk": {
+ "name": "routine_runs_trigger_id_routine_triggers_id_fk",
+ "tableFrom": "routine_runs",
+ "tableTo": "routine_triggers",
+ "columnsFrom": [
+ "trigger_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "routine_runs_linked_issue_id_issues_id_fk": {
+ "name": "routine_runs_linked_issue_id_issues_id_fk",
+ "tableFrom": "routine_runs",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "linked_issue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.routine_triggers": {
+ "name": "routine_triggers",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "routine_id": {
+ "name": "routine_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label": {
+ "name": "label",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "cron_expression": {
+ "name": "cron_expression",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "timezone": {
+ "name": "timezone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "next_run_at": {
+ "name": "next_run_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_fired_at": {
+ "name": "last_fired_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "public_id": {
+ "name": "public_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "secret_id": {
+ "name": "secret_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "signing_mode": {
+ "name": "signing_mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "replay_window_sec": {
+ "name": "replay_window_sec",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_rotated_at": {
+ "name": "last_rotated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_result": {
+ "name": "last_result",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_agent_id": {
+ "name": "created_by_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_by_agent_id": {
+ "name": "updated_by_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_by_user_id": {
+ "name": "updated_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "routine_triggers_company_routine_idx": {
+ "name": "routine_triggers_company_routine_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "routine_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "routine_triggers_company_kind_idx": {
+ "name": "routine_triggers_company_kind_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "routine_triggers_next_run_idx": {
+ "name": "routine_triggers_next_run_idx",
+ "columns": [
+ {
+ "expression": "next_run_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "routine_triggers_public_id_idx": {
+ "name": "routine_triggers_public_id_idx",
+ "columns": [
+ {
+ "expression": "public_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "routine_triggers_public_id_uq": {
+ "name": "routine_triggers_public_id_uq",
+ "columns": [
+ {
+ "expression": "public_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "routine_triggers_company_id_companies_id_fk": {
+ "name": "routine_triggers_company_id_companies_id_fk",
+ "tableFrom": "routine_triggers",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "routine_triggers_routine_id_routines_id_fk": {
+ "name": "routine_triggers_routine_id_routines_id_fk",
+ "tableFrom": "routine_triggers",
+ "tableTo": "routines",
+ "columnsFrom": [
+ "routine_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "routine_triggers_secret_id_company_secrets_id_fk": {
+ "name": "routine_triggers_secret_id_company_secrets_id_fk",
+ "tableFrom": "routine_triggers",
+ "tableTo": "company_secrets",
+ "columnsFrom": [
+ "secret_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "routine_triggers_created_by_agent_id_agents_id_fk": {
+ "name": "routine_triggers_created_by_agent_id_agents_id_fk",
+ "tableFrom": "routine_triggers",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "created_by_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "routine_triggers_updated_by_agent_id_agents_id_fk": {
+ "name": "routine_triggers_updated_by_agent_id_agents_id_fk",
+ "tableFrom": "routine_triggers",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "updated_by_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.routines": {
+ "name": "routines",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "goal_id": {
+ "name": "goal_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "parent_issue_id": {
+ "name": "parent_issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "assignee_agent_id": {
+ "name": "assignee_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "priority": {
+ "name": "priority",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'medium'"
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "concurrency_policy": {
+ "name": "concurrency_policy",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'coalesce_if_active'"
+ },
+ "catch_up_policy": {
+ "name": "catch_up_policy",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'skip_missed'"
+ },
+ "variables": {
+ "name": "variables",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "created_by_agent_id": {
+ "name": "created_by_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_by_agent_id": {
+ "name": "updated_by_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_by_user_id": {
+ "name": "updated_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_triggered_at": {
+ "name": "last_triggered_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_enqueued_at": {
+ "name": "last_enqueued_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "routines_company_status_idx": {
+ "name": "routines_company_status_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "routines_company_assignee_idx": {
+ "name": "routines_company_assignee_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "assignee_agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "routines_company_project_idx": {
+ "name": "routines_company_project_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "routines_company_id_companies_id_fk": {
+ "name": "routines_company_id_companies_id_fk",
+ "tableFrom": "routines",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "routines_project_id_projects_id_fk": {
+ "name": "routines_project_id_projects_id_fk",
+ "tableFrom": "routines",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "routines_goal_id_goals_id_fk": {
+ "name": "routines_goal_id_goals_id_fk",
+ "tableFrom": "routines",
+ "tableTo": "goals",
+ "columnsFrom": [
+ "goal_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "routines_parent_issue_id_issues_id_fk": {
+ "name": "routines_parent_issue_id_issues_id_fk",
+ "tableFrom": "routines",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "parent_issue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "routines_assignee_agent_id_agents_id_fk": {
+ "name": "routines_assignee_agent_id_agents_id_fk",
+ "tableFrom": "routines",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "assignee_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "routines_created_by_agent_id_agents_id_fk": {
+ "name": "routines_created_by_agent_id_agents_id_fk",
+ "tableFrom": "routines",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "created_by_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "routines_updated_by_agent_id_agents_id_fk": {
+ "name": "routines_updated_by_agent_id_agents_id_fk",
+ "tableFrom": "routines",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "updated_by_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.workspace_operations": {
+ "name": "workspace_operations",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "execution_workspace_id": {
+ "name": "execution_workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "heartbeat_run_id": {
+ "name": "heartbeat_run_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "phase": {
+ "name": "phase",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "command": {
+ "name": "command",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cwd": {
+ "name": "cwd",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'running'"
+ },
+ "exit_code": {
+ "name": "exit_code",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "log_store": {
+ "name": "log_store",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "log_ref": {
+ "name": "log_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "log_bytes": {
+ "name": "log_bytes",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "log_sha256": {
+ "name": "log_sha256",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "log_compressed": {
+ "name": "log_compressed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "stdout_excerpt": {
+ "name": "stdout_excerpt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stderr_excerpt": {
+ "name": "stderr_excerpt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "finished_at": {
+ "name": "finished_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "workspace_operations_company_run_started_idx": {
+ "name": "workspace_operations_company_run_started_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "heartbeat_run_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "started_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "workspace_operations_company_workspace_started_idx": {
+ "name": "workspace_operations_company_workspace_started_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "execution_workspace_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "started_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "workspace_operations_company_id_companies_id_fk": {
+ "name": "workspace_operations_company_id_companies_id_fk",
+ "tableFrom": "workspace_operations",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "workspace_operations_execution_workspace_id_execution_workspaces_id_fk": {
+ "name": "workspace_operations_execution_workspace_id_execution_workspaces_id_fk",
+ "tableFrom": "workspace_operations",
+ "tableTo": "execution_workspaces",
+ "columnsFrom": [
+ "execution_workspace_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "workspace_operations_heartbeat_run_id_heartbeat_runs_id_fk": {
+ "name": "workspace_operations_heartbeat_run_id_heartbeat_runs_id_fk",
+ "tableFrom": "workspace_operations",
+ "tableTo": "heartbeat_runs",
+ "columnsFrom": [
+ "heartbeat_run_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.workspace_runtime_services": {
+ "name": "workspace_runtime_services",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "project_workspace_id": {
+ "name": "project_workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "execution_workspace_id": {
+ "name": "execution_workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope_type": {
+ "name": "scope_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope_id": {
+ "name": "scope_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "service_name": {
+ "name": "service_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "lifecycle": {
+ "name": "lifecycle",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reuse_key": {
+ "name": "reuse_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "command": {
+ "name": "command",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cwd": {
+ "name": "cwd",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "port": {
+ "name": "port",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "url": {
+ "name": "url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_ref": {
+ "name": "provider_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owner_agent_id": {
+ "name": "owner_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_by_run_id": {
+ "name": "started_by_run_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "stopped_at": {
+ "name": "stopped_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stop_policy": {
+ "name": "stop_policy",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "health_status": {
+ "name": "health_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'unknown'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "workspace_runtime_services_company_workspace_status_idx": {
+ "name": "workspace_runtime_services_company_workspace_status_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "project_workspace_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "workspace_runtime_services_company_execution_workspace_status_idx": {
+ "name": "workspace_runtime_services_company_execution_workspace_status_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "execution_workspace_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "workspace_runtime_services_company_project_status_idx": {
+ "name": "workspace_runtime_services_company_project_status_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "workspace_runtime_services_run_idx": {
+ "name": "workspace_runtime_services_run_idx",
+ "columns": [
+ {
+ "expression": "started_by_run_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "workspace_runtime_services_company_updated_idx": {
+ "name": "workspace_runtime_services_company_updated_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "workspace_runtime_services_company_id_companies_id_fk": {
+ "name": "workspace_runtime_services_company_id_companies_id_fk",
+ "tableFrom": "workspace_runtime_services",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "company_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "workspace_runtime_services_project_id_projects_id_fk": {
+ "name": "workspace_runtime_services_project_id_projects_id_fk",
+ "tableFrom": "workspace_runtime_services",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "workspace_runtime_services_project_workspace_id_project_workspaces_id_fk": {
+ "name": "workspace_runtime_services_project_workspace_id_project_workspaces_id_fk",
+ "tableFrom": "workspace_runtime_services",
+ "tableTo": "project_workspaces",
+ "columnsFrom": [
+ "project_workspace_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "workspace_runtime_services_execution_workspace_id_execution_workspaces_id_fk": {
+ "name": "workspace_runtime_services_execution_workspace_id_execution_workspaces_id_fk",
+ "tableFrom": "workspace_runtime_services",
+ "tableTo": "execution_workspaces",
+ "columnsFrom": [
+ "execution_workspace_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "workspace_runtime_services_issue_id_issues_id_fk": {
+ "name": "workspace_runtime_services_issue_id_issues_id_fk",
+ "tableFrom": "workspace_runtime_services",
+ "tableTo": "issues",
+ "columnsFrom": [
+ "issue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "workspace_runtime_services_owner_agent_id_agents_id_fk": {
+ "name": "workspace_runtime_services_owner_agent_id_agents_id_fk",
+ "tableFrom": "workspace_runtime_services",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "owner_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "workspace_runtime_services_started_by_run_id_heartbeat_runs_id_fk": {
+ "name": "workspace_runtime_services_started_by_run_id_heartbeat_runs_id_fk",
+ "tableFrom": "workspace_runtime_services",
+ "tableTo": "heartbeat_runs",
+ "columnsFrom": [
+ "started_by_run_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/packages/db/src/migrations/meta/_journal.json b/packages/db/src/migrations/meta/_journal.json
index f1a57f4d..0253407b 100644
--- a/packages/db/src/migrations/meta/_journal.json
+++ b/packages/db/src/migrations/meta/_journal.json
@@ -355,6 +355,13 @@
{
"idx": 50,
"version": "7",
+ "when": 1775487782768,
+ "tag": "0050_stiff_luckman",
+ "breakpoints": true
+ },
+ {
+ "idx": 51,
+ "version": "7",
"when": 1775524651831,
"tag": "0051_young_korg",
"breakpoints": true
diff --git a/packages/db/src/schema/projects.ts b/packages/db/src/schema/projects.ts
index 50520a3b..0e6af757 100644
--- a/packages/db/src/schema/projects.ts
+++ b/packages/db/src/schema/projects.ts
@@ -1,4 +1,5 @@
import { pgTable, uuid, text, timestamp, date, index, jsonb } from "drizzle-orm/pg-core";
+import type { AgentEnvConfig } from "@paperclipai/shared";
import { companies } from "./companies.js";
import { goals } from "./goals.js";
import { agents } from "./agents.js";
@@ -15,6 +16,7 @@ export const projects = pgTable(
leadAgentId: uuid("lead_agent_id").references(() => agents.id),
targetDate: date("target_date"),
color: text("color"),
+ env: jsonb("env").$type(),
pauseReason: text("pause_reason"),
pausedAt: timestamp("paused_at", { withTimezone: true }),
executionWorkspacePolicy: jsonb("execution_workspace_policy").$type>(),
diff --git a/packages/shared/src/types/company-portability.ts b/packages/shared/src/types/company-portability.ts
index d3f3c5c3..356c620d 100644
--- a/packages/shared/src/types/company-portability.ts
+++ b/packages/shared/src/types/company-portability.ts
@@ -1,3 +1,6 @@
+import type { AgentEnvConfig } from "./secrets.js";
+import type { RoutineVariable } from "./routine.js";
+
export interface CompanyPortabilityInclude {
company: boolean;
agents: boolean;
@@ -10,6 +13,7 @@ export interface CompanyPortabilityEnvInput {
key: string;
description: string | null;
agentSlug: string | null;
+ projectSlug: string | null;
kind: "secret" | "plain";
requirement: "required" | "optional";
defaultValue: string | null;
@@ -52,13 +56,12 @@ export interface CompanyPortabilityProjectManifestEntry {
targetDate: string | null;
color: string | null;
status: string | null;
+ env: AgentEnvConfig | null;
executionWorkspacePolicy: Record | null;
workspaces: CompanyPortabilityProjectWorkspaceManifestEntry[];
metadata: Record | null;
}
-import type { RoutineVariable } from "./routine.js";
-
export interface CompanyPortabilityProjectWorkspaceManifestEntry {
key: string;
name: string;
diff --git a/packages/shared/src/types/project.ts b/packages/shared/src/types/project.ts
index d843b425..6bd7da1b 100644
--- a/packages/shared/src/types/project.ts
+++ b/packages/shared/src/types/project.ts
@@ -1,4 +1,5 @@
import type { PauseReason, ProjectStatus } from "../constants.js";
+import type { AgentEnvConfig } from "./secrets.js";
import type {
ProjectExecutionWorkspacePolicy,
ProjectWorkspaceRuntimeConfig,
@@ -65,6 +66,7 @@ export interface Project {
leadAgentId: string | null;
targetDate: string | null;
color: string | null;
+ env: AgentEnvConfig | null;
pauseReason: PauseReason | null;
pausedAt: Date | null;
executionWorkspacePolicy: ProjectExecutionWorkspacePolicy | null;
diff --git a/packages/shared/src/validators/company-portability.ts b/packages/shared/src/validators/company-portability.ts
index 97004b35..952c9cce 100644
--- a/packages/shared/src/validators/company-portability.ts
+++ b/packages/shared/src/validators/company-portability.ts
@@ -15,6 +15,7 @@ export const portabilityEnvInputSchema = z.object({
key: z.string().min(1),
description: z.string().nullable(),
agentSlug: z.string().min(1).nullable(),
+ projectSlug: z.string().min(1).nullable(),
kind: z.enum(["secret", "plain"]),
requirement: z.enum(["required", "optional"]),
defaultValue: z.string().nullable(),
diff --git a/packages/shared/src/validators/project.ts b/packages/shared/src/validators/project.ts
index 89308ff4..f0030fb7 100644
--- a/packages/shared/src/validators/project.ts
+++ b/packages/shared/src/validators/project.ts
@@ -1,5 +1,6 @@
import { z } from "zod";
import { PROJECT_STATUSES } from "../constants.js";
+import { envConfigSchema } from "./secret.js";
const executionWorkspaceStrategySchema = z
.object({
@@ -102,6 +103,7 @@ const projectFields = {
leadAgentId: z.string().uuid().optional().nullable(),
targetDate: z.string().optional().nullable(),
color: z.string().optional().nullable(),
+ env: envConfigSchema.optional().nullable(),
executionWorkspacePolicy: projectExecutionWorkspacePolicySchema.optional().nullable(),
archivedAt: z.string().datetime().optional().nullable(),
};
diff --git a/scripts/dev-runner-output.mjs b/scripts/dev-runner-output.mjs
new file mode 100644
index 00000000..50f5076d
--- /dev/null
+++ b/scripts/dev-runner-output.mjs
@@ -0,0 +1,93 @@
+const DEFAULT_CAPTURED_OUTPUT_BYTES = 256 * 1024;
+const DEFAULT_JSON_RESPONSE_BYTES = 64 * 1024;
+
+function normalizeByteLimit(maxBytes) {
+ return Math.max(1, Math.trunc(maxBytes));
+}
+
+export function createCapturedOutputBuffer(maxBytes = DEFAULT_CAPTURED_OUTPUT_BYTES) {
+ const limit = normalizeByteLimit(maxBytes);
+ const chunks = [];
+ let bufferedBytes = 0;
+ let totalBytes = 0;
+ let truncated = false;
+
+ return {
+ append(chunk) {
+ if (chunk === null || chunk === undefined) return;
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
+ if (buffer.length === 0) return;
+
+ chunks.push(buffer);
+ bufferedBytes += buffer.length;
+ totalBytes += buffer.length;
+
+ while (bufferedBytes > limit && chunks.length > 0) {
+ const overflow = bufferedBytes - limit;
+ const head = chunks[0];
+ if (head.length <= overflow) {
+ chunks.shift();
+ bufferedBytes -= head.length;
+ truncated = true;
+ continue;
+ }
+
+ chunks[0] = head.subarray(overflow);
+ bufferedBytes -= overflow;
+ truncated = true;
+ }
+ },
+
+ finish() {
+ const body = Buffer.concat(chunks).toString("utf8");
+ if (!truncated) {
+ return {
+ text: body,
+ truncated,
+ totalBytes,
+ };
+ }
+
+ return {
+ text: `[output truncated to last ${limit} bytes; total ${totalBytes} bytes]\n${body}`,
+ truncated,
+ totalBytes,
+ };
+ },
+ };
+}
+
+export async function parseJsonResponseWithLimit(response, maxBytes = DEFAULT_JSON_RESPONSE_BYTES) {
+ const limit = normalizeByteLimit(maxBytes);
+ const contentLength = Number.parseInt(response.headers.get("content-length") ?? "", 10);
+ if (Number.isFinite(contentLength) && contentLength > limit) {
+ throw new Error(`Response exceeds ${limit} bytes`);
+ }
+
+ if (!response.body) {
+ return JSON.parse("");
+ }
+
+ const reader = response.body.getReader();
+ const decoder = new TextDecoder();
+ let text = "";
+ let totalBytes = 0;
+
+ try {
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) break;
+ totalBytes += value.byteLength;
+ if (totalBytes > limit) {
+ await reader.cancel("response too large");
+ throw new Error(`Response exceeds ${limit} bytes`);
+ }
+ text += decoder.decode(value, { stream: true });
+ }
+ text += decoder.decode();
+ } finally {
+ reader.releaseLock();
+ }
+
+ return JSON.parse(text);
+}
diff --git a/scripts/dev-runner-output.ts b/scripts/dev-runner-output.ts
new file mode 100644
index 00000000..029e7bfe
--- /dev/null
+++ b/scripts/dev-runner-output.ts
@@ -0,0 +1,102 @@
+const DEFAULT_CAPTURED_OUTPUT_BYTES = 256 * 1024;
+const DEFAULT_JSON_RESPONSE_BYTES = 64 * 1024;
+
+export type CapturedOutput = {
+ text: string;
+ truncated: boolean;
+ totalBytes: number;
+};
+
+function normalizeByteLimit(maxBytes: number) {
+ return Math.max(1, Math.trunc(maxBytes));
+}
+
+export function createCapturedOutputBuffer(maxBytes = DEFAULT_CAPTURED_OUTPUT_BYTES) {
+ const limit = normalizeByteLimit(maxBytes);
+ const chunks: Buffer[] = [];
+ let bufferedBytes = 0;
+ let totalBytes = 0;
+ let truncated = false;
+
+ return {
+ append(chunk: Buffer | string | null | undefined) {
+ if (chunk === null || chunk === undefined) return;
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
+ if (buffer.length === 0) return;
+
+ chunks.push(buffer);
+ bufferedBytes += buffer.length;
+ totalBytes += buffer.length;
+
+ while (bufferedBytes > limit && chunks.length > 0) {
+ const overflow = bufferedBytes - limit;
+ const head = chunks[0]!;
+ if (head.length <= overflow) {
+ chunks.shift();
+ bufferedBytes -= head.length;
+ truncated = true;
+ continue;
+ }
+
+ chunks[0] = head.subarray(overflow);
+ bufferedBytes -= overflow;
+ truncated = true;
+ }
+ },
+
+ finish(): CapturedOutput {
+ const body = Buffer.concat(chunks).toString("utf8");
+ if (!truncated) {
+ return {
+ text: body,
+ truncated,
+ totalBytes,
+ };
+ }
+
+ return {
+ text: `[output truncated to last ${limit} bytes; total ${totalBytes} bytes]\n${body}`,
+ truncated,
+ totalBytes,
+ };
+ },
+ };
+}
+
+export async function parseJsonResponseWithLimit(
+ response: Response,
+ maxBytes = DEFAULT_JSON_RESPONSE_BYTES,
+): Promise {
+ const limit = normalizeByteLimit(maxBytes);
+ const contentLength = Number.parseInt(response.headers.get("content-length") ?? "", 10);
+ if (Number.isFinite(contentLength) && contentLength > limit) {
+ throw new Error(`Response exceeds ${limit} bytes`);
+ }
+
+ if (!response.body) {
+ throw new Error("Response has no body");
+ }
+
+ const reader = response.body.getReader();
+ const decoder = new TextDecoder();
+ let text = "";
+ let totalBytes = 0;
+
+ try {
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) break;
+ totalBytes += value.byteLength;
+ if (totalBytes > limit) {
+ await reader.cancel("response too large");
+ throw new Error(`Response exceeds ${limit} bytes`);
+ }
+ text += decoder.decode(value, { stream: true });
+ }
+ text += decoder.decode();
+ } finally {
+ reader.releaseLock();
+ }
+
+ return JSON.parse(text) as T;
+}
diff --git a/scripts/dev-runner.mjs b/scripts/dev-runner.mjs
index 091dbb19..4f4f7c90 100644
--- a/scripts/dev-runner.mjs
+++ b/scripts/dev-runner.mjs
@@ -5,6 +5,7 @@ import path from "node:path";
import { createInterface } from "node:readline/promises";
import { stdin, stdout } from "node:process";
import { fileURLToPath } from "node:url";
+import { createCapturedOutputBuffer, parseJsonResponseWithLimit } from "./dev-runner-output.mjs";
import { shouldTrackDevServerPath } from "./dev-runner-paths.mjs";
const mode = process.argv[2] === "watch" ? "watch" : "dev";
@@ -250,30 +251,33 @@ async function runPnpm(args, options = {}) {
const spawned = spawn(pnpmBin, args, {
stdio: options.stdio ?? ["ignore", "pipe", "pipe"],
env: options.env ?? process.env,
+ cwd: options.cwd,
shell: process.platform === "win32",
});
- let stdoutBuffer = "";
- let stderrBuffer = "";
+ const stdoutBuffer = createCapturedOutputBuffer();
+ const stderrBuffer = createCapturedOutputBuffer();
if (spawned.stdout) {
spawned.stdout.on("data", (chunk) => {
- stdoutBuffer += String(chunk);
+ stdoutBuffer.append(chunk);
});
}
if (spawned.stderr) {
spawned.stderr.on("data", (chunk) => {
- stderrBuffer += String(chunk);
+ stderrBuffer.append(chunk);
});
}
spawned.on("error", reject);
spawned.on("exit", (code, signal) => {
+ const stdout = stdoutBuffer.finish();
+ const stderr = stderrBuffer.finish();
resolve({
code: code ?? 0,
signal,
- stdout: stdoutBuffer,
- stderr: stderrBuffer,
+ stdout: stdout.text,
+ stderr: stderr.text,
});
});
});
@@ -426,7 +430,7 @@ async function getDevHealthPayload() {
if (!response.ok) {
throw new Error(`Health request failed (${response.status})`);
}
- return await response.json();
+ return await parseJsonResponseWithLimit(response);
}
async function waitForChildExit() {
diff --git a/scripts/dev-runner.ts b/scripts/dev-runner.ts
index aed49c1b..756a6b92 100644
--- a/scripts/dev-runner.ts
+++ b/scripts/dev-runner.ts
@@ -4,6 +4,7 @@ import { existsSync, mkdirSync, readdirSync, rmSync, statSync, writeFileSync } f
import path from "node:path";
import { createInterface } from "node:readline/promises";
import { stdin, stdout } from "node:process";
+import { createCapturedOutputBuffer, parseJsonResponseWithLimit } from "./dev-runner-output.mjs";
import { shouldTrackDevServerPath } from "./dev-runner-paths.mjs";
import { createDevServiceIdentity, repoRoot } from "./dev-service-profile.ts";
import {
@@ -315,27 +316,29 @@ async function runPnpm(args: string[], options: {
shell: process.platform === "win32",
});
- let stdoutBuffer = "";
- let stderrBuffer = "";
+ const stdoutBuffer = createCapturedOutputBuffer();
+ const stderrBuffer = createCapturedOutputBuffer();
if (spawned.stdout) {
spawned.stdout.on("data", (chunk) => {
- stdoutBuffer += String(chunk);
+ stdoutBuffer.append(chunk);
});
}
if (spawned.stderr) {
spawned.stderr.on("data", (chunk) => {
- stderrBuffer += String(chunk);
+ stderrBuffer.append(chunk);
});
}
spawned.on("error", reject);
spawned.on("exit", (code, signal) => {
+ const stdout = stdoutBuffer.finish();
+ const stderr = stderrBuffer.finish();
resolve({
code: code ?? 0,
signal,
- stdout: stdoutBuffer,
- stderr: stderrBuffer,
+ stdout: stdout.text,
+ stderr: stderr.text,
});
});
});
@@ -484,7 +487,7 @@ async function getDevHealthPayload() {
if (!response.ok) {
throw new Error(`Health request failed (${response.status})`);
}
- return await response.json();
+ return await parseJsonResponseWithLimit<{ devServer?: { enabled?: boolean; autoRestartEnabled?: boolean; activeRunCount?: number } }>(response);
}
async function waitForChildExit() {
diff --git a/scripts/ensure-workspace-package-links.ts b/scripts/ensure-workspace-package-links.ts
index 430ba589..8ff86b71 100644
--- a/scripts/ensure-workspace-package-links.ts
+++ b/scripts/ensure-workspace-package-links.ts
@@ -1,10 +1,11 @@
#!/usr/bin/env -S node --import tsx
-import { spawn } from "node:child_process";
+import fs from "node:fs/promises";
import { existsSync, readdirSync, readFileSync, realpathSync } from "node:fs";
import path from "node:path";
import { repoRoot } from "./dev-service-profile.ts";
type WorkspaceLinkMismatch = {
+ workspaceDir: string;
packageName: string;
expectedPath: string;
actualPath: string | null;
@@ -44,11 +45,11 @@ function discoverWorkspacePackagePaths(rootDir: string): Map {
const workspacePackagePaths = discoverWorkspacePackagePaths(repoRoot);
-function findServerWorkspaceLinkMismatches(): WorkspaceLinkMismatch[] {
- const serverPackageJson = readJsonFile(path.join(repoRoot, "server", "package.json"));
+function findWorkspaceLinkMismatches(workspaceDir: string): WorkspaceLinkMismatch[] {
+ const packageJson = readJsonFile(path.join(repoRoot, workspaceDir, "package.json"));
const dependencies = {
- ...(serverPackageJson.dependencies as Record | undefined),
- ...(serverPackageJson.devDependencies as Record | undefined),
+ ...(packageJson.dependencies as Record | undefined),
+ ...(packageJson.devDependencies as Record | undefined),
};
const mismatches: WorkspaceLinkMismatch[] = [];
@@ -58,11 +59,12 @@ function findServerWorkspaceLinkMismatches(): WorkspaceLinkMismatch[] {
const expectedPath = workspacePackagePaths.get(packageName);
if (!expectedPath) continue;
- const linkPath = path.join(repoRoot, "server", "node_modules", ...packageName.split("/"));
+ const linkPath = path.join(repoRoot, workspaceDir, "node_modules", ...packageName.split("/"));
const actualPath = existsSync(linkPath) ? path.resolve(realpathSync(linkPath)) : null;
if (actualPath === path.resolve(expectedPath)) continue;
mismatches.push({
+ workspaceDir,
packageName,
expectedPath: path.resolve(expectedPath),
actualPath,
@@ -72,53 +74,32 @@ function findServerWorkspaceLinkMismatches(): WorkspaceLinkMismatch[] {
return mismatches;
}
-function runCommand(command: string, args: string[], cwd: string) {
- return new Promise((resolve, reject) => {
- const child = spawn(command, args, {
- cwd,
- env: process.env,
- stdio: "inherit",
- });
-
- child.on("error", reject);
- child.on("exit", (code, signal) => {
- if (code === 0) {
- resolve();
- return;
- }
- reject(
- new Error(
- `${command} ${args.join(" ")} failed with ${signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`}`,
- ),
- );
- });
- });
-}
-
-async function ensureServerWorkspaceLinksCurrent() {
- const mismatches = findServerWorkspaceLinkMismatches();
+async function ensureWorkspaceLinksCurrent(workspaceDir: string) {
+ const mismatches = findWorkspaceLinkMismatches(workspaceDir);
if (mismatches.length === 0) return;
- console.log("[paperclip] detected stale workspace package links for server; relinking dependencies...");
+ console.log(`[paperclip] detected stale workspace package links for ${workspaceDir}; relinking dependencies...`);
for (const mismatch of mismatches) {
console.log(
`[paperclip] ${mismatch.packageName}: ${mismatch.actualPath ?? "missing"} -> ${mismatch.expectedPath}`,
);
}
- const pnpmBin = process.platform === "win32" ? "pnpm.cmd" : "pnpm";
- await runCommand(
- pnpmBin,
- ["install", "--force", "--config.confirmModulesPurge=false"],
- repoRoot,
- );
+ for (const mismatch of mismatches) {
+ const linkPath = path.join(repoRoot, mismatch.workspaceDir, "node_modules", ...mismatch.packageName.split("/"));
+ await fs.mkdir(path.dirname(linkPath), { recursive: true });
+ await fs.rm(linkPath, { recursive: true, force: true });
+ await fs.symlink(mismatch.expectedPath, linkPath);
+ }
- const remainingMismatches = findServerWorkspaceLinkMismatches();
+ const remainingMismatches = findWorkspaceLinkMismatches(workspaceDir);
if (remainingMismatches.length === 0) return;
throw new Error(
- `Workspace relink did not repair all server package links: ${remainingMismatches.map((item) => item.packageName).join(", ")}`,
+ `Workspace relink did not repair all ${workspaceDir} package links: ${remainingMismatches.map((item) => item.packageName).join(", ")}`,
);
}
-await ensureServerWorkspaceLinksCurrent();
+for (const workspaceDir of ["server", "ui"]) {
+ await ensureWorkspaceLinksCurrent(workspaceDir);
+}
diff --git a/scripts/provision-worktree.sh b/scripts/provision-worktree.sh
index 861a6037..7b84acaf 100644
--- a/scripts/provision-worktree.sh
+++ b/scripts/provision-worktree.sh
@@ -361,7 +361,7 @@ if [[ -f "$worktree_cwd/package.json" && -f "$worktree_cwd/pnpm-lock.yaml" ]]; t
done < <(list_base_node_modules_paths)
if [[ "$needs_install" -eq 1 ]]; then
- backup_suffix=".paperclip-backup-$BASHPID"
+ backup_suffix=".paperclip-backup-${BASHPID:-$$}"
moved_symlink_paths=()
while IFS= read -r relative_path; do
@@ -377,6 +377,7 @@ if [[ -f "$worktree_cwd/package.json" && -f "$worktree_cwd/pnpm-lock.yaml" ]]; t
restore_moved_symlinks() {
local relative_path target_path backup_path
+ [[ ${#moved_symlink_paths[@]} -gt 0 ]] || return 0
for relative_path in "${moved_symlink_paths[@]}"; do
target_path="$worktree_cwd/$relative_path"
backup_path="${target_path}${backup_suffix}"
@@ -388,6 +389,7 @@ if [[ -f "$worktree_cwd/package.json" && -f "$worktree_cwd/pnpm-lock.yaml" ]]; t
cleanup_moved_symlinks() {
local relative_path target_path backup_path
+ [[ ${#moved_symlink_paths[@]} -gt 0 ]] || return 0
for relative_path in "${moved_symlink_paths[@]}"; do
target_path="$worktree_cwd/$relative_path"
backup_path="${target_path}${backup_suffix}"
diff --git a/server/src/__tests__/company-portability.test.ts b/server/src/__tests__/company-portability.test.ts
index d14ae8c0..b8bf822d 100644
--- a/server/src/__tests__/company-portability.test.ts
+++ b/server/src/__tests__/company-portability.test.ts
@@ -1149,6 +1149,7 @@ describe("company portability", () => {
key: "ANTHROPIC_API_KEY",
description: "Provide ANTHROPIC_API_KEY for agent claudecoder",
agentSlug: "claudecoder",
+ projectSlug: null,
kind: "secret",
requirement: "optional",
defaultValue: "",
@@ -1158,6 +1159,7 @@ describe("company portability", () => {
key: "GH_TOKEN",
description: "Provide GH_TOKEN for agent claudecoder",
agentSlug: "claudecoder",
+ projectSlug: null,
kind: "secret",
requirement: "optional",
defaultValue: "",
@@ -1166,6 +1168,128 @@ describe("company portability", () => {
]);
});
+ it("exports project env as portable inputs without concrete values", async () => {
+ const portability = companyPortabilityService({} as any);
+
+ projectSvc.list.mockResolvedValue([
+ {
+ id: "project-1",
+ name: "Launch",
+ urlKey: "launch",
+ description: "Ship it",
+ leadAgentId: "agent-1",
+ targetDate: null,
+ color: null,
+ status: "planned",
+ env: {
+ OPENAI_API_KEY: {
+ type: "plain",
+ value: "sk-project-secret",
+ },
+ DOCS_MODE: {
+ type: "plain",
+ value: "strict",
+ },
+ GITHUB_TOKEN: {
+ type: "secret_ref",
+ secretId: "11111111-1111-1111-1111-111111111111",
+ version: "latest",
+ },
+ },
+ executionWorkspacePolicy: null,
+ workspaces: [],
+ metadata: null,
+ },
+ ]);
+
+ const exported = await portability.exportBundle("company-1", {
+ include: {
+ company: false,
+ agents: false,
+ projects: true,
+ issues: false,
+ },
+ });
+
+ const extension = asTextFile(exported.files[".paperclip.yaml"]);
+ expect(extension).toContain("OPENAI_API_KEY:");
+ expect(extension).toContain("DOCS_MODE:");
+ expect(extension).toContain("GITHUB_TOKEN:");
+ expect(extension).not.toContain("sk-project-secret");
+ expect(extension).not.toContain('type: "secret_ref"');
+ expect(extension).not.toContain("11111111-1111-1111-1111-111111111111");
+ expect(extension).toContain('default: "strict"');
+ expect(extension).toContain('kind: "secret"');
+ expect(extension).toContain('kind: "plain"');
+ });
+
+ it("reads project env inputs back from .paperclip.yaml during preview import", async () => {
+ const portability = companyPortabilityService({} as any);
+
+ projectSvc.list.mockResolvedValue([
+ {
+ id: "project-1",
+ name: "Launch",
+ urlKey: "launch",
+ description: "Ship it",
+ leadAgentId: "agent-1",
+ targetDate: null,
+ color: null,
+ status: "planned",
+ env: {
+ OPENAI_API_KEY: {
+ type: "plain",
+ value: "sk-project-secret",
+ },
+ },
+ executionWorkspacePolicy: null,
+ workspaces: [],
+ metadata: null,
+ },
+ ]);
+
+ const exported = await portability.exportBundle("company-1", {
+ include: {
+ company: false,
+ agents: false,
+ projects: true,
+ issues: false,
+ },
+ });
+
+ const preview = await portability.previewImport({
+ source: {
+ type: "inline",
+ rootPath: exported.rootPath,
+ files: exported.files,
+ },
+ include: {
+ company: false,
+ agents: false,
+ projects: true,
+ issues: false,
+ },
+ target: {
+ mode: "new_company",
+ newCompanyName: "Imported Paperclip",
+ },
+ agents: "all",
+ collisionStrategy: "rename",
+ });
+
+ expect(preview.errors).toEqual([]);
+ expect(preview.envInputs).toContainEqual({
+ key: "OPENAI_API_KEY",
+ description: "Optional default for OPENAI_API_KEY on project launch",
+ agentSlug: null,
+ projectSlug: "launch",
+ kind: "secret",
+ requirement: "optional",
+ defaultValue: "",
+ portability: "portable",
+ });
+ });
+
it("exports routines as recurring task packages with Paperclip routine extensions", async () => {
const portability = companyPortabilityService({} as any);
diff --git a/server/src/__tests__/dev-runner-output.test.ts b/server/src/__tests__/dev-runner-output.test.ts
new file mode 100644
index 00000000..024317a0
--- /dev/null
+++ b/server/src/__tests__/dev-runner-output.test.ts
@@ -0,0 +1,45 @@
+import { describe, expect, it } from "vitest";
+import { createCapturedOutputBuffer, parseJsonResponseWithLimit } from "../../../scripts/dev-runner-output.mjs";
+
+describe("createCapturedOutputBuffer", () => {
+ it("keeps small output unchanged", () => {
+ const capture = createCapturedOutputBuffer(32);
+ capture.append("hello");
+ capture.append(" world");
+
+ expect(capture.finish()).toEqual({
+ text: "hello world",
+ totalBytes: 11,
+ truncated: false,
+ });
+ });
+
+ it("retains only the bounded tail when output grows large", () => {
+ const capture = createCapturedOutputBuffer(8);
+ capture.append("abcd");
+ capture.append(Buffer.from("efgh"));
+ capture.append("ijkl");
+
+ const result = capture.finish();
+ expect(result.truncated).toBe(true);
+ expect(result.totalBytes).toBe(12);
+ expect(result.text).toContain("total 12 bytes");
+ expect(result.text.endsWith("efghijkl")).toBe(true);
+ });
+
+ it("parses bounded JSON responses", async () => {
+ const response = new Response(JSON.stringify({ ok: true }), {
+ headers: { "content-type": "application/json" },
+ });
+
+ await expect(parseJsonResponseWithLimit<{ ok: boolean }>(response, 64)).resolves.toEqual({ ok: true });
+ });
+
+ it("rejects oversized JSON responses before parsing them", async () => {
+ const response = new Response(JSON.stringify({ payload: "x".repeat(128) }), {
+ headers: { "content-type": "application/json" },
+ });
+
+ await expect(parseJsonResponseWithLimit(response, 32)).rejects.toThrow("Response exceeds 32 bytes");
+ });
+});
diff --git a/server/src/__tests__/dev-server-status.test.ts b/server/src/__tests__/dev-server-status.test.ts
index d178f941..52eef387 100644
--- a/server/src/__tests__/dev-server-status.test.ts
+++ b/server/src/__tests__/dev-server-status.test.ts
@@ -63,4 +63,14 @@ describe("dev server status helpers", () => {
waitingForIdle: true,
});
});
+
+ it("ignores oversized persisted status files", () => {
+ const filePath = createTempStatusFile({
+ dirty: true,
+ changedPathsSample: ["x".repeat(70 * 1024)],
+ pendingMigrations: [],
+ });
+
+ expect(readPersistedDevServerStatus({ PAPERCLIP_DEV_SERVER_STATUS_FILE: filePath })).toBeNull();
+ });
});
diff --git a/server/src/__tests__/heartbeat-project-env.test.ts b/server/src/__tests__/heartbeat-project-env.test.ts
new file mode 100644
index 00000000..85f2b051
--- /dev/null
+++ b/server/src/__tests__/heartbeat-project-env.test.ts
@@ -0,0 +1,65 @@
+import { describe, expect, it, vi } from "vitest";
+import { resolveExecutionRunAdapterConfig } from "../services/heartbeat.ts";
+
+describe("resolveExecutionRunAdapterConfig", () => {
+ it("overlays project env on top of agent env and unions secret keys", async () => {
+ const resolveAdapterConfigForRuntime = vi.fn().mockResolvedValue({
+ config: {
+ env: {
+ SHARED_KEY: "agent",
+ AGENT_ONLY: "agent-only",
+ },
+ other: "value",
+ },
+ secretKeys: new Set(["AGENT_SECRET"]),
+ });
+ const resolveEnvBindings = vi.fn().mockResolvedValue({
+ env: {
+ SHARED_KEY: "project",
+ PROJECT_ONLY: "project-only",
+ },
+ secretKeys: new Set(["PROJECT_SECRET"]),
+ });
+
+ const result = await resolveExecutionRunAdapterConfig({
+ companyId: "company-1",
+ executionRunConfig: { env: { SHARED_KEY: "agent" } },
+ projectEnv: { SHARED_KEY: "project" },
+ secretsSvc: {
+ resolveAdapterConfigForRuntime,
+ resolveEnvBindings,
+ } as any,
+ });
+
+ expect(result.resolvedConfig).toMatchObject({
+ other: "value",
+ env: {
+ SHARED_KEY: "project",
+ AGENT_ONLY: "agent-only",
+ PROJECT_ONLY: "project-only",
+ },
+ });
+ expect(Array.from(result.secretKeys).sort()).toEqual(["AGENT_SECRET", "PROJECT_SECRET"]);
+ });
+
+ it("skips project env resolution when the project has no bindings", async () => {
+ const resolveAdapterConfigForRuntime = vi.fn().mockResolvedValue({
+ config: { env: { AGENT_ONLY: "agent-only" } },
+ secretKeys: new Set(),
+ });
+ const resolveEnvBindings = vi.fn();
+
+ const result = await resolveExecutionRunAdapterConfig({
+ companyId: "company-1",
+ executionRunConfig: { env: { AGENT_ONLY: "agent-only" } },
+ projectEnv: null,
+ secretsSvc: {
+ resolveAdapterConfigForRuntime,
+ resolveEnvBindings,
+ } as any,
+ });
+
+ expect(result.resolvedConfig.env).toEqual({ AGENT_ONLY: "agent-only" });
+ expect(resolveEnvBindings).not.toHaveBeenCalled();
+ });
+});
diff --git a/server/src/__tests__/project-goal-telemetry-routes.test.ts b/server/src/__tests__/project-goal-telemetry-routes.test.ts
index 2e4c2052..4653ca65 100644
--- a/server/src/__tests__/project-goal-telemetry-routes.test.ts
+++ b/server/src/__tests__/project-goal-telemetry-routes.test.ts
@@ -22,6 +22,9 @@ const mockGoalService = vi.hoisted(() => ({
}));
const mockWorkspaceOperationService = vi.hoisted(() => ({}));
+const mockSecretService = vi.hoisted(() => ({
+ normalizeEnvBindingsForPersistence: vi.fn(),
+}));
const mockLogActivity = vi.hoisted(() => vi.fn());
const mockTrackProjectCreated = vi.hoisted(() => vi.fn());
const mockTrackGoalCreated = vi.hoisted(() => vi.fn());
@@ -46,6 +49,7 @@ vi.mock("../services/index.js", () => ({
goalService: () => mockGoalService,
logActivity: mockLogActivity,
projectService: () => mockProjectService,
+ secretService: () => mockSecretService,
workspaceOperationService: () => mockWorkspaceOperationService,
}));
@@ -77,6 +81,7 @@ describe("project and goal telemetry routes", () => {
vi.clearAllMocks();
mockGetTelemetryClient.mockReturnValue({ track: vi.fn() });
mockProjectService.resolveByReference.mockResolvedValue({ ambiguous: false, project: null });
+ mockSecretService.normalizeEnvBindingsForPersistence.mockImplementation(async (_companyId, env) => env);
mockProjectService.create.mockResolvedValue({
id: "project-1",
companyId: "company-1",
diff --git a/server/src/__tests__/project-routes-env.test.ts b/server/src/__tests__/project-routes-env.test.ts
new file mode 100644
index 00000000..c9cb6085
--- /dev/null
+++ b/server/src/__tests__/project-routes-env.test.ts
@@ -0,0 +1,188 @@
+import express from "express";
+import request from "supertest";
+import { beforeEach, describe, expect, it, vi } from "vitest";
+
+const mockProjectService = vi.hoisted(() => ({
+ list: vi.fn(),
+ getById: vi.fn(),
+ create: vi.fn(),
+ update: vi.fn(),
+ createWorkspace: vi.fn(),
+ listWorkspaces: vi.fn(),
+ updateWorkspace: vi.fn(),
+ removeWorkspace: vi.fn(),
+ remove: vi.fn(),
+ resolveByReference: vi.fn(),
+}));
+const mockSecretService = vi.hoisted(() => ({
+ normalizeEnvBindingsForPersistence: vi.fn(),
+}));
+const mockWorkspaceOperationService = vi.hoisted(() => ({}));
+const mockLogActivity = vi.hoisted(() => vi.fn());
+const mockTrackProjectCreated = vi.hoisted(() => vi.fn());
+const mockGetTelemetryClient = vi.hoisted(() => vi.fn());
+
+vi.mock("@paperclipai/shared/telemetry", async () => {
+ const actual = await vi.importActual(
+ "@paperclipai/shared/telemetry",
+ );
+ return {
+ ...actual,
+ trackProjectCreated: mockTrackProjectCreated,
+ };
+});
+
+vi.mock("../telemetry.js", () => ({
+ getTelemetryClient: mockGetTelemetryClient,
+}));
+
+vi.mock("../services/index.js", () => ({
+ logActivity: mockLogActivity,
+ projectService: () => mockProjectService,
+ secretService: () => mockSecretService,
+ workspaceOperationService: () => mockWorkspaceOperationService,
+}));
+
+vi.mock("../services/workspace-runtime.js", () => ({
+ startRuntimeServicesForWorkspaceControl: vi.fn(),
+ stopRuntimeServicesForProjectWorkspace: vi.fn(),
+}));
+
+async function createApp() {
+ const { projectRoutes } = await import("../routes/projects.js");
+ const { errorHandler } = await import("../middleware/index.js");
+ const app = express();
+ app.use(express.json());
+ app.use((req, _res, next) => {
+ (req as any).actor = {
+ type: "board",
+ userId: "board-user",
+ companyIds: ["company-1"],
+ source: "local_implicit",
+ isInstanceAdmin: false,
+ };
+ next();
+ });
+ app.use("/api", projectRoutes({} as any));
+ app.use(errorHandler);
+ return app;
+}
+
+function buildProject(overrides: Record = {}) {
+ return {
+ id: "project-1",
+ companyId: "company-1",
+ urlKey: "project-1",
+ goalId: null,
+ goalIds: [],
+ goals: [],
+ name: "Project",
+ description: null,
+ status: "backlog",
+ leadAgentId: null,
+ targetDate: null,
+ color: null,
+ env: null,
+ pauseReason: null,
+ pausedAt: null,
+ executionWorkspacePolicy: null,
+ codebase: {
+ workspaceId: null,
+ repoUrl: null,
+ repoRef: null,
+ defaultRef: null,
+ repoName: null,
+ localFolder: null,
+ managedFolder: "/tmp/project",
+ effectiveLocalFolder: "/tmp/project",
+ origin: "managed_checkout",
+ },
+ workspaces: [],
+ primaryWorkspace: null,
+ archivedAt: null,
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ ...overrides,
+ };
+}
+
+describe("project env routes", () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ mockGetTelemetryClient.mockReturnValue({ track: vi.fn() });
+ mockProjectService.resolveByReference.mockResolvedValue({ ambiguous: false, project: null });
+ mockProjectService.createWorkspace.mockResolvedValue(null);
+ mockProjectService.listWorkspaces.mockResolvedValue([]);
+ mockSecretService.normalizeEnvBindingsForPersistence.mockImplementation(async (_companyId, env) => env);
+ });
+
+ it("normalizes env bindings on create and logs only env keys", async () => {
+ const normalizedEnv = {
+ API_KEY: {
+ type: "secret_ref",
+ secretId: "11111111-1111-4111-8111-111111111111",
+ version: "latest",
+ },
+ };
+ mockSecretService.normalizeEnvBindingsForPersistence.mockResolvedValue(normalizedEnv);
+ mockProjectService.create.mockResolvedValue(buildProject({ env: normalizedEnv }));
+
+ const app = await createApp();
+ const res = await request(app)
+ .post("/api/companies/company-1/projects")
+ .send({
+ name: "Project",
+ env: normalizedEnv,
+ });
+
+ expect(res.status, JSON.stringify(res.body)).toBe(201);
+ expect(mockSecretService.normalizeEnvBindingsForPersistence).toHaveBeenCalledWith(
+ "company-1",
+ normalizedEnv,
+ expect.objectContaining({ fieldPath: "env" }),
+ );
+ expect(mockProjectService.create).toHaveBeenCalledWith(
+ "company-1",
+ expect.objectContaining({ env: normalizedEnv }),
+ );
+ expect(mockLogActivity).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.objectContaining({
+ details: expect.objectContaining({
+ envKeys: ["API_KEY"],
+ }),
+ }),
+ );
+ });
+
+ it("normalizes env bindings on update and avoids logging raw values", async () => {
+ const normalizedEnv = {
+ PLAIN_KEY: { type: "plain", value: "top-secret" },
+ };
+ mockSecretService.normalizeEnvBindingsForPersistence.mockResolvedValue(normalizedEnv);
+ mockProjectService.getById.mockResolvedValue(buildProject());
+ mockProjectService.update.mockResolvedValue(buildProject({ env: normalizedEnv }));
+
+ const app = await createApp();
+ const res = await request(app)
+ .patch("/api/projects/project-1")
+ .send({
+ env: normalizedEnv,
+ });
+
+ expect(res.status, JSON.stringify(res.body)).toBe(200);
+ expect(mockProjectService.update).toHaveBeenCalledWith(
+ "project-1",
+ expect.objectContaining({ env: normalizedEnv }),
+ );
+ expect(mockLogActivity).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.objectContaining({
+ details: {
+ changedKeys: ["env"],
+ envKeys: ["PLAIN_KEY"],
+ },
+ }),
+ );
+ });
+});
diff --git a/server/src/__tests__/workspace-runtime.test.ts b/server/src/__tests__/workspace-runtime.test.ts
index 911010f5..b5e1dc31 100644
--- a/server/src/__tests__/workspace-runtime.test.ts
+++ b/server/src/__tests__/workspace-runtime.test.ts
@@ -20,6 +20,7 @@ import {
import { eq } from "drizzle-orm";
import {
cleanupExecutionWorkspaceArtifacts,
+ ensureServerWorkspaceLinksCurrent,
ensureRuntimeServicesForRun,
normalizeAdapterManagedRuntimeServices,
reconcilePersistedRuntimeServicesOnStartup,
@@ -187,6 +188,75 @@ describe("sanitizeRuntimeServiceBaseEnv", () => {
});
});
+describe("ensureServerWorkspaceLinksCurrent", () => {
+ it("relinks stale server workspace dependencies inside the current repo root", async () => {
+ const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-runtime-links-"));
+ const staleRoot = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-runtime-links-stale-"));
+ const serverNodeModulesScopeDir = path.join(repoRoot, "server", "node_modules", "@paperclipai");
+ const expectedPackageDir = path.join(repoRoot, "packages", "db");
+ const stalePackageDir = path.join(staleRoot, "db");
+
+ await fs.mkdir(path.join(repoRoot, "server"), { recursive: true });
+ await fs.mkdir(expectedPackageDir, { recursive: true });
+ await fs.mkdir(stalePackageDir, { recursive: true });
+ await fs.mkdir(serverNodeModulesScopeDir, { recursive: true });
+ await fs.writeFile(path.join(repoRoot, "pnpm-workspace.yaml"), "packages:\n - packages/*\n - server\n", "utf8");
+ await fs.writeFile(
+ path.join(repoRoot, "server", "package.json"),
+ JSON.stringify({
+ name: "@paperclipai/server",
+ dependencies: {
+ "@paperclipai/db": "workspace:*",
+ },
+ }),
+ "utf8",
+ );
+ await fs.writeFile(
+ path.join(expectedPackageDir, "package.json"),
+ JSON.stringify({ name: "@paperclipai/db" }),
+ "utf8",
+ );
+ await fs.writeFile(
+ path.join(stalePackageDir, "package.json"),
+ JSON.stringify({ name: "@paperclipai/db" }),
+ "utf8",
+ );
+ await fs.symlink(stalePackageDir, path.join(serverNodeModulesScopeDir, "db"));
+
+ await ensureServerWorkspaceLinksCurrent(path.join(repoRoot, "server"));
+ expect(await fs.realpath(path.join(serverNodeModulesScopeDir, "db"))).toBe(await fs.realpath(expectedPackageDir));
+ });
+
+ it("skips relinking when server workspace dependencies already point at the repo", async () => {
+ const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-runtime-links-current-"));
+ const serverNodeModulesScopeDir = path.join(repoRoot, "server", "node_modules", "@paperclipai");
+ const expectedPackageDir = path.join(repoRoot, "packages", "db");
+
+ await fs.mkdir(path.join(repoRoot, "server"), { recursive: true });
+ await fs.mkdir(expectedPackageDir, { recursive: true });
+ await fs.mkdir(serverNodeModulesScopeDir, { recursive: true });
+ await fs.writeFile(path.join(repoRoot, "pnpm-workspace.yaml"), "packages:\n - packages/*\n - server\n", "utf8");
+ await fs.writeFile(
+ path.join(repoRoot, "server", "package.json"),
+ JSON.stringify({
+ name: "@paperclipai/server",
+ dependencies: {
+ "@paperclipai/db": "workspace:*",
+ },
+ }),
+ "utf8",
+ );
+ await fs.writeFile(
+ path.join(expectedPackageDir, "package.json"),
+ JSON.stringify({ name: "@paperclipai/db" }),
+ "utf8",
+ );
+ await fs.symlink(expectedPackageDir, path.join(serverNodeModulesScopeDir, "db"));
+
+ await ensureServerWorkspaceLinksCurrent(path.join(repoRoot, "server"));
+ });
+});
+
describe("realizeExecutionWorkspace", () => {
it("creates and reuses a git worktree for an issue-scoped branch", async () => {
const repoRoot = await createTempRepo();
@@ -413,6 +483,96 @@ describe("realizeExecutionWorkspace", () => {
await expect(fs.readFile(path.join(reused.cwd, ".paperclip-provision-created"), "utf8")).resolves.toBe("false\n");
});
+ it("uses the latest repo-managed provision script when reusing an existing worktree", async () => {
+ const repoRoot = await createTempRepo();
+ await fs.mkdir(path.join(repoRoot, "scripts"), { recursive: true });
+ await fs.writeFile(
+ path.join(repoRoot, "scripts", "provision.sh"),
+ [
+ "#!/usr/bin/env bash",
+ "set -euo pipefail",
+ "printf 'v1\\n' > .paperclip-provision-version",
+ ].join("\n"),
+ "utf8",
+ );
+ await runGit(repoRoot, ["add", "scripts/provision.sh"]);
+ await runGit(repoRoot, ["commit", "-m", "Add initial provision script"]);
+
+ const initial = await realizeExecutionWorkspace({
+ base: {
+ baseCwd: repoRoot,
+ source: "project_primary",
+ projectId: "project-1",
+ workspaceId: "workspace-1",
+ repoUrl: null,
+ repoRef: "HEAD",
+ },
+ config: {
+ workspaceStrategy: {
+ type: "git_worktree",
+ branchTemplate: "{{issue.identifier}}-{{slug}}",
+ provisionCommand: "bash ./scripts/provision.sh",
+ },
+ },
+ issue: {
+ id: "issue-1",
+ identifier: "PAP-449",
+ title: "Reuse latest provision script",
+ },
+ agent: {
+ id: "agent-1",
+ name: "Codex Coder",
+ companyId: "company-1",
+ },
+ });
+
+ await expect(fs.readFile(path.join(initial.cwd, ".paperclip-provision-version"), "utf8")).resolves.toBe("v1\n");
+
+ await fs.writeFile(
+ path.join(repoRoot, "scripts", "provision.sh"),
+ [
+ "#!/usr/bin/env bash",
+ "set -euo pipefail",
+ "printf 'v2\\n' > .paperclip-provision-version",
+ ].join("\n"),
+ "utf8",
+ );
+ await runGit(repoRoot, ["add", "scripts/provision.sh"]);
+ await runGit(repoRoot, ["commit", "-m", "Update provision script"]);
+
+ await expect(fs.readFile(path.join(initial.cwd, "scripts", "provision.sh"), "utf8")).resolves.toContain("v1");
+
+ const reused = await realizeExecutionWorkspace({
+ base: {
+ baseCwd: repoRoot,
+ source: "project_primary",
+ projectId: "project-1",
+ workspaceId: "workspace-1",
+ repoUrl: null,
+ repoRef: "HEAD",
+ },
+ config: {
+ workspaceStrategy: {
+ type: "git_worktree",
+ branchTemplate: "{{issue.identifier}}-{{slug}}",
+ provisionCommand: "bash ./scripts/provision.sh",
+ },
+ },
+ issue: {
+ id: "issue-1",
+ identifier: "PAP-449",
+ title: "Reuse latest provision script",
+ },
+ agent: {
+ id: "agent-1",
+ name: "Codex Coder",
+ companyId: "company-1",
+ },
+ });
+
+ await expect(fs.readFile(path.join(reused.cwd, ".paperclip-provision-version"), "utf8")).resolves.toBe("v2\n");
+ });
+
it("writes an isolated repo-local Paperclip config and worktree branding when provisioning", async () => {
const repoRoot = await createTempRepo();
const previousCwd = process.cwd();
@@ -663,9 +823,82 @@ describe("realizeExecutionWorkspace", () => {
await fs.realpath(path.join(repoRoot, "packages", "shared")),
);
},
- 15_000,
+ 30_000,
);
+ it("provisions successfully when install is needed but there are no symlinked node_modules to move", async () => {
+ const repoRoot = await createTempRepo();
+ await fs.mkdir(path.join(repoRoot, "scripts"), { recursive: true });
+ await fs.writeFile(
+ path.join(repoRoot, "package.json"),
+ JSON.stringify(
+ {
+ name: "workspace-root",
+ private: true,
+ packageManager: "pnpm@9.15.4",
+ },
+ null,
+ 2,
+ ),
+ "utf8",
+ );
+ await fs.writeFile(
+ path.join(repoRoot, "pnpm-lock.yaml"),
+ [
+ "lockfileVersion: '9.0'",
+ "",
+ "settings:",
+ " autoInstallPeers: true",
+ " excludeLinksFromLockfile: false",
+ "",
+ "importers:",
+ " .: {}",
+ "",
+ ].join("\n"),
+ "utf8",
+ );
+ await fs.copyFile(provisionWorktreeScriptPath, path.join(repoRoot, "scripts", "provision-worktree.sh"));
+ await fs.chmod(path.join(repoRoot, "scripts", "provision-worktree.sh"), 0o755);
+
+ await fs.mkdir(path.join(repoRoot, "node_modules"), { recursive: true });
+ await fs.writeFile(path.join(repoRoot, "node_modules", ".keep"), "", "utf8");
+
+ await runGit(repoRoot, ["add", "package.json", "pnpm-lock.yaml", "scripts/provision-worktree.sh"]);
+ await runGit(repoRoot, ["commit", "-m", "Add minimal provision fixture"]);
+
+ const workspace = await realizeExecutionWorkspace({
+ base: {
+ baseCwd: repoRoot,
+ source: "project_primary",
+ projectId: "project-1",
+ workspaceId: "workspace-1",
+ repoUrl: null,
+ repoRef: "HEAD",
+ },
+ config: {
+ workspaceStrategy: {
+ type: "git_worktree",
+ branchTemplate: "{{issue.identifier}}-{{slug}}",
+ provisionCommand: "bash ./scripts/provision-worktree.sh",
+ },
+ },
+ issue: {
+ id: "issue-1",
+ identifier: "PAP-552",
+ title: "Install without moved symlinks",
+ },
+ agent: {
+ id: "agent-1",
+ name: "Codex Coder",
+ companyId: "company-1",
+ },
+ });
+
+ await expect(fs.readFile(path.join(workspace.cwd, ".paperclip", "config.json"), "utf8")).resolves.toContain(
+ "\"database\"",
+ );
+ }, 30_000);
+
it("records worktree setup and provision operations when a recorder is provided", async () => {
const repoRoot = await createTempRepo();
const { recorder, operations } = createWorkspaceOperationRecorderDouble();
@@ -724,6 +957,57 @@ describe("realizeExecutionWorkspace", () => {
expect(operations[1]?.command).toBe("bash ./scripts/provision.sh");
});
+ it("truncates oversized provision command output before storing it in memory", async () => {
+ const repoRoot = await createTempRepo();
+ const { recorder, operations } = createWorkspaceOperationRecorderDouble();
+
+ await fs.mkdir(path.join(repoRoot, "scripts"), { recursive: true });
+ await fs.writeFile(
+ path.join(repoRoot, "scripts", "noisy.js"),
+ 'process.stdout.write("x".repeat(400000));\n',
+ "utf8",
+ );
+ await runGit(repoRoot, ["add", "scripts/noisy.js"]);
+ await runGit(repoRoot, ["commit", "-m", "Add noisy provision script"]);
+
+ await realizeExecutionWorkspace({
+ base: {
+ baseCwd: repoRoot,
+ source: "project_primary",
+ projectId: "project-1",
+ workspaceId: "workspace-1",
+ repoUrl: null,
+ repoRef: "HEAD",
+ },
+ config: {
+ workspaceStrategy: {
+ type: "git_worktree",
+ branchTemplate: "{{issue.identifier}}-{{slug}}",
+ provisionCommand: "node ./scripts/noisy.js",
+ },
+ },
+ issue: {
+ id: "issue-1",
+ identifier: "PAP-1142",
+ title: "Limit noisy provision output",
+ },
+ agent: {
+ id: "agent-1",
+ name: "Codex Coder",
+ companyId: "company-1",
+ },
+ recorder,
+ });
+
+ const provisionOperation = operations.find((operation) => operation.phase === "workspace_provision");
+ expect(provisionOperation?.result.metadata).toMatchObject({
+ stdoutTruncated: true,
+ stderrTruncated: false,
+ });
+ expect(provisionOperation?.result.stdout).toContain("[output truncated to last");
+ expect(provisionOperation?.result.stdout?.length ?? 0).toBeLessThan(300000);
+ });
+
it("reuses an existing branch without resetting it when recreating a missing worktree", async () => {
const repoRoot = await createTempRepo();
const branchName = "PAP-450-recreate-missing-worktree";
diff --git a/server/src/dev-server-status.ts b/server/src/dev-server-status.ts
index aecb0fc9..ec78bfe8 100644
--- a/server/src/dev-server-status.ts
+++ b/server/src/dev-server-status.ts
@@ -1,4 +1,6 @@
-import { existsSync, readFileSync } from "node:fs";
+import { existsSync, readFileSync, statSync } from "node:fs";
+
+const MAX_PERSISTED_DEV_SERVER_STATUS_BYTES = 64 * 1024;
export type PersistedDevServerStatus = {
dirty: boolean;
@@ -44,6 +46,9 @@ export function readPersistedDevServerStatus(
if (!filePath || !existsSync(filePath)) return null;
try {
+ if (statSync(filePath).size > MAX_PERSISTED_DEV_SERVER_STATUS_BYTES) {
+ return null;
+ }
const raw = JSON.parse(readFileSync(filePath, "utf8")) as Record;
const changedPathsSample = normalizeStringArray(raw.changedPathsSample).slice(0, 5);
const pendingMigrations = normalizeStringArray(raw.pendingMigrations);
diff --git a/server/src/routes/projects.ts b/server/src/routes/projects.ts
index 482a6983..cd523dce 100644
--- a/server/src/routes/projects.ts
+++ b/server/src/routes/projects.ts
@@ -9,7 +9,7 @@ import {
} from "@paperclipai/shared";
import { trackProjectCreated } from "@paperclipai/shared/telemetry";
import { validate } from "../middleware/validate.js";
-import { projectService, logActivity, workspaceOperationService } from "../services/index.js";
+import { projectService, logActivity, secretService, workspaceOperationService } from "../services/index.js";
import { conflict } from "../errors.js";
import { assertCompanyAccess, getActorInfo } from "./authz.js";
import { startRuntimeServicesForWorkspaceControl, stopRuntimeServicesForProjectWorkspace } from "../services/workspace-runtime.js";
@@ -18,7 +18,9 @@ import { getTelemetryClient } from "../telemetry.js";
export function projectRoutes(db: Db) {
const router = Router();
const svc = projectService(db);
+ const secretsSvc = secretService(db);
const workspaceOperations = workspaceOperationService(db);
+ const strictSecretsMode = process.env.PAPERCLIP_SECRETS_STRICT_MODE === "true";
async function resolveCompanyIdForProjectReference(req: Request) {
const companyIdQuery = req.query.companyId;
@@ -82,6 +84,13 @@ export function projectRoutes(db: Db) {
};
const { workspace, ...projectData } = req.body as CreateProjectPayload;
+ if (projectData.env !== undefined) {
+ projectData.env = await secretsSvc.normalizeEnvBindingsForPersistence(
+ companyId,
+ projectData.env,
+ { strictMode: strictSecretsMode, fieldPath: "env" },
+ );
+ }
const project = await svc.create(companyId, projectData);
let createdWorkspaceId: string | null = null;
if (workspace) {
@@ -107,6 +116,7 @@ export function projectRoutes(db: Db) {
details: {
name: project.name,
workspaceId: createdWorkspaceId,
+ envKeys: project.env ? Object.keys(project.env).sort() : [],
},
});
const telemetryClient = getTelemetryClient();
@@ -128,6 +138,12 @@ export function projectRoutes(db: Db) {
if (typeof body.archivedAt === "string") {
body.archivedAt = new Date(body.archivedAt);
}
+ if (body.env !== undefined) {
+ body.env = await secretsSvc.normalizeEnvBindingsForPersistence(existing.companyId, body.env, {
+ strictMode: strictSecretsMode,
+ fieldPath: "env",
+ });
+ }
const project = await svc.update(id, body);
if (!project) {
res.status(404).json({ error: "Project not found" });
@@ -143,7 +159,13 @@ export function projectRoutes(db: Db) {
action: "project.updated",
entityType: "project",
entityId: project.id,
- details: req.body,
+ details: {
+ changedKeys: Object.keys(req.body).sort(),
+ envKeys:
+ body.env && typeof body.env === "object" && !Array.isArray(body.env)
+ ? Object.keys(body.env as Record).sort()
+ : undefined,
+ },
});
res.json(project);
diff --git a/server/src/services/company-portability.ts b/server/src/services/company-portability.ts
index 097e62fc..1c0fc90e 100644
--- a/server/src/services/company-portability.ts
+++ b/server/src/services/company-portability.ts
@@ -27,6 +27,7 @@ import type {
CompanyPortabilitySidebarOrder,
CompanyPortabilitySkillManifestEntry,
CompanySkill,
+ AgentEnvConfig,
RoutineVariable,
} from "@paperclipai/shared";
import {
@@ -39,6 +40,7 @@ import {
ROUTINE_TRIGGER_KINDS,
ROUTINE_TRIGGER_SIGNING_MODES,
deriveProjectUrlKey,
+ envConfigSchema,
normalizeAgentUrlKey,
} from "@paperclipai/shared";
import {
@@ -387,6 +389,88 @@ function isSensitiveEnvKey(key: string) {
);
}
+function normalizePortableProjectEnv(value: unknown): AgentEnvConfig | null {
+ const parsed = envConfigSchema.safeParse(value);
+ return parsed.success ? parsed.data : null;
+}
+
+function extractPortableScopedEnvInputs(
+ scope: {
+ label: string;
+ warningPrefix: string;
+ agentSlug: string | null;
+ projectSlug: string | null;
+ },
+ envValue: unknown,
+ warnings: string[],
+): CompanyPortabilityEnvInput[] {
+ if (!isPlainRecord(envValue)) return [];
+ const env = envValue as Record;
+ const inputs: CompanyPortabilityEnvInput[] = [];
+
+ for (const [key, binding] of Object.entries(env)) {
+ if (key.toUpperCase() === "PATH") {
+ warnings.push(`${scope.warningPrefix} PATH override was omitted from export because it is system-dependent.`);
+ continue;
+ }
+
+ if (isPlainRecord(binding) && binding.type === "secret_ref") {
+ inputs.push({
+ key,
+ description: `Provide ${key} for ${scope.label}`,
+ agentSlug: scope.agentSlug,
+ projectSlug: scope.projectSlug,
+ kind: "secret",
+ requirement: "optional",
+ defaultValue: "",
+ portability: "portable",
+ });
+ continue;
+ }
+
+ if (isPlainRecord(binding) && binding.type === "plain") {
+ const defaultValue = asString(binding.value);
+ const isSensitive = isSensitiveEnvKey(key);
+ const portability = defaultValue && isAbsoluteCommand(defaultValue)
+ ? "system_dependent"
+ : "portable";
+ if (portability === "system_dependent") {
+ warnings.push(`${scope.warningPrefix} env ${key} default was exported as system-dependent.`);
+ }
+ inputs.push({
+ key,
+ description: `Optional default for ${key} on ${scope.label}`,
+ agentSlug: scope.agentSlug,
+ projectSlug: scope.projectSlug,
+ kind: isSensitive ? "secret" : "plain",
+ requirement: "optional",
+ defaultValue: isSensitive ? "" : defaultValue ?? "",
+ portability,
+ });
+ continue;
+ }
+
+ if (typeof binding === "string") {
+ const portability = isAbsoluteCommand(binding) ? "system_dependent" : "portable";
+ if (portability === "system_dependent") {
+ warnings.push(`${scope.warningPrefix} env ${key} default was exported as system-dependent.`);
+ }
+ inputs.push({
+ key,
+ description: `Optional default for ${key} on ${scope.label}`,
+ agentSlug: scope.agentSlug,
+ projectSlug: scope.projectSlug,
+ kind: isSensitiveEnvKey(key) ? "secret" : "plain",
+ requirement: "optional",
+ defaultValue: isSensitiveEnvKey(key) ? "" : binding,
+ portability,
+ });
+ }
+ }
+
+ return inputs;
+}
+
type ResolvedSource = {
manifest: CompanyPortabilityManifest;
files: Record;
@@ -419,6 +503,7 @@ type ProjectLike = {
targetDate: string | null;
color: string | null;
status: string;
+ env: Record | null;
executionWorkspacePolicy: Record | null;
workspaces?: Array<{
id: string;
@@ -1528,68 +1613,33 @@ function extractPortableEnvInputs(
envValue: unknown,
warnings: string[],
): CompanyPortabilityEnvInput[] {
- if (!isPlainRecord(envValue)) return [];
- const env = envValue as Record;
- const inputs: CompanyPortabilityEnvInput[] = [];
+ return extractPortableScopedEnvInputs(
+ {
+ label: `agent ${agentSlug}`,
+ warningPrefix: `Agent ${agentSlug}`,
+ agentSlug,
+ projectSlug: null,
+ },
+ envValue,
+ warnings,
+ );
+}
- for (const [key, binding] of Object.entries(env)) {
- if (key.toUpperCase() === "PATH") {
- warnings.push(`Agent ${agentSlug} PATH override was omitted from export because it is system-dependent.`);
- continue;
- }
-
- if (isPlainRecord(binding) && binding.type === "secret_ref") {
- inputs.push({
- key,
- description: `Provide ${key} for agent ${agentSlug}`,
- agentSlug,
- kind: "secret",
- requirement: "optional",
- defaultValue: "",
- portability: "portable",
- });
- continue;
- }
-
- if (isPlainRecord(binding) && binding.type === "plain") {
- const defaultValue = asString(binding.value);
- const isSensitive = isSensitiveEnvKey(key);
- const portability = defaultValue && isAbsoluteCommand(defaultValue)
- ? "system_dependent"
- : "portable";
- if (portability === "system_dependent") {
- warnings.push(`Agent ${agentSlug} env ${key} default was exported as system-dependent.`);
- }
- inputs.push({
- key,
- description: `Optional default for ${key} on agent ${agentSlug}`,
- agentSlug,
- kind: isSensitive ? "secret" : "plain",
- requirement: "optional",
- defaultValue: isSensitive ? "" : defaultValue ?? "",
- portability,
- });
- continue;
- }
-
- if (typeof binding === "string") {
- const portability = isAbsoluteCommand(binding) ? "system_dependent" : "portable";
- if (portability === "system_dependent") {
- warnings.push(`Agent ${agentSlug} env ${key} default was exported as system-dependent.`);
- }
- inputs.push({
- key,
- description: `Optional default for ${key} on agent ${agentSlug}`,
- agentSlug,
- kind: isSensitiveEnvKey(key) ? "secret" : "plain",
- requirement: "optional",
- defaultValue: binding,
- portability,
- });
- }
- }
-
- return inputs;
+function extractPortableProjectEnvInputs(
+ projectSlug: string,
+ envValue: unknown,
+ warnings: string[],
+): CompanyPortabilityEnvInput[] {
+ return extractPortableScopedEnvInputs(
+ {
+ label: `project ${projectSlug}`,
+ warningPrefix: `Project ${projectSlug}`,
+ agentSlug: null,
+ projectSlug,
+ },
+ envValue,
+ warnings,
+ );
}
function jsonEqual(left: unknown, right: unknown): boolean {
@@ -2175,7 +2225,7 @@ function dedupeEnvInputs(values: CompanyPortabilityManifest["envInputs"]) {
const seen = new Set();
const out: CompanyPortabilityManifest["envInputs"] = [];
for (const value of values) {
- const key = `${value.agentSlug ?? ""}:${value.key.toUpperCase()}`;
+ const key = `${value.agentSlug ?? ""}:${value.projectSlug ?? ""}:${value.key.toUpperCase()}`;
if (seen.has(key)) continue;
seen.add(key);
out.push(value);
@@ -2232,6 +2282,31 @@ function readAgentEnvInputs(
key,
description: asString(record.description) ?? null,
agentSlug,
+ projectSlug: null,
+ kind: record.kind === "plain" ? "plain" : "secret",
+ requirement: record.requirement === "required" ? "required" : "optional",
+ defaultValue: typeof record.default === "string" ? record.default : null,
+ portability: record.portability === "system_dependent" ? "system_dependent" : "portable",
+ }];
+ });
+}
+
+function readProjectEnvInputs(
+ extension: Record,
+ projectSlug: string,
+): CompanyPortabilityManifest["envInputs"] {
+ const inputs = isPlainRecord(extension.inputs) ? extension.inputs : null;
+ const env = inputs && isPlainRecord(inputs.env) ? inputs.env : null;
+ if (!env) return [];
+
+ return Object.entries(env).flatMap(([key, value]) => {
+ if (!isPlainRecord(value)) return [];
+ const record = value as EnvInputRecord;
+ return [{
+ key,
+ description: asString(record.description) ?? null,
+ agentSlug: null,
+ projectSlug,
kind: record.kind === "plain" ? "plain" : "secret",
requirement: record.requirement === "required" ? "required" : "optional",
defaultValue: typeof record.default === "string" ? record.default : null,
@@ -2531,12 +2606,14 @@ function buildManifestFromPackageFiles(
targetDate: asString(extension.targetDate),
color: asString(extension.color),
status: asString(extension.status),
+ env: normalizePortableProjectEnv(extension.env),
executionWorkspacePolicy: isPlainRecord(extension.executionWorkspacePolicy)
? extension.executionWorkspacePolicy
: null,
workspaces,
metadata: isPlainRecord(extension.metadata) ? extension.metadata : null,
});
+ manifest.envInputs.push(...readProjectEnvInputs(extension, slug));
if (frontmatter.kind && frontmatter.kind !== "project") {
warnings.push(`Project markdown ${projectPath} does not declare kind: project in frontmatter.`);
}
@@ -3144,6 +3221,14 @@ export function companyPortabilityService(db: Db, storage?: StorageService) {
for (const project of selectedProjectRows) {
const slug = projectSlugById.get(project.id)!;
const projectPath = `projects/${slug}/PROJECT.md`;
+ const envInputsStart = envInputs.length;
+ const exportedEnvInputs = extractPortableProjectEnvInputs(slug, project.env, warnings);
+ envInputs.push(...exportedEnvInputs);
+ const projectEnvInputs = dedupeEnvInputs(
+ envInputs
+ .slice(envInputsStart)
+ .filter((inputValue) => inputValue.projectSlug === slug),
+ );
const portableWorkspaces = await buildPortableProjectWorkspaces(slug, project.workspaces, warnings);
projectWorkspaceKeyByProjectId.set(project.id, portableWorkspaces.workspaceKeyById);
files[projectPath] = buildMarkdown(
@@ -3167,6 +3252,11 @@ export function companyPortabilityService(db: Db, storage?: StorageService) {
) ?? undefined,
workspaces: portableWorkspaces.extension,
});
+ if (isPlainRecord(extension) && projectEnvInputs.length > 0) {
+ extension.inputs = {
+ env: buildEnvInputMap(projectEnvInputs),
+ };
+ }
paperclipProjectsOut[slug] = isPlainRecord(extension) ? extension : {};
}
@@ -3506,7 +3596,12 @@ export function companyPortabilityService(db: Db, storage?: StorageService) {
for (const envInput of manifest.envInputs) {
if (envInput.portability === "system_dependent") {
- warnings.push(`Environment input ${envInput.key}${envInput.agentSlug ? ` for ${envInput.agentSlug}` : ""} is system-dependent and may need manual adjustment after import.`);
+ const scope = envInput.agentSlug
+ ? ` for agent ${envInput.agentSlug}`
+ : envInput.projectSlug
+ ? ` for project ${envInput.projectSlug}`
+ : "";
+ warnings.push(`Environment input ${envInput.key}${scope} is system-dependent and may need manual adjustment after import.`);
}
}
@@ -4095,6 +4190,7 @@ export function companyPortabilityService(db: Db, storage?: StorageService) {
status: manifestProject.status && PROJECT_STATUSES.includes(manifestProject.status as any)
? manifestProject.status as typeof PROJECT_STATUSES[number]
: "backlog",
+ env: manifestProject.env,
executionWorkspacePolicy: stripPortableProjectExecutionWorkspaceRefs(manifestProject.executionWorkspacePolicy),
};
diff --git a/server/src/services/heartbeat.ts b/server/src/services/heartbeat.ts
index 0a3a1479..365d2225 100644
--- a/server/src/services/heartbeat.ts
+++ b/server/src/services/heartbeat.ts
@@ -86,6 +86,36 @@ const SESSIONED_LOCAL_ADAPTERS = new Set([
"pi_local",
]);
+type RuntimeConfigSecretResolver = Pick<
+ ReturnType,
+ "resolveAdapterConfigForRuntime" | "resolveEnvBindings"
+>;
+
+export async function resolveExecutionRunAdapterConfig(input: {
+ companyId: string;
+ executionRunConfig: Record;
+ projectEnv: unknown;
+ secretsSvc: RuntimeConfigSecretResolver;
+}) {
+ const { config: resolvedConfig, secretKeys } = await input.secretsSvc.resolveAdapterConfigForRuntime(
+ input.companyId,
+ input.executionRunConfig,
+ );
+ const projectEnvResolution = input.projectEnv
+ ? await input.secretsSvc.resolveEnvBindings(input.companyId, input.projectEnv)
+ : { env: {}, secretKeys: new Set() };
+ if (Object.keys(projectEnvResolution.env).length > 0) {
+ resolvedConfig.env = {
+ ...parseObject(resolvedConfig.env),
+ ...projectEnvResolution.env,
+ };
+ for (const key of projectEnvResolution.secretKeys) {
+ secretKeys.add(key);
+ }
+ }
+ return { resolvedConfig, secretKeys };
+}
+
export function applyPersistedExecutionWorkspaceConfig(input: {
config: Record;
workspaceConfig: ExecutionWorkspaceConfig | null;
@@ -2309,17 +2339,20 @@ export function heartbeatService(db: Db) {
: null;
const contextProjectId = readNonEmptyString(context.projectId);
const executionProjectId = issueContext?.projectId ?? contextProjectId;
- const projectExecutionWorkspacePolicy = executionProjectId
+ const projectContext = executionProjectId
? await db
- .select({ executionWorkspacePolicy: projects.executionWorkspacePolicy })
+ .select({
+ executionWorkspacePolicy: projects.executionWorkspacePolicy,
+ env: projects.env,
+ })
.from(projects)
.where(and(eq(projects.id, executionProjectId), eq(projects.companyId, agent.companyId)))
- .then((rows) =>
- gateProjectExecutionWorkspacePolicy(
- parseProjectExecutionWorkspacePolicy(rows[0]?.executionWorkspacePolicy),
- isolatedWorkspacesEnabled,
- ))
+ .then((rows) => rows[0] ?? null)
: null;
+ const projectExecutionWorkspacePolicy = gateProjectExecutionWorkspacePolicy(
+ parseProjectExecutionWorkspacePolicy(projectContext?.executionWorkspacePolicy),
+ isolatedWorkspacesEnabled,
+ );
const taskSession = taskKey
? await getTaskSession(agent.companyId, agent.id, agent.adapterType, taskKey)
: null;
@@ -2416,10 +2449,12 @@ export function heartbeatService(db: Db) {
: persistedWorkspaceManagedConfig;
const configSnapshot = buildExecutionWorkspaceConfigSnapshot(mergedConfig);
const executionRunConfig = stripWorkspaceRuntimeFromExecutionRunConfig(mergedConfig);
- const { config: resolvedConfig, secretKeys } = await secretsSvc.resolveAdapterConfigForRuntime(
- agent.companyId,
+ const { resolvedConfig, secretKeys } = await resolveExecutionRunAdapterConfig({
+ companyId: agent.companyId,
executionRunConfig,
- );
+ projectEnv: projectContext?.env ?? null,
+ secretsSvc,
+ });
const runtimeSkillEntries = await companySkills.listRuntimeSkillEntries(agent.companyId);
const runtimeConfig = {
...resolvedConfig,
diff --git a/server/src/services/secrets.ts b/server/src/services/secrets.ts
index 6317f067..d288d4b3 100644
--- a/server/src/services/secrets.ts
+++ b/server/src/services/secrets.ts
@@ -39,6 +39,11 @@ function canonicalizeBinding(binding: EnvBinding): CanonicalEnvBinding {
}
export function secretService(db: Db) {
+ type NormalizeEnvOptions = {
+ strictMode?: boolean;
+ fieldPath?: string;
+ };
+
async function getById(id: string) {
return db
.select()
@@ -94,10 +99,10 @@ export function secretService(db: Db) {
async function normalizeEnvConfig(
companyId: string,
envValue: unknown,
- opts?: { strictMode?: boolean },
+ opts?: NormalizeEnvOptions,
): Promise {
const record = asRecord(envValue);
- if (!record) throw unprocessable("adapterConfig.env must be an object");
+ if (!record) throw unprocessable(`${opts?.fieldPath ?? "env"} must be an object`);
const normalized: AgentEnvConfig = {};
for (const [key, rawBinding] of Object.entries(record)) {
@@ -292,6 +297,12 @@ export function secretService(db: Db) {
opts?: { strictMode?: boolean },
) => normalizeAdapterConfigForPersistenceInternal(companyId, adapterConfig, opts),
+ normalizeEnvBindingsForPersistence: async (
+ companyId: string,
+ envValue: unknown,
+ opts?: NormalizeEnvOptions,
+ ) => normalizeEnvConfig(companyId, envValue, opts),
+
normalizeHireApprovalPayloadForPersistence: async (
companyId: string,
payload: Record,
diff --git a/server/src/services/workspace-runtime.ts b/server/src/services/workspace-runtime.ts
index 44040311..fc75d0d5 100644
--- a/server/src/services/workspace-runtime.ts
+++ b/server/src/services/workspace-runtime.ts
@@ -1,4 +1,5 @@
import { spawn, type ChildProcess } from "node:child_process";
+import { existsSync, readdirSync, readFileSync, realpathSync } from "node:fs";
import fs from "node:fs/promises";
import net from "node:net";
import { createHash, randomUUID } from "node:crypto";
@@ -101,6 +102,18 @@ interface RuntimeServiceRecord extends RuntimeServiceRef {
const runtimeServicesById = new Map();
const runtimeServicesByReuseKey = new Map();
const runtimeServiceLeasesByRun = new Map();
+const DEFAULT_EXECUTE_PROCESS_OUTPUT_BYTES = 256 * 1024;
+
+type ProcessOutputCapture = {
+ text: string;
+ truncated: boolean;
+ totalBytes: number;
+};
+
+type ProcessOutputAccumulator = {
+ append(chunk: string): void;
+ finish(): ProcessOutputCapture;
+};
export async function resetRuntimeServicesForTests() {
for (const record of runtimeServicesById.values()) {
@@ -122,6 +135,128 @@ function stableStringify(value: unknown): string {
return JSON.stringify(value);
}
+type WorkspaceLinkMismatch = {
+ packageName: string;
+ expectedPath: string;
+ actualPath: string | null;
+};
+
+function readJsonFile(filePath: string): Record {
+ return JSON.parse(readFileSync(filePath, "utf8")) as Record;
+}
+
+function findWorkspaceRoot(startCwd: string) {
+ let current = path.resolve(startCwd);
+ while (true) {
+ if (existsSync(path.join(current, "pnpm-workspace.yaml"))) {
+ return current;
+ }
+ const parent = path.dirname(current);
+ if (parent === current) return null;
+ current = parent;
+ }
+}
+
+function discoverWorkspacePackagePaths(rootDir: string): Map {
+ const packagePaths = new Map();
+ const ignoredDirNames = new Set([".git", ".paperclip", "dist", "node_modules"]);
+
+ function visit(dirPath: string) {
+ if (!existsSync(dirPath)) return;
+
+ const packageJsonPath = path.join(dirPath, "package.json");
+ if (existsSync(packageJsonPath)) {
+ const packageJson = readJsonFile(packageJsonPath);
+ if (typeof packageJson.name === "string" && packageJson.name.length > 0) {
+ packagePaths.set(packageJson.name, dirPath);
+ }
+ }
+
+ for (const entry of readdirSync(dirPath, { withFileTypes: true })) {
+ if (!entry.isDirectory()) continue;
+ if (ignoredDirNames.has(entry.name)) continue;
+ visit(path.join(dirPath, entry.name));
+ }
+ }
+
+ visit(path.join(rootDir, "packages"));
+ visit(path.join(rootDir, "server"));
+ visit(path.join(rootDir, "ui"));
+ visit(path.join(rootDir, "cli"));
+
+ return packagePaths;
+}
+
+function findServerWorkspaceLinkMismatches(rootDir: string): WorkspaceLinkMismatch[] {
+ const serverPackageJsonPath = path.join(rootDir, "server", "package.json");
+ if (!existsSync(serverPackageJsonPath)) return [];
+
+ const serverPackageJson = readJsonFile(serverPackageJsonPath);
+ const dependencies = {
+ ...(serverPackageJson.dependencies as Record | undefined),
+ ...(serverPackageJson.devDependencies as Record | undefined),
+ };
+ const workspacePackagePaths = discoverWorkspacePackagePaths(rootDir);
+ const mismatches: WorkspaceLinkMismatch[] = [];
+
+ for (const [packageName, version] of Object.entries(dependencies)) {
+ if (typeof version !== "string" || !version.startsWith("workspace:")) continue;
+
+ const expectedPath = workspacePackagePaths.get(packageName);
+ if (!expectedPath) continue;
+ const normalizedExpectedPath = existsSync(expectedPath) ? path.resolve(realpathSync(expectedPath)) : path.resolve(expectedPath);
+
+ const linkPath = path.join(rootDir, "server", "node_modules", ...packageName.split("/"));
+ const actualPath = existsSync(linkPath) ? path.resolve(realpathSync(linkPath)) : null;
+ if (actualPath === normalizedExpectedPath) continue;
+
+ mismatches.push({
+ packageName,
+ expectedPath: normalizedExpectedPath,
+ actualPath,
+ });
+ }
+
+ return mismatches;
+}
+
+export async function ensureServerWorkspaceLinksCurrent(
+ startCwd: string,
+ opts?: {
+ onLog?: (stream: "stdout" | "stderr", chunk: string) => Promise;
+ },
+) {
+ const workspaceRoot = findWorkspaceRoot(startCwd);
+ if (!workspaceRoot) return;
+
+ const mismatches = findServerWorkspaceLinkMismatches(workspaceRoot);
+ if (mismatches.length === 0) return;
+
+ if (opts?.onLog) {
+ await opts.onLog("stdout", "[runtime] detected stale workspace package links for server; relinking dependencies...\n");
+ for (const mismatch of mismatches) {
+ await opts.onLog(
+ "stdout",
+ `[runtime] ${mismatch.packageName}: ${mismatch.actualPath ?? "missing"} -> ${mismatch.expectedPath}\n`,
+ );
+ }
+ }
+
+ for (const mismatch of mismatches) {
+ const linkPath = path.join(workspaceRoot, "server", "node_modules", ...mismatch.packageName.split("/"));
+ await fs.mkdir(path.dirname(linkPath), { recursive: true });
+ await fs.rm(linkPath, { recursive: true, force: true });
+ await fs.symlink(mismatch.expectedPath, linkPath);
+ }
+
+ const remainingMismatches = findServerWorkspaceLinkMismatches(workspaceRoot);
+ if (remainingMismatches.length === 0) return;
+
+ throw new Error(
+ `Workspace relink did not repair all server package links: ${remainingMismatches.map((item) => item.packageName).join(", ")}`,
+ );
+}
+
export function sanitizeRuntimeServiceBaseEnv(baseEnv: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
const env: NodeJS.ProcessEnv = { ...baseEnv };
for (const key of Object.keys(env)) {
@@ -258,30 +393,96 @@ function formatCommandForDisplay(command: string, args: string[]) {
.join(" ");
}
+function createProcessOutputCapture(maxBytes: number): ProcessOutputAccumulator {
+ const limit = Math.max(1, Math.trunc(maxBytes));
+ let chunks: string[] = [];
+ let truncated = false;
+ let totalBytes = 0;
+
+ return {
+ append(chunk: string) {
+ if (!chunk) return;
+ chunks.push(chunk);
+ totalBytes += Buffer.byteLength(chunk, "utf8");
+
+ let currentBytes = chunks.reduce((sum, value) => sum + Buffer.byteLength(value, "utf8"), 0);
+ if (currentBytes <= limit) return;
+
+ const combined = Buffer.from(chunks.join(""), "utf8");
+ const tail = combined.subarray(Math.max(0, combined.length - limit)).toString("utf8");
+ chunks = [tail];
+ truncated = true;
+ currentBytes = Buffer.byteLength(tail, "utf8");
+ if (currentBytes > limit) {
+ chunks = [Buffer.from(tail, "utf8").subarray(Math.max(0, currentBytes - limit)).toString("utf8")];
+ }
+ },
+ finish(): ProcessOutputCapture {
+ const text = chunks.join("");
+ if (!truncated) {
+ return {
+ text,
+ truncated: false,
+ totalBytes,
+ };
+ }
+ return {
+ text: `[output truncated to last ${limit} bytes; total ${totalBytes} bytes]\n${text}`,
+ truncated: true,
+ totalBytes,
+ };
+ },
+ };
+}
+
async function executeProcess(input: {
command: string;
args: string[];
cwd: string;
env?: NodeJS.ProcessEnv;
-}): Promise<{ stdout: string; stderr: string; code: number | null }> {
- const proc = await new Promise<{ stdout: string; stderr: string; code: number | null }>((resolve, reject) => {
+ maxStdoutBytes?: number;
+ maxStderrBytes?: number;
+}): Promise<{
+ stdout: string;
+ stderr: string;
+ code: number | null;
+ stdoutTruncated: boolean;
+ stderrTruncated: boolean;
+ stdoutBytes: number;
+ stderrBytes: number;
+}> {
+ const proc = await new Promise<{
+ stdout: ProcessOutputAccumulator;
+ stderr: ProcessOutputAccumulator;
+ code: number | null;
+ }>((resolve, reject) => {
const child = spawn(input.command, input.args, {
cwd: input.cwd,
stdio: ["ignore", "pipe", "pipe"],
env: input.env ?? process.env,
});
- let stdout = "";
- let stderr = "";
+ const stdout = createProcessOutputCapture(input.maxStdoutBytes ?? DEFAULT_EXECUTE_PROCESS_OUTPUT_BYTES);
+ const stderr = createProcessOutputCapture(input.maxStderrBytes ?? DEFAULT_EXECUTE_PROCESS_OUTPUT_BYTES);
child.stdout?.on("data", (chunk) => {
- stdout += String(chunk);
+ stdout.append(String(chunk));
});
child.stderr?.on("data", (chunk) => {
- stderr += String(chunk);
+ stderr.append(String(chunk));
});
child.on("error", reject);
child.on("close", (code) => resolve({ stdout, stderr, code }));
});
- return proc;
+ const stdout = proc.stdout.finish();
+ const stderr = proc.stderr.finish();
+ return {
+ stdout: stdout.text,
+ stderr: stderr.text,
+ code: proc.code,
+ stdoutTruncated: stdout.truncated,
+ stderrTruncated: stderr.truncated,
+ stdoutBytes: stdout.totalBytes,
+ stderrBytes: stderr.totalBytes,
+ };
}
async function runGit(args: string[], cwd: string): Promise {
@@ -377,8 +578,35 @@ function buildWorkspaceCommandEnv(input: {
return env;
}
+function quoteShellArg(value: string) {
+ return `'${value.replace(/'/g, `'\\''`)}'`;
+}
+
+function resolveRepoManagedWorkspaceCommand(command: string, repoRoot: string) {
+ const patterns = [
+ /^(?(?:bash|sh|zsh)\s+)(?["']?)(?\.\/[^"'\s]+)\k(?(?:\s.*)?)$/s,
+ /^(?["']?)(?\.\/[^"'\s]+)\k(?(?:\s.*)?)$/s,
+ ];
+
+ for (const pattern of patterns) {
+ const match = command.match(pattern);
+ if (!match?.groups) continue;
+
+ const relativePath = match.groups.relative;
+ const repoManagedPath = path.join(repoRoot, relativePath.slice(2));
+ if (!existsSync(repoManagedPath)) continue;
+
+ const prefix = match.groups.prefix ?? "";
+ const suffix = match.groups.suffix ?? "";
+ return `${prefix}${quoteShellArg(repoManagedPath)}${suffix}`;
+ }
+
+ return command;
+}
+
async function runWorkspaceCommand(input: {
command: string;
+ resolvedCommand?: string;
cwd: string;
env: NodeJS.ProcessEnv;
label: string;
@@ -386,7 +614,7 @@ async function runWorkspaceCommand(input: {
const shell = resolveShell();
const proc = await executeProcess({
command: shell,
- args: ["-c", input.command],
+ args: ["-c", input.resolvedCommand ?? input.command],
cwd: input.cwd,
env: input.env,
});
@@ -438,6 +666,15 @@ async function recordGitOperation(
stdout: result.stdout,
stderr: result.stderr,
system: result.code === 0 ? input.successMessage ?? null : null,
+ metadata:
+ result.stdoutTruncated || result.stderrTruncated
+ ? {
+ stdoutTruncated: result.stdoutTruncated,
+ stderrTruncated: result.stderrTruncated,
+ stdoutBytes: result.stdoutBytes,
+ stderrBytes: result.stderrBytes,
+ }
+ : null,
};
},
});
@@ -458,6 +695,7 @@ async function recordWorkspaceCommandOperation(
input: {
phase: "workspace_provision" | "workspace_teardown";
command: string;
+ resolvedCommand?: string;
cwd: string;
env: NodeJS.ProcessEnv;
label: string;
@@ -482,7 +720,7 @@ async function recordWorkspaceCommandOperation(
const shell = resolveShell();
const result = await executeProcess({
command: shell,
- args: ["-c", input.command],
+ args: ["-c", input.resolvedCommand ?? input.command],
cwd: input.cwd,
env: input.env,
});
@@ -495,6 +733,15 @@ async function recordWorkspaceCommandOperation(
stdout: result.stdout,
stderr: result.stderr,
system: result.code === 0 ? input.successMessage ?? null : null,
+ metadata:
+ result.stdoutTruncated || result.stderrTruncated
+ ? {
+ stdoutTruncated: result.stdoutTruncated,
+ stderrTruncated: result.stderrTruncated,
+ stdoutBytes: result.stdoutBytes,
+ stderrBytes: result.stderrBytes,
+ }
+ : null,
};
},
});
@@ -522,10 +769,12 @@ async function provisionExecutionWorktree(input: {
}) {
const provisionCommand = asString(input.strategy.provisionCommand, "").trim();
if (!provisionCommand) return;
+ const resolvedProvisionCommand = resolveRepoManagedWorkspaceCommand(provisionCommand, input.repoRoot);
await recordWorkspaceCommandOperation(input.recorder, {
phase: "workspace_provision",
command: provisionCommand,
+ resolvedCommand: resolvedProvisionCommand,
cwd: input.worktreePath,
env: buildWorkspaceCommandEnv({
base: input.base,
@@ -542,6 +791,7 @@ async function provisionExecutionWorktree(input: {
worktreePath: input.worktreePath,
branchName: input.branchName,
created: input.created,
+ resolvedCommand: resolvedProvisionCommand === provisionCommand ? null : resolvedProvisionCommand,
},
successMessage: `Provisioned workspace at ${input.worktreePath}\n`,
});
@@ -769,6 +1019,12 @@ export async function cleanupExecutionWorkspaceArtifacts(input: {
}) {
const warnings: string[] = [];
const workspacePath = input.workspace.providerRef ?? input.workspace.cwd;
+ const repoRoot = input.workspace.providerType === "git_worktree" && workspacePath
+ ? await resolveGitRepoRootForWorkspaceCleanup(
+ workspacePath,
+ input.projectWorkspace?.cwd ?? null,
+ )
+ : null;
const cleanupEnv = buildExecutionWorkspaceCleanupEnv({
workspace: input.workspace,
projectWorkspaceCwd: input.projectWorkspace?.cwd ?? null,
@@ -784,9 +1040,13 @@ export async function cleanupExecutionWorkspaceArtifacts(input: {
for (const command of cleanupCommands) {
try {
+ const resolvedCommand = repoRoot
+ ? resolveRepoManagedWorkspaceCommand(command, repoRoot)
+ : command;
await recordWorkspaceCommandOperation(input.recorder, {
phase: "workspace_teardown",
command,
+ resolvedCommand,
cwd: workspacePath ?? input.projectWorkspace?.cwd ?? process.cwd(),
env: cleanupEnv,
label: `Execution workspace cleanup command "${command}"`,
@@ -795,6 +1055,7 @@ export async function cleanupExecutionWorkspaceArtifacts(input: {
workspacePath,
branchName: input.workspace.branchName,
providerType: input.workspace.providerType,
+ resolvedCommand: resolvedCommand === command ? null : resolvedCommand,
},
successMessage: `Completed cleanup command "${command}"\n`,
});
@@ -804,10 +1065,6 @@ export async function cleanupExecutionWorkspaceArtifacts(input: {
}
if (input.workspace.providerType === "git_worktree" && workspacePath) {
- const repoRoot = await resolveGitRepoRootForWorkspaceCleanup(
- workspacePath,
- input.projectWorkspace?.cwd ?? null,
- );
const worktreeExists = await directoryExists(workspacePath);
if (worktreeExists) {
if (!repoRoot) {
@@ -1374,7 +1631,11 @@ async function startLocalRuntimeService(input: {
);
}
}
-
+
+ await ensureServerWorkspaceLinksCurrent(serviceCwd, {
+ onLog: input.onLog,
+ });
+
const shell = resolveShell();
const child = spawn(shell, ["-lc", command], {
cwd: serviceCwd,
diff --git a/tests/e2e/playwright.config.ts b/tests/e2e/playwright.config.ts
index 33022502..572b012a 100644
--- a/tests/e2e/playwright.config.ts
+++ b/tests/e2e/playwright.config.ts
@@ -1,6 +1,8 @@
import { defineConfig } from "@playwright/test";
-const PORT = Number(process.env.PAPERCLIP_E2E_PORT ?? 3100);
+// Use a dedicated port so e2e tests always start their own server in local_trusted mode,
+// even when the dev server is running on :3100 in authenticated mode.
+const PORT = Number(process.env.PAPERCLIP_E2E_PORT ?? 3199);
const BASE_URL = `http://127.0.0.1:${PORT}`;
export default defineConfig({
@@ -29,6 +31,11 @@ export default defineConfig({
timeout: 120_000,
stdout: "pipe",
stderr: "pipe",
+ env: {
+ ...process.env,
+ PORT: String(PORT),
+ PAPERCLIP_DEPLOYMENT_MODE: "local_trusted",
+ },
},
outputDir: "./test-results",
reporter: [["list"], ["html", { open: "never", outputFolder: "./playwright-report" }]],
diff --git a/tests/e2e/signoff-policy.spec.ts b/tests/e2e/signoff-policy.spec.ts
new file mode 100644
index 00000000..97e67746
--- /dev/null
+++ b/tests/e2e/signoff-policy.spec.ts
@@ -0,0 +1,399 @@
+import { test, expect, request as pwRequest, type APIRequestContext } from "@playwright/test";
+
+/**
+ * E2E: Signoff execution policy flow.
+ *
+ * Validates the full signoff lifecycle through the API and UI:
+ * 1. Create a company with executor + reviewer + approver agents
+ * 2. Create an issue with a two-stage execution policy (review → approval)
+ * 3. Executor marks done → issue routes to reviewer (in_review)
+ * 4. Reviewer approves → issue routes to approver
+ * 5. Approver approves → execution completes, issue marked done
+ * 6. Verify "changes requested" flow returns to executor
+ *
+ * Requires local_trusted deployment mode (set in playwright.config.ts webServer env).
+ *
+ * Agent auth flow:
+ * - Board request (local_trusted auto-auth) handles setup/teardown.
+ * - Agent-specific actions use API keys + heartbeat run IDs.
+ * - Reviewers/approvers invoke heartbeat runs (gets run IDs) then PATCH
+ * directly without checkout (checkout would force in_progress, breaking
+ * the in_review state the signoff policy requires).
+ */
+
+const PORT = Number(process.env.PAPERCLIP_E2E_PORT ?? 3199);
+const BASE_URL = `http://127.0.0.1:${PORT}`;
+const COMPANY_NAME = `E2E-Signoff-${Date.now()}`;
+
+interface AgentAuth {
+ agentId: string;
+ token: string;
+ keyId: string;
+ request: APIRequestContext;
+}
+
+interface TestContext {
+ companyId: string;
+ companyPrefix: string;
+ executor: AgentAuth;
+ reviewer: AgentAuth;
+ approver: AgentAuth;
+ boardRequest: APIRequestContext;
+ issueIds: string[];
+}
+
+/** Create an authenticated APIRequestContext for an agent (token set, no run ID yet). */
+async function createAgentRequest(token: string): Promise {
+ return pwRequest.newContext({
+ baseURL: BASE_URL,
+ extraHTTPHeaders: { Authorization: `Bearer ${token}` },
+ });
+}
+
+/** Invoke a heartbeat run for an agent, returning the run ID. */
+async function invokeHeartbeat(board: APIRequestContext, agentId: string): Promise {
+ const res = await board.post(`${BASE_URL}/api/agents/${agentId}/heartbeat/invoke`);
+ expect(res.ok()).toBe(true);
+ const run = await res.json();
+ return run.id;
+}
+
+/** PATCH an issue as an agent with a fresh heartbeat run ID. */
+async function agentPatch(
+ board: APIRequestContext,
+ agent: AgentAuth,
+ issueId: string,
+ data: Record,
+) {
+ const runId = await invokeHeartbeat(board, agent.agentId);
+ const res = await agent.request.patch(`${BASE_URL}/api/issues/${issueId}`, {
+ headers: { "X-Paperclip-Run-Id": runId },
+ data,
+ });
+ return res;
+}
+
+/** Checkout an issue as an agent, then PATCH it. Used for executor mark-done. */
+async function agentCheckoutAndPatch(
+ board: APIRequestContext,
+ agent: AgentAuth,
+ issueId: string,
+ expectedStatuses: string[],
+ patchData: Record,
+) {
+ const runId = await invokeHeartbeat(board, agent.agentId);
+ // Checkout (sets executionRunId so PATCH is allowed)
+ const checkoutRes = await agent.request.post(`${BASE_URL}/api/issues/${issueId}/checkout`, {
+ headers: { "X-Paperclip-Run-Id": runId },
+ data: { agentId: agent.agentId, expectedStatuses },
+ });
+ if (!checkoutRes.ok()) {
+ // If agent checkout fails (e.g. run expired), fall back to board checkout
+ // then PATCH with the agent's identity
+ const boardCheckout = await board.post(`${BASE_URL}/api/issues/${issueId}/checkout`, {
+ data: { agentId: agent.agentId, expectedStatuses },
+ });
+ if (!boardCheckout.ok()) {
+ throw new Error(`Board checkout failed: ${await boardCheckout.text()}`);
+ }
+ // Board PATCH (executor mark-done triggers signoff regardless of actor)
+ const res = await board.patch(`${BASE_URL}/api/issues/${issueId}`, {
+ data: patchData,
+ });
+ return res;
+ }
+ // PATCH with agent identity
+ const res = await agent.request.patch(`${BASE_URL}/api/issues/${issueId}`, {
+ headers: { "X-Paperclip-Run-Id": runId },
+ data: patchData,
+ });
+ return res;
+}
+
+async function setupCompany(boardRequest: APIRequestContext): Promise {
+ // Verify server is in local_trusted mode
+ const healthRes = await boardRequest.get(`${BASE_URL}/api/health`);
+ expect(healthRes.ok()).toBe(true);
+ const health = await healthRes.json();
+ if (health.deploymentMode !== "local_trusted") {
+ throw new Error(
+ `Signoff e2e tests require local_trusted deployment mode, ` +
+ `but server is in "${health.deploymentMode}" mode. ` +
+ `Set PAPERCLIP_DEPLOYMENT_MODE=local_trusted or use the webServer config.`,
+ );
+ }
+
+ // Create company
+ const companyRes = await boardRequest.post(`${BASE_URL}/api/companies`, {
+ data: { name: COMPANY_NAME },
+ });
+ if (!companyRes.ok()) {
+ const errBody = await companyRes.text();
+ throw new Error(`POST /api/companies → ${companyRes.status()}: ${errBody}`);
+ }
+ const company = await companyRes.json();
+ const companyId = company.id;
+ const companyPrefix = company.issuePrefix ?? company.prefix ?? company.urlKey ?? "E2E";
+
+ // Helper: create agent + API key + request context
+ async function createAgent(name: string, role: string, title: string): Promise {
+ const agentRes = await boardRequest.post(`${BASE_URL}/api/companies/${companyId}/agents`, {
+ data: { name, role, title, adapterType: "process", adapterConfig: { command: "echo done" } },
+ });
+ expect(agentRes.ok()).toBe(true);
+ const agent = await agentRes.json();
+
+ const keyRes = await boardRequest.post(`${BASE_URL}/api/agents/${agent.id}/keys`, {
+ data: { name: `e2e-${name.toLowerCase()}` },
+ });
+ expect(keyRes.ok()).toBe(true);
+ const keyData = await keyRes.json();
+
+ return {
+ agentId: agent.id,
+ token: keyData.token,
+ keyId: keyData.id,
+ request: await createAgentRequest(keyData.token),
+ };
+ }
+
+ const executor = await createAgent("Executor", "engineer", "Software Engineer");
+ const reviewer = await createAgent("Reviewer", "qa", "QA Engineer");
+ const approver = await createAgent("Approver", "cto", "CTO");
+
+ return {
+ companyId,
+ companyPrefix,
+ executor,
+ reviewer,
+ approver,
+ boardRequest,
+ issueIds: [],
+ };
+}
+
+async function createIssueWithPolicy(ctx: TestContext, title: string, stages?: unknown[]) {
+ const defaultStages = [
+ { type: "review", participants: [{ type: "agent", agentId: ctx.reviewer.agentId }] },
+ { type: "approval", participants: [{ type: "agent", agentId: ctx.approver.agentId }] },
+ ];
+ const res = await ctx.boardRequest.post(`${BASE_URL}/api/companies/${ctx.companyId}/issues`, {
+ data: {
+ title,
+ status: "in_progress",
+ assigneeAgentId: ctx.executor.agentId,
+ executionPolicy: { stages: stages ?? defaultStages },
+ },
+ });
+ expect(res.ok()).toBe(true);
+ const issue = await res.json();
+ ctx.issueIds.push(issue.id);
+ return issue;
+}
+
+test.describe("Signoff execution policy", () => {
+ let ctx: TestContext;
+
+ test.beforeAll(async () => {
+ const boardRequest = await pwRequest.newContext({ baseURL: BASE_URL });
+ ctx = await setupCompany(boardRequest);
+ });
+
+ test.afterAll(async () => {
+ if (!ctx) return;
+ const board = ctx.boardRequest;
+
+ // Dispose agent request contexts
+ for (const agent of [ctx.executor, ctx.reviewer, ctx.approver]) {
+ await agent.request.dispose();
+ }
+
+ // Clean up issues, keys, agents, company (best-effort)
+ for (const issueId of ctx.issueIds) {
+ await board.patch(`${BASE_URL}/api/issues/${issueId}`, {
+ data: { status: "cancelled", comment: "E2E test cleanup." },
+ }).catch(() => {});
+ }
+ for (const agent of [ctx.executor, ctx.reviewer, ctx.approver]) {
+ await board.delete(`${BASE_URL}/api/agents/${agent.agentId}/keys/${agent.keyId}`).catch(() => {});
+ await board.delete(`${BASE_URL}/api/agents/${agent.agentId}`).catch(() => {});
+ }
+ await board.delete(`${BASE_URL}/api/companies/${ctx.companyId}`).catch(() => {});
+ await board.dispose();
+ });
+
+ test("happy path: executor → review → approval → done", async ({ page }) => {
+ const issue = await createIssueWithPolicy(ctx, "Signoff happy path");
+ const issueId = issue.id;
+
+ // Verify policy was saved
+ expect(issue.executionPolicy).toBeTruthy();
+ expect(issue.executionPolicy.stages).toHaveLength(2);
+ expect(issue.executionPolicy.stages[0].type).toBe("review");
+ expect(issue.executionPolicy.stages[1].type).toBe("approval");
+
+ // Step 1: Executor marks done → should route to reviewer
+ const step1Res = await agentCheckoutAndPatch(
+ ctx.boardRequest, ctx.executor, issueId, ["in_progress"],
+ { status: "done", comment: "Implemented the feature, ready for review." },
+ );
+ expect(step1Res.ok()).toBe(true);
+ const step1Issue = await step1Res.json();
+
+ expect(step1Issue.status).toBe("in_review");
+ expect(step1Issue.assigneeAgentId).toBe(ctx.reviewer.agentId);
+ expect(step1Issue.executionState).toBeTruthy();
+ expect(step1Issue.executionState.status).toBe("pending");
+ expect(step1Issue.executionState.currentStageType).toBe("review");
+ expect(step1Issue.executionState.returnAssignee).toMatchObject({
+ type: "agent",
+ agentId: ctx.executor.agentId,
+ });
+
+ // Step 2: Navigate to issue in UI and verify execution label
+ await page.goto(`/${ctx.companyPrefix}/issues/${issue.identifier}`);
+ await expect(page.locator("text=Review pending")).toBeVisible({ timeout: 10_000 });
+
+ // Step 3: Reviewer approves → should route to approver
+ const step3Res = await agentPatch(
+ ctx.boardRequest, ctx.reviewer, issueId,
+ { status: "done", comment: "QA signoff complete. Looks good." },
+ );
+ expect(step3Res.ok()).toBe(true);
+ const step3Issue = await step3Res.json();
+
+ expect(step3Issue.status).toBe("in_review");
+ expect(step3Issue.assigneeAgentId).toBe(ctx.approver.agentId);
+ expect(step3Issue.executionState.status).toBe("pending");
+ expect(step3Issue.executionState.currentStageType).toBe("approval");
+ expect(step3Issue.executionState.completedStageIds).toHaveLength(1);
+
+ // Step 4: Verify UI shows approval pending
+ await page.reload();
+ await expect(page.locator("text=Approval pending")).toBeVisible({ timeout: 10_000 });
+
+ // Step 5: Approver approves → should complete
+ const step5Res = await agentPatch(
+ ctx.boardRequest, ctx.approver, issueId,
+ { status: "done", comment: "Approved. Ship it." },
+ );
+ expect(step5Res.ok()).toBe(true);
+ const step5Issue = await step5Res.json();
+
+ expect(step5Issue.status).toBe("done");
+ expect(step5Issue.executionState.status).toBe("completed");
+ expect(step5Issue.executionState.completedStageIds).toHaveLength(2);
+ expect(step5Issue.executionState.lastDecisionOutcome).toBe("approved");
+ });
+
+ test("changes requested: reviewer bounces back to executor", async () => {
+ const issue = await createIssueWithPolicy(ctx, "Signoff changes requested");
+ const issueId = issue.id;
+
+ // Executor marks done → routes to reviewer
+ const doneRes = await agentCheckoutAndPatch(
+ ctx.boardRequest, ctx.executor, issueId, ["in_progress"],
+ { status: "done", comment: "Ready for review." },
+ );
+ expect(doneRes.ok()).toBe(true);
+ expect((await doneRes.json()).status).toBe("in_review");
+
+ // Reviewer requests changes → returns to executor
+ const changesRes = await agentPatch(
+ ctx.boardRequest, ctx.reviewer, issueId,
+ { status: "in_progress", comment: "Needs another pass on edge cases." },
+ );
+ expect(changesRes.ok()).toBe(true);
+ const changesIssue = await changesRes.json();
+
+ expect(changesIssue.status).toBe("in_progress");
+ expect(changesIssue.assigneeAgentId).toBe(ctx.executor.agentId);
+ expect(changesIssue.executionState.status).toBe("changes_requested");
+ expect(changesIssue.executionState.lastDecisionOutcome).toBe("changes_requested");
+
+ // Executor re-submits → goes back to reviewer (same stage)
+ const resubmitRes = await agentCheckoutAndPatch(
+ ctx.boardRequest, ctx.executor, issueId, ["in_progress"],
+ { status: "done", comment: "Fixed the edge cases." },
+ );
+ expect(resubmitRes.ok()).toBe(true);
+ const resubmitIssue = await resubmitRes.json();
+
+ expect(resubmitIssue.status).toBe("in_review");
+ expect(resubmitIssue.assigneeAgentId).toBe(ctx.reviewer.agentId);
+ expect(resubmitIssue.executionState.status).toBe("pending");
+ expect(resubmitIssue.executionState.currentStageType).toBe("review");
+ });
+
+ test("comment required: approval without comment fails", async () => {
+ const issue = await createIssueWithPolicy(ctx, "Signoff comment required");
+ const issueId = issue.id;
+
+ // Executor marks done → routes to reviewer
+ await agentCheckoutAndPatch(
+ ctx.boardRequest, ctx.executor, issueId, ["in_progress"],
+ { status: "done", comment: "Done." },
+ );
+
+ // Reviewer tries to approve without comment → should fail
+ const noCommentRes = await agentPatch(
+ ctx.boardRequest, ctx.reviewer, issueId,
+ { status: "done" },
+ );
+ expect(noCommentRes.ok()).toBe(false);
+ const errorBody = await noCommentRes.json();
+ expect(JSON.stringify(errorBody)).toContain("comment");
+ });
+
+ test("non-participant cannot advance stage", async () => {
+ const issue = await createIssueWithPolicy(ctx, "Signoff access control");
+ const issueId = issue.id;
+
+ // Executor marks done → routes to reviewer
+ const doneRes = await agentCheckoutAndPatch(
+ ctx.boardRequest, ctx.executor, issueId, ["in_progress"],
+ { status: "done", comment: "Done." },
+ );
+ expect(doneRes.ok()).toBe(true);
+
+ // Verify issue is in_review with reviewer
+ const issueRes = await ctx.boardRequest.get(`${BASE_URL}/api/issues/${issueId}`);
+ const inReviewIssue = await issueRes.json();
+ expect(inReviewIssue.status).toBe("in_review");
+ expect(inReviewIssue.assigneeAgentId).toBe(ctx.reviewer.agentId);
+ expect(inReviewIssue.executionState.currentStageType).toBe("review");
+
+ // Non-participant (approver at this stage) tries to advance → should be rejected
+ const advanceRes = await agentPatch(
+ ctx.boardRequest, ctx.approver, issueId,
+ { status: "done", comment: "I'm the approver, not the reviewer." },
+ );
+ expect(advanceRes.ok()).toBe(false);
+ expect(advanceRes.status()).toBeGreaterThanOrEqual(400);
+ });
+
+ test("review-only policy: reviewer approval completes execution", async () => {
+ const issue = await createIssueWithPolicy(ctx, "Signoff review-only", [
+ { type: "review", participants: [{ type: "agent", agentId: ctx.reviewer.agentId }] },
+ ]);
+
+ // Executor marks done → routes to reviewer
+ const doneRes = await agentCheckoutAndPatch(
+ ctx.boardRequest, ctx.executor, issue.id, ["in_progress"],
+ { status: "done", comment: "Ready for review." },
+ );
+ expect(doneRes.ok()).toBe(true);
+ expect((await doneRes.json()).status).toBe("in_review");
+
+ // Reviewer approves → should complete immediately (no approval stage)
+ const approveRes = await agentPatch(
+ ctx.boardRequest, ctx.reviewer, issue.id,
+ { status: "done", comment: "LGTM." },
+ );
+ expect(approveRes.ok()).toBe(true);
+ const doneIssue = await approveRes.json();
+ expect(doneIssue.status).toBe("done");
+ expect(doneIssue.executionState.status).toBe("completed");
+ expect(doneIssue.executionState.completedStageIds).toHaveLength(1);
+ });
+});
diff --git a/ui/src/components/AgentConfigForm.tsx b/ui/src/components/AgentConfigForm.tsx
index 8bc95ef6..3bb2433c 100644
--- a/ui/src/components/AgentConfigForm.tsx
+++ b/ui/src/components/AgentConfigForm.tsx
@@ -44,6 +44,7 @@ import { MarkdownEditor } from "./MarkdownEditor";
import { ChoosePathButton } from "./PathInstructionsModal";
import { OpenCodeLogoIcon } from "./OpenCodeLogoIcon";
import { ReportsToPicker } from "./ReportsToPicker";
+import { EnvVarEditor } from "./EnvVarEditor";
import { shouldShowLegacyWorkingDirectoryField } from "../lib/legacy-agent-config";
import { listAdapterOptions, listVisibleAdapterTypes } from "../adapters/metadata";
import { getAdapterLabel } from "../adapters/adapter-display-registry";
@@ -1082,269 +1083,6 @@ function AdapterTypeDropdown({
);
}
-function EnvVarEditor({
- value,
- secrets,
- onCreateSecret,
- onChange,
-}: {
- value: Record;
- secrets: CompanySecret[];
- onCreateSecret: (name: string, value: string) => Promise;
- onChange: (env: Record | undefined) => void;
-}) {
- type Row = {
- key: string;
- source: "plain" | "secret";
- plainValue: string;
- secretId: string;
- };
-
- function toRows(rec: Record | null | undefined): Row[] {
- if (!rec || typeof rec !== "object") {
- return [{ key: "", source: "plain", plainValue: "", secretId: "" }];
- }
- const entries = Object.entries(rec).map(([k, binding]) => {
- if (typeof binding === "string") {
- return {
- key: k,
- source: "plain" as const,
- plainValue: binding,
- secretId: "",
- };
- }
- if (
- typeof binding === "object" &&
- binding !== null &&
- "type" in binding &&
- (binding as { type?: unknown }).type === "secret_ref"
- ) {
- const recBinding = binding as { secretId?: unknown };
- return {
- key: k,
- source: "secret" as const,
- plainValue: "",
- secretId: typeof recBinding.secretId === "string" ? recBinding.secretId : "",
- };
- }
- if (
- typeof binding === "object" &&
- binding !== null &&
- "type" in binding &&
- (binding as { type?: unknown }).type === "plain"
- ) {
- const recBinding = binding as { value?: unknown };
- return {
- key: k,
- source: "plain" as const,
- plainValue: typeof recBinding.value === "string" ? recBinding.value : "",
- secretId: "",
- };
- }
- return {
- key: k,
- source: "plain" as const,
- plainValue: "",
- secretId: "",
- };
- });
- return [...entries, { key: "", source: "plain", plainValue: "", secretId: "" }];
- }
-
- const [rows, setRows] = useState(() => toRows(value));
- const [sealError, setSealError] = useState(null);
- const valueRef = useRef(value);
- const emittingRef = useRef(false);
-
- // Sync when value identity changes (overlay reset after save).
- // Skip re-sync when the change was triggered by our own emit() to avoid
- // reverting local row state (e.g. a secret-transition dropdown choice).
- useEffect(() => {
- if (emittingRef.current) {
- emittingRef.current = false;
- valueRef.current = value;
- return;
- }
- if (value !== valueRef.current) {
- valueRef.current = value;
- setRows(toRows(value));
- }
- }, [value]);
-
- function emit(nextRows: Row[]) {
- const rec: Record = {};
- for (const row of nextRows) {
- const k = row.key.trim();
- if (!k) continue;
- if (row.source === "secret") {
- if (row.secretId) {
- rec[k] = { type: "secret_ref", secretId: row.secretId, version: "latest" };
- } else {
- // Row is transitioning to secret but user hasn't picked one yet.
- // Preserve the plain value so it isn't silently dropped.
- rec[k] = { type: "plain", value: row.plainValue };
- }
- } else {
- rec[k] = { type: "plain", value: row.plainValue };
- }
- }
- emittingRef.current = true;
- onChange(Object.keys(rec).length > 0 ? rec : undefined);
- }
-
- function updateRow(i: number, patch: Partial) {
- const withPatch = rows.map((r, idx) => (idx === i ? { ...r, ...patch } : r));
- if (
- withPatch[withPatch.length - 1].key ||
- withPatch[withPatch.length - 1].plainValue ||
- withPatch[withPatch.length - 1].secretId
- ) {
- withPatch.push({ key: "", source: "plain", plainValue: "", secretId: "" });
- }
- setRows(withPatch);
- emit(withPatch);
- }
-
- function removeRow(i: number) {
- const next = rows.filter((_, idx) => idx !== i);
- if (
- next.length === 0 ||
- next[next.length - 1].key ||
- next[next.length - 1].plainValue ||
- next[next.length - 1].secretId
- ) {
- next.push({ key: "", source: "plain", plainValue: "", secretId: "" });
- }
- setRows(next);
- emit(next);
- }
-
- function defaultSecretName(key: string): string {
- return key
- .trim()
- .toLowerCase()
- .replace(/[^a-z0-9_]+/g, "_")
- .replace(/^_+|_+$/g, "")
- .slice(0, 64);
- }
-
- async function sealRow(i: number) {
- const row = rows[i];
- if (!row) return;
- const key = row.key.trim();
- const plain = row.plainValue;
- if (!key || plain.length === 0) return;
-
- const suggested = defaultSecretName(key) || "secret";
- const name = window.prompt("Secret name", suggested)?.trim();
- if (!name) return;
-
- try {
- setSealError(null);
- const created = await onCreateSecret(name, plain);
- updateRow(i, {
- source: "secret",
- secretId: created.id,
- });
- } catch (err) {
- setSealError(err instanceof Error ? err.message : "Failed to create secret");
- }
- }
-
- return (
-
- {rows.map((row, i) => {
- const isTrailing =
- i === rows.length - 1 &&
- !row.key &&
- !row.plainValue &&
- !row.secretId;
- return (
-
-
updateRow(i, { key: e.target.value })}
- />
-
- {row.source === "secret" ? (
- <>
-
-
- >
- ) : (
- <>
-
updateRow(i, { plainValue: e.target.value })}
- />
-
- >
- )}
- {!isTrailing ? (
-
- ) : (
-
- )}
-
- );
- })}
- {sealError &&
{sealError}
}
-
- PAPERCLIP_* variables are injected automatically at runtime.
-
-
- );
-}
-
function ModelDropdown({
models,
value,
diff --git a/ui/src/components/EnvVarEditor.tsx b/ui/src/components/EnvVarEditor.tsx
new file mode 100644
index 00000000..01df6d55
--- /dev/null
+++ b/ui/src/components/EnvVarEditor.tsx
@@ -0,0 +1,252 @@
+import { useEffect, useRef, useState } from "react";
+import type { CompanySecret, EnvBinding } from "@paperclipai/shared";
+import { X } from "lucide-react";
+import { cn } from "../lib/utils";
+
+const inputClass =
+ "w-full rounded-md border border-border px-2.5 py-1.5 bg-transparent outline-none text-sm font-mono placeholder:text-muted-foreground/40";
+
+type Row = {
+ key: string;
+ source: "plain" | "secret";
+ plainValue: string;
+ secretId: string;
+};
+
+function toRows(rec: Record | null | undefined): Row[] {
+ if (!rec || typeof rec !== "object") {
+ return [{ key: "", source: "plain", plainValue: "", secretId: "" }];
+ }
+ const entries = Object.entries(rec).map(([key, binding]) => {
+ if (typeof binding === "string") {
+ return { key, source: "plain" as const, plainValue: binding, secretId: "" };
+ }
+ if (
+ typeof binding === "object" &&
+ binding !== null &&
+ "type" in binding &&
+ (binding as { type?: unknown }).type === "secret_ref"
+ ) {
+ const record = binding as { secretId?: unknown };
+ return {
+ key,
+ source: "secret" as const,
+ plainValue: "",
+ secretId: typeof record.secretId === "string" ? record.secretId : "",
+ };
+ }
+ if (
+ typeof binding === "object" &&
+ binding !== null &&
+ "type" in binding &&
+ (binding as { type?: unknown }).type === "plain"
+ ) {
+ const record = binding as { value?: unknown };
+ return {
+ key,
+ source: "plain" as const,
+ plainValue: typeof record.value === "string" ? record.value : "",
+ secretId: "",
+ };
+ }
+ return { key, source: "plain" as const, plainValue: "", secretId: "" };
+ });
+ return [...entries, { key: "", source: "plain", plainValue: "", secretId: "" }];
+}
+
+export function EnvVarEditor({
+ value,
+ secrets,
+ onCreateSecret,
+ onChange,
+}: {
+ value: Record;
+ secrets: CompanySecret[];
+ onCreateSecret: (name: string, value: string) => Promise;
+ onChange: (env: Record | undefined) => void;
+}) {
+ const [rows, setRows] = useState(() => toRows(value));
+ const [sealError, setSealError] = useState(null);
+ const valueRef = useRef(value);
+ const emittingRef = useRef(false);
+
+ useEffect(() => {
+ if (emittingRef.current) {
+ emittingRef.current = false;
+ valueRef.current = value;
+ return;
+ }
+ if (value !== valueRef.current) {
+ valueRef.current = value;
+ setRows(toRows(value));
+ }
+ }, [value]);
+
+ function emit(nextRows: Row[]) {
+ const rec: Record = {};
+ for (const row of nextRows) {
+ const key = row.key.trim();
+ if (!key) continue;
+ if (row.source === "secret") {
+ if (row.secretId) {
+ rec[key] = { type: "secret_ref", secretId: row.secretId, version: "latest" };
+ } else {
+ rec[key] = { type: "plain", value: row.plainValue };
+ }
+ } else {
+ rec[key] = { type: "plain", value: row.plainValue };
+ }
+ }
+ emittingRef.current = true;
+ onChange(Object.keys(rec).length > 0 ? rec : undefined);
+ }
+
+ function updateRow(index: number, patch: Partial) {
+ const withPatch = rows.map((row, rowIndex) => (rowIndex === index ? { ...row, ...patch } : row));
+ if (
+ withPatch[withPatch.length - 1].key ||
+ withPatch[withPatch.length - 1].plainValue ||
+ withPatch[withPatch.length - 1].secretId
+ ) {
+ withPatch.push({ key: "", source: "plain", plainValue: "", secretId: "" });
+ }
+ setRows(withPatch);
+ emit(withPatch);
+ }
+
+ function removeRow(index: number) {
+ const next = rows.filter((_, rowIndex) => rowIndex !== index);
+ if (
+ next.length === 0 ||
+ next[next.length - 1].key ||
+ next[next.length - 1].plainValue ||
+ next[next.length - 1].secretId
+ ) {
+ next.push({ key: "", source: "plain", plainValue: "", secretId: "" });
+ }
+ setRows(next);
+ emit(next);
+ }
+
+ function defaultSecretName(key: string) {
+ return key
+ .trim()
+ .toLowerCase()
+ .replace(/[^a-z0-9_]+/g, "_")
+ .replace(/^_+|_+$/g, "")
+ .slice(0, 64);
+ }
+
+ async function sealRow(index: number) {
+ const row = rows[index];
+ if (!row) return;
+ const key = row.key.trim();
+ const plain = row.plainValue;
+ if (!key || plain.length === 0) return;
+
+ const suggested = defaultSecretName(key) || "secret";
+ const name = window.prompt("Secret name", suggested)?.trim();
+ if (!name) return;
+
+ try {
+ setSealError(null);
+ const created = await onCreateSecret(name, plain);
+ updateRow(index, { source: "secret", secretId: created.id });
+ } catch (error) {
+ setSealError(error instanceof Error ? error.message : "Failed to create secret");
+ }
+ }
+
+ return (
+
+ {rows.map((row, index) => {
+ const isTrailing =
+ index === rows.length - 1 &&
+ !row.key &&
+ !row.plainValue &&
+ !row.secretId;
+ return (
+
+
updateRow(index, { key: event.target.value })}
+ />
+
+ {row.source === "secret" ? (
+ <>
+
+
+ >
+ ) : (
+ <>
+
updateRow(index, { plainValue: event.target.value })}
+ />
+
+ >
+ )}
+ {!isTrailing ? (
+
+ ) : (
+
+ )}
+
+ );
+ })}
+ {sealError &&
{sealError}
}
+
+ PAPERCLIP_* variables are injected automatically at runtime.
+
+
+ );
+}
diff --git a/ui/src/components/ProjectProperties.tsx b/ui/src/components/ProjectProperties.tsx
index 9fe4d943..85172fd4 100644
--- a/ui/src/components/ProjectProperties.tsx
+++ b/ui/src/components/ProjectProperties.tsx
@@ -7,6 +7,7 @@ import { cn, formatDate } from "../lib/utils";
import { goalsApi } from "../api/goals";
import { instanceSettingsApi } from "../api/instanceSettings";
import { projectsApi } from "../api/projects";
+import { secretsApi } from "../api/secrets";
import { useCompany } from "../context/CompanyContext";
import { queryKeys } from "../lib/queryKeys";
import { statusBadge, statusBadgeDefault } from "../lib/status-colors";
@@ -19,6 +20,7 @@ import { ChoosePathButton } from "./PathInstructionsModal";
import { ToggleSwitch } from "@/components/ui/toggle-switch";
import { DraftInput } from "./agent-config-primitives";
import { InlineEditor } from "./InlineEditor";
+import { EnvVarEditor } from "./EnvVarEditor";
const PROJECT_STATUSES = [
{ value: "backlog", label: "Backlog" },
@@ -43,6 +45,7 @@ export type ProjectConfigFieldKey =
| "description"
| "status"
| "goals"
+ | "env"
| "execution_workspace_enabled"
| "execution_workspace_default_mode"
| "execution_workspace_base_ref"
@@ -245,6 +248,21 @@ export function ProjectProperties({ project, onUpdate, onFieldUpdate, getFieldSa
queryFn: () => instanceSettingsApi.getExperimental(),
retry: false,
});
+ const { data: availableSecrets = [] } = useQuery({
+ queryKey: selectedCompanyId ? queryKeys.secrets.list(selectedCompanyId) : ["secrets", "none"],
+ queryFn: () => secretsApi.list(selectedCompanyId!),
+ enabled: Boolean(selectedCompanyId),
+ });
+ const createSecret = useMutation({
+ mutationFn: (input: { name: string; value: string }) => {
+ if (!selectedCompanyId) throw new Error("Select a company to create secrets");
+ return secretsApi.create(selectedCompanyId, input);
+ },
+ onSuccess: () => {
+ if (!selectedCompanyId) return;
+ queryClient.invalidateQueries({ queryKey: queryKeys.secrets.list(selectedCompanyId) });
+ },
+ });
const linkedGoalIds = project.goalIds.length > 0
? project.goalIds
@@ -583,6 +601,26 @@ export function ProjectProperties({ project, onUpdate, onFieldUpdate, getFieldSa
)}
+ }
+ alignStart
+ valueClassName="space-y-2"
+ >
+
+
{
+ const created = await createSecret.mutateAsync({ name, value });
+ return created;
+ }}
+ onChange={(env) => commitField("env", { env: env ?? null })}
+ />
+
+ Applied to all runs for issues in this project. Project values override agent env on key conflicts.
+
+
+
}>
{formatDate(project.createdAt)}
diff --git a/ui/src/components/RoutineRunVariablesDialog.test.tsx b/ui/src/components/RoutineRunVariablesDialog.test.tsx
index ea59a92b..03b18da0 100644
--- a/ui/src/components/RoutineRunVariablesDialog.test.tsx
+++ b/ui/src/components/RoutineRunVariablesDialog.test.tsx
@@ -58,6 +58,7 @@ function createProject(): Project {
leadAgentId: null,
targetDate: null,
color: "#22c55e",
+ env: null,
pauseReason: null,
pausedAt: null,
archivedAt: null,
diff --git a/ui/src/lib/company-portability-sidebar.test.ts b/ui/src/lib/company-portability-sidebar.test.ts
index 1bc7b06d..68d0e13f 100644
--- a/ui/src/lib/company-portability-sidebar.test.ts
+++ b/ui/src/lib/company-portability-sidebar.test.ts
@@ -45,6 +45,7 @@ function makeProject(id: string, name: string): Project {
leadAgentId: null,
targetDate: null,
color: null,
+ env: null,
pauseReason: null,
pausedAt: null,
executionWorkspacePolicy: null,
diff --git a/ui/src/lib/company-routes.test.ts b/ui/src/lib/company-routes.test.ts
index d6dc2668..ced25744 100644
--- a/ui/src/lib/company-routes.test.ts
+++ b/ui/src/lib/company-routes.test.ts
@@ -20,4 +20,29 @@ describe("company routes", () => {
"/execution-workspaces/workspace-123",
);
});
+
+ /**
+ * Regression tests for https://github.com/paperclipai/paperclip/issues/2910
+ *
+ * The Export and Import links on the Company Settings page used plain
+ * `` anchors which bypass the router's Link
+ * wrapper. Without the wrapper, the company prefix is never applied and
+ * the links resolve to `/company/export` instead of `/:prefix/company/export`,
+ * producing a "Company not found" error.
+ *
+ * The fix replaces the `` elements with the prefix-aware `` from
+ * `@/lib/router`. These tests assert that the underlying `applyCompanyPrefix`
+ * utility (used by that Link) correctly rewrites the export/import paths.
+ */
+ it("applies company prefix to /company/export", () => {
+ expect(applyCompanyPrefix("/company/export", "PAP")).toBe("/PAP/company/export");
+ });
+
+ it("applies company prefix to /company/import", () => {
+ expect(applyCompanyPrefix("/company/import", "PAP")).toBe("/PAP/company/import");
+ });
+
+ it("does not double-apply the prefix if already present", () => {
+ expect(applyCompanyPrefix("/PAP/company/export", "PAP")).toBe("/PAP/company/export");
+ });
});
diff --git a/ui/src/pages/CompanySettings.tsx b/ui/src/pages/CompanySettings.tsx
index e0db3706..30b39627 100644
--- a/ui/src/pages/CompanySettings.tsx
+++ b/ui/src/pages/CompanySettings.tsx
@@ -1,4 +1,5 @@
import { ChangeEvent, useEffect, useState } from "react";
+import { Link } from "@/lib/router";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { DEFAULT_FEEDBACK_DATA_SHARING_TERMS_VERSION } from "@paperclipai/shared";
import { useCompany } from "../context/CompanyContext";
@@ -548,16 +549,16 @@ export function CompanySettings() {