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 zcmdSBbx_sa+c!#Cn3Rg3NOyNjDBU3--K}(o7)Y1ICItzVZfT?&38lLPHeJ%^+UoCl z&Y3y${Bh1ZXWrx7Gq)mc_Fmt0UDv193Q&}nxQ=}f8x0NZx|F22G8)WMzhwkUANZ!PLFTpdOA!K5EZ`XQ{)g*iIRuq+-Wx%^X zzfOjl%f@X^&@{0uV_A-h^z%FP^o9hlh&?;A$2=D>80pGwZSb zv#!4h5UEz*KZvAw?T;MpEM4(u)GDFTD470{{WL(i*tA=##=Ud_J4E;RX|5Kh)z2I} zD%k?+A0H*cDjb&9M@zHq7dqtPIp7bW%Rcbam}`Igh2oGuIXOGtxMmdAzk3-C`g!LzsD~3sLO0W9rfI=IWhVj{~Lw zVTZ*o=DL&ZgDIaehtC>?hMh5tQ?7H@FBW*jFh1Kym?g8a^LhSeDXz7fdz)X8C7*P( zH>BrtetN+Bb7Qo0wQ8gGcymUKJ*huKhW%$asTi9{hw`z0 zZCWn(YS*m?0XaX>*EyztBzgXPOCo|FPgrLWzw;9CejMa zG@1z`<~4kqua;7mG|Ja?R6wc#0Vl`_euiuvDQpa_z4IQ=~r z=6~b9eOV--HFCIcto->wrM2B`6CuxwX;_+WQ#Q<<8-@-AHw*Aap9>xpPmFZ`X0E!9 zXP|3}pT9O;mbj?XsNL0R*c@0$LM0m)o-{LF;lMEYStaY&M%LbN?C|V9mhkUsOx@ap zNsp0YGqWtiiIMA>vWNz@Quqv-4{^S}`^ZWl%-gwm-8nZl)%eov>?fmk1Ba7lf|A|+i;&P4cDsEKzQb7%I~N$HU8T$}g#*{La7 zB(BIieZ@R&UN&5*$xvdbF9VaH+l-dI+&*U~Aw=9%-ci_+Jj6evgzHZCpXoW2+kNkq zdR^_dvp!bdyKCTNPP4=!TMRqZQQ&lUfTN1N0jC+(n%C#dv+@e*>d)L_c3fV6tTwi& zLJJPvQKjz;!fVKD^$&d_)h_q;3LX%5K5xxBJ3Ssq=_j5^%$(kBYLBAM-F!$}kXL(3YWr-9Z_T!p+(zG^ayY8~&d!GBiDHAG~FutB=z zP13v{-s7@v%d?gLhR>{fk`b$W^y_0-SDh^Vg(7R@MXcxiXzcfux$XLR$tq&++b?z@ z61NJqJ(?4q*R~UNBVX%|efuo>2D__Gmi=j-Hpj;1Ok?{B2d7)K15|w8D&uu9cHL;`J<$z2)YIzPPQNNC_c5`3UvQ2Nw`cc|M zcIUhxb1J|dtdi|F4c%Gno{O8bVY$(IezK#mn9OSQWWc`~p7s~1C@RDU+gsx8o0%LP zSXl}w@)JDuR;k?9!;h+2C(_yC$u=*GSX4dA=1s_#hg*{l@rkw%=UGI`&l2xHTncYZTsx;3&ROV;rFMy!X|EACTh;O^NOb{q23^Jx}K+o67vdp;$C|PVR7-NK8dYU6taHd%RTRDRT^Gj zSwlCxj8LjMq=*@9Y(;PLZL`_+XcB7;Rp-uVygzG|lNfvC7fzV7LWUhq z7*ZF$q>L4Bj`~(#(hRzsa>!QgP4%MVt;xQ-^OHYK=Ut@Chbw8&8#7vlGi2^q3Jpqj zGx|ZxF=J}xn83PuTx7OwoW$qE6Iy3CcX2^)yFh1oHg0&Q!_p6rQ{%Wu?aQ+o_r0|F zQmX`Tvs9Vv>G<_GN6v0%BX7%VbR;GZIWIBiDX=Sp=dx{|kI8|D>EV$HIhhz7C8TxmGB9 zxt0CW*QBJR`&H3>62W&xE}8j#>W=3>r&rHuBgC34{z<-hgm`52g3dpQZc}Ce1==Wv z#eYQk^J0^*n_g>eHR+78nXInx3i^(&)^6=}312ebLort|bJkGn)QsB-*8G zcQ=v6z41OtLSB_H^Lt*>%HlljN#r#;Aay#~9Z(=1!F!nN%X*>wsSC6+8VIXJP zZD~^5ks^~!v(XX@5)UM+vOA32?W#Dmu6HN&^|uzaPxi;2C(z@+y-Bj}cC#Sg#0VR37MWWZ<(4&5r`lLnm(`oB z`__vFs{1;-iMpv;Rz1>jRX?+z=G&$eY?i)$-3xQ2N)AyJD&QU9b z2U7y?m_&mSg@;*bq?zud=U946_%|Bf_zIBAis|j{kCczjs_#{Nnnw!9l*_SdADdz+WnvO} z9paPU%OdzAez!%CU%k!n!PPt_q(m0I`vJFzAC~H6%yU7C*;^L`(=M>rIIYI)nQPpQ z8F4dVxNc?pC4Jh|)XesGdcv^L%_n?@(XAc^2V;B=EnERgk zgr@R7p{9)Qqm78b!}yF$5^#$YU3cgel)!b~&&sR>9BXMZxXLiJ_BHoe`My?800i`zVp`Oj;H}fuKF5 zeG{P|YnXab9zRPCBka;^%bxojP`*0`k~oiEso>fE_!511s?qcCyG>qOMwk3*?ioqB zgd3r?xFKow+|LvT!oAg@C5~^9m%t;G)>OcxdIlvoVBb-1KUqTGGf zZZEVqs3NY}Svr&Kwuq=_X8oY1k|AwUDfOi%De4RV22MHAG~-n6ozvmxht_UQLD{G! zov&k*W_n45xFvMy`n{$t4*$9I%POZkKT`S!0Fm%J{`&sa&gh20l?PLvJ0XIc4hsd# znHXI9^=M1g?`pA3ml!{{%(q94mD!ACDUj?~Wn`=8Zo6+SCGoqw%Y0tF^_Jl7+jAf+ znpcayO;owia^t*4>SfxI{ldLQv&nXicihc}WhT*Zxg(lh^L_(GKq!yRgIP{zqz1jf zJF%w47ZWYWR(MXM<S;8x2Tj z9s{K*PT^a$8X2BV^f_G4N?~|DJl1`tnXS$^>OT=(9$C|MHiizkaSlJYf+ac0Vf?Ed zbe%b3Jbui8q|N73c}S}(Dp~TUYvog;=WCNSRbwW1W)?h;RlK?J4vrA@kz+3Ee@M0- zj|{_qX#oVStBv~P7tjOV%jon8)gDYu*%OTLG=w$<-3_Q!EfLr#^mNR*pq796HDA7l zq%ORjj&O?S(r=I4UvUb*(u8<(~lC0~*o2^*q!-1*s%dv-|ku2#v# z$k{ZFwS~{HitVufIY4eUsLI3lC{4F8`zlaYKUtHOho&`i1En&0?*; zk_B1B+Hj$fy79X%>`fq3jDNL{h_)yu9X|TVU%2LfzwrN&7}8XFh9@c}21WUvTHyix z=$qHC9ajdIl}vRC4V$4!AChD~4!R9Px(p+R{wY%o-Qz&~m_KX!?rYN{v}9qQv*m#- z7ze+8`b<{4?e-?|mlJp+*i61W&DVu5ECV!jLXp$?HiO4O0A%;!+a)8&)Wv^5aXc%x zJ@a1%emha=lX`s)!oN{%l(*fTEXaRKulh+5`ik9LOW)Fff^ZVAgK1pU7ww8|5}%VT_(DZ@ zjdF%`mm!c%nHYxDGV6nlzK@q0;Vn9x{NBC`^Xt&?-ejQ z$k!@e0VPEA+@SHTTj<|4!JRjxLW>&cN#ZxVB-2lBw~Oixmp&j0OK49){E<8d843PEE{QK%sA&j39BY@95mUcTw`nl>2IKbI_fe4+MB5LW!}Eu3NJRrAhDOwO5yJ zhTh{0JTt9*sKdUBdFjUP`UJntXbI{^MNwTpB?9{D8TLT%2;Rf3wzXR_`~{Nm>DidO zaa%%;eadk5)?oC)4BBqIDJ%fe&gWymQw7@Gr$vdfKEJ-F-Xsz7+^9KV_*)ou?+{s! zrYao#<3=l;Rsq~<<|!}17`?-wvAL3+ACC@ti@4Kn^i$o`1U_oba--JJU5;~6!Y@V8gpIVX+^jb_xB;s%5g!pFWE&)Lh1>h&_xqqBnBVpb(kqQ-J&CC| zgSZWw-hpnz(glEc%IjbP9=KkWb4V;OrnE)bON0rV2wIiQC~5`g<$e=Dq{c=^%-UaH zz_jWWI%de2+g}?imIONGwKfWI!J2*>5ehQuz%9GfL(yv z6#tC|2Y&Jh?w?N_aUM{ZB9az70ur6);kHt-&5thx1VdFW8>8oxextFJ13ry!uY*cb za9#zRWuOoUJ5cD~DG}#VO4ED9CV;cwn+0To6rxq@c?fJTU$;hjn940rtF+@}>+DRUKR|K@-RiG`6cg~2iW^>q6V6D%?hczis*%alu29d> z=qoU2gvyJEfm*i+tbcEiz-jfwZrJKIS8?X3Cu~bl8pd~}k_C8q5ofAlYZAJ6E{M!! zuBR&Ot=$RS12dj&F(DSFy~#-$J&vvj9=lnrE4UhYOZw&JeSWTcKjj6g6YzKfNe>L1w*qR>lQ6`cJ zT#P>~=$3wbQJWhVnX2`YGr}zZW~c;P>#43=hSJ9;|KC`I}hYrwzK*~)(mt`J?}$GBODdPy@|TBBl=TXDv;J|f%m6tVo!rUTTGZ7 zJW*`cE8w>MJqtv=xGZ)N_O>fq)i^Fk60l^7 z)!K zr3{P^tH+Ws`rY@D%O|V`M5(J2E>ov4k70zEA9O`g_kx2U1Q5Sh@8%8hmlzkID;9!W z0080mUM8r~oR))M9hY$lT|xYU3B)Z3isE>!mxrG%d_i*xoQ8p@O{qqE>}kLR@S=Cw zO@j};^hI@`v9y;2mqO!$J<>c>Jb_u0whN%$Pi z0g`6s1_CB5x19j}oV^~p?WSe{Q*?m@Gw>W&X6D9Z5$@C5J4Wo3|-o&i;ddbQOxoQOVH{nIefs+!G)t1w5B z{m}Corzf|aE>6dDT4~{cVZvxX2jz5fEs9Q! z&U9jXYdiXa^pvbA^6KPa3uQ5x_UE72|vy*AY z7K~=fB|wwIE0#(CV8N!gu2bdgmW0xJ;Jd$?^*%^51sfuim{(Lkb8-ycMT*ZEf=8}n zqSlK)BB8w&nhFE15*HLnSEvf+Qg4dcOX#E3P+ZVF__Aavs2Gc>Q8ik84ND1^fnF_5 z{K^wEadr*pW?<`7SPW2=UoaAn4|4vH5Jju5&m&T=iFVG2T!e09tbf}cfYsmLanjlR z2+jNVCh6lkcw8ZOSsT{$04`DysgzKt;TB5DrGLCm#1;Ot&0h8LXRLmIik(rbe86>q z50{9OI#?)$`tN;Ibmb7Fbp;6}?A{_uR50ryK2mhG25uBV_W094e=CJX|HEP??XN5q z?f?pGQ-gLTn}6hLxI-IY&@57v$Pl-v=qPyq2708`nT!h?mFd0MK3EU6})DP`@53;rV|4&@X-xbXLtlX`bnZ z9A=$mn&|R^0;6gIR#oV8epYMxE%_0#;EU-;FtB8=#75ISE6dTbgziQxv(8KAaao0H zO|iz^NqU_AXe2~9)sytPnSj;18hb&rJ(f^sRGZOyCkNfU2NQs6NHHI7RNuK~}i)}z^<5?f5bVG8X5OcK+>$B3bx4Co43gxyJ9CmCaF+ZL}ZMzdp@n(Ze7yKdB*8eY=( z0Dqy^CI_EZ`MWuwVov}=YZI@cXfI&nPY+^#2CTMk?FSW&>z?AiLd%ZEj_pA3XE4m` zk+wYWdrY|lW~ZG{S3s8(UFLfUljq<3F1%JVB%rm0Ho;uIiSf8j`0RJ1eX~5B{~*PL zZ8zl{>+OY~z-vMX*;`bp3ZZm5W0~3SA1M-*X!qH*1QSwluN10ELn&H3$)aYB^kcDW zQ@z9ZR50qjq<2|VE}ojnm-m?o{ggXJ=a1!X?Rfo13y{{Xap!C{=;)(z#k(W5xLBb` zQ34QI3nv;hM7k8f!A05?J-S+*YFN%5D+KK+7(CSX8J>nDi85X9uX1^got${}gF#J? z-|~0E#E1H5vt$b@`3jY6pO%W9IWiY2;}oGVNN7$=+TB2UcXGmisJ=pGmulj`Rp0Wa^_}*a((?2k6z7dL(h(+W;eNebV~(1T~XTTyDjdIe?KtA-H>6f z5q}le8LVk-CTg@sgFnU`Fg}7%fTV%4Ms}POH=JYpHGkq^j7WHxMQ`<$`QGY!8nAJdA4+gH7lzHYq zg!-I}^^m_FT2;oy-@u*{Vw+Y%4=v&owl;ax_ygbQ`X z^7&8kryRj~{_rf5@?4RWyRPE_(bI_W^vtqs+85kpSXVf8M2c#-3>8|pJ z@f}~58(Df>W$_M=GRQ{5OAAsY_D=%aw>|9Q6e6j9pnM?4%S_L=1Cd8c8Va72oPRZJJ{I7f@P#Jfv zz9}6zUiInzSmq0NnClH{XD5dN&WlnFP=w@&7_<}UkpQW`fJCs^8ue*IX~XIGbE=@n zzG|*kv^|qjOq=vJN35(z+C5E}R1nlq8Tp9St)#zfyyT?aB-ND3>{zTrR zblGbzguClwv|!c~2A`!QmtQZ=BBKsxwDE9~%K0{i3+JFY7+eu_s3o$u0OEl7{A{8M zz!O8=j+RyUDorCUEr_y@i8=Ai9_zX-M8ot{HYxQ_UzCa{27?7i&&Sfv_q02J6rSZ( zSpr?v->!p_wuLcA{)SCc{!!c%85y5_%r}99pnls|7q8bIt|rtW#M6%M4IPnZi-%LOeEObMH;zlW?sm$e?ty)S61KWWJMTGC3#DMQF$39y>VwT<{GT{Mr)3~K%k5FrCz0z6` zGJwFb9{{X&*_cdXeB5H1@V27J_Uvet`F3($?Zs()G>f0u)bh6q0#E{mdLsMf#Nf-u z4A^~_ZV(sz860GQoa+v!rPJmI|gjq~6^OTA&ZO-|b*`GWjvFY(0t zc5kKh%=vq3{O4th-M#dqiJZkT%AR>en@jh$jnX3SMcG&3CVXLt7(TfbK>~^&>FOx= z&x?K~Qhs|rStTKm+pvrYxtR6NW=Vrvb*{Z+#s)8qr&Q%Q4=HK(Wfv#T8$Leoom*`W z48!L~uotN#{=B^gN+2Yl{PZ(A*m{n7KkCEY&ldjIS zDX(%w0L?$*^1~x#CzYc;Z)tnJY^ zeZYm^O|bpY3k&f_L6sboieoXDKA^rxJ=(f+N#$ewzX+Qo1^FnXrKWJF%t>~33j3sD zAN|e1i5EfJy12&t;KIB8fHS?v>i=URPBh2+vO0tp`m+=^kZqvq0RqUCh~cp4|MYug z0dg<#Q)*10dHQ~Q{rWXNlQxgq7gcp@+~3>&v?$RgHC287%1}{XwcGZ5;hzU+SOcOT zpc-btYdy)p0&Pc2KJT@WS@E4a1Bdai+;xF%95Qy%nNG849x^@aNE-E=yLNrh4#pBM_Oz-d?MCF(Vp?PYa2fVOUA1Bied95DxVP{lMQWGw(A5QOW-%Nhs7X zxHLfWcH5~_1UfJ~uDBvUFD@lWgxn*@v!e~c_3}C0d%CKZ0Sf}<$K#BQBUoLt`?pNl zFj?dixWH-gP<1b}ors4i0Ke}|5w3$M&TA?eGBw#rOiwTYp<|ampPHVKddV-gy8d6Kp+hgenGhFps|dM{nFR>+k!?ol%v_W*|qiU)g|!D(~_l5 z5(M1XQp@25fuH)Ul@5XKUkV@0d2Mv-0L@0!%vV}5Dp_s2(VS}PBdUW zzBrat6WaYZz}ke-9nT4I9U`e#Vi^@3kJG&&m?vk0DhhH4w&rU)UF^NPzxv1_cSc;O zQqL}U^$ycGD)(j5sl0u-)k-48n(5P*_88KPAwWF*#2hp0MXHvFL8Djzg~@)}=3u_< z5VDBBhVrK%DWeFC2cy|#%BvNp^pN@65*U9}4YxvhHqVw)d?eBHYmewI?Gu+Tb;gF# z&B2#!m9II8JUM|JMb0qw+YNNsfsBX_d!6~Nn2+niFoKkCk#?_cN@+6;$yGNAa|t&B zgHO4gMeQYNvYXm0Ri_#^@uWAAxA!_6gxM0j!)8+Q8v z1m$L}8(K+xy7(ePCdx3DO4?=^aI>h*ExA(iuS@gPx$RJOKG~-l;q#$NPy?Z~WZCp% z3OM=$c-9P~JFjjE#Q`YWP$>2*LA#%Bp2@3U?+Z%~inMZs$Fm|-1DAYl$03vK7}L>{ zEGUy$IKWIf3;wk7x3r?Y4*GmWvLfxLat@E==VvyPv59}M1q#Nj;t1?!eQ6>^b)&=Q zu$7XE$t`x6x4*ET`aOJfZD!a5fSGEKWZ6dbPUztf1df^T{ z!!dzKm0s;j(^nA5(-I^_Z4Eud@q0ldTYki8TU(DZm!g_`4K%x63K1{?hCeb9vDxR1k^e}f>`rB-CE6qpu6uM5)1 zCZq-&RYoZr5EBviLo2?E)Ef3aTviIrOq2B8b;98+jG0&kF+SK8<~k}MLVFhsbgqxb z7U=t(kenH_DPkbCvb?w$d?Q-9)5(O5S|_ivK)Iv^1cBvVse6GxWc?j@Wur#=3@MD+ zX=6eT`wQLoEOB{Eo_UxpVBK^}=Brq!{Ovhi{}S2Fz)mj{ZaPtu_rb!Thha0;?#hzw zWBtjLM*o}K6NzE^+|%82POsuMU+tS^xJHc)=@dKBXh&rr?h%=TR2qcm+krXLI2OVN?U0UB?|rB3H*;x{opW) zZt(4OqIfPFX`6roBSRdCx}MetID9k>cKX5)(#_u?b5da~()Rxe#@7r@{3{=2?w2E~ zj#u!-)4`7_U;$k%t*oif13K<)X5B8mPBkCXZ#2YOh00yp7&;KlF1wTvIGS0J7o7{f z3fqp}8LB_AP>U}ASZBFV1G}#9x~xJ~V?gpCW_R7Ne9WqW`95A;w^udIg5gD@jzulQ zPA94s$6FYmW!ciMwAU77u~k7=iI0`-KzL95wnR6b=Vj>|M1GH1m9}SL-k0Y?(r!Kh z2SVHtuZ%WkG^*%nha+Egv?KP5*h+6b_>UH#nI~F4*07`N zes;J-tNkob?uj+msUSCvO+$F%mf;LxKci%^&b!*+WEjK_5$sMDJ??Q1`i}ex#03?! z0SCYOUUf_7amAMYK{QP~p)-Q{I!~r$;$Zd zv9D)aIO)FQ<@WhWh%rQ%dUXfIrz{VHDk&0=P1gD3djTdRK@KROQT8ZNU%`F=9Cljw zQZQQBNF^2_qA29}OEJewU$IL7D+uGm*y;EpLL)O_(`d+|@s0HpsqfnJ*1}xf0p0pT zkL7YQzONv@^W=*14zlOKpzU{5ykqj5#Upj0QgwKBd)#s61pDO(w#-*8-|21^Q#k~G zXJfEr0EII6KdQbiU1Rhni>m^5^=#o3NgIR-nkM=5l0A;P`pCpuFh{OC{NOO+wCI0* z@9TmX2q%~GfVKz*pa_WVIfzR#pqSI#aA@cnNAJmN9t*>huRiCM+k(wPwXu||L zdRC}reRzMm#^V5BL9{kLx2-nci5}+k)z8t7WZXss9(FjOt!x8yWG$62-GyO@GtVPD zQ^BJ8R`YHpVao|d)I#MrH)zH&tLUl}V*HMXDVlvtka`)y7DjQ}b%00ia~i{9M)6$} z>u2X4k0*MIxHHmkFNhx~7#6FJrtur3A(vz zmw>|vp|?tDV=vuc6MD^UZ$)jABHgh^;y0g{e#PVuaj(gFQ2h6+X?#6<{4q#wQy2_n z*-lgrzIJPTK2{du!VqF~v+FZ07bR=lg~dVN3blj*)5L+IzmWF^B}u3g5vQf`0hBf7 z=*8>zeiK@u9MuD8%AKT4?_*)wm{mxB1zC>;{fB)s0@WvMZwyLFfedQmDvZ<84M2{! zge&5k{g3iboVYHKY?5_>o^-`9hW;kI*Ny7#&S6@r6pXbJ@SOXfLW^G(dNExO06k~( z<6R!puDgaS(7U|>ZvgbGy8Z&XE#Mgxn?!aXuJuHmfIO32z5zZz88{pyUb{GM0Y|l7 z)GI)eA=0E!NnUrl*?2RPIsqj3Oa_7FQHKcF*bOvIN@;Uoq9Z%KND40uXJ|B_i1b4V z%7H{fNKCNpussziz+P+_Wx=@&D4_c|B-+GcZMB|pXqja4F&s0IjAJ1+KxNLUc6Ln3 zS54c(UykMs5hSu0U^G9A35iYoZlApskM|hBX$IkGtN{RyDcIt-m%&)D({;D`7!1Mh z-Z5cE@xJ64IKQGfxd|m>Y8^an_JU4i!d%ar!8S0Tx#6xhQ6r{q_ zX5yZPcC1`}l3W!y&)i z4{F8{$U@yTgN2_MoxrmP=(;=4Gu7K1R|aP}dJybc@a*PoZfwA*krqeU{h1H(iW7M{ z{0_^Mt%nddyU0uba}q6>2fVXVGO?dInn^BEPh_?4R3RVeqA>L*^OcvN#HBg%y6>9j zi)8{WnN9T?_?-WSbP$nX`Yj*?v}0z?qSr4T*EqiA?dRx7H#t*h6r{?70P~MOvEgn(^s%&JA?OrJk?W@@t6&uZfn89 zqHA=FYZbZV_v-MGGDQfXCO1KA9@@z}pe#QMjU*!P%JsTOc%Q0jH+J5r|LODYzHhD% zDcuRs2#A5WPN)1m`PE0RjXwP0>tjnn_uWtJh3I5htkpW@#Sx5m?c`nJFV+Y`*&guw;h@R_>2NFo;xQ}L*^f3{v=Mt#mNZ!m~6G9iB40! z(#`{_M=Ab>nj(YO&j1RoXtV`@=Yl$1`Vc6 zAmo&i(NR#OYn573p|N)Z!F=}jQ?Q1_9l&~k1fjh7aSo!)5a}uL!(GAvZTAkh?Ks3h zw4rVQ_N&XjL!b#j)u?QkmioY{EQs6;+yZ3`qE@ZIT~i=(ii%@_G7fP9Cgt=tm>Q>u9p2F}aWsva7neB-&OU zv-RH0Aqu#iQ>K&^m-2Rqe8&3*~Lr6=w&t1)SX^9y>A^_ompuGeF50gcEjmi_H7d zVpbEe$>Zyi9c01Y&v3S9J9LBo2lb`!o+jd7^iG7TlD-}=t8@g}o8?-d(=d3J&0*hw zws85=ysiVaBOFkq*#|5#aLpn)+N5$VkoW%kpvFXUbRL*NUPrsE%m{ms0U_OF@%Q+r zBnvDch#mye=b$L@zL-WscZCx`SUUY1poSk>-E0i>U-a8U-qma+U$5-$s0Zf~Ak zA`~`R)4wp(!`qlH`p`Pl0ZT2)Prz|V&gYY!pjv}t^Mc44a0Q4;?2RB1)4}u4^Z^|p zpaYVpb9L$FN7|5RYieFaLMZWZOAp8~3zcY9ux(*~b%hE4dz;q+U(%r5C@7=aL60-K36L~UUUNo}vdGnP-}F*rgUJghmGBqu;+3BXL=t$+|A5F?Mh z6;#N0r@$PQK!FXL2a^ztqhKQLb%@+78~yu{;fLpSoz{=v{>_?jZO*i(4I1TZSInL- zg-PYkt5pe|K!gXCoq*_F>Sg49rxHl1TjvQ{&>AY-b~67xw(|$zxk4Gl5fB<84fWY< z9=;q|-5^ge6Y|(EIZ^&2gW}*^F+DG;GJse5Oicf`K5;gguhv6~q4>|x5EpMy=oR~a zan^QLYZez01+Y8+=yc-ZXCG%NCBy3juhA-}gTnJsAJD3KZ?Mx7r_{ zgg}uCZ#CgT^qO9$l4GFS`_vs0u%8!D8L&!Q*cPB=IRSJB&A*3N)58EN$o|)N(zmI{ z;3PndV;7E){tGzWz>k=Cc`>P$Ls|9hZ@FMSRuzXJ%(yeQL= zu}^gGo-|HeGCV=A-gKxh(BU{NNoL0Fvs{`AAkR^T_k!m3@0p(`-u+FO4l_fg+Ge-_ z_}F<&+A6?g&+CaWK|mrsfOb?(!GiZnj(aAi&RFX#GVyRVXBCy-8iAulN01k6-44Ot z2m2LN@bsOf-hTsXI|UCDJ+4XSouD)F?gW4thl|rNUx!~I))Sb(yic#UdLsd`s5M)| z9iWo}W(G2Fa2xu-zsCEYeIAr{jBDOe0UsFzX_9mM)LH3XXkMEK(Aozry7hb30kuBO zRu2h<#svcu)uUc^z3U4ipnoEPvhn8NNl#F%4gyMEEHdrB_n#O3m{x@W=g{oF zVe%SdFQV)8w2zA2rcwL=@56C0M{{8J5aw35SnA(n_w&N!@~C6!ZQ-axLN?_%x2ffc z$+DM2iMZ1X7c>8(1>|`>dk$GFn5|3IokL+DZmZ>J*nx(QA0n8au=you6=HZvOLL z;1r3^S;Bt~{5OUCuQ~8f%7*y@%KcyAMQG5XKKOr9`R9B(7=Mmdk&*rX>Z*bNT`xl* z%_A_GZ4D#2ckiC-IZY(YQhIC(xB~%-QJq36g6u-KVe<{>#vy4>g5v(&cEHLAvkvCm zmhiQ;C##z0SCEs)`7{53o&$U9F~&cKi>7GU#D*Y=D0GZ0hN5;DABWxtrZFJdXrrId zMsjs)M(y7`$v=gPfjr$$n8CwQD*!1WdFHYdY_-V zye*UYC&{0`-ECFml*cEh^9$+`C%x}5HoRPy3ua=?qCLbKY5qQ9?cf{DL z09#ta!2pm(KI_%hB9I`VKo?cuZiEmcKbTSAvPgqQf9l2yFb$^4m9a0F&Y3Je2?_o9Fc zR6551EleV#>x66+0Lo17#+N`tkD<@_A8$cGN1gora|=S7FnrYiXSX1zKyx?uha*)* zEMU(O0VaV2!mnPwqAuBy2iu^oPgAm+#A=jTBbE^W@L=Q~zzDOBCwm+ir`7^lG{CXn z9PdV448x%FJ@AXyg(LOzk`p0O7)jSkEJbkLP_j~jZsH=qb9FmX7cGIB8N$8iiKLavzq(4E;oJBiR0 zrslrk2N--HOv006J_k>$6?WLKWvlYty%~L@TG{YqltJ&Yji^8N6Fda`p_?_mRI}lP zH=G>|re|SS&)Ovn zt5-KkNNNpC52h3__{2Krhsf`#m;vt^ZEsMv-z8SDG&ouGGPOD39bLDt1Krb;8P zsUf1~z8#!WkSD>tA+g^T^lE!Z*CS-kK6I`djkwkUt1Hz8>HvD7Xk>ZgBjCjvBTz=8 zJKZV5K2VYYQ?JE}&|7DTfBAruTf1mGCz&)I!To#^TpEN>&W-B}#BVK}A-fO?_D{U2 z**Bz*U7`-?Ie_j5x(m+$EX$_NDJIv8Z5kokhdJ&sukD(|q&PcY)W3F@#TV^~wA*^c zlExYAONtxgNG37&sBiR)M_ugCFCRz@a695;nCsiZQ*?Xs(d&Ft2+p@%J z$QBGc2D-+BwJ{fnA1;bNin|+}fjSJ7b(7(#0Qp?udHBr{%lAoee8uvuv4ey9o16t% zy)w85BdiVuIz>j5e%=wj9u2YS%txmDuA2{;QPzySq`vdB`@$Ow^H?|W9ypiFFm-Wl- zR(p?%u2G%(8N+1?vj7%&{ZrTZzz8q?1ay}QS(uyaYj^wAuth}!X_m!sVS8Z=eNf~$ z(t!`jn{(f35c0YjVJ!+`c4;q7xtE9RqhE{&kE~5x4BGS<8R;2x9a(U-it{NHem?%S z&*nFai_G~a9kb4uke$nmlvx&W_cqw*MNgh%Om5)Y`y4G(uPmX5&d%@s*YaacGd?Q= zJu(&W6LG7G6`4=`!zJ8iWfcN z8ev^e#R_X}yL)8H-i+ycPj+ev=e#b zNqN584P)#wG;Z{S;9|_hyuBATocaIyT}9eFuDKtasuBCl3a6+RfY;zZ>hh}ib8o{N z&Ix?pwa#Q-4LDnHLtIs#JUX;`(kSuT~VAOgq_Tta24G&Pa zHn@#ze^$-_AYgwI50L`8%Kex8YWKfrP*C1@0rxhfwf(ub0S1)OPmm;1CEE&1z1mHD zHU8Y&kXI6waQ{F!$Y-UNCO04P>=IkSm`MyM&_<72p-CtO4FS1?JVgF5z3WWZBUF$* za0YVEo|D7#X-`gknKlfbbJ(3&pC8TRo z(^(1Md$jnUQis#tMYqL}$BKLQjaLRYFL&|3w!W&MTO8&Ht5J(08ZBTDkZV5A#1Tn& z?YUHaiHVP#kUMQ!wedC%1{h*L*w)8>JB4_sEPjh*o%p9!?C8Sbsf5NpJMM+!cj{ zBa(fBi{?Rq`Z2(BHTm!;_`zwFZV?{b1I1SiYULHNHN+1A&Kw#R(C8jNkseMxPGS-mV&y) z|LJN6eZ38AHnePr8? z!ST&K-TmgL{Oo)OoJnhm-RRhyC^NTmrCLH$pCX6XYOY8Arf<{;wyqWbP)uza$B6zM_5QgU%V zKIf;;ws7h>=o;H;EhH30>PJwtepyZ?PV(fh1-vn5i#2WZdo=xCO1Ktke$uNGBDDm8N!|4pW?~3H z(fef)W7mgk<5oT3^q%2`E`e{I<3X!Ux3g}^(SlU$8k0>6mp2ghrU}lZB;M9%?88Hk zq*XNHW?r7o@WNzq3tI=!mava?{#o2-|F$jW}5>#6tqe17NlJAZtCx8L`i z+d2K!&3diJ<36tIx?lH9%DL>`i!v|+tXm&f@qc;ym7a=|INT!R&axc`3i$pa*=;6C zu(nAbG~S*gR&{KI#SlctOs>~SnJ1dvG5H5A`O%@%(_DfQ){3G8><4g`aN60!C52e` zaVk@}_ELD?+o^{~y3^NHOb`US`AM;O4f(c+y8ijs>7gSh;woIIindk+WlXi}m5cwb zKCT-k2(_+K@IB}IEl>}qu-X>?Gk$L2HTHUD2XB!jA*&)MIqyYsP0HF8c+v)DLj{{?@ zx8A3jdgT;hL*_~huN6M|!{`pIu;H~S+AYJLv3&bswLINS{i{b=PpXqAYXxGK+dS*& z2vfKr?$))f;$1A=)wvwA=}{R>QBt$xsucIR4Oj*9j$BDyqde7KdgJs@N2$K3bsVPm zkBWYu_djXXYO|Z|{R75+Q--{IeF@ZsB0Nt%MDSBDioG7seRi;C-1xxKSdk~$Lp?O_ zP8!#A!IenQd9qDiT0K-m4o-+U=S)j^t<$kiR_WyX4-9dm&HPkKx=9Rjd)rgWuAilFkJ zU*5Vw&#ur8#y`=Llor4WGTE==z2v;kF46@|cNFF@m{a}xRD;3;!FoatyGrp&d)P}q z-d}yl%L+6KxWln9D`y6v@?@A(0+kje8D!#6;CO=3j8VICuiCAt0!K5$31u<3<2py zqka9)(1u2YmxwBe@%-ox{?zs-3b{zS1x3U=rhBIQL@pgTDXp+D-C`yOLYHJ2j_*-s!Y( z%CdoMM7_3m!P5%N%fAZ8N7sVce5Z5|#ovxG!p|Xm!3LKbp$+}H(klY;hcRJWw%LVR z2M_NRTyuOq>X^fR_xovhm4@LV&()Qm8aqe(6VDX`H=|vJVUJ6|q-2_d<{TzGeoAM) zUGb+EfDfk)MpZYrqL2`dCkAuySul%U-@RP#cCx_a8$g#IKxDvKf>RCTR@t)v3$(y_ zWZr2!1-8HrwopgCpi$R%_0$E+=NbN&OelQWn5!xNBf}4fh&x$eH?;jQ;gtx;EKB=E z1Bylg!i4p*uIPF=s`&o=aUiWo=7_`DOe;i(VY8J*5Cmyg3qu3Ye1ZFE-XQJz(MaaG zH9`iXH|0HXKB0r|opg_=5Aj&=(x(CJLSg*H2m0JotaR3u_jj$|v{f%0y+$dZKAZq> zLwf`egihDD*~V1wbHfn#Fc^`m1@@Y`7L0A-D})*uc`~a}N@@5lARB%)P(OSN!t^Hk z8&l9yB)C(NK*j4hp!GX-##EfJJJ>@^c;mBVd_a8zB>O+8__>UFvHmr3~Gsq>&~{SpfM zG|jAg?1Exj<+7;!59Mo7YYwB8(^0SgJ@bJ*#(VTA^K5eo^>#n_+NIUu=>S|Je-k#BYM&r zp?K8ijom+96hWRl3=}Xr0L1I((sIIIB=$Gt<+&^>Mk+%+hpP9Ng$tW*|D6URF?NT3 zteE8<(N;jtbVx41K>=3Vxt@K(YIJrs9P+bBJw`7@!uG5y6*=bOt*OCUdSogb7PVus z!0m`=0N@^Y9R?~|tcz#9q3~Q*_R)mCdSjHrQj<7U@+YvVORnoUFKRz1pf3_g5! z%jVxvu-3&$94V{mMhXT20Na2-=RI~CxKCdV<0Pd2G@6(yS%CpgI2Y%#P!Jqe1ie9A z2Af5M#SK3Ijaw^FYQ%4_|3dsC{5oDpR}Fxnz4_ylmx$8ZPN{9Ds(})jqRIWyTTW&*M$xP7top z#Qkr?dkp%wJ@^A1Po{^U7^Z#TgOkI~B-)B2sI`k4?D^14O>fNZufr%>V1!g{m-$5t zlXpfN9}J&A;8+pv)KOwH@a_&jn1#TzRc(o724u3~Qf2i&)j@Jt-`QMM~ zEEebwYE7e-cOnF2LdyEcRMR5O$yz!4y9lZpJV|mf8rd412M`evLPzLdTzkFg&xGOTD^?*&NXFOyahjS%Tgb0QnBH*D{j| zXmr*o28nzCpr7(#X9(^06S7B_wODq%r5aCQk#hSH;Zbi?5hd96GPZG~0Kw06L3C=H z5s#k3@n(U?9;-a+%4ryJ$i`+jrw#h;zzyasj0}5xA8}X0s`D-Mt3{I^4?xnBp?z5e zkf9WYq!5@vN_@!c@i1puFRLHCf=Xcl&mC1ottSwe;pn}JQJz7UW6)1a%JQMSN*DoM zqY`~I$Y>GP58g2MJBq*i{XYAU;(BLRO$4E;01Ui?|q>+UGhfn-jO0g)XorN0nLZ zF94;cfr1Fi2n~ko)_8hwbuvfI^B0iB1h@$t z{LOGTKJAZt*4oU*8R81%#p%cyQ31cYL_@s74Z|=r%(SS~ zllX*Bat_r~LMIBqrjRwqLFK{cAQK4@24kqDo>^-(y^D@7DL8}L4_G&5g!IR;1 z7eYz?j52hd3JUj!x&O2;k4YRz{xtaqxd2rLc=6B?Dx;wZtIeN56{KMBDQzw4a?@j@ zi;BsZx}{*~sQ6?_I?ETnMmt^ahaawJ)PT1RG2~X}wH}nK?u(~3mY@E`0?yDTPBh80Hj$BUlQt_0a%bWh&%vt< z{xt9FcyXBHZ*`)rN3w+Q`hAzBoz z)HT|uQ<1%^+L>;>A-ka!UG@Zq%Tii>Bt=C;zGQjg{2sqa7%mZB>vnpP-kzFG znR==|G?$U3^h$8iDBV&Z@S1$4S32MpHAE>WY`iM9;Pl&d24Xqcdu|xN#(_WLHfN(| z3{?s)Y_BQ*{$g361ht6z11QL!@9TsM1)=`(U)k*i7+Fbk4&mGQcEc`Q%r+cEKiUH& z;E2`rx-s~Oi01ygik@&0!E6!W4V142E2lHU;>|BizPN7rOHImirHJ-QRoVC&fCz-& z1Br8OrfDO}=P4BGnrmOf5)L%l()i9zJgTj1s}?&*U#AF*omsmxo0wLh!5yshopK(; z&DB4$M^NupOV2qfnnE*V`CLdn-52b2E7Fs`y;SW-fRL>RWw8X`n$VF=x6swm*aN=$ zun@WYXzG{ZVkDC<(gNeU?)=cXN^Sd>IIo=Z^obbR92#@5@S7 z`r+{Ic#6%o{tVCK>LW~1LFfzr{EIVO;K-dpbG*UD&pbD^2^I3t1&rzIxs4iPM zdYp%1AS4<>dyG-PUM{ofwXvQn&T^b1!OQ42vS0@;L-78X>ob1U8O=2rO~1(g+ZM&l ztov-N(zWsl+{EHT;X}Klaipu~H_on=F$4Rk7#00i=oBoxgEL0iD9d!N#3P-h3|oc~ zI@+}_!4%;W5|=+AQAGIg{KVlb-tPRXQ=IPJj;CO=|0clpOGvop0LY|H1vlPS=rjF{#k@!2?5G>Ebo2!dDz)9) zvh0t;HRr;a7q`qeZKO9NX)3){vQ++`^z&lou|Kawbe>V!yu)%_cyLJi#j;_XS%||uM0!(!o8!GF^T~>#c zK6sw0>s-Ht+JRb&FuFHAx4#0PYJapm8i36_05+cgb;B_V#?05w4&J_~!czj#TV3c)v+B0f>hngN+d_(4&(zf;sv?t~kPyh>Swm4tl z4fz-c=|hBY*S9_#oDKUwJ|P(+DJ36jZ)D~Xj@t4&&$8niI_ibJ1k;W^Q{e=@c@8$=wE846J`)LO;(|>51ywVNN(_N{|=YS zem4{9eD90cq;ZDI7cQdvx&zhjpCT#_jfGajM?^&)dcaZbY!SATbPSFfF;uhLqwTap z041?p1j|9fo2u+1l}N256NP!rllS|0DEAYpXL}dwiVqfF#K)M_lM{pAeQ4D2RPnbU zwAam7F;_w|CYvKe6QWAQHRMa>32yhGy8h~AvgO(5-KbV+RsZUSj?FjQcD4L0Dh{2o zCA@8Zm>No2%*+9x6v)e+q_A)6CduRml;y@*1C_l8}tH+%lDVq1iF_Ns7h zpp<>nueD)oZVqMF20)%pSx_B5hMOE$F6(RiGf5q zm4qWhTX8! zW6Uj?#kogLs%+Kk1fga;(=~(RaYVq^a-af*-rH^Js8^}A0F%Tp)=d5J*nBlXbAK_3 z@T7S+oLGxGrAz4GydWoMr8aBr+tFkbLqc}4Re9IfmNfAT1qs@Uuk5Are2=148NuLf zC>IZ13Gk*w=^Lpwz2%$-bQ^W3E`SgNBUOqRzu5FGW%~$mIYP*a{j=^LaIxYEQ<~tk ze@tm2{Ol`<3O^+$KfcxO-4cf%Bw3v!cRsaJS-b}+t#VZ(JZa)<)k^(1MZxM8$fZPA>(G8rTV>Ph3rPX)#wkn&;$E*H$_6Bw=^9DF7a{X< zN__~K1EVK*y%2d!=Bu;@;Lac%gYqJH@TQLWHZ|T$Dzy#=__r8-7Cj(y0;T;=bhBe@ zQ0O}iHSC?@Kcp2!Jp!%Oj|P~;Z151ANBivW9z$1wosl=bmRJVo`rKeD~&d`+Flhdy4J)#m2y=* z_9e7STRtXbS0Qo^{SlW5nq%Qn7CaOWiEu;MTE1xyP;8^mJ_7I^zZXR>JKG6zu zz!({;Ta*>MzU;4gv0)SeB4Q+z(r+5tQ~^6Q+~Q3QEU{aw%g5y&nzu=t!V_8J zJf<6{IogPndB5Rfx}tq$zu$$szvgN37%KdfZkG+gCf5yYwe-#ouU*8=_^R zEwtHNC-3)ndTqTKTVR~v_6^&5oqG~_(!#!kM6XXtps}ZHwQ6}sty0{rx#zI*L&f}< zuQobjqZHl->@TLIz6z=~alPB*#3(r~ca|&MIQ|`qqP-GyD_3@7Di9K$-s{h8hP#U; zX#QNdy-K|N=0IjBc-=s6%X|8yZljj%DTj%_HrNkO&!705HrStJ?CZH!N4}wkp)ute z%VEpY)QKeJQV2sq9nd5nt(!K_j~%A{UY{*?y~wLawjrO6Fn%eQNNd$YM`z@*nCHfE z84!unBfe!nykv#DU5>sfe*K<`Z$DcP`296|u?CC;2$e>mdOIbhzR&$w^FF?N7sPf< z6rD;bR}`I!(oi?**Y5g9a6GWfCf(BG?PG(lsITtK=6@octRl52V=#j{)o@NBWzG8u z$+DB)w{5=DR!wvVEb^`t-{h9-7^i+cJ>BzX zB;j9DCdVpYgSz?Op6h>e+gT9fEjW^z{zEubp1BeHKki-|{(sq8{9hvJzlrGj-+XWW zt46B-<<)XG-tf`@$^7)`Q`w??5JuayRQCZCTOA2i1fg|}?ttTcs0Vh7wtLEe9YT~ehLc8 z|Gxcgj`y}QqJw&Z)VDBYi>U?V^xPyEUiZS2T4)(1x^G)B&A(UxFem}Ix8_NPA!8v& zOajT%TT^I?J{rS(jKcA#>xD<~M$JVMATO*sXMh?IYv2Gh!EE~`+LmjH7ho=3y+_DM z0dvcBf+A+^pKX$Vsh==KVD0bN1>dv*iqV*ljd*veZ z9jg%GMfD=sqyqwUOx?{Si8c~R>FFZIk@Yz+xmz`DTEgFruAk8ZMlA%3TV8S5(4~vZ zEr%g$7+j9=#<3b94KUa+CL1>GT^?n#xV?SvF`x|58x1@E8BDZNYA|XO4m)_oM2GYt zC?4qpfMon49LWjn08gjBG`tpKze4-rav|YnBvXX%YH>bSpXqj4i^6}^$|JSiz?NRY zLMMVeJKhs_=_u67Lyt=#g>Yn1_wj@pr*f2116Uc+mU_2(YHX<%T}BaCol zGS3GwL;vgETfR|udHI|rUbW8p6X*@1-`B&?`KagEi4VQp?*mxfCf3SF4{R{td0&E> zUyytdxdG^X^l(GHE&Z{h4@frO0^Ty0fA%^0{@MO1_Gp_r746Pa&r-%)@t(n(-q)kV z<7f>W7>n<~a=&x}tIHXRf>|hLU}#b|`y>e2?wy~4dCH9FO{U~W@ne8b{-+ghJE^ZZ zn!2$0!3@a`%wp()ovByy0w}doD}Dpv+Qp<{A|&>(T1lcid@w1ds~obn_4n7_9U}bN z28pEtDxT{YmN7RgcQlDlL6lG^uO5|O)cU#M0wr|aEi9kY^-no!g*=9>@XBW;0e5to zRu$=d;MHfhhxHw#Mx=;PbZlAV{C2?)sX&KBLL!7$xL@e3^ZV1 zSKqc)cWJ8}L*iORUt$1sS5UEk_PF>ivb7Ppxz}(2hDbaI7qw)h$JdcrcX@Pwuv#~- zF_qQfluHKGq)aiy=kn)Q{d@n_tQ5qFRV<04a$gR=_h;U$_MGj_{4nd<2Omxn3ci;Ah^_h5O8<^@oj#^hl=K`Q(!}?|w!KZxd&beRA zCg@^Si7G{^^MDf*dHm1(SjKCq-^5OBckNfvs}N?TV%QoOB&=&R3+8(1EQA$q%q27` zd}-nx(%Q$Fgg-kUDoT^&1Qs#N=mj;c?PR&#b&E5zR6jG+zw~bb^?+bfMd7U*;xtCy z7s6S0$?aRz*~e<`H7d3$&foT&bB%Rd<_q-i==AzDqOUVhgpqigFME5(UI3F zeN1AWTa^YUiI}k#yuOGUVGO>AzqnwmG?o2gc zSl?*X|9Y7qM>zM(Vv`_qDZ=OD&w#kE9+i2q$HZ!A>T6FafUg{@u6Fwi1KDYzoaeqJ zU+_6qjTCWSv`;Wn$bX*x*$3eW?E-t>Jt_G$G=QJSL4+e>gQ2t9_<%3GR>cy7dG;oP z0$S` z(OQ9Jfvs!1|7Yp%C3t~6UftQI_{7N&(qE^BuFHlC?Og0-jpeRgry$kXLHA@MaQd|l zrN}yMmrEs7U19|FWk5>b9BtJ<%|&X^+*(U}3x?_M58+8~*>jh>Z?FCixpIKrZ{( zI(B69SuAAxE96~ZdU18}utMTW^*eXurGIy^)fGNq3?VY z43{)S^W^MY;H&BTD_b9o_!~**Nh1mBPqz~G`RO5b{rMVh^~NZ$d*thD(*m?yo^xxu|=>P4LQpBx_x8@BYX0810IEFDc#jlZm%g`mu zg4syIT30!6-`b7O%RG1Z_M2Uo2gRYEh$<gkJbpd{4qw>+AxQ!8Fk{cHjlr{ zru6%47dz;GZIWVe<@kEB+kSS<=mhDNVOsp63kSu~z1=Oqg>he0zSGP2%dRT9JbcbO9m71_su!f#uQhRdy=Mz(sj-C< zy}j#-UFE0mU+zM&Uqz*r8pSy*_J{UDO#F;G`B6uF(N;0`U148ujGU`}4XJJtCEX$I zYp3sDF}!zETZZ5DesT_-O$KX>`A_QK@LD zeJPQ*Di2lWcI23c&0JH^PPqAVio~_I$i92c67PpN)Xe%36%q^r(28coJ26d~3|zdO z_TGSahoxeW5i;1E4s4v0+djd;9_ZfK@;>rlQQ^bYLmPub>w-FnSre+g`*-D*AD+@! z-Ou)ls8s*i&lbQT9`2~~+$@z@tP9046WAp?9p=L{;W2p$doWmL1%eXmYk1jrd4OUb zoHo1vJIH+=H%gQiCG7@NN`%tKx;9uL-vkGtDp*?M);3 zw0Yin`D1JrPoX3scqa?@*-PBN$cnZ61o<}l+Op=w=q%(Zbawh_`%*wjH_n!Q5gc=k z0?}dhs;}Wm7i0op?MKA6M20QD-~L%SM5Ui`()~sQ1QahU?DN)w=k9ebsR-%NLx*!2 zoeo&cGdaTGK8sGZeO-R=YvdvGoV`=+P)*2tDvevLyQ~&td30IL&w4mU33libSRlJ< z^h?)Yyz;%w+o{Dmsc)yPgpTCh=G`Ek=H({K+>!`=(teMCCpq73mQ-b8XtpS_yG+-H zb8Mve@pma3`E>AG**Qn^BSx3ChA~F3F8e|yDl~>CFzG3|Lv{CyXG`Q*8~d~GRgBza z=jJuCBp0kWh=S)4Z-9r>DdHK(%HBSCQF5c!-~V%}|3_B)U+$a#tBv?hv$aPdOiAnq zOa$Dt4!)aR_RP{Aon$4~PDD>MVeIWX)zGM7K~E8+%PWwM#*Z0e!2`jt)&k=O^0?oz z{P&g8w}NAz!{X;?@7qtP}B$H0<-7ZvTfVoAZ-}_ zDy)lvDoh3&>nWDc2zmQUCfgK0XnoLn!XojSYmHDkXOYpkF)Mz{VxJg|fOFnt?86v= zHrF;E=bHDsSkE-g2H!j|uBkIA-k9Y6R%h}?r4O~hyhra9ZUccX?m_Zkd>!vPf%)j6 z=GZW|8&flTQ^VX!Ck|F91t=et^3DDGwT!S`QM-KHnCHAlxa7H+<0gU)wE`^S$GC{E z{SG#C3K%dPQVK8=&m4SFBY}q`zH;y~Rljw1G3WQ4c0Z1~d{SS({4W*&t0oDrQhRRs z5%Q)QL0zeWqon4ErVOK?9Ob|2ICeSCdn*4Kxo7*lzwDoHWJLV?U+Ud`T0xb^-^l-S z2&qpSv_8nDN_RdVMT9bxJe+=&>B;{ZD{=D5wx3GJXn zd#+dQOf!$fJPmTW=A&DDz(PfeTx~9n^YjkB-m4*cAlI)obH0ZXVOsHm##%jpLNo5S zXIHmmT~Z+N#4Bxs9C}h|eb*k~kxqq^k|Ik6}~t3b8Er zm;Ae4Vx80Y=>1J-F5D`!8Fdn3a5%%Q(#$`uK}DR$@(%0mu($RZTpqm5`N@ll({{Oc zinDqFWjv%8h^19+n0;iw>xvjzLLcQ(7AU%nw;t-iVEMrH)BApG55C??cv?|k zgvcFxLJpO&C4WRgJ_M~}f-BwOS`XDv#jaRjsLS z*SZ~1`hN79(#iH{L)Q4z;d^*Y+!m|+^7RzU5K+7WOoXscS&gjEeAo<=a)1(kLhl3& z?qisMCnkJxA8)|-Wh+#-4@b7WrA1g1n;NJ8z2s)mnJG8%^^&d0e25wWeB$VXw1x*^|J2%CBuLR zr?2Hn03xG>PzuvHnyLOunkxt(?IYLd9;|>f23p|(#ZFYy=fVTB)!po{imc?}dDtK|B`AMX z4)Jz|lqhAOzO1Ju3cCQ0X;|>FCjg6#+A6x5!Enp8hHki|O zT$)!yH_H`UP`-}Q#DfjwKLI)L+&FZp*N)^JA)(bbvvZ`St5^laTK7++GbSp$USrqE z_&pFlJ>Q+$?d_B;Qin){zzr)C^RRv?^VXI&5{&J9DT<&2Q^yx4I4mS!{SCCc2*m7!ZPg&xllhcQ+U_yJEJ)0xM?LgWy~?n!o^CU zS^=__v*ipf1Q~O$U~MGPf+UuUqMtZ6BOv(Ax{m47^rdn+-&qkf5;ZEdweNV%YK2JSv_= zfURsy8Z1%w*a$le1V<@IceBMMeUIS(iN~eT&=n|d#(8-jELZaUT|J9jCRZY)EMaa~ z!eQ8a^zJ3ygXf(3m)336aiJdipqi3c%#gCHw#=C#@!ZBI-#A{TFIn6$P{c8)vpTVa zoysKmwne|z8|R5NKl4G~`k5u95_Sf{&+bPYv%bPrRdwTcCsk~JyJ3df=xLEHU1(w0 zLZe1}#J5!ZaFlpEW}#QrcA8Hin7aO_tC*-H(RSzt)+K~LHC<#{xv8La@9`Xjy6J{l zsu~wvGBd6nqPuetsow5(MvL)OJ9mkrOyfq!L-%leV!TRIw%dtxwA2)BJnCM-NJZ)f z*J>cmv-f3R=0Ufds38jPV%N9s1_?@G0bfC5iV4(=*r*tj3VLOf6T~AX4Bf>e#7l0HP*~>!VA!G&`63Jsp*`DpcJhoX7q4wGW2!$>l~fM@rnTV;vum^>`W7@H_a>6b#- zLT4V|{fT#0ZIehm7O_Z~pE;{%Z?MKV6~W5o(7~*yUDt{Uc}^4y_5|2d^YMDj|kWww6BCQ>;sf4Uhn58V?LGcn!>H@w5L{JB*? z!kx_S^-kZtYBoKPpD4mzuf%C+CcQW+yyCLAX+(WO`cRS*97oOa8=ifdOpxa;|E!jm z=kvR>t&SA0qFmo*e!=ysOyg{xX|Jgd=lx(MO_|VMsFRHH5_lLJ>w<)L8@XPH{-Z6H zX4z-XGu=3H=!_HT_(uPeN|~CHNflNT_}NdfMHoksy(F_mz zZG{BoJD3ApyG@iC(oI!LUIZHJ1oynuLjrEF`}QRzI;($Y&(_9S4`OT1Il7vXfG@Fp z!`?EiS$aV~xK9)ue9)C^AHKClw{Td4q0ejR*s=HQeEG{QS%aEl5~n`%uUHp5TJ1Wf zOv=#SHe$-(D9W`xs1w^CB~4WHveMjUqhp4Mx0_BPrn zj?R;|uJh_Fx!8TNbU8bUyn~8qsA$ynbU3$RhGS^|L$mNH`PFfa4L%B6%5?k-bb@Ty zZ7LNt2E$hnX|~un<=N7BOAKY$0vsRicaWu$zxxoOG}!PDh=B0dhl`)DC|>g8m;;oh zRT31b;pFGk5UZ0_&><6P73uFps5K?nocg|dAREd?f{Y==nNSfoi#3)hGn-A~Rj6`BPfjhuCQ2Ez7a>vZ*3!Rf?qJI2x zI8bJ(J(Ry*NNhe(Sl}+A@*il8B8)!B{n6epEvlxgqEg)AK|klo?HDdGphZtN<4v#g z63eat-`2PytO_eW%H$b5KeV?BN3(n)R^l0X<7|$>vG@QI$^3OF1U@7^02hX86kN8WQ#vIgvJwS(o$o2 z$#@ewl?}~@Zz@R5wi<$49Q@QX@|L-?r@+CVFE9qP9z#4G78rx!;!}NoH~-fcdJ`4* zI_=kr{G*-uS*D_2_ni<*&{SZLa1Rn2uI-#Ri`_pk@A?YjNIk{-xB00v-c7X|o>QKJ z-)N@xyQhH1NsJd$L@C(pVg$`V~9kKJDf|=&i1!wRS@L zf)LwAH6fPQCth-wX3&wSG-_wgA8OvClXrXQZG&@Ak3tcc2~INCy~A!mY*$tmt@BP# zcD8joSzb@Huu3!5c0J{4=}DF_LbIpiGRzekO-pF?>O9SqgVP6J#e1NhEAhD1VjeZu zPFZ{?-EObs%_fvy5wxba`SWSd>U7jg5AkfW6M$e1ic$e%2)i3Xh)Vt9Vo__NvlOg%e<0pp=<#g@zZy}?r zKAbM?tBZVYjO1Bon8$0-{C-3rGgA-fD+(%F@}Lu*{+N;Q!ntt4UeJsBK|jvjnGnd^ zMc~Y_j!F*$h8L4*J3L^#Agp3SIK75Q^EpsQ6A2o7C0eMYBkf_iAsd4yLzpU~p!L{f zl4$W1axKl&ET%69->h*;1PYq zQ}ekNV8T?Jm2EX{F&d{6hB!>D0>_I$ZNy{KelE^2w{)4ApDe){FFXJ;O+(?^O=6IP zXyfgekJ{*=s3rqDpX#(er_*tFMdzkW+7^qc0{={gyFVevt+Uv?nCrd9+42avQ|d5E z*(qp>v#x_Tm|U2;y20aY_53kmi@ha^t`k#fNa9qrv6Z7B|qV1dOXc%#VY}u1iEIVPe6E_ zycl#0E0NTlLOVw~-S<|?>n(_-HL;hgPCz3GIe7BLWa_;aW%<_mM3k_+)#ycfjg9)M z&9^h-2FJcS(V6xyI!7W}HvKL=)qRuFC_u;g>ee*7>C#8eU8h;OmbH2{w?uX1PE1ul z%Vzohw_ja4B8#i=Q}EF#+%zkMnE-Giq(KCG zILgvW=vD&^M?G0YtVBg42{Z~i@Oq90VnsZTtW7a6`jb#kI1%HpI6QKrFFF#IO5Gka zp8jr(3xMvbu@<)w4eB9n0UGS|K6Le%4uL>IxQhn_SO|#xVQcOE`X3;>QJ8OTsfG0= zwKg2*aE$n&Q4el#BwpCCrur#0%N8=VR@GL-?yW z8#r+TT(xDvz^$n0UDbMB7&90%yjqok=m36ls_Cwk!f&;mu;A92v>KFTlF-fkleuFm z_z{QHk-PJwUToudMNS|&@|4VvQsUMi(aUAAP>#He(vpGQUKoXf_RK3yilg z>aBO~73ST+1j2ImGTLUi{n|ox+Drxx04iwXUJkhj-^bAl%@rbgL$1|T!>4>Z z)2ZN!Utmmt#uIzOQu;BzFJ|3sVNJis4w4~~G0-VcS6ykk(gm2=c#HfGz%LuFBrxy| z9jOiHa)#|LG`2ol8D-*t0Di&1?fT8lQUEHCJN(?eENuL78Z)Y>HaHN=>g+)S<8@}4 z(M4$vBHT1_r+k4N7Q=Q8#nOoK5>eNHwHelhDImxNxL|(S`VAyI(QxBq zuuoD9A>sP~fh7D*H{PQ)`UVoUxNGPi4@}{iW@7U#cs8~x5#C07>K z5C1H!Irs}0*?lU1P<2*`rs?}usFy>rhw%xyz4k(>hx;Z&od+{e>NKCXQhC?ecH6P} zA?i7NnzK@hWYqVW!r_Utm2&#RV?Cuk;(VcFsLXV-uQh?lSr_3yT*KsuE+MfH{I^Is zlXcNa;Yg_!>D3kMc&9taeiNbC^+|QG+EbG+av8D$`z*tBrcX^kIx(LO53?$=c9E56 z_qw7OLb9dA8T_O0>*t&1{EJrGNY*C^ZzvSVqdO3o4fq!=%l)u{FI-H&5U-X5NTv6{ z=SC_FctsuAygrL>?BJkz+>O-8;ydE>pGUO$Sx5N?+P%5j?jwP~@yW6M8|7c1l zQW*T<-Ddts=Ri&%vBP;=38|ikPj?or6zNLnxY$%0Tz5Xj2^2vAl?uodI=!4h7wNG5 zm;7$dp~kDvSZBQgOVTLSxy^cXZZ4$8xKez~Ztjz+Kg_@Bn4Vnoz4nf$B#aJH;)f`F z;v9OfMF~8@!+x$eJsj8H&`%p~EmfjAcPPVkvdZk{lxW3!cJaH75>io{DKU+-C+-!A z#%HEaBDT?G(f@t(s;Y_N7m22=h?u|k&&zIbt`=uWte((F@t(@%+z5bnUJQ;#+8#k?N zv$P3(m)MhS7IMW?`eAia(TrzltfVMBB#K+yuF?wVEMdhMQT6k;{9zxGke(l)DM^~2 zHMnWeV|Yki1BF`Otnob*1d()SK^FnX=%1*%o4xr5My`gW^u%^bDB@H*=}tH_1i#%@ z;i)O0{?SqmonZgdkNi)ZYv~`o=faH^d4L#c`7M9;Ei2?s_$@-gqe>X#AVr%BB?6_)Uw)t!c#QhUBi~ zx0*V%oz~pdo)3i$zhF|dGW$%y?IOi`Dv%oyowWw#fSC`n+{XBXbSk>r(A@OD&7{7F+j7 zm26E}^R}Md;AqkZQ@^VxqNLml=H+fw3{Mc7$31G=PB!F5e$CL5_W|`fzjgeauPgKq^t8o>Wd|*{R+_ws zo-^Q+Sd9v(LC0EubjjJEn zUwY-vJX1Pw7b6q9SGPm9r1Ybb>`L`EQ5An(uKocBf8Um}e6|uraW4I;4VfdjbI2rP zo`PpLIH`QFvVM+gzawn@Exrpz0na5i%d+#ISvP?>3wRQIda24GCF)c$aado<-+kuK zqaVS`o=2H}im!>AvuY7#bW0=_Y%6>Hi5(U@dj9*1d2XAE_XUzUf~J;@>?dMr2DG_{ z9*N7>^0`SlY6>pCZt-oTKkL^aPwVzA{6Obc^~x2N9G^yv{VfOE`MEX13%rSnhr2wX zGjTMh>l)SK$fpA(EUmvx7!u2duMd4oVjL^<9qs2V*$M)uCW(h*62G=e_YPVp{&VBmM6)J#W0y{7bGpL4I({9L1D!R#I$FGyic`UbiESK#Vy#?~bRhYSgZ0a9ZiUXvh(c7j6T4 zg8kAJR6C@UBOZ4=#_CbS0?l9`6ix4LQV*RZY=Ij2&Y#d@D6#ml?`QZO%FudYA3}^r z+q>nKfVfyCN(n||G$7M;anjPd(&#PhJ+j)BJ{W{sou!UiwCS{YPOLlIJ$RHQ)Ae54 zwH0E_kAJ@|1AwHbvSlQ_yu-EQrYXdlgc50=<$C9SMjn;1X0nBGz}e&SNlmSn-`t0Z z^RxW6ueM!V#%9X*cVCEoSa!K2ZpI)vI^HR!Hsnwu59+f#4$2ULtD8*T!@zmNQ0W`q z?+!}yd%`(RcPBvAVG#jiiDo6gg|oRtN;+NVK;<|8ewh1N)?=%kbR`H&%{i6&N-T?)c3r_GlDOBDG+ z&_Cd?MvAeuWb3J_sE`&+^;`#VSf}%@!%uiGwA2QUDcgq(IvwvolGeT$jFfyy`cCDa z!#$F2B6gEG7;c{J;wxDwjCm+*8vFqH*11^o{)IMR;qzcfX zXOFmp7p=(PPcj>8l&YOE+9I+2cMtf^Dc|awWvJt>dwyDs%o*ZeH{ftPI_2s>`DH0x zGXCz2%M%hL={>-H&x+VOT0);YoAgTace!uWpMtB1w3@mABNVU$b>0(5vuer7-SpTz0OR8EcVt$D;q}- z4KXR2_hH?UyB)El%9f}OjuWGv%P@n|?=Gk~YaVyju|)p3u8Sz9^`-y3YRj^rJMjPw z$$b9v&u_Yl8oY~JN){VN3vkw7Y%aTDx~fftjb3BY$x0_HKHZrNlx<)~WP*J_C9N z0A)QAM;%_@xJd7?(UQ`3!D@P~Ozo1NA8oo9^U1XtPhUP&pxM3UHt(fj7~{BFQ*zzS{$nx}?ffTB?$!xv(qjHw zTNMupi9!h`QtVJfo2K3RX9qJ^;;wO+$EpXlzFpC+n*R_e_K7rm>~K3na_=(40mB*( z&1L*^<+a|nU%b8?sq100yx?H@>#DT&vpT`X-9nyUm$V*>)n9Mvn%r8mfweEd%v*yo zJ}hPH+0os*EwAVJPoXf1?4WOnnwGgJ{Wjob6azW>D^B%rZ+4qd@WEj&b9v7fA)(Q& zN%GUrMeaDB{1TTd#rKNq+HxTaS94)}=(Bv+guMdA8pX;EIonL1FD@>cOYeJUm;I%m zc}`l9xvlvXoDwf!JUn>*b8uqHr9l(!)YvyG)YCj~s?%dj9nWY>4<&BTSW$TJ`1|m1 z^39&DAJDRy#k}=!zPCa#Ob)tnbDDhi+|iW?AD4%U31JK0@*Y^eiF-*tEdNF3n?&l> z4PBSzBeLTyRwO&$ecMOrTYSN{K26ekZ;g0gLg|&|>zz`2Hjfy`H8tG0!sp=LH& z=3K?GH%JJSi!-WG(tmp&D1Ti`e~~pT;vDD@hW2!`M-yZASSy@=M*fD`^+M-ES*8lICj= zs?olsX}ZgEHf`PrLU9@$#<3@5L;Ns{O23)KdA zW_P`VztqoXSO(47FE&groJ|jt7#^3Gr=I@Bz*j7)&==!(&h_HQabhiln>uOj3nQKK z$|sJyf33u8pKvoWpA33aEm^A`#q;!7E&%2f@#mM5Ne5Q&c$(3rSI%$9=Sh^iesabMQLE9An{ zp}L6pu{7tCo?{n&mWH%tD_Lk?tsqQ!Fx%isiZI>XyTJNm;pFBiPDQ%~=-Z${yde~K zPwixs@SsmPUvFBhC_Mge-5r z*+ThqnXt*2IW2Kh&RmfeX}mZ^HzfZvb;8Xtg3*E$;n!>Fb{INEF}W89K7INW-dopW zcvogjxQ3YaC7|m0X4(q<>¨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 z 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 literal 0 HcmV?d00001 diff --git a/packages/db/src/migrations/0075_cultured_sebastian_shaw.sql b/packages/db/src/migrations/0075_cultured_sebastian_shaw.sql new file mode 100644 index 00000000..4633db39 --- /dev/null +++ b/packages/db/src/migrations/0075_cultured_sebastian_shaw.sql @@ -0,0 +1,7 @@ +ALTER TABLE "issues" ADD COLUMN IF NOT EXISTS "monitor_next_check_at" timestamp with time zone;--> 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 = {};