From 57229d0f24961061927078a8a3b2fad2b8990778 Mon Sep 17 00:00:00 2001 From: Dotta <34892728+cryppadotta@users.noreply.github.com> Date: Sun, 3 May 2026 08:58:53 -0500 Subject: [PATCH] [codex] Add issue monitor liveness controls (#4988) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Thinking Path > - Paperclip is a control plane for autonomous AI companies where work must stay observable, governable, and recoverable. > - The task/heartbeat subsystem owns agent execution continuity, issue state transitions, and visible recovery behavior. > - Waiting on an external service is not the same as being blocked when the assignee still owns a future check. > - The gap was that agents had no first-class one-shot monitor state for external-service waits, so recovery could look stalled or require ad hoc comments. > - This pull request adds bounded issue monitors that can wake the owner, clear exhausted waits, and produce explicit recovery behavior. > - It also surfaces monitor status in the board UI and documents when to use monitors versus `blocked`. > - The benefit is clearer liveness semantics for asynchronous waits without weakening single-assignee task ownership. ## What Changed - Added issue monitor fields, shared types, validators, constants, and an idempotent `0075` migration for scheduled monitor state. - Added server-side monitor scheduling, dispatch, recovery bounds, activity logging, and external-ref redaction. - Added board/agent route coverage for monitor permissions and child monitor scheduling. - Added issue detail/property UI for monitor state, a monitor activity card, and Storybook stories for review surfaces. - Documented monitor semantics and recovery policy behavior in `doc/execution-semantics.md`. - Addressed Greptile review feedback by preserving monitor state in skipped-stage builders and making board monitor saves send `scheduledBy: "board"`. ## Verification - `pnpm install --frozen-lockfile` - `pnpm run preflight:workspace-links && pnpm exec vitest run server/src/__tests__/issue-execution-policy-routes.test.ts server/src/__tests__/issue-execution-policy.test.ts server/src/__tests__/issue-monitor-scheduler.test.ts server/src/__tests__/recovery-classifiers.test.ts ui/src/components/IssueMonitorActivityCard.test.tsx ui/src/components/IssueProperties.test.tsx ui/src/lib/activity-format.test.ts` - First run passed 5 files and failed to collect 2 server suites because the worktree was missing the optional `acpx/runtime` dependency. - After `pnpm install --frozen-lockfile`, reran the 2 failed suites successfully. - `pnpm exec vitest run server/src/__tests__/issue-monitor-scheduler.test.ts server/src/__tests__/recovery-classifiers.test.ts` - `pnpm --filter @paperclipai/shared typecheck && pnpm --filter @paperclipai/db typecheck && pnpm --filter @paperclipai/server typecheck && pnpm --filter @paperclipai/ui typecheck` - `pnpm exec vitest run server/src/__tests__/issue-execution-policy.test.ts ui/src/components/IssueProperties.test.tsx` - `pnpm --filter @paperclipai/server typecheck && pnpm --filter @paperclipai/ui typecheck` - `pnpm exec vitest run ui/src/components/IssueMonitorActivityCard.test.tsx ui/src/components/IssueProperties.test.tsx` - `pnpm --filter @paperclipai/ui typecheck` - Storybook screenshot captured from `http://127.0.0.1:6006/iframe.html?viewMode=story&id=product-issue-monitor-surfaces--monitor-surfaces` with Playwright. ## Screenshots ![Issue monitor Storybook surfaces](https://raw.githubusercontent.com/paperclipai/paperclip/PAP-2945-when-a-task-is-waiting-for-an-_external-service_-what-state-should-it-be-in-and-what-recovery-method-could-it-h/docs/pr-screenshots/pap-2945/monitor-surfaces.png) ## Risks - Medium: this changes heartbeat recovery behavior for scheduled external-service waits, so regressions could affect wake timing or recovery issue creation. - Migration risk is reduced by using `IF NOT EXISTS` for the new issue monitor columns and index. - External monitor references are treated as secret-adjacent and are intentionally omitted from visible activity/wake payloads. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - OpenAI Codex, GPT-5 coding agent with repository tool use and terminal execution. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] If this change affects the UI, I have included before/after screenshots or Storybook review surfaces - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip --- doc/execution-semantics.md | 31 +- .../pap-2945/monitor-surfaces.png | Bin 0 -> 184552 bytes .../0075_cultured_sebastian_shaw.sql | 7 + .../db/src/migrations/meta/0075_snapshot.json | 15945 ++++++++++++++++ packages/db/src/migrations/meta/_journal.json | 7 + packages/db/src/schema/issues.ts | 7 + packages/shared/src/constants.ts | 30 + packages/shared/src/index.ts | 12 + packages/shared/src/types/index.ts | 2 + packages/shared/src/types/issue.ts | 41 + packages/shared/src/validators/issue.ts | 36 + .../issue-execution-policy-routes.test.ts | 220 +- .../__tests__/issue-execution-policy.test.ts | 185 + .../__tests__/issue-monitor-scheduler.test.ts | 448 + .../__tests__/recovery-classifiers.test.ts | 94 + server/src/routes/issues.ts | 183 +- server/src/services/heartbeat.ts | 704 +- server/src/services/issue-execution-policy.ts | 494 +- server/src/services/issues.ts | 16 + .../services/recovery/issue-graph-liveness.ts | 45 + server/src/services/recovery/service.ts | 4 + ui/src/api/issues.ts | 1 + .../IssueMonitorActivityCard.test.tsx | 196 + .../components/IssueMonitorActivityCard.tsx | 71 + ui/src/components/IssueProperties.test.tsx | 80 + ui/src/components/IssueProperties.tsx | 167 +- ui/src/lib/activity-format.test.ts | 8 + ui/src/lib/activity-format.ts | 24 + ui/src/lib/issue-execution-policy.ts | 4 +- ui/src/lib/issue-monitor.ts | 12 + ui/src/pages/IssueDetail.tsx | 35 + .../stories/monitor-surfaces.stories.tsx | 235 + 32 files changed, 19324 insertions(+), 20 deletions(-) create mode 100644 docs/pr-screenshots/pap-2945/monitor-surfaces.png create mode 100644 packages/db/src/migrations/0075_cultured_sebastian_shaw.sql create mode 100644 packages/db/src/migrations/meta/0075_snapshot.json create mode 100644 server/src/__tests__/issue-monitor-scheduler.test.ts create mode 100644 ui/src/components/IssueMonitorActivityCard.test.tsx create mode 100644 ui/src/components/IssueMonitorActivityCard.tsx create mode 100644 ui/src/lib/issue-monitor.ts create mode 100644 ui/storybook/stories/monitor-surfaces.stories.tsx diff --git a/doc/execution-semantics.md b/doc/execution-semantics.md index b014253b..9b561f28 100644 --- a/doc/execution-semantics.md +++ b/doc/execution-semantics.md @@ -67,13 +67,15 @@ This is the right state for: - waiting on another issue - waiting on a human decision -- waiting on an external dependency or system +- waiting on an external dependency or system when Paperclip does not own a scheduled re-check - work that automatic recovery could not safely continue ### `in_review` Execution work is paused because the next move belongs to a reviewer or approver, not the current executor. +An external review service can also be a valid review path when the issue keeps an agent assignee and has an active one-shot monitor that will wake that assignee to check the service later. + ### `done` The work is complete and terminal. @@ -164,6 +166,7 @@ The valid action-path primitives are: - a queued wake or continuation that can be delivered to the responsible agent - a typed execution-policy participant, such as `executionState.currentParticipant` - a pending issue-thread interaction or linked approval that is waiting for a specific responder +- a one-shot issue monitor (`executionPolicy.monitor.nextCheckAt`) that will wake the assignee for a future check - a human owner via `assigneeUserId` - a first-class blocker chain whose unresolved leaf issues are themselves healthy - an open explicit recovery issue that names the owner and action needed to restore liveness @@ -188,6 +191,7 @@ A healthy active-work state means at least one of these is true: - there is an active run for the issue - there is already a queued continuation wake +- there is an active one-shot monitor that will wake the assignee for a future check - there is an open explicit recovery issue for the lost execution path An agent-owned `in_progress` issue is stalled when it has no active run, no queued continuation, and no explicit recovery surface. A still-running but silent process is not automatically stalled; it is handled by the active-run watchdog contract. @@ -202,11 +206,34 @@ A healthy `in_review` issue has at least one valid action path: - a pending issue-thread interaction or linked approval waiting for a named responder - a human owner via `assigneeUserId` - an active run or queued wake that is expected to process the review state +- an active one-shot monitor for an external service or async review loop that the assignee owns - an open explicit recovery issue for an ambiguous review handoff Agent-assigned `in_review` with no typed participant is only healthy when one of the other paths exists. Assignment to the same agent that produced the handoff is not, by itself, a review path. -An `in_review` issue is stalled when it has no typed participant, no pending interaction or approval, no user owner, no active run, no queued wake, and no explicit recovery issue. Paperclip should surface that state as recovery work rather than silently completing the issue or leaving blocker chains parked indefinitely. +An `in_review` issue is stalled when it has no typed participant, no pending interaction or approval, no user owner, no active monitor, no active run, no queued wake, and no explicit recovery issue. Paperclip should surface that state as recovery work rather than silently completing the issue or leaving blocker chains parked indefinitely. + +### Issue monitors + +An issue monitor is a one-shot deferred action path for agent-owned issues in `in_progress` or `in_review`. + +Use a monitor when the current assignee owns a future check against an async system or external service. Examples include Greptile review loops, GitHub checks, Vercel deployments, or provider jobs where the agent should come back later and decide what happens next. + +Monitor policy lives under `executionPolicy.monitor` and includes: + +- `nextCheckAt`: when Paperclip should wake the assignee +- `notes`: non-secret instructions for what the assignee should check +- `serviceName`: optional non-secret external-service context +- `externalRef`: optional external-service reference input; Paperclip treats it as secret-adjacent, redacts it before persistence/visibility, and omits it from activity and wake payloads +- `timeoutAt`, `maxAttempts`, and `recoveryPolicy`: optional recovery hints for bounded waits + +Monitors are not recurring intervals. When a monitor fires, Paperclip clears the scheduled monitor and queues an `issue_monitor_due` wake for the assignee. If the external service is still pending, the assignee must explicitly re-arm the monitor with a new `nextCheckAt`. If the issue moves to `done`, `cancelled`, an invalid status, or a human/unassigned owner, the monitor is cleared. + +Because `serviceName` and `notes` remain visible in issue activity and wake context, operators should keep them short and non-secret. Put enough context for the assignee to know what to inspect, but do not include signed URLs, bearer tokens, customer secrets, tenant-private identifiers, or provider links with embedded credentials. + +Monitor bounds are enforced. Paperclip rejects attempts to re-arm a monitor whose `timeoutAt` or `maxAttempts` is already exhausted. When a scheduled monitor reaches an exhausted bound at trigger time, Paperclip clears it and follows `recoveryPolicy`: `wake_owner` queues a bounded recovery wake for the assignee, `create_recovery_issue` opens visible recovery work, and `escalate_to_board` records a board-visible escalation comment/activity. + +Use `blocked` instead of a monitor when no Paperclip assignee owns a responsible polling path. In that case, name the external owner/action or create first-class recovery/blocker work. ### `blocked` diff --git a/docs/pr-screenshots/pap-2945/monitor-surfaces.png b/docs/pr-screenshots/pap-2945/monitor-surfaces.png new file mode 100644 index 0000000000000000000000000000000000000000..a84ad637d2d04dc639074ee68e43c24a548a0e3d GIT binary patch literal 184552 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

#p~7z07ungFjvU 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

4wzijHYUB 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 z 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 statement-breakpoint +ALTER TABLE "issues" ADD COLUMN IF NOT EXISTS "monitor_wake_requested_at" timestamp with time zone;--> statement-breakpoint +ALTER TABLE "issues" ADD COLUMN IF NOT EXISTS "monitor_last_triggered_at" timestamp with time zone;--> statement-breakpoint +ALTER TABLE "issues" ADD COLUMN IF NOT EXISTS "monitor_attempt_count" integer DEFAULT 0 NOT NULL;--> statement-breakpoint +ALTER TABLE "issues" ADD COLUMN IF NOT EXISTS "monitor_notes" text;--> statement-breakpoint +ALTER TABLE "issues" ADD COLUMN IF NOT EXISTS "monitor_scheduled_by" text;--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "issues_company_monitor_due_idx" ON "issues" USING btree ("company_id","monitor_next_check_at"); diff --git a/packages/db/src/migrations/meta/0075_snapshot.json b/packages/db/src/migrations/meta/0075_snapshot.json new file mode 100644 index 00000000..e1938e2b --- /dev/null +++ b/packages/db/src/migrations/meta/0075_snapshot.json @@ -0,0 +1,15945 @@ +{ + "id": "fdc9cd8b-5423-4d64-b255-9bc1497fdd6a", + "prevId": "12b3d91e-98ee-4a48-aecb-891f5bde6eda", + "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" + }, + "default_environment_id": { + "name": "default_environment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "budget_monthly_cents": { + "name": "budget_monthly_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "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": {} + }, + "agents_company_default_environment_idx": { + "name": "agents_company_default_environment_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "default_environment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agents_company_id_companies_id_fk": { + "name": "agents_company_id_companies_id_fk", + "tableFrom": "agents", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agents_reports_to_agents_id_fk": { + "name": "agents_reports_to_agents_id_fk", + "tableFrom": "agents", + "tableTo": "agents", + "columnsFrom": [ + "reports_to" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agents_default_environment_id_environments_id_fk": { + "name": "agents_default_environment_id_environments_id_fk", + "tableFrom": "agents", + "tableTo": "environments", + "columnsFrom": [ + "default_environment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "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 + }, + "attachment_max_bytes": { + "name": "attachment_max_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 10485760 + }, + "require_board_approval_for_new_agents": { + "name": "require_board_approval_for_new_agents", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "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.company_user_sidebar_preferences": { + "name": "company_user_sidebar_preferences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "project_order": { + "name": "project_order", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "company_user_sidebar_preferences_company_idx": { + "name": "company_user_sidebar_preferences_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_user_sidebar_preferences_user_idx": { + "name": "company_user_sidebar_preferences_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "company_user_sidebar_preferences_company_user_uq": { + "name": "company_user_sidebar_preferences_company_user_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "company_user_sidebar_preferences_company_id_companies_id_fk": { + "name": "company_user_sidebar_preferences_company_id_companies_id_fk", + "tableFrom": "company_user_sidebar_preferences", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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.environment_leases": { + "name": "environment_leases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "environment_id": { + "name": "environment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "execution_workspace_id": { + "name": "execution_workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "heartbeat_run_id": { + "name": "heartbeat_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "lease_policy": { + "name": "lease_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'ephemeral'" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_lease_id": { + "name": "provider_lease_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "acquired_at": { + "name": "acquired_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "released_at": { + "name": "released_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cleanup_status": { + "name": "cleanup_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "environment_leases_company_environment_status_idx": { + "name": "environment_leases_company_environment_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "environment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_leases_company_execution_workspace_idx": { + "name": "environment_leases_company_execution_workspace_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_leases_company_issue_idx": { + "name": "environment_leases_company_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_leases_heartbeat_run_idx": { + "name": "environment_leases_heartbeat_run_idx", + "columns": [ + { + "expression": "heartbeat_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_leases_company_last_used_idx": { + "name": "environment_leases_company_last_used_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_used_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_leases_provider_lease_idx": { + "name": "environment_leases_provider_lease_idx", + "columns": [ + { + "expression": "provider_lease_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "environment_leases_company_id_companies_id_fk": { + "name": "environment_leases_company_id_companies_id_fk", + "tableFrom": "environment_leases", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "environment_leases_environment_id_environments_id_fk": { + "name": "environment_leases_environment_id_environments_id_fk", + "tableFrom": "environment_leases", + "tableTo": "environments", + "columnsFrom": [ + "environment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "environment_leases_execution_workspace_id_execution_workspaces_id_fk": { + "name": "environment_leases_execution_workspace_id_execution_workspaces_id_fk", + "tableFrom": "environment_leases", + "tableTo": "execution_workspaces", + "columnsFrom": [ + "execution_workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "environment_leases_issue_id_issues_id_fk": { + "name": "environment_leases_issue_id_issues_id_fk", + "tableFrom": "environment_leases", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "environment_leases_heartbeat_run_id_heartbeat_runs_id_fk": { + "name": "environment_leases_heartbeat_run_id_heartbeat_runs_id_fk", + "tableFrom": "environment_leases", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "heartbeat_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.environments": { + "name": "environments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "driver": { + "name": "driver", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'local'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "environments_company_status_idx": { + "name": "environments_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": {} + }, + "environments_company_driver_idx": { + "name": "environments_company_driver_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "driver", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"environments\".\"driver\" = 'local'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "environments_company_name_idx": { + "name": "environments_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": { + "environments_company_id_companies_id_fk": { + "name": "environments_company_id_companies_id_fk", + "tableFrom": "environments", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "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_run_watchdog_decisions": { + "name": "heartbeat_run_watchdog_decisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "evaluation_issue_id": { + "name": "evaluation_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "decision": { + "name": "decision", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "snoozed_until": { + "name": "snoozed_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_run_id": { + "name": "created_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "heartbeat_run_watchdog_decisions_company_run_created_idx": { + "name": "heartbeat_run_watchdog_decisions_company_run_created_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_run_watchdog_decisions_company_run_snooze_idx": { + "name": "heartbeat_run_watchdog_decisions_company_run_snooze_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "snoozed_until", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "heartbeat_run_watchdog_decisions_company_id_companies_id_fk": { + "name": "heartbeat_run_watchdog_decisions_company_id_companies_id_fk", + "tableFrom": "heartbeat_run_watchdog_decisions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "heartbeat_run_watchdog_decisions_run_id_heartbeat_runs_id_fk": { + "name": "heartbeat_run_watchdog_decisions_run_id_heartbeat_runs_id_fk", + "tableFrom": "heartbeat_run_watchdog_decisions", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "heartbeat_run_watchdog_decisions_evaluation_issue_id_issues_id_fk": { + "name": "heartbeat_run_watchdog_decisions_evaluation_issue_id_issues_id_fk", + "tableFrom": "heartbeat_run_watchdog_decisions", + "tableTo": "issues", + "columnsFrom": [ + "evaluation_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "heartbeat_run_watchdog_decisions_created_by_agent_id_agents_id_fk": { + "name": "heartbeat_run_watchdog_decisions_created_by_agent_id_agents_id_fk", + "tableFrom": "heartbeat_run_watchdog_decisions", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "heartbeat_run_watchdog_decisions_created_by_run_id_heartbeat_runs_id_fk": { + "name": "heartbeat_run_watchdog_decisions_created_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "heartbeat_run_watchdog_decisions", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "created_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.heartbeat_runs": { + "name": "heartbeat_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "invocation_source": { + "name": "invocation_source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'on_demand'" + }, + "trigger_detail": { + "name": "trigger_detail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "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_group_id": { + "name": "process_group_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "process_started_at": { + "name": "process_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_output_at": { + "name": "last_output_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_output_seq": { + "name": "last_output_seq", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_output_stream": { + "name": "last_output_stream", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_output_bytes": { + "name": "last_output_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "retry_of_run_id": { + "name": "retry_of_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "process_loss_retry_count": { + "name": "process_loss_retry_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "scheduled_retry_at": { + "name": "scheduled_retry_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "scheduled_retry_attempt": { + "name": "scheduled_retry_attempt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "scheduled_retry_reason": { + "name": "scheduled_retry_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "issue_comment_status": { + "name": "issue_comment_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'not_applicable'" + }, + "issue_comment_satisfied_by_comment_id": { + "name": "issue_comment_satisfied_by_comment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issue_comment_retry_queued_at": { + "name": "issue_comment_retry_queued_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "liveness_state": { + "name": "liveness_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "liveness_reason": { + "name": "liveness_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "continuation_attempt": { + "name": "continuation_attempt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_useful_action_at": { + "name": "last_useful_action_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "next_action": { + "name": "next_action", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "context_snapshot": { + "name": "context_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "heartbeat_runs_company_agent_started_idx": { + "name": "heartbeat_runs_company_agent_started_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_runs_company_liveness_idx": { + "name": "heartbeat_runs_company_liveness_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "liveness_state", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_runs_company_status_last_output_idx": { + "name": "heartbeat_runs_company_status_last_output_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_output_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "heartbeat_runs_company_status_process_started_idx": { + "name": "heartbeat_runs_company_status_process_started_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "process_started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "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.inbox_dismissals": { + "name": "inbox_dismissals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "item_key": { + "name": "item_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dismissed_at": { + "name": "dismissed_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": { + "inbox_dismissals_company_user_idx": { + "name": "inbox_dismissals_company_user_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inbox_dismissals_company_item_idx": { + "name": "inbox_dismissals_company_item_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "item_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inbox_dismissals_company_user_item_idx": { + "name": "inbox_dismissals_company_user_item_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "item_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "inbox_dismissals_company_id_companies_id_fk": { + "name": "inbox_dismissals_company_id_companies_id_fk", + "tableFrom": "inbox_dismissals", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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": {} + }, + "issue_comments_body_search_idx": { + "name": "issue_comments_body_search_idx", + "columns": [ + { + "expression": "body", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "issue_comments_company_id_companies_id_fk": { + "name": "issue_comments_company_id_companies_id_fk", + "tableFrom": "issue_comments", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_comments_issue_id_issues_id_fk": { + "name": "issue_comments_issue_id_issues_id_fk", + "tableFrom": "issue_comments", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "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_execution_decisions": { + "name": "issue_execution_decisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "stage_id": { + "name": "stage_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "stage_type": { + "name": "stage_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_agent_id": { + "name": "actor_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_run_id": { + "name": "created_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_execution_decisions_company_issue_idx": { + "name": "issue_execution_decisions_company_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_execution_decisions_stage_idx": { + "name": "issue_execution_decisions_stage_idx", + "columns": [ + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "stage_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_execution_decisions_company_id_companies_id_fk": { + "name": "issue_execution_decisions_company_id_companies_id_fk", + "tableFrom": "issue_execution_decisions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_execution_decisions_issue_id_issues_id_fk": { + "name": "issue_execution_decisions_issue_id_issues_id_fk", + "tableFrom": "issue_execution_decisions", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_execution_decisions_actor_agent_id_agents_id_fk": { + "name": "issue_execution_decisions_actor_agent_id_agents_id_fk", + "tableFrom": "issue_execution_decisions", + "tableTo": "agents", + "columnsFrom": [ + "actor_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_execution_decisions_created_by_run_id_heartbeat_runs_id_fk": { + "name": "issue_execution_decisions_created_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_execution_decisions", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "created_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_inbox_archives": { + "name": "issue_inbox_archives", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "archived_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_reference_mentions": { + "name": "issue_reference_mentions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_issue_id": { + "name": "source_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "target_issue_id": { + "name": "target_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_kind": { + "name": "source_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_record_id": { + "name": "source_record_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "document_key": { + "name": "document_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "matched_text": { + "name": "matched_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_reference_mentions_company_source_issue_idx": { + "name": "issue_reference_mentions_company_source_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_reference_mentions_company_target_issue_idx": { + "name": "issue_reference_mentions_company_target_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_reference_mentions_company_issue_pair_idx": { + "name": "issue_reference_mentions_company_issue_pair_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_reference_mentions_company_source_mention_record_uq": { + "name": "issue_reference_mentions_company_source_mention_record_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_record_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issue_reference_mentions\".\"source_record_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_reference_mentions_company_source_mention_null_record_uq": { + "name": "issue_reference_mentions_company_source_mention_null_record_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issue_reference_mentions\".\"source_record_id\" is null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_reference_mentions_company_id_companies_id_fk": { + "name": "issue_reference_mentions_company_id_companies_id_fk", + "tableFrom": "issue_reference_mentions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_reference_mentions_source_issue_id_issues_id_fk": { + "name": "issue_reference_mentions_source_issue_id_issues_id_fk", + "tableFrom": "issue_reference_mentions", + "tableTo": "issues", + "columnsFrom": [ + "source_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_reference_mentions_target_issue_id_issues_id_fk": { + "name": "issue_reference_mentions_target_issue_id_issues_id_fk", + "tableFrom": "issue_reference_mentions", + "tableTo": "issues", + "columnsFrom": [ + "target_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_relations": { + "name": "issue_relations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "related_issue_id": { + "name": "related_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_relations_company_issue_idx": { + "name": "issue_relations_company_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_relations_company_related_issue_idx": { + "name": "issue_relations_company_related_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "related_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_relations_company_type_idx": { + "name": "issue_relations_company_type_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_relations_company_edge_uq": { + "name": "issue_relations_company_edge_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "related_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_relations_company_id_companies_id_fk": { + "name": "issue_relations_company_id_companies_id_fk", + "tableFrom": "issue_relations", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_relations_issue_id_issues_id_fk": { + "name": "issue_relations_issue_id_issues_id_fk", + "tableFrom": "issue_relations", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_relations_related_issue_id_issues_id_fk": { + "name": "issue_relations_related_issue_id_issues_id_fk", + "tableFrom": "issue_relations", + "tableTo": "issues", + "columnsFrom": [ + "related_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_relations_created_by_agent_id_agents_id_fk": { + "name": "issue_relations_created_by_agent_id_agents_id_fk", + "tableFrom": "issue_relations", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_thread_interactions": { + "name": "issue_thread_interactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "continuation_policy": { + "name": "continuation_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'wake_assignee'" + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_comment_id": { + "name": "source_comment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source_run_id": { + "name": "source_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resolved_by_agent_id": { + "name": "resolved_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "resolved_by_user_id": { + "name": "resolved_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_thread_interactions_issue_idx": { + "name": "issue_thread_interactions_issue_idx", + "columns": [ + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_thread_interactions_company_issue_created_at_idx": { + "name": "issue_thread_interactions_company_issue_created_at_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_thread_interactions_company_issue_status_idx": { + "name": "issue_thread_interactions_company_issue_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_thread_interactions_company_issue_idempotency_uq": { + "name": "issue_thread_interactions_company_issue_idempotency_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issue_thread_interactions\".\"idempotency_key\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_thread_interactions_source_comment_idx": { + "name": "issue_thread_interactions_source_comment_idx", + "columns": [ + { + "expression": "source_comment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_thread_interactions_company_id_companies_id_fk": { + "name": "issue_thread_interactions_company_id_companies_id_fk", + "tableFrom": "issue_thread_interactions", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_thread_interactions_issue_id_issues_id_fk": { + "name": "issue_thread_interactions_issue_id_issues_id_fk", + "tableFrom": "issue_thread_interactions", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_thread_interactions_source_comment_id_issue_comments_id_fk": { + "name": "issue_thread_interactions_source_comment_id_issue_comments_id_fk", + "tableFrom": "issue_thread_interactions", + "tableTo": "issue_comments", + "columnsFrom": [ + "source_comment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_thread_interactions_source_run_id_heartbeat_runs_id_fk": { + "name": "issue_thread_interactions_source_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_thread_interactions", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "source_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_thread_interactions_created_by_agent_id_agents_id_fk": { + "name": "issue_thread_interactions_created_by_agent_id_agents_id_fk", + "tableFrom": "issue_thread_interactions", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_thread_interactions_resolved_by_agent_id_agents_id_fk": { + "name": "issue_thread_interactions_resolved_by_agent_id_agents_id_fk", + "tableFrom": "issue_thread_interactions", + "tableTo": "agents", + "columnsFrom": [ + "resolved_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_tree_hold_members": { + "name": "issue_tree_hold_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "hold_id": { + "name": "hold_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "parent_issue_id": { + "name": "parent_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "depth": { + "name": "depth", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "issue_identifier": { + "name": "issue_identifier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "issue_title": { + "name": "issue_title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_status": { + "name": "issue_status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "assignee_agent_id": { + "name": "assignee_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "assignee_user_id": { + "name": "assignee_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_run_id": { + "name": "active_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "active_run_status": { + "name": "active_run_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "skipped": { + "name": "skipped", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "skip_reason": { + "name": "skip_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_tree_hold_members_hold_issue_uq": { + "name": "issue_tree_hold_members_hold_issue_uq", + "columns": [ + { + "expression": "hold_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_tree_hold_members_company_issue_idx": { + "name": "issue_tree_hold_members_company_issue_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_tree_hold_members_hold_depth_idx": { + "name": "issue_tree_hold_members_hold_depth_idx", + "columns": [ + { + "expression": "hold_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "depth", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_tree_hold_members_company_id_companies_id_fk": { + "name": "issue_tree_hold_members_company_id_companies_id_fk", + "tableFrom": "issue_tree_hold_members", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_tree_hold_members_hold_id_issue_tree_holds_id_fk": { + "name": "issue_tree_hold_members_hold_id_issue_tree_holds_id_fk", + "tableFrom": "issue_tree_hold_members", + "tableTo": "issue_tree_holds", + "columnsFrom": [ + "hold_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_tree_hold_members_issue_id_issues_id_fk": { + "name": "issue_tree_hold_members_issue_id_issues_id_fk", + "tableFrom": "issue_tree_hold_members", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_tree_hold_members_parent_issue_id_issues_id_fk": { + "name": "issue_tree_hold_members_parent_issue_id_issues_id_fk", + "tableFrom": "issue_tree_hold_members", + "tableTo": "issues", + "columnsFrom": [ + "parent_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_tree_hold_members_assignee_agent_id_agents_id_fk": { + "name": "issue_tree_hold_members_assignee_agent_id_agents_id_fk", + "tableFrom": "issue_tree_hold_members", + "tableTo": "agents", + "columnsFrom": [ + "assignee_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_tree_hold_members_active_run_id_heartbeat_runs_id_fk": { + "name": "issue_tree_hold_members_active_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_tree_hold_members", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "active_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_tree_holds": { + "name": "issue_tree_holds", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "root_issue_id": { + "name": "root_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "release_policy": { + "name": "release_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_by_actor_type": { + "name": "created_by_actor_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'system'" + }, + "created_by_agent_id": { + "name": "created_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_run_id": { + "name": "created_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "released_at": { + "name": "released_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "released_by_actor_type": { + "name": "released_by_actor_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "released_by_agent_id": { + "name": "released_by_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "released_by_user_id": { + "name": "released_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "released_by_run_id": { + "name": "released_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "release_reason": { + "name": "release_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "release_metadata": { + "name": "release_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_tree_holds_company_root_status_idx": { + "name": "issue_tree_holds_company_root_status_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "root_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issue_tree_holds_company_status_mode_idx": { + "name": "issue_tree_holds_company_status_mode_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "mode", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_tree_holds_company_id_companies_id_fk": { + "name": "issue_tree_holds_company_id_companies_id_fk", + "tableFrom": "issue_tree_holds", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issue_tree_holds_root_issue_id_issues_id_fk": { + "name": "issue_tree_holds_root_issue_id_issues_id_fk", + "tableFrom": "issue_tree_holds", + "tableTo": "issues", + "columnsFrom": [ + "root_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_tree_holds_created_by_agent_id_agents_id_fk": { + "name": "issue_tree_holds_created_by_agent_id_agents_id_fk", + "tableFrom": "issue_tree_holds", + "tableTo": "agents", + "columnsFrom": [ + "created_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_tree_holds_created_by_run_id_heartbeat_runs_id_fk": { + "name": "issue_tree_holds_created_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_tree_holds", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "created_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_tree_holds_released_by_agent_id_agents_id_fk": { + "name": "issue_tree_holds_released_by_agent_id_agents_id_fk", + "tableFrom": "issue_tree_holds", + "tableTo": "agents", + "columnsFrom": [ + "released_by_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "issue_tree_holds_released_by_run_id_heartbeat_runs_id_fk": { + "name": "issue_tree_holds_released_by_run_id_heartbeat_runs_id_fk", + "tableFrom": "issue_tree_holds", + "tableTo": "heartbeat_runs", + "columnsFrom": [ + "released_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_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 + }, + "origin_fingerprint": { + "name": "origin_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'default'" + }, + "request_depth": { + "name": "request_depth", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "billing_code": { + "name": "billing_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assignee_adapter_overrides": { + "name": "assignee_adapter_overrides", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "execution_policy": { + "name": "execution_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "execution_state": { + "name": "execution_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "monitor_next_check_at": { + "name": "monitor_next_check_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "monitor_wake_requested_at": { + "name": "monitor_wake_requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "monitor_last_triggered_at": { + "name": "monitor_last_triggered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "monitor_attempt_count": { + "name": "monitor_attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "monitor_notes": { + "name": "monitor_notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "monitor_scheduled_by": { + "name": "monitor_scheduled_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "execution_workspace_id": { + "name": "execution_workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "execution_workspace_preference": { + "name": "execution_workspace_preference", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "execution_workspace_settings": { + "name": "execution_workspace_settings", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "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_company_monitor_due_idx": { + "name": "issues_company_monitor_due_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "monitor_next_check_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_identifier_idx": { + "name": "issues_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_title_search_idx": { + "name": "issues_title_search_idx", + "columns": [ + { + "expression": "title", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "issues_identifier_search_idx": { + "name": "issues_identifier_search_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "issues_description_search_idx": { + "name": "issues_description_search_idx", + "columns": [ + { + "expression": "description", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "issues_open_routine_execution_uq": { + "name": "issues_open_routine_execution_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issues\".\"origin_kind\" = 'routine_execution'\n and \"issues\".\"origin_id\" is not null\n and \"issues\".\"hidden_at\" is null\n and \"issues\".\"execution_run_id\" is not null\n and \"issues\".\"status\" in ('backlog', 'todo', 'in_progress', 'in_review', 'blocked')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_active_liveness_recovery_incident_uq": { + "name": "issues_active_liveness_recovery_incident_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issues\".\"origin_kind\" = 'harness_liveness_escalation'\n and \"issues\".\"origin_id\" is not null\n and \"issues\".\"hidden_at\" is null\n and \"issues\".\"status\" not in ('done', 'cancelled')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_active_liveness_recovery_leaf_uq": { + "name": "issues_active_liveness_recovery_leaf_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issues\".\"origin_kind\" = 'harness_liveness_escalation'\n and \"issues\".\"origin_fingerprint\" <> 'default'\n and \"issues\".\"hidden_at\" is null\n and \"issues\".\"status\" not in ('done', 'cancelled')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_active_stale_run_evaluation_uq": { + "name": "issues_active_stale_run_evaluation_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issues\".\"origin_kind\" = 'stale_active_run_evaluation'\n and \"issues\".\"origin_id\" is not null\n and \"issues\".\"hidden_at\" is null\n and \"issues\".\"status\" not in ('done', 'cancelled')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_active_productivity_review_uq": { + "name": "issues_active_productivity_review_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issues\".\"origin_kind\" = 'issue_productivity_review'\n and \"issues\".\"origin_id\" is not null\n and \"issues\".\"hidden_at\" is null\n and \"issues\".\"status\" not in ('done', 'cancelled')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_active_stranded_issue_recovery_uq": { + "name": "issues_active_stranded_issue_recovery_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "origin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"issues\".\"origin_kind\" = 'stranded_issue_recovery'\n and \"issues\".\"origin_id\" is not null\n and \"issues\".\"hidden_at\" is null\n and \"issues\".\"status\" not in ('done', 'cancelled')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "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": {} + }, + "join_requests_pending_human_user_uq": { + "name": "join_requests_pending_human_user_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "requesting_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"join_requests\".\"request_type\" = 'human' AND \"join_requests\".\"status\" = 'pending_approval' AND \"join_requests\".\"requesting_user_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "join_requests_pending_human_email_uq": { + "name": "join_requests_pending_human_email_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"request_email_snapshot\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"join_requests\".\"request_type\" = 'human' AND \"join_requests\".\"status\" = 'pending_approval' AND \"join_requests\".\"request_email_snapshot\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "join_requests_invite_id_invites_id_fk": { + "name": "join_requests_invite_id_invites_id_fk", + "tableFrom": "join_requests", + "tableTo": "invites", + "columnsFrom": [ + "invite_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "join_requests_company_id_companies_id_fk": { + "name": "join_requests_company_id_companies_id_fk", + "tableFrom": "join_requests", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "join_requests_created_agent_id_agents_id_fk": { + "name": "join_requests_created_agent_id_agents_id_fk", + "tableFrom": "join_requests", + "tableTo": "agents", + "columnsFrom": [ + "created_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.labels": { + "name": "labels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "labels_company_idx": { + "name": "labels_company_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "labels_company_name_idx": { + "name": "labels_company_name_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "labels_company_id_companies_id_fk": { + "name": "labels_company_id_companies_id_fk", + "tableFrom": "labels", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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_database_namespaces": { + "name": "plugin_database_namespaces", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "plugin_key": { + "name": "plugin_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "namespace_name": { + "name": "namespace_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "namespace_mode": { + "name": "namespace_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'schema'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "plugin_database_namespaces_plugin_idx": { + "name": "plugin_database_namespaces_plugin_idx", + "columns": [ + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_database_namespaces_namespace_idx": { + "name": "plugin_database_namespaces_namespace_idx", + "columns": [ + { + "expression": "namespace_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_database_namespaces_status_idx": { + "name": "plugin_database_namespaces_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "plugin_database_namespaces_plugin_id_plugins_id_fk": { + "name": "plugin_database_namespaces_plugin_id_plugins_id_fk", + "tableFrom": "plugin_database_namespaces", + "tableTo": "plugins", + "columnsFrom": [ + "plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plugin_entities": { + "name": "plugin_entities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "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_migrations": { + "name": "plugin_migrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "plugin_key": { + "name": "plugin_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "namespace_name": { + "name": "namespace_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "migration_key": { + "name": "migration_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "checksum": { + "name": "checksum", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plugin_version": { + "name": "plugin_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "plugin_migrations_plugin_key_idx": { + "name": "plugin_migrations_plugin_key_idx", + "columns": [ + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "migration_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_migrations_plugin_idx": { + "name": "plugin_migrations_plugin_idx", + "columns": [ + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plugin_migrations_status_idx": { + "name": "plugin_migrations_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "plugin_migrations_plugin_id_plugins_id_fk": { + "name": "plugin_migrations_plugin_id_plugins_id_fk", + "tableFrom": "plugin_migrations", + "tableTo": "plugins", + "columnsFrom": [ + "plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plugin_state": { + "name": "plugin_state", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "scope_kind": { + "name": "scope_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope_id": { + "name": "scope_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "namespace": { + "name": "namespace", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'default'" + }, + "state_key": { + "name": "state_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value_json": { + "name": "value_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "plugin_state_plugin_scope_idx": { + "name": "plugin_state_plugin_scope_idx", + "columns": [ + { + "expression": "plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "plugin_state_plugin_id_plugins_id_fk": { + "name": "plugin_state_plugin_id_plugins_id_fk", + "tableFrom": "plugin_state", + "tableTo": "plugins", + "columnsFrom": [ + "plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "plugin_state_unique_entry_idx": { + "name": "plugin_state_unique_entry_idx", + "nullsNotDistinct": true, + "columns": [ + "plugin_id", + "scope_kind", + "scope_id", + "namespace", + "state_key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plugin_webhook_deliveries": { + "name": "plugin_webhook_deliveries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "plugin_id": { + "name": "plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "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 + }, + "dispatch_fingerprint": { + "name": "dispatch_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "linked_issue_id": { + "name": "linked_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "coalesced_into_run_id": { + "name": "coalesced_into_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "routine_runs_company_routine_idx": { + "name": "routine_runs_company_routine_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "routine_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routine_runs_trigger_idx": { + "name": "routine_runs_trigger_idx", + "columns": [ + { + "expression": "trigger_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routine_runs_dispatch_fingerprint_idx": { + "name": "routine_runs_dispatch_fingerprint_idx", + "columns": [ + { + "expression": "routine_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "dispatch_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routine_runs_linked_issue_idx": { + "name": "routine_runs_linked_issue_idx", + "columns": [ + { + "expression": "linked_issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "routine_runs_trigger_idempotency_idx": { + "name": "routine_runs_trigger_idempotency_idx", + "columns": [ + { + "expression": "trigger_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "routine_runs_company_id_companies_id_fk": { + "name": "routine_runs_company_id_companies_id_fk", + "tableFrom": "routine_runs", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "routine_runs_routine_id_routines_id_fk": { + "name": "routine_runs_routine_id_routines_id_fk", + "tableFrom": "routine_runs", + "tableTo": "routines", + "columnsFrom": [ + "routine_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "routine_runs_trigger_id_routine_triggers_id_fk": { + "name": "routine_runs_trigger_id_routine_triggers_id_fk", + "tableFrom": "routine_runs", + "tableTo": "routine_triggers", + "columnsFrom": [ + "trigger_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "routine_runs_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": false + }, + "goal_id": { + "name": "goal_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "parent_issue_id": { + "name": "parent_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assignee_agent_id": { + "name": "assignee_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "priority": { + "name": "priority", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'medium'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "concurrency_policy": { + "name": "concurrency_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'coalesce_if_active'" + }, + "catch_up_policy": { + "name": "catch_up_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'skip_missed'" + }, + "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.user_sidebar_preferences": { + "name": "user_sidebar_preferences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "company_order": { + "name": "company_order", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "user_sidebar_preferences_user_uq": { + "name": "user_sidebar_preferences_user_uq", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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 c19e2fec..f0d21b99 100644 --- a/packages/db/src/migrations/meta/_journal.json +++ b/packages/db/src/migrations/meta/_journal.json @@ -526,6 +526,13 @@ "when": 1777384535070, "tag": "0074_striped_genesis", "breakpoints": true + }, + { + "idx": 75, + "version": "7", + "when": 1777572332006, + "tag": "0075_cultured_sebastian_shaw", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/db/src/schema/issues.ts b/packages/db/src/schema/issues.ts index 1b757d6a..e848afb5 100644 --- a/packages/db/src/schema/issues.ts +++ b/packages/db/src/schema/issues.ts @@ -50,6 +50,12 @@ export const issues = pgTable( assigneeAdapterOverrides: jsonb("assignee_adapter_overrides").$type>(), executionPolicy: jsonb("execution_policy").$type>(), executionState: jsonb("execution_state").$type>(), + monitorNextCheckAt: timestamp("monitor_next_check_at", { withTimezone: true }), + monitorWakeRequestedAt: timestamp("monitor_wake_requested_at", { withTimezone: true }), + monitorLastTriggeredAt: timestamp("monitor_last_triggered_at", { withTimezone: true }), + monitorAttemptCount: integer("monitor_attempt_count").notNull().default(0), + monitorNotes: text("monitor_notes"), + monitorScheduledBy: text("monitor_scheduled_by"), executionWorkspaceId: uuid("execution_workspace_id") .references((): AnyPgColumn => executionWorkspaces.id, { onDelete: "set null" }), executionWorkspacePreference: text("execution_workspace_preference"), @@ -78,6 +84,7 @@ export const issues = pgTable( originIdx: index("issues_company_origin_idx").on(table.companyId, table.originKind, table.originId), projectWorkspaceIdx: index("issues_company_project_workspace_idx").on(table.companyId, table.projectWorkspaceId), executionWorkspaceIdx: index("issues_company_execution_workspace_idx").on(table.companyId, table.executionWorkspaceId), + dueMonitorIdx: index("issues_company_monitor_due_idx").on(table.companyId, table.monitorNextCheckAt), identifierIdx: uniqueIndex("issues_identifier_idx").on(table.identifier), titleSearchIdx: index("issues_title_search_idx").using("gin", table.title.op("gin_trgm_ops")), identifierSearchIdx: index("issues_identifier_search_idx").using("gin", table.identifier.op("gin_trgm_ops")), diff --git a/packages/shared/src/constants.ts b/packages/shared/src/constants.ts index 8cbe9eef..3724d42d 100644 --- a/packages/shared/src/constants.ts +++ b/packages/shared/src/constants.ts @@ -221,9 +221,39 @@ export type IssueExecutionPolicyMode = (typeof ISSUE_EXECUTION_POLICY_MODES)[num export const ISSUE_EXECUTION_STAGE_TYPES = ["review", "approval"] as const; export type IssueExecutionStageType = (typeof ISSUE_EXECUTION_STAGE_TYPES)[number]; +export const ISSUE_MONITOR_SCHEDULED_BY = ["assignee", "board"] as const; +export type IssueMonitorScheduledBy = (typeof ISSUE_MONITOR_SCHEDULED_BY)[number]; + +export const ISSUE_EXECUTION_MONITOR_KINDS = ["external_service"] as const; +export type IssueExecutionMonitorKind = (typeof ISSUE_EXECUTION_MONITOR_KINDS)[number]; + +export const ISSUE_EXECUTION_MONITOR_RECOVERY_POLICIES = [ + "wake_owner", + "create_recovery_issue", + "escalate_to_board", +] as const; +export type IssueExecutionMonitorRecoveryPolicy = + (typeof ISSUE_EXECUTION_MONITOR_RECOVERY_POLICIES)[number]; + export const ISSUE_EXECUTION_STATE_STATUSES = ["idle", "pending", "changes_requested", "completed"] as const; export type IssueExecutionStateStatus = (typeof ISSUE_EXECUTION_STATE_STATUSES)[number]; +export const ISSUE_EXECUTION_MONITOR_STATE_STATUSES = ["scheduled", "triggered", "cleared"] as const; +export type IssueExecutionMonitorStateStatus = (typeof ISSUE_EXECUTION_MONITOR_STATE_STATUSES)[number]; + +export const ISSUE_EXECUTION_MONITOR_CLEAR_REASONS = [ + "manual", + "triggered", + "done", + "cancelled", + "invalid_status", + "invalid_assignee", + "dispatch_skipped", + "timeout_exceeded", + "max_attempts_exhausted", +] as const; +export type IssueExecutionMonitorClearReason = (typeof ISSUE_EXECUTION_MONITOR_CLEAR_REASONS)[number]; + export const ISSUE_EXECUTION_DECISION_OUTCOMES = ["approved", "changes_requested"] as const; export type IssueExecutionDecisionOutcome = (typeof ISSUE_EXECUTION_DECISION_OUTCOMES)[number]; diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index a70ff848..87a298c7 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -35,7 +35,12 @@ export { ISSUE_REFERENCE_SOURCE_KINDS, ISSUE_EXECUTION_POLICY_MODES, ISSUE_EXECUTION_STAGE_TYPES, + ISSUE_MONITOR_SCHEDULED_BY, + ISSUE_EXECUTION_MONITOR_KINDS, + ISSUE_EXECUTION_MONITOR_RECOVERY_POLICIES, ISSUE_EXECUTION_STATE_STATUSES, + ISSUE_EXECUTION_MONITOR_STATE_STATUSES, + ISSUE_EXECUTION_MONITOR_CLEAR_REASONS, ISSUE_EXECUTION_DECISION_OUTCOMES, GOAL_LEVELS, GOAL_STATUSES, @@ -136,7 +141,12 @@ export { type IssueReferenceSourceKind, type IssueExecutionPolicyMode, type IssueExecutionStageType, + type IssueMonitorScheduledBy, + type IssueExecutionMonitorKind, + type IssueExecutionMonitorRecoveryPolicy, type IssueExecutionStateStatus, + type IssueExecutionMonitorStateStatus, + type IssueExecutionMonitorClearReason, type IssueExecutionDecisionOutcome, type GoalLevel, type GoalStatus, @@ -340,6 +350,8 @@ export type { IssueReferenceSource, IssueRelatedWorkItem, IssueRelatedWorkSummary, + IssueExecutionMonitorPolicy, + IssueExecutionMonitorState, IssueRelation, IssueRelationIssueSummary, IssueExecutionPolicy, diff --git a/packages/shared/src/types/index.ts b/packages/shared/src/types/index.ts index 25166759..35eb94c6 100644 --- a/packages/shared/src/types/index.ts +++ b/packages/shared/src/types/index.ts @@ -145,6 +145,8 @@ export type { IssueRelatedWorkSummary, IssueRelation, IssueRelationIssueSummary, + IssueExecutionMonitorPolicy, + IssueExecutionMonitorState, IssueExecutionPolicy, IssueExecutionState, IssueExecutionStage, diff --git a/packages/shared/src/types/issue.ts b/packages/shared/src/types/issue.ts index 2fa8cb8b..56e92d5a 100644 --- a/packages/shared/src/types/issue.ts +++ b/packages/shared/src/types/issue.ts @@ -1,5 +1,10 @@ import type { + IssueExecutionMonitorClearReason, + IssueExecutionMonitorKind, + IssueExecutionMonitorRecoveryPolicy, + IssueExecutionMonitorStateStatus, IssueExecutionDecisionOutcome, + IssueMonitorScheduledBy, IssueExecutionPolicyMode, IssueReferenceSourceKind, IssueExecutionStageType, @@ -201,10 +206,40 @@ export interface IssueExecutionStage { participants: IssueExecutionStageParticipant[]; } +export interface IssueExecutionMonitorPolicy { + nextCheckAt: string; + notes: string | null; + scheduledBy: IssueMonitorScheduledBy; + kind?: IssueExecutionMonitorKind | null; + serviceName?: string | null; + externalRef?: string | null; + timeoutAt?: string | null; + maxAttempts?: number | null; + recoveryPolicy?: IssueExecutionMonitorRecoveryPolicy | null; +} + export interface IssueExecutionPolicy { mode: IssueExecutionPolicyMode; commentRequired: boolean; stages: IssueExecutionStage[]; + monitor?: IssueExecutionMonitorPolicy | null; +} + +export interface IssueExecutionMonitorState { + status: IssueExecutionMonitorStateStatus; + nextCheckAt: string | null; + lastTriggeredAt: string | null; + attemptCount: number; + notes: string | null; + scheduledBy: IssueMonitorScheduledBy | null; + kind?: IssueExecutionMonitorKind | null; + serviceName?: string | null; + externalRef?: string | null; + timeoutAt?: string | null; + maxAttempts?: number | null; + recoveryPolicy?: IssueExecutionMonitorRecoveryPolicy | null; + clearedAt: string | null; + clearReason: IssueExecutionMonitorClearReason | null; } export interface IssueReviewRequest { @@ -222,6 +257,7 @@ export interface IssueExecutionState { completedStageIds: string[]; lastDecisionId: string | null; lastDecisionOutcome: IssueExecutionDecisionOutcome | null; + monitor?: IssueExecutionMonitorState | null; } export interface IssueExecutionDecision { @@ -270,6 +306,11 @@ export interface Issue { assigneeAdapterOverrides: IssueAssigneeAdapterOverrides | null; executionPolicy?: IssueExecutionPolicy | null; executionState?: IssueExecutionState | null; + monitorNextCheckAt?: Date | null; + monitorLastTriggeredAt?: Date | null; + monitorAttemptCount?: number; + monitorNotes?: string | null; + monitorScheduledBy?: IssueMonitorScheduledBy | null; executionWorkspaceId: string | null; executionWorkspacePreference: string | null; executionWorkspaceSettings: IssueExecutionWorkspaceSettings | null; diff --git a/packages/shared/src/validators/issue.ts b/packages/shared/src/validators/issue.ts index 9533e839..57ff440d 100644 --- a/packages/shared/src/validators/issue.ts +++ b/packages/shared/src/validators/issue.ts @@ -1,9 +1,14 @@ import { z } from "zod"; import { ISSUE_EXECUTION_DECISION_OUTCOMES, + ISSUE_EXECUTION_MONITOR_CLEAR_REASONS, + ISSUE_EXECUTION_MONITOR_KINDS, + ISSUE_EXECUTION_MONITOR_RECOVERY_POLICIES, + ISSUE_EXECUTION_MONITOR_STATE_STATUSES, ISSUE_EXECUTION_POLICY_MODES, ISSUE_EXECUTION_STAGE_TYPES, ISSUE_EXECUTION_STATE_STATUSES, + ISSUE_MONITOR_SCHEDULED_BY, ISSUE_PRIORITIES, clampIssueRequestDepth, ISSUE_STATUSES, @@ -103,10 +108,40 @@ export const issueExecutionStageSchema = z.object({ participants: z.array(issueExecutionStageParticipantSchema).default([]), }); +export const issueExecutionMonitorPolicySchema = z.object({ + nextCheckAt: z.string().datetime(), + notes: z.string().max(500).optional().nullable().default(null), + scheduledBy: z.enum(ISSUE_MONITOR_SCHEDULED_BY).optional().default("assignee"), + kind: z.enum(ISSUE_EXECUTION_MONITOR_KINDS).optional().nullable().default(null), + serviceName: z.string().trim().min(1).max(120).optional().nullable().default(null), + externalRef: z.string().trim().min(1).max(500).optional().nullable().default(null), + timeoutAt: z.string().datetime().optional().nullable().default(null), + maxAttempts: z.number().int().positive().max(100).optional().nullable().default(null), + recoveryPolicy: z.enum(ISSUE_EXECUTION_MONITOR_RECOVERY_POLICIES).optional().nullable().default(null), +}); + export const issueExecutionPolicySchema = z.object({ mode: z.enum(ISSUE_EXECUTION_POLICY_MODES).optional().default("normal"), commentRequired: z.boolean().optional().default(true), stages: z.array(issueExecutionStageSchema).default([]), + monitor: issueExecutionMonitorPolicySchema.optional().nullable(), +}); + +export const issueExecutionMonitorStateSchema = z.object({ + status: z.enum(ISSUE_EXECUTION_MONITOR_STATE_STATUSES), + nextCheckAt: z.string().datetime().nullable(), + lastTriggeredAt: z.string().datetime().nullable(), + attemptCount: z.number().int().nonnegative().default(0), + notes: z.string().max(500).nullable(), + scheduledBy: z.enum(ISSUE_MONITOR_SCHEDULED_BY).nullable(), + kind: z.enum(ISSUE_EXECUTION_MONITOR_KINDS).nullable().optional().default(null), + serviceName: z.string().trim().min(1).max(120).nullable().optional().default(null), + externalRef: z.string().trim().min(1).max(500).nullable().optional().default(null), + timeoutAt: z.string().datetime().nullable().optional().default(null), + maxAttempts: z.number().int().positive().max(100).nullable().optional().default(null), + recoveryPolicy: z.enum(ISSUE_EXECUTION_MONITOR_RECOVERY_POLICIES).nullable().optional().default(null), + clearedAt: z.string().datetime().nullable(), + clearReason: z.enum(ISSUE_EXECUTION_MONITOR_CLEAR_REASONS).nullable(), }); export const issueReviewRequestSchema = z.object({ @@ -124,6 +159,7 @@ export const issueExecutionStateSchema = z.object({ completedStageIds: z.array(z.string().uuid()).default([]), lastDecisionId: z.string().uuid().nullable(), lastDecisionOutcome: z.enum(ISSUE_EXECUTION_DECISION_OUTCOMES).nullable(), + monitor: issueExecutionMonitorStateSchema.optional().nullable(), }); const issueRequestDepthInputSchema = z diff --git a/server/src/__tests__/issue-execution-policy-routes.test.ts b/server/src/__tests__/issue-execution-policy-routes.test.ts index f21e7b95..b705c1fe 100644 --- a/server/src/__tests__/issue-execution-policy-routes.test.ts +++ b/server/src/__tests__/issue-execution-policy-routes.test.ts @@ -7,6 +7,7 @@ const mockIssueService = vi.hoisted(() => ({ getById: vi.fn(), assertCheckoutOwner: vi.fn(), update: vi.fn(), + createChild: vi.fn(), addComment: vi.fn(), findMentionedAgents: vi.fn(), getRelationSummaries: vi.fn(), @@ -16,21 +17,26 @@ const mockIssueService = vi.hoisted(() => ({ const mockHeartbeatService = vi.hoisted(() => ({ wakeup: vi.fn(async () => undefined), + triggerIssueMonitor: vi.fn(async () => ({ outcome: "triggered" as const })), reportRunActivity: vi.fn(async () => undefined), getRun: vi.fn(async () => null), getActiveRunForAgent: vi.fn(async () => null), cancelRun: vi.fn(async () => null), })); +const mockAccessService = vi.hoisted(() => ({ + canUser: vi.fn(async () => false), + hasPermission: vi.fn(async () => false), +})); + +const mockLogActivity = vi.hoisted(() => vi.fn(async () => undefined)); + function registerModuleMocks() { vi.doMock("../services/index.js", () => ({ companyService: () => ({ getById: vi.fn(async () => ({ id: "company-1", attachmentMaxBytes: 10 * 1024 * 1024 })), }), - accessService: () => ({ - canUser: vi.fn(async () => false), - hasPermission: vi.fn(async () => false), - }), + accessService: () => mockAccessService, agentService: () => ({ getById: vi.fn(async () => null), }), @@ -42,6 +48,9 @@ function registerModuleMocks() { }), goalService: () => ({}), heartbeatService: () => mockHeartbeatService, + environmentService: () => ({ + getById: vi.fn(async () => null), + }), instanceSettingsService: () => ({ get: vi.fn(async () => ({ id: "instance-settings-1", @@ -67,7 +76,7 @@ function registerModuleMocks() { syncIssue: async () => undefined, }), issueService: () => mockIssueService, - logActivity: vi.fn(async () => undefined), + logActivity: mockLogActivity, projectService: () => ({}), routineService: () => ({ syncRunStatusForIssue: vi.fn(async () => undefined), @@ -76,7 +85,22 @@ function registerModuleMocks() { })); } -async function createApp() { +type TestActor = + | { + type: "board"; + userId: string; + companyIds: string[]; + source: "local_implicit"; + isInstanceAdmin: boolean; + } + | { + type: "agent"; + agentId: string; + companyId: string; + runId: string | null; + }; + +async function createApp(actor?: TestActor) { const [{ errorHandler }, { issueRoutes }] = await Promise.all([ import("../middleware/index.js"), import("../routes/issues.js"), @@ -84,7 +108,7 @@ async function createApp() { const app = express(); app.use(express.json()); app.use((req, _res, next) => { - (req as any).actor = { + (req as any).actor = actor ?? { type: "board", userId: "local-board", companyIds: ["company-1"], @@ -111,6 +135,17 @@ describe("issue execution policy routes", () => { mockIssueService.getRelationSummaries.mockResolvedValue({ blockedBy: [], blocks: [] }); mockIssueService.listWakeableBlockedDependents.mockResolvedValue([]); mockIssueService.getWakeableParentAfterChildCompletion.mockResolvedValue(null); + mockIssueService.createChild.mockResolvedValue({ + issue: { + id: "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb", + companyId: "company-1", + identifier: "PAP-1002", + title: "Child issue", + }, + parentBlockerAdded: false, + }); + mockAccessService.canUser.mockResolvedValue(false); + mockAccessService.hasPermission.mockResolvedValue(false); }); it("does not auto-start execution review when reviewers are added to an already in_review issue", async () => { @@ -162,4 +197,175 @@ describe("issue execution policy routes", () => { expect(updatePatch.executionState).toBeUndefined(); expect(mockHeartbeatService.wakeup).not.toHaveBeenCalled(); }); + + it("triggers a scheduled monitor immediately from the dedicated route", async () => { + const issue = { + id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", + companyId: "company-1", + status: "in_progress", + assigneeAgentId: "33333333-3333-4333-8333-333333333333", + assigneeUserId: null, + createdByUserId: "local-board", + identifier: "PAP-1001", + title: "Manual monitor trigger", + executionPolicy: normalizeIssueExecutionPolicy({ + monitor: { + nextCheckAt: "2026-04-11T12:30:00.000Z", + notes: "Check deployment", + scheduledBy: "board", + }, + }), + executionState: null, + }; + mockIssueService.getById.mockResolvedValue(issue); + + const res = await request(await createApp()) + .post("/api/issues/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/monitor/check-now") + .send({}); + + expect(res.status).toBe(200); + expect(res.body).toEqual({ ok: true }); + expect(mockHeartbeatService.triggerIssueMonitor).toHaveBeenCalledWith( + "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", + expect.objectContaining({ + actorType: "user", + actorId: "local-board", + agentId: null, + }), + ); + }); + + it("lets a board user create a child issue with a scheduled monitor", async () => { + mockIssueService.getById.mockResolvedValue({ + id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", + companyId: "company-1", + status: "in_progress", + assigneeAgentId: "11111111-1111-4111-8111-111111111111", + assigneeUserId: null, + createdByUserId: "local-board", + identifier: "PAP-1001", + title: "Parent issue", + executionPolicy: null, + executionState: null, + }); + + const res = await request(await createApp()) + .post("/api/issues/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/children") + .send({ + title: "Child monitor", + status: "in_review", + assigneeAgentId: "33333333-3333-4333-8333-333333333333", + executionPolicy: { + monitor: { + nextCheckAt: "2026-04-11T12:30:00.000Z", + scheduledBy: "assignee", + }, + }, + }); + + expect(res.status).toBe(201); + const createPayload = mockIssueService.createChild.mock.calls[0]?.[1] as { + executionPolicy: { monitor: { scheduledBy: string } }; + }; + expect(createPayload.executionPolicy.monitor.scheduledBy).toBe("board"); + expect(mockLogActivity).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ + action: "issue.monitor_scheduled", + details: expect.objectContaining({ + scheduledBy: "board", + }), + }), + ); + }); + + it("rejects child monitor scheduling by a non-assignee agent even with task assignment permission", async () => { + mockAccessService.hasPermission.mockResolvedValue(true); + mockIssueService.getById.mockResolvedValue({ + id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", + companyId: "company-1", + status: "in_progress", + assigneeAgentId: "11111111-1111-4111-8111-111111111111", + assigneeUserId: null, + createdByUserId: "local-board", + identifier: "PAP-1001", + title: "Parent issue", + executionPolicy: null, + executionState: null, + }); + + const res = await request(await createApp({ + type: "agent", + agentId: "22222222-2222-4222-8222-222222222222", + companyId: "company-1", + runId: "run-1", + })) + .post("/api/issues/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/children") + .send({ + title: "Child monitor", + status: "in_review", + assigneeAgentId: "33333333-3333-4333-8333-333333333333", + executionPolicy: { + monitor: { + nextCheckAt: "2026-04-11T12:30:00.000Z", + scheduledBy: "board", + }, + }, + }); + + expect(res.status).toBe(403); + expect(res.body.error).toBe("Only the assignee agent or a board user can manage issue monitors"); + expect(mockIssueService.createChild).not.toHaveBeenCalled(); + }); + + it("normalizes spoofed child monitor scheduledBy to the assignee actor", async () => { + mockAccessService.hasPermission.mockResolvedValue(true); + mockIssueService.getById.mockResolvedValue({ + id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", + companyId: "company-1", + status: "in_progress", + assigneeAgentId: "33333333-3333-4333-8333-333333333333", + assigneeUserId: null, + createdByUserId: "local-board", + identifier: "PAP-1001", + title: "Parent issue", + executionPolicy: null, + executionState: null, + }); + + const res = await request(await createApp({ + type: "agent", + agentId: "33333333-3333-4333-8333-333333333333", + companyId: "company-1", + runId: "run-1", + })) + .post("/api/issues/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/children") + .send({ + title: "Child monitor", + status: "in_review", + assigneeAgentId: "33333333-3333-4333-8333-333333333333", + executionPolicy: { + monitor: { + nextCheckAt: "2026-04-11T12:30:00.000Z", + scheduledBy: "board", + externalRef: "https://example.test/deploy?token=secret", + }, + }, + }); + + expect(res.status).toBe(201); + const createPayload = mockIssueService.createChild.mock.calls[0]?.[1] as { + executionPolicy: { monitor: { scheduledBy: string; externalRef: string | null } }; + }; + expect(createPayload.executionPolicy.monitor.scheduledBy).toBe("assignee"); + expect(createPayload.executionPolicy.monitor.externalRef).toBe("[redacted]"); + expect(mockLogActivity).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ + action: "issue.monitor_scheduled", + entityId: "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb", + details: expect.not.objectContaining({ externalRef: expect.anything() }), + }), + ); + }); }); diff --git a/server/src/__tests__/issue-execution-policy.test.ts b/server/src/__tests__/issue-execution-policy.test.ts index 37a64dc2..c66dde8f 100644 --- a/server/src/__tests__/issue-execution-policy.test.ts +++ b/server/src/__tests__/issue-execution-policy.test.ts @@ -112,6 +112,26 @@ describe("normalizeIssueExecutionPolicy", () => { it("throws for invalid input", () => { expect(() => normalizeIssueExecutionPolicy({ stages: [{ type: "invalid_type" }] })).toThrow(); }); + + it("keeps monitor-only policies", () => { + const result = normalizeIssueExecutionPolicy({ + monitor: { + nextCheckAt: "2026-04-11T12:30:00.000Z", + notes: "Check deployment", + externalRef: "https://example.test/deploy?token=secret", + }, + stages: [], + }); + expect(result).toMatchObject({ + stages: [], + monitor: { + nextCheckAt: "2026-04-11T12:30:00.000Z", + notes: "Check deployment", + scheduledBy: "assignee", + externalRef: "[redacted]", + }, + }); + }); }); describe("parseIssueExecutionState", () => { @@ -1261,4 +1281,169 @@ describe("issue execution policy transitions", () => { }); }); }); + + describe("monitor policy", () => { + it("schedules a one-shot monitor on an active agent-owned issue", () => { + const policy = normalizeIssueExecutionPolicy({ + stages: [], + monitor: { + nextCheckAt: "2026-04-11T12:30:00.000Z", + notes: "Check deployment", + scheduledBy: "board", + }, + })!; + + const result = applyIssueExecutionPolicyTransition({ + issue: { + status: "in_progress", + assigneeAgentId: coderAgentId, + assigneeUserId: null, + executionPolicy: null, + executionState: null, + monitorAttemptCount: 0, + monitorNextCheckAt: null, + monitorLastTriggeredAt: null, + monitorNotes: null, + monitorScheduledBy: null, + }, + policy, + previousPolicy: null, + requestedAssigneePatch: {}, + actor: { userId: boardUserId }, + monitorExplicitlyUpdated: true, + }); + + expect(result.patch.monitorNextCheckAt).toEqual(new Date("2026-04-11T12:30:00.000Z")); + expect(result.patch.monitorScheduledBy).toBe("board"); + expect(result.patch.executionState).toMatchObject({ + status: "idle", + monitor: { + status: "scheduled", + nextCheckAt: "2026-04-11T12:30:00.000Z", + notes: "Check deployment", + scheduledBy: "board", + }, + }); + }); + + it("auto-clears a scheduled monitor when the issue moves to done", () => { + const policy = normalizeIssueExecutionPolicy({ + stages: [], + monitor: { + nextCheckAt: "2026-04-11T12:30:00.000Z", + notes: "Check deployment", + scheduledBy: "assignee", + }, + })!; + + const result = applyIssueExecutionPolicyTransition({ + issue: { + status: "in_progress", + assigneeAgentId: coderAgentId, + assigneeUserId: null, + executionPolicy: policy, + executionState: { + status: "idle", + currentStageId: null, + currentStageIndex: null, + currentStageType: null, + currentParticipant: null, + returnAssignee: null, + completedStageIds: [], + lastDecisionId: null, + lastDecisionOutcome: null, + monitor: { + status: "scheduled", + nextCheckAt: "2026-04-11T12:30:00.000Z", + lastTriggeredAt: null, + attemptCount: 0, + notes: "Check deployment", + scheduledBy: "assignee", + clearedAt: null, + clearReason: null, + }, + }, + monitorAttemptCount: 0, + monitorNextCheckAt: new Date("2026-04-11T12:30:00.000Z"), + monitorLastTriggeredAt: null, + monitorNotes: "Check deployment", + monitorScheduledBy: "assignee", + }, + policy, + previousPolicy: policy, + requestedStatus: "done", + requestedAssigneePatch: {}, + actor: { agentId: coderAgentId }, + }); + + expect(result.patch.executionPolicy).toBeNull(); + expect(result.patch.monitorNextCheckAt).toBeNull(); + expect(result.patch.executionState).toMatchObject({ + monitor: { + status: "cleared", + clearReason: "done", + }, + }); + }); + + it("rejects explicitly scheduling a monitor on an invalid issue state", () => { + const policy = normalizeIssueExecutionPolicy({ + stages: [], + monitor: { + nextCheckAt: "2026-04-11T12:30:00.000Z", + notes: "Check deployment", + }, + })!; + + expect(() => + applyIssueExecutionPolicyTransition({ + issue: { + status: "blocked", + assigneeAgentId: coderAgentId, + assigneeUserId: null, + executionPolicy: null, + executionState: null, + }, + policy, + previousPolicy: null, + requestedAssigneePatch: {}, + actor: { agentId: coderAgentId }, + monitorExplicitlyUpdated: true, + }), + ).toThrow("Monitor can only be scheduled"); + }); + + it("rejects explicitly re-arming a monitor after max attempts are exhausted", () => { + const policy = normalizeIssueExecutionPolicy({ + stages: [], + monitor: { + nextCheckAt: "2099-04-11T12:30:00.000Z", + maxAttempts: 1, + scheduledBy: "assignee", + }, + })!; + + expect(() => + applyIssueExecutionPolicyTransition({ + issue: { + status: "in_review", + assigneeAgentId: coderAgentId, + assigneeUserId: null, + executionPolicy: null, + executionState: null, + monitorAttemptCount: 1, + monitorNextCheckAt: null, + monitorLastTriggeredAt: null, + monitorNotes: null, + monitorScheduledBy: "assignee", + }, + policy, + previousPolicy: null, + requestedAssigneePatch: {}, + actor: { agentId: coderAgentId }, + monitorExplicitlyUpdated: true, + }), + ).toThrow("Monitor bounds are already exhausted"); + }); + }); }); diff --git a/server/src/__tests__/issue-monitor-scheduler.test.ts b/server/src/__tests__/issue-monitor-scheduler.test.ts new file mode 100644 index 00000000..44fa27e0 --- /dev/null +++ b/server/src/__tests__/issue-monitor-scheduler.test.ts @@ -0,0 +1,448 @@ +import { randomUUID } from "node:crypto"; +import { eq, sql } from "drizzle-orm"; +import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; +import { + activityLog, + agentRuntimeState, + agentWakeupRequests, + agents, + companies, + companySkills, + createDb, + documentRevisions, + documents, + environmentLeases, + heartbeatRunEvents, + heartbeatRuns, + issueComments, + issueDocuments, + issues, + workspaceRuntimeServices, +} from "@paperclipai/db"; +import { + getEmbeddedPostgresTestSupport, + startEmbeddedPostgresTestDatabase, +} from "./helpers/embedded-postgres.js"; +import { heartbeatService } from "../services/heartbeat.ts"; +import { normalizeIssueExecutionPolicy, parseIssueExecutionState } from "../services/issue-execution-policy.ts"; + +const embeddedPostgresSupport = await getEmbeddedPostgresTestSupport(); +const describeEmbeddedPostgres = embeddedPostgresSupport.supported ? describe : describe.skip; + +if (!embeddedPostgresSupport.supported) { + console.warn( + `Skipping embedded Postgres issue monitor scheduler tests on this host: ${embeddedPostgresSupport.reason ?? "unsupported environment"}`, + ); +} + +describeEmbeddedPostgres("issue monitor scheduler", () => { + let db!: ReturnType; + let tempDb: Awaited> | null = null; + const seededAgentIds = new Set(); + + beforeAll(async () => { + tempDb = await startEmbeddedPostgresTestDatabase("paperclip-issue-monitor-"); + db = createDb(tempDb.connectionString); + }, 20_000); + + async function waitForHeartbeatIdle(timeoutMs = 3_000) { + const deadline = Date.now() + timeoutMs; + while (Date.now() < deadline) { + const active = await db + .select({ id: heartbeatRuns.id }) + .from(heartbeatRuns) + .where(sql`${heartbeatRuns.status} in ('queued', 'running', 'scheduled_retry')`); + if (active.length === 0) return; + await new Promise((resolve) => setTimeout(resolve, 50)); + } + throw new Error("Timed out waiting for issue monitor heartbeat runs to settle"); + } + + async function heartbeatSideEffectFingerprint() { + const [active, events, activity, leases, runtimeServices] = await Promise.all([ + db + .select({ count: sql`count(*)` }) + .from(heartbeatRuns) + .where(sql`${heartbeatRuns.status} in ('queued', 'running', 'scheduled_retry')`), + db.select({ count: sql`count(*)` }).from(heartbeatRunEvents), + db.select({ count: sql`count(*)` }).from(activityLog), + db.select({ count: sql`count(*)` }).from(environmentLeases), + db.select({ count: sql`count(*)` }).from(workspaceRuntimeServices), + ]); + + return [ + active[0]?.count ?? 0, + events[0]?.count ?? 0, + activity[0]?.count ?? 0, + leases[0]?.count ?? 0, + runtimeServices[0]?.count ?? 0, + ].join(":"); + } + + async function waitForHeartbeatSideEffectsSettled(timeoutMs = 5_000, quietMs = 500) { + const deadline = Date.now() + timeoutMs; + let previous = ""; + let stableSince = Date.now(); + while (Date.now() < deadline) { + const current = await heartbeatSideEffectFingerprint(); + const activeCount = Number(current.split(":")[0] ?? 0); + if (current !== previous || activeCount > 0) { + previous = current; + stableSince = Date.now(); + } else if (Date.now() - stableSince >= quietMs) { + return; + } + await new Promise((resolve) => setTimeout(resolve, 50)); + } + throw new Error("Timed out waiting for issue monitor heartbeat side effects to settle"); + } + + async function cleanupRows() { + await waitForHeartbeatSideEffectsSettled(); + await db.delete(heartbeatRunEvents); + await db.delete(issueComments); + await db.delete(documentRevisions); + await db.delete(issueDocuments); + await db.delete(documents); + await db.delete(activityLog); + await db.delete(environmentLeases); + await db.delete(workspaceRuntimeServices); + await db.delete(issues); + await db.delete(heartbeatRuns); + await db.delete(agentWakeupRequests); + await db.delete(agentRuntimeState); + await db.delete(agents); + await db.delete(companySkills); + await db.delete(companies); + } + + afterEach(async () => { + seededAgentIds.clear(); + let lastError: unknown = null; + for (let attempt = 0; attempt < 3; attempt += 1) { + try { + await cleanupRows(); + return; + } catch (error) { + lastError = error; + await new Promise((resolve) => setTimeout(resolve, 100)); + } + } + throw lastError; + }); + + afterAll(async () => { + await tempDb?.cleanup(); + }); + + async function seedFixture(input?: { + agentStatus?: "active" | "paused"; + issueStatus?: "in_progress" | "in_review"; + monitorAttemptCount?: number; + monitor?: Record; + }) { + const companyId = randomUUID(); + const agentId = randomUUID(); + const issueId = randomUUID(); + const nextCheckAt = new Date("2026-04-11T12:30:00.000Z"); + const issuePrefix = `T${companyId.replace(/-/g, "").slice(0, 6).toUpperCase()}`; + + const monitorAttemptCount = input?.monitorAttemptCount ?? 0; + const monitor = { + nextCheckAt: nextCheckAt.toISOString(), + notes: "Check deploy", + scheduledBy: "assignee", + ...(input?.monitor ?? {}), + }; + + await db.insert(companies).values({ + id: companyId, + name: "Paperclip", + issuePrefix, + requireBoardApprovalForNewAgents: false, + }); + + await db.insert(agents).values({ + id: agentId, + companyId, + name: "Monitor Bot", + role: "engineer", + status: input?.agentStatus ?? "active", + adapterType: "process", + adapterConfig: { + command: process.execPath, + args: ["-e", ""], + cwd: process.cwd(), + }, + runtimeConfig: { + heartbeat: { + enabled: false, + wakeOnDemand: true, + }, + }, + permissions: {}, + }); + seededAgentIds.add(agentId); + + await db.insert(issues).values({ + id: issueId, + companyId, + title: "Watch external deploy", + status: input?.issueStatus ?? "in_progress", + priority: "medium", + assigneeAgentId: agentId, + issueNumber: 1, + identifier: `${issuePrefix}-1`, + executionPolicy: { + mode: "normal", + commentRequired: true, + stages: [], + monitor, + }, + executionState: { + status: "idle", + currentStageId: null, + currentStageIndex: null, + currentStageType: null, + currentParticipant: null, + returnAssignee: null, + completedStageIds: [], + lastDecisionId: null, + lastDecisionOutcome: null, + monitor: { + status: "scheduled", + nextCheckAt: nextCheckAt.toISOString(), + lastTriggeredAt: null, + attemptCount: monitorAttemptCount, + notes: "Check deploy", + scheduledBy: "assignee", + serviceName: typeof monitor.serviceName === "string" ? monitor.serviceName : null, + externalRef: typeof monitor.externalRef === "string" ? monitor.externalRef : null, + timeoutAt: typeof monitor.timeoutAt === "string" ? monitor.timeoutAt : null, + maxAttempts: typeof monitor.maxAttempts === "number" ? monitor.maxAttempts : null, + recoveryPolicy: typeof monitor.recoveryPolicy === "string" ? monitor.recoveryPolicy : null, + clearedAt: null, + clearReason: null, + }, + }, + monitorNextCheckAt: nextCheckAt, + monitorAttemptCount, + monitorNotes: "Check deploy", + monitorScheduledBy: "assignee", + }); + + return { companyId, agentId, issueId, nextCheckAt }; + } + + it("triggers due issue monitors once and clears the one-shot schedule", async () => { + const { issueId, agentId } = await seedFixture(); + const heartbeat = heartbeatService(db); + const tickAt = new Date("2026-04-11T12:31:00.000Z"); + + const result = await heartbeat.tickTimers(tickAt); + + expect(result.enqueued).toBe(1); + + const issue = await db.select().from(issues).where(eq(issues.id, issueId)).then((rows) => rows[0]!); + expect(issue.monitorNextCheckAt).toBeNull(); + expect(issue.monitorAttemptCount).toBe(1); + expect(issue.monitorLastTriggeredAt?.toISOString()).toBe(tickAt.toISOString()); + expect(normalizeIssueExecutionPolicy(issue.executionPolicy ?? null)?.monitor ?? null).toBeNull(); + expect(parseIssueExecutionState(issue.executionState)?.monitor).toMatchObject({ + status: "triggered", + lastTriggeredAt: tickAt.toISOString(), + attemptCount: 1, + }); + + const wakeup = await db + .select() + .from(agentWakeupRequests) + .where(eq(agentWakeupRequests.agentId, agentId)) + .then((rows) => rows[0] ?? null); + expect(wakeup?.reason).toBe("issue_monitor_due"); + + const activity = await db + .select() + .from(activityLog) + .where(eq(activityLog.entityId, issueId)) + .then((rows) => rows.map((row) => row.action)); + expect(activity).toContain("issue.monitor_triggered"); + }); + + it("lets the board trigger a scheduled issue monitor immediately", async () => { + const { issueId, agentId, nextCheckAt } = await seedFixture(); + const heartbeat = heartbeatService(db); + const triggeredAt = new Date("2026-04-11T12:00:00.000Z"); + + const result = await heartbeat.triggerIssueMonitor(issueId, { + now: triggeredAt, + actorType: "user", + actorId: "local-board", + }); + + expect(result.outcome).toBe("triggered"); + + const issue = await db.select().from(issues).where(eq(issues.id, issueId)).then((rows) => rows[0]!); + expect(issue.monitorNextCheckAt).toBeNull(); + expect(issue.monitorLastTriggeredAt?.toISOString()).toBe(triggeredAt.toISOString()); + expect(issue.monitorAttemptCount).toBe(1); + expect(normalizeIssueExecutionPolicy(issue.executionPolicy ?? null)?.monitor ?? null).toBeNull(); + + const wakeup = await db + .select() + .from(agentWakeupRequests) + .where(eq(agentWakeupRequests.agentId, agentId)) + .then((rows) => rows[0] ?? null); + expect(wakeup?.reason).toBe("issue_monitor_due"); + expect(wakeup?.payload).toMatchObject({ + issueId, + nextCheckAt: nextCheckAt.toISOString(), + source: "manual", + }); + + const activity = await db + .select() + .from(activityLog) + .where(eq(activityLog.entityId, issueId)) + .orderBy(activityLog.createdAt); + expect(activity.map((row) => row.action)).toContain("issue.monitor_triggered"); + const triggerEvent = activity.find((row) => row.action === "issue.monitor_triggered"); + expect(triggerEvent?.actorType).toBe("user"); + expect(triggerEvent?.actorId).toBe("local-board"); + expect(triggerEvent?.details).toMatchObject({ + nextCheckAt: nextCheckAt.toISOString(), + source: "manual", + }); + }); + + it("clears due monitors that cannot be dispatched and records a skip", async () => { + const { issueId } = await seedFixture({ agentStatus: "paused" }); + const heartbeat = heartbeatService(db); + const tickAt = new Date("2026-04-11T12:31:00.000Z"); + + const result = await heartbeat.tickTimers(tickAt); + + expect(result.skipped).toBe(1); + + const issue = await db.select().from(issues).where(eq(issues.id, issueId)).then((rows) => rows[0]!); + expect(issue.monitorNextCheckAt).toBeNull(); + expect(parseIssueExecutionState(issue.executionState)?.monitor).toMatchObject({ + status: "cleared", + clearReason: "dispatch_skipped", + }); + + const activity = await db + .select() + .from(activityLog) + .where(eq(activityLog.entityId, issueId)) + .then((rows) => rows.map((row) => row.action)); + expect(activity).toContain("issue.monitor_skipped"); + }); + + it("clears exhausted monitors and queues bounded owner recovery instead of another due check", async () => { + const { issueId, agentId } = await seedFixture({ + monitorAttemptCount: 1, + monitor: { + maxAttempts: 1, + recoveryPolicy: "wake_owner", + }, + }); + const heartbeat = heartbeatService(db); + const tickAt = new Date("2026-04-11T12:31:00.000Z"); + + const result = await heartbeat.tickTimers(tickAt); + + expect(result.enqueued).toBe(0); + expect(result.skipped).toBe(1); + + const issue = await db.select().from(issues).where(eq(issues.id, issueId)).then((rows) => rows[0]!); + expect(issue.monitorNextCheckAt).toBeNull(); + expect(parseIssueExecutionState(issue.executionState)?.monitor).toMatchObject({ + status: "cleared", + clearReason: "max_attempts_exhausted", + }); + + const wakeup = await db + .select() + .from(agentWakeupRequests) + .where(eq(agentWakeupRequests.agentId, agentId)) + .then((rows) => rows[0] ?? null); + expect(wakeup?.reason).toBe("issue_monitor_recovery"); + expect(wakeup?.payload).toMatchObject({ + issueId, + clearReason: "max_attempts_exhausted", + maxAttempts: 1, + }); + + const activity = await db + .select() + .from(activityLog) + .where(eq(activityLog.entityId, issueId)) + .then((rows) => rows.map((row) => row.action)); + expect(activity).toContain("issue.monitor_exhausted"); + expect(activity).toContain("issue.monitor_recovery_wake_queued"); + expect(activity).not.toContain("issue.monitor_triggered"); + }); + + it("clears timed-out monitors and creates a visible recovery issue when requested", async () => { + const { issueId, companyId } = await seedFixture({ + monitor: { + timeoutAt: "2026-04-11T12:00:00.000Z", + recoveryPolicy: "create_recovery_issue", + }, + }); + const heartbeat = heartbeatService(db); + const tickAt = new Date("2026-04-11T12:31:00.000Z"); + + const result = await heartbeat.tickTimers(tickAt); + + expect(result.enqueued).toBe(0); + expect(result.skipped).toBe(1); + + const issue = await db.select().from(issues).where(eq(issues.id, issueId)).then((rows) => rows[0]!); + expect(issue.monitorNextCheckAt).toBeNull(); + expect(parseIssueExecutionState(issue.executionState)?.monitor).toMatchObject({ + status: "cleared", + clearReason: "timeout_exceeded", + }); + + const recoveryIssue = await db + .select() + .from(issues) + .where(eq(issues.originId, issueId)) + .then((rows) => rows.find((row) => row.companyId === companyId && row.originKind === "stranded_issue_recovery") ?? null); + expect(recoveryIssue).toMatchObject({ + parentId: issueId, + priority: "high", + }); + expect(["todo", "in_progress"]).toContain(recoveryIssue?.status); + }); + + it("omits external monitor refs from wake payloads and activity details", async () => { + const { issueId, agentId } = await seedFixture({ + monitor: { + serviceName: "Deploy provider", + externalRef: "https://provider.example/deploy/123?token=secret", + }, + }); + const heartbeat = heartbeatService(db); + const tickAt = new Date("2026-04-11T12:31:00.000Z"); + + await heartbeat.tickTimers(tickAt); + + const wakeup = await db + .select() + .from(agentWakeupRequests) + .where(eq(agentWakeupRequests.agentId, agentId)) + .then((rows) => rows[0] ?? null); + expect(JSON.stringify(wakeup?.payload)).not.toContain("provider.example"); + expect(wakeup?.payload).not.toHaveProperty("externalRef"); + + const activity = await db + .select() + .from(activityLog) + .where(eq(activityLog.entityId, issueId)); + expect(JSON.stringify(activity.map((row) => row.details))).not.toContain("provider.example"); + expect(activity.find((row) => row.action === "issue.monitor_triggered")?.details).not.toHaveProperty("externalRef"); + }); +}); diff --git a/server/src/__tests__/recovery-classifiers.test.ts b/server/src/__tests__/recovery-classifiers.test.ts index f16b29f9..72243d3f 100644 --- a/server/src/__tests__/recovery-classifiers.test.ts +++ b/server/src/__tests__/recovery-classifiers.test.ts @@ -74,6 +74,100 @@ describe("recovery classifier boundary", () => { expect(classifyIssueGraphLiveness(input)).toEqual(classifyIssueGraphLivenessCompat(input)); }); + it("treats a scheduled monitor as an explicit review action path", () => { + const findings = classifyIssueGraphLiveness({ + now: "2026-04-30T18:00:00.000Z", + issues: [ + { + id: issueId, + companyId, + identifier: "PAP-2945", + title: "Wait for external review", + status: "in_review", + assigneeAgentId: agentId, + assigneeUserId: null, + createdByAgentId: null, + createdByUserId: null, + executionState: null, + monitorNextCheckAt: "2026-04-30T19:00:00.000Z", + }, + ], + relations: [], + agents: [ + { + id: agentId, + companyId, + name: "Coder", + role: "engineer", + status: "idle", + reportsTo: managerId, + }, + ], + }); + + expect(findings).toEqual([]); + }); + + it("does not treat overdue or exhausted monitors as explicit waiting paths", () => { + const baseIssue = { + id: issueId, + companyId, + identifier: "PAP-2945", + title: "Wait for external review", + status: "in_review", + assigneeAgentId: agentId, + assigneeUserId: null, + createdByAgentId: null, + createdByUserId: null, + }; + const agents = [ + { + id: agentId, + companyId, + name: "Coder", + role: "engineer", + status: "idle", + reportsTo: managerId, + }, + ]; + + const overdue = classifyIssueGraphLiveness({ + now: "2026-04-30T20:00:00.000Z", + issues: [ + { + ...baseIssue, + executionState: null, + monitorNextCheckAt: "2026-04-30T19:00:00.000Z", + }, + ], + relations: [], + agents, + }); + + const exhausted = classifyIssueGraphLiveness({ + now: "2026-04-30T18:00:00.000Z", + issues: [ + { + ...baseIssue, + executionPolicy: { + monitor: { + nextCheckAt: "2026-04-30T19:00:00.000Z", + maxAttempts: 1, + }, + }, + executionState: null, + monitorNextCheckAt: "2026-04-30T19:00:00.000Z", + monitorAttemptCount: 1, + }, + ], + relations: [], + agents, + }); + + expect(overdue[0]?.state).toBe("in_review_without_action_path"); + expect(exhausted[0]?.state).toBe("in_review_without_action_path"); + }); + it("keeps run liveness continuation decision parity with the compatibility export", () => { const input = { run: { diff --git a/server/src/routes/issues.ts b/server/src/routes/issues.ts index 60916144..4a6492b5 100644 --- a/server/src/routes/issues.ts +++ b/server/src/routes/issues.ts @@ -81,6 +81,8 @@ import { applyIssueExecutionPolicyTransition, normalizeIssueExecutionPolicy, parseIssueExecutionState, + redactIssueMonitorExternalRef, + setIssueExecutionPolicyMonitorScheduledBy, } from "../services/issue-execution-policy.js"; import type { PluginWorkerManager } from "../services/plugin-worker-manager.js"; @@ -165,6 +167,53 @@ function summarizeIssueReferenceActivityDetails(input: }; } +function monitorPoliciesEqual(left: NormalizedExecutionPolicy | null, right: NormalizedExecutionPolicy | null) { + return JSON.stringify(left?.monitor ?? null) === JSON.stringify(right?.monitor ?? null); +} + +function applyActorMonitorScheduledBy( + policy: NormalizedExecutionPolicy | null, + actorType: "agent" | "user", +) { + return setIssueExecutionPolicyMonitorScheduledBy(policy, actorType === "user" ? "board" : "assignee"); +} + +function assertCanManageIssueMonitor(req: Request, assigneeAgentId: string | null, monitorChanged: boolean) { + if (!monitorChanged) return; + if (req.actor.type === "board") return; + if (req.actor.type === "agent" && req.actor.agentId && req.actor.agentId === assigneeAgentId) return; + throw forbidden("Only the assignee agent or a board user can manage issue monitors"); +} + +function summarizeIssueMonitor( + issue: { + monitorNextCheckAt?: Date | null; + monitorLastTriggeredAt?: Date | null; + monitorAttemptCount?: number | null; + monitorNotes?: string | null; + monitorScheduledBy?: string | null; + executionState?: unknown; + }, + policy: NormalizedExecutionPolicy | null, +) { + const state = parseIssueExecutionState(issue.executionState); + return { + nextCheckAt: issue.monitorNextCheckAt?.toISOString() ?? policy?.monitor?.nextCheckAt ?? null, + lastTriggeredAt: issue.monitorLastTriggeredAt?.toISOString() ?? state?.monitor?.lastTriggeredAt ?? null, + attemptCount: issue.monitorAttemptCount ?? state?.monitor?.attemptCount ?? 0, + notes: policy?.monitor?.notes ?? issue.monitorNotes ?? state?.monitor?.notes ?? null, + scheduledBy: issue.monitorScheduledBy ?? policy?.monitor?.scheduledBy ?? state?.monitor?.scheduledBy ?? null, + kind: policy?.monitor?.kind ?? state?.monitor?.kind ?? null, + serviceName: policy?.monitor?.serviceName ?? state?.monitor?.serviceName ?? null, + externalRef: redactIssueMonitorExternalRef(policy?.monitor?.externalRef ?? state?.monitor?.externalRef ?? null), + timeoutAt: policy?.monitor?.timeoutAt ?? state?.monitor?.timeoutAt ?? null, + maxAttempts: policy?.monitor?.maxAttempts ?? state?.monitor?.maxAttempts ?? null, + recoveryPolicy: policy?.monitor?.recoveryPolicy ?? state?.monitor?.recoveryPolicy ?? null, + status: state?.monitor?.status ?? (policy?.monitor ? "scheduled" : null), + clearReason: state?.monitor?.clearReason ?? null, + }; +} + function activityExecutionParticipantKey(participant: ActivityExecutionParticipant): string { return participant.type === "agent" ? `agent:${participant.agentId}` : `user:${participant.userId}`; } @@ -1812,7 +1861,11 @@ export function issueRoutes( await assertIssueEnvironmentSelection(companyId, req.body.executionWorkspaceSettings?.environmentId); const actor = getActorInfo(req); - const executionPolicy = normalizeIssueExecutionPolicy(req.body.executionPolicy); + const executionPolicy = applyActorMonitorScheduledBy( + normalizeIssueExecutionPolicy(req.body.executionPolicy), + actor.actorType, + ); + assertCanManageIssueMonitor(req, req.body.assigneeAgentId ?? null, Boolean(executionPolicy?.monitor)); const issue = await svc.create(companyId, { ...req.body, executionPolicy, @@ -1847,6 +1900,29 @@ export function issueRoutes( }, }); + if (executionPolicy?.monitor) { + await logActivity(db, { + companyId, + actorType: actor.actorType, + actorId: actor.actorId, + agentId: actor.agentId, + runId: actor.runId, + action: "issue.monitor_scheduled", + entityType: "issue", + entityId: issue.id, + details: { + identifier: issue.identifier, + nextCheckAt: executionPolicy.monitor.nextCheckAt, + notes: executionPolicy.monitor.notes, + scheduledBy: executionPolicy.monitor.scheduledBy, + serviceName: executionPolicy.monitor.serviceName ?? null, + timeoutAt: executionPolicy.monitor.timeoutAt ?? null, + maxAttempts: executionPolicy.monitor.maxAttempts ?? null, + recoveryPolicy: executionPolicy.monitor.recoveryPolicy ?? null, + }, + }); + } + void queueIssueAssignmentWakeup({ heartbeat, issue, @@ -1879,7 +1955,11 @@ export function issueRoutes( await assertIssueEnvironmentSelection(parent.companyId, req.body.executionWorkspaceSettings?.environmentId); const actor = getActorInfo(req); - const executionPolicy = normalizeIssueExecutionPolicy(req.body.executionPolicy); + const executionPolicy = applyActorMonitorScheduledBy( + normalizeIssueExecutionPolicy(req.body.executionPolicy), + actor.actorType, + ); + assertCanManageIssueMonitor(req, req.body.assigneeAgentId ?? null, Boolean(executionPolicy?.monitor)); const { issue, parentBlockerAdded } = await svc.createChild(parent.id, { ...req.body, executionPolicy, @@ -1908,6 +1988,30 @@ export function issueRoutes( }, }); + if (executionPolicy?.monitor) { + await logActivity(db, { + companyId: parent.companyId, + actorType: actor.actorType, + actorId: actor.actorId, + agentId: actor.agentId, + runId: actor.runId, + action: "issue.monitor_scheduled", + entityType: "issue", + entityId: issue.id, + details: { + identifier: issue.identifier, + parentId: parent.id, + nextCheckAt: executionPolicy.monitor.nextCheckAt, + notes: executionPolicy.monitor.notes, + scheduledBy: executionPolicy.monitor.scheduledBy, + serviceName: executionPolicy.monitor.serviceName ?? null, + timeoutAt: executionPolicy.monitor.timeoutAt ?? null, + maxAttempts: executionPolicy.monitor.maxAttempts ?? null, + recoveryPolicy: executionPolicy.monitor.recoveryPolicy ?? null, + }, + }); + } + void queueIssueAssignmentWakeup({ heartbeat, issue, @@ -1921,6 +2025,27 @@ export function issueRoutes( res.status(201).json(issue); }); + router.post("/issues/:id/monitor/check-now", async (req, res) => { + const id = req.params.id as string; + const issue = await svc.getById(id); + if (!issue) { + res.status(404).json({ error: "Issue not found" }); + return; + } + assertCompanyAccess(req, issue.companyId); + assertCanManageIssueMonitor(req, issue.assigneeAgentId, true); + + const actor = getActorInfo(req); + await heartbeat.triggerIssueMonitor(issue.id, { + actorType: actor.actorType, + actorId: actor.actorId, + agentId: actor.agentId ?? null, + runId: actor.runId ?? null, + }); + + res.json({ ok: true }); + }); + router.patch("/issues/:id", validate(updateIssueRouteSchema), async (req, res) => { const id = req.params.id as string; const existing = await svc.getById(id); @@ -2043,7 +2168,10 @@ export function issueRoutes( updateFields.status = "todo"; } if (req.body.executionPolicy !== undefined) { - updateFields.executionPolicy = normalizeIssueExecutionPolicy(req.body.executionPolicy); + updateFields.executionPolicy = applyActorMonitorScheduledBy( + normalizeIssueExecutionPolicy(req.body.executionPolicy), + actor.actorType, + ); } const previousExecutionPolicy = normalizeIssueExecutionPolicy(existing.executionPolicy ?? null); const nextExecutionPolicy = @@ -2053,10 +2181,13 @@ export function issueRoutes( if (normalizedAssigneeAgentId !== undefined) { updateFields.assigneeAgentId = normalizedAssigneeAgentId; } + const monitorChanged = monitorPoliciesEqual(previousExecutionPolicy, nextExecutionPolicy) === false; + assertCanManageIssueMonitor(req, existing.assigneeAgentId, req.body.executionPolicy !== undefined && monitorChanged); const transition = applyIssueExecutionPolicyTransition({ issue: existing, policy: nextExecutionPolicy, + previousPolicy: previousExecutionPolicy, requestedStatus: typeof updateFields.status === "string" ? updateFields.status : undefined, requestedAssigneePatch: { assigneeAgentId: normalizedAssigneeAgentId, @@ -2069,6 +2200,7 @@ export function issueRoutes( }, commentBody, reviewRequest: reviewRequest === undefined ? undefined : reviewRequest, + monitorExplicitlyUpdated: req.body.executionPolicy !== undefined && monitorChanged, }); const decisionId = transition.decision ? randomUUID() : null; if (decisionId) { @@ -2372,6 +2504,51 @@ export function issueRoutes( }); } + const nextStoredExecutionPolicy = normalizeIssueExecutionPolicy(issue.executionPolicy ?? null); + const previousMonitor = summarizeIssueMonitor(existing, previousExecutionPolicy); + const nextMonitor = summarizeIssueMonitor(issue, nextStoredExecutionPolicy); + const monitorScheduledChanged = previousMonitor.nextCheckAt !== nextMonitor.nextCheckAt; + if (nextMonitor.nextCheckAt && (monitorScheduledChanged || previousMonitor.notes !== nextMonitor.notes)) { + await logActivity(db, { + companyId: issue.companyId, + actorType: actor.actorType, + actorId: actor.actorId, + agentId: actor.agentId, + runId: actor.runId, + action: "issue.monitor_scheduled", + entityType: "issue", + entityId: issue.id, + details: { + identifier: issue.identifier, + nextCheckAt: nextMonitor.nextCheckAt, + previousNextCheckAt: previousMonitor.nextCheckAt, + notes: nextMonitor.notes, + scheduledBy: nextMonitor.scheduledBy, + serviceName: nextMonitor.serviceName, + timeoutAt: nextMonitor.timeoutAt, + maxAttempts: nextMonitor.maxAttempts, + recoveryPolicy: nextMonitor.recoveryPolicy, + }, + }); + } else if (!nextMonitor.nextCheckAt && previousMonitor.nextCheckAt) { + await logActivity(db, { + companyId: issue.companyId, + actorType: actor.actorType, + actorId: actor.actorId, + agentId: actor.agentId, + runId: actor.runId, + action: "issue.monitor_cleared", + entityType: "issue", + entityId: issue.id, + details: { + identifier: issue.identifier, + previousNextCheckAt: previousMonitor.nextCheckAt, + reason: nextMonitor.clearReason ?? "manual", + notes: previousMonitor.notes, + }, + }); + } + if (issue.status === "done" && existing.status !== "done") { const tc = getTelemetryClient(); if (tc && actor.agentId) { diff --git a/server/src/services/heartbeat.ts b/server/src/services/heartbeat.ts index 330a8b70..4d335f5e 100644 --- a/server/src/services/heartbeat.ts +++ b/server/src/services/heartbeat.ts @@ -3,7 +3,7 @@ import path from "node:path"; import { execFile as execFileCallback } from "node:child_process"; import { promisify } from "node:util"; import { randomUUID } from "node:crypto"; -import { and, asc, desc, eq, getTableColumns, gt, inArray, isNull, lte, notInArray, or, sql } from "drizzle-orm"; +import { and, asc, desc, eq, getTableColumns, gt, inArray, isNull, lt, lte, notInArray, or, sql } from "drizzle-orm"; import type { Db } from "@paperclipai/db"; import { AGENT_DEFAULT_MAX_CONCURRENT_RUNS, @@ -14,6 +14,9 @@ import { type EnvironmentLeaseStatus, type ExecutionWorkspace, type ExecutionWorkspaceConfig, + type IssueExecutionMonitorClearReason, + type IssueExecutionMonitorPolicy, + type IssueExecutionMonitorRecoveryPolicy, type ModelProfileKey, type RunLivenessState, } from "@paperclipai/shared"; @@ -85,7 +88,12 @@ import { sanitizeRuntimeServiceBaseEnv, } from "./workspace-runtime.js"; import { issueService } from "./issues.js"; -import { parseIssueExecutionState } from "./issue-execution-policy.js"; +import { + buildIssueMonitorClearedPatch, + buildIssueMonitorTriggeredPatch, + normalizeIssueExecutionPolicy, + parseIssueExecutionState, +} from "./issue-execution-policy.js"; import { ISSUE_TREE_CONTROL_INTERACTION_WAKE_REASONS, isVerifiedIssueTreeControlInteractionWake, @@ -2328,6 +2336,689 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {}) .then((rows) => rows[0] ?? null); } + const issueMonitorDispatchColumns = { + id: issues.id, + companyId: issues.companyId, + projectId: issues.projectId, + goalId: issues.goalId, + identifier: issues.identifier, + title: issues.title, + status: issues.status, + priority: issues.priority, + assigneeAgentId: issues.assigneeAgentId, + assigneeUserId: issues.assigneeUserId, + billingCode: issues.billingCode, + executionPolicy: issues.executionPolicy, + executionState: issues.executionState, + monitorNextCheckAt: issues.monitorNextCheckAt, + monitorWakeRequestedAt: issues.monitorWakeRequestedAt, + monitorLastTriggeredAt: issues.monitorLastTriggeredAt, + monitorAttemptCount: issues.monitorAttemptCount, + monitorNotes: issues.monitorNotes, + monitorScheduledBy: issues.monitorScheduledBy, + }; + + interface IssueMonitorDispatchRow { + id: string; + companyId: string; + projectId: string | null; + goalId: string | null; + identifier: string | null; + title: string; + status: string; + priority: string; + assigneeAgentId: string | null; + assigneeUserId: string | null; + billingCode: string | null; + executionPolicy: Record | null; + executionState: Record | null; + monitorNextCheckAt: Date | null; + monitorWakeRequestedAt: Date | null; + monitorLastTriggeredAt: Date | null; + monitorAttemptCount: number | null; + monitorNotes: string | null; + monitorScheduledBy: string | null; + } + + function parseMonitorDate(value: string | null | undefined) { + if (!value) return null; + const date = new Date(value); + return Number.isNaN(date.getTime()) ? null : date; + } + + function issueMonitorLimitClearReason(input: { + monitor: IssueExecutionMonitorPolicy | null; + nextAttemptCount: number; + now: Date; + }): IssueExecutionMonitorClearReason | null { + const timeoutAt = parseMonitorDate(input.monitor?.timeoutAt ?? null); + if (timeoutAt && input.now.getTime() >= timeoutAt.getTime()) { + return "timeout_exceeded"; + } + const maxAttempts = input.monitor?.maxAttempts ?? null; + if (maxAttempts !== null && input.nextAttemptCount > maxAttempts) { + return "max_attempts_exhausted"; + } + return null; + } + + function monitorRecoveryPolicy( + monitor: IssueExecutionMonitorPolicy | null, + ): IssueExecutionMonitorRecoveryPolicy { + return monitor?.recoveryPolicy ?? "wake_owner"; + } + + function monitorRecoveryDetails(input: { + claimed: IssueMonitorDispatchRow; + scheduledAtIso: string; + nextAttemptCount: number; + clearReason: IssueExecutionMonitorClearReason; + recoveryPolicy: IssueExecutionMonitorRecoveryPolicy; + monitor: IssueExecutionMonitorPolicy | null; + source: "manual" | "scheduled"; + }) { + return { + identifier: input.claimed.identifier, + nextCheckAt: input.scheduledAtIso, + attemptedAttemptCount: input.nextAttemptCount, + notes: input.claimed.monitorNotes ?? null, + serviceName: input.monitor?.serviceName ?? null, + timeoutAt: input.monitor?.timeoutAt ?? null, + maxAttempts: input.monitor?.maxAttempts ?? null, + clearReason: input.clearReason, + recoveryPolicy: input.recoveryPolicy, + source: input.source, + }; + } + + function formatIssueIdentifierLink(identifier: string | null, fallback: string) { + if (!identifier) return fallback; + const prefix = identifier.split("-")[0]; + if (!prefix || !/^[A-Z][A-Z0-9]*-\d+$/.test(identifier)) return identifier; + return `[${identifier}](/${prefix}/issues/${identifier})`; + } + + function monitorRecoveryComment(input: { + issue: IssueMonitorDispatchRow; + clearReason: IssueExecutionMonitorClearReason; + recoveryPolicy: IssueExecutionMonitorRecoveryPolicy; + nextAttemptCount: number; + }) { + const label = formatIssueIdentifierLink(input.issue.identifier, input.issue.id); + const reason = + input.clearReason === "timeout_exceeded" + ? "its timeout was reached" + : "its maximum attempt count was reached"; + return [ + `Paperclip cleared the scheduled external-service monitor for ${label} because ${reason}.`, + "", + `- Attempt count: ${input.nextAttemptCount}`, + `- Recovery policy: ${input.recoveryPolicy}`, + "", + "Next action: inspect the external service state, record the result on this issue, and restore an explicit execution or waiting path if more work remains.", + ].join("\n"); + } + + async function findOpenIssueMonitorRecoveryIssue(claimed: IssueMonitorDispatchRow) { + return db + .select() + .from(issues) + .where( + and( + eq(issues.companyId, claimed.companyId), + eq(issues.originKind, RECOVERY_ORIGIN_KINDS.strandedIssueRecovery), + eq(issues.originId, claimed.id), + isNull(issues.hiddenAt), + notInArray(issues.status, ["done", "cancelled"]), + ), + ) + .orderBy(desc(issues.createdAt)) + .limit(1) + .then((rows) => rows[0] ?? null); + } + + async function performIssueMonitorRecovery(input: { + claimed: IssueMonitorDispatchRow; + scheduledAtIso: string; + nextAttemptCount: number; + clearReason: IssueExecutionMonitorClearReason; + recoveryPolicy: IssueExecutionMonitorRecoveryPolicy; + monitor: IssueExecutionMonitorPolicy | null; + actorType: "user" | "agent" | "system"; + actorId: string; + agentId: string | null; + runId: string | null; + activitySource: "manual" | "scheduled"; + }) { + const details = monitorRecoveryDetails({ + claimed: input.claimed, + scheduledAtIso: input.scheduledAtIso, + nextAttemptCount: input.nextAttemptCount, + clearReason: input.clearReason, + recoveryPolicy: input.recoveryPolicy, + monitor: input.monitor, + source: input.activitySource, + }); + + if (input.recoveryPolicy === "create_recovery_issue") { + let recoveryIssue = await findOpenIssueMonitorRecoveryIssue(input.claimed); + if (!recoveryIssue) { + recoveryIssue = await issuesSvc.create(input.claimed.companyId, { + title: `Recover external-service monitor for ${input.claimed.identifier ?? input.claimed.title}`, + description: monitorRecoveryComment({ + issue: input.claimed, + clearReason: input.clearReason, + recoveryPolicy: input.recoveryPolicy, + nextAttemptCount: input.nextAttemptCount, + }), + status: "todo", + priority: "high", + parentId: input.claimed.id, + projectId: input.claimed.projectId, + goalId: input.claimed.goalId, + assigneeAgentId: input.claimed.assigneeAgentId, + originKind: RECOVERY_ORIGIN_KINDS.strandedIssueRecovery, + originId: input.claimed.id, + originFingerprint: `issue_monitor:${input.clearReason}`, + billingCode: input.claimed.billingCode, + }); + } + + if (recoveryIssue.assigneeAgentId) { + await enqueueWakeup(recoveryIssue.assigneeAgentId, { + source: "automation", + triggerDetail: "system", + reason: "issue_monitor_recovery_issue", + idempotencyKey: `issue-monitor-recovery-issue:${input.claimed.id}:${input.clearReason}:${input.scheduledAtIso}`, + payload: { issueId: recoveryIssue.id, sourceIssueId: input.claimed.id }, + requestedByActorType: input.actorType, + requestedByActorId: input.actorId, + contextSnapshot: { + issueId: recoveryIssue.id, + sourceIssueId: input.claimed.id, + source: "issue.monitor.recovery_issue", + wakeReason: "issue_monitor_recovery_issue", + }, + }); + } + + await logActivity(db, { + companyId: input.claimed.companyId, + actorType: input.actorType, + actorId: input.actorId, + agentId: input.agentId, + runId: input.runId, + action: "issue.monitor_recovery_issue_created", + entityType: "issue", + entityId: input.claimed.id, + details: { + ...details, + recoveryIssueId: recoveryIssue.id, + recoveryIdentifier: recoveryIssue.identifier, + }, + }); + return; + } + + if (input.recoveryPolicy === "escalate_to_board") { + await db.insert(issueComments).values({ + companyId: input.claimed.companyId, + issueId: input.claimed.id, + body: monitorRecoveryComment({ + issue: input.claimed, + clearReason: input.clearReason, + recoveryPolicy: input.recoveryPolicy, + nextAttemptCount: input.nextAttemptCount, + }), + }); + + await logActivity(db, { + companyId: input.claimed.companyId, + actorType: input.actorType, + actorId: input.actorId, + agentId: input.agentId, + runId: input.runId, + action: "issue.monitor_escalated_to_board", + entityType: "issue", + entityId: input.claimed.id, + details, + }); + return; + } + + await enqueueWakeup(input.claimed.assigneeAgentId!, { + source: "automation", + triggerDetail: "system", + reason: "issue_monitor_recovery", + idempotencyKey: `issue-monitor-recovery:${input.claimed.id}:${input.clearReason}:${input.scheduledAtIso}`, + payload: { + issueId: input.claimed.id, + monitorAttemptCount: input.nextAttemptCount, + monitorNotes: input.claimed.monitorNotes ?? null, + clearReason: input.clearReason, + serviceName: input.monitor?.serviceName ?? null, + timeoutAt: input.monitor?.timeoutAt ?? null, + maxAttempts: input.monitor?.maxAttempts ?? null, + }, + requestedByActorType: input.actorType, + requestedByActorId: input.actorId, + contextSnapshot: { + issueId: input.claimed.id, + source: "issue.monitor.recovery", + wakeReason: "issue_monitor_recovery", + monitorAttemptCount: input.nextAttemptCount, + monitorNotes: input.claimed.monitorNotes ?? null, + clearReason: input.clearReason, + serviceName: input.monitor?.serviceName ?? null, + timeoutAt: input.monitor?.timeoutAt ?? null, + maxAttempts: input.monitor?.maxAttempts ?? null, + }, + }); + + await logActivity(db, { + companyId: input.claimed.companyId, + actorType: input.actorType, + actorId: input.actorId, + agentId: input.agentId, + runId: input.runId, + action: "issue.monitor_recovery_wake_queued", + entityType: "issue", + entityId: input.claimed.id, + details, + }); + } + + async function clearIssueMonitorAndRecover(input: { + claimed: IssueMonitorDispatchRow; + policy: ReturnType; + scheduledAtIso: string; + nextAttemptCount: number; + clearReason: IssueExecutionMonitorClearReason; + recoveryPolicy: IssueExecutionMonitorRecoveryPolicy; + monitor: IssueExecutionMonitorPolicy | null; + now: Date; + actorType: "user" | "agent" | "system"; + actorId: string; + agentId: string | null; + runId: string | null; + activitySource: "manual" | "scheduled"; + }) { + await db + .update(issues) + .set({ + ...buildIssueMonitorClearedPatch({ + issue: input.claimed, + policy: input.policy, + clearReason: input.clearReason, + clearedAt: input.now, + }), + updatedAt: input.now, + }) + .where(eq(issues.id, input.claimed.id)); + + await logActivity(db, { + companyId: input.claimed.companyId, + actorType: input.actorType, + actorId: input.actorId, + agentId: input.agentId, + runId: input.runId, + action: "issue.monitor_exhausted", + entityType: "issue", + entityId: input.claimed.id, + details: monitorRecoveryDetails({ + claimed: input.claimed, + scheduledAtIso: input.scheduledAtIso, + nextAttemptCount: input.nextAttemptCount, + clearReason: input.clearReason, + recoveryPolicy: input.recoveryPolicy, + monitor: input.monitor, + source: input.activitySource, + }), + }); + + await performIssueMonitorRecovery({ + claimed: input.claimed, + scheduledAtIso: input.scheduledAtIso, + nextAttemptCount: input.nextAttemptCount, + clearReason: input.clearReason, + recoveryPolicy: input.recoveryPolicy, + monitor: input.monitor, + actorType: input.actorType, + actorId: input.actorId, + agentId: input.agentId, + runId: input.runId, + activitySource: input.activitySource, + }); + + return { outcome: "skipped" as const, reason: input.clearReason }; + } + + async function dispatchClaimedIssueMonitor( + claimed: IssueMonitorDispatchRow, + input: { + now: Date; + source: "automation" | "on_demand"; + triggerDetail: "manual" | "system"; + wakeReason: string; + actorType: "user" | "agent" | "system"; + actorId: string; + agentId: string | null; + runId: string | null; + clearOnClientError: boolean; + activitySource: "manual" | "scheduled"; + }, + ) { + if (!claimed.assigneeAgentId || !claimed.monitorNextCheckAt) { + throw conflict("Issue monitor is not ready to dispatch"); + } + + const scheduledAtIso = claimed.monitorNextCheckAt.toISOString(); + const nextAttemptCount = (claimed.monitorAttemptCount ?? 0) + 1; + const policy = normalizeIssueExecutionPolicy(claimed.executionPolicy ?? null); + const monitor = policy?.monitor ?? null; + const clearReason = issueMonitorLimitClearReason({ monitor, nextAttemptCount, now: input.now }); + const recoveryPolicy = monitorRecoveryPolicy(monitor); + const monitorMetadata = { + serviceName: monitor?.serviceName ?? null, + timeoutAt: monitor?.timeoutAt ?? null, + maxAttempts: monitor?.maxAttempts ?? null, + recoveryPolicy: monitor?.recoveryPolicy ?? null, + }; + + if (clearReason) { + return clearIssueMonitorAndRecover({ + claimed, + policy, + scheduledAtIso, + nextAttemptCount, + clearReason, + recoveryPolicy, + monitor, + now: input.now, + actorType: input.actorType, + actorId: input.actorId, + agentId: input.agentId, + runId: input.runId, + activitySource: input.activitySource, + }); + } + + try { + await enqueueWakeup(claimed.assigneeAgentId, { + source: input.source, + triggerDetail: input.triggerDetail, + reason: input.wakeReason, + idempotencyKey: `issue-monitor:${claimed.id}:${scheduledAtIso}`, + payload: { + issueId: claimed.id, + nextCheckAt: scheduledAtIso, + monitorAttemptCount: nextAttemptCount, + monitorNotes: claimed.monitorNotes ?? null, + ...monitorMetadata, + source: input.activitySource, + }, + requestedByActorType: input.actorType, + requestedByActorId: input.actorId, + contextSnapshot: { + issueId: claimed.id, + source: "issue.monitor", + wakeReason: input.wakeReason, + nextCheckAt: scheduledAtIso, + monitorAttemptCount: nextAttemptCount, + monitorNotes: claimed.monitorNotes ?? null, + ...monitorMetadata, + manualTrigger: input.activitySource === "manual", + }, + }); + + await db + .update(issues) + .set({ + ...buildIssueMonitorTriggeredPatch({ + issue: claimed, + policy, + triggeredAt: input.now, + }), + updatedAt: new Date(), + }) + .where(eq(issues.id, claimed.id)); + + await logActivity(db, { + companyId: claimed.companyId, + actorType: input.actorType, + actorId: input.actorId, + agentId: input.agentId, + runId: input.runId, + action: "issue.monitor_triggered", + entityType: "issue", + entityId: claimed.id, + details: { + identifier: claimed.identifier, + nextCheckAt: scheduledAtIso, + lastTriggeredAt: input.now.toISOString(), + attemptCount: nextAttemptCount, + notes: claimed.monitorNotes ?? null, + ...monitorMetadata, + source: input.activitySource, + }, + }); + + return { outcome: "triggered" as const }; + } catch (err) { + if (err instanceof HttpError && err.status >= 400 && err.status < 500) { + if (input.clearOnClientError) { + await db + .update(issues) + .set({ + ...buildIssueMonitorClearedPatch({ + issue: claimed, + policy, + clearReason: "dispatch_skipped", + clearedAt: input.now, + }), + updatedAt: new Date(), + }) + .where(eq(issues.id, claimed.id)); + + await logActivity(db, { + companyId: claimed.companyId, + actorType: input.actorType, + actorId: input.actorId, + agentId: input.agentId, + runId: input.runId, + action: "issue.monitor_skipped", + entityType: "issue", + entityId: claimed.id, + details: { + identifier: claimed.identifier, + nextCheckAt: scheduledAtIso, + attemptCount: nextAttemptCount, + notes: claimed.monitorNotes ?? null, + reason: err.message, + source: input.activitySource, + }, + }); + + return { outcome: "skipped" as const, reason: err.message }; + } + + await db + .update(issues) + .set({ + monitorWakeRequestedAt: null, + updatedAt: new Date(), + }) + .where(eq(issues.id, claimed.id)); + } else { + await db + .update(issues) + .set({ + monitorWakeRequestedAt: null, + updatedAt: new Date(), + }) + .where(eq(issues.id, claimed.id)); + } + + throw err; + } + } + + async function triggerIssueMonitor(issueId: string, input?: { + now?: Date; + actorType?: "user" | "agent" | "system"; + actorId?: string | null; + agentId?: string | null; + runId?: string | null; + wakeReason?: string; + }) { + const now = input?.now ?? new Date(); + const actorType = input?.actorType ?? "system"; + const actorId = input?.actorId ?? (actorType === "system" ? "heartbeat_scheduler" : null); + if (!actorId) { + throw conflict("Issue monitor trigger requires an actor"); + } + + const issue = await db + .select(issueMonitorDispatchColumns) + .from(issues) + .where(eq(issues.id, issueId)) + .limit(1) + .then((rows) => rows[0] ?? null); + if (!issue) { + throw notFound("Issue not found"); + } + if (!issue.monitorNextCheckAt) { + throw conflict("Issue has no scheduled monitor"); + } + if (!issue.assigneeAgentId || issue.assigneeUserId) { + throw conflict("Issue monitor requires an agent assignee"); + } + if (!["in_progress", "in_review"].includes(issue.status)) { + throw conflict("Issue monitor can only run while the issue is in progress or in review"); + } + + const staleClaimThreshold = new Date(now.getTime() - 5 * 60 * 1000); + const claimed = await db.transaction(async (tx) => { + const [updated] = await tx + .update(issues) + .set({ + monitorWakeRequestedAt: now, + updatedAt: now, + }) + .where( + and( + eq(issues.id, issueId), + sql`${issues.monitorNextCheckAt} is not null`, + isNull(issues.assigneeUserId), + sql`${issues.assigneeAgentId} is not null`, + inArray(issues.status, ["in_progress", "in_review"]), + or( + isNull(issues.monitorWakeRequestedAt), + lt(issues.monitorWakeRequestedAt, staleClaimThreshold), + ), + ), + ) + .returning(); + return (updated ?? null) as IssueMonitorDispatchRow | null; + }); + + if (!claimed) { + throw conflict("Issue monitor check is already in progress"); + } + + return dispatchClaimedIssueMonitor(claimed, { + now, + source: "on_demand", + triggerDetail: "manual", + wakeReason: input?.wakeReason ?? "issue_monitor_due", + actorType, + actorId, + agentId: input?.agentId ?? null, + runId: input?.runId ?? null, + clearOnClientError: false, + activitySource: "manual", + }); + } + + async function tickDueIssueMonitors(now = new Date()) { + const staleClaimThreshold = new Date(now.getTime() - 5 * 60 * 1000); + const dueMonitors = await db + .select(issueMonitorDispatchColumns) + .from(issues) + .where( + and( + sql`${issues.monitorNextCheckAt} is not null`, + lte(issues.monitorNextCheckAt, now), + isNull(issues.assigneeUserId), + sql`${issues.assigneeAgentId} is not null`, + inArray(issues.status, ["in_progress", "in_review"]), + or( + isNull(issues.monitorWakeRequestedAt), + lt(issues.monitorWakeRequestedAt, staleClaimThreshold), + ), + ), + ) + .orderBy(asc(issues.monitorNextCheckAt), asc(issues.updatedAt)) + .limit(50); + + let triggered = 0; + let skipped = 0; + + for (const due of dueMonitors) { + const claimed = await db.transaction(async (tx) => { + const [updated] = await tx + .update(issues) + .set({ + monitorWakeRequestedAt: now, + updatedAt: now, + }) + .where( + and( + eq(issues.id, due.id), + sql`${issues.monitorNextCheckAt} is not null`, + lte(issues.monitorNextCheckAt, now), + isNull(issues.assigneeUserId), + sql`${issues.assigneeAgentId} is not null`, + inArray(issues.status, ["in_progress", "in_review"]), + or( + isNull(issues.monitorWakeRequestedAt), + lt(issues.monitorWakeRequestedAt, staleClaimThreshold), + ), + ), + ) + .returning(); + return (updated ?? null) as IssueMonitorDispatchRow | null; + }); + + if (!claimed) continue; + + try { + const result = await dispatchClaimedIssueMonitor(claimed, { + now, + source: "automation", + triggerDetail: "system", + wakeReason: "issue_monitor_due", + actorType: "system", + actorId: "heartbeat_scheduler", + agentId: null, + runId: null, + clearOnClientError: true, + activitySource: "scheduled", + }); + if (result.outcome === "triggered") triggered += 1; + if (result.outcome === "skipped") skipped += 1; + } catch (err) { + logger.error({ err, issueId: claimed.id }, "issue monitor tick failed"); + } + } + + return { + checked: dueMonitors.length, + triggered, + skipped, + }; + } + async function getOldestRunForSession(agentId: string, sessionId: string) { return db .select({ @@ -7735,6 +8426,7 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {}) }), wakeup: enqueueWakeup, + triggerIssueMonitor, reportRunActivity: clearDetachedRunWarning, @@ -7804,7 +8496,13 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {}) else skipped += 1; } - return { checked, enqueued, skipped }; + const issueMonitors = await tickDueIssueMonitors(now); + + return { + checked: checked + issueMonitors.checked, + enqueued: enqueued + issueMonitors.triggered, + skipped: skipped + issueMonitors.skipped, + }; }, cancelRun: (runId: string) => cancelRunInternal(runId), diff --git a/server/src/services/issue-execution-policy.ts b/server/src/services/issue-execution-policy.ts index 9a78512d..37b75c84 100644 --- a/server/src/services/issue-execution-policy.ts +++ b/server/src/services/issue-execution-policy.ts @@ -1,5 +1,15 @@ import { randomUUID } from "node:crypto"; -import type { IssueExecutionDecision, IssueExecutionPolicy, IssueExecutionStage, IssueExecutionStagePrincipal, IssueExecutionState } from "@paperclipai/shared"; +import type { + IssueExecutionDecision, + IssueExecutionMonitorClearReason, + IssueExecutionMonitorPolicy, + IssueExecutionMonitorState, + IssueExecutionPolicy, + IssueExecutionStage, + IssueExecutionStagePrincipal, + IssueExecutionState, + IssueMonitorScheduledBy, +} from "@paperclipai/shared"; import { issueExecutionPolicySchema, issueExecutionStateSchema } from "@paperclipai/shared"; import { unprocessable } from "../errors.js"; @@ -12,6 +22,12 @@ type IssueLike = AssigneeLike & { status: string; executionPolicy?: IssueExecutionPolicy | Record | null; executionState?: IssueExecutionState | Record | null; + monitorNextCheckAt?: Date | null; + monitorWakeRequestedAt?: Date | null; + monitorLastTriggeredAt?: Date | null; + monitorAttemptCount?: number | null; + monitorNotes?: string | null; + monitorScheduledBy?: string | null; }; type ActorLike = { @@ -27,11 +43,13 @@ type RequestedAssigneePatch = { type TransitionInput = { issue: IssueLike; policy: IssueExecutionPolicy | null; + previousPolicy?: IssueExecutionPolicy | null; requestedStatus?: string; requestedAssigneePatch: RequestedAssigneePatch; actor: ActorLike; commentBody?: string | null; reviewRequest?: IssueExecutionState["reviewRequest"] | null; + monitorExplicitlyUpdated?: boolean; }; type TransitionResult = { @@ -43,6 +61,280 @@ type TransitionResult = { const COMPLETED_STATUS: IssueExecutionState["status"] = "completed"; const PENDING_STATUS: IssueExecutionState["status"] = "pending"; const CHANGES_REQUESTED_STATUS: IssueExecutionState["status"] = "changes_requested"; +const MONITOR_INVALID_MESSAGE = "Monitor can only be scheduled on issues assigned to an agent in in_progress or in_review"; +const MONITOR_BOUNDS_EXHAUSTED_MESSAGE = "Monitor bounds are already exhausted"; +export const REDACTED_ISSUE_MONITOR_EXTERNAL_REF = "[redacted]"; + +function normalizeMonitorNotes(notes: string | null | undefined) { + if (typeof notes !== "string") return null; + const trimmed = notes.trim(); + return trimmed.length > 0 ? trimmed : null; +} + +function normalizeMonitorText(value: string | null | undefined) { + if (typeof value !== "string") return null; + const trimmed = value.trim(); + return trimmed.length > 0 ? trimmed : null; +} + +export function redactIssueMonitorExternalRef(value: string | null | undefined) { + return normalizeMonitorText(value) ? REDACTED_ISSUE_MONITOR_EXTERNAL_REF : null; +} + +function monitorMetadataFromPolicy(monitor: IssueExecutionMonitorPolicy) { + return { + kind: monitor.kind ?? null, + serviceName: normalizeMonitorText(monitor.serviceName), + externalRef: redactIssueMonitorExternalRef(monitor.externalRef), + timeoutAt: monitor.timeoutAt ?? null, + maxAttempts: monitor.maxAttempts ?? null, + recoveryPolicy: monitor.recoveryPolicy ?? null, + }; +} + +function monitorMetadataFromState(state: IssueExecutionMonitorState | null | undefined) { + return { + kind: state?.kind ?? null, + serviceName: normalizeMonitorText(state?.serviceName), + externalRef: redactIssueMonitorExternalRef(state?.externalRef), + timeoutAt: state?.timeoutAt ?? null, + maxAttempts: state?.maxAttempts ?? null, + recoveryPolicy: state?.recoveryPolicy ?? null, + }; +} + +function blankExecutionState(): IssueExecutionState { + return { + status: "idle", + currentStageId: null, + currentStageIndex: null, + currentStageType: null, + currentParticipant: null, + returnAssignee: null, + reviewRequest: null, + completedStageIds: [], + lastDecisionId: null, + lastDecisionOutcome: null, + monitor: null, + }; +} + +function isoString(value: Date | string | null | undefined): string | null { + if (!value) return null; + if (value instanceof Date) return value.toISOString(); + return value; +} + +function monitorStatesEqual(left: IssueExecutionMonitorState | null, right: IssueExecutionMonitorState | null): boolean { + return JSON.stringify(left ?? null) === JSON.stringify(right ?? null); +} + +function executionStateWithMonitor( + stageState: IssueExecutionState | null, + monitorState: IssueExecutionMonitorState | null, +): IssueExecutionState | null { + if (!stageState && !monitorState) return null; + const base = stageState ? { ...stageState } : blankExecutionState(); + return { + ...base, + monitor: monitorState, + }; +} + +function derivePersistedMonitorState(input: { + issue: IssueLike; + state: IssueExecutionState | null; + policy: IssueExecutionPolicy | null; +}): IssueExecutionMonitorState | null { + const fromState = input.state?.monitor ?? null; + const scheduledMonitor = input.policy?.monitor ?? null; + const nextCheckAt = isoString(input.issue.monitorNextCheckAt) ?? scheduledMonitor?.nextCheckAt ?? fromState?.nextCheckAt ?? null; + const lastTriggeredAt = isoString(input.issue.monitorLastTriggeredAt) ?? fromState?.lastTriggeredAt ?? null; + const attemptCount = input.issue.monitorAttemptCount ?? fromState?.attemptCount ?? 0; + const notes = scheduledMonitor?.notes ?? normalizeMonitorNotes(input.issue.monitorNotes) ?? fromState?.notes ?? null; + const scheduledByRaw = input.issue.monitorScheduledBy ?? scheduledMonitor?.scheduledBy ?? fromState?.scheduledBy ?? null; + const scheduledBy = + scheduledByRaw === "assignee" || scheduledByRaw === "board" ? scheduledByRaw : null; + const metadata = scheduledMonitor ? monitorMetadataFromPolicy(scheduledMonitor) : monitorMetadataFromState(fromState); + + if (nextCheckAt) { + return { + status: "scheduled", + nextCheckAt, + lastTriggeredAt, + attemptCount, + notes, + scheduledBy, + ...metadata, + clearedAt: null, + clearReason: null, + }; + } + + if (fromState?.status === "cleared") { + return { + ...fromState, + notes, + scheduledBy, + attemptCount, + lastTriggeredAt, + ...metadata, + }; + } + + if (fromState?.status === "triggered" || lastTriggeredAt || attemptCount > 0) { + return { + status: "triggered", + nextCheckAt: null, + lastTriggeredAt, + attemptCount, + notes, + scheduledBy, + ...metadata, + clearedAt: null, + clearReason: null, + }; + } + + return null; +} + +function buildScheduledMonitorState( + previous: IssueExecutionMonitorState | null, + monitor: IssueExecutionMonitorPolicy, +): IssueExecutionMonitorState { + return { + status: "scheduled", + nextCheckAt: monitor.nextCheckAt, + lastTriggeredAt: previous?.lastTriggeredAt ?? null, + attemptCount: previous?.attemptCount ?? 0, + notes: monitor.notes ?? null, + scheduledBy: monitor.scheduledBy, + ...monitorMetadataFromPolicy(monitor), + clearedAt: null, + clearReason: null, + }; +} + +function buildTriggeredMonitorState(input: { + previous: IssueExecutionMonitorState | null; + triggeredAt: Date; +}): IssueExecutionMonitorState { + return { + status: "triggered", + nextCheckAt: null, + lastTriggeredAt: input.triggeredAt.toISOString(), + attemptCount: (input.previous?.attemptCount ?? 0) + 1, + notes: input.previous?.notes ?? null, + scheduledBy: input.previous?.scheduledBy ?? null, + ...monitorMetadataFromState(input.previous), + clearedAt: null, + clearReason: null, + }; +} + +function buildClearedMonitorState(input: { + previous: IssueExecutionMonitorState | null; + clearReason: IssueExecutionMonitorClearReason; + clearedAt: Date; +}): IssueExecutionMonitorState { + return { + status: "cleared", + nextCheckAt: null, + lastTriggeredAt: input.previous?.lastTriggeredAt ?? null, + attemptCount: input.previous?.attemptCount ?? 0, + notes: input.previous?.notes ?? null, + scheduledBy: input.previous?.scheduledBy ?? null, + ...monitorMetadataFromState(input.previous), + clearedAt: input.clearedAt.toISOString(), + clearReason: input.clearReason, + }; +} + +function issueAllowsMonitor(status: string, assigneeAgentId: string | null, assigneeUserId: string | null) { + return Boolean(assigneeAgentId) && !assigneeUserId && (status === "in_progress" || status === "in_review"); +} + +function monitorClearReasonForIssue( + status: string, + assigneeAgentId: string | null, + assigneeUserId: string | null, +): IssueExecutionMonitorClearReason | null { + if (status === "done") return "done"; + if (status === "cancelled") return "cancelled"; + if (!issueAllowsMonitor(status, assigneeAgentId, assigneeUserId)) { + if (assigneeUserId || !assigneeAgentId) return "invalid_assignee"; + return "invalid_status"; + } + return null; +} + +function parseMonitorDate(value: string | null | undefined) { + if (!value) return null; + const date = new Date(value); + return Number.isNaN(date.getTime()) ? null : date; +} + +function exhaustedMonitorClearReason(input: { + monitor: IssueExecutionMonitorPolicy; + attemptCount: number; + now: Date; +}): IssueExecutionMonitorClearReason | null { + const timeoutAt = parseMonitorDate(input.monitor.timeoutAt ?? null); + if (timeoutAt && input.now.getTime() >= timeoutAt.getTime()) { + return "timeout_exceeded"; + } + const maxAttempts = input.monitor.maxAttempts ?? null; + if (maxAttempts !== null && input.attemptCount >= maxAttempts) { + return "max_attempts_exhausted"; + } + return null; +} + +function nextAssigneeIds(input: { + issue: IssueLike; + requestedAssigneePatch: RequestedAssigneePatch; + stagePatch: Record; +}) { + const assigneeAgentId = + input.stagePatch.assigneeAgentId !== undefined + ? (input.stagePatch.assigneeAgentId as string | null) + : input.requestedAssigneePatch.assigneeAgentId !== undefined + ? input.requestedAssigneePatch.assigneeAgentId ?? null + : input.issue.assigneeAgentId ?? null; + const assigneeUserId = + input.stagePatch.assigneeUserId !== undefined + ? (input.stagePatch.assigneeUserId as string | null) + : input.requestedAssigneePatch.assigneeUserId !== undefined + ? input.requestedAssigneePatch.assigneeUserId ?? null + : input.issue.assigneeUserId ?? null; + return { assigneeAgentId, assigneeUserId }; +} + +export function stripMonitorFromExecutionPolicy(policy: IssueExecutionPolicy | null): IssueExecutionPolicy | null { + if (!policy) return null; + if (!policy.monitor) return policy; + if (policy.stages.length === 0) return null; + return { + mode: policy.mode, + commentRequired: policy.commentRequired, + stages: policy.stages, + }; +} + +export function setIssueExecutionPolicyMonitorScheduledBy( + policy: IssueExecutionPolicy | null, + scheduledBy: IssueMonitorScheduledBy, +): IssueExecutionPolicy | null { + if (!policy?.monitor) return policy; + return { + ...policy, + monitor: { + ...policy.monitor, + scheduledBy, + }, + }; +} export function normalizeIssueExecutionPolicy(input: unknown): IssueExecutionPolicy | null { if (input == null) return null; @@ -81,12 +373,27 @@ export function normalizeIssueExecutionPolicy(input: unknown): IssueExecutionPol }) .filter((stage): stage is NonNullable => stage !== null); - if (stages.length === 0) return null; + const monitor = parsed.data.monitor + ? { + nextCheckAt: parsed.data.monitor.nextCheckAt, + notes: normalizeMonitorNotes(parsed.data.monitor.notes), + scheduledBy: parsed.data.monitor.scheduledBy, + kind: parsed.data.monitor.kind ?? null, + serviceName: normalizeMonitorText(parsed.data.monitor.serviceName), + externalRef: redactIssueMonitorExternalRef(parsed.data.monitor.externalRef), + timeoutAt: parsed.data.monitor.timeoutAt ?? null, + maxAttempts: parsed.data.monitor.maxAttempts ?? null, + recoveryPolicy: parsed.data.monitor.recoveryPolicy ?? null, + } + : null; + + if (stages.length === 0 && !monitor) return null; return { mode: parsed.data.mode ?? "normal", commentRequired: true, stages, + ...(monitor ? { monitor } : {}), }; } @@ -173,6 +480,7 @@ function buildCompletedState(previous: IssueExecutionState | null, currentStage: completedStageIds, lastDecisionId: previous?.lastDecisionId ?? null, lastDecisionOutcome: "approved", + monitor: previous?.monitor ?? null, }; } @@ -192,6 +500,7 @@ function buildStateWithCompletedStages(input: { completedStageIds: input.completedStageIds, lastDecisionId: input.previous?.lastDecisionId ?? null, lastDecisionOutcome: input.previous?.lastDecisionOutcome ?? null, + monitor: input.previous?.monitor ?? null, }; } @@ -211,6 +520,7 @@ function buildSkippedStageCompletedState(input: { completedStageIds: input.completedStageIds, lastDecisionId: input.previous?.lastDecisionId ?? null, lastDecisionOutcome: input.previous?.lastDecisionOutcome ?? null, + monitor: input.previous?.monitor ?? null, }; } @@ -233,6 +543,7 @@ function buildPendingState(input: { completedStageIds: input.previous?.completedStageIds ?? [], lastDecisionId: input.previous?.lastDecisionId ?? null, lastDecisionOutcome: input.previous?.lastDecisionOutcome ?? null, + monitor: input.previous?.monitor ?? null, }; } @@ -293,7 +604,7 @@ function canAutoSkipPendingStage(input: { input.stage.participants.every((participant) => principalsEqual(participant, input.returnAssignee)); } -export function applyIssueExecutionPolicyTransition(input: TransitionInput): TransitionResult { +function applyIssueExecutionStageTransition(input: TransitionInput): TransitionResult { const patch: Record = {}; const existingState = parseIssueExecutionState(input.issue.executionState); const currentAssignee = assigneePrincipal(input.issue); @@ -560,3 +871,180 @@ export function applyIssueExecutionPolicyTransition(input: TransitionInput): Tra workflowControlledAssignment: true, }; } + +function applyMonitorTransition(input: TransitionInput, stagePatch: Record) { + const patch: Record = {}; + const previousPolicy = input.previousPolicy ?? normalizeIssueExecutionPolicy(input.issue.executionPolicy ?? null); + const existingState = parseIssueExecutionState(input.issue.executionState); + const currentMonitorState = derivePersistedMonitorState({ + issue: input.issue, + state: existingState, + policy: previousPolicy, + }); + const nextStatus = + typeof stagePatch.status === "string" + ? (stagePatch.status as string) + : input.requestedStatus ?? input.issue.status; + const { assigneeAgentId, assigneeUserId } = nextAssigneeIds({ + issue: input.issue, + requestedAssigneePatch: input.requestedAssigneePatch, + stagePatch, + }); + const stageState = + stagePatch.executionState !== undefined + ? parseIssueExecutionState(stagePatch.executionState) + : existingState; + const invalidReason = input.policy?.monitor + ? monitorClearReasonForIssue(nextStatus, assigneeAgentId, assigneeUserId) + : null; + + let targetMonitorState = currentMonitorState; + + if (input.policy?.monitor) { + if (invalidReason) { + if (input.monitorExplicitlyUpdated) { + throw unprocessable(MONITOR_INVALID_MESSAGE); + } + patch.executionPolicy = stripMonitorFromExecutionPolicy(input.policy); + patch.monitorNextCheckAt = null; + patch.monitorWakeRequestedAt = null; + targetMonitorState = buildClearedMonitorState({ + previous: currentMonitorState, + clearReason: invalidReason, + clearedAt: new Date(), + }); + } else { + const exhaustedReason = exhaustedMonitorClearReason({ + monitor: input.policy.monitor, + attemptCount: currentMonitorState?.attemptCount ?? 0, + now: new Date(), + }); + if (exhaustedReason) { + if (input.monitorExplicitlyUpdated) { + throw unprocessable(MONITOR_BOUNDS_EXHAUSTED_MESSAGE, { clearReason: exhaustedReason }); + } + patch.executionPolicy = stripMonitorFromExecutionPolicy(input.policy); + patch.monitorNextCheckAt = null; + patch.monitorWakeRequestedAt = null; + targetMonitorState = buildClearedMonitorState({ + previous: currentMonitorState, + clearReason: exhaustedReason, + clearedAt: new Date(), + }); + } else { + patch.monitorNextCheckAt = new Date(input.policy.monitor.nextCheckAt); + patch.monitorWakeRequestedAt = null; + patch.monitorNotes = input.policy.monitor.notes ?? null; + patch.monitorScheduledBy = input.policy.monitor.scheduledBy; + targetMonitorState = buildScheduledMonitorState(currentMonitorState, input.policy.monitor); + } + } + } else if (previousPolicy?.monitor) { + patch.monitorNextCheckAt = null; + patch.monitorWakeRequestedAt = null; + targetMonitorState = buildClearedMonitorState({ + previous: currentMonitorState, + clearReason: + input.monitorExplicitlyUpdated + ? "manual" + : monitorClearReasonForIssue(nextStatus, assigneeAgentId, assigneeUserId) ?? "manual", + clearedAt: new Date(), + }); + } + + if (stagePatch.executionState !== undefined || !monitorStatesEqual(currentMonitorState, targetMonitorState)) { + patch.executionState = executionStateWithMonitor(stageState, targetMonitorState); + } + + return patch; +} + +export function buildInitialIssueMonitorFields(input: { + policy: IssueExecutionPolicy | null; + status: string; + assigneeAgentId?: string | null; + assigneeUserId?: string | null; +}) { + if (!input.policy?.monitor) return {}; + if (!issueAllowsMonitor(input.status, input.assigneeAgentId ?? null, input.assigneeUserId ?? null)) { + throw unprocessable(MONITOR_INVALID_MESSAGE); + } + const exhaustedReason = exhaustedMonitorClearReason({ + monitor: input.policy.monitor, + attemptCount: 0, + now: new Date(), + }); + if (exhaustedReason) { + throw unprocessable(MONITOR_BOUNDS_EXHAUSTED_MESSAGE, { clearReason: exhaustedReason }); + } + + const monitorState = buildScheduledMonitorState(null, input.policy.monitor); + return { + monitorNextCheckAt: new Date(input.policy.monitor.nextCheckAt), + monitorWakeRequestedAt: null, + monitorNotes: input.policy.monitor.notes ?? null, + monitorScheduledBy: input.policy.monitor.scheduledBy, + executionState: executionStateWithMonitor(null, monitorState) as Record | null, + }; +} + +export function buildIssueMonitorTriggeredPatch(input: { + issue: IssueLike; + policy: IssueExecutionPolicy | null; + triggeredAt: Date; +}) { + const existingState = parseIssueExecutionState(input.issue.executionState); + const currentMonitorState = derivePersistedMonitorState({ + issue: input.issue, + state: existingState, + policy: input.policy, + }); + const nextMonitorState = buildTriggeredMonitorState({ + previous: currentMonitorState, + triggeredAt: input.triggeredAt, + }); + + return { + executionPolicy: stripMonitorFromExecutionPolicy(input.policy) as Record | null, + executionState: executionStateWithMonitor(existingState, nextMonitorState) as Record | null, + monitorNextCheckAt: null, + monitorWakeRequestedAt: null, + monitorLastTriggeredAt: input.triggeredAt, + monitorAttemptCount: nextMonitorState.attemptCount, + monitorNotes: nextMonitorState.notes, + monitorScheduledBy: nextMonitorState.scheduledBy, + }; +} + +export function buildIssueMonitorClearedPatch(input: { + issue: IssueLike; + policy: IssueExecutionPolicy | null; + clearReason: IssueExecutionMonitorClearReason; + clearedAt?: Date; +}) { + const existingState = parseIssueExecutionState(input.issue.executionState); + const currentMonitorState = derivePersistedMonitorState({ + issue: input.issue, + state: existingState, + policy: input.policy, + }); + const nextMonitorState = buildClearedMonitorState({ + previous: currentMonitorState, + clearReason: input.clearReason, + clearedAt: input.clearedAt ?? new Date(), + }); + + return { + executionPolicy: stripMonitorFromExecutionPolicy(input.policy) as Record | null, + executionState: executionStateWithMonitor(existingState, nextMonitorState) as Record | null, + monitorNextCheckAt: null, + monitorWakeRequestedAt: null, + }; +} + +export function applyIssueExecutionPolicyTransition(input: TransitionInput): TransitionResult { + const stageResult = applyIssueExecutionStageTransition(input); + const monitorPatch = applyMonitorTransition(input, stageResult.patch); + Object.assign(stageResult.patch, monitorPatch); + return stageResult; +} diff --git a/server/src/services/issues.ts b/server/src/services/issues.ts index 8990a730..8f49238d 100644 --- a/server/src/services/issues.ts +++ b/server/src/services/issues.ts @@ -43,6 +43,7 @@ import { parseProjectExecutionWorkspacePolicy, } from "./execution-workspace-policy.js"; import { mergeExecutionWorkspaceConfig } from "./execution-workspaces.js"; +import { buildInitialIssueMonitorFields, normalizeIssueExecutionPolicy } from "./issue-execution-policy.js"; import { instanceSettingsService } from "./instance-settings.js"; import { redactCurrentUserText } from "../log-redaction.js"; import { resolveIssueGoalId, resolveNextIssueGoalId } from "./issue-goal-fallback.js"; @@ -1421,6 +1422,12 @@ const issueListSelect = { assigneeAdapterOverrides: issues.assigneeAdapterOverrides, executionPolicy: sql`null`, executionState: sql`null`, + monitorNextCheckAt: issues.monitorNextCheckAt, + monitorWakeRequestedAt: issues.monitorWakeRequestedAt, + monitorLastTriggeredAt: issues.monitorLastTriggeredAt, + monitorAttemptCount: issues.monitorAttemptCount, + monitorNotes: issues.monitorNotes, + monitorScheduledBy: issues.monitorScheduledBy, executionWorkspaceId: issues.executionWorkspaceId, executionWorkspacePreference: issues.executionWorkspacePreference, executionWorkspaceSettings: sql`null`, @@ -2815,6 +2822,15 @@ export function issueService(db: Db) { if (values.status === "cancelled") { values.cancelledAt = new Date(); } + Object.assign( + values, + buildInitialIssueMonitorFields({ + policy: normalizeIssueExecutionPolicy(issueData.executionPolicy ?? null), + status: values.status ?? "backlog", + assigneeAgentId: values.assigneeAgentId ?? null, + assigneeUserId: values.assigneeUserId ?? null, + }), + ); const [issue] = await tx.insert(issues).values(values).returning(); if (inputLabelIds) { diff --git a/server/src/services/recovery/issue-graph-liveness.ts b/server/src/services/recovery/issue-graph-liveness.ts index 68c64183..734de446 100644 --- a/server/src/services/recovery/issue-graph-liveness.ts +++ b/server/src/services/recovery/issue-graph-liveness.ts @@ -22,7 +22,10 @@ export interface IssueLivenessIssueInput { assigneeUserId?: string | null; createdByAgentId?: string | null; createdByUserId?: string | null; + executionPolicy?: Record | null; executionState?: Record | null; + monitorNextCheckAt?: Date | string | null; + monitorAttemptCount?: number | null; } export interface IssueLivenessRelationInput { @@ -99,6 +102,7 @@ export interface IssueGraphLivenessInput { pendingInteractions?: IssueLivenessWaitingPathInput[]; pendingApprovals?: IssueLivenessWaitingPathInput[]; openRecoveryIssues?: IssueLivenessWaitingPathInput[]; + now?: Date | string; } const INVOKABLE_AGENT_STATUSES = new Set(["active", "idle", "running", "error"]); @@ -140,6 +144,45 @@ function hasWaitingPath( return waitingPaths.some((entry) => entry.companyId === companyId && entry.issueId === issueId); } +function readRecord(value: unknown): Record | null { + return value && typeof value === "object" && !Array.isArray(value) + ? value as Record + : null; +} + +function readPositiveInteger(value: unknown): number | null { + return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : null; +} + +function readDateMs(value: unknown): number | null { + if (!(typeof value === "string" || value instanceof Date)) return null; + const date = value instanceof Date ? value : new Date(value); + const time = date.getTime(); + return Number.isNaN(time) ? null : time; +} + +function monitorFromIssue(issue: IssueLivenessIssueInput) { + const policyMonitor = readRecord(readRecord(issue.executionPolicy)?.monitor); + const stateMonitor = readRecord(readRecord(issue.executionState)?.monitor); + return { policyMonitor, stateMonitor }; +} + +function hasScheduledMonitor(issue: IssueLivenessIssueInput, nowMs: number) { + const nextCheckAtMs = readDateMs(issue.monitorNextCheckAt); + if (nextCheckAtMs === null || nextCheckAtMs <= nowMs) return false; + + const { policyMonitor, stateMonitor } = monitorFromIssue(issue); + const timeoutAtMs = readDateMs(policyMonitor?.timeoutAt ?? stateMonitor?.timeoutAt); + if (timeoutAtMs !== null && timeoutAtMs <= nowMs) return false; + + const maxAttempts = readPositiveInteger(policyMonitor?.maxAttempts ?? stateMonitor?.maxAttempts); + const stateAttemptCount = readPositiveInteger(stateMonitor?.attemptCount) ?? 0; + const attemptCount = issue.monitorAttemptCount ?? stateAttemptCount; + if (maxAttempts !== null && attemptCount >= maxAttempts) return false; + + return true; +} + function readPrincipalAgentId(principal: unknown): string | null { if (!principal || typeof principal !== "object") return null; const value = principal as Record; @@ -308,6 +351,7 @@ function finding(input: { } export function classifyIssueGraphLiveness(input: IssueGraphLivenessInput): IssueLivenessFinding[] { + const nowMs = readDateMs(input.now ?? new Date()) ?? Date.now(); const issuesById = new Map(input.issues.map((issue) => [issue.id, issue])); const agentsById = new Map(input.agents.map((agent) => [agent.id, agent])); const blockersByBlockedIssueId = new Map(); @@ -351,6 +395,7 @@ export function classifyIssueGraphLiveness(input: IssueGraphLivenessInput): Issu function hasExplicitWaitingPath(issue: IssueLivenessIssueInput) { return Boolean(issue.assigneeUserId) || + hasScheduledMonitor(issue, nowMs) || hasActiveExecutionPath(issue.companyId, issue.id, activeRuns, queuedWakeRequests) || hasWaitingPath(issue.companyId, issue.id, pendingInteractions) || hasWaitingPath(issue.companyId, issue.id, pendingApprovals) || diff --git a/server/src/services/recovery/service.ts b/server/src/services/recovery/service.ts index 87192d7a..23f2922d 100644 --- a/server/src/services/recovery/service.ts +++ b/server/src/services/recovery/service.ts @@ -1836,7 +1836,10 @@ export function recoveryService(db: Db, deps: { enqueueWakeup: RecoveryWakeup }) assigneeUserId: issues.assigneeUserId, createdByAgentId: issues.createdByAgentId, createdByUserId: issues.createdByUserId, + executionPolicy: issues.executionPolicy, executionState: issues.executionState, + monitorNextCheckAt: issues.monitorNextCheckAt, + monitorAttemptCount: issues.monitorAttemptCount, }) .from(issues) .where( @@ -1966,6 +1969,7 @@ export function recoveryService(db: Db, deps: { enqueueWakeup: RecoveryWakeup }) pendingInteractions: interactionRows, pendingApprovals: approvalRows, openRecoveryIssues, + now: new Date(), }); } diff --git a/ui/src/api/issues.ts b/ui/src/api/issues.ts index 027222a0..acae3a71 100644 --- a/ui/src/api/issues.ts +++ b/ui/src/api/issues.ts @@ -126,6 +126,7 @@ export const issuesApi = { }>(`/issues/${id}/tree-control/state`), releaseTreeHold: (id: string, holdId: string, data: ReleaseIssueTreeHold) => api.post(`/issues/${id}/tree-holds/${holdId}/release`, data), + checkMonitorNow: (id: string) => api.post<{ ok: true }>(`/issues/${id}/monitor/check-now`, {}), remove: (id: string) => api.delete(`/issues/${id}`), checkout: (id: string, agentId: string) => api.post(`/issues/${id}/checkout`, { diff --git a/ui/src/components/IssueMonitorActivityCard.test.tsx b/ui/src/components/IssueMonitorActivityCard.test.tsx new file mode 100644 index 00000000..d00aa464 --- /dev/null +++ b/ui/src/components/IssueMonitorActivityCard.test.tsx @@ -0,0 +1,196 @@ +// @vitest-environment jsdom + +import { act } from "react"; +import { createRoot } from "react-dom/client"; +import type { Issue } from "@paperclipai/shared"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { IssueMonitorActivityCard } from "./IssueMonitorActivityCard"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; + +function createIssue(overrides: Partial = {}): Issue { + return { + id: "issue-1", + companyId: "company-1", + projectId: null, + projectWorkspaceId: null, + goalId: null, + parentId: null, + title: "Watch deploy", + description: null, + status: "in_progress", + priority: "medium", + assigneeAgentId: "agent-1", + assigneeUserId: null, + checkoutRunId: null, + executionRunId: null, + executionAgentNameKey: null, + executionLockedAt: null, + createdByAgentId: null, + createdByUserId: "local-board", + issueNumber: 1, + identifier: "PAP-1", + requestDepth: 0, + billingCode: null, + assigneeAdapterOverrides: null, + executionPolicy: { + mode: "normal", + commentRequired: true, + stages: [], + monitor: { + nextCheckAt: "2026-04-11T12:30:00.000Z", + notes: "Check deployment health", + scheduledBy: "board", + }, + }, + executionState: { + status: "idle", + currentStageId: null, + currentStageIndex: null, + currentStageType: null, + currentParticipant: null, + returnAssignee: null, + reviewRequest: null, + completedStageIds: [], + lastDecisionId: null, + lastDecisionOutcome: null, + monitor: { + status: "scheduled", + nextCheckAt: "2026-04-11T12:30:00.000Z", + lastTriggeredAt: null, + attemptCount: 0, + notes: "Check deployment health", + scheduledBy: "board", + clearedAt: null, + clearReason: null, + }, + }, + monitorNextCheckAt: new Date("2026-04-11T12:30:00.000Z"), + monitorLastTriggeredAt: null, + monitorAttemptCount: 0, + monitorNotes: "Check deployment health", + monitorScheduledBy: "board", + executionWorkspaceId: null, + executionWorkspacePreference: null, + executionWorkspaceSettings: null, + startedAt: null, + completedAt: null, + cancelledAt: null, + hiddenAt: null, + createdAt: new Date("2026-04-11T10:00:00.000Z"), + updatedAt: new Date("2026-04-11T10:00:00.000Z"), + ...overrides, + }; +} + +describe("IssueMonitorActivityCard", () => { + let container: HTMLDivElement; + + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date("2026-04-11T12:00:00.000Z")); + container = document.createElement("div"); + document.body.appendChild(container); + }); + + afterEach(() => { + vi.useRealTimers(); + container.remove(); + }); + + it("renders the scheduled monitor details and check-now action", () => { + const onCheckNow = vi.fn(); + const root = createRoot(container); + + act(() => { + root.render(); + }); + + expect(container.textContent).toContain("Monitor scheduled"); + expect(container.textContent).toContain("Next check"); + expect(container.textContent).toContain("in 30m"); + expect(container.textContent).toContain("Check deployment health"); + + const button = Array.from(container.querySelectorAll("button")).find((candidate) => + candidate.textContent?.includes("Check now"), + ); + expect(button).toBeTruthy(); + + act(() => { + button?.dispatchEvent(new MouseEvent("click", { bubbles: true })); + }); + + expect(onCheckNow).toHaveBeenCalledTimes(1); + + act(() => root.unmount()); + }); + + it("does not render external references from monitor metadata", () => { + const root = createRoot(container); + + act(() => { + root.render( + , + ); + }); + + expect(container.textContent).toContain("Deploy provider"); + expect(container.textContent).not.toContain("provider.example"); + expect(container.textContent).not.toContain("token=secret"); + + act(() => root.unmount()); + }); + + it("renders nothing when the issue has no scheduled monitor", () => { + const root = createRoot(container); + + act(() => { + root.render( + , + ); + }); + + expect(container.textContent).toBe(""); + + act(() => root.unmount()); + }); +}); diff --git a/ui/src/components/IssueMonitorActivityCard.tsx b/ui/src/components/IssueMonitorActivityCard.tsx new file mode 100644 index 00000000..bc1716fc --- /dev/null +++ b/ui/src/components/IssueMonitorActivityCard.tsx @@ -0,0 +1,71 @@ +import type { Issue } from "@paperclipai/shared"; +import { Button } from "@/components/ui/button"; +import { formatMonitorOffset } from "@/lib/issue-monitor"; +import { formatDateTime } from "@/lib/utils"; + +function resolveScheduledMonitor(issue: Issue) { + const nextCheckAt = + issue.monitorNextCheckAt?.toISOString() ?? + issue.executionPolicy?.monitor?.nextCheckAt ?? + issue.executionState?.monitor?.nextCheckAt ?? + null; + if (!nextCheckAt) return null; + + return { + nextCheckAt, + notes: issue.executionPolicy?.monitor?.notes ?? issue.monitorNotes ?? issue.executionState?.monitor?.notes ?? null, + attemptCount: issue.monitorAttemptCount ?? issue.executionState?.monitor?.attemptCount ?? 0, + serviceName: issue.executionPolicy?.monitor?.serviceName ?? issue.executionState?.monitor?.serviceName ?? null, + }; +} + +interface IssueMonitorActivityCardProps { + issue: Issue; + onCheckNow?: (() => void) | null; + checkingNow?: boolean; +} + +export function IssueMonitorActivityCard({ + issue, + onCheckNow = null, + checkingNow = false, +}: IssueMonitorActivityCardProps) { + const monitor = resolveScheduledMonitor(issue); + if (!monitor) return null; + + return ( +

+
+
+
Monitor scheduled
+
+ Next check {formatDateTime(monitor.nextCheckAt)} ({formatMonitorOffset(monitor.nextCheckAt)}) +
+ {monitor.notes ? ( +
{monitor.notes}
+ ) : null} + {monitor.serviceName ? ( +
+ {monitor.serviceName} +
+ ) : null} + {monitor.attemptCount > 0 ? ( +
Attempt {monitor.attemptCount}
+ ) : null} +
+ {onCheckNow ? ( + + ) : null} +
+
+ ); +} diff --git a/ui/src/components/IssueProperties.test.tsx b/ui/src/components/IssueProperties.test.tsx index bb381c67..927098b5 100644 --- a/ui/src/components/IssueProperties.test.tsx +++ b/ui/src/components/IssueProperties.test.tsx @@ -969,4 +969,84 @@ describe("IssueProperties", () => { act(() => root.unmount()); }); + + it("renders monitor controls and clears an existing monitor", async () => { + const onUpdate = vi.fn(); + const root = renderProperties(container, { + issue: createIssue({ + status: "in_progress", + assigneeAgentId: "agent-1", + executionPolicy: createExecutionPolicy({ + monitor: { + nextCheckAt: "2026-04-11T12:30:00.000Z", + notes: "Check deployment", + scheduledBy: "board", + }, + }), + executionState: createExecutionState({ + status: "idle", + currentStageId: null, + currentStageIndex: null, + currentStageType: null, + currentParticipant: null, + returnAssignee: null, + lastDecisionOutcome: null, + monitor: { + status: "scheduled", + nextCheckAt: "2026-04-11T12:30:00.000Z", + lastTriggeredAt: null, + attemptCount: 0, + notes: "Check deployment", + scheduledBy: "board", + clearedAt: null, + clearReason: null, + }, + }), + }), + childIssues: [], + onUpdate, + inline: true, + }); + await flush(); + + expect(container.textContent).toContain("Monitor"); + expect(container.textContent).toContain("Next check"); + expect(container.querySelector('input[type="datetime-local"]')).toBeNull(); + expect(container.querySelector('input[placeholder="What should the agent re-check?"]')).toBeNull(); + + const monitorTrigger = Array.from(container.querySelectorAll("button")) + .find((button) => button.textContent?.includes("Next check")); + expect(monitorTrigger).not.toBeUndefined(); + + await act(async () => { + monitorTrigger!.dispatchEvent(new MouseEvent("click", { bubbles: true })); + }); + await flush(); + + const inputs = Array.from(container.querySelectorAll("input")); + const datetimeInput = inputs.find((input) => input.getAttribute("type") === "datetime-local"); + const textInput = inputs.find((input) => input.getAttribute("placeholder") === "What should the agent re-check?"); + const clearButton = Array.from(container.querySelectorAll("button")) + .find((button) => button.textContent?.includes("Clear")); + + expect(datetimeInput).toBeTruthy(); + expect(textInput).toBeTruthy(); + expect(clearButton).toBeTruthy(); + expect(datetimeInput!.value).toBeTruthy(); + expect(textInput!.value).toBe("Check deployment"); + + act(() => { + clearButton!.dispatchEvent(new MouseEvent("click", { bubbles: true })); + }); + + expect(onUpdate).toHaveBeenCalledWith({ + executionPolicy: { + mode: "normal", + commentRequired: true, + stages: [], + }, + }); + + act(() => root.unmount()); + }); }); diff --git a/ui/src/components/IssueProperties.tsx b/ui/src/components/IssueProperties.tsx index c11631a8..da36be76 100644 --- a/ui/src/components/IssueProperties.tsx +++ b/ui/src/components/IssueProperties.tsx @@ -25,6 +25,7 @@ import { getRecentProjectIds, trackRecentProject } from "../lib/recent-projects" import { orderItemsBySelectedAndRecent } from "../lib/recent-selections"; import { formatAssigneeUserLabel } from "../lib/assignees"; import { buildExecutionPolicy, stageParticipantValues } from "../lib/issue-execution-policy"; +import { formatMonitorOffset } from "../lib/issue-monitor"; import { StatusIcon } from "./StatusIcon"; import { PriorityIcon } from "./PriorityIcon"; import { Identity } from "./Identity"; @@ -33,7 +34,7 @@ import { formatDate, cn, projectUrl } from "../lib/utils"; import { timeAgo } from "../lib/timeAgo"; import { Separator } from "@/components/ui/separator"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; -import { User, Hexagon, ArrowUpRight, Tag, Plus, GitBranch, FolderOpen, Check, ExternalLink } from "lucide-react"; +import { User, Hexagon, ArrowUpRight, Tag, Plus, GitBranch, FolderOpen, Check, ExternalLink, Clock } from "lucide-react"; import { AgentIcon } from "./AgentIconPicker"; function TruncatedCopyable({ value, icon: Icon }: { value: string; icon: React.ComponentType<{ className?: string }> }) { @@ -118,6 +119,14 @@ function issuesWorkspaceFilterHref(workspaceId: string) { return `/issues?${params.toString()}`; } +function toDateTimeLocalValue(value: string | null | undefined) { + if (!value) return ""; + const date = new Date(value); + if (Number.isNaN(date.getTime())) return ""; + const offsetMs = date.getTimezoneOffset() * 60_000; + return new Date(date.getTime() - offsetMs).toISOString().slice(0, 16); +} + interface IssuePropertiesProps { issue: Issue; childIssues?: Issue[]; @@ -219,10 +228,14 @@ export function IssueProperties({ const [reviewerSearch, setReviewerSearch] = useState(""); const [approversOpen, setApproversOpen] = useState(false); const [approverSearch, setApproverSearch] = useState(""); + const [monitorOpen, setMonitorOpen] = useState(false); const [labelsOpen, setLabelsOpen] = useState(false); const [labelSearch, setLabelSearch] = useState(""); const [newLabelName, setNewLabelName] = useState(""); const [newLabelColor, setNewLabelColor] = useState("#6366f1"); + const [monitorAtInput, setMonitorAtInput] = useState(() => toDateTimeLocalValue(issue.executionPolicy?.monitor?.nextCheckAt)); + const [monitorNotesInput, setMonitorNotesInput] = useState(issue.executionPolicy?.monitor?.notes ?? ""); + const [monitorServiceInput, setMonitorServiceInput] = useState(issue.executionPolicy?.monitor?.serviceName ?? ""); const { data: session } = useQuery({ queryKey: queryKeys.auth.session, @@ -459,6 +472,145 @@ export function IssueProperties({ } return `${stageLabel} pending${participantLabel ? ` with ${participantLabel}` : ""}`; })(); + useEffect(() => { + setMonitorAtInput(toDateTimeLocalValue(issue.executionPolicy?.monitor?.nextCheckAt)); + setMonitorNotesInput(issue.executionPolicy?.monitor?.notes ?? ""); + setMonitorServiceInput(issue.executionPolicy?.monitor?.serviceName ?? ""); + }, [ + issue.executionPolicy?.monitor?.nextCheckAt, + issue.executionPolicy?.monitor?.notes, + issue.executionPolicy?.monitor?.serviceName, + ]); + + const updateMonitor = (nextMonitor: Issue["executionPolicy"] extends infer T + ? T extends { monitor?: infer M | null } | null | undefined + ? M | null + : never + : never) => { + const basePolicy = buildExecutionPolicy({ + existingPolicy: issue.executionPolicy ?? null, + reviewerValues, + approverValues, + }); + if (!basePolicy && !nextMonitor) { + onUpdate({ executionPolicy: null }); + return; + } + onUpdate({ + executionPolicy: { + mode: basePolicy?.mode ?? issue.executionPolicy?.mode ?? "normal", + commentRequired: true, + stages: basePolicy?.stages ?? [], + ...(nextMonitor ? { monitor: nextMonitor } : {}), + }, + }); + }; + const saveMonitor = () => { + if (!monitorAtInput) return; + const nextCheckAt = new Date(monitorAtInput); + if (Number.isNaN(nextCheckAt.getTime())) return; + const serviceName = monitorServiceInput.trim() || null; + updateMonitor({ + nextCheckAt: nextCheckAt.toISOString(), + notes: monitorNotesInput.trim() || null, + scheduledBy: "board", + kind: serviceName ? "external_service" : null, + serviceName, + externalRef: null, + }); + setMonitorOpen(false); + }; + const clearMonitor = () => { + updateMonitor(null); + setMonitorOpen(false); + }; + const currentMonitorLabel = (() => { + if (issue.executionPolicy?.monitor?.nextCheckAt) { + return `Next check ${formatDate(new Date(issue.executionPolicy.monitor.nextCheckAt))}`; + } + if (issue.executionState?.monitor?.status === "cleared") { + return "Cleared"; + } + if (issue.monitorLastTriggeredAt) { + return `Last triggered ${timeAgo(issue.monitorLastTriggeredAt)}`; + } + return "Not scheduled"; + })(); + const monitorNextCheckAt = issue.executionPolicy?.monitor?.nextCheckAt ?? null; + const monitorTrigger = ( + + {monitorNextCheckAt ? ( + + ); + const monitorAttemptBadge = issue.monitorAttemptCount && issue.monitorAttemptCount > 0 ? ( + + Attempt {issue.monitorAttemptCount} + + ) : null; + const monitorContent = ( +
+
+ setMonitorAtInput(e.target.value)} + /> + setMonitorNotesInput(e.target.value)} + /> +
+
+ setMonitorServiceInput(e.target.value)} + /> +
+ + {issue.executionPolicy?.monitor ? ( + + ) : null} +
+
+
+ ); + const selectedIssueLabels = useMemo(() => { const selectedIds = issue.labelIds ?? []; if (selectedIds.length === 0) return issue.labels ?? []; @@ -1248,6 +1400,19 @@ export function IssueProperties({ )} + + {monitorContent} + + {issue.requestDepth > 0 && ( {issue.requestDepth} diff --git a/ui/src/lib/activity-format.test.ts b/ui/src/lib/activity-format.test.ts index 25f2d37a..605d3632 100644 --- a/ui/src/lib/activity-format.test.ts +++ b/ui/src/lib/activity-format.test.ts @@ -57,4 +57,12 @@ describe("activity formatting", () => { expect(formatActivityVerb("issue.reviewers_updated", details, { agentMap })).toBe("updated reviewers on"); expect(formatIssueActivityAction("issue.reviewers_updated", details, { agentMap })).toBe("updated reviewers"); }); + + it("formats monitor activity with direct verbs", () => { + expect(formatActivityVerb("issue.monitor_scheduled")).toBe("scheduled monitor on"); + expect(formatActivityVerb("issue.monitor_exhausted")).toBe("exhausted monitor on"); + expect(formatIssueActivityAction("issue.monitor_triggered")).toBe("triggered a monitor"); + expect(formatIssueActivityAction("issue.monitor_cleared")).toBe("cleared a monitor"); + expect(formatIssueActivityAction("issue.monitor_recovery_issue_created")).toBe("created a monitor recovery issue"); + }); }); diff --git a/ui/src/lib/activity-format.ts b/ui/src/lib/activity-format.ts index 98608e71..a1ed1f25 100644 --- a/ui/src/lib/activity-format.ts +++ b/ui/src/lib/activity-format.ts @@ -33,6 +33,14 @@ const ACTIVITY_ROW_VERBS: Record = { "issue.document_created": "created document for", "issue.document_updated": "updated document on", "issue.document_deleted": "deleted document from", + "issue.monitor_scheduled": "scheduled monitor on", + "issue.monitor_triggered": "triggered monitor for", + "issue.monitor_cleared": "cleared monitor on", + "issue.monitor_skipped": "skipped monitor for", + "issue.monitor_exhausted": "exhausted monitor on", + "issue.monitor_recovery_wake_queued": "queued monitor recovery for", + "issue.monitor_recovery_issue_created": "created monitor recovery for", + "issue.monitor_escalated_to_board": "escalated monitor for", "issue.commented": "commented on", "issue.deleted": "deleted", "agent.created": "created", @@ -75,6 +83,14 @@ const ISSUE_ACTIVITY_LABELS: Record = { "issue.document_created": "created a document", "issue.document_updated": "updated a document", "issue.document_deleted": "deleted a document", + "issue.monitor_scheduled": "scheduled a monitor", + "issue.monitor_triggered": "triggered a monitor", + "issue.monitor_cleared": "cleared a monitor", + "issue.monitor_skipped": "skipped a monitor", + "issue.monitor_exhausted": "exhausted a monitor", + "issue.monitor_recovery_wake_queued": "queued a monitor recovery wake", + "issue.monitor_recovery_issue_created": "created a monitor recovery issue", + "issue.monitor_escalated_to_board": "escalated a monitor to the board", "issue.deleted": "deleted the issue", "agent.created": "created an agent", "agent.updated": "updated the agent", @@ -296,6 +312,14 @@ export function formatIssueActivityAction( }); if (structuredChange) return structuredChange; + if (action.startsWith("issue.monitor_") && details) { + const serviceName = typeof details.serviceName === "string" && details.serviceName.trim() + ? details.serviceName.trim() + : null; + const base = ISSUE_ACTIVITY_LABELS[action] ?? action.replace(/[._]/g, " "); + return serviceName ? `${base} for ${serviceName}` : base; + } + if ( (action === "issue.document_created" || action === "issue.document_updated" || action === "issue.document_deleted") && details diff --git a/ui/src/lib/issue-execution-policy.ts b/ui/src/lib/issue-execution-policy.ts index 4f3bf3b5..b30302c8 100644 --- a/ui/src/lib/issue-execution-policy.ts +++ b/ui/src/lib/issue-execution-policy.ts @@ -62,6 +62,7 @@ export function buildExecutionPolicy(input: { }): IssueExecutionPolicy | null { const mode = input.existingPolicy?.mode ?? "normal"; const stages: IssueExecutionPolicy["stages"] = []; + const monitor = input.existingPolicy?.monitor ?? null; const existingReviewStage = input.existingPolicy?.stages.find((stage) => stage.type === "review"); const reviewParticipants = mergeParticipants(existingReviewStage?.participants, input.reviewerValues); @@ -85,11 +86,12 @@ export function buildExecutionPolicy(input: { }); } - if (stages.length === 0) return null; + if (stages.length === 0 && !monitor) return null; return { mode, commentRequired: true, stages, + ...(monitor ? { monitor } : {}), }; } diff --git a/ui/src/lib/issue-monitor.ts b/ui/src/lib/issue-monitor.ts new file mode 100644 index 00000000..3b0f8bde --- /dev/null +++ b/ui/src/lib/issue-monitor.ts @@ -0,0 +1,12 @@ +export function formatMonitorOffset(nextCheckAt: Date | string): string { + const deltaMs = new Date(nextCheckAt).getTime() - Date.now(); + const absMinutes = Math.round(Math.abs(deltaMs) / 60_000); + if (absMinutes <= 0) return "now"; + if (absMinutes < 60) return deltaMs >= 0 ? `in ${absMinutes}m` : `${absMinutes}m ago`; + + const absHours = Math.round(absMinutes / 60); + if (absHours < 24) return deltaMs >= 0 ? `in ${absHours}h` : `${absHours}h ago`; + + const absDays = Math.round(absHours / 24); + return deltaMs >= 0 ? `in ${absDays}d` : `${absDays}d ago`; +} diff --git a/ui/src/pages/IssueDetail.tsx b/ui/src/pages/IssueDetail.tsx index 9f71cf65..d0cda457 100644 --- a/ui/src/pages/IssueDetail.tsx +++ b/ui/src/pages/IssueDetail.tsx @@ -70,6 +70,7 @@ import { IssuesList } from "../components/IssuesList"; import { AgentIcon } from "../components/AgentIconPicker"; import { IssueReferenceActivitySummary } from "../components/IssueReferenceActivitySummary"; import { IssueRelatedWorkPanel } from "../components/IssueRelatedWorkPanel"; +import { IssueMonitorActivityCard } from "../components/IssueMonitorActivityCard"; import { IssueProperties } from "../components/IssueProperties"; import { IssueRunLedger } from "../components/IssueRunLedger"; import { IssueWorkspaceCard } from "../components/IssueWorkspaceCard"; @@ -881,6 +882,7 @@ const IssueDetailChatTab = memo(function IssueDetailChatTab({ }); type IssueDetailActivityTabProps = { + issue: Issue; issueId: string; companyId: string; issueStatus: Issue["status"]; @@ -891,10 +893,13 @@ type IssueDetailActivityTabProps = { userProfileMap: Map; pendingApprovalAction: { approvalId: string; action: "approve" | "reject" } | null; onApprovalAction: (approvalId: string, action: "approve" | "reject") => void; + onCheckMonitorNow: () => void; + checkingMonitorNow: boolean; handoffFocusSignal?: number; }; function IssueDetailActivityTab({ + issue, issueId, companyId, issueStatus, @@ -905,6 +910,8 @@ function IssueDetailActivityTab({ userProfileMap, pendingApprovalAction, onApprovalAction, + onCheckMonitorNow, + checkingMonitorNow, handoffFocusSignal = 0, }: IssueDetailActivityTabProps) { const { data: activity, isLoading: activityLoading } = useQuery({ @@ -1091,6 +1098,11 @@ function IssueDetailActivityTab({ )} + ); } @@ -1754,6 +1766,26 @@ export function IssueDetail() { updateChildIssue.mutate({ id, data }); }, [updateChildIssue]); + const checkIssueMonitorNow = useMutation({ + mutationFn: () => issuesApi.checkMonitorNow(issueId!), + onSuccess: () => { + invalidateIssueDetail(); + invalidateIssueRunState(); + invalidateIssueCollections(); + pushToast({ + title: "Monitor check queued", + tone: "success", + }); + }, + onError: (err) => { + pushToast({ + title: "Monitor check failed", + body: err instanceof Error ? err.message : "Unable to trigger the monitor right now", + tone: "error", + }); + }, + }); + const approvalDecision = useMutation({ mutationFn: async ({ approvalId, action }: { approvalId: string; action: "approve" | "reject" }) => { if (action === "approve") { @@ -3676,6 +3708,7 @@ export function IssueDetail() { {detailTab === "activity" ? ( { approvalDecision.mutate({ approvalId, action }); }} + onCheckMonitorNow={() => checkIssueMonitorNow.mutate()} + checkingMonitorNow={checkIssueMonitorNow.isPending} /> ) : null} diff --git a/ui/storybook/stories/monitor-surfaces.stories.tsx b/ui/storybook/stories/monitor-surfaces.stories.tsx new file mode 100644 index 00000000..a8af38f5 --- /dev/null +++ b/ui/storybook/stories/monitor-surfaces.stories.tsx @@ -0,0 +1,235 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; +import type { Issue } from "@paperclipai/shared"; +import { IssueMonitorActivityCard } from "@/components/IssueMonitorActivityCard"; +import { IssueProperties } from "@/components/IssueProperties"; +import { + storybookExecutionWorkspaces, + storybookIssueDocuments, + storybookIssues, +} from "../fixtures/paperclipData"; + +const issueDocumentSummaries = storybookIssueDocuments.map(({ body: _body, ...summary }) => summary); + +const baseIssue: Issue = { + ...storybookIssues[0]!, + planDocument: storybookIssueDocuments.find((document) => document.key === "plan") ?? null, + documentSummaries: issueDocumentSummaries, + currentExecutionWorkspace: storybookExecutionWorkspaces[0]!, +}; + +const inFiveMinutes = () => new Date(Date.now() + 5 * 60_000); +const inTwoHours = () => new Date(Date.now() + 2 * 60 * 60_000); + +const monitoredIssue: Issue = { + ...baseIssue, + monitorNextCheckAt: inFiveMinutes(), + monitorNotes: "Polling Greptile for completed analysis.", + monitorAttemptCount: 2, + executionPolicy: { + ...(baseIssue.executionPolicy ?? { mode: "normal", commentRequired: true, stages: [] }), + monitor: { + nextCheckAt: inFiveMinutes().toISOString(), + notes: "Polling Greptile for completed analysis.", + kind: "external_service", + scheduledBy: "assignee", + serviceName: "Greptile", + externalRef: "https://app.greptile.com/runs/abc123", + }, + }, +}; + +const longerWaitIssue: Issue = { + ...baseIssue, + monitorNextCheckAt: inTwoHours(), + monitorNotes: null, + monitorAttemptCount: 0, + executionPolicy: { + ...(baseIssue.executionPolicy ?? { mode: "normal", commentRequired: true, stages: [] }), + monitor: { + nextCheckAt: inTwoHours().toISOString(), + notes: null, + kind: null, + scheduledBy: "assignee", + serviceName: null, + externalRef: null, + }, + }, +}; + +const triggeredIssue: Issue = { + ...baseIssue, + monitorNextCheckAt: null, + monitorLastTriggeredAt: new Date(Date.now() - 3 * 60_000), + monitorAttemptCount: 3, + monitorNotes: "Greptile review was checked and needs another pass.", + executionPolicy: { + ...(baseIssue.executionPolicy ?? { mode: "normal", commentRequired: true, stages: [] }), + }, + executionState: { + ...(baseIssue.executionState ?? { + status: "pending", + currentStageId: null, + currentStageIndex: null, + currentStageType: null, + currentParticipant: null, + returnAssignee: null, + reviewRequest: null, + completedStageIds: [], + lastDecisionId: null, + lastDecisionOutcome: null, + }), + monitor: null, + }, +}; + +const clearedIssue: Issue = { + ...baseIssue, + monitorNextCheckAt: null, + monitorLastTriggeredAt: null, + monitorAttemptCount: 0, + monitorNotes: null, + executionPolicy: { + ...(baseIssue.executionPolicy ?? { mode: "normal", commentRequired: true, stages: [] }), + }, + executionState: { + ...(baseIssue.executionState ?? { + status: "pending", + currentStageId: null, + currentStageIndex: null, + currentStageType: null, + currentParticipant: null, + returnAssignee: null, + reviewRequest: null, + completedStageIds: [], + lastDecisionId: null, + lastDecisionOutcome: null, + }), + monitor: { + status: "cleared", + nextCheckAt: null, + lastTriggeredAt: null, + attemptCount: 0, + notes: null, + scheduledBy: "board", + kind: null, + serviceName: null, + externalRef: null, + timeoutAt: null, + maxAttempts: null, + recoveryPolicy: null, + clearedAt: new Date(Date.now() - 60_000).toISOString(), + clearReason: "manual", + }, + }, +}; + +function MonitorSurfaceStories() { + return ( +
+
+
+ IssueMonitorActivityCard - external service (Greptile) +
+ undefined} + checkingNow={false} + /> +
+ +
+
+ IssueMonitorActivityCard - generic 2h wait, no service metadata +
+ undefined} + checkingNow={false} + /> +
+ +
+
+ IssueMonitorActivityCard - returns null when no monitor is set +
+
+ (intentionally renders nothing for issues without a scheduled monitor) +
+ undefined} /> +
+ +
+
+
+ IssueProperties Monitor row - Not scheduled (default state) +
+
+ undefined} + inline + /> +
+
+ +
+
+ IssueProperties Monitor row - Scheduled (Greptile, in 5m) +
+
+ undefined} + inline + /> +
+
+ +
+
+ IssueProperties Monitor row - Triggered recently +
+
+ undefined} + inline + /> +
+
+ +
+
+ IssueProperties Monitor row - Cleared +
+
+ undefined} + inline + /> +
+
+
+
+ ); +} + +const meta = { + title: "Product/Issue Monitor surfaces", + component: MonitorSurfaceStories, + parameters: { + docs: { + description: { + component: + "Surfaces the IssueMonitorActivityCard and IssueProperties Monitor row in scheduled / not-scheduled / external-service variants for UX review.", + }, + }, + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const MonitorSurfaces: Story = {};