@tickernelz/paperclip-pro-db 2026.1011.3 → 2026.1012.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/dist/backup-lib.d.ts +2 -0
  2. package/dist/backup-lib.d.ts.map +1 -1
  3. package/dist/backup-lib.js +52 -15
  4. package/dist/backup-lib.js.map +1 -1
  5. package/dist/backup-lib.test.js +64 -2
  6. package/dist/backup-lib.test.js.map +1 -1
  7. package/dist/client.d.ts.map +1 -1
  8. package/dist/client.js +81 -42
  9. package/dist/client.js.map +1 -1
  10. package/dist/client.test.js +33 -0
  11. package/dist/client.test.js.map +1 -1
  12. package/dist/connections-v3-schema-core-migration.test.js +3 -0
  13. package/dist/connections-v3-schema-core-migration.test.js.map +1 -1
  14. package/dist/heartbeat-run-issue-privacy-migration.test.d.ts +2 -0
  15. package/dist/heartbeat-run-issue-privacy-migration.test.d.ts.map +1 -0
  16. package/dist/heartbeat-run-issue-privacy-migration.test.js +265 -0
  17. package/dist/heartbeat-run-issue-privacy-migration.test.js.map +1 -0
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +1 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/migrations/0315_chunky_chamber.sql +3 -0
  23. package/dist/migrations/0316_lumpy_maria_hill.sql +157 -0
  24. package/dist/migrations/0317_colossal_otto_octavius.sql +24 -0
  25. package/dist/migrations/0318_supreme_garia.sql +1 -0
  26. package/dist/migrations/0319_curvy_shadow_king.sql +27 -0
  27. package/dist/migrations/0320_chubby_vin_gonzales.sql +7 -0
  28. package/dist/migrations/0321_familiar_titania.sql +4 -0
  29. package/dist/migrations/0322_cool_naoko.sql +3 -0
  30. package/dist/migrations/0323_whole_steel_serpent.sql +20 -0
  31. package/dist/migrations/0324_loving_the_hood.sql +77 -0
  32. package/dist/migrations/0325_agent_commentary.sql +33 -0
  33. package/dist/migrations/0326_mcp_file_transfers.sql +48 -0
  34. package/dist/migrations/0327_easy_eternity.sql +232 -0
  35. package/dist/migrations/0328_private_task_access.sql +278 -0
  36. package/dist/migrations/0329_private_task_draft_assets.sql +28 -0
  37. package/dist/migrations/0330_rapid_emma_frost.sql +44 -0
  38. package/dist/migrations/0331_premium_slayback.sql +81 -0
  39. package/dist/migrations/0332_messy_famine.sql +189 -0
  40. package/dist/migrations/0333_strong_blacklash.sql +23 -0
  41. package/dist/migrations/0334_heavy_captain_midlands.sql +15 -0
  42. package/dist/migrations/0335_daily_onslaught.sql +16 -0
  43. package/dist/migrations/0336_acoustic_human_fly.sql +1 -0
  44. package/dist/migrations/0337_reflective_kree.sql +98 -0
  45. package/dist/migrations/0338_petite_shockwave.sql +4 -0
  46. package/dist/migrations/0339_long_franklin_storm.sql +153 -0
  47. package/dist/migrations/0340_aberrant_machine_man.sql +6 -0
  48. package/dist/migrations/0341_blushing_screwball.sql +17 -0
  49. package/dist/migrations/0342_sad_proteus.sql +23 -0
  50. package/dist/migrations/0343_chunky_mentallo.sql +52 -0
  51. package/dist/migrations/0344_free_wolf_cub.sql +18 -0
  52. package/dist/migrations/0345_spooky_white_tiger.sql +7 -0
  53. package/dist/migrations/0346_panoramic_spyke.sql +74 -0
  54. package/dist/migrations/0347_little_night_nurse.sql +1 -0
  55. package/dist/migrations/meta/_journal.json +232 -1
  56. package/dist/private-task-draft-assets-migration.test.d.ts +2 -0
  57. package/dist/private-task-draft-assets-migration.test.d.ts.map +1 -0
  58. package/dist/private-task-draft-assets-migration.test.js +35 -0
  59. package/dist/private-task-draft-assets-migration.test.js.map +1 -0
  60. package/dist/question-response-history-migration.test.d.ts +2 -0
  61. package/dist/question-response-history-migration.test.d.ts.map +1 -0
  62. package/dist/question-response-history-migration.test.js +53 -0
  63. package/dist/question-response-history-migration.test.js.map +1 -0
  64. package/dist/schema/accounting.d.ts +1282 -0
  65. package/dist/schema/accounting.d.ts.map +1 -0
  66. package/dist/schema/accounting.js +120 -0
  67. package/dist/schema/accounting.js.map +1 -0
  68. package/dist/schema/adapter_auth_sessions.d.ts +2 -2
  69. package/dist/schema/agent_commentary.d.ts +181 -0
  70. package/dist/schema/agent_commentary.d.ts.map +1 -0
  71. package/dist/schema/agent_commentary.js +24 -0
  72. package/dist/schema/agent_commentary.js.map +1 -0
  73. package/dist/schema/agent_identity_keys.d.ts +131 -0
  74. package/dist/schema/agent_identity_keys.d.ts.map +1 -0
  75. package/dist/schema/agent_identity_keys.js +20 -0
  76. package/dist/schema/agent_identity_keys.js.map +1 -0
  77. package/dist/schema/agent_runtime_state.d.ts +2 -2
  78. package/dist/schema/agent_runtime_state.js +2 -2
  79. package/dist/schema/agent_runtime_state.js.map +1 -1
  80. package/dist/schema/agent_wakeup_requests.d.ts.map +1 -1
  81. package/dist/schema/agent_wakeup_requests.js +6 -0
  82. package/dist/schema/agent_wakeup_requests.js.map +1 -1
  83. package/dist/schema/agents.d.ts +132 -2
  84. package/dist/schema/agents.d.ts.map +1 -1
  85. package/dist/schema/agents.js +10 -2
  86. package/dist/schema/agents.js.map +1 -1
  87. package/dist/schema/ai_connection_defaults.d.ts +2 -2
  88. package/dist/schema/ai_connection_defaults.js +1 -1
  89. package/dist/schema/ai_connection_defaults.js.map +1 -1
  90. package/dist/schema/ai_connection_routing.d.ts +8 -8
  91. package/dist/schema/ai_provider_defaults.d.ts +2 -2
  92. package/dist/schema/ai_provider_defaults.js +1 -1
  93. package/dist/schema/ai_provider_defaults.js.map +1 -1
  94. package/dist/schema/ai_subscriptions.d.ts +619 -0
  95. package/dist/schema/ai_subscriptions.d.ts.map +1 -0
  96. package/dist/schema/ai_subscriptions.js +61 -0
  97. package/dist/schema/ai_subscriptions.js.map +1 -0
  98. package/dist/schema/budget_incidents.d.ts +2 -2
  99. package/dist/schema/budget_incidents.js +3 -3
  100. package/dist/schema/budget_incidents.js.map +1 -1
  101. package/dist/schema/budget_policies.d.ts +68 -0
  102. package/dist/schema/budget_policies.d.ts.map +1 -1
  103. package/dist/schema/budget_policies.js +7 -1
  104. package/dist/schema/budget_policies.js.map +1 -1
  105. package/dist/schema/chat_channels.d.ts +21 -4
  106. package/dist/schema/chat_channels.d.ts.map +1 -1
  107. package/dist/schema/chat_channels.js +3 -2
  108. package/dist/schema/chat_channels.js.map +1 -1
  109. package/dist/schema/chat_github.d.ts +108 -0
  110. package/dist/schema/chat_github.d.ts.map +1 -1
  111. package/dist/schema/chat_github.js +7 -0
  112. package/dist/schema/chat_github.js.map +1 -1
  113. package/dist/schema/chat_slack_registrations.d.ts +287 -0
  114. package/dist/schema/chat_slack_registrations.d.ts.map +1 -0
  115. package/dist/schema/chat_slack_registrations.js +26 -0
  116. package/dist/schema/chat_slack_registrations.js.map +1 -0
  117. package/dist/schema/chat_voice_callbacks.d.ts +128 -0
  118. package/dist/schema/chat_voice_callbacks.d.ts.map +1 -0
  119. package/dist/schema/chat_voice_callbacks.js +18 -0
  120. package/dist/schema/chat_voice_callbacks.js.map +1 -0
  121. package/dist/schema/chat_voice_inbound.d.ts +461 -0
  122. package/dist/schema/chat_voice_inbound.d.ts.map +1 -0
  123. package/dist/schema/chat_voice_inbound.js +45 -0
  124. package/dist/schema/chat_voice_inbound.js.map +1 -0
  125. package/dist/schema/chat_voice_reports.d.ts +184 -0
  126. package/dist/schema/chat_voice_reports.d.ts.map +1 -0
  127. package/dist/schema/chat_voice_reports.js +24 -0
  128. package/dist/schema/chat_voice_reports.js.map +1 -0
  129. package/dist/schema/chat_voice_sessions.d.ts +730 -0
  130. package/dist/schema/chat_voice_sessions.d.ts.map +1 -0
  131. package/dist/schema/chat_voice_sessions.js +85 -0
  132. package/dist/schema/chat_voice_sessions.js.map +1 -0
  133. package/dist/schema/companies.d.ts +19 -2
  134. package/dist/schema/companies.d.ts.map +1 -1
  135. package/dist/schema/companies.js +3 -2
  136. package/dist/schema/companies.js.map +1 -1
  137. package/dist/schema/cost_events.d.ts +141 -3
  138. package/dist/schema/cost_events.d.ts.map +1 -1
  139. package/dist/schema/cost_events.js +20 -3
  140. package/dist/schema/cost_events.js.map +1 -1
  141. package/dist/schema/decision_models.d.ts +579 -0
  142. package/dist/schema/decision_models.d.ts.map +1 -0
  143. package/dist/schema/decision_models.js +50 -0
  144. package/dist/schema/decision_models.js.map +1 -0
  145. package/dist/schema/dot_runner.d.ts +980 -0
  146. package/dist/schema/dot_runner.d.ts.map +1 -0
  147. package/dist/schema/dot_runner.js +82 -0
  148. package/dist/schema/dot_runner.js.map +1 -0
  149. package/dist/schema/fast_responses.d.ts +662 -0
  150. package/dist/schema/fast_responses.d.ts.map +1 -0
  151. package/dist/schema/fast_responses.js +65 -0
  152. package/dist/schema/fast_responses.js.map +1 -0
  153. package/dist/schema/finance_events.d.ts +36 -2
  154. package/dist/schema/finance_events.d.ts.map +1 -1
  155. package/dist/schema/finance_events.js +5 -2
  156. package/dist/schema/finance_events.js.map +1 -1
  157. package/dist/schema/heartbeat_runs.d.ts +138 -0
  158. package/dist/schema/heartbeat_runs.d.ts.map +1 -1
  159. package/dist/schema/heartbeat_runs.js +17 -1
  160. package/dist/schema/heartbeat_runs.js.map +1 -1
  161. package/dist/schema/index.d.ts +17 -0
  162. package/dist/schema/index.d.ts.map +1 -1
  163. package/dist/schema/index.js +17 -0
  164. package/dist/schema/index.js.map +1 -1
  165. package/dist/schema/issue_access_grants.d.ts +161 -0
  166. package/dist/schema/issue_access_grants.d.ts.map +1 -0
  167. package/dist/schema/issue_access_grants.js +21 -0
  168. package/dist/schema/issue_access_grants.js.map +1 -0
  169. package/dist/schema/issue_comments.d.ts +36 -0
  170. package/dist/schema/issue_comments.d.ts.map +1 -1
  171. package/dist/schema/issue_comments.js +2 -0
  172. package/dist/schema/issue_comments.js.map +1 -1
  173. package/dist/schema/issues.d.ts +51 -0
  174. package/dist/schema/issues.d.ts.map +1 -1
  175. package/dist/schema/issues.js +6 -0
  176. package/dist/schema/issues.js.map +1 -1
  177. package/dist/schema/plugin_lifecycle_acknowledgments.d.ts +59 -0
  178. package/dist/schema/plugin_lifecycle_acknowledgments.d.ts.map +1 -0
  179. package/dist/schema/plugin_lifecycle_acknowledgments.js +12 -0
  180. package/dist/schema/plugin_lifecycle_acknowledgments.js.map +1 -0
  181. package/dist/schema/project_access_members.d.ts +111 -0
  182. package/dist/schema/project_access_members.d.ts.map +1 -0
  183. package/dist/schema/project_access_members.js +18 -0
  184. package/dist/schema/project_access_members.js.map +1 -0
  185. package/dist/schema/projects.d.ts +51 -0
  186. package/dist/schema/projects.d.ts.map +1 -1
  187. package/dist/schema/projects.js +9 -1
  188. package/dist/schema/projects.js.map +1 -1
  189. package/dist/schema/public_mcp.d.ts +2100 -0
  190. package/dist/schema/public_mcp.d.ts.map +1 -0
  191. package/dist/schema/public_mcp.js +191 -0
  192. package/dist/schema/public_mcp.js.map +1 -0
  193. package/dist/schema/resource_lifecycle_events.d.ts +2 -2
  194. package/dist/schema/resource_lifecycle_events.d.ts.map +1 -1
  195. package/dist/schema/resource_lifecycle_events.js +2 -1
  196. package/dist/schema/resource_lifecycle_events.js.map +1 -1
  197. package/dist/schema/status_cards.d.ts +2 -2
  198. package/dist/schema/status_cards.js +2 -2
  199. package/dist/schema/status_cards.js.map +1 -1
  200. package/dist/schema/tool_access.d.ts +18 -0
  201. package/dist/schema/tool_access.d.ts.map +1 -1
  202. package/dist/schema/tool_access.js +3 -3
  203. package/dist/schema/tool_access.js.map +1 -1
  204. package/dist/schema/user_company_preferences.d.ts +127 -0
  205. package/dist/schema/user_company_preferences.d.ts.map +1 -0
  206. package/dist/schema/user_company_preferences.js +16 -0
  207. package/dist/schema/user_company_preferences.js.map +1 -0
  208. package/dist/speko-voice-migration.test.d.ts +2 -0
  209. package/dist/speko-voice-migration.test.d.ts.map +1 -0
  210. package/dist/speko-voice-migration.test.js +88 -0
  211. package/dist/speko-voice-migration.test.js.map +1 -0
  212. package/dist/work-signals.d.ts +20 -0
  213. package/dist/work-signals.d.ts.map +1 -0
  214. package/dist/work-signals.js +123 -0
  215. package/dist/work-signals.js.map +1 -0
  216. package/dist/work-signals.test.d.ts +2 -0
  217. package/dist/work-signals.test.d.ts.map +1 -0
  218. package/dist/work-signals.test.js +177 -0
  219. package/dist/work-signals.test.js.map +1 -0
  220. package/package.json +4 -4
  221. package/dist/seed.d.ts +0 -2
  222. package/dist/seed.d.ts.map +0 -1
  223. package/dist/seed.js +0 -91
  224. package/dist/seed.js.map +0 -1
@@ -0,0 +1,265 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import fs from "node:fs";
3
+ import { afterEach, describe, expect, it } from "vitest";
4
+ import postgres from "postgres";
5
+ import { applyPendingMigrations } from "./client.js";
6
+ import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase, } from "./test-embedded-postgres.js";
7
+ const MIGRATION_FILE = "0328_private_task_access.sql";
8
+ const cleanups = [];
9
+ const embeddedPostgresSupport = await getEmbeddedPostgresTestSupport();
10
+ if (!embeddedPostgresSupport.supported)
11
+ console.warn(`Private-task migration checks unavailable: ${embeddedPostgresSupport.reason}`);
12
+ const describeEmbeddedPostgres = embeddedPostgresSupport.supported ? describe : describe.skip;
13
+ async function migrationHash() {
14
+ const content = await fs.promises.readFile(new URL(`./migrations/${MIGRATION_FILE}`, import.meta.url), "utf8");
15
+ return createHash("sha256").update(content).digest("hex");
16
+ }
17
+ afterEach(async () => {
18
+ while (cleanups.length > 0)
19
+ await cleanups.pop()?.();
20
+ });
21
+ describeEmbeddedPostgres("heartbeat run issue privacy migration", () => {
22
+ it("backfills every valid context issue binding and preserves maintenance runs", async () => {
23
+ const database = await startEmbeddedPostgresTestDatabase("paperclip-run-issue-privacy-");
24
+ cleanups.push(database.cleanup);
25
+ const sql = postgres(database.connectionString, { max: 1, onnotice: () => { } });
26
+ cleanups.push(async () => sql.end());
27
+ await sql `DELETE FROM "drizzle"."__drizzle_migrations" WHERE "hash" = ${await migrationHash()}`;
28
+ await sql `DROP TRIGGER IF EXISTS heartbeat_runs_set_scope_kind ON heartbeat_runs`;
29
+ const companyId = randomUUID();
30
+ const agentId = randomUUID();
31
+ const firstIssueId = randomUUID();
32
+ const secondIssueId = randomUUID();
33
+ const firstRunId = randomUUID();
34
+ const secondRunId = randomUUID();
35
+ const maintenanceRunId = randomUUID();
36
+ await sql `
37
+ INSERT INTO "companies" ("id", "name", "issue_prefix")
38
+ VALUES (${companyId}, 'Run Privacy', 'RPR')
39
+ `;
40
+ await sql `
41
+ INSERT INTO "agents" ("id", "company_id", "name", "role", "adapter_type", "adapter_config")
42
+ VALUES (${agentId}, ${companyId}, 'Runner', 'engineer', 'process', '{}'::jsonb)
43
+ `;
44
+ await sql `
45
+ INSERT INTO "issues" ("id", "company_id", "title", "identifier")
46
+ VALUES
47
+ (${firstIssueId}, ${companyId}, 'First private task', 'RPR-1'),
48
+ (${secondIssueId}, ${companyId}, 'Second private task', 'RPR-2')
49
+ `;
50
+ await sql `
51
+ INSERT INTO "heartbeat_runs" (
52
+ "id", "company_id", "agent_id", "status", "issue_id", "context_snapshot"
53
+ )
54
+ VALUES
55
+ (${firstRunId}, ${companyId}, ${agentId}, 'succeeded', NULL, ${sql.json({ issueId: firstIssueId })}),
56
+ (${secondRunId}, ${companyId}, ${agentId}, 'failed', NULL, ${sql.json({ taskId: secondIssueId })}),
57
+ (${maintenanceRunId}, ${companyId}, ${agentId}, 'succeeded', NULL, ${sql.json({ wakeReason: "heartbeat_timer" })})
58
+ `;
59
+ const legacyProjectId = randomUUID();
60
+ await sql `INSERT INTO projects (id, company_id, name, visibility) VALUES (${legacyProjectId}, ${companyId}, 'Legacy private project', 'private')`;
61
+ await sql `INSERT INTO activity_log (company_id, actor_type, actor_id, action, entity_type, entity_id)
62
+ VALUES (${companyId}, 'user', 'original-owner', 'project.created', 'project', ${legacyProjectId})`;
63
+ await sql `INSERT INTO project_access_members (company_id, project_id, subject_type, subject_id)
64
+ VALUES (${companyId}, ${legacyProjectId}, 'user', 'ordinary-reader')`;
65
+ const agentProjectId = randomUUID(), runProjectId = randomUUID(), preferredProjectId = randomUUID();
66
+ await sql `UPDATE heartbeat_runs SET responsible_user_id = 'run-owner' WHERE id = ${secondRunId}`;
67
+ await sql `INSERT INTO projects (id, company_id, name, visibility) VALUES
68
+ (${agentProjectId}, ${companyId}, 'Agent API project', 'private'),
69
+ (${runProjectId}, ${companyId}, 'Run-backed project', 'private'),
70
+ (${preferredProjectId}, ${companyId}, 'Recorded owner project', 'private')`;
71
+ await sql `INSERT INTO activity_log (company_id, actor_type, actor_id, responsible_user_id, run_id, action, entity_type, entity_id) VALUES
72
+ (${companyId}, 'agent', ${agentId}, 'activity-owner', NULL, 'project.created', 'project', ${agentProjectId}),
73
+ (${companyId}, 'agent', ${agentId}, NULL, ${secondRunId}, 'project.created', 'project', ${runProjectId}),
74
+ (${companyId}, 'agent', ${agentId}, 'activity-owner', ${secondRunId}, 'project.created', 'project', ${preferredProjectId})`;
75
+ await applyPendingMigrations(database.connectionString);
76
+ for (const [projectId, owner] of [[agentProjectId, 'activity-owner'], [runProjectId, 'run-owner'], [preferredProjectId, 'activity-owner']]) {
77
+ expect(await sql `SELECT privacy_owner_user_id FROM projects WHERE id = ${projectId}`).toEqual([{ privacy_owner_user_id: owner }]);
78
+ expect(await sql `SELECT subject_id FROM project_access_members WHERE project_id = ${projectId}`).toEqual([{ subject_id: owner }]);
79
+ }
80
+ const [legacyProject] = await sql `SELECT privacy_owner_user_id FROM projects WHERE id = ${legacyProjectId}`;
81
+ expect(legacyProject.privacy_owner_user_id).toBe('original-owner');
82
+ expect(await sql `SELECT subject_id FROM project_access_members WHERE project_id = ${legacyProjectId} ORDER BY subject_id`)
83
+ .toEqual([{ subject_id: 'ordinary-reader' }, { subject_id: 'original-owner' }]);
84
+ const rows = await sql `
85
+ SELECT "id", "issue_id", "scope_kind"
86
+ FROM "heartbeat_runs"
87
+ WHERE "id" IN (${firstRunId}, ${secondRunId}, ${maintenanceRunId})
88
+ ORDER BY "id"
89
+ `;
90
+ const issueByRun = new Map(rows.map((row) => [row.id, row.issue_id]));
91
+ expect(issueByRun.get(firstRunId)).toBe(firstIssueId);
92
+ expect(issueByRun.get(secondRunId)).toBe(secondIssueId);
93
+ expect(issueByRun.get(maintenanceRunId)).toBeNull();
94
+ expect(rows.find((row) => row.id === firstRunId)?.scope_kind).toBe("issue");
95
+ expect(rows.find((row) => row.id === secondRunId)?.scope_kind).toBe("issue");
96
+ expect(rows.find((row) => row.id === maintenanceRunId)?.scope_kind).toBe("company");
97
+ const [parity] = await sql `
98
+ SELECT
99
+ count(*) FILTER (WHERE "context_snapshot" ? 'issueId' OR "context_snapshot" ? 'taskId')::int AS "expected_count",
100
+ count(*) FILTER (WHERE ("context_snapshot" ? 'issueId' OR "context_snapshot" ? 'taskId') AND "issue_id" IS NOT NULL)::int AS "populated_count"
101
+ FROM "heartbeat_runs"
102
+ WHERE "id" IN (${firstRunId}, ${secondRunId}, ${maintenanceRunId})
103
+ `;
104
+ expect(parity).toEqual({ expected_count: 2, populated_count: 2 });
105
+ const [newRun] = await sql `
106
+ INSERT INTO heartbeat_runs (company_id, agent_id, status, context_snapshot)
107
+ VALUES (${companyId}, ${agentId}, 'succeeded', ${sql.json({ taskId: secondIssueId })})
108
+ RETURNING id, issue_id, scope_kind`;
109
+ expect(newRun).toMatchObject({ issue_id: secondIssueId, scope_kind: "issue" });
110
+ await sql `UPDATE heartbeat_runs SET context_snapshot = '{}'::jsonb, scope_kind = 'company' WHERE id = ${newRun.id}`;
111
+ expect(await sql `SELECT issue_id, scope_kind FROM heartbeat_runs WHERE id = ${newRun.id}`)
112
+ .toEqual([{ issue_id: secondIssueId, scope_kind: "issue" }]);
113
+ const indexes = await sql `
114
+ SELECT "indexname"
115
+ FROM "pg_indexes"
116
+ WHERE "schemaname" = 'public'
117
+ AND "indexname" = 'heartbeat_runs_company_issue_created_idx'
118
+ `;
119
+ expect(indexes).toHaveLength(1);
120
+ }, 30_000);
121
+ it("fails closed when a legacy issue claim cannot be resolved", async () => {
122
+ const database = await startEmbeddedPostgresTestDatabase("paperclip-run-issue-privacy-invalid-");
123
+ cleanups.push(database.cleanup);
124
+ const sql = postgres(database.connectionString, { max: 1, onnotice: () => { } });
125
+ cleanups.push(async () => sql.end());
126
+ await sql `DELETE FROM "drizzle"."__drizzle_migrations" WHERE "hash" = ${await migrationHash()}`;
127
+ await sql `DROP TRIGGER IF EXISTS heartbeat_runs_set_scope_kind ON heartbeat_runs`;
128
+ const companyId = randomUUID();
129
+ const otherCompanyId = randomUUID();
130
+ const agentId = randomUUID();
131
+ const foreignIssueId = randomUUID();
132
+ await sql `
133
+ INSERT INTO "companies" ("id", "name", "issue_prefix")
134
+ VALUES
135
+ (${companyId}, 'Run Privacy', 'RPI'),
136
+ (${otherCompanyId}, 'Other Company', 'RPO')
137
+ `;
138
+ await sql `
139
+ INSERT INTO "agents" ("id", "company_id", "name", "role", "adapter_type", "adapter_config")
140
+ VALUES (${agentId}, ${companyId}, 'Runner', 'engineer', 'process', '{}'::jsonb)
141
+ `;
142
+ await sql `
143
+ INSERT INTO "issues" ("id", "company_id", "title", "identifier")
144
+ VALUES (${foreignIssueId}, ${otherCompanyId}, 'Foreign task', 'RPO-1')
145
+ `;
146
+ await sql `
147
+ INSERT INTO "heartbeat_runs" ("company_id", "agent_id", "status", "context_snapshot")
148
+ VALUES
149
+ (${companyId}, ${agentId}, 'succeeded', ${sql.json({ issueId: "not-a-uuid" })}),
150
+ (${companyId}, ${agentId}, 'succeeded', ${sql.json({ issueId: randomUUID() })}),
151
+ (${companyId}, ${agentId}, 'succeeded', ${sql.json({ issueId: foreignIssueId })}),
152
+ (${companyId}, ${agentId}, 'succeeded', ${sql.json({ taskId: "not-a-uuid" })}),
153
+ (${companyId}, ${agentId}, 'succeeded', ${sql.json({ taskId: randomUUID() })}),
154
+ (${companyId}, ${agentId}, 'succeeded', ${sql.json({ taskId: foreignIssueId })}),
155
+ (${companyId}, ${agentId}, 'succeeded', ${sql.json({ taskId: null })})
156
+ `;
157
+ await applyPendingMigrations(database.connectionString);
158
+ const rows = await sql `
159
+ SELECT issue_id, scope_kind FROM heartbeat_runs WHERE company_id = ${companyId}`;
160
+ expect(rows).toHaveLength(7);
161
+ expect(rows.every(row => row.scope_kind === "issue" && row.issue_id === null)).toBe(true);
162
+ // Re-running the migration preserves fail-closed tombstones and constraints.
163
+ await sql `DELETE FROM "drizzle"."__drizzle_migrations" WHERE "hash" = ${await migrationHash()}`;
164
+ await applyPendingMigrations(database.connectionString);
165
+ }, 30_000);
166
+ it("releases schema and completed-batch row locks and resumes after interruption", async () => {
167
+ const database = await startEmbeddedPostgresTestDatabase("paperclip-privacy-batch-commits-");
168
+ cleanups.push(database.cleanup);
169
+ const sql = postgres(database.connectionString, { max: 1, onnotice: () => { } });
170
+ cleanups.push(async () => sql.end());
171
+ const companyId = randomUUID(), agentId = randomUUID(), issueId = randomUUID();
172
+ const runId = (n) => `00000000-0000-0000-0000-${String(n).padStart(12, "0")}`;
173
+ await sql `INSERT INTO companies (id, name, issue_prefix) VALUES (${companyId}, 'Batch commits', 'BCH')`;
174
+ await sql `INSERT INTO agents (id, company_id, name, adapter_type) VALUES (${agentId}, ${companyId}, 'Runner', 'process')`;
175
+ await sql `INSERT INTO issues (id, company_id, title, visibility) VALUES (${issueId}, ${companyId}, 'Private task', 'private')`;
176
+ await sql `DROP TRIGGER heartbeat_runs_set_scope_kind ON heartbeat_runs`;
177
+ await sql `INSERT INTO heartbeat_runs (id, company_id, agent_id, status, context_snapshot)
178
+ SELECT ('00000000-0000-0000-0000-' || lpad(n::text, 12, '0'))::uuid,
179
+ ${companyId}::uuid, ${agentId}::uuid, 'succeeded', ${sql.json({ taskId: issueId })}::jsonb
180
+ FROM generate_series(1, 1001) n`;
181
+ // A failed concurrent build must be repaired rather than skipped by
182
+ // CREATE INDEX IF NOT EXISTS when the migration is retried.
183
+ await sql `DROP INDEX heartbeat_runs_company_issue_created_idx`;
184
+ await expect(sql.unsafe("CREATE UNIQUE INDEX CONCURRENTLY heartbeat_runs_company_issue_created_idx ON heartbeat_runs (company_id)"))
185
+ .rejects.toThrow();
186
+ await sql.unsafe(`CREATE FUNCTION interrupt_privacy_batch_fixture() RETURNS trigger LANGUAGE plpgsql AS $$
187
+ BEGIN
188
+ PERFORM pg_advisory_xact_lock(736319937);
189
+ RAISE EXCEPTION 'fixture interrupted second batch';
190
+ END $$`);
191
+ await sql.unsafe(`CREATE TRIGGER zzz_interrupt_privacy_batch_fixture BEFORE UPDATE ON heartbeat_runs
192
+ FOR EACH ROW WHEN (OLD.scope_kind = 'company' AND NEW.id = '${runId(1001)}'::uuid)
193
+ EXECUTE FUNCTION interrupt_privacy_batch_fixture()`);
194
+ const hash = await migrationHash();
195
+ await sql `DELETE FROM drizzle.__drizzle_migrations WHERE hash = ${hash}`;
196
+ await sql `SELECT pg_advisory_lock(736319937)`;
197
+ const migration = applyPendingMigrations(database.connectionString).then(() => null, error => error);
198
+ let contender;
199
+ try {
200
+ // The second batch is blocked while another session can see the first
201
+ // committed batch and write a row whose lock has already been released.
202
+ await expect.poll(async () => {
203
+ const [row] = await sql `SELECT count(*)::int AS n FROM heartbeat_runs
204
+ WHERE company_id = ${companyId} AND scope_kind = 'issue'`;
205
+ return row.n;
206
+ }, { timeout: 10_000 }).toBe(1000);
207
+ // The session lock survives batch commits and blocks another migrator.
208
+ contender = applyPendingMigrations(database.connectionString).then(() => null, error => error);
209
+ await expect.poll(async () => {
210
+ const [row] = await sql `SELECT count(*)::int AS n FROM pg_stat_activity
211
+ WHERE datname = current_database() AND query LIKE '%SELECT pg_try_advisory_lock%'
212
+ AND query NOT LIKE '%FROM pg_stat_activity%'
213
+ AND strpos(query, 'paperclip:migrations') > 0`;
214
+ return row.n;
215
+ }, { timeout: 10_000 }).toBe(1);
216
+ expect(await sql `SELECT hash FROM drizzle.__drizzle_migrations WHERE hash = ${hash}`).toHaveLength(0);
217
+ await sql `SET lock_timeout = '500ms'`;
218
+ await sql `UPDATE heartbeat_runs SET context_snapshot = context_snapshot || '{"probe":"visible"}'::jsonb
219
+ WHERE id = ${runId(1)}`;
220
+ expect(await sql `SELECT context_snapshot->>'probe' AS probe FROM heartbeat_runs WHERE id = ${runId(1)}`)
221
+ .toEqual([{ probe: "visible" }]);
222
+ }
223
+ finally {
224
+ await sql `SELECT pg_advisory_unlock(736319937)`;
225
+ await sql `RESET lock_timeout`;
226
+ await migration;
227
+ await contender;
228
+ }
229
+ expect(await migration).toMatchObject({ message: "fixture interrupted second batch" });
230
+ expect(await contender).toMatchObject({ message: "fixture interrupted second batch" });
231
+ expect(await sql `SELECT hash FROM drizzle.__drizzle_migrations WHERE hash = ${hash}`).toHaveLength(0);
232
+ await sql `DROP TRIGGER zzz_interrupt_privacy_batch_fixture ON heartbeat_runs`;
233
+ await sql `DROP FUNCTION interrupt_privacy_batch_fixture()`;
234
+ await Promise.all([applyPendingMigrations(database.connectionString), applyPendingMigrations(database.connectionString)]);
235
+ const [finished] = await sql `SELECT count(*)::int AS n FROM heartbeat_runs
236
+ WHERE company_id = ${companyId} AND scope_kind = 'issue' AND issue_id = ${issueId}`;
237
+ expect(finished.n).toBe(1001);
238
+ expect(await sql `SELECT indisvalid FROM pg_index WHERE indexrelid = 'heartbeat_runs_company_issue_created_idx'::regclass`)
239
+ .toEqual([{ indisvalid: true }]);
240
+ expect(await sql `SELECT hash FROM drizzle.__drizzle_migrations WHERE hash = ${hash}`).toHaveLength(1);
241
+ }, 30_000);
242
+ it("keeps all privacy triggers installed when replacement is interrupted", async () => {
243
+ const database = await startEmbeddedPostgresTestDatabase("paperclip-privacy-trigger-atomic-");
244
+ cleanups.push(database.cleanup);
245
+ const sql = postgres(database.connectionString, { max: 1, onnotice: () => { } });
246
+ cleanups.push(async () => sql.end());
247
+ const content = await fs.promises.readFile(new URL(`./migrations/${MIGRATION_FILE}`, import.meta.url), "utf8");
248
+ const replacements = content.split("--> statement-breakpoint").filter(statement => /DROP TRIGGER IF EXISTS/.test(statement));
249
+ expect(replacements).toHaveLength(4);
250
+ for (const statement of replacements) {
251
+ const name = statement.match(/DROP TRIGGER IF EXISTS ([A-Za-z_]+)/)[1];
252
+ const interrupted = statement.replace("CREATE TRIGGER", "RAISE EXCEPTION 'fixture interrupted trigger replacement';\n CREATE TRIGGER");
253
+ await expect(sql.unsafe(interrupted)).rejects.toThrow("fixture interrupted trigger replacement");
254
+ expect(await sql `SELECT tgname FROM pg_trigger WHERE tgname = ${name} AND NOT tgisinternal`).toEqual([{ tgname: name }]);
255
+ }
256
+ const companyId = randomUUID(), agentId = randomUUID(), issueId = randomUUID();
257
+ await sql `INSERT INTO companies (id, name, issue_prefix) VALUES (${companyId}, 'Atomic triggers', 'ATR')`;
258
+ await sql `INSERT INTO agents (id, company_id, name, adapter_type) VALUES (${agentId}, ${companyId}, 'Runner', 'process')`;
259
+ await sql `INSERT INTO issues (id, company_id, title, visibility) VALUES (${issueId}, ${companyId}, 'Private', 'private')`;
260
+ expect(await sql `INSERT INTO heartbeat_runs (company_id, agent_id, status, context_snapshot)
261
+ VALUES (${companyId}, ${agentId}, 'succeeded', ${sql.json({ taskId: issueId })}) RETURNING issue_id, scope_kind`)
262
+ .toEqual([{ issue_id: issueId, scope_kind: "issue" }]);
263
+ }, 30_000);
264
+ });
265
+ //# sourceMappingURL=heartbeat-run-issue-privacy-migration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heartbeat-run-issue-privacy-migration.test.js","sourceRoot":"","sources":["../src/heartbeat-run-issue-privacy-migration.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EACL,8BAA8B,EAC9B,iCAAiC,GAClC,MAAM,6BAA6B,CAAC;AAErC,MAAM,cAAc,GAAG,8BAA8B,CAAC;AACtD,MAAM,QAAQ,GAA+B,EAAE,CAAC;AAChD,MAAM,uBAAuB,GAAG,MAAM,8BAA8B,EAAE,CAAC;AACvE,IAAI,CAAC,uBAAuB,CAAC,SAAS;IAAE,OAAO,CAAC,IAAI,CAAC,8CAA8C,uBAAuB,CAAC,MAAM,EAAE,CAAC,CAAC;AACrI,MAAM,wBAAwB,GAAG,uBAAuB,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;AAE9F,KAAK,UAAU,aAAa;IAC1B,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,gBAAgB,cAAc,EAAE,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;IAC/G,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,CAAC,KAAK,IAAI,EAAE;IACnB,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,QAAQ,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC;AACvD,CAAC,CAAC,CAAC;AAEH,wBAAwB,CAAC,uCAAuC,EAAE,GAAG,EAAE;IACrE,EAAE,CAAC,4EAA4E,EAAE,KAAK,IAAI,EAAE;QAC1F,MAAM,QAAQ,GAAG,MAAM,iCAAiC,CAAC,8BAA8B,CAAC,CAAC;QACzF,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChC,MAAM,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAC,CAAC;QAChF,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;QAErC,MAAM,GAAG,CAAA,+DAA+D,MAAM,aAAa,EAAE,EAAE,CAAC;QAEhG,MAAM,GAAG,CAAA,wEAAwE,CAAC;QAClF,MAAM,SAAS,GAAG,UAAU,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;QAC7B,MAAM,YAAY,GAAG,UAAU,EAAE,CAAC;QAClC,MAAM,aAAa,GAAG,UAAU,EAAE,CAAC;QACnC,MAAM,UAAU,GAAG,UAAU,EAAE,CAAC;QAChC,MAAM,WAAW,GAAG,UAAU,EAAE,CAAC;QACjC,MAAM,gBAAgB,GAAG,UAAU,EAAE,CAAC;QAEtC,MAAM,GAAG,CAAA;;gBAEG,SAAS;KACpB,CAAC;QACF,MAAM,GAAG,CAAA;;gBAEG,OAAO,KAAK,SAAS;KAChC,CAAC;QACF,MAAM,GAAG,CAAA;;;WAGF,YAAY,KAAK,SAAS;WAC1B,aAAa,KAAK,SAAS;KACjC,CAAC;QACF,MAAM,GAAG,CAAA;;;;;WAKF,UAAU,KAAK,SAAS,KAAK,OAAO,wBAAwB,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;WAC/F,WAAW,KAAK,SAAS,KAAK,OAAO,qBAAqB,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;WAC7F,gBAAgB,KAAK,SAAS,KAAK,OAAO,wBAAwB,GAAG,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,iBAAiB,EAAE,CAAC;KACnH,CAAC;QAEF,MAAM,eAAe,GAAG,UAAU,EAAE,CAAC;QACrC,MAAM,GAAG,CAAA,mEAAmE,eAAe,KAAK,SAAS,wCAAwC,CAAC;QAClJ,MAAM,GAAG,CAAA;gBACG,SAAS,6DAA6D,eAAe,GAAG,CAAC;QACrG,MAAM,GAAG,CAAA;gBACG,SAAS,KAAK,eAAe,8BAA8B,CAAC;QAExE,MAAM,cAAc,GAAG,UAAU,EAAE,EAAE,YAAY,GAAG,UAAU,EAAE,EAAE,kBAAkB,GAAG,UAAU,EAAE,CAAC;QACpG,MAAM,GAAG,CAAA,0EAA0E,WAAW,EAAE,CAAC;QACjG,MAAM,GAAG,CAAA;SACJ,cAAc,KAAK,SAAS;SAC5B,YAAY,KAAK,SAAS;SAC1B,kBAAkB,KAAK,SAAS,wCAAwC,CAAC;QAC9E,MAAM,GAAG,CAAA;SACJ,SAAS,cAAc,OAAO,2DAA2D,cAAc;SACvG,SAAS,cAAc,OAAO,WAAW,WAAW,mCAAmC,YAAY;SACnG,SAAS,cAAc,OAAO,uBAAuB,WAAW,mCAAmC,kBAAkB,GAAG,CAAC;QAE9H,MAAM,sBAAsB,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;QACxD,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,cAAc,EAAE,gBAAgB,CAAC,EAAE,CAAC,YAAY,EAAE,WAAW,CAAC,EAAE,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC;YAC3I,MAAM,CAAC,MAAM,GAAG,CAAA,yDAAyD,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,qBAAqB,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;YAClI,MAAM,CAAC,MAAM,GAAG,CAAA,oEAAoE,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACpI,CAAC;QACD,MAAM,CAAC,aAAa,CAAC,GAAG,MAAM,GAAG,CAAA,yDAAyD,eAAe,EAAE,CAAC;QAC5G,MAAM,CAAC,aAAa,CAAC,qBAAqB,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACnE,MAAM,CAAC,MAAM,GAAG,CAAA,oEAAoE,eAAe,sBAAsB,CAAC;aACvH,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,iBAAiB,EAAE,EAAE,EAAE,UAAU,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC;QAGlF,MAAM,IAAI,GAAG,MAAM,GAAG,CAA+D;;;uBAGlE,UAAU,KAAK,WAAW,KAAK,gBAAgB;;KAEjE,CAAC;QACF,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACtE,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACtD,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACxD,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACpD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,UAAU,CAAC,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5E,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,WAAW,CAAC,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7E,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,gBAAgB,CAAC,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAEpF,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,CAAuD;;;;;uBAK9D,UAAU,KAAK,WAAW,KAAK,gBAAgB;KACjE,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,CAAC,CAAC;QAElE,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,CAA+D;;gBAE7E,SAAS,KAAK,OAAO,kBAAkB,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;yCACjD,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC;QAC/E,MAAM,GAAG,CAAA,+FAA+F,MAAM,CAAC,EAAE,EAAE,CAAC;QACpH,MAAM,CAAC,MAAM,GAAG,CAAA,8DAA8D,MAAM,CAAC,EAAE,EAAE,CAAC;aACvF,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QAE/D,MAAM,OAAO,GAAG,MAAM,GAAG,CAAyB;;;;;KAKjD,CAAC;QACF,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC,EAAE,MAAM,CAAC,CAAC;IAEX,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QACzE,MAAM,QAAQ,GAAG,MAAM,iCAAiC,CAAC,sCAAsC,CAAC,CAAC;QACjG,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChC,MAAM,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAC,CAAC;QAChF,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;QAErC,MAAM,GAAG,CAAA,+DAA+D,MAAM,aAAa,EAAE,EAAE,CAAC;QAEhG,MAAM,GAAG,CAAA,wEAAwE,CAAC;QAClF,MAAM,SAAS,GAAG,UAAU,EAAE,CAAC;QAC/B,MAAM,cAAc,GAAG,UAAU,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;QAC7B,MAAM,cAAc,GAAG,UAAU,EAAE,CAAC;QACpC,MAAM,GAAG,CAAA;;;WAGF,SAAS;WACT,cAAc;KACpB,CAAC;QACF,MAAM,GAAG,CAAA;;gBAEG,OAAO,KAAK,SAAS;KAChC,CAAC;QACF,MAAM,GAAG,CAAA;;gBAEG,cAAc,KAAK,cAAc;KAC5C,CAAC;QACF,MAAM,GAAG,CAAA;;;WAGF,SAAS,KAAK,OAAO,kBAAkB,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;WAC1E,SAAS,KAAK,OAAO,kBAAkB,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,CAAC;WAC1E,SAAS,KAAK,OAAO,kBAAkB,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC;WAC5E,SAAS,KAAK,OAAO,kBAAkB,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;WACzE,SAAS,KAAK,OAAO,kBAAkB,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,CAAC;WACzE,SAAS,KAAK,OAAO,kBAAkB,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;WAC3E,SAAS,KAAK,OAAO,kBAAkB,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;KACvE,CAAC;QAEF,MAAM,sBAAsB,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAmD;2EACF,SAAS,EAAE,CAAC;QACnF,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1F,6EAA6E;QAC7E,MAAM,GAAG,CAAA,+DAA+D,MAAM,aAAa,EAAE,EAAE,CAAC;QAChG,MAAM,sBAAsB,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAC1D,CAAC,EAAE,MAAM,CAAC,CAAC;IAEX,EAAE,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;QAC5F,MAAM,QAAQ,GAAG,MAAM,iCAAiC,CAAC,kCAAkC,CAAC,CAAC;QAC7F,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChC,MAAM,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAC,CAAC;QAChF,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;QACrC,MAAM,SAAS,GAAG,UAAU,EAAE,EAAE,OAAO,GAAG,UAAU,EAAE,EAAE,OAAO,GAAG,UAAU,EAAE,CAAC;QAC/E,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,2BAA2B,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC;QACtF,MAAM,GAAG,CAAA,0DAA0D,SAAS,2BAA2B,CAAC;QACxG,MAAM,GAAG,CAAA,mEAAmE,OAAO,KAAK,SAAS,wBAAwB,CAAC;QAC1H,MAAM,GAAG,CAAA,kEAAkE,OAAO,KAAK,SAAS,8BAA8B,CAAC;QAC/H,MAAM,GAAG,CAAA,8DAA8D,CAAC;QACxE,MAAM,GAAG,CAAA;;UAEH,SAAS,WAAW,OAAO,wBAAwB,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;sCACpD,CAAC;QACnC,oEAAoE;QACpE,4DAA4D;QAC5D,MAAM,GAAG,CAAA,qDAAqD,CAAC;QAC/D,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,0GAA0G,CAAC,CAAC;aACjI,OAAO,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,GAAG,CAAC,MAAM,CAAC;;;;aAIR,CAAC,CAAC;QACX,MAAM,GAAG,CAAC,MAAM,CAAC;oEAC+C,KAAK,CAAC,IAAI,CAAC;yDACtB,CAAC,CAAC;QACvD,MAAM,IAAI,GAAG,MAAM,aAAa,EAAE,CAAC;QACnC,MAAM,GAAG,CAAA,yDAAyD,IAAI,EAAE,CAAC;QACzE,MAAM,GAAG,CAAA,oCAAoC,CAAC;QAC9C,MAAM,SAAS,GAAG,sBAAsB,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;QACrG,IAAI,SAAuC,CAAC;QAC5C,IAAI,CAAC;YACH,sEAAsE;YACtE,wEAAwE;YACxE,MAAM,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;gBAC3B,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,GAAG,CAAA;+BACA,SAAS,2BAA2B,CAAC;gBAC5D,OAAO,GAAG,CAAC,CAAC,CAAC;YACf,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnC,uEAAuE;YACvE,SAAS,GAAG,sBAAsB,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;YAC/F,MAAM,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;gBAC3B,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,GAAG,CAAA;;;0DAG2B,CAAC;gBACnD,OAAO,GAAG,CAAC,CAAC,CAAC;YACf,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChC,MAAM,CAAC,MAAM,GAAG,CAAA,8DAA8D,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACtG,MAAM,GAAG,CAAA,4BAA4B,CAAC;YACtC,MAAM,GAAG,CAAA;qBACM,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1B,MAAM,CAAC,MAAM,GAAG,CAAA,6EAA6E,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;iBACrG,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;QACrC,CAAC;gBAAS,CAAC;YACT,MAAM,GAAG,CAAA,sCAAsC,CAAC;YAChD,MAAM,GAAG,CAAA,oBAAoB,CAAC;YAC9B,MAAM,SAAS,CAAC;YAChB,MAAM,SAAS,CAAC;QAClB,CAAC;QACD,MAAM,CAAC,MAAM,SAAS,CAAC,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,kCAAkC,EAAE,CAAC,CAAC;QACvF,MAAM,CAAC,MAAM,SAAS,CAAC,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,kCAAkC,EAAE,CAAC,CAAC;QACvF,MAAM,CAAC,MAAM,GAAG,CAAA,8DAA8D,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACtG,MAAM,GAAG,CAAA,oEAAoE,CAAC;QAC9E,MAAM,GAAG,CAAA,iDAAiD,CAAC;QAC3D,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,sBAAsB,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,sBAAsB,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;QAC1H,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,GAAG,CAAA;2BACL,SAAS,4CAA4C,OAAO,EAAE,CAAC;QACtF,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9B,MAAM,CAAC,MAAM,GAAG,CAAA,yGAAyG,CAAC;aACvH,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,MAAM,GAAG,CAAA,8DAA8D,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACxG,CAAC,EAAE,MAAM,CAAC,CAAC;IAEX,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,QAAQ,GAAG,MAAM,iCAAiC,CAAC,mCAAmC,CAAC,CAAC;QAC9F,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChC,MAAM,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAC,CAAC;QAChF,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;QACrC,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,gBAAgB,cAAc,EAAE,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;QAC/G,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,wBAAwB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QAC7H,MAAM,CAAC,YAAY,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACrC,KAAK,MAAM,SAAS,IAAI,YAAY,EAAE,CAAC;YACrC,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,qCAAqC,CAAE,CAAC,CAAC,CAAC,CAAC;YACxE,MAAM,WAAW,GAAG,SAAS,CAAC,OAAO,CAAC,gBAAgB,EAAE,8EAA8E,CAAC,CAAC;YACxI,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,yCAAyC,CAAC,CAAC;YACjG,MAAM,CAAC,MAAM,GAAG,CAAA,gDAAgD,IAAI,uBAAuB,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC3H,CAAC;QACD,MAAM,SAAS,GAAG,UAAU,EAAE,EAAE,OAAO,GAAG,UAAU,EAAE,EAAE,OAAO,GAAG,UAAU,EAAE,CAAC;QAC/E,MAAM,GAAG,CAAA,0DAA0D,SAAS,6BAA6B,CAAC;QAC1G,MAAM,GAAG,CAAA,mEAAmE,OAAO,KAAK,SAAS,wBAAwB,CAAC;QAC1H,MAAM,GAAG,CAAA,kEAAkE,OAAO,KAAK,SAAS,yBAAyB,CAAC;QAC1H,MAAM,CAAC,MAAM,GAAG,CAAA;gBACJ,SAAS,KAAK,OAAO,kBAAkB,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,kCAAkC,CAAC;aAChH,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IAC3D,CAAC,EAAE,MAAM,CAAC,CAAC;AAEb,CAAC,CAAC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -7,4 +7,5 @@ export { loadWithoutEmbeddedPostgresExitHooks } from "./embedded-postgres-lifecy
7
7
  export { issueRelations } from "./schema/issue_relations.js";
8
8
  export { issueReferenceMentions } from "./schema/issue_reference_mentions.js";
9
9
  export * from "./schema/index.js";
10
+ export { signalDatabaseWork, subscribeDatabaseWork, databaseWorkPending, reconcileDatabaseWork } from "./work-signals.js";
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,sBAAsB,EACtB,gCAAgC,EAChC,KAAK,cAAc,EACnB,KAAK,+BAA+B,EACpC,sBAAsB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,EAAE,GACR,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,8BAA8B,EAC9B,iCAAiC,EACjC,iCAAiC,EACjC,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,GACjC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,0BAA0B,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,yBAAyB,GAC/B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,+BAA+B,EAC/B,2BAA2B,GAC5B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,+BAA+B,EAC/B,oCAAoC,GACrC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,oCAAoC,EAAE,MAAM,kCAAkC,CAAC;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAC9E,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,sBAAsB,EACtB,gCAAgC,EAChC,KAAK,cAAc,EACnB,KAAK,+BAA+B,EACpC,sBAAsB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,EAAE,GACR,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,8BAA8B,EAC9B,iCAAiC,EACjC,iCAAiC,EACjC,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,GACjC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,0BAA0B,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,yBAAyB,GAC/B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,+BAA+B,EAC/B,2BAA2B,GAC5B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,+BAA+B,EAC/B,oCAAoC,GACrC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,oCAAoC,EAAE,MAAM,kCAAkC,CAAC;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAC9E,cAAc,mBAAmB,CAAC;AAElC,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -7,4 +7,5 @@ export { loadWithoutEmbeddedPostgresExitHooks } from "./embedded-postgres-lifecy
7
7
  export { issueRelations } from "./schema/issue_relations.js";
8
8
  export { issueReferenceMentions } from "./schema/issue_reference_mentions.js";
9
9
  export * from "./schema/index.js";
10
+ export { signalDatabaseWork, subscribeDatabaseWork, databaseWorkPending, reconcileDatabaseWork } from "./work-signals.js";
10
11
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,sBAAsB,EACtB,gCAAgC,EAGhC,sBAAsB,GAGvB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,8BAA8B,EAC9B,iCAAiC,EACjC,iCAAiC,GAGlC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,0BAA0B,GAK3B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,+BAA+B,EAC/B,2BAA2B,GAC5B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,+BAA+B,EAC/B,oCAAoC,GACrC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,oCAAoC,EAAE,MAAM,kCAAkC,CAAC;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAC9E,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,sBAAsB,EACtB,gCAAgC,EAGhC,sBAAsB,GAGvB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,8BAA8B,EAC9B,iCAAiC,EACjC,iCAAiC,GAGlC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,0BAA0B,GAK3B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,+BAA+B,EAC/B,2BAA2B,GAC5B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,+BAA+B,EAC/B,oCAAoC,GACrC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,oCAAoC,EAAE,MAAM,kCAAkC,CAAC;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAC9E,cAAc,mBAAmB,CAAC;AAElC,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,3 @@
1
+ -- Older JavaScript worktree backups may omit CHECK constraints.
2
+ ALTER TABLE "resource_lifecycle_events" DROP CONSTRAINT IF EXISTS "resource_lifecycle_events_action_check";--> statement-breakpoint
3
+ ALTER TABLE "resource_lifecycle_events" ADD CONSTRAINT "resource_lifecycle_events_action_check" CHECK ("resource_lifecycle_events"."action" = 'create' OR ("resource_lifecycle_events"."resource_type" = 'project' AND "resource_lifecycle_events"."action" = 'update') OR ("resource_lifecycle_events"."resource_type" = 'agent' AND "resource_lifecycle_events"."action" IN ('pause', 'resume', 'terminate')));
@@ -0,0 +1,157 @@
1
+ CREATE TABLE IF NOT EXISTS "mcp_mutation_receipts" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "company_id" uuid NOT NULL,
4
+ "user_id" text NOT NULL,
5
+ "grant_id" uuid NOT NULL,
6
+ "operation" text NOT NULL,
7
+ "request_id" uuid NOT NULL,
8
+ "arguments_hash" text NOT NULL,
9
+ "status" text DEFAULT 'reserved' NOT NULL,
10
+ "result" jsonb,
11
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
12
+ );
13
+ --> statement-breakpoint
14
+ CREATE TABLE IF NOT EXISTS "mcp_oauth_clients" (
15
+ "id" text PRIMARY KEY NOT NULL,
16
+ "name" text NOT NULL,
17
+ "redirect_uris" jsonb NOT NULL,
18
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
19
+ );
20
+ --> statement-breakpoint
21
+ CREATE TABLE IF NOT EXISTS "mcp_oauth_grants" (
22
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
23
+ "company_id" uuid NOT NULL,
24
+ "user_id" text NOT NULL,
25
+ "client_id" text NOT NULL,
26
+ "resource" text NOT NULL,
27
+ "scopes" jsonb NOT NULL,
28
+ "revoked_at" timestamp with time zone,
29
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
30
+ );
31
+ --> statement-breakpoint
32
+ CREATE TABLE IF NOT EXISTS "mcp_oauth_requests" (
33
+ "id" text PRIMARY KEY NOT NULL,
34
+ "client_id" text NOT NULL,
35
+ "redirect_uri" text NOT NULL,
36
+ "resource" text NOT NULL,
37
+ "scopes" jsonb NOT NULL,
38
+ "state" text,
39
+ "challenge" text NOT NULL,
40
+ "grant_id" uuid,
41
+ "code_hash" text,
42
+ "decided_at" timestamp with time zone,
43
+ "consumed_at" timestamp with time zone,
44
+ "expires_at" timestamp with time zone NOT NULL,
45
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
46
+ );
47
+ --> statement-breakpoint
48
+ CREATE TABLE IF NOT EXISTS "mcp_oauth_tokens" (
49
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
50
+ "grant_id" uuid NOT NULL,
51
+ "token_hash" text NOT NULL,
52
+ "kind" text NOT NULL,
53
+ "used_at" timestamp with time zone,
54
+ "expires_at" timestamp with time zone NOT NULL,
55
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
56
+ );
57
+ --> statement-breakpoint
58
+ DO $$ BEGIN
59
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'mcp_mutation_receipts_company_id_companies_id_fk' AND conrelid = 'public.mcp_mutation_receipts'::regclass) THEN
60
+ ALTER TABLE "mcp_mutation_receipts" ADD CONSTRAINT "mcp_mutation_receipts_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action;
61
+ END IF;
62
+ END $$;--> statement-breakpoint
63
+ DO $$ BEGIN
64
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'mcp_mutation_receipts_user_id_user_id_fk' AND conrelid = 'public.mcp_mutation_receipts'::regclass) THEN
65
+ ALTER TABLE "mcp_mutation_receipts" ADD CONSTRAINT "mcp_mutation_receipts_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
66
+ END IF;
67
+ END $$;--> statement-breakpoint
68
+ DO $$ BEGIN
69
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'mcp_mutation_receipts_grant_id_mcp_oauth_grants_id_fk' AND conrelid = 'public.mcp_mutation_receipts'::regclass) THEN
70
+ ALTER TABLE "mcp_mutation_receipts" ADD CONSTRAINT "mcp_mutation_receipts_grant_id_mcp_oauth_grants_id_fk" FOREIGN KEY ("grant_id") REFERENCES "public"."mcp_oauth_grants"("id") ON DELETE cascade ON UPDATE no action;
71
+ END IF;
72
+ END $$;--> statement-breakpoint
73
+ DO $$ BEGIN
74
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'mcp_oauth_grants_company_id_companies_id_fk' AND conrelid = 'public.mcp_oauth_grants'::regclass) THEN
75
+ ALTER TABLE "mcp_oauth_grants" ADD CONSTRAINT "mcp_oauth_grants_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action;
76
+ END IF;
77
+ END $$;--> statement-breakpoint
78
+ DO $$ BEGIN
79
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'mcp_oauth_grants_user_id_user_id_fk' AND conrelid = 'public.mcp_oauth_grants'::regclass) THEN
80
+ ALTER TABLE "mcp_oauth_grants" ADD CONSTRAINT "mcp_oauth_grants_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
81
+ END IF;
82
+ END $$;--> statement-breakpoint
83
+ DO $$ BEGIN
84
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'mcp_oauth_grants_client_id_mcp_oauth_clients_id_fk' AND conrelid = 'public.mcp_oauth_grants'::regclass) THEN
85
+ ALTER TABLE "mcp_oauth_grants" ADD CONSTRAINT "mcp_oauth_grants_client_id_mcp_oauth_clients_id_fk" FOREIGN KEY ("client_id") REFERENCES "public"."mcp_oauth_clients"("id") ON DELETE cascade ON UPDATE no action;
86
+ END IF;
87
+ END $$;--> statement-breakpoint
88
+ DO $$ BEGIN
89
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'mcp_oauth_requests_client_id_mcp_oauth_clients_id_fk' AND conrelid = 'public.mcp_oauth_requests'::regclass) THEN
90
+ ALTER TABLE "mcp_oauth_requests" ADD CONSTRAINT "mcp_oauth_requests_client_id_mcp_oauth_clients_id_fk" FOREIGN KEY ("client_id") REFERENCES "public"."mcp_oauth_clients"("id") ON DELETE cascade ON UPDATE no action;
91
+ END IF;
92
+ END $$;--> statement-breakpoint
93
+ DO $$ BEGIN
94
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'mcp_oauth_requests_grant_id_mcp_oauth_grants_id_fk' AND conrelid = 'public.mcp_oauth_requests'::regclass) THEN
95
+ ALTER TABLE "mcp_oauth_requests" ADD CONSTRAINT "mcp_oauth_requests_grant_id_mcp_oauth_grants_id_fk" FOREIGN KEY ("grant_id") REFERENCES "public"."mcp_oauth_grants"("id") ON DELETE cascade ON UPDATE no action;
96
+ END IF;
97
+ END $$;--> statement-breakpoint
98
+ DO $$ BEGIN
99
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'mcp_oauth_tokens_grant_id_mcp_oauth_grants_id_fk' AND conrelid = 'public.mcp_oauth_tokens'::regclass) THEN
100
+ ALTER TABLE "mcp_oauth_tokens" ADD CONSTRAINT "mcp_oauth_tokens_grant_id_mcp_oauth_grants_id_fk" FOREIGN KEY ("grant_id") REFERENCES "public"."mcp_oauth_grants"("id") ON DELETE cascade ON UPDATE no action;
101
+ END IF;
102
+ END $$;--> statement-breakpoint
103
+ CREATE UNIQUE INDEX IF NOT EXISTS "mcp_mutation_receipts_request_uq" ON "mcp_mutation_receipts" USING btree ("company_id","user_id","operation","request_id");--> statement-breakpoint
104
+ CREATE INDEX IF NOT EXISTS "mcp_mutation_receipts_company_idx" ON "mcp_mutation_receipts" USING btree ("company_id");--> statement-breakpoint
105
+ CREATE INDEX IF NOT EXISTS "mcp_oauth_grants_user_company_idx" ON "mcp_oauth_grants" USING btree ("user_id","company_id");--> statement-breakpoint
106
+ CREATE UNIQUE INDEX IF NOT EXISTS "mcp_oauth_requests_code_uq" ON "mcp_oauth_requests" USING btree ("code_hash");--> statement-breakpoint
107
+ CREATE INDEX IF NOT EXISTS "mcp_oauth_requests_expiry_idx" ON "mcp_oauth_requests" USING btree ("expires_at");--> statement-breakpoint
108
+ CREATE UNIQUE INDEX IF NOT EXISTS "mcp_oauth_tokens_hash_uq" ON "mcp_oauth_tokens" USING btree ("token_hash");--> statement-breakpoint
109
+ CREATE INDEX IF NOT EXISTS "mcp_oauth_tokens_grant_idx" ON "mcp_oauth_tokens" USING btree ("grant_id");--> statement-breakpoint
110
+ CREATE INDEX IF NOT EXISTS "mcp_oauth_tokens_expiry_idx" ON "mcp_oauth_tokens" USING btree ("expires_at");
111
+
112
+ --> statement-breakpoint
113
+ ALTER TABLE "mcp_oauth_clients" ADD COLUMN IF NOT EXISTS "registration_source_hash" text;
114
+
115
+ --> statement-breakpoint
116
+ CREATE TABLE IF NOT EXISTS "mcp_event_deliveries" (
117
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
118
+ "subscription_id" text NOT NULL,
119
+ "activity_id" uuid NOT NULL,
120
+ "event" jsonb NOT NULL,
121
+ "attempts" integer DEFAULT 0 NOT NULL,
122
+ "next_attempt_at" timestamp with time zone DEFAULT now() NOT NULL,
123
+ "finished_at" timestamp with time zone,
124
+ "outcome" text
125
+ );
126
+ --> statement-breakpoint
127
+ CREATE TABLE IF NOT EXISTS "mcp_event_subscriptions" (
128
+ "id" text PRIMARY KEY NOT NULL,
129
+ "company_id" uuid NOT NULL,
130
+ "grant_id" uuid NOT NULL,
131
+ "name" text NOT NULL,
132
+ "task_id" uuid NOT NULL,
133
+ "arguments" jsonb NOT NULL,
134
+ "delivery_material" jsonb NOT NULL,
135
+ "verified_at" timestamp with time zone NOT NULL,
136
+ "starts_at" timestamp with time zone DEFAULT now() NOT NULL,
137
+ "expires_at" timestamp with time zone NOT NULL,
138
+ "stopped_at" timestamp with time zone,
139
+ "scanned_at" timestamp with time zone DEFAULT now() NOT NULL
140
+ );
141
+ --> statement-breakpoint
142
+ DO $$ BEGIN
143
+ ALTER TABLE "mcp_event_deliveries" ADD CONSTRAINT "mcp_event_deliveries_subscription_id_mcp_event_subscriptions_id_fk" FOREIGN KEY ("subscription_id") REFERENCES "public"."mcp_event_subscriptions"("id") ON DELETE cascade ON UPDATE no action;
144
+ EXCEPTION WHEN duplicate_object THEN NULL;
145
+ END $$;--> statement-breakpoint
146
+ DO $$ BEGIN
147
+ ALTER TABLE "mcp_event_subscriptions" ADD CONSTRAINT "mcp_event_subscriptions_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action;
148
+ EXCEPTION WHEN duplicate_object THEN NULL;
149
+ END $$;--> statement-breakpoint
150
+ DO $$ BEGIN
151
+ ALTER TABLE "mcp_event_subscriptions" ADD CONSTRAINT "mcp_event_subscriptions_grant_id_mcp_oauth_grants_id_fk" FOREIGN KEY ("grant_id") REFERENCES "public"."mcp_oauth_grants"("id") ON DELETE cascade ON UPDATE no action;
152
+ EXCEPTION WHEN duplicate_object THEN NULL;
153
+ END $$;--> statement-breakpoint
154
+ CREATE UNIQUE INDEX IF NOT EXISTS "mcp_event_deliveries_activity_uq" ON "mcp_event_deliveries" USING btree ("subscription_id","activity_id");--> statement-breakpoint
155
+ CREATE INDEX IF NOT EXISTS "mcp_event_deliveries_due_idx" ON "mcp_event_deliveries" USING btree ("next_attempt_at");--> statement-breakpoint
156
+ CREATE INDEX IF NOT EXISTS "mcp_event_subscriptions_expiry_idx" ON "mcp_event_subscriptions" USING btree ("expires_at");--> statement-breakpoint
157
+ CREATE INDEX IF NOT EXISTS "mcp_event_subscriptions_company_idx" ON "mcp_event_subscriptions" USING btree ("company_id");
@@ -0,0 +1,24 @@
1
+ CREATE TABLE IF NOT EXISTS "mcp_event_admissions" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "subscription_id" text NOT NULL,
4
+ "company_id" uuid NOT NULL,
5
+ "grant_id" uuid NOT NULL,
6
+ "reserves_subscription" boolean NOT NULL,
7
+ "expires_at" timestamp with time zone NOT NULL,
8
+ "finished_at" timestamp with time zone,
9
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
10
+ );
11
+ --> statement-breakpoint
12
+ DO $$ BEGIN
13
+ ALTER TABLE "mcp_event_admissions" ADD CONSTRAINT "mcp_event_admissions_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action;
14
+ EXCEPTION WHEN duplicate_object THEN NULL;
15
+ END $$;
16
+ --> statement-breakpoint
17
+ DO $$ BEGIN
18
+ ALTER TABLE "mcp_event_admissions" ADD CONSTRAINT "mcp_event_admissions_grant_id_mcp_oauth_grants_id_fk" FOREIGN KEY ("grant_id") REFERENCES "public"."mcp_oauth_grants"("id") ON DELETE cascade ON UPDATE no action;
19
+ EXCEPTION WHEN duplicate_object THEN NULL;
20
+ END $$;
21
+ --> statement-breakpoint
22
+ CREATE INDEX IF NOT EXISTS "mcp_event_admissions_expiry_idx" ON "mcp_event_admissions" USING btree ("expires_at");--> statement-breakpoint
23
+ CREATE INDEX IF NOT EXISTS "mcp_event_admissions_grant_idx" ON "mcp_event_admissions" USING btree ("grant_id");--> statement-breakpoint
24
+ CREATE INDEX IF NOT EXISTS "mcp_event_admissions_company_idx" ON "mcp_event_admissions" USING btree ("company_id");
@@ -0,0 +1 @@
1
+ ALTER TABLE "mcp_oauth_requests" ADD COLUMN IF NOT EXISTS "requested_company_id" uuid;
@@ -0,0 +1,27 @@
1
+ CREATE TABLE IF NOT EXISTS "mcp_oauth_device_requests" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "client_id" text NOT NULL,
4
+ "device_code_hash" text NOT NULL,
5
+ "user_code_hash" text NOT NULL,
6
+ "resource" text NOT NULL,
7
+ "scopes" jsonb NOT NULL,
8
+ "requested_company_id" uuid,
9
+ "source_hash" text NOT NULL,
10
+ "status" text DEFAULT 'pending' NOT NULL,
11
+ "grant_id" uuid,
12
+ "interval_seconds" integer DEFAULT 5 NOT NULL,
13
+ "next_poll_at" timestamp with time zone DEFAULT now() NOT NULL,
14
+ "expires_at" timestamp with time zone NOT NULL,
15
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
16
+ );
17
+ --> statement-breakpoint
18
+ ALTER TABLE "mcp_oauth_clients" ADD COLUMN IF NOT EXISTS "grant_types" jsonb DEFAULT '["authorization_code","refresh_token"]'::jsonb NOT NULL;--> statement-breakpoint
19
+ DO $$ BEGIN
20
+ ALTER TABLE "mcp_oauth_device_requests" ADD CONSTRAINT "mcp_oauth_device_requests_client_id_mcp_oauth_clients_id_fk" FOREIGN KEY ("client_id") REFERENCES "public"."mcp_oauth_clients"("id") ON DELETE cascade ON UPDATE no action;
21
+ EXCEPTION WHEN duplicate_object THEN NULL; END $$;--> statement-breakpoint
22
+ DO $$ BEGIN
23
+ ALTER TABLE "mcp_oauth_device_requests" ADD CONSTRAINT "mcp_oauth_device_requests_grant_id_mcp_oauth_grants_id_fk" FOREIGN KEY ("grant_id") REFERENCES "public"."mcp_oauth_grants"("id") ON DELETE cascade ON UPDATE no action;
24
+ EXCEPTION WHEN duplicate_object THEN NULL; END $$;--> statement-breakpoint
25
+ CREATE UNIQUE INDEX IF NOT EXISTS "mcp_oauth_device_code_uq" ON "mcp_oauth_device_requests" USING btree ("device_code_hash");--> statement-breakpoint
26
+ CREATE UNIQUE INDEX IF NOT EXISTS "mcp_oauth_user_code_uq" ON "mcp_oauth_device_requests" USING btree ("user_code_hash");--> statement-breakpoint
27
+ CREATE INDEX IF NOT EXISTS "mcp_oauth_device_expiry_idx" ON "mcp_oauth_device_requests" USING btree ("expires_at");
@@ -0,0 +1,7 @@
1
+ CREATE TABLE IF NOT EXISTS "mcp_oauth_metadata_admissions" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "source_hash" text NOT NULL,
4
+ "expires_at" timestamp with time zone NOT NULL
5
+ );
6
+ --> statement-breakpoint
7
+ CREATE INDEX IF NOT EXISTS "mcp_oauth_metadata_admissions_expiry_idx" ON "mcp_oauth_metadata_admissions" USING btree ("expires_at");
@@ -0,0 +1,4 @@
1
+ ALTER TABLE "ai_connection_defaults" DROP CONSTRAINT IF EXISTS "ai_connection_defaults_provider_check";--> statement-breakpoint
2
+ ALTER TABLE "ai_provider_defaults" DROP CONSTRAINT IF EXISTS "ai_provider_defaults_provider_check";--> statement-breakpoint
3
+ ALTER TABLE "ai_connection_defaults" ADD CONSTRAINT "ai_connection_defaults_provider_check" CHECK ("ai_connection_defaults"."provider" in ('anthropic','openai','openrouter','xai','google'));--> statement-breakpoint
4
+ ALTER TABLE "ai_provider_defaults" ADD CONSTRAINT "ai_provider_defaults_provider_check" CHECK ("ai_provider_defaults"."provider" in ('anthropic','openai','openrouter','xai','google'));
@@ -0,0 +1,3 @@
1
+ -- Older JavaScript restores can omit check constraints.
2
+ ALTER TABLE "resource_lifecycle_events" DROP CONSTRAINT IF EXISTS "resource_lifecycle_events_action_check";--> statement-breakpoint
3
+ ALTER TABLE "resource_lifecycle_events" ADD CONSTRAINT "resource_lifecycle_events_action_check" CHECK ("resource_lifecycle_events"."action" = 'create' OR ("resource_lifecycle_events"."resource_type" = 'project' AND "resource_lifecycle_events"."action" IN ('update', 'archive')) OR ("resource_lifecycle_events"."resource_type" = 'agent' AND "resource_lifecycle_events"."action" IN ('pause', 'resume', 'terminate')));
@@ -0,0 +1,20 @@
1
+ CREATE TABLE IF NOT EXISTS "agent_identity_keys" (
2
+ "agent_id" uuid PRIMARY KEY NOT NULL,
3
+ "company_id" uuid NOT NULL,
4
+ "algorithm" text NOT NULL,
5
+ "key_id" text NOT NULL,
6
+ "public_key_pem" text NOT NULL,
7
+ "private_key_material" jsonb NOT NULL,
8
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
9
+ CONSTRAINT "agent_identity_keys_algorithm_check" CHECK ("agent_identity_keys"."algorithm" = 'Ed25519')
10
+ );
11
+ --> statement-breakpoint
12
+ DO $$ BEGIN
13
+ IF NOT EXISTS (
14
+ SELECT 1 FROM pg_constraint
15
+ WHERE conrelid = 'public.agent_identity_keys'::regclass
16
+ AND conname = 'agent_identity_keys_owner_fk'
17
+ ) THEN
18
+ ALTER TABLE "agent_identity_keys" ADD CONSTRAINT "agent_identity_keys_owner_fk" FOREIGN KEY ("company_id","agent_id") REFERENCES "public"."agents"("company_id","id") ON DELETE cascade ON UPDATE no action;
19
+ END IF;
20
+ END $$;