@runfusion/fusion 0.76.0-beta.1 → 0.76.0-beta.3

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 (151) hide show
  1. package/dist/bin.js +826 -272
  2. package/dist/child-process-worker.js +740 -249
  3. package/dist/client/.vite/manifest.json +261 -261
  4. package/dist/client/assets/{AgentDetailView-Cbl1sw6i.js → AgentDetailView-T767kKTg.js} +1 -1
  5. package/dist/client/assets/{AgentPermissionPolicyEditor-DD1U5M9p.js → AgentPermissionPolicyEditor--u9jyUf2.js} +1 -1
  6. package/dist/client/assets/{AgentsView-BMcCbple.js → AgentsView-6Ifu7qDr.js} +5 -5
  7. package/dist/client/assets/ChatView-BV3lixQi.js +13 -0
  8. package/dist/client/assets/{CommandCenter-D7mcD61V.js → CommandCenter-CSRYoveB.js} +1 -1
  9. package/dist/client/assets/{DevServerView-DaqCn0p5.js → DevServerView-B9So_oYx.js} +1 -1
  10. package/dist/client/assets/{DirectoryPicker-CLdqqqYN.js → DirectoryPicker-BhyDL_2m.js} +1 -1
  11. package/dist/client/assets/{DocumentsView-BOROVxPm.js → DocumentsView-pbERA7O-.js} +1 -1
  12. package/dist/client/assets/{EvalsView-BQMXRUlQ.js → EvalsView-DJQ4ltCg.js} +1 -1
  13. package/dist/client/assets/{ExperimentalAgentOnboardingModal-1GNeCIQT.js → ExperimentalAgentOnboardingModal-BwJuzkzB.js} +1 -1
  14. package/dist/client/assets/{GoalsView-Cr3c_2GC.js → GoalsView-B6X5_0Ve.js} +1 -1
  15. package/dist/client/assets/InsightsView-CJgI0J7S.js +11 -0
  16. package/dist/client/assets/{InsightsView-Gn5lOBA0.css → InsightsView-DAAXuX4v.css} +1 -1
  17. package/dist/client/assets/{MemoryView-C0VzzMSw.js → MemoryView-BHq1AHPu.js} +1 -1
  18. package/dist/client/assets/{PiExtensionsManager-D1kkkVtP.js → PiExtensionsManager-B8At6fO8.js} +1 -1
  19. package/dist/client/assets/{PluginManager-BW-_Zyo5.js → PluginManager-k7hV7_9e.js} +1 -1
  20. package/dist/client/assets/{PullRequestView-Cmlo4nWA.js → PullRequestView-B1hZFrWo.js} +1 -1
  21. package/dist/client/assets/{ReportModal-BWuaroTF.js → ReportModal-C46V_zEa.js} +1 -1
  22. package/dist/client/assets/{ResearchView-BmkF_vZU.js → ResearchView-D8G85BYN.js} +2 -2
  23. package/dist/client/assets/{SecretsView-DVc8Hky0.js → SecretsView-CTri9LRK.js} +1 -1
  24. package/dist/client/assets/{SessionTerminal-DCXrxA57.js → SessionTerminal-BRf6r4Vp.js} +1 -1
  25. package/dist/client/assets/SettingsModal-D0RBeZik.js +21 -0
  26. package/dist/client/assets/{SettingsModal-DqwpLnDU.js → SettingsModal-rUQcSTJp.js} +1 -1
  27. package/dist/client/assets/{SettingsTextareaRow-B5spAoOY.js → SettingsTextareaRow-BirlBcem.js} +1 -1
  28. package/dist/client/assets/{SetupWizardModal-VTBLyFzr.js → SetupWizardModal-xnCBM9EL.js} +2 -2
  29. package/dist/client/assets/{SkillsView-CR91ey9d.js → SkillsView-O5U0ep50.js} +1 -1
  30. package/dist/client/assets/{WorkflowNodeEditor-BzGh_Fd9.js → WorkflowNodeEditor-BX4d2qqF.js} +3 -3
  31. package/dist/client/assets/{agent-import-generation-D27NKQsr.js → agent-import-generation-DnqVkPkz.js} +1 -1
  32. package/dist/client/assets/{agentsOrgChartLayout-C2K_M3x3.js → agentsOrgChartLayout-CKuzbhFf.js} +1 -1
  33. package/dist/client/assets/app-B2FwULu2.js +5 -0
  34. package/dist/client/assets/{app-BA9gw9hx.js → app-B6nJaqAl.js} +2 -2
  35. package/dist/client/assets/{app-B_lScKc1.js → app-CGXZeIRc.js} +2 -2
  36. package/dist/client/assets/{app-DG_vjDw5.js → app-CGtkeBF5.js} +3 -3
  37. package/dist/client/assets/{app-GBgoY-oh.js → app-CNzN1Plu.js} +2 -2
  38. package/dist/client/assets/{app-B0KFGP8D.js → app-DLs-f5yM.js} +3 -3
  39. package/dist/client/assets/{app-DeHIOCEP.js → app-DsvtcRUG.js} +2 -2
  40. package/dist/client/assets/{architectureDiagram-3BPJPVTR-BQy8UcRx.js → architectureDiagram-3BPJPVTR-gi467xY0.js} +1 -1
  41. package/dist/client/assets/{blockDiagram-GPEHLZMM-CZG1bqFQ.js → blockDiagram-GPEHLZMM-B_gpcYiR.js} +1 -1
  42. package/dist/client/assets/{c4Diagram-AAUBKEIU-CZmO_kg3.js → c4Diagram-AAUBKEIU-DeylEYsQ.js} +1 -1
  43. package/dist/client/assets/channel-BoE8DeM4.js +1 -0
  44. package/dist/client/assets/{chunk-2J33WTMH-jsad6DTE.js → chunk-2J33WTMH-BL2WY0HN.js} +1 -1
  45. package/dist/client/assets/{chunk-4BX2VUAB-DX7SuvwP.js → chunk-4BX2VUAB-BkLExGMe.js} +1 -1
  46. package/dist/client/assets/{chunk-55IACEB6-De20xFgU.js → chunk-55IACEB6-CSTvCF5T.js} +1 -1
  47. package/dist/client/assets/{chunk-727SXJPM-CNiri9yW.js → chunk-727SXJPM-9J8pKk4x.js} +1 -1
  48. package/dist/client/assets/{chunk-AQP2D5EJ-DvDqG-ax.js → chunk-AQP2D5EJ-DXC1vPWr.js} +1 -1
  49. package/dist/client/assets/{chunk-FMBD7UC4-DkMdKaUq.js → chunk-FMBD7UC4-CFRUb-zD.js} +1 -1
  50. package/dist/client/assets/{chunk-ND2GUHAM-Do5BwIrI.js → chunk-ND2GUHAM-B87HFYS8.js} +1 -1
  51. package/dist/client/assets/{chunk-QZHKN3VN-BnJgt9vl.js → chunk-QZHKN3VN-CTjHiGbd.js} +1 -1
  52. package/dist/client/assets/classDiagram-4FO5ZUOK-qnbLX_9_.js +1 -0
  53. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-qnbLX_9_.js +1 -0
  54. package/dist/client/assets/{cose-bilkent-S5V4N54A-D-ivAzzF.js → cose-bilkent-S5V4N54A-D3Edq3L9.js} +1 -1
  55. package/dist/client/assets/{dagre-BM42HDAG-Bpd9L0or.js → dagre-BM42HDAG-CwwbeQfN.js} +1 -1
  56. package/dist/client/assets/{dashboard-view-CgBo8Ctn.js → dashboard-view-BLAoW3D4.js} +1 -1
  57. package/dist/client/assets/{dashboard-view-DbaRE8Ma.js → dashboard-view-DAlJxqiJ.js} +1 -1
  58. package/dist/client/assets/{dashboard-view-DSvVO6Vr.js → dashboard-view-QxQwJFLz.js} +1 -1
  59. package/dist/client/assets/{dashboard-view-C-XU9CVv.js → dashboard-view-aPzfv99T.js} +1 -1
  60. package/dist/client/assets/{diagram-2AECGRRQ-B6TT1NWU.js → diagram-2AECGRRQ-koJWWXE4.js} +1 -1
  61. package/dist/client/assets/{diagram-5GNKFQAL-DpRS2yRw.js → diagram-5GNKFQAL-Yob5cOKT.js} +1 -1
  62. package/dist/client/assets/{diagram-KO2AKTUF-BVbZr02C.js → diagram-KO2AKTUF-DelvFSkK.js} +1 -1
  63. package/dist/client/assets/{diagram-LMA3HP47-DzENbfal.js → diagram-LMA3HP47-CMput2h2.js} +1 -1
  64. package/dist/client/assets/{diagram-OG6HWLK6-BIePG5SY.js → diagram-OG6HWLK6-04VXDl79.js} +1 -1
  65. package/dist/client/assets/{erDiagram-TEJ5UH35-CQo3WBwo.js → erDiagram-TEJ5UH35-C9iP8Uyp.js} +1 -1
  66. package/dist/client/assets/{flowDiagram-I6XJVG4X-Ct2H4jYo.js → flowDiagram-I6XJVG4X-DENQvu_t.js} +1 -1
  67. package/dist/client/assets/{folder-open-DAh8mpTX.js → folder-open-D-554oji.js} +1 -1
  68. package/dist/client/assets/{ganttDiagram-6RSMTGT7-DyOLCG4B.js → ganttDiagram-6RSMTGT7-BqOttZAT.js} +1 -1
  69. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-sC28-su3.js → gitGraphDiagram-PVQCEYII-CDcydi-5.js} +1 -1
  70. package/dist/client/assets/{index-BzAldNrK.css → index-CXTd3LAB.css} +1 -1
  71. package/dist/client/assets/index-CtYRoouR.js +2818 -0
  72. package/dist/client/assets/{infoDiagram-5YYISTIA-Q81_5aBZ.js → infoDiagram-5YYISTIA-B5LQQnxt.js} +1 -1
  73. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-Cwkjs0z5.js → ishikawaDiagram-YF4QCWOH-DpFEt7D2.js} +1 -1
  74. package/dist/client/assets/{journeyDiagram-JHISSGLW-BXcBV1Bc.js → journeyDiagram-JHISSGLW-MELtgu22.js} +1 -1
  75. package/dist/client/assets/{kanban-definition-UN3LZRKU-BI7hW892.js → kanban-definition-UN3LZRKU-h33PLATd.js} +1 -1
  76. package/dist/client/assets/{mermaid.core-B8oDA6jj.js → mermaid.core-Dk2mUPOq.js} +4 -4
  77. package/dist/client/assets/{mindmap-definition-RKZ34NQL-COG6_9eD.js → mindmap-definition-RKZ34NQL-B_T4O2-B.js} +1 -1
  78. package/dist/client/assets/{pieDiagram-4H26LBE5-D11brZ_N.js → pieDiagram-4H26LBE5-B1YIFkRl.js} +1 -1
  79. package/dist/client/assets/{puzzle-D3_Z_rlW.js → puzzle-hvecxlpJ.js} +1 -1
  80. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-jmhDVZcv.js → quadrantDiagram-W4KKPZXB-BoGQR_hO.js} +1 -1
  81. package/dist/client/assets/{requirementDiagram-4Y6WPE33-BKCUWI1E.js → requirementDiagram-4Y6WPE33-BSDmUBIx.js} +1 -1
  82. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-HsZdEl2t.js → sankeyDiagram-5OEKKPKP-Dz_GqOa5.js} +1 -1
  83. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-CsDf7Fup.js → sequenceDiagram-3UESZ5HK-B6twjYUH.js} +1 -1
  84. package/dist/client/assets/{shield-alert-C16k8wNu.js → shield-alert-BX4WRJOv.js} +1 -1
  85. package/dist/client/assets/{standing-instructions-template-BqAyftqe.js → standing-instructions-template-BJY317CC.js} +1 -1
  86. package/dist/client/assets/{stateDiagram-AJRCARHV-fvzWhltB.js → stateDiagram-AJRCARHV-DuwqWLx-.js} +1 -1
  87. package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-cnt42Sr_.js → stateDiagram-v2-BHNVJYJU-Bu8Sk187.js} +1 -1
  88. package/dist/client/assets/{timeline-definition-PNZ67QCA-DsZFCSMb.js → timeline-definition-PNZ67QCA-BS6VnqSI.js} +1 -1
  89. package/dist/client/assets/{upload-_s5sL5Pq.js → upload-CaDoyaMW.js} +1 -1
  90. package/dist/client/assets/{users-DH1U5ISc.js → users-C5uCLwVM.js} +1 -1
  91. package/dist/client/assets/{vennDiagram-CIIHVFJN-B3KDWS-e.js → vennDiagram-CIIHVFJN-CUbzqT5Y.js} +1 -1
  92. package/dist/client/assets/{wardley-L42UT6IY-BKs5MtQS.js → wardley-L42UT6IY-Mv-NotFS.js} +1 -1
  93. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-CNsEoXVB.js → wardleyDiagram-YWT4CUSO-DuYpvLZX.js} +1 -1
  94. package/dist/client/assets/{xychartDiagram-2RQKCTM6-kq_gW4QC.js → xychartDiagram-2RQKCTM6-24_7QXdw.js} +1 -1
  95. package/dist/client/index.html +2 -2
  96. package/dist/client/version.json +1 -1
  97. package/dist/droid-cli/package.json +1 -1
  98. package/dist/extension.js +932 -254
  99. package/dist/migrations/0057_fn_9004_project_ownership_default_reconciliation.sql +13 -0
  100. package/dist/migrations/0058_fn_9014_message_archive.sql +13 -0
  101. package/dist/pi-claude-cli/package.json +5 -5
  102. package/dist/plugin-sdk/index.d.ts +23 -0
  103. package/dist/plugin-sdk/index.js +71 -23
  104. package/dist/plugins/fusion-plugin-acp-runtime/bundled.js +71 -23
  105. package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
  106. package/dist/plugins/fusion-plugin-claude-runtime/bundled.js +71 -23
  107. package/dist/plugins/fusion-plugin-claude-runtime/package.json +1 -1
  108. package/dist/plugins/fusion-plugin-cli-printing-press/bundled.js +71 -23
  109. package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
  110. package/dist/plugins/fusion-plugin-compound-engineering/bundled.js +71 -23
  111. package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
  112. package/dist/plugins/fusion-plugin-cursor-runtime/bundled.js +71 -23
  113. package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
  114. package/dist/plugins/fusion-plugin-dependency-graph/bundled.js +71 -23
  115. package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
  116. package/dist/plugins/fusion-plugin-droid-runtime/bundled.js +73 -25
  117. package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
  118. package/dist/plugins/fusion-plugin-grok-runtime/bundled.js +71 -23
  119. package/dist/plugins/fusion-plugin-grok-runtime/package.json +1 -1
  120. package/dist/plugins/fusion-plugin-hermes-runtime/bundled.js +71 -23
  121. package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
  122. package/dist/plugins/fusion-plugin-linear-import/bundled.js +71 -23
  123. package/dist/plugins/fusion-plugin-linear-import/package.json +1 -1
  124. package/dist/plugins/fusion-plugin-omp-runtime/bundled.js +71 -23
  125. package/dist/plugins/fusion-plugin-omp-runtime/package.json +1 -1
  126. package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +71 -23
  127. package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
  128. package/dist/plugins/fusion-plugin-paperclip-runtime/bundled.js +71 -23
  129. package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
  130. package/dist/plugins/fusion-plugin-quality/bundled.js +71 -23
  131. package/dist/plugins/fusion-plugin-quality/package.json +1 -1
  132. package/dist/plugins/fusion-plugin-reports/bundled.js +71 -23
  133. package/dist/plugins/fusion-plugin-reports/package.json +1 -1
  134. package/dist/plugins/fusion-plugin-roadmap/bundled.js +71 -23
  135. package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
  136. package/dist/plugins/fusion-plugin-todos/bundled.js +71 -23
  137. package/dist/plugins/fusion-plugin-todos/package.json +1 -1
  138. package/dist/plugins/fusion-plugin-whatsapp-chat/bundled.js +71 -23
  139. package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
  140. package/package.json +3 -3
  141. package/skill/fusion/SKILL.md +1 -1
  142. package/skill/fusion/references/extension-tools.md +22 -0
  143. package/skill/fusion/references/fusion-capabilities.md +2 -0
  144. package/dist/client/assets/ChatView-DcM-ARpZ.js +0 -13
  145. package/dist/client/assets/InsightsView-DgVBVQM4.js +0 -11
  146. package/dist/client/assets/SettingsModal-DZ32F1c-.js +0 -21
  147. package/dist/client/assets/app-DBCGrz-f.js +0 -5
  148. package/dist/client/assets/channel-Cwl9FefC.js +0 -1
  149. package/dist/client/assets/classDiagram-4FO5ZUOK-BZtWCoHC.js +0 -1
  150. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BZtWCoHC.js +0 -1
  151. package/dist/client/assets/index-BlGpMJSZ.js +0 -2818
@@ -2145,6 +2145,8 @@ var init_settings_schema = __esm({
2145
2145
  DEFAULT_PROJECT_SETTINGS = {
2146
2146
  // FNXC:TaskRecommendations 2026-08-08-05:02: completion follows-ups stay bounded by default; 0 disables writing them.
2147
2147
  maxRecommendationsPerTask: 3,
2148
+ // FNXC:TaskRecommendations 2026-08-13-03:56: surface completed-task proposals by default; operators can suppress the notice without suppressing capture.
2149
+ recommendationMailboxNoticeEnabled: true,
2148
2150
  globalPause: false,
2149
2151
  globalPauseReason: void 0,
2150
2152
  defaultWorkflowId: void 0,
@@ -21717,9 +21719,14 @@ var init_project = __esm({
21717
21719
  // now keyed per-project on `project_id` (the effective PK). `id` is retained
21718
21720
  // for column-shape parity (always 1) but is no longer the PK and no longer
21719
21721
  // CHECK-constrained. Single-project / SQLite-parity callers leave project_id
21720
- // at its '' default (one row), preserving the pre-isolation behavior.
21722
+ // through the database ownership trigger, preserving the pre-isolation behavior.
21723
+ /*
21724
+ FNXC:MultiProjectIsolation 2026-08-12-15:43:
21725
+ Migration 0006 owns config's project_id default and leaves its catalog PK as (project_id).
21726
+ Drizzle emits DEFAULT while the database GUC/trigger selects the effective ownership partition.
21727
+ */
21721
21728
  id: integer("id").default(1),
21722
- projectId: text("project_id").notNull().default("").primaryKey(),
21729
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`).primaryKey(),
21723
21730
  nextId: integer("next_id").default(1),
21724
21731
  nextWorkflowStepId: integer("next_workflow_step_id").default(1),
21725
21732
  // FNXC:SqliteFinalRemoval 2026-06-28:
@@ -22113,9 +22120,13 @@ var init_project = __esm({
22113
22120
  automations = projectSchema.table("automations", {
22114
22121
  /*
22115
22122
  * FNXC:AutomationIsolation 2026-07-13-22:37:
22116
- * Automations are partitioned by the AsyncDataLayer's project ID because embedded PostgreSQL consolidates the per-project SQLite files into one table. The composite key deliberately permits the same automation ID in two projects without allowing either project's CRUD or cron-claim path to address the other row. The empty default preserves an explicit partition for legacy and project-agnostic callers until startup stamps migrated rows.
22123
+ * Automations are partitioned by the AsyncDataLayer's project ID because embedded PostgreSQL consolidates the per-project SQLite files into one table. The composite key deliberately permits the same automation ID in two projects without allowing either project's CRUD or cron-claim path to address the other row.
22124
+ *
22125
+ * FNXC:MultiProjectIsolation 2026-08-12-15:43:
22126
+ * Migration 0006 owns the default through the database GUC/trigger; Drizzle emits DEFAULT
22127
+ * without replacing the physical legacy fallback or the fail-closed bound writer contract.
22117
22128
  */
22118
- projectId: text("project_id").notNull().default(""),
22129
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
22119
22130
  id: text("id").notNull(),
22120
22131
  name: text("name").notNull(),
22121
22132
  description: text("description"),
@@ -22414,7 +22425,8 @@ var init_project = __esm({
22414
22425
  index("idxTaskDocumentsTaskId").on(t.taskId)
22415
22426
  ]);
22416
22427
  artifacts = projectSchema.table("artifacts", {
22417
- id: text("id").primaryKey(),
22428
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
22429
+ id: text("id").notNull(),
22418
22430
  type: text("type").notNull(),
22419
22431
  title: text("title").notNull(),
22420
22432
  description: text("description"),
@@ -22429,7 +22441,8 @@ var init_project = __esm({
22429
22441
  createdAt: text("created_at").notNull(),
22430
22442
  updatedAt: text("updated_at").notNull()
22431
22443
  }, (t) => [
22432
- foreignKey({ columns: [t.taskId], foreignColumns: [tasks.id] }).onDelete("cascade"),
22444
+ primaryKey({ columns: [t.projectId, t.id] }),
22445
+ foreignKey({ name: "artifacts_task_id_fkey", columns: [t.projectId, t.taskId], foreignColumns: [tasks.projectId, tasks.id] }).onDelete("cascade"),
22433
22446
  index("idxArtifactsTaskId").on(t.taskId),
22434
22447
  index("idxArtifactsAuthorId").on(t.authorId),
22435
22448
  index("idxArtifactsType").on(t.type),
@@ -22619,7 +22632,8 @@ var init_project = __esm({
22619
22632
  index("idxEvalRunEventsRunIdSeq").on(t.runId, t.seq)
22620
22633
  ]);
22621
22634
  secrets = projectSchema.table("secrets", {
22622
- id: text("id").primaryKey(),
22635
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
22636
+ id: text("id").notNull(),
22623
22637
  key: text("key").notNull(),
22624
22638
  valueCiphertext: bytea("value_ciphertext").notNull(),
22625
22639
  nonce: bytea("nonce").notNull(),
@@ -22632,7 +22646,8 @@ var init_project = __esm({
22632
22646
  lastReadAt: text("last_read_at"),
22633
22647
  lastReadBy: text("last_read_by")
22634
22648
  }, (t) => [
22635
- unique("secrets_key_unique").on(t.key),
22649
+ primaryKey({ columns: [t.projectId, t.id] }),
22650
+ unique("secrets_key_unique").on(t.projectId, t.key),
22636
22651
  check("secrets_access_policy_check", sql`${t.accessPolicy} IN ('auto', 'prompt', 'deny')`),
22637
22652
  check("secrets_env_exportable_check", sql`${t.envExportable} IN (0, 1)`)
22638
22653
  ]);
@@ -22674,10 +22689,11 @@ var init_project = __esm({
22674
22689
  updatedAt: text("updated_at").notNull()
22675
22690
  }, (t) => [primaryKey({ columns: [t.projectId, t.id] })]);
22676
22691
  branchGroups = projectSchema.table("branch_groups", {
22677
- id: text("id").primaryKey(),
22692
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
22693
+ id: text("id").notNull(),
22678
22694
  sourceType: text("source_type").notNull(),
22679
22695
  sourceId: text("source_id").notNull(),
22680
- branchName: text("branch_name").notNull().unique(),
22696
+ branchName: text("branch_name").notNull(),
22681
22697
  worktreePath: text("worktree_path"),
22682
22698
  autoMerge: integer("auto_merge").notNull().default(0),
22683
22699
  prState: text("pr_state").notNull().default("none"),
@@ -22693,6 +22709,8 @@ var init_project = __esm({
22693
22709
  updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
22694
22710
  closedAt: bigint("closed_at", { mode: "number" })
22695
22711
  }, (t) => [
22712
+ primaryKey({ columns: [t.projectId, t.id] }),
22713
+ unique("branch_groups_branch_name_key").on(t.projectId, t.branchName),
22696
22714
  check("branch_groups_source_type_check", sql`${t.sourceType} IN ('mission','planning','new-task')`),
22697
22715
  check("branch_groups_pr_state_check", sql`${t.prState} IN ('none','open','merged','closed')`),
22698
22716
  check("branch_groups_status_check", sql`${t.status} IN ('open','finalized','abandoned')`),
@@ -23094,12 +23112,14 @@ var init_project = __esm({
23094
23112
  index("idxUsageEventsKindTs").on(t.kind, t.ts)
23095
23113
  ]);
23096
23114
  pluginActivations = projectSchema.table("plugin_activations", {
23097
- id: integer("id").generatedAlwaysAsIdentity().primaryKey(),
23115
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23116
+ id: integer("id").generatedAlwaysAsIdentity().notNull(),
23098
23117
  pluginId: text("plugin_id").notNull(),
23099
23118
  source: text("source").notNull(),
23100
23119
  pluginVersion: text("plugin_version"),
23101
23120
  activatedAt: text("activated_at").notNull()
23102
23121
  }, (t) => [
23122
+ primaryKey({ columns: [t.projectId, t.id] }),
23103
23123
  index("idxPluginActivationsActivatedAt").on(t.activatedAt),
23104
23124
  index("idxPluginActivationsPluginId").on(t.pluginId)
23105
23125
  ]);
@@ -23124,8 +23144,13 @@ var init_project = __esm({
23124
23144
  index("idxKnowledgePagesUpdatedAt").on(t.updatedAt)
23125
23145
  ]);
23126
23146
  deployments = projectSchema.table("deployments", {
23127
- id: integer("id").generatedAlwaysAsIdentity().primaryKey(),
23128
- projectId: text("project_id").notNull().default(""),
23147
+ id: integer("id").generatedAlwaysAsIdentity().notNull(),
23148
+ /*
23149
+ FNXC:MultiProjectIsolation 2026-08-12-15:43:
23150
+ Migration 0006 rebuilt deployments_pkey as (project_id, id). Drizzle emits DEFAULT while the
23151
+ database GUC/trigger keeps ownership stamping and the physical legacy fallback authoritative.
23152
+ */
23153
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23129
23154
  deploymentId: text("deployment_id").notNull(),
23130
23155
  service: text("service"),
23131
23156
  environment: text("environment"),
@@ -23136,6 +23161,7 @@ var init_project = __esm({
23136
23161
  meta: jsonb("meta"),
23137
23162
  createdAt: text("created_at").notNull()
23138
23163
  }, (t) => [
23164
+ primaryKey({ columns: [t.projectId, t.id], name: "deployments_pkey" }),
23139
23165
  uniqueIndex("idxDeploymentsProjectDeploymentId").on(t.projectId, t.deploymentId),
23140
23166
  index("idxDeploymentsProjectDeployedAt").on(t.projectId, t.deployedAt),
23141
23167
  index("idxDeploymentsDeployedAt").on(t.deployedAt),
@@ -23143,7 +23169,12 @@ var init_project = __esm({
23143
23169
  ]);
23144
23170
  githubCheckStates = projectSchema.table("github_check_states", {
23145
23171
  id: integer("id").generatedAlwaysAsIdentity().notNull(),
23146
- projectId: text("project_id").notNull().default(""),
23172
+ /*
23173
+ FNXC:MultiProjectIsolation 2026-08-12-15:43:
23174
+ Migration 0048 post-dated 0006, so FN-9004 reconciles its physical default to the ownership
23175
+ expression. Drizzle emits DEFAULT while the database GUC/trigger remains the stamping authority.
23176
+ */
23177
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23147
23178
  repo: text("repo").notNull(),
23148
23179
  headSha: text("head_sha").notNull(),
23149
23180
  checkName: text("check_name").notNull(),
@@ -23163,9 +23194,14 @@ var init_project = __esm({
23163
23194
  index("idxGithubCheckStatesProjectReceived").on(t.projectId, t.receivedAt)
23164
23195
  ]);
23165
23196
  incidents = projectSchema.table("incidents", {
23166
- id: integer("id").generatedAlwaysAsIdentity().primaryKey(),
23167
- projectId: text("project_id").notNull().default(""),
23168
- incidentId: text("incident_id").notNull().unique(),
23197
+ id: integer("id").generatedAlwaysAsIdentity().notNull(),
23198
+ /*
23199
+ FNXC:MultiProjectIsolation 2026-08-12-15:43:
23200
+ Migration 0006 rebuilt incident keys with project_id first. Drizzle emits DEFAULT while the
23201
+ database GUC/trigger retains ownership stamping and its physical legacy fallback.
23202
+ */
23203
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23204
+ incidentId: text("incident_id").notNull(),
23169
23205
  groupingKey: text("grouping_key").notNull(),
23170
23206
  title: text("title").notNull(),
23171
23207
  severity: text("severity"),
@@ -23179,6 +23215,8 @@ var init_project = __esm({
23179
23215
  createdAt: text("created_at").notNull(),
23180
23216
  updatedAt: text("updated_at").notNull()
23181
23217
  }, (t) => [
23218
+ primaryKey({ columns: [t.projectId, t.id], name: "incidents_pkey" }),
23219
+ unique("incidents_incident_id_key").on(t.projectId, t.incidentId),
23182
23220
  index("idxIncidentsProjectOpenedAt").on(t.projectId, t.openedAt),
23183
23221
  index("idxIncidentsProjectStatus").on(t.projectId, t.status),
23184
23222
  index("idxIncidentsGroupingKey").on(t.groupingKey),
@@ -23233,12 +23271,14 @@ var init_project = __esm({
23233
23271
  content: text("content").notNull(),
23234
23272
  type: text("type").notNull(),
23235
23273
  read: integer("read").default(0),
23274
+ archived: integer("archived").default(0),
23236
23275
  metadata: jsonb("metadata"),
23237
23276
  createdAt: text("created_at").notNull(),
23238
23277
  updatedAt: text("updated_at").notNull()
23239
23278
  }, (t) => [
23240
23279
  primaryKey({ columns: [t.projectId, t.id] }),
23241
23280
  index("idxMessagesTo").on(t.toId, t.toType, t.read),
23281
+ index("idxMessagesToArchived").on(t.toId, t.toType, t.archived),
23242
23282
  index("idxMessagesFrom").on(t.fromId, t.fromType),
23243
23283
  index("idxMessagesCreatedAt").on(t.createdAt)
23244
23284
  ]);
@@ -23339,7 +23379,8 @@ var init_project = __esm({
23339
23379
  index("idx_cli_sessions_project_state").on(t.projectId, t.agentState)
23340
23380
  ]);
23341
23381
  chatMessages = projectSchema.table("chat_messages", {
23342
- id: text("id").primaryKey(),
23382
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23383
+ id: text("id").notNull(),
23343
23384
  sessionId: text("session_id").notNull(),
23344
23385
  role: text("role").notNull(),
23345
23386
  content: text("content").notNull(),
@@ -23348,6 +23389,7 @@ var init_project = __esm({
23348
23389
  createdAt: text("created_at").notNull(),
23349
23390
  attachments: jsonb("attachments")
23350
23391
  }, (t) => [
23392
+ primaryKey({ columns: [t.projectId, t.id] }),
23351
23393
  index("idxChatMessagesSessionId").on(t.sessionId),
23352
23394
  index("idxChatMessagesCreatedAt").on(t.createdAt)
23353
23395
  ]);
@@ -23373,7 +23415,8 @@ var init_project = __esm({
23373
23415
  index("idxChatTokenUsageCreatedAt").on(t.createdAt)
23374
23416
  ]);
23375
23417
  runAuditEvents = projectSchema.table("run_audit_events", {
23376
- id: text("id").primaryKey(),
23418
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23419
+ id: text("id").notNull(),
23377
23420
  timestamp: text("timestamp").notNull(),
23378
23421
  taskId: text("task_id"),
23379
23422
  agentId: text("agent_id").notNull(),
@@ -23383,6 +23426,7 @@ var init_project = __esm({
23383
23426
  target: text("target").notNull(),
23384
23427
  metadata: jsonb("metadata")
23385
23428
  }, (t) => [
23429
+ primaryKey({ columns: [t.projectId, t.id] }),
23386
23430
  index("idxRunAuditEventsRunIdTimestamp").on(t.runId, t.timestamp),
23387
23431
  index("idxRunAuditEventsTaskIdTimestamp").on(t.taskId, t.timestamp),
23388
23432
  index("idxRunAuditEventsTimestamp").on(t.timestamp)
@@ -23483,13 +23527,14 @@ var init_project = __esm({
23483
23527
  index("idxMissionLineageStopsMissionId").on(t.projectId, t.missionId)
23484
23528
  ]);
23485
23529
  verificationCache = projectSchema.table("verification_cache", {
23530
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23486
23531
  treeSha: text("tree_sha").notNull(),
23487
23532
  testCommand: text("test_command").notNull().default(""),
23488
23533
  buildCommand: text("build_command").notNull().default(""),
23489
23534
  recordedAt: text("recorded_at").notNull(),
23490
23535
  taskId: text("task_id")
23491
23536
  }, (t) => [
23492
- primaryKey({ columns: [t.treeSha, t.testCommand, t.buildCommand] }),
23537
+ primaryKey({ columns: [t.projectId, t.treeSha, t.testCommand, t.buildCommand] }),
23493
23538
  index("idxVerificationCacheRecordedAt").on(t.recordedAt)
23494
23539
  ]);
23495
23540
  importTranslationCache = projectSchema.table("import_translation_cache", {
@@ -23522,7 +23567,8 @@ var init_project = __esm({
23522
23567
  index("idxImportTranslationCacheRecordedAt").on(t.recordedAt)
23523
23568
  ]);
23524
23569
  approvalRequests = projectSchema.table("approval_requests", {
23525
- id: text("id").primaryKey(),
23570
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23571
+ id: text("id").notNull(),
23526
23572
  status: text("status").notNull(),
23527
23573
  requesterActorId: text("requester_actor_id").notNull(),
23528
23574
  requesterActorType: text("requester_actor_type").notNull(),
@@ -23541,13 +23587,14 @@ var init_project = __esm({
23541
23587
  createdAt: text("created_at").notNull(),
23542
23588
  updatedAt: text("updated_at").notNull()
23543
23589
  }, (t) => [
23590
+ primaryKey({ columns: [t.projectId, t.id] }),
23544
23591
  index("idxApprovalRequestsStatusCreatedAt").on(t.status, t.createdAt),
23545
23592
  index("idxApprovalRequestsRequesterCreatedAt").on(t.requesterActorId, t.createdAt),
23546
23593
  index("idxApprovalRequestsTaskCreatedAt").on(t.taskId, t.createdAt)
23547
23594
  ]);
23548
23595
  approvalRequestAuditEvents = projectSchema.table("approval_request_audit_events", {
23549
- projectId: text("project_id").notNull().default(""),
23550
- id: text("id").primaryKey(),
23596
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23597
+ id: text("id").notNull(),
23551
23598
  requestId: text("request_id").notNull(),
23552
23599
  eventType: text("event_type").notNull(),
23553
23600
  actorId: text("actor_id").notNull(),
@@ -23556,6 +23603,7 @@ var init_project = __esm({
23556
23603
  note: text("note"),
23557
23604
  createdAt: text("created_at").notNull()
23558
23605
  }, (t) => [
23606
+ primaryKey({ columns: [t.projectId, t.id] }),
23559
23607
  index("idxApprovalRequestAuditRequestCreatedAt").on(t.requestId, t.createdAt, t.id),
23560
23608
  index("idxApprovalRequestAuditProjectCreatedAt").on(t.projectId, t.createdAt)
23561
23609
  ]);
@@ -27045,6 +27093,27 @@ var init_grok_provider = __esm({
27045
27093
  apiKey: "$GROK_API_KEY",
27046
27094
  api: "openai-completions",
27047
27095
  models: [
27096
+ /*
27097
+ FNXC:ModelRegistry 2026-08-12-20:17:
27098
+ FN-9008 requires Grok 4.6 to be selectable in every model area. Keep it in this shared
27099
+ catalog because pi's registerProvider() replaces provider model lists wholesale, while every
27100
+ Fusion host seeds and re-merges this export. xAI's model docs verify grok-4.6, its 500k-token
27101
+ context window, and configurable reasoning; max output and per-model image input remain
27102
+ upstream-pending-verification, so retain the conservative text-only and 65536-token defaults.
27103
+ Source: https://docs.x.ai/developers/models
27104
+ */
27105
+ {
27106
+ id: "grok-4.6",
27107
+ name: "Grok 4.6",
27108
+ reasoning: true,
27109
+ input: ["text"],
27110
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
27111
+ contextWindow: 5e5,
27112
+ maxTokens: 65536,
27113
+ compat: {
27114
+ supportsDeveloperRole: false
27115
+ }
27116
+ },
27048
27117
  {
27049
27118
  id: "grok-4.5",
27050
27119
  name: "Grok 4.5",
@@ -35003,6 +35072,7 @@ function rowToMessage(row) {
35003
35072
  content: row.content,
35004
35073
  type: row.type,
35005
35074
  read: (row.read ?? 0) === 1,
35075
+ archived: (row.archived ?? 0) === 1,
35006
35076
  metadata: row.metadata ?? void 0,
35007
35077
  createdAt: row.createdAt,
35008
35078
  updatedAt: row.updatedAt
@@ -35014,6 +35084,11 @@ function participantIdsForLookup(ownerId, ownerType) {
35014
35084
  }
35015
35085
  return [ownerId];
35016
35086
  }
35087
+ function archivedCondition(archived) {
35088
+ if (archived === true) return eq(project_exports.messages.archived, 1);
35089
+ if (archived === false) return or(eq(project_exports.messages.archived, 0), sql`${project_exports.messages.archived} IS NULL`);
35090
+ return or(eq(project_exports.messages.archived, 0), sql`${project_exports.messages.archived} IS NULL`);
35091
+ }
35017
35092
  async function sendMessage(handle, message) {
35018
35093
  const sanitizedContent = sanitizeTextValue(message.content);
35019
35094
  const sanitizedMetadata = sanitizeJsonbValue(message.metadata);
@@ -35026,6 +35101,7 @@ async function sendMessage(handle, message) {
35026
35101
  content: sanitizedContent,
35027
35102
  type: message.type,
35028
35103
  read: message.read ? 1 : 0,
35104
+ archived: message.archived ? 1 : 0,
35029
35105
  metadata: sanitizedMetadata,
35030
35106
  createdAt: message.createdAt,
35031
35107
  updatedAt: message.updatedAt
@@ -35034,7 +35110,8 @@ async function sendMessage(handle, message) {
35034
35110
  ...message,
35035
35111
  content: sanitizedContent,
35036
35112
  metadata: sanitizedMetadata,
35037
- read: message.read ? 1 : 0
35113
+ read: message.read ? 1 : 0,
35114
+ archived: message.archived ? 1 : 0
35038
35115
  });
35039
35116
  }
35040
35117
  async function sendMessageOnce(handle, message) {
@@ -35047,6 +35124,7 @@ async function sendMessageOnce(handle, message) {
35047
35124
  content: message.content,
35048
35125
  type: message.type,
35049
35126
  read: message.read ? 1 : 0,
35127
+ archived: message.archived ? 1 : 0,
35050
35128
  metadata: message.metadata,
35051
35129
  createdAt: message.createdAt,
35052
35130
  updatedAt: message.updatedAt
@@ -35098,6 +35176,7 @@ async function queryMessagesByParticipant(handle, direction, ownerId, ownerType,
35098
35176
  if (filter?.read !== void 0) {
35099
35177
  conditions.push(eq(project_exports.messages.read, filter.read ? 1 : 0));
35100
35178
  }
35179
+ conditions.push(archivedCondition(filter?.archived));
35101
35180
  const limit = filter?.limit ?? 100;
35102
35181
  const offset = filter?.offset ?? 0;
35103
35182
  const rows = await handle.select(messageColumns).from(project_exports.messages).where(and(...conditions)).orderBy(desc(project_exports.messages.createdAt), desc(project_exports.messages.id)).limit(limit).offset(offset);
@@ -35111,6 +35190,13 @@ async function markMessageAsRead(handle, messageId) {
35111
35190
  await handle.update(project_exports.messages).set({ read: 1, updatedAt: now }).where(eq(project_exports.messages.id, messageId));
35112
35191
  return await getMessage(handle, messageId);
35113
35192
  }
35193
+ async function setMessageArchived(handle, id, archived) {
35194
+ const existing = await getMessage(handle, id);
35195
+ if (!existing) return null;
35196
+ if (existing.archived === archived) return existing;
35197
+ const rows = await handle.update(project_exports.messages).set({ archived: archived ? 1 : 0, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(project_exports.messages.id, id)).returning(messageColumns);
35198
+ return rows[0] ? rowToMessage(rows[0]) : null;
35199
+ }
35114
35200
  async function markAllMessagesAsRead(handle, ownerId, ownerType) {
35115
35201
  const now = (/* @__PURE__ */ new Date()).toISOString();
35116
35202
  const participantIds = participantIdsForLookup(ownerId, ownerType);
@@ -35118,7 +35204,8 @@ async function markAllMessagesAsRead(handle, ownerId, ownerType) {
35118
35204
  and(
35119
35205
  inArray(project_exports.messages.toId, participantIds),
35120
35206
  eq(project_exports.messages.toType, ownerType),
35121
- eq(project_exports.messages.read, 0)
35207
+ eq(project_exports.messages.read, 0),
35208
+ archivedCondition()
35122
35209
  )
35123
35210
  );
35124
35211
  const count2 = countRows[0]?.count ?? 0;
@@ -35126,7 +35213,8 @@ async function markAllMessagesAsRead(handle, ownerId, ownerType) {
35126
35213
  and(
35127
35214
  inArray(project_exports.messages.toId, participantIds),
35128
35215
  eq(project_exports.messages.toType, ownerType),
35129
- eq(project_exports.messages.read, 0)
35216
+ eq(project_exports.messages.read, 0),
35217
+ archivedCondition()
35130
35218
  )
35131
35219
  );
35132
35220
  return count2;
@@ -35148,19 +35236,22 @@ async function getConversation(handle, participantA, participantB, options) {
35148
35236
  const aIds = participantIdsForLookup(participantA.id, participantA.type);
35149
35237
  const bIds = participantIdsForLookup(participantB.id, participantB.type);
35150
35238
  const rows = await handle.select(messageColumns).from(project_exports.messages).where(
35151
- or(
35152
- and(
35153
- inArray(project_exports.messages.fromId, aIds),
35154
- eq(project_exports.messages.fromType, participantA.type),
35155
- inArray(project_exports.messages.toId, bIds),
35156
- eq(project_exports.messages.toType, participantB.type)
35239
+ and(
35240
+ or(
35241
+ and(
35242
+ inArray(project_exports.messages.fromId, aIds),
35243
+ eq(project_exports.messages.fromType, participantA.type),
35244
+ inArray(project_exports.messages.toId, bIds),
35245
+ eq(project_exports.messages.toType, participantB.type)
35246
+ ),
35247
+ and(
35248
+ inArray(project_exports.messages.fromId, bIds),
35249
+ eq(project_exports.messages.fromType, participantB.type),
35250
+ inArray(project_exports.messages.toId, aIds),
35251
+ eq(project_exports.messages.toType, participantA.type)
35252
+ )
35157
35253
  ),
35158
- and(
35159
- inArray(project_exports.messages.fromId, bIds),
35160
- eq(project_exports.messages.fromType, participantB.type),
35161
- inArray(project_exports.messages.toId, aIds),
35162
- eq(project_exports.messages.toType, participantA.type)
35163
- )
35254
+ archivedCondition(options?.archived)
35164
35255
  )
35165
35256
  ).orderBy(desc(project_exports.messages.createdAt)).limit(limit);
35166
35257
  return rows.reverse().map((row) => rowToMessage(row));
@@ -35171,14 +35262,16 @@ async function getMailbox(handle, ownerId, ownerType) {
35171
35262
  and(
35172
35263
  inArray(project_exports.messages.toId, participantIds),
35173
35264
  eq(project_exports.messages.toType, ownerType),
35174
- eq(project_exports.messages.read, 0)
35265
+ eq(project_exports.messages.read, 0),
35266
+ archivedCondition()
35175
35267
  )
35176
35268
  );
35177
35269
  const unreadCount = unreadRows[0]?.count ?? 0;
35178
35270
  const lastRows = await handle.select(messageColumns).from(project_exports.messages).where(
35179
35271
  and(
35180
35272
  inArray(project_exports.messages.toId, participantIds),
35181
- eq(project_exports.messages.toType, ownerType)
35273
+ eq(project_exports.messages.toType, ownerType),
35274
+ archivedCondition()
35182
35275
  )
35183
35276
  ).orderBy(desc(project_exports.messages.createdAt), desc(project_exports.messages.id)).limit(1);
35184
35277
  return {
@@ -35188,15 +35281,16 @@ async function getMailbox(handle, ownerId, ownerType) {
35188
35281
  lastMessage: lastRows[0] ? rowToMessage(lastRows[0]) : void 0
35189
35282
  };
35190
35283
  }
35191
- async function getAllAgentToAgentMessages(handle) {
35192
- const rows = await handle.select(messageColumns).from(project_exports.messages).where(eq(project_exports.messages.type, "agent-to-agent")).orderBy(desc(project_exports.messages.createdAt), desc(project_exports.messages.id));
35284
+ async function getAllAgentToAgentMessages(handle, filter) {
35285
+ const rows = await handle.select(messageColumns).from(project_exports.messages).where(and(eq(project_exports.messages.type, "agent-to-agent"), archivedCondition(filter?.archived))).orderBy(desc(project_exports.messages.createdAt), desc(project_exports.messages.id));
35193
35286
  return rows.map((row) => rowToMessage(row));
35194
35287
  }
35195
35288
  async function getUnreadAgentToAgentCount(handle) {
35196
35289
  const rows = await handle.select({ count: sql`count(*)::int` }).from(project_exports.messages).where(
35197
35290
  and(
35198
35291
  eq(project_exports.messages.type, "agent-to-agent"),
35199
- eq(project_exports.messages.read, 0)
35292
+ eq(project_exports.messages.read, 0),
35293
+ archivedCondition()
35200
35294
  )
35201
35295
  );
35202
35296
  return rows[0]?.count ?? 0;
@@ -35218,6 +35312,7 @@ var init_async_message_store = __esm({
35218
35312
  content: project_exports.messages.content,
35219
35313
  type: project_exports.messages.type,
35220
35314
  read: project_exports.messages.read,
35315
+ archived: project_exports.messages.archived,
35221
35316
  metadata: project_exports.messages.metadata,
35222
35317
  createdAt: project_exports.messages.createdAt,
35223
35318
  updatedAt: project_exports.messages.updatedAt
@@ -35403,12 +35498,12 @@ var init_message_store = __esm({
35403
35498
  }
35404
35499
  const sqliteDb = this.db;
35405
35500
  this.stmtInsert = sqliteDb.prepare(`
35406
- INSERT INTO messages (id, fromId, fromType, toId, toType, content, type, read, metadata, createdAt, updatedAt)
35407
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
35501
+ INSERT INTO messages (id, fromId, fromType, toId, toType, content, type, read, archived, metadata, createdAt, updatedAt)
35502
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
35408
35503
  `);
35409
35504
  this.stmtInsertOnce = sqliteDb.prepare(`
35410
- INSERT OR IGNORE INTO messages (id, fromId, fromType, toId, toType, content, type, read, metadata, createdAt, updatedAt)
35411
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
35505
+ INSERT OR IGNORE INTO messages (id, fromId, fromType, toId, toType, content, type, read, archived, metadata, createdAt, updatedAt)
35506
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
35412
35507
  `);
35413
35508
  this.stmtGetById = sqliteDb.prepare(`
35414
35509
  SELECT * FROM messages WHERE id = ?
@@ -35446,6 +35541,7 @@ var init_message_store = __esm({
35446
35541
  content: row.content,
35447
35542
  type: row.type,
35448
35543
  read: row.read === 1,
35544
+ archived: (row.archived ?? 0) === 1,
35449
35545
  metadata: fromJson(row.metadata),
35450
35546
  createdAt: row.createdAt,
35451
35547
  updatedAt: row.updatedAt
@@ -35479,6 +35575,7 @@ var init_message_store = __esm({
35479
35575
  content: sanitizeTextValue(input.content),
35480
35576
  type: input.type,
35481
35577
  read: false,
35578
+ archived: false,
35482
35579
  metadata: sanitizeJsonbValue(input.metadata),
35483
35580
  createdAt: now,
35484
35581
  updatedAt: now
@@ -35494,6 +35591,7 @@ var init_message_store = __esm({
35494
35591
  content: message.content,
35495
35592
  type: message.type,
35496
35593
  read: message.read,
35594
+ archived: message.archived,
35497
35595
  metadata: message.metadata ?? null,
35498
35596
  createdAt: message.createdAt,
35499
35597
  updatedAt: message.updatedAt
@@ -35508,6 +35606,7 @@ var init_message_store = __esm({
35508
35606
  message.content,
35509
35607
  message.type,
35510
35608
  message.read ? 1 : 0,
35609
+ message.archived ? 1 : 0,
35511
35610
  toJsonNullable(message.metadata),
35512
35611
  message.createdAt,
35513
35612
  message.updatedAt
@@ -35566,6 +35665,7 @@ var init_message_store = __esm({
35566
35665
  content: sanitizeTextValue(input.content),
35567
35666
  type: input.type,
35568
35667
  read: false,
35668
+ archived: false,
35569
35669
  metadata: sanitizeJsonbValue(input.metadata),
35570
35670
  createdAt: now,
35571
35671
  updatedAt: now
@@ -35586,6 +35686,7 @@ var init_message_store = __esm({
35586
35686
  message.content,
35587
35687
  message.type,
35588
35688
  0,
35689
+ 0,
35589
35690
  toJsonNullable(message.metadata),
35590
35691
  message.createdAt,
35591
35692
  message.updatedAt
@@ -35717,6 +35818,7 @@ var init_message_store = __esm({
35717
35818
  whereClauses.push("read = ?");
35718
35819
  params.push(filter.read ? 1 : 0);
35719
35820
  }
35821
+ whereClauses.push(filter?.archived === true ? "archived = 1" : "(archived = 0 OR archived IS NULL)");
35720
35822
  const whereSql = whereClauses.join(" AND ");
35721
35823
  const limit = filter?.limit ?? 100;
35722
35824
  const offset = filter?.offset ?? 0;
@@ -35767,15 +35869,39 @@ var init_message_store = __esm({
35767
35869
  const participantIds = this.getParticipantIdsForLookup(ownerId, ownerType);
35768
35870
  const toIdPredicate = participantIds.length === 1 ? "toId = ?" : `toId IN (${participantIds.map(() => "?").join(", ")})`;
35769
35871
  const unreadRow = this.db.prepare(`
35770
- SELECT COUNT(*) as count FROM messages WHERE ${toIdPredicate} AND toType = ? AND read = 0
35872
+ SELECT COUNT(*) as count FROM messages WHERE ${toIdPredicate} AND toType = ? AND read = 0 AND (archived = 0 OR archived IS NULL)
35771
35873
  `).get(...participantIds, ownerType);
35772
35874
  const count2 = unreadRow?.count ?? 0;
35773
35875
  this.db.prepare(`
35774
- UPDATE messages SET read = 1, updatedAt = ? WHERE ${toIdPredicate} AND toType = ? AND read = 0
35876
+ UPDATE messages SET read = 1, updatedAt = ? WHERE ${toIdPredicate} AND toType = ? AND read = 0 AND (archived = 0 OR archived IS NULL)
35775
35877
  `).run(now, ...participantIds, ownerType);
35776
35878
  this.db.bumpLastModified();
35777
35879
  return count2;
35778
35880
  }
35881
+ /** Archive a message without destroying its correspondence history. */
35882
+ async archiveMessage(id) {
35883
+ return this.setMessageArchived(id, true);
35884
+ }
35885
+ /** Restore a previously archived message to default mailbox lists. */
35886
+ async unarchiveMessage(id) {
35887
+ return this.setMessageArchived(id, false);
35888
+ }
35889
+ async setMessageArchived(id, archived) {
35890
+ if (this.asyncLayer) {
35891
+ const updated2 = await setMessageArchived(this.asyncLayer.db, id, archived);
35892
+ if (!updated2) throw new Error(`Message ${id} not found`);
35893
+ this.emit(archived ? "message:archived" : "message:unarchived", updated2);
35894
+ return updated2;
35895
+ }
35896
+ const existing = await this.getMessage(id);
35897
+ if (!existing) throw new Error(`Message ${id} not found`);
35898
+ if (existing.archived === archived) return existing;
35899
+ this.db.prepare("UPDATE messages SET archived = ?, updatedAt = ? WHERE id = ?").run(archived ? 1 : 0, (/* @__PURE__ */ new Date()).toISOString(), id);
35900
+ this.db.bumpLastModified();
35901
+ const updated = await this.getMessage(id);
35902
+ this.emit(archived ? "message:archived" : "message:unarchived", updated);
35903
+ return updated;
35904
+ }
35779
35905
  /**
35780
35906
  * Delete a message by ID.
35781
35907
  * @param id - The message ID
@@ -35866,7 +35992,7 @@ var init_message_store = __esm({
35866
35992
  (${participantAFromPredicate} AND fromType = ? AND ${participantBToPredicate} AND toType = ?)
35867
35993
  OR
35868
35994
  (${participantBFromPredicate} AND fromType = ? AND ${participantAToPredicate} AND toType = ?)
35869
- )
35995
+ ) AND ${options?.archived === true ? "archived = 1" : "(archived = 0 OR archived IS NULL)"}
35870
35996
  ORDER BY createdAt DESC
35871
35997
  LIMIT ?
35872
35998
  `).all(
@@ -35901,11 +36027,11 @@ var init_message_store = __esm({
35901
36027
  const participantIds = this.getParticipantIdsForLookup(ownerId, ownerType);
35902
36028
  const toIdPredicate = participantIds.length === 1 ? "toId = ?" : `toId IN (${participantIds.map(() => "?").join(", ")})`;
35903
36029
  const unreadRow = this.db.prepare(`
35904
- SELECT COUNT(*) as count FROM messages WHERE ${toIdPredicate} AND toType = ? AND read = 0
36030
+ SELECT COUNT(*) as count FROM messages WHERE ${toIdPredicate} AND toType = ? AND read = 0 AND (archived = 0 OR archived IS NULL)
35905
36031
  `).get(...participantIds, ownerType);
35906
36032
  const unreadCount = unreadRow?.count ?? 0;
35907
36033
  const lastRow = this.db.prepare(`
35908
- SELECT * FROM messages WHERE ${toIdPredicate} AND toType = ? ORDER BY createdAt DESC, rowid DESC LIMIT 1
36034
+ SELECT * FROM messages WHERE ${toIdPredicate} AND toType = ? AND (archived = 0 OR archived IS NULL) ORDER BY createdAt DESC, rowid DESC LIMIT 1
35909
36035
  `).get(...participantIds, ownerType);
35910
36036
  const lastMessage = lastRow ? this.rowToMessage(lastRow) : void 0;
35911
36037
  return {
@@ -35919,13 +36045,13 @@ var init_message_store = __esm({
35919
36045
  * Get all agent-to-agent messages across all agents.
35920
36046
  * @returns Array of messages (newest first)
35921
36047
  */
35922
- async getAllAgentToAgentMessages() {
36048
+ async getAllAgentToAgentMessages(filter) {
35923
36049
  if (this.asyncLayer) {
35924
- return getAllAgentToAgentMessages(this.asyncLayer.db);
36050
+ return getAllAgentToAgentMessages(this.asyncLayer.db, filter);
35925
36051
  }
35926
36052
  const rows = this.db.prepare(`
35927
36053
  SELECT * FROM messages
35928
- WHERE type = ?
36054
+ WHERE type = ? AND ${filter?.archived === true ? "archived = 1" : "(archived = 0 OR archived IS NULL)"}
35929
36055
  ORDER BY createdAt DESC, rowid DESC
35930
36056
  `).all("agent-to-agent");
35931
36057
  return rows.map((row) => this.rowToMessage(row));
@@ -35939,7 +36065,7 @@ var init_message_store = __esm({
35939
36065
  }
35940
36066
  const row = this.db.prepare(`
35941
36067
  SELECT COUNT(*) as count FROM messages
35942
- WHERE type = ? AND read = 0
36068
+ WHERE type = ? AND read = 0 AND (archived = 0 OR archived IS NULL)
35943
36069
  `).get("agent-to-agent");
35944
36070
  return row?.count ?? 0;
35945
36071
  }
@@ -36038,6 +36164,7 @@ async function createApprovalRequest(layer, input) {
36038
36164
  };
36039
36165
  await layer.transactionImmediate(async (tx) => {
36040
36166
  await tx.insert(project_exports.approvalRequests).values({
36167
+ ...layer.projectId?.trim() ? { projectId: layer.projectId } : {},
36041
36168
  id: request.id,
36042
36169
  status: request.status,
36043
36170
  requesterActorId: request.requester.actorId,
@@ -36061,12 +36188,12 @@ async function createApprovalRequest(layer, input) {
36061
36188
  });
36062
36189
  return request;
36063
36190
  }
36064
- async function getApprovalRequest(handle, id) {
36065
- const rows = await handle.select().from(project_exports.approvalRequests).where(eq(project_exports.approvalRequests.id, id));
36191
+ async function getApprovalRequest(handle, id, projectId2) {
36192
+ const rows = await handle.select().from(project_exports.approvalRequests).where(and(eq(project_exports.approvalRequests.id, id), projectScopeFor(project_exports.approvalRequests.projectId, projectId2)));
36066
36193
  return rows[0] ? rowToRequest(rows[0]) : null;
36067
36194
  }
36068
- async function listApprovalRequests(handle, input = {}) {
36069
- const conditions = [];
36195
+ async function listApprovalRequests(handle, input = {}, projectId2) {
36196
+ const conditions = [projectScopeFor(project_exports.approvalRequests.projectId, projectId2)];
36070
36197
  if (input.status) conditions.push(eq(project_exports.approvalRequests.status, input.status));
36071
36198
  if (input.requesterActorId) conditions.push(eq(project_exports.approvalRequests.requesterActorId, input.requesterActorId));
36072
36199
  if (input.taskId) conditions.push(eq(project_exports.approvalRequests.taskId, input.taskId));
@@ -36080,7 +36207,7 @@ async function listApprovalRequests(handle, input = {}) {
36080
36207
  async function decideApprovalRequest(layer, requestId, status, input) {
36081
36208
  const now = (/* @__PURE__ */ new Date()).toISOString();
36082
36209
  return layer.transactionImmediate(async (tx) => {
36083
- const existing = await getApprovalRequest(tx, requestId);
36210
+ const existing = await getApprovalRequest(tx, requestId, layer.projectId);
36084
36211
  if (!existing) throw new Error(`Approval request ${requestId} not found`);
36085
36212
  if (!isValidApprovalRequestTransition(existing.status, status)) {
36086
36213
  throw new Error(`Invalid approval request transition: ${existing.status} -> ${status}`);
@@ -36091,23 +36218,24 @@ async function decideApprovalRequest(layer, requestId, status, input) {
36091
36218
  const updatedRows = await tx.update(project_exports.approvalRequests).set({ status, decidedAt: now, updatedAt: now }).where(
36092
36219
  and(
36093
36220
  eq(project_exports.approvalRequests.id, requestId),
36094
- eq(project_exports.approvalRequests.status, existing.status)
36221
+ eq(project_exports.approvalRequests.status, existing.status),
36222
+ projectScopeFor(project_exports.approvalRequests.projectId, layer.projectId)
36095
36223
  )
36096
36224
  ).returning({ id: project_exports.approvalRequests.id });
36097
36225
  if (updatedRows.length === 0) {
36098
- const raced = await getApprovalRequest(tx, requestId);
36226
+ const raced = await getApprovalRequest(tx, requestId, layer.projectId);
36099
36227
  throw new Error(
36100
36228
  `Invalid approval request transition: ${raced?.status ?? existing.status} -> ${status}`
36101
36229
  );
36102
36230
  }
36103
36231
  await appendAuditEvent(tx, layer.projectId ?? "", requestId, status, input.actor, now, input.note);
36104
- return await getApprovalRequest(tx, requestId);
36232
+ return await getApprovalRequest(tx, requestId, layer.projectId);
36105
36233
  });
36106
36234
  }
36107
36235
  async function markApprovalRequestCompleted(layer, requestId, input) {
36108
36236
  const now = (/* @__PURE__ */ new Date()).toISOString();
36109
36237
  return layer.transactionImmediate(async (tx) => {
36110
- const existing = await getApprovalRequest(tx, requestId);
36238
+ const existing = await getApprovalRequest(tx, requestId, layer.projectId);
36111
36239
  if (!existing) throw new Error(`Approval request ${requestId} not found`);
36112
36240
  if (!isValidApprovalRequestTransition(existing.status, "completed")) {
36113
36241
  throw new Error(`Invalid approval request transition: ${existing.status} -> completed`);
@@ -36121,21 +36249,22 @@ async function markApprovalRequestCompleted(layer, requestId, input) {
36121
36249
  const updatedRows = await tx.update(project_exports.approvalRequests).set({ status: "completed", completedAt: now, updatedAt: now }).where(
36122
36250
  and(
36123
36251
  eq(project_exports.approvalRequests.id, requestId),
36124
- eq(project_exports.approvalRequests.status, existing.status)
36252
+ eq(project_exports.approvalRequests.status, existing.status),
36253
+ projectScopeFor(project_exports.approvalRequests.projectId, layer.projectId)
36125
36254
  )
36126
36255
  ).returning({ id: project_exports.approvalRequests.id });
36127
36256
  if (updatedRows.length === 0) {
36128
- const raced = await getApprovalRequest(tx, requestId);
36257
+ const raced = await getApprovalRequest(tx, requestId, layer.projectId);
36129
36258
  throw new Error(
36130
36259
  `Invalid approval request transition: ${raced?.status ?? existing.status} -> completed`
36131
36260
  );
36132
36261
  }
36133
36262
  await appendAuditEvent(tx, layer.projectId ?? "", requestId, "completed", input.actor, now, input.note);
36134
- return await getApprovalRequest(tx, requestId);
36263
+ return await getApprovalRequest(tx, requestId, layer.projectId);
36135
36264
  });
36136
36265
  }
36137
- async function getApprovalAuditHistory(handle, requestId) {
36138
- const rows = await handle.select().from(project_exports.approvalRequestAuditEvents).where(eq(project_exports.approvalRequestAuditEvents.requestId, requestId)).orderBy(
36266
+ async function getApprovalAuditHistory(handle, requestId, projectId2) {
36267
+ const rows = await handle.select().from(project_exports.approvalRequestAuditEvents).where(and(eq(project_exports.approvalRequestAuditEvents.requestId, requestId), projectScopeFor(project_exports.approvalRequestAuditEvents.projectId, projectId2))).orderBy(
36139
36268
  sql`${project_exports.approvalRequestAuditEvents.createdAt} ASC, ${project_exports.approvalRequestAuditEvents.id} ASC`
36140
36269
  );
36141
36270
  return rows.map((row) => rowToAuditEvent(row));
@@ -36145,6 +36274,7 @@ var init_async_approval_request_store = __esm({
36145
36274
  "use strict";
36146
36275
  init_drizzle_orm();
36147
36276
  init_schema2();
36277
+ init_data_layer();
36148
36278
  init_agents();
36149
36279
  init_types();
36150
36280
  }
@@ -36158,6 +36288,7 @@ var init_approval_request_store = __esm({
36158
36288
  "use strict";
36159
36289
  init_drizzle_orm();
36160
36290
  init_db2();
36291
+ init_data_layer();
36161
36292
  init_async_approval_request_store();
36162
36293
  init_schema2();
36163
36294
  init_async_agent_activity();
@@ -36286,23 +36417,26 @@ var init_approval_request_store = __esm({
36286
36417
  return created;
36287
36418
  }
36288
36419
  async get(id) {
36289
- return getApprovalRequest(this.asyncLayer.db, id);
36420
+ return getApprovalRequest(this.asyncLayer.db, id, this.asyncLayer.projectId);
36290
36421
  }
36291
36422
  async list(input = {}) {
36292
- return listApprovalRequests(this.asyncLayer.db, input);
36423
+ return listApprovalRequests(this.asyncLayer.db, input, this.asyncLayer.projectId);
36293
36424
  }
36294
36425
  async getPendingCountsByActor() {
36295
36426
  const table = project_exports.approvalRequests;
36296
36427
  const rows = await this.asyncLayer.db.select({
36297
36428
  actorId: table.requesterActorId,
36298
36429
  requestCount: count()
36299
- }).from(table).where(eq(table.status, "pending")).groupBy(table.requesterActorId);
36430
+ }).from(table).where(and(eq(table.status, "pending"), projectScopeFor(table.projectId, this.asyncLayer.projectId))).groupBy(table.requesterActorId);
36300
36431
  return new Map(rows.map((row) => [row.actorId, Number(row.requestCount)]));
36301
36432
  }
36302
36433
  async findLatestByDedupeKey(input) {
36303
36434
  if (this.backendMode) {
36304
36435
  const table = project_exports.approvalRequests;
36305
- const conditions = [eq(table.requesterActorId, input.requesterActorId)];
36436
+ const conditions = [
36437
+ eq(table.requesterActorId, input.requesterActorId),
36438
+ projectScopeFor(table.projectId, this.asyncLayer.projectId)
36439
+ ];
36306
36440
  if (input.taskId !== void 0) {
36307
36441
  conditions.push(eq(table.taskId, input.taskId));
36308
36442
  }
@@ -36341,7 +36475,7 @@ var init_approval_request_store = __esm({
36341
36475
  return markApprovalRequestCompleted(this.asyncLayer, requestId, input);
36342
36476
  }
36343
36477
  async getAuditHistory(requestId) {
36344
- return getApprovalAuditHistory(this.asyncLayer.db, requestId);
36478
+ return getApprovalAuditHistory(this.asyncLayer.db, requestId, this.asyncLayer.projectId);
36345
36479
  }
36346
36480
  };
36347
36481
  }
@@ -40527,6 +40661,8 @@ async function applySchemaBaseline(db, options = {}) {
40527
40661
  const agentRatingProjectIsolationAlreadyApplied = applied.includes(AGENT_RATING_PROJECT_ISOLATION_VERSION);
40528
40662
  const agentRatingsProjectPartitionAlreadyApplied = applied.includes(AGENT_RATINGS_PROJECT_PARTITION_VERSION);
40529
40663
  const projectOwnershipDeclarationDriftAlreadyApplied = applied.includes(PROJECT_OWNERSHIP_DECLARATION_DRIFT_VERSION);
40664
+ const projectOwnershipDefaultReconciliationAlreadyApplied = applied.includes(PROJECT_OWNERSHIP_DEFAULT_RECONCILIATION_VERSION);
40665
+ const messageArchiveSchemaAlreadyApplied = applied.includes(MESSAGE_ARCHIVE_SCHEMA_VERSION);
40530
40666
  assertBinaryNotOlderThanDatabase(applied);
40531
40667
  let schemaChanged = false;
40532
40668
  if (!baselineAlreadyApplied) {
@@ -41010,17 +41146,29 @@ async function applySchemaBaseline(db, options = {}) {
41010
41146
  await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${PROJECT_OWNERSHIP_DECLARATION_DRIFT_VERSION}) ON CONFLICT (version) DO NOTHING`);
41011
41147
  schemaChanged = true;
41012
41148
  }
41149
+ if (!projectOwnershipDefaultReconciliationAlreadyApplied) {
41150
+ const migrationSql = await readFile4(PROJECT_OWNERSHIP_DEFAULT_RECONCILIATION_MIGRATION_PATH, "utf8");
41151
+ await tx.execute(sql.raw(migrationSql));
41152
+ await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${PROJECT_OWNERSHIP_DEFAULT_RECONCILIATION_VERSION}) ON CONFLICT (version) DO NOTHING`);
41153
+ schemaChanged = true;
41154
+ }
41155
+ if (!messageArchiveSchemaAlreadyApplied) {
41156
+ const migrationSql = await readFile4(MESSAGE_ARCHIVE_SCHEMA_MIGRATION_PATH, "utf8");
41157
+ await tx.execute(sql.raw(migrationSql));
41158
+ await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${MESSAGE_ARCHIVE_SCHEMA_VERSION}) ON CONFLICT (version) DO NOTHING`);
41159
+ schemaChanged = true;
41160
+ }
41013
41161
  return { applied: schemaChanged, pluginHooksRun: pluginHooks.length };
41014
41162
  });
41015
41163
  }
41016
- var SCHEMA_BASELINE_VERSION, TASK_DECLARED_SYMBOLS_VERSION, INITIAL_SCHEMA_VERSION, AUTOMATION_ISOLATION_SCHEMA_VERSION, ANALYTICS_ISOLATION_SCHEMA_VERSION, MONITOR_APPROVAL_ISOLATION_SCHEMA_VERSION, LEGACY_CUTOVER_PRESERVATION_SCHEMA_VERSION, MULTI_PROJECT_CUTOVER_SCHEMA_VERSION, PROJECT_OWNERSHIP_SCHEMA_VERSION, SQLITE_SCHEMA_PARITY_VERSION, SESSION_ADVISOR_ENABLED_SCHEMA_VERSION, MISSION_FIX_IDEMPOTENCY_VERSION, IMPORT_TRANSLATION_CACHE_VERSION, IMPORT_TRANSLATION_CACHE_SCOPE_FIX_VERSION, IMPORT_TRANSLATION_CACHE_LEGACY_PARTITION_BACKFILL_VERSION, OWNER_PROJECT_ID_SPLIT_VERSION, CHAT_SESSION_PINS_VERSION, EXECUTOR_TOOL_FAILURE_RETRY_VERSION, EXECUTOR_ESCALATION_ATTEMPT_VERSION, GLOBAL_ROUTINES_SCHEMA_VERSION, TASK_MERGER_MODEL_LANE_VERSION, BULK_COMPLETION_REFUSAL_AT_VERSION, TASK_PROPOSAL_CLAIM_VERSION, CONFIGURATION_REVISIONS_VERSION, IDEATION_SCHEMA_VERSION, RESEARCH_FEATURE_PROVENANCE_VERSION, TASK_VERIFICATION_REQUEST_VERSION, SYMBOL_LOCKS_SCHEMA_VERSION, BIGINT_COUNTERS_VERSION, PLANNING_ACTIVE_TIMING_VERSION, SQLITE_MIGRATION_RUNTIME_READ_VERSION, WORKFLOW_TASK_CONTINUATIONS_VERSION, LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_VERSION, TASK_WEDGE_NOTIFICATION_VERSION, MILESTONE_ASSERTION_PROVENANCE_VERSION, MISSION_LINEAGE_STOP_VERSION, CHAT_SESSION_TAGS_VERSION, DROP_GLOBAL_CONCURRENCY_VERSION, MISSION_TASK_PREFIX_VERSION, CREDENTIAL_INSTANCE_SELECTION_VERSION, TASK_LIFECYCLE_OUTBOX_VERSION, TASK_LIFECYCLE_CONSUMERS_VERSION, VALIDATOR_INPUT_FINGERPRINT_VERSION, UNPLANNED_EXECUTION_BLOCK_DEDUPE_VERSION, QUEUED_EPISODE_SIGNATURE_VERSION, MULTI_ROLE_WORKFLOW_AGENTS_VERSION, WORKFLOW_PRINCIPAL_FENCE_VERSION, TASK_RECOMMENDATIONS_VERSION, GITHUB_CHECK_STATES_VERSION, AGENT_ACTIVITY_EVENTS_VERSION, SPEC_LOCK_DRIFT_REPORT_VERSION, SPEC_LOCK_SOURCE_REVISION_BIGINT_VERSION, MEMORY_RECALL_RECORDS_VERSION, MISSION_FEATURE_SPEC_ALIGNMENT_VERSION, AGENT_RATING_PROJECT_ISOLATION_VERSION, AGENT_RATINGS_PROJECT_PARTITION_VERSION, PROJECT_OWNERSHIP_DECLARATION_DRIFT_VERSION, LEGACY_ADOPTION_DRAINED_MARKER_FUNCTION, StaleBinarySchemaError, MIGRATION_BOOKKEEPING_TABLE, LEGACY_ADOPTION_DRAINED_MARKER, __dirname, MIGRATIONS_DIR, BASELINE_MIGRATION_PATH, AUTOMATION_ISOLATION_MIGRATION_PATH, ANALYTICS_ISOLATION_MIGRATION_PATH, MONITOR_APPROVAL_ISOLATION_MIGRATION_PATH, LEGACY_CUTOVER_PRESERVATION_MIGRATION_PATH, MULTI_PROJECT_CUTOVER_MIGRATION_PATH, PROJECT_OWNERSHIP_MIGRATION_PATH, SQLITE_SCHEMA_PARITY_MIGRATION_PATH, SESSION_ADVISOR_ENABLED_MIGRATION_PATH, MISSION_FIX_IDEMPOTENCY_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_SCOPE_FIX_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_LEGACY_PARTITION_BACKFILL_MIGRATION_PATH, OWNER_PROJECT_ID_SPLIT_MIGRATION_PATH, CHAT_SESSION_PINS_MIGRATION_PATH, EXECUTOR_TOOL_FAILURE_RETRY_MIGRATION_PATH, EXECUTOR_ESCALATION_ATTEMPT_MIGRATION_PATH, GLOBAL_ROUTINES_MIGRATION_PATH, TASK_MERGER_MODEL_LANE_MIGRATION_PATH, BULK_COMPLETION_REFUSAL_AT_MIGRATION_PATH, TASK_PROPOSAL_CLAIM_MIGRATION_PATH, CONFIGURATION_REVISIONS_MIGRATION_PATH, IDEATION_MIGRATION_PATH, RESEARCH_FEATURE_PROVENANCE_MIGRATION_PATH, TASK_VERIFICATION_REQUEST_MIGRATION_PATH, SYMBOL_LOCKS_MIGRATION_PATH, BIGINT_COUNTERS_MIGRATION_PATH, WORKFLOW_IR_PIN_AND_LEGACY_ADOPTION_VERSION, WORKFLOW_IR_PIN_AND_LEGACY_ADOPTION_MIGRATION_PATH, PLANNING_ACTIVE_TIMING_MIGRATION_PATH, TASK_DECLARED_SYMBOLS_MIGRATION_PATH, SQLITE_MIGRATION_RUNTIME_READ_PATH, WORKFLOW_TASK_CONTINUATIONS_PATH, LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_PATH, TASK_WEDGE_NOTIFICATION_MIGRATION_PATH, MILESTONE_ASSERTION_PROVENANCE_MIGRATION_PATH, MISSION_LINEAGE_STOP_MIGRATION_PATH, CHAT_SESSION_TAGS_MIGRATION_PATH, DROP_GLOBAL_CONCURRENCY_MIGRATION_PATH, MISSION_TASK_PREFIX_MIGRATION_PATH, CREDENTIAL_INSTANCE_SELECTION_MIGRATION_PATH, TASK_LIFECYCLE_OUTBOX_MIGRATION_PATH, TASK_LIFECYCLE_CONSUMERS_MIGRATION_PATH, VALIDATOR_INPUT_FINGERPRINT_MIGRATION_PATH, UNPLANNED_EXECUTION_BLOCK_DEDUPE_MIGRATION_PATH, QUEUED_EPISODE_SIGNATURE_MIGRATION_PATH, MULTI_ROLE_WORKFLOW_AGENTS_MIGRATION_PATH, WORKFLOW_PRINCIPAL_FENCE_MIGRATION_PATH, TASK_RECOMMENDATIONS_MIGRATION_PATH, GITHUB_CHECK_STATES_MIGRATION_PATH, AGENT_ACTIVITY_EVENTS_MIGRATION_PATH, SPEC_LOCK_DRIFT_REPORT_MIGRATION_PATH, SPEC_LOCK_SOURCE_REVISION_BIGINT_MIGRATION_PATH, MEMORY_RECALL_RECORDS_MIGRATION_PATH, MISSION_FEATURE_SPEC_ALIGNMENT_MIGRATION_PATH, AGENT_RATING_PROJECT_ISOLATION_MIGRATION_PATH, AGENT_RATINGS_PROJECT_PARTITION_MIGRATION_PATH, PROJECT_OWNERSHIP_DECLARATION_DRIFT_MIGRATION_PATH;
41164
+ var SCHEMA_BASELINE_VERSION, TASK_DECLARED_SYMBOLS_VERSION, INITIAL_SCHEMA_VERSION, AUTOMATION_ISOLATION_SCHEMA_VERSION, ANALYTICS_ISOLATION_SCHEMA_VERSION, MONITOR_APPROVAL_ISOLATION_SCHEMA_VERSION, LEGACY_CUTOVER_PRESERVATION_SCHEMA_VERSION, MULTI_PROJECT_CUTOVER_SCHEMA_VERSION, PROJECT_OWNERSHIP_SCHEMA_VERSION, SQLITE_SCHEMA_PARITY_VERSION, SESSION_ADVISOR_ENABLED_SCHEMA_VERSION, MISSION_FIX_IDEMPOTENCY_VERSION, IMPORT_TRANSLATION_CACHE_VERSION, IMPORT_TRANSLATION_CACHE_SCOPE_FIX_VERSION, IMPORT_TRANSLATION_CACHE_LEGACY_PARTITION_BACKFILL_VERSION, OWNER_PROJECT_ID_SPLIT_VERSION, CHAT_SESSION_PINS_VERSION, EXECUTOR_TOOL_FAILURE_RETRY_VERSION, EXECUTOR_ESCALATION_ATTEMPT_VERSION, GLOBAL_ROUTINES_SCHEMA_VERSION, TASK_MERGER_MODEL_LANE_VERSION, BULK_COMPLETION_REFUSAL_AT_VERSION, TASK_PROPOSAL_CLAIM_VERSION, CONFIGURATION_REVISIONS_VERSION, IDEATION_SCHEMA_VERSION, RESEARCH_FEATURE_PROVENANCE_VERSION, TASK_VERIFICATION_REQUEST_VERSION, SYMBOL_LOCKS_SCHEMA_VERSION, BIGINT_COUNTERS_VERSION, PLANNING_ACTIVE_TIMING_VERSION, SQLITE_MIGRATION_RUNTIME_READ_VERSION, WORKFLOW_TASK_CONTINUATIONS_VERSION, LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_VERSION, TASK_WEDGE_NOTIFICATION_VERSION, MILESTONE_ASSERTION_PROVENANCE_VERSION, MISSION_LINEAGE_STOP_VERSION, CHAT_SESSION_TAGS_VERSION, DROP_GLOBAL_CONCURRENCY_VERSION, MISSION_TASK_PREFIX_VERSION, CREDENTIAL_INSTANCE_SELECTION_VERSION, TASK_LIFECYCLE_OUTBOX_VERSION, TASK_LIFECYCLE_CONSUMERS_VERSION, VALIDATOR_INPUT_FINGERPRINT_VERSION, UNPLANNED_EXECUTION_BLOCK_DEDUPE_VERSION, QUEUED_EPISODE_SIGNATURE_VERSION, MULTI_ROLE_WORKFLOW_AGENTS_VERSION, WORKFLOW_PRINCIPAL_FENCE_VERSION, TASK_RECOMMENDATIONS_VERSION, GITHUB_CHECK_STATES_VERSION, AGENT_ACTIVITY_EVENTS_VERSION, SPEC_LOCK_DRIFT_REPORT_VERSION, SPEC_LOCK_SOURCE_REVISION_BIGINT_VERSION, MEMORY_RECALL_RECORDS_VERSION, MISSION_FEATURE_SPEC_ALIGNMENT_VERSION, AGENT_RATING_PROJECT_ISOLATION_VERSION, AGENT_RATINGS_PROJECT_PARTITION_VERSION, PROJECT_OWNERSHIP_DECLARATION_DRIFT_VERSION, PROJECT_OWNERSHIP_DEFAULT_RECONCILIATION_VERSION, MESSAGE_ARCHIVE_SCHEMA_VERSION, LEGACY_ADOPTION_DRAINED_MARKER_FUNCTION, StaleBinarySchemaError, MIGRATION_BOOKKEEPING_TABLE, LEGACY_ADOPTION_DRAINED_MARKER, __dirname, MIGRATIONS_DIR, BASELINE_MIGRATION_PATH, AUTOMATION_ISOLATION_MIGRATION_PATH, ANALYTICS_ISOLATION_MIGRATION_PATH, MONITOR_APPROVAL_ISOLATION_MIGRATION_PATH, LEGACY_CUTOVER_PRESERVATION_MIGRATION_PATH, MULTI_PROJECT_CUTOVER_MIGRATION_PATH, PROJECT_OWNERSHIP_MIGRATION_PATH, SQLITE_SCHEMA_PARITY_MIGRATION_PATH, SESSION_ADVISOR_ENABLED_MIGRATION_PATH, MISSION_FIX_IDEMPOTENCY_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_SCOPE_FIX_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_LEGACY_PARTITION_BACKFILL_MIGRATION_PATH, OWNER_PROJECT_ID_SPLIT_MIGRATION_PATH, CHAT_SESSION_PINS_MIGRATION_PATH, EXECUTOR_TOOL_FAILURE_RETRY_MIGRATION_PATH, EXECUTOR_ESCALATION_ATTEMPT_MIGRATION_PATH, GLOBAL_ROUTINES_MIGRATION_PATH, TASK_MERGER_MODEL_LANE_MIGRATION_PATH, BULK_COMPLETION_REFUSAL_AT_MIGRATION_PATH, TASK_PROPOSAL_CLAIM_MIGRATION_PATH, CONFIGURATION_REVISIONS_MIGRATION_PATH, IDEATION_MIGRATION_PATH, RESEARCH_FEATURE_PROVENANCE_MIGRATION_PATH, TASK_VERIFICATION_REQUEST_MIGRATION_PATH, SYMBOL_LOCKS_MIGRATION_PATH, BIGINT_COUNTERS_MIGRATION_PATH, WORKFLOW_IR_PIN_AND_LEGACY_ADOPTION_VERSION, WORKFLOW_IR_PIN_AND_LEGACY_ADOPTION_MIGRATION_PATH, PLANNING_ACTIVE_TIMING_MIGRATION_PATH, TASK_DECLARED_SYMBOLS_MIGRATION_PATH, SQLITE_MIGRATION_RUNTIME_READ_PATH, WORKFLOW_TASK_CONTINUATIONS_PATH, LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_PATH, TASK_WEDGE_NOTIFICATION_MIGRATION_PATH, MILESTONE_ASSERTION_PROVENANCE_MIGRATION_PATH, MISSION_LINEAGE_STOP_MIGRATION_PATH, CHAT_SESSION_TAGS_MIGRATION_PATH, DROP_GLOBAL_CONCURRENCY_MIGRATION_PATH, MISSION_TASK_PREFIX_MIGRATION_PATH, CREDENTIAL_INSTANCE_SELECTION_MIGRATION_PATH, TASK_LIFECYCLE_OUTBOX_MIGRATION_PATH, TASK_LIFECYCLE_CONSUMERS_MIGRATION_PATH, VALIDATOR_INPUT_FINGERPRINT_MIGRATION_PATH, UNPLANNED_EXECUTION_BLOCK_DEDUPE_MIGRATION_PATH, QUEUED_EPISODE_SIGNATURE_MIGRATION_PATH, MULTI_ROLE_WORKFLOW_AGENTS_MIGRATION_PATH, WORKFLOW_PRINCIPAL_FENCE_MIGRATION_PATH, TASK_RECOMMENDATIONS_MIGRATION_PATH, GITHUB_CHECK_STATES_MIGRATION_PATH, AGENT_ACTIVITY_EVENTS_MIGRATION_PATH, SPEC_LOCK_DRIFT_REPORT_MIGRATION_PATH, SPEC_LOCK_SOURCE_REVISION_BIGINT_MIGRATION_PATH, MEMORY_RECALL_RECORDS_MIGRATION_PATH, MISSION_FEATURE_SPEC_ALIGNMENT_MIGRATION_PATH, AGENT_RATING_PROJECT_ISOLATION_MIGRATION_PATH, AGENT_RATINGS_PROJECT_PARTITION_MIGRATION_PATH, PROJECT_OWNERSHIP_DECLARATION_DRIFT_MIGRATION_PATH, PROJECT_OWNERSHIP_DEFAULT_RECONCILIATION_MIGRATION_PATH, MESSAGE_ARCHIVE_SCHEMA_MIGRATION_PATH;
41017
41165
  var init_schema_applier = __esm({
41018
41166
  "../core/src/postgres/schema-applier.ts"() {
41019
41167
  "use strict";
41020
41168
  init_drizzle_orm();
41021
41169
  init_plugin_schema_hook();
41022
41170
  init_advisory_locks();
41023
- SCHEMA_BASELINE_VERSION = "0056";
41171
+ SCHEMA_BASELINE_VERSION = "0058";
41024
41172
  TASK_DECLARED_SYMBOLS_VERSION = "0028";
41025
41173
  INITIAL_SCHEMA_VERSION = "0000";
41026
41174
  AUTOMATION_ISOLATION_SCHEMA_VERSION = "0001";
@@ -41077,6 +41225,8 @@ var init_schema_applier = __esm({
41077
41225
  AGENT_RATING_PROJECT_ISOLATION_VERSION = "0054";
41078
41226
  AGENT_RATINGS_PROJECT_PARTITION_VERSION = "0055";
41079
41227
  PROJECT_OWNERSHIP_DECLARATION_DRIFT_VERSION = "0056";
41228
+ PROJECT_OWNERSHIP_DEFAULT_RECONCILIATION_VERSION = "0057";
41229
+ MESSAGE_ARCHIVE_SCHEMA_VERSION = "0058";
41080
41230
  LEGACY_ADOPTION_DRAINED_MARKER_FUNCTION = "fusion_mark_legacy_adoption_drained";
41081
41231
  StaleBinarySchemaError = class extends Error {
41082
41232
  constructor(databaseVersion, binaryVersion) {
@@ -41213,6 +41363,8 @@ var init_schema_applier = __esm({
41213
41363
  AGENT_RATING_PROJECT_ISOLATION_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0054_fn_8957_agent_rating_project_isolation.sql");
41214
41364
  AGENT_RATINGS_PROJECT_PARTITION_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0055_fn_8988_agent_ratings_project_partition.sql");
41215
41365
  PROJECT_OWNERSHIP_DECLARATION_DRIFT_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0056_fn_8997_project_ownership_declaration_drift.sql");
41366
+ PROJECT_OWNERSHIP_DEFAULT_RECONCILIATION_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0057_fn_9004_project_ownership_default_reconciliation.sql");
41367
+ MESSAGE_ARCHIVE_SCHEMA_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0058_fn_9014_message_archive.sql");
41216
41368
  }
41217
41369
  });
41218
41370
 
@@ -68568,20 +68720,32 @@ async function replaceActiveTaskWorkflowContinuation(layer, input) {
68568
68720
  return upsertWorkflowWorkItem(layer, input, tx);
68569
68721
  }));
68570
68722
  }
68571
- async function seedStrandedPlanReviewContinuation(layer, input) {
68723
+ async function seedStrandedPlanReviewContinuation(layer, input, options = {}) {
68572
68724
  return layer.transactionImmediate(async (tx) => withTaskWorkflowSerialization(tx, layer.projectId, input.taskId, async () => {
68573
68725
  const active2 = await tx.select({ id: project_exports.workflowWorkItems.id }).from(project_exports.workflowWorkItems).where(and(
68574
68726
  projectScopeFor(project_exports.workflowWorkItems.projectId, layer.projectId),
68575
68727
  eq(project_exports.workflowWorkItems.taskId, input.taskId),
68576
68728
  inArray(project_exports.workflowWorkItems.state, [...ACTIVE_WORKFLOW_WORK_ITEM_STATES])
68577
68729
  ));
68578
- if (active2.length > 0) return { seeded: false, reason: "active-continuation" };
68730
+ const blocking = options.retirePredecessorId ? active2.filter((row) => row.id !== options.retirePredecessorId) : active2;
68731
+ if (blocking.length > 0) return { seeded: false, reason: "active-continuation" };
68579
68732
  const taskRows = await tx.select({ workflowStepResults: project_exports.tasks.workflowStepResults }).from(project_exports.tasks).where(and(
68580
68733
  projectScopeFor(project_exports.tasks.projectId, layer.projectId),
68581
68734
  eq(project_exports.tasks.id, input.taskId)
68582
68735
  )).limit(1);
68583
68736
  const results = taskRows[0]?.workflowStepResults;
68584
68737
  if (results?.some(isPlanReviewSatisfied)) return { seeded: false, reason: "plan-review-passed" };
68738
+ if (options.retirePredecessorId) {
68739
+ const predRows = await tx.select().from(project_exports.workflowWorkItems).where(and(
68740
+ projectScopeFor(project_exports.workflowWorkItems.projectId, layer.projectId),
68741
+ eq(project_exports.workflowWorkItems.id, options.retirePredecessorId),
68742
+ eq(project_exports.workflowWorkItems.taskId, input.taskId)
68743
+ )).limit(1);
68744
+ const pred = predRows[0];
68745
+ if (pred && !isTerminalWorkflowWorkItemState(normalizeWorkflowWorkItemState(pred.state))) {
68746
+ await transitionWorkflowWorkItem(layer, pred.id, "succeeded", { leaseOwner: null, leaseExpiresAt: null, lastError: null }, tx);
68747
+ }
68748
+ }
68585
68749
  const item = await upsertWorkflowWorkItem(layer, input, tx);
68586
68750
  return { seeded: true, workItemId: item.id };
68587
68751
  }));
@@ -68745,8 +68909,10 @@ function safeJsonParse(value) {
68745
68909
  return null;
68746
68910
  }
68747
68911
  }
68748
- async function queryRunAuditEvents(db, filter = {}) {
68749
- const conditions = [];
68912
+ async function queryRunAuditEvents(db, filter = {}, projectId2) {
68913
+ const conditions = [projectScopeFor(project_exports.runAuditEvents.projectId, projectId2)].filter(
68914
+ (condition) => condition !== void 0
68915
+ );
68750
68916
  if (filter.runId) {
68751
68917
  conditions.push(eq(project_exports.runAuditEvents.runId, filter.runId));
68752
68918
  }
@@ -68892,6 +69058,7 @@ var init_async_audit = __esm({
68892
69058
  init_drizzle_orm();
68893
69059
  init_schema2();
68894
69060
  init_data_layer();
69061
+ init_data_layer();
68895
69062
  LEGACY_REVIEW_LANES3 = ["in-review"];
68896
69063
  LEGACY_COMPLETE_LANES = ["done"];
68897
69064
  }
@@ -69281,7 +69448,10 @@ async function insertArtifactRow(layer, input, stored, archivedColumns) {
69281
69448
  createdAt: now,
69282
69449
  updatedAt: now
69283
69450
  });
69284
- const rows = await tx.select().from(project_exports.artifacts).where(eq(project_exports.artifacts.id, id)).limit(1);
69451
+ const rows = await tx.select().from(project_exports.artifacts).where(and(
69452
+ eq(project_exports.artifacts.id, id),
69453
+ projectScopeFor(project_exports.artifacts.projectId, layer.projectId)
69454
+ )).limit(1);
69285
69455
  const row = rows[0];
69286
69456
  if (!row) throw new Error(`Failed to register artifact ${id}`);
69287
69457
  return rowToArtifact2(row);
@@ -69289,7 +69459,7 @@ async function insertArtifactRow(layer, input, stored, archivedColumns) {
69289
69459
  }
69290
69460
  async function updateArtifactRow(layer, id, updates, archivedColumns) {
69291
69461
  return layer.transactionImmediate(async (tx) => {
69292
- const existing = await getArtifact(tx, id);
69462
+ const existing = await getArtifact(tx, id, layer.projectId);
69293
69463
  if (!existing) {
69294
69464
  throw new Error(`Artifact ${id} not found`);
69295
69465
  }
@@ -69309,29 +69479,38 @@ async function updateArtifactRow(layer, id, updates, archivedColumns) {
69309
69479
  description: updates.description !== void 0 ? updates.description : existing.description ?? null,
69310
69480
  content: updates.content !== void 0 ? updates.content : existing.content ?? null,
69311
69481
  updatedAt: now
69312
- }).where(eq(project_exports.artifacts.id, id));
69313
- const updated = await getArtifact(tx, id);
69482
+ }).where(and(
69483
+ eq(project_exports.artifacts.id, id),
69484
+ projectScopeFor(project_exports.artifacts.projectId, layer.projectId)
69485
+ ));
69486
+ const updated = await getArtifact(tx, id, layer.projectId);
69314
69487
  if (!updated) {
69315
69488
  throw new Error(`Failed to update artifact ${id}`);
69316
69489
  }
69317
69490
  return updated;
69318
69491
  });
69319
69492
  }
69320
- async function getArtifact(db, id) {
69321
- const rows = await db.select().from(project_exports.artifacts).where(eq(project_exports.artifacts.id, id)).limit(1);
69493
+ async function getArtifact(db, id, projectId2) {
69494
+ const rows = await db.select().from(project_exports.artifacts).where(and(
69495
+ eq(project_exports.artifacts.id, id),
69496
+ projectScopeFor(project_exports.artifacts.projectId, projectId2)
69497
+ )).limit(1);
69322
69498
  const row = rows[0];
69323
69499
  return row ? rowToArtifact2(row) : null;
69324
69500
  }
69325
69501
  async function getArtifacts(db, taskId, projectId2, archivedColumns) {
69326
69502
  const column = await getLiveTaskColumn(db, taskId, projectId2, archivedColumns);
69327
69503
  if (column === null || column === "archived") return [];
69328
- const rows = await db.select().from(project_exports.artifacts).where(eq(project_exports.artifacts.taskId, taskId)).orderBy(desc(project_exports.artifacts.createdAt));
69504
+ const rows = await db.select().from(project_exports.artifacts).where(and(
69505
+ eq(project_exports.artifacts.taskId, taskId),
69506
+ projectScopeFor(project_exports.artifacts.projectId, projectId2)
69507
+ )).orderBy(desc(project_exports.artifacts.createdAt));
69329
69508
  return rows.map((row) => rowToArtifact2(row));
69330
69509
  }
69331
- async function listArtifacts(db, options) {
69510
+ async function listArtifacts(db, options, projectId2) {
69332
69511
  const limit = Math.min(Math.max(1, options?.limit ?? 200), 1e3);
69333
69512
  const offset = Math.max(0, options?.offset ?? 0);
69334
- const conditions = [];
69513
+ const conditions = [projectScopeFor(project_exports.artifacts.projectId, projectId2)];
69335
69514
  if (options?.type) {
69336
69515
  conditions.push(eq(project_exports.artifacts.type, options.type));
69337
69516
  }
@@ -69370,7 +69549,10 @@ async function listArtifacts(db, options) {
69370
69549
  taskColumn: project_exports.tasks.column
69371
69550
  }).from(project_exports.artifacts).leftJoin(
69372
69551
  project_exports.tasks,
69373
- eq(project_exports.artifacts.taskId, project_exports.tasks.id)
69552
+ and(
69553
+ eq(project_exports.artifacts.taskId, project_exports.tasks.id),
69554
+ eq(project_exports.artifacts.projectId, project_exports.tasks.projectId)
69555
+ )
69374
69556
  ).where(and(...conditions)).orderBy(desc(project_exports.artifacts.createdAt)).limit(limit).offset(offset);
69375
69557
  return rows.map((row) => {
69376
69558
  const artifact = rowToArtifact2(row);
@@ -70535,7 +70717,7 @@ function rowToBranchGroup2(row) {
70535
70717
  closedAt: row.closedAt ?? void 0
70536
70718
  };
70537
70719
  }
70538
- async function createBranchGroup(db, input) {
70720
+ async function createBranchGroup(db, input, projectId2) {
70539
70721
  validateBranchGroupBranchName(input.branchName);
70540
70722
  const now = Date.now();
70541
70723
  const id = generateBranchGroupId2();
@@ -70554,44 +70736,52 @@ async function createBranchGroup(db, input) {
70554
70736
  updatedAt: now,
70555
70737
  closedAt: input.closedAt ?? null
70556
70738
  });
70557
- const created = await getBranchGroup(db, id);
70739
+ const created = await getBranchGroup(db, id, projectId2);
70558
70740
  if (!created) throw new Error(`Failed to read branch group ${id} after create`);
70559
70741
  return created;
70560
70742
  }
70561
- async function getBranchGroup(db, id) {
70562
- const rows = await db.select().from(project_exports.branchGroups).where(eq(project_exports.branchGroups.id, id)).limit(1);
70743
+ async function getBranchGroup(db, id, projectId2) {
70744
+ const rows = await db.select().from(project_exports.branchGroups).where(and(
70745
+ eq(project_exports.branchGroups.id, id),
70746
+ projectScopeFor(project_exports.branchGroups.projectId, projectId2)
70747
+ )).limit(1);
70563
70748
  const row = rows[0];
70564
70749
  return row ? rowToBranchGroup2(row) : null;
70565
70750
  }
70566
- async function getBranchGroupBySource(db, sourceType, sourceId) {
70751
+ async function getBranchGroupBySource(db, sourceType, sourceId, projectId2) {
70567
70752
  const rows = await db.select().from(project_exports.branchGroups).where(
70568
70753
  and(
70569
70754
  eq(project_exports.branchGroups.sourceType, sourceType),
70570
- eq(project_exports.branchGroups.sourceId, sourceId)
70755
+ eq(project_exports.branchGroups.sourceId, sourceId),
70756
+ projectScopeFor(project_exports.branchGroups.projectId, projectId2)
70571
70757
  )
70572
70758
  ).limit(1);
70573
70759
  const row = rows[0];
70574
70760
  return row ? rowToBranchGroup2(row) : null;
70575
70761
  }
70576
- async function getBranchGroupByBranchName(db, branchName) {
70577
- const rows = await db.select().from(project_exports.branchGroups).where(eq(project_exports.branchGroups.branchName, branchName)).orderBy(sql`${project_exports.branchGroups.createdAt} DESC`).limit(1);
70762
+ async function getBranchGroupByBranchName(db, branchName, projectId2) {
70763
+ const rows = await db.select().from(project_exports.branchGroups).where(and(
70764
+ eq(project_exports.branchGroups.branchName, branchName),
70765
+ projectScopeFor(project_exports.branchGroups.projectId, projectId2)
70766
+ )).orderBy(sql`${project_exports.branchGroups.createdAt} DESC`).limit(1);
70578
70767
  const row = rows[0];
70579
70768
  return row ? rowToBranchGroup2(row) : null;
70580
70769
  }
70581
- async function ensureBranchGroupForSource(db, sourceType, sourceId, init) {
70582
- const existing = await getBranchGroupBySource(db, sourceType, sourceId);
70770
+ async function ensureBranchGroupForSource(db, sourceType, sourceId, init, projectId2) {
70771
+ const existing = await getBranchGroupBySource(db, sourceType, sourceId, projectId2);
70583
70772
  if (existing) return existing;
70584
- const existingByBranch = await getBranchGroupByBranchName(db, init.branchName);
70773
+ const existingByBranch = await getBranchGroupByBranchName(db, init.branchName, projectId2);
70585
70774
  if (existingByBranch) return existingByBranch;
70586
- return createBranchGroup(db, { sourceType, sourceId, ...init });
70775
+ return createBranchGroup(db, { sourceType, sourceId, ...init }, projectId2);
70587
70776
  }
70588
- async function listBranchGroups(db, options) {
70589
- const query = db.select().from(project_exports.branchGroups).orderBy(asc(project_exports.branchGroups.createdAt));
70590
- const rows = options?.status ? await query.where(eq(project_exports.branchGroups.status, options.status)) : await query;
70777
+ async function listBranchGroups(db, options, projectId2) {
70778
+ const conditions = [projectScopeFor(project_exports.branchGroups.projectId, projectId2)];
70779
+ if (options?.status) conditions.push(eq(project_exports.branchGroups.status, options.status));
70780
+ const rows = await db.select().from(project_exports.branchGroups).where(and(...conditions)).orderBy(asc(project_exports.branchGroups.createdAt));
70591
70781
  return rows.map((row) => rowToBranchGroup2(row));
70592
70782
  }
70593
- async function updateBranchGroup(db, id, patch) {
70594
- const current = await getBranchGroup(db, id);
70783
+ async function updateBranchGroup(db, id, patch, projectId2) {
70784
+ const current = await getBranchGroup(db, id, projectId2);
70595
70785
  if (!current) throw new Error(`Branch group ${id} not found`);
70596
70786
  if (patch.branchName !== void 0) {
70597
70787
  validateBranchGroupBranchName(patch.branchName);
@@ -70610,8 +70800,11 @@ async function updateBranchGroup(db, id, patch) {
70610
70800
  status: nextStatus,
70611
70801
  updatedAt: now,
70612
70802
  closedAt: nextClosedAt
70613
- }).where(eq(project_exports.branchGroups.id, id));
70614
- const updated = await getBranchGroup(db, id);
70803
+ }).where(and(
70804
+ eq(project_exports.branchGroups.id, id),
70805
+ projectScopeFor(project_exports.branchGroups.projectId, projectId2)
70806
+ ));
70807
+ const updated = await getBranchGroup(db, id, projectId2);
70615
70808
  if (!updated) throw new Error(`Branch group ${id} disappeared after update`);
70616
70809
  return updated;
70617
70810
  }
@@ -70768,6 +70961,7 @@ var init_async_branch_groups = __esm({
70768
70961
  "use strict";
70769
70962
  init_drizzle_orm();
70770
70963
  init_schema2();
70964
+ init_data_layer();
70771
70965
  init_branch_assignment();
70772
70966
  }
70773
70967
  });
@@ -80581,6 +80775,9 @@ import { randomUUID as randomUUID21 } from "node:crypto";
80581
80775
  function tableForScope(scope) {
80582
80776
  return scope === "project" ? project_exports.secrets : central_exports.secretsGlobal;
80583
80777
  }
80778
+ function scopeForSecretTable(scope, table, projectId2) {
80779
+ return scope === "project" ? projectScopeFor(table.projectId, projectId2) : void 0;
80780
+ }
80584
80781
  function isAccessPolicy(value) {
80585
80782
  return value === "auto" || value === "prompt" || value === "deny";
80586
80783
  }
@@ -80603,23 +80800,23 @@ function rowToRecord2(row, scope) {
80603
80800
  lastReadBy: row.lastReadBy
80604
80801
  };
80605
80802
  }
80606
- async function getSecretMetadata(handle, id, scope) {
80803
+ async function getSecretMetadata(handle, id, scope, projectId2) {
80607
80804
  const table = tableForScope(scope);
80608
- const rows = await handle.select(metadataColumns(table)).from(table).where(eq(table.id, id)).limit(1);
80805
+ const rows = await handle.select(metadataColumns(table)).from(table).where(and(eq(table.id, id), scopeForSecretTable(scope, table, projectId2))).limit(1);
80609
80806
  const row = rows[0];
80610
80807
  return row ? rowToRecord2(row, scope) : null;
80611
80808
  }
80612
- async function listSecrets(handle, scope) {
80809
+ async function listSecrets(handle, scope, projectId2) {
80613
80810
  if (scope) {
80614
80811
  const table = tableForScope(scope);
80615
- const rows = await handle.select(metadataColumns(table)).from(table).orderBy(asc(sql`lower(${table.key})`));
80812
+ const rows = await handle.select(metadataColumns(table)).from(table).where(scopeForSecretTable(scope, table, projectId2)).orderBy(asc(sql`lower(${table.key})`));
80616
80813
  return rows.map((row) => rowToRecord2(row, scope));
80617
80814
  }
80618
- const project = await listSecrets(handle, "project");
80619
- const global2 = await listSecrets(handle, "global");
80815
+ const project = await listSecrets(handle, "project", projectId2);
80816
+ const global2 = await listSecrets(handle, "global", projectId2);
80620
80817
  return [...project, ...global2];
80621
80818
  }
80622
- async function createSecret(handle, cipher, input) {
80819
+ async function createSecret(handle, cipher, input, projectId2) {
80623
80820
  const key = input.key.trim();
80624
80821
  if (!key) {
80625
80822
  throw new SecretsStoreError({ code: "invalid-key", message: "Secret key is required" });
@@ -80633,6 +80830,7 @@ async function createSecret(handle, cipher, input) {
80633
80830
  const table = tableForScope(input.scope);
80634
80831
  try {
80635
80832
  await handle.insert(table).values({
80833
+ ...input.scope === "project" && projectId2?.trim() ? { projectId: projectId2 } : {},
80636
80834
  id,
80637
80835
  key,
80638
80836
  valueCiphertext: encrypted.ciphertext,
@@ -80652,14 +80850,14 @@ async function createSecret(handle, cipher, input) {
80652
80850
  }
80653
80851
  throw error2;
80654
80852
  }
80655
- const created = await getSecretMetadata(handle, id, input.scope);
80853
+ const created = await getSecretMetadata(handle, id, input.scope, projectId2);
80656
80854
  if (!created) {
80657
80855
  throw new SecretsStoreError({ code: "not-found", message: "Secret insert succeeded but row could not be read back" });
80658
80856
  }
80659
80857
  return created;
80660
80858
  }
80661
- async function updateSecret(handle, cipher, id, scope, patch) {
80662
- const existing = await getSecretMetadata(handle, id, scope);
80859
+ async function updateSecret(handle, cipher, id, scope, patch, projectId2) {
80860
+ const existing = await getSecretMetadata(handle, id, scope, projectId2);
80663
80861
  if (!existing) {
80664
80862
  throw new SecretsStoreError({ code: "not-found", message: "Secret not found" });
80665
80863
  }
@@ -80693,34 +80891,34 @@ async function updateSecret(handle, cipher, id, scope, patch) {
80693
80891
  updates.nonce = encrypted.nonce;
80694
80892
  }
80695
80893
  try {
80696
- await handle.update(table).set(updates).where(eq(table.id, id));
80894
+ await handle.update(table).set(updates).where(and(eq(table.id, id), scopeForSecretTable(scope, table, projectId2)));
80697
80895
  } catch (error2) {
80698
80896
  if (isPostgresUniqueError(error2)) {
80699
80897
  throw new SecretsStoreError({ code: "duplicate-key", message: "Secret key already exists" });
80700
80898
  }
80701
80899
  throw error2;
80702
80900
  }
80703
- const updated = await getSecretMetadata(handle, id, scope);
80901
+ const updated = await getSecretMetadata(handle, id, scope, projectId2);
80704
80902
  if (!updated) {
80705
80903
  throw new SecretsStoreError({ code: "not-found", message: "Secret update succeeded but row could not be read back" });
80706
80904
  }
80707
80905
  return updated;
80708
80906
  }
80709
- async function deleteSecret(handle, id, scope) {
80710
- const existing = await getSecretMetadata(handle, id, scope);
80907
+ async function deleteSecret(handle, id, scope, projectId2) {
80908
+ const existing = await getSecretMetadata(handle, id, scope, projectId2);
80711
80909
  if (!existing) {
80712
80910
  throw new SecretsStoreError({ code: "not-found", message: "Secret not found" });
80713
80911
  }
80714
80912
  const table = tableForScope(scope);
80715
- await handle.delete(table).where(eq(table.id, id));
80913
+ await handle.delete(table).where(and(eq(table.id, id), scopeForSecretTable(scope, table, projectId2)));
80716
80914
  }
80717
- async function revealSecret(handle, cipher, id, scope, reader) {
80915
+ async function revealSecret(handle, cipher, id, scope, reader, projectId2) {
80718
80916
  const table = tableForScope(scope);
80719
80917
  const rows = await handle.select({
80720
80918
  ...metadataColumns(table),
80721
80919
  valueCiphertext: table.valueCiphertext,
80722
80920
  nonce: table.nonce
80723
- }).from(table).where(eq(table.id, id)).limit(1);
80921
+ }).from(table).where(and(eq(table.id, id), scopeForSecretTable(scope, table, projectId2))).limit(1);
80724
80922
  const row = rows[0];
80725
80923
  if (!row) {
80726
80924
  throw new SecretsStoreError({ code: "not-found", message: "Secret not found" });
@@ -80739,7 +80937,7 @@ async function revealSecret(handle, cipher, id, scope, reader) {
80739
80937
  }
80740
80938
  const now = (/* @__PURE__ */ new Date()).toISOString();
80741
80939
  const lastReadBy = reader.userId ?? reader.agentId ?? null;
80742
- await handle.update(table).set({ lastReadAt: now, lastReadBy, updatedAt: now }).where(eq(table.id, id));
80940
+ await handle.update(table).set({ lastReadAt: now, lastReadBy, updatedAt: now }).where(and(eq(table.id, id), scopeForSecretTable(scope, table, projectId2)));
80743
80941
  return { key: row.key, plaintextValue };
80744
80942
  }
80745
80943
  var severityAuditLog10, SecretsStoreError, metadataColumns;
@@ -80750,6 +80948,7 @@ var init_async_secrets_store = __esm({
80750
80948
  init_drizzle_orm();
80751
80949
  init_postgres_errors();
80752
80950
  init_schema2();
80951
+ init_data_layer();
80753
80952
  init_secrets_crypto();
80754
80953
  severityAuditLog10 = createLogger2("core-async-secrets-store");
80755
80954
  SecretsStoreError = class extends Error {
@@ -80845,7 +81044,7 @@ var init_secrets_store = __esm({
80845
81044
  };
80846
81045
  }
80847
81046
  async listSecrets(scope) {
80848
- return listSecrets(this.asyncLayer.db, scope);
81047
+ return listSecrets(this.asyncLayer.db, scope, this.asyncLayer.projectId);
80849
81048
  }
80850
81049
  async listEnvExportable(opts) {
80851
81050
  const keyPrefix = opts?.keyPrefix;
@@ -80888,7 +81087,7 @@ var init_secrets_store = __esm({
80888
81087
  return [...exported.values()];
80889
81088
  }
80890
81089
  async getSecretMetadata(id, scope) {
80891
- return getSecretMetadata(this.asyncLayer.db, id, scope);
81090
+ return getSecretMetadata(this.asyncLayer.db, id, scope, this.asyncLayer.projectId);
80892
81091
  }
80893
81092
  async createSecret(input) {
80894
81093
  const key = input.key.trim();
@@ -80898,12 +81097,12 @@ var init_secrets_store = __esm({
80898
81097
  if (input.accessPolicy && !isAccessPolicy2(input.accessPolicy)) {
80899
81098
  throw new SecretsStoreError2({ code: "invalid-policy", message: "Invalid access policy" });
80900
81099
  }
80901
- const created = await createSecret(this.asyncLayer.db, this.cipher, input);
81100
+ const created = await createSecret(this.asyncLayer.db, this.cipher, input, this.asyncLayer.projectId);
80902
81101
  this.emitAudit({ mutationType: "secret:create", scope: input.scope, secretId: created.id, key: created.key });
80903
81102
  return created;
80904
81103
  }
80905
81104
  async updateSecret(id, scope, patch) {
80906
- const updated = await updateSecret(this.asyncLayer.db, this.cipher, id, scope, patch);
81105
+ const updated = await updateSecret(this.asyncLayer.db, this.cipher, id, scope, patch, this.asyncLayer.projectId);
80907
81106
  this.emitAudit({ mutationType: "secret:update", scope, secretId: updated.id, key: updated.key });
80908
81107
  return updated;
80909
81108
  }
@@ -80912,12 +81111,12 @@ var init_secrets_store = __esm({
80912
81111
  if (!existing) {
80913
81112
  throw new SecretsStoreError2({ code: "not-found", message: "Secret not found" });
80914
81113
  }
80915
- await deleteSecret(this.asyncLayer.db, id, scope);
81114
+ await deleteSecret(this.asyncLayer.db, id, scope, this.asyncLayer.projectId);
80916
81115
  this.emitAudit({ mutationType: "secret:delete", scope, secretId: id, key: existing.key });
80917
81116
  return;
80918
81117
  }
80919
81118
  async revealSecret(id, scope, reader) {
80920
- const revealed = await revealSecret(this.asyncLayer.db, this.cipher, id, scope, reader);
81119
+ const revealed = await revealSecret(this.asyncLayer.db, this.cipher, id, scope, reader, this.asyncLayer.projectId);
80921
81120
  this.emitAudit({ mutationType: "secret:read", scope, secretId: id, key: revealed.key, actor: reader });
80922
81121
  return revealed;
80923
81122
  }
@@ -82018,6 +82217,7 @@ async function getVerificationCacheHitImpl(store, treeSha, testCommand, buildCom
82018
82217
  const normalizedBuild = buildCommand2 ?? "";
82019
82218
  const table = project_exports.verificationCache;
82020
82219
  const rows = await store.asyncLayer.db.select({ recordedAt: table.recordedAt, taskId: table.taskId }).from(table).where(and(
82220
+ projectScopeFor(table.projectId, store.asyncLayer.projectId),
82021
82221
  eq(table.treeSha, treeSha),
82022
82222
  eq(table.testCommand, normalizedTest),
82023
82223
  eq(table.buildCommand, normalizedBuild)
@@ -82760,7 +82960,7 @@ async function invokeTaskCreatedHookImpl(store, task) {
82760
82960
  }
82761
82961
  async function createBranchGroupImpl(store, input) {
82762
82962
  const layer = store.asyncLayer;
82763
- return createBranchGroup(layer.db, input);
82963
+ return createBranchGroup(layer.db, input, layer.projectId);
82764
82964
  }
82765
82965
  var init_task_id_integrity2 = __esm({
82766
82966
  "../core/src/task-store/task-id-integrity.ts"() {
@@ -82845,22 +83045,23 @@ import { readFile as readFile7, writeFile as writeFile5 } from "node:fs/promises
82845
83045
  import { join as join23 } from "node:path";
82846
83046
  async function getBranchGroupImpl(store, id) {
82847
83047
  const layer = store.asyncLayer;
82848
- return getBranchGroup(layer.db, id);
83048
+ return getBranchGroup(layer.db, id, layer.projectId);
82849
83049
  }
82850
83050
  async function getBranchGroupBySourceImpl(store, sourceType, sourceId) {
82851
83051
  const layer = store.asyncLayer;
82852
- return getBranchGroupBySource(layer.db, sourceType, sourceId);
83052
+ return getBranchGroupBySource(layer.db, sourceType, sourceId, layer.projectId);
82853
83053
  }
82854
83054
  async function getBranchGroupByBranchNameImpl(store, branchName) {
82855
83055
  const layer = store.asyncLayer;
82856
- return getBranchGroupByBranchName(layer.db, branchName);
83056
+ return getBranchGroupByBranchName(layer.db, branchName, layer.projectId);
82857
83057
  }
82858
83058
  async function ensureBranchGroupForSourceImpl(store, sourceType, sourceId, init) {
82859
- return ensureBranchGroupForSource(store.asyncLayer.db, sourceType, sourceId, init);
83059
+ const layer = store.asyncLayer;
83060
+ return ensureBranchGroupForSource(layer.db, sourceType, sourceId, init, layer.projectId);
82860
83061
  }
82861
83062
  async function listBranchGroupsImpl(store, options) {
82862
83063
  const layer = store.asyncLayer;
82863
- return listBranchGroups(layer.db, options);
83064
+ return listBranchGroups(layer.db, options, layer.projectId);
82864
83065
  }
82865
83066
  async function listTasksByBranchGroupImpl(store, groupId) {
82866
83067
  const tasks2 = await store.listTasks({ includeArchived: true, slim: false });
@@ -84402,7 +84603,7 @@ function insertArtifactRowImpl(store, input, id, now, stored) {
84402
84603
  }
84403
84604
  async function getArtifactImpl(store, id) {
84404
84605
  const layer = store.asyncLayer;
84405
- return getArtifact(layer.db, id);
84606
+ return getArtifact(layer.db, id, layer.projectId);
84406
84607
  }
84407
84608
  async function updateArtifactImpl(store, id, updates) {
84408
84609
  const layer = store.asyncLayer;
@@ -89271,6 +89472,7 @@ var init_async_archive_lineage = __esm({
89271
89472
  "use strict";
89272
89473
  init_drizzle_orm();
89273
89474
  init_schema2();
89475
+ init_data_layer();
89274
89476
  init_async_persistence();
89275
89477
  init_async_lifecycle();
89276
89478
  init_lineage_approval_invalidation();
@@ -92738,7 +92940,7 @@ async function _maybeAutoArchiveSameAgentDuplicateBackendImpl(store, task, input
92738
92940
  }
92739
92941
  async function updateBranchGroupImpl(store, id, patch) {
92740
92942
  const layer = store.asyncLayer;
92741
- return updateBranchGroup(layer.db, id, patch);
92943
+ return updateBranchGroup(layer.db, id, patch, layer.projectId);
92742
92944
  }
92743
92945
  async function updatePrEntityImpl(store, id, patch) {
92744
92946
  const layer = store.asyncLayer;
@@ -93248,13 +93450,19 @@ async function deleteAttachmentArtifactRows(store, taskId, filename) {
93248
93450
  const linkedArtifactIds = artifacts2.filter((artifact) => artifact.metadata?.source === "attachment" && artifact.metadata.attachmentFilename === filename).map((artifact) => artifact.id);
93249
93451
  if (linkedArtifactIds.length === 0) return;
93250
93452
  for (const artifactId of linkedArtifactIds) {
93251
- await layer.db.delete(project_exports.artifacts).where(eq(project_exports.artifacts.id, artifactId));
93453
+ await layer.db.delete(project_exports.artifacts).where(and(
93454
+ eq(project_exports.artifacts.id, artifactId),
93455
+ projectScopeFor(project_exports.artifacts.projectId, layer.projectId)
93456
+ ));
93252
93457
  }
93253
93458
  return;
93254
93459
  }
93255
93460
  async function getArtifactsForAttachmentCleanup(store, taskId) {
93256
93461
  const layer = store.asyncLayer;
93257
- const rows = await layer.db.select().from(project_exports.artifacts).where(eq(project_exports.artifacts.taskId, taskId));
93462
+ const rows = await layer.db.select().from(project_exports.artifacts).where(and(
93463
+ eq(project_exports.artifacts.taskId, taskId),
93464
+ projectScopeFor(project_exports.artifacts.projectId, layer.projectId)
93465
+ ));
93258
93466
  return rows;
93259
93467
  }
93260
93468
  async function deleteAttachmentImpl(store, id, filename) {
@@ -95670,7 +95878,7 @@ function clearLinkedAgentTaskIdsImpl(store, taskId, updatedAt = (/* @__PURE__ */
95670
95878
  `).run(updatedAt, updatedAt, taskId);
95671
95879
  }
95672
95880
  async function listArtifactsImpl(store, options) {
95673
- return listArtifacts(store.asyncLayer.db, options);
95881
+ return listArtifacts(store.asyncLayer.db, options, store.asyncLayer.projectId);
95674
95882
  }
95675
95883
  async function rehomeOccupantImpl(store, taskId, targetColumn, reason, metadata) {
95676
95884
  let current;
@@ -96915,8 +97123,8 @@ async function upsertWorkflowWorkItemImpl(store, input, tx) {
96915
97123
  async function replaceActiveTaskWorkflowContinuationImpl(store, input) {
96916
97124
  return replaceActiveTaskWorkflowContinuation(store.asyncLayer, input);
96917
97125
  }
96918
- async function seedStrandedPlanReviewContinuationImpl(store, input) {
96919
- return seedStrandedPlanReviewContinuation(store.asyncLayer, input);
97126
+ async function seedStrandedPlanReviewContinuationImpl(store, input, options = {}) {
97127
+ return seedStrandedPlanReviewContinuation(store.asyncLayer, input, options);
96920
97128
  }
96921
97129
  async function transitionWorkflowWorkItemImpl(store, id, state, patch = {}, tx) {
96922
97130
  return transitionWorkflowWorkItem(store.asyncLayer, id, state, patch, tx);
@@ -101059,6 +101267,37 @@ async function getTaskVerificationRequestAsyncImpl(store, taskId) {
101059
101267
  const row = rows[0];
101060
101268
  return row ? { taskId: row.taskId, requestId: row.requestId, status: row.status, profile: row.profile, command: row.command, scope: row.scope, requestedBy: row.requestedBy, requestedAt: row.requestedAt, ...row.startedAt ? { startedAt: row.startedAt } : {}, ...row.completedAt ? { completedAt: row.completedAt } : {}, ...row.result ? { result: row.result } : {}, ...row.rejectionReason ? { rejectionReason: row.rejectionReason } : {} } : null;
101061
101269
  }
101270
+ async function listTaskRecommendationsImpl(store, options) {
101271
+ const layer = store.asyncLayer;
101272
+ const completeColumns = options?.completeColumns ?? await resolveProjectColumnsForRoles(store, ["complete"]);
101273
+ const rawLimit = options?.limit;
101274
+ const rawOffset = options?.offset;
101275
+ const limit = typeof rawLimit === "number" && Number.isFinite(rawLimit) ? Math.min(200, Math.max(1, Math.trunc(rawLimit))) : 50;
101276
+ const offset = typeof rawOffset === "number" && Number.isFinite(rawOffset) ? Math.max(0, Math.trunc(rawOffset)) : 0;
101277
+ const columns = [...completeColumns];
101278
+ if (columns.length === 0) return { items: [], rowOffset: offset, rowLimit: limit, returnedRowCount: 0, totalRowCount: 0, hasMore: false };
101279
+ const filter = and(
101280
+ taskProjectScope(layer),
101281
+ isNull(project_exports.tasks.deletedAt),
101282
+ inArray(project_exports.tasks.column, columns),
101283
+ isNotNull(project_exports.tasks.recommendations),
101284
+ sql`jsonb_array_length(${project_exports.tasks.recommendations}) > 0`
101285
+ );
101286
+ const [countRows, rows] = await Promise.all([
101287
+ layer.db.select({ count: sql`count(*)` }).from(project_exports.tasks).where(filter),
101288
+ layer.db.select({ id: project_exports.tasks.id, title: project_exports.tasks.title, column: project_exports.tasks.column, updatedAt: project_exports.tasks.updatedAt, recommendations: project_exports.tasks.recommendations }).from(project_exports.tasks).where(filter).orderBy(desc(project_exports.tasks.updatedAt), desc(project_exports.tasks.id)).limit(limit).offset(offset)
101289
+ ]);
101290
+ const items = [];
101291
+ for (const row of rows) {
101292
+ if (!Array.isArray(row.recommendations) || row.recommendations.length === 0) continue;
101293
+ for (const recommendation of row.recommendations) {
101294
+ items.push({ taskId: row.id, taskTitle: row.title ?? void 0, taskColumn: row.column, updatedAt: row.updatedAt, recommendation });
101295
+ }
101296
+ }
101297
+ const totalRowCount = Number(countRows[0]?.count ?? 0);
101298
+ const returnedRowCount = rows.length;
101299
+ return { items, rowOffset: offset, rowLimit: limit, returnedRowCount, totalRowCount, hasMore: offset + returnedRowCount < totalRowCount };
101300
+ }
101062
101301
  var LEGACY_REVIEW_LANES5, LEGACY_ARCHIVE_LANES2;
101063
101302
  var init_reads = __esm({
101064
101303
  "../core/src/task-store/reads.ts"() {
@@ -101081,6 +101320,7 @@ var init_reads = __esm({
101081
101320
  init_retry_summary();
101082
101321
  init_errors3();
101083
101322
  init_project_lane_vocabulary();
101323
+ init_data_layer();
101084
101324
  init_async_persistence();
101085
101325
  init_async_search();
101086
101326
  init_async_archive_db();
@@ -103187,6 +103427,9 @@ var init_store = __esm({
103187
103427
  async clearWorkflowRunStepInstancesAsync(taskId, keepRunId) {
103188
103428
  return clearWorkflowRunStepInstancesAsyncImpl(this, taskId, keepRunId);
103189
103429
  }
103430
+ async listTaskRecommendations(options) {
103431
+ return listTaskRecommendationsImpl(this, options);
103432
+ }
103190
103433
  async listTasksForGithubTrackingReconcile(options) {
103191
103434
  return listTasksForGithubTrackingReconcileImpl(this, options);
103192
103435
  }
@@ -103913,8 +104156,8 @@ var init_store = __esm({
103913
104156
  async replaceActiveTaskWorkflowContinuation(input) {
103914
104157
  return replaceActiveTaskWorkflowContinuationImpl(this, input);
103915
104158
  }
103916
- async seedStrandedPlanReviewContinuation(input) {
103917
- return seedStrandedPlanReviewContinuationImpl(this, input);
104159
+ async seedStrandedPlanReviewContinuation(input, options = {}) {
104160
+ return seedStrandedPlanReviewContinuationImpl(this, input, options);
103918
104161
  }
103919
104162
  async transitionWorkflowWorkItem(id, state, patch = {}, tx) {
103920
104163
  return transitionWorkflowWorkItemImpl(this, id, state, patch, tx);
@@ -103997,7 +104240,11 @@ var init_store = __esm({
103997
104240
  /** PostgreSQL-authoritative audit reader; sync fallback remains for test doubles. */
103998
104241
  async getRunAuditEventsAsync(options = {}) {
103999
104242
  if (this.asyncLayer) {
104000
- const events = await queryRunAuditEvents(this.asyncLayer.db, options);
104243
+ const events = await queryRunAuditEvents(
104244
+ this.asyncLayer.db,
104245
+ options,
104246
+ this.asyncLayer.projectId
104247
+ );
104001
104248
  return events.map((event) => ({
104002
104249
  ...event,
104003
104250
  taskId: event.taskId ?? void 0,
@@ -107575,7 +107822,9 @@ function rangeWhere(query) {
107575
107822
  async function aggregatePluginActivations(dbOrLayer, query = {}) {
107576
107823
  if ("ping" in dbOrLayer) {
107577
107824
  const layer = dbOrLayer;
107578
- const conditions = [];
107825
+ const conditions = [projectScopeFor(project_exports.pluginActivations.projectId, layer.projectId)].filter(
107826
+ (condition) => condition !== void 0
107827
+ );
107579
107828
  if (query.from !== void 0) conditions.push(gte(project_exports.pluginActivations.activatedAt, query.from));
107580
107829
  if (query.to !== void 0) conditions.push(lte(project_exports.pluginActivations.activatedAt, query.to));
107581
107830
  const where2 = conditions.length > 0 ? and(...conditions) : void 0;
@@ -107614,6 +107863,7 @@ async function aggregatePluginActivations(dbOrLayer, query = {}) {
107614
107863
  var init_plugin_activation_analytics = __esm({
107615
107864
  "../core/src/plugins/plugin-activation-analytics.ts"() {
107616
107865
  "use strict";
107866
+ init_data_layer();
107617
107867
  init_drizzle_orm();
107618
107868
  init_schema2();
107619
107869
  }
@@ -130611,6 +130861,18 @@ function rowToMessage2(row) {
130611
130861
  createdAt: row.createdAt
130612
130862
  };
130613
130863
  }
130864
+ function chatMessageProjectConditions(handle, projectId2) {
130865
+ const messageScope = projectScopeFor(project_exports.chatMessages.projectId, projectId2);
130866
+ const sessionScope = projectScopeFor(project_exports.chatSessions.projectId, projectId2);
130867
+ if (!messageScope || !sessionScope) return [];
130868
+ return [
130869
+ messageScope,
130870
+ exists(handle.select({ id: project_exports.chatSessions.id }).from(project_exports.chatSessions).where(and(
130871
+ eq(project_exports.chatSessions.id, project_exports.chatMessages.sessionId),
130872
+ sessionScope
130873
+ )))
130874
+ ];
130875
+ }
130614
130876
  function rowToRoom(row) {
130615
130877
  return {
130616
130878
  id: row.id,
@@ -130782,7 +131044,7 @@ async function replaceChatSessionTags(layer, sessionId, projectId2, tagIds) {
130782
131044
  return attachTags(tx, session);
130783
131045
  });
130784
131046
  }
130785
- async function addChatMessage(handle, message) {
131047
+ async function addChatMessage(handle, message, projectId2) {
130786
131048
  const sanitizedAttachments = message.attachments === void 0 ? void 0 : sanitizeJsonbValue(message.attachments);
130787
131049
  const sanitized = {
130788
131050
  ...message,
@@ -130801,15 +131063,24 @@ async function addChatMessage(handle, message) {
130801
131063
  attachments: sanitizedAttachments ?? null,
130802
131064
  createdAt: sanitized.createdAt
130803
131065
  });
130804
- await handle.update(project_exports.chatSessions).set({ updatedAt: sanitized.createdAt }).where(eq(project_exports.chatSessions.id, sanitized.sessionId));
131066
+ await handle.update(project_exports.chatSessions).set({ updatedAt: sanitized.createdAt }).where(and(
131067
+ eq(project_exports.chatSessions.id, sanitized.sessionId),
131068
+ projectScopeFor(project_exports.chatSessions.projectId, projectId2)
131069
+ ));
130805
131070
  return sanitized;
130806
131071
  }
130807
- async function getChatMessage(handle, id) {
130808
- const rows = await handle.select().from(project_exports.chatMessages).where(eq(project_exports.chatMessages.id, id));
131072
+ async function getChatMessage(handle, id, projectId2) {
131073
+ const rows = await handle.select().from(project_exports.chatMessages).where(and(
131074
+ eq(project_exports.chatMessages.id, id),
131075
+ ...chatMessageProjectConditions(handle, projectId2)
131076
+ ));
130809
131077
  return rows[0] ? rowToMessage2(rows[0]) : void 0;
130810
131078
  }
130811
- async function getChatMessages(handle, sessionId, filter) {
130812
- const conditions = [eq(project_exports.chatMessages.sessionId, sessionId)];
131079
+ async function getChatMessages(handle, sessionId, filter, projectId2) {
131080
+ const conditions = [
131081
+ eq(project_exports.chatMessages.sessionId, sessionId),
131082
+ ...chatMessageProjectConditions(handle, projectId2)
131083
+ ];
130813
131084
  if (filter?.before) {
130814
131085
  conditions.push(lte(project_exports.chatMessages.createdAt, filter.before));
130815
131086
  }
@@ -130819,9 +131090,12 @@ async function getChatMessages(handle, sessionId, filter) {
130819
131090
  const rows = await handle.select().from(project_exports.chatMessages).where(and(...conditions)).orderBy(filter?.order === "desc" ? desc(orderCol) : asc(orderCol)).limit(limit).offset(offset);
130820
131091
  return rows.map(rowToMessage2);
130821
131092
  }
130822
- async function getLastMessageForSessions(handle, sessionIds) {
131093
+ async function getLastMessageForSessions(handle, sessionIds, projectId2) {
130823
131094
  if (sessionIds.length === 0) return /* @__PURE__ */ new Map();
130824
- const rows = await handle.select().from(project_exports.chatMessages).where(inArray(project_exports.chatMessages.sessionId, sessionIds)).orderBy(
131095
+ const rows = await handle.select().from(project_exports.chatMessages).where(and(
131096
+ inArray(project_exports.chatMessages.sessionId, sessionIds),
131097
+ ...chatMessageProjectConditions(handle, projectId2)
131098
+ )).orderBy(
130825
131099
  desc(project_exports.chatMessages.createdAt),
130826
131100
  desc(project_exports.chatMessages.id)
130827
131101
  );
@@ -130987,8 +131261,8 @@ async function setInFlightGeneration(handle, id, inFlightGeneration) {
130987
131261
  await handle.update(project_exports.chatSessions).set({ inFlightGeneration: sanitizedInFlightGeneration }).where(eq(project_exports.chatSessions.id, id));
130988
131262
  return getChatSession(handle, id);
130989
131263
  }
130990
- async function addChatMessageAttachment(handle, sessionId, messageId, attachment) {
130991
- const message = await getChatMessage(handle, messageId);
131264
+ async function addChatMessageAttachment(handle, sessionId, messageId, attachment, projectId2) {
131265
+ const message = await getChatMessage(handle, messageId, projectId2);
130992
131266
  if (!message || message.sessionId !== sessionId) {
130993
131267
  throw new Error(`Message ${messageId} not found in session ${sessionId}`);
130994
131268
  }
@@ -130996,25 +131270,35 @@ async function addChatMessageAttachment(handle, sessionId, messageId, attachment
130996
131270
  ...message.attachments ?? [],
130997
131271
  attachment
130998
131272
  ]);
130999
- await handle.update(project_exports.chatMessages).set({ attachments: updatedAttachments }).where(eq(project_exports.chatMessages.id, messageId));
131000
- const updated = await getChatMessage(handle, messageId);
131273
+ await handle.update(project_exports.chatMessages).set({ attachments: updatedAttachments }).where(and(
131274
+ eq(project_exports.chatMessages.id, messageId),
131275
+ ...chatMessageProjectConditions(handle, projectId2)
131276
+ ));
131277
+ const updated = await getChatMessage(handle, messageId, projectId2);
131001
131278
  if (!updated) throw new Error(`Failed to update message ${messageId}`);
131002
131279
  return updated;
131003
131280
  }
131004
- async function deleteChatMessage(handle, id) {
131005
- const existing = await getChatMessage(handle, id);
131281
+ async function deleteChatMessage(handle, id, projectId2) {
131282
+ const existing = await getChatMessage(handle, id, projectId2);
131006
131283
  if (!existing) return false;
131007
- await handle.delete(project_exports.chatMessages).where(eq(project_exports.chatMessages.id, id));
131008
- await handle.update(project_exports.chatSessions).set({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(project_exports.chatSessions.id, existing.sessionId));
131284
+ await handle.delete(project_exports.chatMessages).where(and(
131285
+ eq(project_exports.chatMessages.id, id),
131286
+ ...chatMessageProjectConditions(handle, projectId2)
131287
+ ));
131288
+ await handle.update(project_exports.chatSessions).set({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and(
131289
+ eq(project_exports.chatSessions.id, existing.sessionId),
131290
+ projectScopeFor(project_exports.chatSessions.projectId, projectId2)
131291
+ ));
131009
131292
  return true;
131010
131293
  }
131011
- async function searchChatSessionsByMessageContent(handle, query, sessionIds) {
131294
+ async function searchChatSessionsByMessageContent(handle, query, sessionIds, projectId2) {
131012
131295
  const trimmed = query.trim();
131013
131296
  if (!trimmed || sessionIds.length === 0) return /* @__PURE__ */ new Map();
131014
131297
  const escaped = trimmed.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
131015
131298
  const rows = await handle.select().from(project_exports.chatMessages).where(and(
131016
131299
  inArray(project_exports.chatMessages.sessionId, sessionIds),
131017
- ilike(project_exports.chatMessages.content, `%${escaped}%`)
131300
+ ilike(project_exports.chatMessages.content, `%${escaped}%`),
131301
+ ...chatMessageProjectConditions(handle, projectId2)
131018
131302
  )).orderBy(
131019
131303
  desc(project_exports.chatMessages.createdAt),
131020
131304
  desc(project_exports.chatMessages.id)
@@ -131028,13 +131312,16 @@ async function searchChatSessionsByMessageContent(handle, query, sessionIds) {
131028
131312
  }
131029
131313
  return result;
131030
131314
  }
131031
- async function deleteChatMessagesFrom(handle, sessionId, fromMessageId) {
131032
- const orderedRows = await handle.select().from(project_exports.chatMessages).where(eq(project_exports.chatMessages.sessionId, sessionId)).orderBy(
131315
+ async function deleteChatMessagesFrom(handle, sessionId, fromMessageId, projectId2) {
131316
+ const orderedRows = await handle.select().from(project_exports.chatMessages).where(and(
131317
+ eq(project_exports.chatMessages.sessionId, sessionId),
131318
+ ...chatMessageProjectConditions(handle, projectId2)
131319
+ )).orderBy(
131033
131320
  asc(project_exports.chatMessages.createdAt),
131034
131321
  asc(project_exports.chatMessages.id)
131035
131322
  );
131036
131323
  const ordered2 = orderedRows.map(rowToMessage2);
131037
- const target = await getChatMessage(handle, fromMessageId);
131324
+ const target = await getChatMessage(handle, fromMessageId, projectId2);
131038
131325
  if (!target || target.sessionId !== sessionId) {
131039
131326
  return { deletedIds: [], retained: ordered2 };
131040
131327
  }
@@ -131047,19 +131334,28 @@ async function deleteChatMessagesFrom(handle, sessionId, fromMessageId) {
131047
131334
  if (deletedIds.length === 0) {
131048
131335
  return { deletedIds: [], retained };
131049
131336
  }
131050
- await handle.delete(project_exports.chatMessages).where(inArray(project_exports.chatMessages.id, deletedIds));
131051
- await handle.update(project_exports.chatSessions).set({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(project_exports.chatSessions.id, sessionId));
131337
+ await handle.delete(project_exports.chatMessages).where(and(
131338
+ inArray(project_exports.chatMessages.id, deletedIds),
131339
+ ...chatMessageProjectConditions(handle, projectId2)
131340
+ ));
131341
+ await handle.update(project_exports.chatSessions).set({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and(
131342
+ eq(project_exports.chatSessions.id, sessionId),
131343
+ projectScopeFor(project_exports.chatSessions.projectId, projectId2)
131344
+ ));
131052
131345
  return { deletedIds, retained };
131053
131346
  }
131054
- async function updateChatMessageMetadata(handle, messageId, metadata, options) {
131055
- const existing = await getChatMessage(handle, messageId);
131347
+ async function updateChatMessageMetadata(handle, messageId, metadata, options, projectId2) {
131348
+ const existing = await getChatMessage(handle, messageId, projectId2);
131056
131349
  if (!existing) {
131057
131350
  throw new Error(`Message ${messageId} not found`);
131058
131351
  }
131059
131352
  const merge2 = options?.merge !== false;
131060
131353
  const nextMetadata = metadata === null ? merge2 ? existing.metadata : null : merge2 ? { ...existing.metadata ?? {}, ...metadata } : metadata;
131061
- await handle.update(project_exports.chatMessages).set({ metadata: sanitizeJsonbValue(nextMetadata) ?? null }).where(eq(project_exports.chatMessages.id, messageId));
131062
- const updated = await getChatMessage(handle, messageId);
131354
+ await handle.update(project_exports.chatMessages).set({ metadata: sanitizeJsonbValue(nextMetadata) ?? null }).where(and(
131355
+ eq(project_exports.chatMessages.id, messageId),
131356
+ ...chatMessageProjectConditions(handle, projectId2)
131357
+ ));
131358
+ const updated = await getChatMessage(handle, messageId, projectId2);
131063
131359
  if (!updated) {
131064
131360
  throw new Error(`Failed to update message ${messageId}`);
131065
131361
  }
@@ -131441,7 +131737,7 @@ var init_chat_store = __esm({
131441
131737
  attachments: input.attachments,
131442
131738
  createdAt: now
131443
131739
  };
131444
- const created = await addChatMessage(this.asyncLayer.db, message);
131740
+ const created = await addChatMessage(this.asyncLayer.db, message, this.asyncLayer.projectId);
131445
131741
  this.emit("chat:message:added", created);
131446
131742
  return created;
131447
131743
  }
@@ -131449,7 +131745,7 @@ var init_chat_store = __esm({
131449
131745
  * Append a file attachment metadata record to an existing message.
131450
131746
  */
131451
131747
  async addMessageAttachment(sessionId, messageId, attachment) {
131452
- const updated = await addChatMessageAttachment(this.asyncLayer.db, sessionId, messageId, attachment);
131748
+ const updated = await addChatMessageAttachment(this.asyncLayer.db, sessionId, messageId, attachment, this.asyncLayer.projectId);
131453
131749
  this.emit("chat:message:updated", updated);
131454
131750
  return updated;
131455
131751
  }
@@ -131461,7 +131757,7 @@ var init_chat_store = __esm({
131461
131757
  * @returns Array of messages ordered by createdAt ASC (default) or DESC
131462
131758
  */
131463
131759
  async getMessages(sessionId, filter) {
131464
- return getChatMessages(this.asyncLayer.db, sessionId, filter);
131760
+ return getChatMessages(this.asyncLayer.db, sessionId, filter, this.asyncLayer.projectId);
131465
131761
  }
131466
131762
  /**
131467
131763
  * Get a message by ID.
@@ -131470,7 +131766,7 @@ var init_chat_store = __esm({
131470
131766
  * @returns The message, or undefined if not found
131471
131767
  */
131472
131768
  async getMessage(id) {
131473
- return getChatMessage(this.asyncLayer.db, id);
131769
+ return getChatMessage(this.asyncLayer.db, id, this.asyncLayer.projectId);
131474
131770
  }
131475
131771
  /**
131476
131772
  * Get the latest message for each session in the provided list.
@@ -131480,10 +131776,10 @@ var init_chat_store = __esm({
131480
131776
  * @returns Map of sessionId -> latest ChatMessage for that session
131481
131777
  */
131482
131778
  async getLastMessageForSessions(sessionIds) {
131483
- return getLastMessageForSessions(this.asyncLayer.db, sessionIds);
131779
+ return getLastMessageForSessions(this.asyncLayer.db, sessionIds, this.asyncLayer.projectId);
131484
131780
  }
131485
131781
  async hasMessages(sessionId) {
131486
- return (await getChatMessages(this.asyncLayer.db, sessionId, { limit: 1 })).length > 0;
131782
+ return (await getChatMessages(this.asyncLayer.db, sessionId, { limit: 1 }, this.asyncLayer.projectId)).length > 0;
131487
131783
  }
131488
131784
  /**
131489
131785
  * Search sessions by message content (not just title/agentId).
@@ -131511,7 +131807,7 @@ var init_chat_store = __esm({
131511
131807
  if (!trimmed || !sessionIds || sessionIds.length === 0) {
131512
131808
  return /* @__PURE__ */ new Map();
131513
131809
  }
131514
- return searchChatSessionsByMessageContent(this.asyncLayer.db, trimmed, sessionIds);
131810
+ return searchChatSessionsByMessageContent(this.asyncLayer.db, trimmed, sessionIds, this.asyncLayer.projectId);
131515
131811
  }
131516
131812
  /**
131517
131813
  * Delete a message by ID.
@@ -131520,9 +131816,9 @@ var init_chat_store = __esm({
131520
131816
  * @returns true if deleted, false if not found
131521
131817
  */
131522
131818
  async deleteMessage(id) {
131523
- const existing = await getChatMessage(this.asyncLayer.db, id);
131819
+ const existing = await getChatMessage(this.asyncLayer.db, id, this.asyncLayer.projectId);
131524
131820
  if (!existing) return false;
131525
- const deleted = await deleteChatMessage(this.asyncLayer.db, id);
131821
+ const deleted = await deleteChatMessage(this.asyncLayer.db, id, this.asyncLayer.projectId);
131526
131822
  if (deleted) {
131527
131823
  this.emit("chat:message:deleted", id);
131528
131824
  const updatedSession = await this.getSession(existing.sessionId);
@@ -131545,7 +131841,7 @@ var init_chat_store = __esm({
131545
131841
  * @returns deletedIds (in ASC order) and retained messages (pre-edit history, ASC order)
131546
131842
  */
131547
131843
  async deleteMessagesFrom(sessionId, fromMessageId) {
131548
- const result = await deleteChatMessagesFrom(this.asyncLayer.db, sessionId, fromMessageId);
131844
+ const result = await deleteChatMessagesFrom(this.asyncLayer.db, sessionId, fromMessageId, this.asyncLayer.projectId);
131549
131845
  if (result.deletedIds.length > 0) {
131550
131846
  for (const id of result.deletedIds) {
131551
131847
  this.emit("chat:message:deleted", id);
@@ -131563,7 +131859,7 @@ var init_chat_store = __esm({
131563
131859
  * is what lets a later edit rewind losslessly via SessionManager.branch()/resetLeaf().
131564
131860
  */
131565
131861
  async updateMessageMetadata(messageId, metadata, options) {
131566
- const updated = await updateChatMessageMetadata(this.asyncLayer.db, messageId, metadata, options);
131862
+ const updated = await updateChatMessageMetadata(this.asyncLayer.db, messageId, metadata, options, this.asyncLayer.projectId);
131567
131863
  this.emit("chat:message:updated", updated);
131568
131864
  return updated;
131569
131865
  }
@@ -132999,6 +133295,77 @@ var init_task_timing = __esm({
132999
133295
  }
133000
133296
  });
133001
133297
 
133298
+ // ../core/src/task-recommendation-notice.ts
133299
+ import { createHash as createHash20 } from "node:crypto";
133300
+ function registerTaskRecommendationNoticeMailbox(store, mailbox) {
133301
+ mailboxes2.set(store, mailbox);
133302
+ return () => {
133303
+ if (mailboxes2.get(store) === mailbox) mailboxes2.delete(store);
133304
+ };
133305
+ }
133306
+ function getTaskRecommendationNoticeMailbox(store) {
133307
+ return mailboxes2.get(store);
133308
+ }
133309
+ function buildTaskRecommendationNoticeContent(task, recommendations) {
133310
+ const title = task.title?.trim();
133311
+ const heading = title ? `## Recommendations from ${task.id} \u2014 ${title}` : `## Recommendations from ${task.id}`;
133312
+ return [
133313
+ heading,
133314
+ "",
133315
+ "These are optional, non-blocking out-of-scope proposals that were **not** executed.",
133316
+ "",
133317
+ ...recommendations.map((recommendation) => `- **${recommendation.title}** \u2014 \`${recommendation.category}\`: ${recommendation.description}`),
133318
+ "",
133319
+ `Open ${task.id}'s **Recommendations** tab to review them. **Create task** files one through normal guarded intake.`
133320
+ ].join("\n");
133321
+ }
133322
+ function buildTaskRecommendationNoticeIdempotencyKey(taskId, recommendations) {
133323
+ const ids = recommendations.map(({ id }) => id).sort().join("\n");
133324
+ const digest = createHash20("sha256").update(ids, "utf8").digest("hex").slice(0, 16);
133325
+ return `task-recommendation-notice:${taskId}:${digest}`;
133326
+ }
133327
+ async function notifyOperatorOfTaskRecommendations(store, task, recommendations, settings) {
133328
+ try {
133329
+ if (settings.recommendationMailboxNoticeEnabled === false || !recommendations?.length) return false;
133330
+ const mailbox = mailboxes2.get(store);
133331
+ if (!mailbox) return false;
133332
+ await mailbox.sendMessageOnce(
133333
+ {
133334
+ fromId: "system",
133335
+ fromType: "system",
133336
+ toId: DASHBOARD_USER_ID,
133337
+ toType: "user",
133338
+ type: "system",
133339
+ content: buildTaskRecommendationNoticeContent(task, recommendations),
133340
+ metadata: {
133341
+ kind: "task-recommendation-notice",
133342
+ taskId: task.id,
133343
+ recommendationCount: recommendations.length,
133344
+ recommendationIds: recommendations.map(({ id }) => id),
133345
+ categories: recommendations.map(({ category }) => category)
133346
+ }
133347
+ },
133348
+ buildTaskRecommendationNoticeIdempotencyKey(task.id, recommendations)
133349
+ );
133350
+ return true;
133351
+ } catch (error2) {
133352
+ noticeLog2.warn(
133353
+ `Operator recommendation notice failed for ${task.id}: ${error2 instanceof Error ? error2.message : String(error2)}`
133354
+ );
133355
+ return false;
133356
+ }
133357
+ }
133358
+ var noticeLog2, mailboxes2;
133359
+ var init_task_recommendation_notice = __esm({
133360
+ "../core/src/task-recommendation-notice.ts"() {
133361
+ "use strict";
133362
+ init_logger2();
133363
+ init_types();
133364
+ noticeLog2 = createLogger2("task-recommendation-notice");
133365
+ mailboxes2 = /* @__PURE__ */ new WeakMap();
133366
+ }
133367
+ });
133368
+
133002
133369
  // ../core/src/session-identity-registry.ts
133003
133370
  import { AsyncLocalStorage } from "node:async_hooks";
133004
133371
  import { resolve as resolve23 } from "node:path";
@@ -133848,6 +134215,8 @@ __export(src_exports, {
133848
134215
  buildTaskDeleteNoticeContent: () => buildTaskDeleteNoticeContent,
133849
134216
  buildTaskDeleteNoticeIdempotencyKey: () => buildTaskDeleteNoticeIdempotencyKey,
133850
134217
  buildTaskLineageTrailer: () => buildTaskLineageTrailer,
134218
+ buildTaskRecommendationNoticeContent: () => buildTaskRecommendationNoticeContent,
134219
+ buildTaskRecommendationNoticeIdempotencyKey: () => buildTaskRecommendationNoticeIdempotencyKey,
133851
134220
  buildToolOutputTruncationMarker: () => buildToolOutputTruncationMarker,
133852
134221
  buildTriageHeartbeatGuidance: () => buildTriageHeartbeatGuidance,
133853
134222
  buildTriageMemoryInstructions: () => buildTriageMemoryInstructions,
@@ -134178,6 +134547,7 @@ __export(src_exports, {
134178
134547
  getTaskMergeBlocker: () => getTaskMergeBlocker,
134179
134548
  getTaskMoveDisposer: () => getTaskMoveDisposer,
134180
134549
  getTaskPriorityRank: () => getTaskPriorityRank,
134550
+ getTaskRecommendationNoticeMailbox: () => getTaskRecommendationNoticeMailbox,
134181
134551
  getTemplatesForRole: () => getTemplatesForRole,
134182
134552
  getTotalAgentActiveMs: () => getTotalAgentActiveMs,
134183
134553
  getTrait: () => getTrait,
@@ -134428,6 +134798,7 @@ __export(src_exports, {
134428
134798
  normalizeWorkflowPromptOverrides: () => normalizeWorkflowPromptOverrides,
134429
134799
  normalizeWorkflowReviewFindings: () => normalizeWorkflowReviewFindings,
134430
134800
  notifyOperatorOfNonOperatorDelete: () => notifyOperatorOfNonOperatorDelete,
134801
+ notifyOperatorOfTaskRecommendations: () => notifyOperatorOfTaskRecommendations,
134431
134802
  overseerAdviceSeverityRank: () => overseerAdviceSeverityRank,
134432
134803
  parseAgentManifest: () => parseAgentManifest,
134433
134804
  parseCompanyArchive: () => parseCompanyArchive,
@@ -134537,6 +134908,7 @@ __export(src_exports, {
134537
134908
  registerStepParser: () => registerStepParser,
134538
134909
  registerTaskDeleteNoticeMailbox: () => registerTaskDeleteNoticeMailbox,
134539
134910
  registerTaskMoveDisposer: () => registerTaskMoveDisposer,
134911
+ registerTaskRecommendationNoticeMailbox: () => registerTaskRecommendationNoticeMailbox,
134540
134912
  registerTraitHookImpl: () => registerTraitHookImpl,
134541
134913
  rekeyFallbackProjectPartition: () => rekeyFallbackProjectPartition,
134542
134914
  releaseEmbeddedRuntimeLease: () => releaseEmbeddedRuntimeLease,
@@ -135164,6 +135536,7 @@ var init_src2 = __esm({
135164
135536
  init_task_document_concurrency();
135165
135537
  init_task_delete_attribution();
135166
135538
  init_task_delete_notice();
135539
+ init_task_recommendation_notice();
135167
135540
  init_session_identity_registry();
135168
135541
  init_task_lifecycle_event_retention();
135169
135542
  init_task_lifecycle_consumer_identity();
@@ -138534,7 +138907,7 @@ ${getErrorStderr(error2) ?? ""}`;
138534
138907
  });
138535
138908
 
138536
138909
  // ../engine/src/worktree/secrets-env-writer.ts
138537
- import { createHash as createHash20 } from "node:crypto";
138910
+ import { createHash as createHash21 } from "node:crypto";
138538
138911
  import { execFile as execFile7 } from "node:child_process";
138539
138912
  import { promises as fs5 } from "node:fs";
138540
138913
  import path2 from "node:path";
@@ -138543,7 +138916,7 @@ function isValidFilename(filename) {
138543
138916
  return !!filename && !filename.includes("/") && !filename.includes("\\") && !filename.includes("..") && !filename.includes("\0") && filename !== FINGERPRINT_FILE;
138544
138917
  }
138545
138918
  function sha2562(content) {
138546
- return createHash20("sha256").update(Buffer.from(content, "utf8")).digest("hex");
138919
+ return createHash21("sha256").update(Buffer.from(content, "utf8")).digest("hex");
138547
138920
  }
138548
138921
  function quote(value) {
138549
138922
  return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t")}"`;
@@ -141206,11 +141579,11 @@ var init_planning_handoff_recovery = __esm({
141206
141579
  // ../engine/src/plan-review-continuation.ts
141207
141580
  async function seedPreReleasePlanReviewContinuation(store, task, ir2, options = {}) {
141208
141581
  const node = resolvePreReleasePlanReviewNode(ir2);
141209
- if (!node || node.column !== task.column) return { seeded: false };
141582
+ if (!node || node.column !== task.column) return { seeded: false, reason: "no-pre-release-plan-review" };
141210
141583
  if (isTaskBlockedOnApproval(task)) return { seeded: false, reason: "awaiting-approval" };
141211
141584
  if (task.paused === true || task.userPaused === true) return { seeded: false, reason: "paused" };
141212
141585
  const items = await store.listWorkflowWorkItemsForTask(task.id);
141213
- const active2 = items.filter((item2) => ACTIVE_WORKFLOW_WORK_ITEM_STATES.includes(item2.state));
141586
+ const active2 = items.filter((item2) => ACTIVE_WORKFLOW_WORK_ITEM_STATES.includes(item2.state) && item2.id !== options.retirePredecessorId);
141214
141587
  if (active2.length > 0) return { seeded: false, reason: "active-continuation" };
141215
141588
  const continuationSequence = items.length;
141216
141589
  const input = {
@@ -141226,6 +141599,9 @@ async function seedPreReleasePlanReviewContinuation(store, task, ir2, options =
141226
141599
  targetColumn: task.column,
141227
141600
  irHash: computeWorkflowIrPin(ir2, node.id).irHash
141228
141601
  };
141602
+ if (options.retirePredecessorId) {
141603
+ return store.seedStrandedPlanReviewContinuation(input, { retirePredecessorId: options.retirePredecessorId });
141604
+ }
141229
141605
  if (options.atomic) return store.seedStrandedPlanReviewContinuation(input);
141230
141606
  const item = await store.replaceActiveTaskWorkflowContinuation(input);
141231
141607
  return { seeded: true, workItemId: item.id };
@@ -141265,7 +141641,7 @@ var init_plan_review_continuation = __esm({
141265
141641
  });
141266
141642
 
141267
141643
  // ../engine/src/execution/hold-release.ts
141268
- import { createHash as createHash21 } from "node:crypto";
141644
+ import { createHash as createHash22 } from "node:crypto";
141269
141645
  import { readFile as readFile33 } from "node:fs/promises";
141270
141646
  function findColumn2(ir2, columnId) {
141271
141647
  if (ir2.version !== "v2") return void 0;
@@ -141303,9 +141679,9 @@ async function checkAndRecordUnplannedExecutionBlock(store, task, ir2) {
141303
141679
  promptContent = "";
141304
141680
  }
141305
141681
  }
141306
- const promptMarker = promptContent.length > 0 ? createHash21("sha256").update(promptContent).digest("hex") : "missing";
141682
+ const promptMarker = promptContent.length > 0 ? createHash22("sha256").update(promptContent).digest("hex") : "missing";
141307
141683
  const dependencies = [...task.dependencies ?? []].sort();
141308
- const episode = createHash21("sha256").update(JSON.stringify({
141684
+ const episode = createHash22("sha256").update(JSON.stringify({
141309
141685
  planReviewNode,
141310
141686
  promptMarker,
141311
141687
  dependencies,
@@ -144276,32 +144652,36 @@ var init_scheduler = __esm({
144276
144652
  const hierarchy = await missionStore.getMissionWithHierarchy(mission.id);
144277
144653
  if (!hierarchy) continue;
144278
144654
  for (const slice of hierarchy.milestones.flatMap((milestone) => milestone.slices).filter((slice2) => slice2.status === "active")) {
144279
- const superseded = await missionStore.reconcileSupersededGeneratedFixFeatures(slice.id);
144280
- fixed += superseded.supersededCount;
144281
- if (superseded.supersededCount > 0) {
144282
- const refreshed = await missionStore.getSlice(slice.id);
144283
- if (refreshed?.status === "complete") {
144284
- await this.onSliceComplete(refreshed);
144285
- continue;
144655
+ try {
144656
+ const superseded = await missionStore.reconcileSupersededGeneratedFixFeatures(slice.id);
144657
+ fixed += superseded.supersededCount;
144658
+ if (superseded.supersededCount > 0) {
144659
+ const refreshed = await missionStore.getSlice(slice.id);
144660
+ if (refreshed?.status === "complete") {
144661
+ await this.onSliceComplete(refreshed);
144662
+ continue;
144663
+ }
144286
144664
  }
144287
- }
144288
- const listFeatures2 = missionStore.listFeatures;
144289
- const features = listFeatures2 ? await listFeatures2(slice.id) : slice.features;
144290
- const supersededFeatureIds = new Set(superseded.featureIds ?? []);
144291
- const autoTriage = mission.autopilotEnabled === true || mission.autoAdvance === true;
144292
- for (const feature of features) {
144293
- if (supersededFeatureIds.has(feature.id) || feature.taskId || !autoTriage || feature.status === "blocked") continue;
144294
- if (feature.status !== "defined" && this.isGeneratedFixFeature(feature)) {
144295
- await missionStore.updateFeature(feature.id, { status: "blocked", loopState: "blocked", taskId: void 0 });
144665
+ const listFeatures2 = missionStore.listFeatures;
144666
+ const features = listFeatures2 ? await listFeatures2.call(missionStore, slice.id) : slice.features;
144667
+ const supersededFeatureIds = new Set(superseded.featureIds ?? []);
144668
+ const autoTriage = mission.autopilotEnabled === true || mission.autoAdvance === true;
144669
+ for (const feature of features) {
144670
+ if (supersededFeatureIds.has(feature.id) || feature.taskId || !autoTriage || feature.status === "blocked") continue;
144671
+ if (feature.status !== "defined" && this.isGeneratedFixFeature(feature)) {
144672
+ await missionStore.updateFeature(feature.id, { status: "blocked", loopState: "blocked", taskId: void 0 });
144673
+ fixed++;
144674
+ continue;
144675
+ }
144676
+ if (feature.status !== "defined" && feature.status !== "triaged" && feature.status !== "in-progress") continue;
144677
+ const ready = feature.status === "defined" ? feature : await missionStore.updateFeature(feature.id, { status: "defined", loopState: "idle", taskId: void 0 });
144678
+ if (ready !== feature) fixed++;
144679
+ const triaged = await missionStore.triageFeature(ready.id);
144296
144680
  fixed++;
144297
- continue;
144681
+ if (triaged.taskId) await this.emitStrandedFeatureTriageAudit(mission.id, slice.id, triaged.id, triaged.taskId);
144298
144682
  }
144299
- if (feature.status !== "defined" && feature.status !== "triaged" && feature.status !== "in-progress") continue;
144300
- const ready = feature.status === "defined" ? feature : await missionStore.updateFeature(feature.id, { status: "defined", loopState: "idle", taskId: void 0 });
144301
- if (ready !== feature) fixed++;
144302
- const triaged = await missionStore.triageFeature(ready.id);
144303
- fixed++;
144304
- if (triaged.taskId) await this.emitStrandedFeatureTriageAudit(mission.id, slice.id, triaged.id, triaged.taskId);
144683
+ } catch (error2) {
144684
+ schedulerLog.error(`Error during active mission recovery reconciliation for slice ${slice.id}:`, error2);
144305
144685
  }
144306
144686
  }
144307
144687
  }
@@ -144342,7 +144722,7 @@ var init_scheduler = __esm({
144342
144722
  });
144343
144723
 
144344
144724
  // ../engine/src/agents/agent-logger.ts
144345
- import { createHash as createHash22 } from "node:crypto";
144725
+ import { createHash as createHash23 } from "node:crypto";
144346
144726
  function truncateToolResultDetail(value) {
144347
144727
  if (value.length <= TOOL_RESULT_DETAIL_LIMIT) return value;
144348
144728
  return `${value.slice(0, TOOL_RESULT_DETAIL_LIMIT)}${TOOL_RESULT_DETAIL_TRUNCATION_NOTICE}`;
@@ -144431,7 +144811,7 @@ function summarizeToolArgs(name2, args) {
144431
144811
  const json2 = JSON.stringify(args);
144432
144812
  if (!json2 || json2 === "{}" || json2 === "[]") return void 0;
144433
144813
  if (json2.length <= STRUCTURED_ARG_SUMMARY_MAX) return json2;
144434
- const hash3 = createHash22("sha256").update(json2).digest("hex").slice(0, STRUCTURED_ARG_HASH_HEX_LEN);
144814
+ const hash3 = createHash23("sha256").update(json2).digest("hex").slice(0, STRUCTURED_ARG_HASH_HEX_LEN);
144435
144815
  const suffix = `\u2026#${hash3}`;
144436
144816
  const keep = Math.max(0, STRUCTURED_ARG_SUMMARY_MAX - suffix.length);
144437
144817
  return `${json2.slice(0, keep)}${suffix}`;
@@ -148783,7 +149163,13 @@ var init_gating_classifications = __esm({
148783
149163
  "fn_post_room_message",
148784
149164
  "fn_update_identity",
148785
149165
  "fn_reflect_on_performance",
148786
- "fn_read_evaluations"
149166
+ /*
149167
+ FNXC:ToolGovernance 2026-08-12-22:11:
149168
+ fn_agent_read_evaluations is a subtree-scoped read. Authorization is enforced by
149169
+ the extension tool's getChainOfCommand boundary, not by this action classifier.
149170
+ */
149171
+ "fn_read_evaluations",
149172
+ "fn_agent_read_evaluations"
148787
149173
  ]);
148788
149174
  COORDINATION_EXEMPT_TOOLS = [
148789
149175
  "read",
@@ -148843,6 +149229,8 @@ var init_gating_classifications = __esm({
148843
149229
  "fn_post_room_message",
148844
149230
  "fn_memory_append",
148845
149231
  "fn_read_evaluations",
149232
+ // FNXC:ToolGovernance 2026-08-12-22:11: The manager read is subtree-authorized in extension.ts; follow-up remains absent so it falls back to task_agent_mutation policy.
149233
+ "fn_agent_read_evaluations",
148846
149234
  "fn_update_identity",
148847
149235
  "fn_reflect_on_performance"
148848
149236
  ];
@@ -148867,7 +149255,7 @@ var init_gating_classifications = __esm({
148867
149255
  });
148868
149256
 
148869
149257
  // ../engine/src/agents/agent-action-gate.ts
148870
- import { createHash as createHash23 } from "node:crypto";
149258
+ import { createHash as createHash24 } from "node:crypto";
148871
149259
  function getExemptTools() {
148872
149260
  if (!_exemptTools) {
148873
149261
  _exemptTools = new Set(DEFAULT_EXEMPT_TOOLS);
@@ -148901,7 +149289,7 @@ function evaluateAgentActionGate(params) {
148901
149289
  if (params.toolName === "bash") {
148902
149290
  const command = extractShellCommand(args);
148903
149291
  const git7 = classifyGitCommand(command);
148904
- resourceId = command ? `cmd:${createHash23("sha256").update(command).digest("hex").slice(0, 16)}` : void 0;
149292
+ resourceId = command ? `cmd:${createHash24("sha256").update(command).digest("hex").slice(0, 16)}` : void 0;
148905
149293
  if (git7?.write) {
148906
149294
  category = "git_write";
148907
149295
  operation = git7.operation;
@@ -177423,7 +177811,7 @@ var init_code_node_runner = __esm({
177423
177811
  // ../engine/src/agent-tools.ts
177424
177812
  import { appendFile as appendFile3, mkdir as mkdir28, readFile as readFile38, readdir as readdir11, realpath as realpath2, stat as stat11, writeFile as writeFile26 } from "node:fs/promises";
177425
177813
  import { existsSync as existsSync52 } from "node:fs";
177426
- import { createHash as createHash24, randomUUID as randomUUID46 } from "node:crypto";
177814
+ import { createHash as createHash25, randomUUID as randomUUID46 } from "node:crypto";
177427
177815
  import { tmpdir as tmpdir5 } from "node:os";
177428
177816
  import { extname as extname4, isAbsolute as isAbsolute14, join as join66, relative as relative14, resolve as resolve37, sep as sep10 } from "node:path";
177429
177817
  import { Type as Type2 } from "@earendil-works/pi-ai";
@@ -177467,7 +177855,7 @@ async function readAgentMemoryWorkspaceLongTerm(rootDir, agentId) {
177467
177855
  }
177468
177856
  }
177469
177857
  function qmdAgentMemoryCollectionName2(rootDir, agentId) {
177470
- const hash3 = createHash24("sha1").update(`${rootDir}:${agentId}`).digest("hex").slice(0, 12);
177858
+ const hash3 = createHash25("sha1").update(`${rootDir}:${agentId}`).digest("hex").slice(0, 12);
177471
177859
  return `fusion-agent-memory-${sanitizeAgentMemoryId(agentId).toLowerCase()}-${hash3}`;
177472
177860
  }
177473
177861
  function buildQmdAgentMemoryCollectionAddArgs(rootDir, agentId) {
@@ -187582,7 +187970,7 @@ var init_verification_utils = __esm({
187582
187970
 
187583
187971
  // ../engine/src/merge/merge-dependency-sync.ts
187584
187972
  import { exec as exec25 } from "node:child_process";
187585
- import { createHash as createHash26 } from "node:crypto";
187973
+ import { createHash as createHash27 } from "node:crypto";
187586
187974
  import { existsSync as existsSync54, readFileSync as readFileSync17, writeFileSync as writeFileSync6 } from "node:fs";
187587
187975
  import { join as join68 } from "node:path";
187588
187976
  import { promisify as promisify35 } from "node:util";
@@ -187609,7 +187997,7 @@ function computeLockfileHash(rootDir) {
187609
187997
  const p2 = join68(rootDir, name2);
187610
187998
  if (existsSync54(p2)) {
187611
187999
  try {
187612
- return createHash26("sha256").update(readFileSync17(p2)).digest("hex");
188000
+ return createHash27("sha256").update(readFileSync17(p2)).digest("hex");
187613
188001
  } catch {
187614
188002
  return null;
187615
188003
  }
@@ -190146,7 +190534,7 @@ var init_mesh_lease_manager = __esm({
190146
190534
  });
190147
190535
 
190148
190536
  // ../engine/src/merge/merger-integration-worktree.ts
190149
- import { createHash as createHash27 } from "node:crypto";
190537
+ import { createHash as createHash28 } from "node:crypto";
190150
190538
  import { exec as exec31, execFile as execFile22 } from "node:child_process";
190151
190539
  import { promisify as promisify45 } from "node:util";
190152
190540
  function quoteAutostashArg(value) {
@@ -190269,7 +190657,7 @@ async function gitDirtyFingerprintLocal(rootDir) {
190269
190657
  )
190270
190658
  ]);
190271
190659
  if (!diffOut && !statusOut) return "";
190272
- return createHash27("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
190660
+ return createHash28("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
190273
190661
  } catch {
190274
190662
  return "";
190275
190663
  }
@@ -191255,7 +191643,7 @@ import { execSync as execSync5, exec as exec33 } from "node:child_process";
191255
191643
  import * as childProcess4 from "node:child_process";
191256
191644
  import { promisify as promisify48 } from "node:util";
191257
191645
  import { existsSync as existsSync57, readFileSync as readFileSync20, writeFileSync as writeFileSync8, unlinkSync as unlinkSync3, renameSync as renameSync4 } from "node:fs";
191258
- import { createHash as createHash28 } from "node:crypto";
191646
+ import { createHash as createHash29 } from "node:crypto";
191259
191647
  import { join as join72, resolve as resolve43 } from "node:path";
191260
191648
  import { hostname as hostname2 } from "node:os";
191261
191649
  import { Type as Type4 } from "typebox";
@@ -191503,7 +191891,7 @@ async function gitDirtyFingerprint(rootDir) {
191503
191891
  )
191504
191892
  ]);
191505
191893
  if (!diffOut && !statusOut) return "";
191506
- return createHash28("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
191894
+ return createHash29("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
191507
191895
  } catch {
191508
191896
  return "";
191509
191897
  }
@@ -200390,7 +200778,7 @@ import { promisify as promisify79 } from "node:util";
200390
200778
  // ../engine/src/runtimes/in-process-runtime.ts
200391
200779
  init_src2();
200392
200780
  init_scheduler();
200393
- import { createHash as createHash31 } from "node:crypto";
200781
+ import { createHash as createHash32 } from "node:crypto";
200394
200782
  import { EventEmitter as EventEmitter27 } from "node:events";
200395
200783
 
200396
200784
  // ../engine/src/executor/public-reexports.ts
@@ -203117,7 +203505,7 @@ init_agent_tools();
203117
203505
  init_agent_logger();
203118
203506
  init_agent_usage_telemetry();
203119
203507
  import { Type as Type3 } from "@earendil-works/pi-ai";
203120
- import { createHash as createHash25 } from "node:crypto";
203508
+ import { createHash as createHash26 } from "node:crypto";
203121
203509
 
203122
203510
  // ../engine/src/agents/approval-mail.ts
203123
203511
  init_src2();
@@ -204745,7 +205133,7 @@ function truncatePrompt(text2, maxChars) {
204745
205133
  ... (truncated, ${text2.length} chars)`;
204746
205134
  }
204747
205135
  function shortContentHash(value) {
204748
- return createHash25("sha256").update(value).digest("hex").slice(0, 8);
205136
+ return createHash26("sha256").update(value).digest("hex").slice(0, 8);
204749
205137
  }
204750
205138
  function buildIdentitySnapshot(args) {
204751
205139
  const { agent, resolvedInstructions, workspaceMemory } = args;
@@ -233233,6 +233621,34 @@ init_effective_settings();
233233
233621
  init_run_audit();
233234
233622
  init_logger();
233235
233623
  init_lifecycle_columns();
233624
+
233625
+ // ../engine/src/executor/completion-recommendation-notice.ts
233626
+ init_src2();
233627
+ var pendingNotices = /* @__PURE__ */ new Set();
233628
+ function dispatchAcceptedCompletionRecommendationNotice(args) {
233629
+ if (!args.recommendations?.length) return;
233630
+ const pending = (async () => {
233631
+ const settings = args.settings ?? await args.store.getSettings();
233632
+ const task = await args.store.getTask(args.taskId);
233633
+ if (!task || task.deletedAt) return;
233634
+ await notifyOperatorOfTaskRecommendations(
233635
+ args.store,
233636
+ { id: args.taskId, title: args.taskTitle ?? task.title },
233637
+ args.recommendations,
233638
+ settings
233639
+ );
233640
+ })();
233641
+ pendingNotices.add(pending);
233642
+ void pending.catch((error2) => {
233643
+ args.log?.warn(
233644
+ `Failed to dispatch recommendation mailbox notice for ${args.taskId}: ${error2 instanceof Error ? error2.message : String(error2)}`
233645
+ );
233646
+ }).finally(() => {
233647
+ pendingNotices.delete(pending);
233648
+ });
233649
+ }
233650
+
233651
+ // ../engine/src/executor/create-task-done-tool.ts
233236
233652
  function createTaskDoneTool(deps, taskId, worktreePath, promptContent, codeReviewVerdicts, onDone, audit) {
233237
233653
  const store = deps.store;
233238
233654
  return {
@@ -233567,6 +233983,16 @@ ${rerunSuffix}`
233567
233983
  bulkCompletionRefusalAt: null
233568
233984
  });
233569
233985
  await store.logEntry(taskId, "Task marked done by agent", void 0, deps.getRunContextFor(taskId));
233986
+ if (completionRecommendations !== void 0) {
233987
+ dispatchAcceptedCompletionRecommendationNotice({
233988
+ store,
233989
+ taskId,
233990
+ taskTitle: task.title,
233991
+ recommendations: completionRecommendations,
233992
+ settings,
233993
+ log: executorLog
233994
+ });
233995
+ }
233570
233996
  const latestTask = await store.getTask(taskId);
233571
233997
  let latestColumn = latestTask.column;
233572
233998
  if (latestColumn === await resolveReboundColumnFor(store, taskId)) {
@@ -245399,6 +245825,16 @@ ${rerunSuffix}` });
245399
245825
  if (latestColumn === await resolveWipTargetForTask(deps.store, task.id) && !hardPauseActive && beforeWatchdog && !beforeWatchdog.deletedAt && !(rejectIfPaused && (beforeWatchdog.paused || beforeWatchdog.userPaused))) {
245400
245826
  deps.scheduleCompletedTaskWatchdog(task.id, "fn_task_done");
245401
245827
  }
245828
+ if (recommendations !== void 0) {
245829
+ dispatchAcceptedCompletionRecommendationNotice({
245830
+ store: deps.store,
245831
+ taskId: task.id,
245832
+ taskTitle: task.title,
245833
+ recommendations,
245834
+ settings,
245835
+ log: executorLog
245836
+ });
245837
+ }
245402
245838
  return { completed: true, hardPauseActive };
245403
245839
  } catch (error2) {
245404
245840
  await restoreNoCommitsExpected();
@@ -250612,7 +251048,7 @@ init_notifier();
250612
251048
 
250613
251049
  // ../engine/src/healing/stuck-task-detector.ts
250614
251050
  init_logger();
250615
- import { createHash as createHash29 } from "node:crypto";
251051
+ import { createHash as createHash30 } from "node:crypto";
250616
251052
  var stuckLog = createLogger("stuck-detector");
250617
251053
  var LOOP_ACTIVITY_THRESHOLD = 60;
250618
251054
  var LOOP_IGNORED_STEP_MIN = 10;
@@ -250623,7 +251059,7 @@ var TOOL_DETAIL_HASH_HEX_LEN = 16;
250623
251059
  var NO_PROGRESS_CHURN_THRESHOLD = 25;
250624
251060
  var VERIFICATION_DEADLINE_GRACE_MS = 5e3;
250625
251061
  function hashToolDetail(detail) {
250626
- return createHash29("sha256").update(detail).digest("hex").slice(0, TOOL_DETAIL_HASH_HEX_LEN);
251062
+ return createHash30("sha256").update(detail).digest("hex").slice(0, TOOL_DETAIL_HASH_HEX_LEN);
250627
251063
  }
250628
251064
  function buildToolFingerprint(toolName, toolDetail) {
250629
251065
  const name2 = toolName.trim().toLowerCase() || "tool";
@@ -253373,7 +253809,7 @@ var MissionAutopilot = class {
253373
253809
  // ../engine/src/missions/mission-execution-loop.ts
253374
253810
  init_src2();
253375
253811
  import { EventEmitter as EventEmitter26 } from "node:events";
253376
- import { createHash as createHash30 } from "node:crypto";
253812
+ import { createHash as createHash31 } from "node:crypto";
253377
253813
 
253378
253814
  // ../engine/src/missions/mission-verification.ts
253379
253815
  init_sandbox();
@@ -253445,7 +253881,7 @@ var MAX_VALIDATION_RESPONSE_BYTES = 256 * 1024;
253445
253881
  var MAX_VALIDATION_JSON_CANDIDATES = 8;
253446
253882
  var VALIDATION_FAILURE_BUDGET_PER_FINGERPRINT = 3;
253447
253883
  function fingerprintMissionValidationInput(landedSha, provider, modelId, systemPrompt, userPrompt) {
253448
- return createHash30("sha256").update(Buffer.from(JSON.stringify(["mission-validation-input-v1", landedSha, provider, modelId, systemPrompt, userPrompt]), "utf8")).digest("hex");
253884
+ return createHash31("sha256").update(Buffer.from(JSON.stringify(["mission-validation-input-v1", landedSha, provider, modelId, systemPrompt, userPrompt]), "utf8")).digest("hex");
253449
253885
  }
253450
253886
  var MissionExecutionLoop = class extends EventEmitter26 {
253451
253887
  running = false;
@@ -257161,7 +257597,7 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
257161
257597
  }
257162
257598
  return;
257163
257599
  }
257164
- const duplicateReport = { outcome: "parked" };
257600
+ const duplicateReport = { outcome: "parked", planningWorkItemId };
257165
257601
  if (await this.tryFinalizeExplicitDuplicateMarker(task, written, settings, {
257166
257602
  isReplan,
257167
257603
  feedback
@@ -257180,6 +257616,7 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
257180
257616
  isReplan,
257181
257617
  feedback
257182
257618
  });
257619
+ finalizeReport.planningWorkItemId = planningWorkItemId;
257183
257620
  this.options.onSpecifyComplete?.(task, finalizeReport);
257184
257621
  } finally {
257185
257622
  this.activeSessions.delete(task.id);
@@ -258754,11 +259191,46 @@ async function reactToSpecificationComplete(deps) {
258754
259191
  return;
258755
259192
  }
258756
259193
  deps.log(`Specified ${deps.taskId} \u2192 todo`);
258757
- const live = await deps.getTask(deps.taskId);
258758
- if (!live || live.paused || live.userPaused) return;
258759
- const ir2 = await deps.resolveIr(live.id);
258760
- await deps.seed(live, ir2);
258761
- deps.kick();
259194
+ const sleep7 = deps.sleep ?? ((ms2) => new Promise((resolve47) => setTimeout(resolve47, ms2)));
259195
+ const QUIET_PARK_REASONS = /* @__PURE__ */ new Set([
259196
+ "no-pre-release-plan-review",
259197
+ "awaiting-approval",
259198
+ "paused",
259199
+ "plan-review-passed"
259200
+ ]);
259201
+ const RETRY_DELAYS_MS2 = [1e3, 5e3];
259202
+ for (let attempt = 0; ; attempt++) {
259203
+ let failure = null;
259204
+ try {
259205
+ const live = await deps.getTask(deps.taskId);
259206
+ if (!live || live.paused || live.userPaused) return;
259207
+ if (live.status === "needs-replan") {
259208
+ deps.log(`Plan Review handoff for ${deps.taskId} not armed (needs-replan)`);
259209
+ return;
259210
+ }
259211
+ const ir2 = await deps.resolveIr(live.id);
259212
+ const result = await deps.seed(live, ir2);
259213
+ if (result.seeded) {
259214
+ deps.kick();
259215
+ return;
259216
+ }
259217
+ if (result.reason && QUIET_PARK_REASONS.has(result.reason)) {
259218
+ deps.log(`Plan Review handoff for ${deps.taskId} not armed (${result.reason})`);
259219
+ return;
259220
+ }
259221
+ failure = result.reason ?? "not-seeded";
259222
+ } catch (error2) {
259223
+ failure = error2 instanceof Error ? error2.message : String(error2);
259224
+ }
259225
+ if (attempt >= RETRY_DELAYS_MS2.length) {
259226
+ deps.warn(
259227
+ `Plan Review handoff for ${deps.taskId} failed after ${attempt + 1} attempts (${failure}) \u2014 stranded-continuation self-healing will recover it`
259228
+ );
259229
+ return;
259230
+ }
259231
+ deps.warn(`Plan Review handoff for ${deps.taskId} did not seed (${failure}) \u2014 retrying`);
259232
+ await sleep7(RETRY_DELAYS_MS2[attempt]);
259233
+ }
258762
259234
  }
258763
259235
  async function drainDuePlanningContinuations(deps) {
258764
259236
  const items = await deps.listDue();
@@ -258895,7 +259367,7 @@ function stableNotificationJson(value) {
258895
259367
  return `{${fields.join(",")}}`;
258896
259368
  }
258897
259369
  function buildCliAgentNotificationDedupeKey(input) {
258898
- const notificationFingerprint = createHash31("sha256").update(stableNotificationJson(input.info.notification ?? null)).digest("hex").slice(0, 16);
259370
+ const notificationFingerprint = createHash32("sha256").update(stableNotificationJson(input.info.notification ?? null)).digest("hex").slice(0, 16);
258899
259371
  const waitingEpoch = input.session?.updatedAt ?? "unknown-waiting-epoch";
258900
259372
  return [
258901
259373
  "cli-agent",
@@ -259007,6 +259479,8 @@ var InProcessRuntime = class extends EventEmitter27 {
259007
259479
  messageStore;
259008
259480
  /** FNXC:TaskDeleteNotice 2026-07-26-16:10: identity-guarded teardown for the delete-notice mailbox seam. */
259009
259481
  unregisterTaskDeleteNoticeMailbox;
259482
+ /** FNXC:TaskRecommendations 2026-08-13-03:56: identity-guarded teardown for the store-scoped recommendation notice seam. */
259483
+ unregisterTaskRecommendationNoticeMailbox;
259010
259484
  chatStore;
259011
259485
  detachAgentLinkSync;
259012
259486
  /**
@@ -259065,7 +259539,8 @@ var InProcessRuntime = class extends EventEmitter27 {
259065
259539
  createTaskStoreForBackend: createTaskStoreForBackend2,
259066
259540
  buildConsumerId: buildConsumerId2,
259067
259541
  createProjectScopedPluginMcpProvider: createProjectScopedPluginMcpProvider2,
259068
- registerTaskDeleteNoticeMailbox: registerTaskDeleteNoticeMailbox2
259542
+ registerTaskDeleteNoticeMailbox: registerTaskDeleteNoticeMailbox2,
259543
+ registerTaskRecommendationNoticeMailbox: registerTaskRecommendationNoticeMailbox2
259069
259544
  } = await Promise.resolve().then(() => (init_src2(), src_exports));
259070
259545
  if (this.config.externalTaskStore) {
259071
259546
  this.taskStore = this.config.externalTaskStore;
@@ -259124,6 +259599,10 @@ var InProcessRuntime = class extends EventEmitter27 {
259124
259599
  this.taskStore,
259125
259600
  this.messageStore
259126
259601
  );
259602
+ this.unregisterTaskRecommendationNoticeMailbox = registerTaskRecommendationNoticeMailbox2(
259603
+ this.taskStore,
259604
+ this.messageStore
259605
+ );
259127
259606
  await yieldEventLoop4();
259128
259607
  const pluginLayer = this.taskStore.getAsyncLayer();
259129
259608
  if (!pluginLayer) {
@@ -259615,9 +260094,19 @@ var InProcessRuntime = class extends EventEmitter27 {
259615
260094
  outcome: report2.outcome,
259616
260095
  getTask: (id) => Promise.resolve(this.taskStore.getTask(id)),
259617
260096
  resolveIr: (id) => resolveWorkflowIrForTask(this.taskStore, id),
259618
- seed: (task, ir2) => seedPreReleasePlanReviewContinuation(this.taskStore, task, ir2),
260097
+ /*
260098
+ FNXC:PlanningHandoffAtomicity 2026-08-13-03:49:
260099
+ The report carries the planning session's own durable work item id so the
260100
+ seeder can retire that exact predecessor row atomically with the successor
260101
+ install. Without it the seeder raced triage's finally-block terminal
260102
+ transition and bailed on its own caller (529 stranded cards in 18 days).
260103
+ */
260104
+ seed: (task, ir2) => seedPreReleasePlanReviewContinuation(this.taskStore, task, ir2, {
260105
+ retirePredecessorId: report2.planningWorkItemId
260106
+ }),
259619
260107
  kick: () => this.kickWorkflowContinuationProcessor(),
259620
- log: (message) => runtimeLog.log(message)
260108
+ log: (message) => runtimeLog.log(message),
260109
+ warn: (message) => runtimeLog.warn(message)
259621
260110
  }).catch((error2) => {
259622
260111
  runtimeLog.error(`Failed to start Todo plan review for ${t.id}:`, error2);
259623
260112
  });
@@ -259905,6 +260394,8 @@ var InProcessRuntime = class extends EventEmitter27 {
259905
260394
  this.backendShutdown = void 0;
259906
260395
  this.unregisterTaskDeleteNoticeMailbox?.();
259907
260396
  this.unregisterTaskDeleteNoticeMailbox = void 0;
260397
+ this.unregisterTaskRecommendationNoticeMailbox?.();
260398
+ this.unregisterTaskRecommendationNoticeMailbox = void 0;
259908
260399
  let stopError;
259909
260400
  try {
259910
260401
  if (this.workflowContinuationTimer) {