@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
package/dist/bin.js CHANGED
@@ -2184,6 +2184,8 @@ var init_settings_schema = __esm({
2184
2184
  DEFAULT_PROJECT_SETTINGS = {
2185
2185
  // FNXC:TaskRecommendations 2026-08-08-05:02: completion follows-ups stay bounded by default; 0 disables writing them.
2186
2186
  maxRecommendationsPerTask: 3,
2187
+ // FNXC:TaskRecommendations 2026-08-13-03:56: surface completed-task proposals by default; operators can suppress the notice without suppressing capture.
2188
+ recommendationMailboxNoticeEnabled: true,
2187
2189
  globalPause: false,
2188
2190
  globalPauseReason: void 0,
2189
2191
  defaultWorkflowId: void 0,
@@ -21756,9 +21758,14 @@ var init_project = __esm({
21756
21758
  // now keyed per-project on `project_id` (the effective PK). `id` is retained
21757
21759
  // for column-shape parity (always 1) but is no longer the PK and no longer
21758
21760
  // CHECK-constrained. Single-project / SQLite-parity callers leave project_id
21759
- // at its '' default (one row), preserving the pre-isolation behavior.
21761
+ // through the database ownership trigger, preserving the pre-isolation behavior.
21762
+ /*
21763
+ FNXC:MultiProjectIsolation 2026-08-12-15:43:
21764
+ Migration 0006 owns config's project_id default and leaves its catalog PK as (project_id).
21765
+ Drizzle emits DEFAULT while the database GUC/trigger selects the effective ownership partition.
21766
+ */
21760
21767
  id: integer("id").default(1),
21761
- projectId: text("project_id").notNull().default("").primaryKey(),
21768
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`).primaryKey(),
21762
21769
  nextId: integer("next_id").default(1),
21763
21770
  nextWorkflowStepId: integer("next_workflow_step_id").default(1),
21764
21771
  // FNXC:SqliteFinalRemoval 2026-06-28:
@@ -22152,9 +22159,13 @@ var init_project = __esm({
22152
22159
  automations = projectSchema.table("automations", {
22153
22160
  /*
22154
22161
  * FNXC:AutomationIsolation 2026-07-13-22:37:
22155
- * 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.
22162
+ * 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.
22163
+ *
22164
+ * FNXC:MultiProjectIsolation 2026-08-12-15:43:
22165
+ * Migration 0006 owns the default through the database GUC/trigger; Drizzle emits DEFAULT
22166
+ * without replacing the physical legacy fallback or the fail-closed bound writer contract.
22156
22167
  */
22157
- projectId: text("project_id").notNull().default(""),
22168
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
22158
22169
  id: text("id").notNull(),
22159
22170
  name: text("name").notNull(),
22160
22171
  description: text("description"),
@@ -22453,7 +22464,8 @@ var init_project = __esm({
22453
22464
  index("idxTaskDocumentsTaskId").on(t.taskId)
22454
22465
  ]);
22455
22466
  artifacts = projectSchema.table("artifacts", {
22456
- id: text("id").primaryKey(),
22467
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
22468
+ id: text("id").notNull(),
22457
22469
  type: text("type").notNull(),
22458
22470
  title: text("title").notNull(),
22459
22471
  description: text("description"),
@@ -22468,7 +22480,8 @@ var init_project = __esm({
22468
22480
  createdAt: text("created_at").notNull(),
22469
22481
  updatedAt: text("updated_at").notNull()
22470
22482
  }, (t) => [
22471
- foreignKey({ columns: [t.taskId], foreignColumns: [tasks.id] }).onDelete("cascade"),
22483
+ primaryKey({ columns: [t.projectId, t.id] }),
22484
+ foreignKey({ name: "artifacts_task_id_fkey", columns: [t.projectId, t.taskId], foreignColumns: [tasks.projectId, tasks.id] }).onDelete("cascade"),
22472
22485
  index("idxArtifactsTaskId").on(t.taskId),
22473
22486
  index("idxArtifactsAuthorId").on(t.authorId),
22474
22487
  index("idxArtifactsType").on(t.type),
@@ -22658,7 +22671,8 @@ var init_project = __esm({
22658
22671
  index("idxEvalRunEventsRunIdSeq").on(t.runId, t.seq)
22659
22672
  ]);
22660
22673
  secrets = projectSchema.table("secrets", {
22661
- id: text("id").primaryKey(),
22674
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
22675
+ id: text("id").notNull(),
22662
22676
  key: text("key").notNull(),
22663
22677
  valueCiphertext: bytea("value_ciphertext").notNull(),
22664
22678
  nonce: bytea("nonce").notNull(),
@@ -22671,7 +22685,8 @@ var init_project = __esm({
22671
22685
  lastReadAt: text("last_read_at"),
22672
22686
  lastReadBy: text("last_read_by")
22673
22687
  }, (t) => [
22674
- unique("secrets_key_unique").on(t.key),
22688
+ primaryKey({ columns: [t.projectId, t.id] }),
22689
+ unique("secrets_key_unique").on(t.projectId, t.key),
22675
22690
  check("secrets_access_policy_check", sql`${t.accessPolicy} IN ('auto', 'prompt', 'deny')`),
22676
22691
  check("secrets_env_exportable_check", sql`${t.envExportable} IN (0, 1)`)
22677
22692
  ]);
@@ -22713,10 +22728,11 @@ var init_project = __esm({
22713
22728
  updatedAt: text("updated_at").notNull()
22714
22729
  }, (t) => [primaryKey({ columns: [t.projectId, t.id] })]);
22715
22730
  branchGroups = projectSchema.table("branch_groups", {
22716
- id: text("id").primaryKey(),
22731
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
22732
+ id: text("id").notNull(),
22717
22733
  sourceType: text("source_type").notNull(),
22718
22734
  sourceId: text("source_id").notNull(),
22719
- branchName: text("branch_name").notNull().unique(),
22735
+ branchName: text("branch_name").notNull(),
22720
22736
  worktreePath: text("worktree_path"),
22721
22737
  autoMerge: integer("auto_merge").notNull().default(0),
22722
22738
  prState: text("pr_state").notNull().default("none"),
@@ -22732,6 +22748,8 @@ var init_project = __esm({
22732
22748
  updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
22733
22749
  closedAt: bigint("closed_at", { mode: "number" })
22734
22750
  }, (t) => [
22751
+ primaryKey({ columns: [t.projectId, t.id] }),
22752
+ unique("branch_groups_branch_name_key").on(t.projectId, t.branchName),
22735
22753
  check("branch_groups_source_type_check", sql`${t.sourceType} IN ('mission','planning','new-task')`),
22736
22754
  check("branch_groups_pr_state_check", sql`${t.prState} IN ('none','open','merged','closed')`),
22737
22755
  check("branch_groups_status_check", sql`${t.status} IN ('open','finalized','abandoned')`),
@@ -23133,12 +23151,14 @@ var init_project = __esm({
23133
23151
  index("idxUsageEventsKindTs").on(t.kind, t.ts)
23134
23152
  ]);
23135
23153
  pluginActivations = projectSchema.table("plugin_activations", {
23136
- id: integer("id").generatedAlwaysAsIdentity().primaryKey(),
23154
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23155
+ id: integer("id").generatedAlwaysAsIdentity().notNull(),
23137
23156
  pluginId: text("plugin_id").notNull(),
23138
23157
  source: text("source").notNull(),
23139
23158
  pluginVersion: text("plugin_version"),
23140
23159
  activatedAt: text("activated_at").notNull()
23141
23160
  }, (t) => [
23161
+ primaryKey({ columns: [t.projectId, t.id] }),
23142
23162
  index("idxPluginActivationsActivatedAt").on(t.activatedAt),
23143
23163
  index("idxPluginActivationsPluginId").on(t.pluginId)
23144
23164
  ]);
@@ -23163,8 +23183,13 @@ var init_project = __esm({
23163
23183
  index("idxKnowledgePagesUpdatedAt").on(t.updatedAt)
23164
23184
  ]);
23165
23185
  deployments = projectSchema.table("deployments", {
23166
- id: integer("id").generatedAlwaysAsIdentity().primaryKey(),
23167
- projectId: text("project_id").notNull().default(""),
23186
+ id: integer("id").generatedAlwaysAsIdentity().notNull(),
23187
+ /*
23188
+ FNXC:MultiProjectIsolation 2026-08-12-15:43:
23189
+ Migration 0006 rebuilt deployments_pkey as (project_id, id). Drizzle emits DEFAULT while the
23190
+ database GUC/trigger keeps ownership stamping and the physical legacy fallback authoritative.
23191
+ */
23192
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23168
23193
  deploymentId: text("deployment_id").notNull(),
23169
23194
  service: text("service"),
23170
23195
  environment: text("environment"),
@@ -23175,6 +23200,7 @@ var init_project = __esm({
23175
23200
  meta: jsonb("meta"),
23176
23201
  createdAt: text("created_at").notNull()
23177
23202
  }, (t) => [
23203
+ primaryKey({ columns: [t.projectId, t.id], name: "deployments_pkey" }),
23178
23204
  uniqueIndex("idxDeploymentsProjectDeploymentId").on(t.projectId, t.deploymentId),
23179
23205
  index("idxDeploymentsProjectDeployedAt").on(t.projectId, t.deployedAt),
23180
23206
  index("idxDeploymentsDeployedAt").on(t.deployedAt),
@@ -23182,7 +23208,12 @@ var init_project = __esm({
23182
23208
  ]);
23183
23209
  githubCheckStates = projectSchema.table("github_check_states", {
23184
23210
  id: integer("id").generatedAlwaysAsIdentity().notNull(),
23185
- projectId: text("project_id").notNull().default(""),
23211
+ /*
23212
+ FNXC:MultiProjectIsolation 2026-08-12-15:43:
23213
+ Migration 0048 post-dated 0006, so FN-9004 reconciles its physical default to the ownership
23214
+ expression. Drizzle emits DEFAULT while the database GUC/trigger remains the stamping authority.
23215
+ */
23216
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23186
23217
  repo: text("repo").notNull(),
23187
23218
  headSha: text("head_sha").notNull(),
23188
23219
  checkName: text("check_name").notNull(),
@@ -23202,9 +23233,14 @@ var init_project = __esm({
23202
23233
  index("idxGithubCheckStatesProjectReceived").on(t.projectId, t.receivedAt)
23203
23234
  ]);
23204
23235
  incidents = projectSchema.table("incidents", {
23205
- id: integer("id").generatedAlwaysAsIdentity().primaryKey(),
23206
- projectId: text("project_id").notNull().default(""),
23207
- incidentId: text("incident_id").notNull().unique(),
23236
+ id: integer("id").generatedAlwaysAsIdentity().notNull(),
23237
+ /*
23238
+ FNXC:MultiProjectIsolation 2026-08-12-15:43:
23239
+ Migration 0006 rebuilt incident keys with project_id first. Drizzle emits DEFAULT while the
23240
+ database GUC/trigger retains ownership stamping and its physical legacy fallback.
23241
+ */
23242
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23243
+ incidentId: text("incident_id").notNull(),
23208
23244
  groupingKey: text("grouping_key").notNull(),
23209
23245
  title: text("title").notNull(),
23210
23246
  severity: text("severity"),
@@ -23218,6 +23254,8 @@ var init_project = __esm({
23218
23254
  createdAt: text("created_at").notNull(),
23219
23255
  updatedAt: text("updated_at").notNull()
23220
23256
  }, (t) => [
23257
+ primaryKey({ columns: [t.projectId, t.id], name: "incidents_pkey" }),
23258
+ unique("incidents_incident_id_key").on(t.projectId, t.incidentId),
23221
23259
  index("idxIncidentsProjectOpenedAt").on(t.projectId, t.openedAt),
23222
23260
  index("idxIncidentsProjectStatus").on(t.projectId, t.status),
23223
23261
  index("idxIncidentsGroupingKey").on(t.groupingKey),
@@ -23272,12 +23310,14 @@ var init_project = __esm({
23272
23310
  content: text("content").notNull(),
23273
23311
  type: text("type").notNull(),
23274
23312
  read: integer("read").default(0),
23313
+ archived: integer("archived").default(0),
23275
23314
  metadata: jsonb("metadata"),
23276
23315
  createdAt: text("created_at").notNull(),
23277
23316
  updatedAt: text("updated_at").notNull()
23278
23317
  }, (t) => [
23279
23318
  primaryKey({ columns: [t.projectId, t.id] }),
23280
23319
  index("idxMessagesTo").on(t.toId, t.toType, t.read),
23320
+ index("idxMessagesToArchived").on(t.toId, t.toType, t.archived),
23281
23321
  index("idxMessagesFrom").on(t.fromId, t.fromType),
23282
23322
  index("idxMessagesCreatedAt").on(t.createdAt)
23283
23323
  ]);
@@ -23378,7 +23418,8 @@ var init_project = __esm({
23378
23418
  index("idx_cli_sessions_project_state").on(t.projectId, t.agentState)
23379
23419
  ]);
23380
23420
  chatMessages = projectSchema.table("chat_messages", {
23381
- id: text("id").primaryKey(),
23421
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23422
+ id: text("id").notNull(),
23382
23423
  sessionId: text("session_id").notNull(),
23383
23424
  role: text("role").notNull(),
23384
23425
  content: text("content").notNull(),
@@ -23387,6 +23428,7 @@ var init_project = __esm({
23387
23428
  createdAt: text("created_at").notNull(),
23388
23429
  attachments: jsonb("attachments")
23389
23430
  }, (t) => [
23431
+ primaryKey({ columns: [t.projectId, t.id] }),
23390
23432
  index("idxChatMessagesSessionId").on(t.sessionId),
23391
23433
  index("idxChatMessagesCreatedAt").on(t.createdAt)
23392
23434
  ]);
@@ -23412,7 +23454,8 @@ var init_project = __esm({
23412
23454
  index("idxChatTokenUsageCreatedAt").on(t.createdAt)
23413
23455
  ]);
23414
23456
  runAuditEvents = projectSchema.table("run_audit_events", {
23415
- id: text("id").primaryKey(),
23457
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23458
+ id: text("id").notNull(),
23416
23459
  timestamp: text("timestamp").notNull(),
23417
23460
  taskId: text("task_id"),
23418
23461
  agentId: text("agent_id").notNull(),
@@ -23422,6 +23465,7 @@ var init_project = __esm({
23422
23465
  target: text("target").notNull(),
23423
23466
  metadata: jsonb("metadata")
23424
23467
  }, (t) => [
23468
+ primaryKey({ columns: [t.projectId, t.id] }),
23425
23469
  index("idxRunAuditEventsRunIdTimestamp").on(t.runId, t.timestamp),
23426
23470
  index("idxRunAuditEventsTaskIdTimestamp").on(t.taskId, t.timestamp),
23427
23471
  index("idxRunAuditEventsTimestamp").on(t.timestamp)
@@ -23522,13 +23566,14 @@ var init_project = __esm({
23522
23566
  index("idxMissionLineageStopsMissionId").on(t.projectId, t.missionId)
23523
23567
  ]);
23524
23568
  verificationCache = projectSchema.table("verification_cache", {
23569
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23525
23570
  treeSha: text("tree_sha").notNull(),
23526
23571
  testCommand: text("test_command").notNull().default(""),
23527
23572
  buildCommand: text("build_command").notNull().default(""),
23528
23573
  recordedAt: text("recorded_at").notNull(),
23529
23574
  taskId: text("task_id")
23530
23575
  }, (t) => [
23531
- primaryKey({ columns: [t.treeSha, t.testCommand, t.buildCommand] }),
23576
+ primaryKey({ columns: [t.projectId, t.treeSha, t.testCommand, t.buildCommand] }),
23532
23577
  index("idxVerificationCacheRecordedAt").on(t.recordedAt)
23533
23578
  ]);
23534
23579
  importTranslationCache = projectSchema.table("import_translation_cache", {
@@ -23561,7 +23606,8 @@ var init_project = __esm({
23561
23606
  index("idxImportTranslationCacheRecordedAt").on(t.recordedAt)
23562
23607
  ]);
23563
23608
  approvalRequests = projectSchema.table("approval_requests", {
23564
- id: text("id").primaryKey(),
23609
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23610
+ id: text("id").notNull(),
23565
23611
  status: text("status").notNull(),
23566
23612
  requesterActorId: text("requester_actor_id").notNull(),
23567
23613
  requesterActorType: text("requester_actor_type").notNull(),
@@ -23580,13 +23626,14 @@ var init_project = __esm({
23580
23626
  createdAt: text("created_at").notNull(),
23581
23627
  updatedAt: text("updated_at").notNull()
23582
23628
  }, (t) => [
23629
+ primaryKey({ columns: [t.projectId, t.id] }),
23583
23630
  index("idxApprovalRequestsStatusCreatedAt").on(t.status, t.createdAt),
23584
23631
  index("idxApprovalRequestsRequesterCreatedAt").on(t.requesterActorId, t.createdAt),
23585
23632
  index("idxApprovalRequestsTaskCreatedAt").on(t.taskId, t.createdAt)
23586
23633
  ]);
23587
23634
  approvalRequestAuditEvents = projectSchema.table("approval_request_audit_events", {
23588
- projectId: text("project_id").notNull().default(""),
23589
- id: text("id").primaryKey(),
23635
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23636
+ id: text("id").notNull(),
23590
23637
  requestId: text("request_id").notNull(),
23591
23638
  eventType: text("event_type").notNull(),
23592
23639
  actorId: text("actor_id").notNull(),
@@ -23595,6 +23642,7 @@ var init_project = __esm({
23595
23642
  note: text("note"),
23596
23643
  createdAt: text("created_at").notNull()
23597
23644
  }, (t) => [
23645
+ primaryKey({ columns: [t.projectId, t.id] }),
23598
23646
  index("idxApprovalRequestAuditRequestCreatedAt").on(t.requestId, t.createdAt, t.id),
23599
23647
  index("idxApprovalRequestAuditProjectCreatedAt").on(t.projectId, t.createdAt)
23600
23648
  ]);
@@ -27084,6 +27132,27 @@ var init_grok_provider = __esm({
27084
27132
  apiKey: "$GROK_API_KEY",
27085
27133
  api: "openai-completions",
27086
27134
  models: [
27135
+ /*
27136
+ FNXC:ModelRegistry 2026-08-12-20:17:
27137
+ FN-9008 requires Grok 4.6 to be selectable in every model area. Keep it in this shared
27138
+ catalog because pi's registerProvider() replaces provider model lists wholesale, while every
27139
+ Fusion host seeds and re-merges this export. xAI's model docs verify grok-4.6, its 500k-token
27140
+ context window, and configurable reasoning; max output and per-model image input remain
27141
+ upstream-pending-verification, so retain the conservative text-only and 65536-token defaults.
27142
+ Source: https://docs.x.ai/developers/models
27143
+ */
27144
+ {
27145
+ id: "grok-4.6",
27146
+ name: "Grok 4.6",
27147
+ reasoning: true,
27148
+ input: ["text"],
27149
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
27150
+ contextWindow: 5e5,
27151
+ maxTokens: 65536,
27152
+ compat: {
27153
+ supportsDeveloperRole: false
27154
+ }
27155
+ },
27087
27156
  {
27088
27157
  id: "grok-4.5",
27089
27158
  name: "Grok 4.5",
@@ -35042,6 +35111,7 @@ function rowToMessage(row) {
35042
35111
  content: row.content,
35043
35112
  type: row.type,
35044
35113
  read: (row.read ?? 0) === 1,
35114
+ archived: (row.archived ?? 0) === 1,
35045
35115
  metadata: row.metadata ?? void 0,
35046
35116
  createdAt: row.createdAt,
35047
35117
  updatedAt: row.updatedAt
@@ -35053,6 +35123,11 @@ function participantIdsForLookup(ownerId, ownerType) {
35053
35123
  }
35054
35124
  return [ownerId];
35055
35125
  }
35126
+ function archivedCondition(archived) {
35127
+ if (archived === true) return eq(project_exports.messages.archived, 1);
35128
+ if (archived === false) return or(eq(project_exports.messages.archived, 0), sql`${project_exports.messages.archived} IS NULL`);
35129
+ return or(eq(project_exports.messages.archived, 0), sql`${project_exports.messages.archived} IS NULL`);
35130
+ }
35056
35131
  async function sendMessage(handle, message) {
35057
35132
  const sanitizedContent = sanitizeTextValue(message.content);
35058
35133
  const sanitizedMetadata = sanitizeJsonbValue(message.metadata);
@@ -35065,6 +35140,7 @@ async function sendMessage(handle, message) {
35065
35140
  content: sanitizedContent,
35066
35141
  type: message.type,
35067
35142
  read: message.read ? 1 : 0,
35143
+ archived: message.archived ? 1 : 0,
35068
35144
  metadata: sanitizedMetadata,
35069
35145
  createdAt: message.createdAt,
35070
35146
  updatedAt: message.updatedAt
@@ -35073,7 +35149,8 @@ async function sendMessage(handle, message) {
35073
35149
  ...message,
35074
35150
  content: sanitizedContent,
35075
35151
  metadata: sanitizedMetadata,
35076
- read: message.read ? 1 : 0
35152
+ read: message.read ? 1 : 0,
35153
+ archived: message.archived ? 1 : 0
35077
35154
  });
35078
35155
  }
35079
35156
  async function sendMessageOnce(handle, message) {
@@ -35086,6 +35163,7 @@ async function sendMessageOnce(handle, message) {
35086
35163
  content: message.content,
35087
35164
  type: message.type,
35088
35165
  read: message.read ? 1 : 0,
35166
+ archived: message.archived ? 1 : 0,
35089
35167
  metadata: message.metadata,
35090
35168
  createdAt: message.createdAt,
35091
35169
  updatedAt: message.updatedAt
@@ -35137,6 +35215,7 @@ async function queryMessagesByParticipant(handle, direction, ownerId, ownerType,
35137
35215
  if (filter2?.read !== void 0) {
35138
35216
  conditions.push(eq(project_exports.messages.read, filter2.read ? 1 : 0));
35139
35217
  }
35218
+ conditions.push(archivedCondition(filter2?.archived));
35140
35219
  const limit2 = filter2?.limit ?? 100;
35141
35220
  const offset = filter2?.offset ?? 0;
35142
35221
  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(limit2).offset(offset);
@@ -35150,6 +35229,13 @@ async function markMessageAsRead(handle, messageId) {
35150
35229
  await handle.update(project_exports.messages).set({ read: 1, updatedAt: now }).where(eq(project_exports.messages.id, messageId));
35151
35230
  return await getMessage(handle, messageId);
35152
35231
  }
35232
+ async function setMessageArchived(handle, id, archived) {
35233
+ const existing = await getMessage(handle, id);
35234
+ if (!existing) return null;
35235
+ if (existing.archived === archived) return existing;
35236
+ 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);
35237
+ return rows[0] ? rowToMessage(rows[0]) : null;
35238
+ }
35153
35239
  async function markAllMessagesAsRead(handle, ownerId, ownerType) {
35154
35240
  const now = (/* @__PURE__ */ new Date()).toISOString();
35155
35241
  const participantIds = participantIdsForLookup(ownerId, ownerType);
@@ -35157,7 +35243,8 @@ async function markAllMessagesAsRead(handle, ownerId, ownerType) {
35157
35243
  and(
35158
35244
  inArray(project_exports.messages.toId, participantIds),
35159
35245
  eq(project_exports.messages.toType, ownerType),
35160
- eq(project_exports.messages.read, 0)
35246
+ eq(project_exports.messages.read, 0),
35247
+ archivedCondition()
35161
35248
  )
35162
35249
  );
35163
35250
  const count2 = countRows[0]?.count ?? 0;
@@ -35165,7 +35252,8 @@ async function markAllMessagesAsRead(handle, ownerId, ownerType) {
35165
35252
  and(
35166
35253
  inArray(project_exports.messages.toId, participantIds),
35167
35254
  eq(project_exports.messages.toType, ownerType),
35168
- eq(project_exports.messages.read, 0)
35255
+ eq(project_exports.messages.read, 0),
35256
+ archivedCondition()
35169
35257
  )
35170
35258
  );
35171
35259
  return count2;
@@ -35187,19 +35275,22 @@ async function getConversation(handle, participantA, participantB, options) {
35187
35275
  const aIds = participantIdsForLookup(participantA.id, participantA.type);
35188
35276
  const bIds = participantIdsForLookup(participantB.id, participantB.type);
35189
35277
  const rows = await handle.select(messageColumns).from(project_exports.messages).where(
35190
- or(
35191
- and(
35192
- inArray(project_exports.messages.fromId, aIds),
35193
- eq(project_exports.messages.fromType, participantA.type),
35194
- inArray(project_exports.messages.toId, bIds),
35195
- eq(project_exports.messages.toType, participantB.type)
35278
+ and(
35279
+ or(
35280
+ and(
35281
+ inArray(project_exports.messages.fromId, aIds),
35282
+ eq(project_exports.messages.fromType, participantA.type),
35283
+ inArray(project_exports.messages.toId, bIds),
35284
+ eq(project_exports.messages.toType, participantB.type)
35285
+ ),
35286
+ and(
35287
+ inArray(project_exports.messages.fromId, bIds),
35288
+ eq(project_exports.messages.fromType, participantB.type),
35289
+ inArray(project_exports.messages.toId, aIds),
35290
+ eq(project_exports.messages.toType, participantA.type)
35291
+ )
35196
35292
  ),
35197
- and(
35198
- inArray(project_exports.messages.fromId, bIds),
35199
- eq(project_exports.messages.fromType, participantB.type),
35200
- inArray(project_exports.messages.toId, aIds),
35201
- eq(project_exports.messages.toType, participantA.type)
35202
- )
35293
+ archivedCondition(options?.archived)
35203
35294
  )
35204
35295
  ).orderBy(desc(project_exports.messages.createdAt)).limit(limit2);
35205
35296
  return rows.reverse().map((row) => rowToMessage(row));
@@ -35210,14 +35301,16 @@ async function getMailbox(handle, ownerId, ownerType) {
35210
35301
  and(
35211
35302
  inArray(project_exports.messages.toId, participantIds),
35212
35303
  eq(project_exports.messages.toType, ownerType),
35213
- eq(project_exports.messages.read, 0)
35304
+ eq(project_exports.messages.read, 0),
35305
+ archivedCondition()
35214
35306
  )
35215
35307
  );
35216
35308
  const unreadCount = unreadRows[0]?.count ?? 0;
35217
35309
  const lastRows = await handle.select(messageColumns).from(project_exports.messages).where(
35218
35310
  and(
35219
35311
  inArray(project_exports.messages.toId, participantIds),
35220
- eq(project_exports.messages.toType, ownerType)
35312
+ eq(project_exports.messages.toType, ownerType),
35313
+ archivedCondition()
35221
35314
  )
35222
35315
  ).orderBy(desc(project_exports.messages.createdAt), desc(project_exports.messages.id)).limit(1);
35223
35316
  return {
@@ -35227,15 +35320,16 @@ async function getMailbox(handle, ownerId, ownerType) {
35227
35320
  lastMessage: lastRows[0] ? rowToMessage(lastRows[0]) : void 0
35228
35321
  };
35229
35322
  }
35230
- async function getAllAgentToAgentMessages(handle) {
35231
- 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));
35323
+ async function getAllAgentToAgentMessages(handle, filter2) {
35324
+ const rows = await handle.select(messageColumns).from(project_exports.messages).where(and(eq(project_exports.messages.type, "agent-to-agent"), archivedCondition(filter2?.archived))).orderBy(desc(project_exports.messages.createdAt), desc(project_exports.messages.id));
35232
35325
  return rows.map((row) => rowToMessage(row));
35233
35326
  }
35234
35327
  async function getUnreadAgentToAgentCount(handle) {
35235
35328
  const rows = await handle.select({ count: sql`count(*)::int` }).from(project_exports.messages).where(
35236
35329
  and(
35237
35330
  eq(project_exports.messages.type, "agent-to-agent"),
35238
- eq(project_exports.messages.read, 0)
35331
+ eq(project_exports.messages.read, 0),
35332
+ archivedCondition()
35239
35333
  )
35240
35334
  );
35241
35335
  return rows[0]?.count ?? 0;
@@ -35257,6 +35351,7 @@ var init_async_message_store = __esm({
35257
35351
  content: project_exports.messages.content,
35258
35352
  type: project_exports.messages.type,
35259
35353
  read: project_exports.messages.read,
35354
+ archived: project_exports.messages.archived,
35260
35355
  metadata: project_exports.messages.metadata,
35261
35356
  createdAt: project_exports.messages.createdAt,
35262
35357
  updatedAt: project_exports.messages.updatedAt
@@ -35442,12 +35537,12 @@ var init_message_store = __esm({
35442
35537
  }
35443
35538
  const sqliteDb = this.db;
35444
35539
  this.stmtInsert = sqliteDb.prepare(`
35445
- INSERT INTO messages (id, fromId, fromType, toId, toType, content, type, read, metadata, createdAt, updatedAt)
35446
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
35540
+ INSERT INTO messages (id, fromId, fromType, toId, toType, content, type, read, archived, metadata, createdAt, updatedAt)
35541
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
35447
35542
  `);
35448
35543
  this.stmtInsertOnce = sqliteDb.prepare(`
35449
- INSERT OR IGNORE INTO messages (id, fromId, fromType, toId, toType, content, type, read, metadata, createdAt, updatedAt)
35450
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
35544
+ INSERT OR IGNORE INTO messages (id, fromId, fromType, toId, toType, content, type, read, archived, metadata, createdAt, updatedAt)
35545
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
35451
35546
  `);
35452
35547
  this.stmtGetById = sqliteDb.prepare(`
35453
35548
  SELECT * FROM messages WHERE id = ?
@@ -35485,6 +35580,7 @@ var init_message_store = __esm({
35485
35580
  content: row.content,
35486
35581
  type: row.type,
35487
35582
  read: row.read === 1,
35583
+ archived: (row.archived ?? 0) === 1,
35488
35584
  metadata: fromJson(row.metadata),
35489
35585
  createdAt: row.createdAt,
35490
35586
  updatedAt: row.updatedAt
@@ -35518,6 +35614,7 @@ var init_message_store = __esm({
35518
35614
  content: sanitizeTextValue(input.content),
35519
35615
  type: input.type,
35520
35616
  read: false,
35617
+ archived: false,
35521
35618
  metadata: sanitizeJsonbValue(input.metadata),
35522
35619
  createdAt: now,
35523
35620
  updatedAt: now
@@ -35533,6 +35630,7 @@ var init_message_store = __esm({
35533
35630
  content: message.content,
35534
35631
  type: message.type,
35535
35632
  read: message.read,
35633
+ archived: message.archived,
35536
35634
  metadata: message.metadata ?? null,
35537
35635
  createdAt: message.createdAt,
35538
35636
  updatedAt: message.updatedAt
@@ -35547,6 +35645,7 @@ var init_message_store = __esm({
35547
35645
  message.content,
35548
35646
  message.type,
35549
35647
  message.read ? 1 : 0,
35648
+ message.archived ? 1 : 0,
35550
35649
  toJsonNullable(message.metadata),
35551
35650
  message.createdAt,
35552
35651
  message.updatedAt
@@ -35605,6 +35704,7 @@ var init_message_store = __esm({
35605
35704
  content: sanitizeTextValue(input.content),
35606
35705
  type: input.type,
35607
35706
  read: false,
35707
+ archived: false,
35608
35708
  metadata: sanitizeJsonbValue(input.metadata),
35609
35709
  createdAt: now,
35610
35710
  updatedAt: now
@@ -35625,6 +35725,7 @@ var init_message_store = __esm({
35625
35725
  message.content,
35626
35726
  message.type,
35627
35727
  0,
35728
+ 0,
35628
35729
  toJsonNullable(message.metadata),
35629
35730
  message.createdAt,
35630
35731
  message.updatedAt
@@ -35756,6 +35857,7 @@ var init_message_store = __esm({
35756
35857
  whereClauses.push("read = ?");
35757
35858
  params.push(filter2.read ? 1 : 0);
35758
35859
  }
35860
+ whereClauses.push(filter2?.archived === true ? "archived = 1" : "(archived = 0 OR archived IS NULL)");
35759
35861
  const whereSql = whereClauses.join(" AND ");
35760
35862
  const limit2 = filter2?.limit ?? 100;
35761
35863
  const offset = filter2?.offset ?? 0;
@@ -35806,15 +35908,39 @@ var init_message_store = __esm({
35806
35908
  const participantIds = this.getParticipantIdsForLookup(ownerId, ownerType);
35807
35909
  const toIdPredicate = participantIds.length === 1 ? "toId = ?" : `toId IN (${participantIds.map(() => "?").join(", ")})`;
35808
35910
  const unreadRow = this.db.prepare(`
35809
- SELECT COUNT(*) as count FROM messages WHERE ${toIdPredicate} AND toType = ? AND read = 0
35911
+ SELECT COUNT(*) as count FROM messages WHERE ${toIdPredicate} AND toType = ? AND read = 0 AND (archived = 0 OR archived IS NULL)
35810
35912
  `).get(...participantIds, ownerType);
35811
35913
  const count2 = unreadRow?.count ?? 0;
35812
35914
  this.db.prepare(`
35813
- UPDATE messages SET read = 1, updatedAt = ? WHERE ${toIdPredicate} AND toType = ? AND read = 0
35915
+ UPDATE messages SET read = 1, updatedAt = ? WHERE ${toIdPredicate} AND toType = ? AND read = 0 AND (archived = 0 OR archived IS NULL)
35814
35916
  `).run(now, ...participantIds, ownerType);
35815
35917
  this.db.bumpLastModified();
35816
35918
  return count2;
35817
35919
  }
35920
+ /** Archive a message without destroying its correspondence history. */
35921
+ async archiveMessage(id) {
35922
+ return this.setMessageArchived(id, true);
35923
+ }
35924
+ /** Restore a previously archived message to default mailbox lists. */
35925
+ async unarchiveMessage(id) {
35926
+ return this.setMessageArchived(id, false);
35927
+ }
35928
+ async setMessageArchived(id, archived) {
35929
+ if (this.asyncLayer) {
35930
+ const updated2 = await setMessageArchived(this.asyncLayer.db, id, archived);
35931
+ if (!updated2) throw new Error(`Message ${id} not found`);
35932
+ this.emit(archived ? "message:archived" : "message:unarchived", updated2);
35933
+ return updated2;
35934
+ }
35935
+ const existing = await this.getMessage(id);
35936
+ if (!existing) throw new Error(`Message ${id} not found`);
35937
+ if (existing.archived === archived) return existing;
35938
+ this.db.prepare("UPDATE messages SET archived = ?, updatedAt = ? WHERE id = ?").run(archived ? 1 : 0, (/* @__PURE__ */ new Date()).toISOString(), id);
35939
+ this.db.bumpLastModified();
35940
+ const updated = await this.getMessage(id);
35941
+ this.emit(archived ? "message:archived" : "message:unarchived", updated);
35942
+ return updated;
35943
+ }
35818
35944
  /**
35819
35945
  * Delete a message by ID.
35820
35946
  * @param id - The message ID
@@ -35905,7 +36031,7 @@ var init_message_store = __esm({
35905
36031
  (${participantAFromPredicate} AND fromType = ? AND ${participantBToPredicate} AND toType = ?)
35906
36032
  OR
35907
36033
  (${participantBFromPredicate} AND fromType = ? AND ${participantAToPredicate} AND toType = ?)
35908
- )
36034
+ ) AND ${options?.archived === true ? "archived = 1" : "(archived = 0 OR archived IS NULL)"}
35909
36035
  ORDER BY createdAt DESC
35910
36036
  LIMIT ?
35911
36037
  `).all(
@@ -35940,11 +36066,11 @@ var init_message_store = __esm({
35940
36066
  const participantIds = this.getParticipantIdsForLookup(ownerId, ownerType);
35941
36067
  const toIdPredicate = participantIds.length === 1 ? "toId = ?" : `toId IN (${participantIds.map(() => "?").join(", ")})`;
35942
36068
  const unreadRow = this.db.prepare(`
35943
- SELECT COUNT(*) as count FROM messages WHERE ${toIdPredicate} AND toType = ? AND read = 0
36069
+ SELECT COUNT(*) as count FROM messages WHERE ${toIdPredicate} AND toType = ? AND read = 0 AND (archived = 0 OR archived IS NULL)
35944
36070
  `).get(...participantIds, ownerType);
35945
36071
  const unreadCount = unreadRow?.count ?? 0;
35946
36072
  const lastRow = this.db.prepare(`
35947
- SELECT * FROM messages WHERE ${toIdPredicate} AND toType = ? ORDER BY createdAt DESC, rowid DESC LIMIT 1
36073
+ SELECT * FROM messages WHERE ${toIdPredicate} AND toType = ? AND (archived = 0 OR archived IS NULL) ORDER BY createdAt DESC, rowid DESC LIMIT 1
35948
36074
  `).get(...participantIds, ownerType);
35949
36075
  const lastMessage = lastRow ? this.rowToMessage(lastRow) : void 0;
35950
36076
  return {
@@ -35958,13 +36084,13 @@ var init_message_store = __esm({
35958
36084
  * Get all agent-to-agent messages across all agents.
35959
36085
  * @returns Array of messages (newest first)
35960
36086
  */
35961
- async getAllAgentToAgentMessages() {
36087
+ async getAllAgentToAgentMessages(filter2) {
35962
36088
  if (this.asyncLayer) {
35963
- return getAllAgentToAgentMessages(this.asyncLayer.db);
36089
+ return getAllAgentToAgentMessages(this.asyncLayer.db, filter2);
35964
36090
  }
35965
36091
  const rows = this.db.prepare(`
35966
36092
  SELECT * FROM messages
35967
- WHERE type = ?
36093
+ WHERE type = ? AND ${filter2?.archived === true ? "archived = 1" : "(archived = 0 OR archived IS NULL)"}
35968
36094
  ORDER BY createdAt DESC, rowid DESC
35969
36095
  `).all("agent-to-agent");
35970
36096
  return rows.map((row) => this.rowToMessage(row));
@@ -35978,7 +36104,7 @@ var init_message_store = __esm({
35978
36104
  }
35979
36105
  const row = this.db.prepare(`
35980
36106
  SELECT COUNT(*) as count FROM messages
35981
- WHERE type = ? AND read = 0
36107
+ WHERE type = ? AND read = 0 AND (archived = 0 OR archived IS NULL)
35982
36108
  `).get("agent-to-agent");
35983
36109
  return row?.count ?? 0;
35984
36110
  }
@@ -36077,6 +36203,7 @@ async function createApprovalRequest(layer, input) {
36077
36203
  };
36078
36204
  await layer.transactionImmediate(async (tx) => {
36079
36205
  await tx.insert(project_exports.approvalRequests).values({
36206
+ ...layer.projectId?.trim() ? { projectId: layer.projectId } : {},
36080
36207
  id: request3.id,
36081
36208
  status: request3.status,
36082
36209
  requesterActorId: request3.requester.actorId,
@@ -36100,12 +36227,12 @@ async function createApprovalRequest(layer, input) {
36100
36227
  });
36101
36228
  return request3;
36102
36229
  }
36103
- async function getApprovalRequest(handle, id) {
36104
- const rows = await handle.select().from(project_exports.approvalRequests).where(eq(project_exports.approvalRequests.id, id));
36230
+ async function getApprovalRequest(handle, id, projectId2) {
36231
+ const rows = await handle.select().from(project_exports.approvalRequests).where(and(eq(project_exports.approvalRequests.id, id), projectScopeFor(project_exports.approvalRequests.projectId, projectId2)));
36105
36232
  return rows[0] ? rowToRequest(rows[0]) : null;
36106
36233
  }
36107
- async function listApprovalRequests(handle, input = {}) {
36108
- const conditions = [];
36234
+ async function listApprovalRequests(handle, input = {}, projectId2) {
36235
+ const conditions = [projectScopeFor(project_exports.approvalRequests.projectId, projectId2)];
36109
36236
  if (input.status) conditions.push(eq(project_exports.approvalRequests.status, input.status));
36110
36237
  if (input.requesterActorId) conditions.push(eq(project_exports.approvalRequests.requesterActorId, input.requesterActorId));
36111
36238
  if (input.taskId) conditions.push(eq(project_exports.approvalRequests.taskId, input.taskId));
@@ -36119,7 +36246,7 @@ async function listApprovalRequests(handle, input = {}) {
36119
36246
  async function decideApprovalRequest(layer, requestId, status, input) {
36120
36247
  const now = (/* @__PURE__ */ new Date()).toISOString();
36121
36248
  return layer.transactionImmediate(async (tx) => {
36122
- const existing = await getApprovalRequest(tx, requestId);
36249
+ const existing = await getApprovalRequest(tx, requestId, layer.projectId);
36123
36250
  if (!existing) throw new Error(`Approval request ${requestId} not found`);
36124
36251
  if (!isValidApprovalRequestTransition(existing.status, status)) {
36125
36252
  throw new Error(`Invalid approval request transition: ${existing.status} -> ${status}`);
@@ -36130,23 +36257,24 @@ async function decideApprovalRequest(layer, requestId, status, input) {
36130
36257
  const updatedRows = await tx.update(project_exports.approvalRequests).set({ status, decidedAt: now, updatedAt: now }).where(
36131
36258
  and(
36132
36259
  eq(project_exports.approvalRequests.id, requestId),
36133
- eq(project_exports.approvalRequests.status, existing.status)
36260
+ eq(project_exports.approvalRequests.status, existing.status),
36261
+ projectScopeFor(project_exports.approvalRequests.projectId, layer.projectId)
36134
36262
  )
36135
36263
  ).returning({ id: project_exports.approvalRequests.id });
36136
36264
  if (updatedRows.length === 0) {
36137
- const raced = await getApprovalRequest(tx, requestId);
36265
+ const raced = await getApprovalRequest(tx, requestId, layer.projectId);
36138
36266
  throw new Error(
36139
36267
  `Invalid approval request transition: ${raced?.status ?? existing.status} -> ${status}`
36140
36268
  );
36141
36269
  }
36142
36270
  await appendAuditEvent(tx, layer.projectId ?? "", requestId, status, input.actor, now, input.note);
36143
- return await getApprovalRequest(tx, requestId);
36271
+ return await getApprovalRequest(tx, requestId, layer.projectId);
36144
36272
  });
36145
36273
  }
36146
36274
  async function markApprovalRequestCompleted(layer, requestId, input) {
36147
36275
  const now = (/* @__PURE__ */ new Date()).toISOString();
36148
36276
  return layer.transactionImmediate(async (tx) => {
36149
- const existing = await getApprovalRequest(tx, requestId);
36277
+ const existing = await getApprovalRequest(tx, requestId, layer.projectId);
36150
36278
  if (!existing) throw new Error(`Approval request ${requestId} not found`);
36151
36279
  if (!isValidApprovalRequestTransition(existing.status, "completed")) {
36152
36280
  throw new Error(`Invalid approval request transition: ${existing.status} -> completed`);
@@ -36160,21 +36288,22 @@ async function markApprovalRequestCompleted(layer, requestId, input) {
36160
36288
  const updatedRows = await tx.update(project_exports.approvalRequests).set({ status: "completed", completedAt: now, updatedAt: now }).where(
36161
36289
  and(
36162
36290
  eq(project_exports.approvalRequests.id, requestId),
36163
- eq(project_exports.approvalRequests.status, existing.status)
36291
+ eq(project_exports.approvalRequests.status, existing.status),
36292
+ projectScopeFor(project_exports.approvalRequests.projectId, layer.projectId)
36164
36293
  )
36165
36294
  ).returning({ id: project_exports.approvalRequests.id });
36166
36295
  if (updatedRows.length === 0) {
36167
- const raced = await getApprovalRequest(tx, requestId);
36296
+ const raced = await getApprovalRequest(tx, requestId, layer.projectId);
36168
36297
  throw new Error(
36169
36298
  `Invalid approval request transition: ${raced?.status ?? existing.status} -> completed`
36170
36299
  );
36171
36300
  }
36172
36301
  await appendAuditEvent(tx, layer.projectId ?? "", requestId, "completed", input.actor, now, input.note);
36173
- return await getApprovalRequest(tx, requestId);
36302
+ return await getApprovalRequest(tx, requestId, layer.projectId);
36174
36303
  });
36175
36304
  }
36176
- async function getApprovalAuditHistory(handle, requestId) {
36177
- const rows = await handle.select().from(project_exports.approvalRequestAuditEvents).where(eq(project_exports.approvalRequestAuditEvents.requestId, requestId)).orderBy(
36305
+ async function getApprovalAuditHistory(handle, requestId, projectId2) {
36306
+ const rows = await handle.select().from(project_exports.approvalRequestAuditEvents).where(and(eq(project_exports.approvalRequestAuditEvents.requestId, requestId), projectScopeFor(project_exports.approvalRequestAuditEvents.projectId, projectId2))).orderBy(
36178
36307
  sql`${project_exports.approvalRequestAuditEvents.createdAt} ASC, ${project_exports.approvalRequestAuditEvents.id} ASC`
36179
36308
  );
36180
36309
  return rows.map((row) => rowToAuditEvent(row));
@@ -36184,6 +36313,7 @@ var init_async_approval_request_store = __esm({
36184
36313
  "use strict";
36185
36314
  init_drizzle_orm();
36186
36315
  init_schema2();
36316
+ init_data_layer();
36187
36317
  init_agents();
36188
36318
  init_types();
36189
36319
  }
@@ -36197,6 +36327,7 @@ var init_approval_request_store = __esm({
36197
36327
  "use strict";
36198
36328
  init_drizzle_orm();
36199
36329
  init_db2();
36330
+ init_data_layer();
36200
36331
  init_async_approval_request_store();
36201
36332
  init_schema2();
36202
36333
  init_async_agent_activity();
@@ -36325,23 +36456,26 @@ var init_approval_request_store = __esm({
36325
36456
  return created;
36326
36457
  }
36327
36458
  async get(id) {
36328
- return getApprovalRequest(this.asyncLayer.db, id);
36459
+ return getApprovalRequest(this.asyncLayer.db, id, this.asyncLayer.projectId);
36329
36460
  }
36330
36461
  async list(input = {}) {
36331
- return listApprovalRequests(this.asyncLayer.db, input);
36462
+ return listApprovalRequests(this.asyncLayer.db, input, this.asyncLayer.projectId);
36332
36463
  }
36333
36464
  async getPendingCountsByActor() {
36334
36465
  const table = project_exports.approvalRequests;
36335
36466
  const rows = await this.asyncLayer.db.select({
36336
36467
  actorId: table.requesterActorId,
36337
36468
  requestCount: count()
36338
- }).from(table).where(eq(table.status, "pending")).groupBy(table.requesterActorId);
36469
+ }).from(table).where(and(eq(table.status, "pending"), projectScopeFor(table.projectId, this.asyncLayer.projectId))).groupBy(table.requesterActorId);
36339
36470
  return new Map(rows.map((row) => [row.actorId, Number(row.requestCount)]));
36340
36471
  }
36341
36472
  async findLatestByDedupeKey(input) {
36342
36473
  if (this.backendMode) {
36343
36474
  const table = project_exports.approvalRequests;
36344
- const conditions = [eq(table.requesterActorId, input.requesterActorId)];
36475
+ const conditions = [
36476
+ eq(table.requesterActorId, input.requesterActorId),
36477
+ projectScopeFor(table.projectId, this.asyncLayer.projectId)
36478
+ ];
36345
36479
  if (input.taskId !== void 0) {
36346
36480
  conditions.push(eq(table.taskId, input.taskId));
36347
36481
  }
@@ -36380,7 +36514,7 @@ var init_approval_request_store = __esm({
36380
36514
  return markApprovalRequestCompleted(this.asyncLayer, requestId, input);
36381
36515
  }
36382
36516
  async getAuditHistory(requestId) {
36383
- return getApprovalAuditHistory(this.asyncLayer.db, requestId);
36517
+ return getApprovalAuditHistory(this.asyncLayer.db, requestId, this.asyncLayer.projectId);
36384
36518
  }
36385
36519
  };
36386
36520
  }
@@ -40566,6 +40700,8 @@ async function applySchemaBaseline(db, options = {}) {
40566
40700
  const agentRatingProjectIsolationAlreadyApplied = applied.includes(AGENT_RATING_PROJECT_ISOLATION_VERSION);
40567
40701
  const agentRatingsProjectPartitionAlreadyApplied = applied.includes(AGENT_RATINGS_PROJECT_PARTITION_VERSION);
40568
40702
  const projectOwnershipDeclarationDriftAlreadyApplied = applied.includes(PROJECT_OWNERSHIP_DECLARATION_DRIFT_VERSION);
40703
+ const projectOwnershipDefaultReconciliationAlreadyApplied = applied.includes(PROJECT_OWNERSHIP_DEFAULT_RECONCILIATION_VERSION);
40704
+ const messageArchiveSchemaAlreadyApplied = applied.includes(MESSAGE_ARCHIVE_SCHEMA_VERSION);
40569
40705
  assertBinaryNotOlderThanDatabase(applied);
40570
40706
  let schemaChanged = false;
40571
40707
  if (!baselineAlreadyApplied) {
@@ -41049,17 +41185,29 @@ async function applySchemaBaseline(db, options = {}) {
41049
41185
  await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${PROJECT_OWNERSHIP_DECLARATION_DRIFT_VERSION}) ON CONFLICT (version) DO NOTHING`);
41050
41186
  schemaChanged = true;
41051
41187
  }
41188
+ if (!projectOwnershipDefaultReconciliationAlreadyApplied) {
41189
+ const migrationSql = await readFile4(PROJECT_OWNERSHIP_DEFAULT_RECONCILIATION_MIGRATION_PATH, "utf8");
41190
+ await tx.execute(sql.raw(migrationSql));
41191
+ await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${PROJECT_OWNERSHIP_DEFAULT_RECONCILIATION_VERSION}) ON CONFLICT (version) DO NOTHING`);
41192
+ schemaChanged = true;
41193
+ }
41194
+ if (!messageArchiveSchemaAlreadyApplied) {
41195
+ const migrationSql = await readFile4(MESSAGE_ARCHIVE_SCHEMA_MIGRATION_PATH, "utf8");
41196
+ await tx.execute(sql.raw(migrationSql));
41197
+ await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${MESSAGE_ARCHIVE_SCHEMA_VERSION}) ON CONFLICT (version) DO NOTHING`);
41198
+ schemaChanged = true;
41199
+ }
41052
41200
  return { applied: schemaChanged, pluginHooksRun: pluginHooks.length };
41053
41201
  });
41054
41202
  }
41055
- 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;
41203
+ 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;
41056
41204
  var init_schema_applier = __esm({
41057
41205
  "../core/src/postgres/schema-applier.ts"() {
41058
41206
  "use strict";
41059
41207
  init_drizzle_orm();
41060
41208
  init_plugin_schema_hook();
41061
41209
  init_advisory_locks();
41062
- SCHEMA_BASELINE_VERSION = "0056";
41210
+ SCHEMA_BASELINE_VERSION = "0058";
41063
41211
  TASK_DECLARED_SYMBOLS_VERSION = "0028";
41064
41212
  INITIAL_SCHEMA_VERSION = "0000";
41065
41213
  AUTOMATION_ISOLATION_SCHEMA_VERSION = "0001";
@@ -41116,6 +41264,8 @@ var init_schema_applier = __esm({
41116
41264
  AGENT_RATING_PROJECT_ISOLATION_VERSION = "0054";
41117
41265
  AGENT_RATINGS_PROJECT_PARTITION_VERSION = "0055";
41118
41266
  PROJECT_OWNERSHIP_DECLARATION_DRIFT_VERSION = "0056";
41267
+ PROJECT_OWNERSHIP_DEFAULT_RECONCILIATION_VERSION = "0057";
41268
+ MESSAGE_ARCHIVE_SCHEMA_VERSION = "0058";
41119
41269
  LEGACY_ADOPTION_DRAINED_MARKER_FUNCTION = "fusion_mark_legacy_adoption_drained";
41120
41270
  StaleBinarySchemaError = class extends Error {
41121
41271
  constructor(databaseVersion, binaryVersion) {
@@ -41252,6 +41402,8 @@ var init_schema_applier = __esm({
41252
41402
  AGENT_RATING_PROJECT_ISOLATION_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0054_fn_8957_agent_rating_project_isolation.sql");
41253
41403
  AGENT_RATINGS_PROJECT_PARTITION_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0055_fn_8988_agent_ratings_project_partition.sql");
41254
41404
  PROJECT_OWNERSHIP_DECLARATION_DRIFT_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0056_fn_8997_project_ownership_declaration_drift.sql");
41405
+ PROJECT_OWNERSHIP_DEFAULT_RECONCILIATION_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0057_fn_9004_project_ownership_default_reconciliation.sql");
41406
+ MESSAGE_ARCHIVE_SCHEMA_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0058_fn_9014_message_archive.sql");
41255
41407
  }
41256
41408
  });
41257
41409
 
@@ -68607,20 +68759,32 @@ async function replaceActiveTaskWorkflowContinuation(layer, input) {
68607
68759
  return upsertWorkflowWorkItem(layer, input, tx);
68608
68760
  }));
68609
68761
  }
68610
- async function seedStrandedPlanReviewContinuation(layer, input) {
68762
+ async function seedStrandedPlanReviewContinuation(layer, input, options = {}) {
68611
68763
  return layer.transactionImmediate(async (tx) => withTaskWorkflowSerialization(tx, layer.projectId, input.taskId, async () => {
68612
68764
  const active2 = await tx.select({ id: project_exports.workflowWorkItems.id }).from(project_exports.workflowWorkItems).where(and(
68613
68765
  projectScopeFor(project_exports.workflowWorkItems.projectId, layer.projectId),
68614
68766
  eq(project_exports.workflowWorkItems.taskId, input.taskId),
68615
68767
  inArray(project_exports.workflowWorkItems.state, [...ACTIVE_WORKFLOW_WORK_ITEM_STATES])
68616
68768
  ));
68617
- if (active2.length > 0) return { seeded: false, reason: "active-continuation" };
68769
+ const blocking = options.retirePredecessorId ? active2.filter((row) => row.id !== options.retirePredecessorId) : active2;
68770
+ if (blocking.length > 0) return { seeded: false, reason: "active-continuation" };
68618
68771
  const taskRows = await tx.select({ workflowStepResults: project_exports.tasks.workflowStepResults }).from(project_exports.tasks).where(and(
68619
68772
  projectScopeFor(project_exports.tasks.projectId, layer.projectId),
68620
68773
  eq(project_exports.tasks.id, input.taskId)
68621
68774
  )).limit(1);
68622
68775
  const results = taskRows[0]?.workflowStepResults;
68623
68776
  if (results?.some(isPlanReviewSatisfied)) return { seeded: false, reason: "plan-review-passed" };
68777
+ if (options.retirePredecessorId) {
68778
+ const predRows = await tx.select().from(project_exports.workflowWorkItems).where(and(
68779
+ projectScopeFor(project_exports.workflowWorkItems.projectId, layer.projectId),
68780
+ eq(project_exports.workflowWorkItems.id, options.retirePredecessorId),
68781
+ eq(project_exports.workflowWorkItems.taskId, input.taskId)
68782
+ )).limit(1);
68783
+ const pred = predRows[0];
68784
+ if (pred && !isTerminalWorkflowWorkItemState(normalizeWorkflowWorkItemState(pred.state))) {
68785
+ await transitionWorkflowWorkItem(layer, pred.id, "succeeded", { leaseOwner: null, leaseExpiresAt: null, lastError: null }, tx);
68786
+ }
68787
+ }
68624
68788
  const item = await upsertWorkflowWorkItem(layer, input, tx);
68625
68789
  return { seeded: true, workItemId: item.id };
68626
68790
  }));
@@ -68784,8 +68948,10 @@ function safeJsonParse(value2) {
68784
68948
  return null;
68785
68949
  }
68786
68950
  }
68787
- async function queryRunAuditEvents(db, filter2 = {}) {
68788
- const conditions = [];
68951
+ async function queryRunAuditEvents(db, filter2 = {}, projectId2) {
68952
+ const conditions = [projectScopeFor(project_exports.runAuditEvents.projectId, projectId2)].filter(
68953
+ (condition) => condition !== void 0
68954
+ );
68789
68955
  if (filter2.runId) {
68790
68956
  conditions.push(eq(project_exports.runAuditEvents.runId, filter2.runId));
68791
68957
  }
@@ -68931,6 +69097,7 @@ var init_async_audit = __esm({
68931
69097
  init_drizzle_orm();
68932
69098
  init_schema2();
68933
69099
  init_data_layer();
69100
+ init_data_layer();
68934
69101
  LEGACY_REVIEW_LANES3 = ["in-review"];
68935
69102
  LEGACY_COMPLETE_LANES = ["done"];
68936
69103
  }
@@ -69320,7 +69487,10 @@ async function insertArtifactRow(layer, input, stored, archivedColumns) {
69320
69487
  createdAt: now,
69321
69488
  updatedAt: now
69322
69489
  });
69323
- const rows = await tx.select().from(project_exports.artifacts).where(eq(project_exports.artifacts.id, id)).limit(1);
69490
+ const rows = await tx.select().from(project_exports.artifacts).where(and(
69491
+ eq(project_exports.artifacts.id, id),
69492
+ projectScopeFor(project_exports.artifacts.projectId, layer.projectId)
69493
+ )).limit(1);
69324
69494
  const row = rows[0];
69325
69495
  if (!row) throw new Error(`Failed to register artifact ${id}`);
69326
69496
  return rowToArtifact2(row);
@@ -69328,7 +69498,7 @@ async function insertArtifactRow(layer, input, stored, archivedColumns) {
69328
69498
  }
69329
69499
  async function updateArtifactRow(layer, id, updates, archivedColumns) {
69330
69500
  return layer.transactionImmediate(async (tx) => {
69331
- const existing = await getArtifact(tx, id);
69501
+ const existing = await getArtifact(tx, id, layer.projectId);
69332
69502
  if (!existing) {
69333
69503
  throw new Error(`Artifact ${id} not found`);
69334
69504
  }
@@ -69348,29 +69518,38 @@ async function updateArtifactRow(layer, id, updates, archivedColumns) {
69348
69518
  description: updates.description !== void 0 ? updates.description : existing.description ?? null,
69349
69519
  content: updates.content !== void 0 ? updates.content : existing.content ?? null,
69350
69520
  updatedAt: now
69351
- }).where(eq(project_exports.artifacts.id, id));
69352
- const updated = await getArtifact(tx, id);
69521
+ }).where(and(
69522
+ eq(project_exports.artifacts.id, id),
69523
+ projectScopeFor(project_exports.artifacts.projectId, layer.projectId)
69524
+ ));
69525
+ const updated = await getArtifact(tx, id, layer.projectId);
69353
69526
  if (!updated) {
69354
69527
  throw new Error(`Failed to update artifact ${id}`);
69355
69528
  }
69356
69529
  return updated;
69357
69530
  });
69358
69531
  }
69359
- async function getArtifact(db, id) {
69360
- const rows = await db.select().from(project_exports.artifacts).where(eq(project_exports.artifacts.id, id)).limit(1);
69532
+ async function getArtifact(db, id, projectId2) {
69533
+ const rows = await db.select().from(project_exports.artifacts).where(and(
69534
+ eq(project_exports.artifacts.id, id),
69535
+ projectScopeFor(project_exports.artifacts.projectId, projectId2)
69536
+ )).limit(1);
69361
69537
  const row = rows[0];
69362
69538
  return row ? rowToArtifact2(row) : null;
69363
69539
  }
69364
69540
  async function getArtifacts(db, taskId, projectId2, archivedColumns) {
69365
69541
  const column = await getLiveTaskColumn(db, taskId, projectId2, archivedColumns);
69366
69542
  if (column === null || column === "archived") return [];
69367
- const rows = await db.select().from(project_exports.artifacts).where(eq(project_exports.artifacts.taskId, taskId)).orderBy(desc(project_exports.artifacts.createdAt));
69543
+ const rows = await db.select().from(project_exports.artifacts).where(and(
69544
+ eq(project_exports.artifacts.taskId, taskId),
69545
+ projectScopeFor(project_exports.artifacts.projectId, projectId2)
69546
+ )).orderBy(desc(project_exports.artifacts.createdAt));
69368
69547
  return rows.map((row) => rowToArtifact2(row));
69369
69548
  }
69370
- async function listArtifacts(db, options) {
69549
+ async function listArtifacts(db, options, projectId2) {
69371
69550
  const limit2 = Math.min(Math.max(1, options?.limit ?? 200), 1e3);
69372
69551
  const offset = Math.max(0, options?.offset ?? 0);
69373
- const conditions = [];
69552
+ const conditions = [projectScopeFor(project_exports.artifacts.projectId, projectId2)];
69374
69553
  if (options?.type) {
69375
69554
  conditions.push(eq(project_exports.artifacts.type, options.type));
69376
69555
  }
@@ -69409,7 +69588,10 @@ async function listArtifacts(db, options) {
69409
69588
  taskColumn: project_exports.tasks.column
69410
69589
  }).from(project_exports.artifacts).leftJoin(
69411
69590
  project_exports.tasks,
69412
- eq(project_exports.artifacts.taskId, project_exports.tasks.id)
69591
+ and(
69592
+ eq(project_exports.artifacts.taskId, project_exports.tasks.id),
69593
+ eq(project_exports.artifacts.projectId, project_exports.tasks.projectId)
69594
+ )
69413
69595
  ).where(and(...conditions)).orderBy(desc(project_exports.artifacts.createdAt)).limit(limit2).offset(offset);
69414
69596
  return rows.map((row) => {
69415
69597
  const artifact = rowToArtifact2(row);
@@ -70574,7 +70756,7 @@ function rowToBranchGroup2(row) {
70574
70756
  closedAt: row.closedAt ?? void 0
70575
70757
  };
70576
70758
  }
70577
- async function createBranchGroup(db, input) {
70759
+ async function createBranchGroup(db, input, projectId2) {
70578
70760
  validateBranchGroupBranchName(input.branchName);
70579
70761
  const now = Date.now();
70580
70762
  const id = generateBranchGroupId2();
@@ -70593,44 +70775,52 @@ async function createBranchGroup(db, input) {
70593
70775
  updatedAt: now,
70594
70776
  closedAt: input.closedAt ?? null
70595
70777
  });
70596
- const created = await getBranchGroup(db, id);
70778
+ const created = await getBranchGroup(db, id, projectId2);
70597
70779
  if (!created) throw new Error(`Failed to read branch group ${id} after create`);
70598
70780
  return created;
70599
70781
  }
70600
- async function getBranchGroup(db, id) {
70601
- const rows = await db.select().from(project_exports.branchGroups).where(eq(project_exports.branchGroups.id, id)).limit(1);
70782
+ async function getBranchGroup(db, id, projectId2) {
70783
+ const rows = await db.select().from(project_exports.branchGroups).where(and(
70784
+ eq(project_exports.branchGroups.id, id),
70785
+ projectScopeFor(project_exports.branchGroups.projectId, projectId2)
70786
+ )).limit(1);
70602
70787
  const row = rows[0];
70603
70788
  return row ? rowToBranchGroup2(row) : null;
70604
70789
  }
70605
- async function getBranchGroupBySource(db, sourceType, sourceId) {
70790
+ async function getBranchGroupBySource(db, sourceType, sourceId, projectId2) {
70606
70791
  const rows = await db.select().from(project_exports.branchGroups).where(
70607
70792
  and(
70608
70793
  eq(project_exports.branchGroups.sourceType, sourceType),
70609
- eq(project_exports.branchGroups.sourceId, sourceId)
70794
+ eq(project_exports.branchGroups.sourceId, sourceId),
70795
+ projectScopeFor(project_exports.branchGroups.projectId, projectId2)
70610
70796
  )
70611
70797
  ).limit(1);
70612
70798
  const row = rows[0];
70613
70799
  return row ? rowToBranchGroup2(row) : null;
70614
70800
  }
70615
- async function getBranchGroupByBranchName(db, branchName) {
70616
- 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);
70801
+ async function getBranchGroupByBranchName(db, branchName, projectId2) {
70802
+ const rows = await db.select().from(project_exports.branchGroups).where(and(
70803
+ eq(project_exports.branchGroups.branchName, branchName),
70804
+ projectScopeFor(project_exports.branchGroups.projectId, projectId2)
70805
+ )).orderBy(sql`${project_exports.branchGroups.createdAt} DESC`).limit(1);
70617
70806
  const row = rows[0];
70618
70807
  return row ? rowToBranchGroup2(row) : null;
70619
70808
  }
70620
- async function ensureBranchGroupForSource(db, sourceType, sourceId, init) {
70621
- const existing = await getBranchGroupBySource(db, sourceType, sourceId);
70809
+ async function ensureBranchGroupForSource(db, sourceType, sourceId, init, projectId2) {
70810
+ const existing = await getBranchGroupBySource(db, sourceType, sourceId, projectId2);
70622
70811
  if (existing) return existing;
70623
- const existingByBranch = await getBranchGroupByBranchName(db, init.branchName);
70812
+ const existingByBranch = await getBranchGroupByBranchName(db, init.branchName, projectId2);
70624
70813
  if (existingByBranch) return existingByBranch;
70625
- return createBranchGroup(db, { sourceType, sourceId, ...init });
70814
+ return createBranchGroup(db, { sourceType, sourceId, ...init }, projectId2);
70626
70815
  }
70627
- async function listBranchGroups(db, options) {
70628
- const query = db.select().from(project_exports.branchGroups).orderBy(asc(project_exports.branchGroups.createdAt));
70629
- const rows = options?.status ? await query.where(eq(project_exports.branchGroups.status, options.status)) : await query;
70816
+ async function listBranchGroups(db, options, projectId2) {
70817
+ const conditions = [projectScopeFor(project_exports.branchGroups.projectId, projectId2)];
70818
+ if (options?.status) conditions.push(eq(project_exports.branchGroups.status, options.status));
70819
+ const rows = await db.select().from(project_exports.branchGroups).where(and(...conditions)).orderBy(asc(project_exports.branchGroups.createdAt));
70630
70820
  return rows.map((row) => rowToBranchGroup2(row));
70631
70821
  }
70632
- async function updateBranchGroup(db, id, patch) {
70633
- const current = await getBranchGroup(db, id);
70822
+ async function updateBranchGroup(db, id, patch, projectId2) {
70823
+ const current = await getBranchGroup(db, id, projectId2);
70634
70824
  if (!current) throw new Error(`Branch group ${id} not found`);
70635
70825
  if (patch.branchName !== void 0) {
70636
70826
  validateBranchGroupBranchName(patch.branchName);
@@ -70649,8 +70839,11 @@ async function updateBranchGroup(db, id, patch) {
70649
70839
  status: nextStatus,
70650
70840
  updatedAt: now,
70651
70841
  closedAt: nextClosedAt
70652
- }).where(eq(project_exports.branchGroups.id, id));
70653
- const updated = await getBranchGroup(db, id);
70842
+ }).where(and(
70843
+ eq(project_exports.branchGroups.id, id),
70844
+ projectScopeFor(project_exports.branchGroups.projectId, projectId2)
70845
+ ));
70846
+ const updated = await getBranchGroup(db, id, projectId2);
70654
70847
  if (!updated) throw new Error(`Branch group ${id} disappeared after update`);
70655
70848
  return updated;
70656
70849
  }
@@ -70807,6 +71000,7 @@ var init_async_branch_groups = __esm({
70807
71000
  "use strict";
70808
71001
  init_drizzle_orm();
70809
71002
  init_schema2();
71003
+ init_data_layer();
70810
71004
  init_branch_assignment();
70811
71005
  }
70812
71006
  });
@@ -80620,6 +80814,9 @@ import { randomUUID as randomUUID21 } from "node:crypto";
80620
80814
  function tableForScope(scope) {
80621
80815
  return scope === "project" ? project_exports.secrets : central_exports.secretsGlobal;
80622
80816
  }
80817
+ function scopeForSecretTable(scope, table, projectId2) {
80818
+ return scope === "project" ? projectScopeFor(table.projectId, projectId2) : void 0;
80819
+ }
80623
80820
  function isAccessPolicy(value2) {
80624
80821
  return value2 === "auto" || value2 === "prompt" || value2 === "deny";
80625
80822
  }
@@ -80642,23 +80839,23 @@ function rowToRecord2(row, scope) {
80642
80839
  lastReadBy: row.lastReadBy
80643
80840
  };
80644
80841
  }
80645
- async function getSecretMetadata(handle, id, scope) {
80842
+ async function getSecretMetadata(handle, id, scope, projectId2) {
80646
80843
  const table = tableForScope(scope);
80647
- const rows = await handle.select(metadataColumns(table)).from(table).where(eq(table.id, id)).limit(1);
80844
+ const rows = await handle.select(metadataColumns(table)).from(table).where(and(eq(table.id, id), scopeForSecretTable(scope, table, projectId2))).limit(1);
80648
80845
  const row = rows[0];
80649
80846
  return row ? rowToRecord2(row, scope) : null;
80650
80847
  }
80651
- async function listSecrets(handle, scope) {
80848
+ async function listSecrets(handle, scope, projectId2) {
80652
80849
  if (scope) {
80653
80850
  const table = tableForScope(scope);
80654
- const rows = await handle.select(metadataColumns(table)).from(table).orderBy(asc(sql`lower(${table.key})`));
80851
+ const rows = await handle.select(metadataColumns(table)).from(table).where(scopeForSecretTable(scope, table, projectId2)).orderBy(asc(sql`lower(${table.key})`));
80655
80852
  return rows.map((row) => rowToRecord2(row, scope));
80656
80853
  }
80657
- const project = await listSecrets(handle, "project");
80658
- const global2 = await listSecrets(handle, "global");
80854
+ const project = await listSecrets(handle, "project", projectId2);
80855
+ const global2 = await listSecrets(handle, "global", projectId2);
80659
80856
  return [...project, ...global2];
80660
80857
  }
80661
- async function createSecret(handle, cipher, input) {
80858
+ async function createSecret(handle, cipher, input, projectId2) {
80662
80859
  const key = input.key.trim();
80663
80860
  if (!key) {
80664
80861
  throw new SecretsStoreError({ code: "invalid-key", message: "Secret key is required" });
@@ -80672,6 +80869,7 @@ async function createSecret(handle, cipher, input) {
80672
80869
  const table = tableForScope(input.scope);
80673
80870
  try {
80674
80871
  await handle.insert(table).values({
80872
+ ...input.scope === "project" && projectId2?.trim() ? { projectId: projectId2 } : {},
80675
80873
  id,
80676
80874
  key,
80677
80875
  valueCiphertext: encrypted.ciphertext,
@@ -80691,14 +80889,14 @@ async function createSecret(handle, cipher, input) {
80691
80889
  }
80692
80890
  throw error3;
80693
80891
  }
80694
- const created = await getSecretMetadata(handle, id, input.scope);
80892
+ const created = await getSecretMetadata(handle, id, input.scope, projectId2);
80695
80893
  if (!created) {
80696
80894
  throw new SecretsStoreError({ code: "not-found", message: "Secret insert succeeded but row could not be read back" });
80697
80895
  }
80698
80896
  return created;
80699
80897
  }
80700
- async function updateSecret(handle, cipher, id, scope, patch) {
80701
- const existing = await getSecretMetadata(handle, id, scope);
80898
+ async function updateSecret(handle, cipher, id, scope, patch, projectId2) {
80899
+ const existing = await getSecretMetadata(handle, id, scope, projectId2);
80702
80900
  if (!existing) {
80703
80901
  throw new SecretsStoreError({ code: "not-found", message: "Secret not found" });
80704
80902
  }
@@ -80732,34 +80930,34 @@ async function updateSecret(handle, cipher, id, scope, patch) {
80732
80930
  updates.nonce = encrypted.nonce;
80733
80931
  }
80734
80932
  try {
80735
- await handle.update(table).set(updates).where(eq(table.id, id));
80933
+ await handle.update(table).set(updates).where(and(eq(table.id, id), scopeForSecretTable(scope, table, projectId2)));
80736
80934
  } catch (error3) {
80737
80935
  if (isPostgresUniqueError(error3)) {
80738
80936
  throw new SecretsStoreError({ code: "duplicate-key", message: "Secret key already exists" });
80739
80937
  }
80740
80938
  throw error3;
80741
80939
  }
80742
- const updated = await getSecretMetadata(handle, id, scope);
80940
+ const updated = await getSecretMetadata(handle, id, scope, projectId2);
80743
80941
  if (!updated) {
80744
80942
  throw new SecretsStoreError({ code: "not-found", message: "Secret update succeeded but row could not be read back" });
80745
80943
  }
80746
80944
  return updated;
80747
80945
  }
80748
- async function deleteSecret(handle, id, scope) {
80749
- const existing = await getSecretMetadata(handle, id, scope);
80946
+ async function deleteSecret(handle, id, scope, projectId2) {
80947
+ const existing = await getSecretMetadata(handle, id, scope, projectId2);
80750
80948
  if (!existing) {
80751
80949
  throw new SecretsStoreError({ code: "not-found", message: "Secret not found" });
80752
80950
  }
80753
80951
  const table = tableForScope(scope);
80754
- await handle.delete(table).where(eq(table.id, id));
80952
+ await handle.delete(table).where(and(eq(table.id, id), scopeForSecretTable(scope, table, projectId2)));
80755
80953
  }
80756
- async function revealSecret(handle, cipher, id, scope, reader) {
80954
+ async function revealSecret(handle, cipher, id, scope, reader, projectId2) {
80757
80955
  const table = tableForScope(scope);
80758
80956
  const rows = await handle.select({
80759
80957
  ...metadataColumns(table),
80760
80958
  valueCiphertext: table.valueCiphertext,
80761
80959
  nonce: table.nonce
80762
- }).from(table).where(eq(table.id, id)).limit(1);
80960
+ }).from(table).where(and(eq(table.id, id), scopeForSecretTable(scope, table, projectId2))).limit(1);
80763
80961
  const row = rows[0];
80764
80962
  if (!row) {
80765
80963
  throw new SecretsStoreError({ code: "not-found", message: "Secret not found" });
@@ -80778,7 +80976,7 @@ async function revealSecret(handle, cipher, id, scope, reader) {
80778
80976
  }
80779
80977
  const now = (/* @__PURE__ */ new Date()).toISOString();
80780
80978
  const lastReadBy = reader.userId ?? reader.agentId ?? null;
80781
- await handle.update(table).set({ lastReadAt: now, lastReadBy, updatedAt: now }).where(eq(table.id, id));
80979
+ await handle.update(table).set({ lastReadAt: now, lastReadBy, updatedAt: now }).where(and(eq(table.id, id), scopeForSecretTable(scope, table, projectId2)));
80782
80980
  return { key: row.key, plaintextValue };
80783
80981
  }
80784
80982
  var severityAuditLog10, SecretsStoreError, metadataColumns;
@@ -80789,6 +80987,7 @@ var init_async_secrets_store = __esm({
80789
80987
  init_drizzle_orm();
80790
80988
  init_postgres_errors();
80791
80989
  init_schema2();
80990
+ init_data_layer();
80792
80991
  init_secrets_crypto();
80793
80992
  severityAuditLog10 = createLogger("core-async-secrets-store");
80794
80993
  SecretsStoreError = class extends Error {
@@ -80884,7 +81083,7 @@ var init_secrets_store = __esm({
80884
81083
  };
80885
81084
  }
80886
81085
  async listSecrets(scope) {
80887
- return listSecrets(this.asyncLayer.db, scope);
81086
+ return listSecrets(this.asyncLayer.db, scope, this.asyncLayer.projectId);
80888
81087
  }
80889
81088
  async listEnvExportable(opts) {
80890
81089
  const keyPrefix = opts?.keyPrefix;
@@ -80927,7 +81126,7 @@ var init_secrets_store = __esm({
80927
81126
  return [...exported.values()];
80928
81127
  }
80929
81128
  async getSecretMetadata(id, scope) {
80930
- return getSecretMetadata(this.asyncLayer.db, id, scope);
81129
+ return getSecretMetadata(this.asyncLayer.db, id, scope, this.asyncLayer.projectId);
80931
81130
  }
80932
81131
  async createSecret(input) {
80933
81132
  const key = input.key.trim();
@@ -80937,12 +81136,12 @@ var init_secrets_store = __esm({
80937
81136
  if (input.accessPolicy && !isAccessPolicy2(input.accessPolicy)) {
80938
81137
  throw new SecretsStoreError2({ code: "invalid-policy", message: "Invalid access policy" });
80939
81138
  }
80940
- const created = await createSecret(this.asyncLayer.db, this.cipher, input);
81139
+ const created = await createSecret(this.asyncLayer.db, this.cipher, input, this.asyncLayer.projectId);
80941
81140
  this.emitAudit({ mutationType: "secret:create", scope: input.scope, secretId: created.id, key: created.key });
80942
81141
  return created;
80943
81142
  }
80944
81143
  async updateSecret(id, scope, patch) {
80945
- const updated = await updateSecret(this.asyncLayer.db, this.cipher, id, scope, patch);
81144
+ const updated = await updateSecret(this.asyncLayer.db, this.cipher, id, scope, patch, this.asyncLayer.projectId);
80946
81145
  this.emitAudit({ mutationType: "secret:update", scope, secretId: updated.id, key: updated.key });
80947
81146
  return updated;
80948
81147
  }
@@ -80951,12 +81150,12 @@ var init_secrets_store = __esm({
80951
81150
  if (!existing) {
80952
81151
  throw new SecretsStoreError2({ code: "not-found", message: "Secret not found" });
80953
81152
  }
80954
- await deleteSecret(this.asyncLayer.db, id, scope);
81153
+ await deleteSecret(this.asyncLayer.db, id, scope, this.asyncLayer.projectId);
80955
81154
  this.emitAudit({ mutationType: "secret:delete", scope, secretId: id, key: existing.key });
80956
81155
  return;
80957
81156
  }
80958
81157
  async revealSecret(id, scope, reader) {
80959
- const revealed = await revealSecret(this.asyncLayer.db, this.cipher, id, scope, reader);
81158
+ const revealed = await revealSecret(this.asyncLayer.db, this.cipher, id, scope, reader, this.asyncLayer.projectId);
80960
81159
  this.emitAudit({ mutationType: "secret:read", scope, secretId: id, key: revealed.key, actor: reader });
80961
81160
  return revealed;
80962
81161
  }
@@ -82057,6 +82256,7 @@ async function getVerificationCacheHitImpl(store, treeSha, testCommand, buildCom
82057
82256
  const normalizedBuild = buildCommand2 ?? "";
82058
82257
  const table = project_exports.verificationCache;
82059
82258
  const rows = await store.asyncLayer.db.select({ recordedAt: table.recordedAt, taskId: table.taskId }).from(table).where(and(
82259
+ projectScopeFor(table.projectId, store.asyncLayer.projectId),
82060
82260
  eq(table.treeSha, treeSha),
82061
82261
  eq(table.testCommand, normalizedTest),
82062
82262
  eq(table.buildCommand, normalizedBuild)
@@ -82799,7 +82999,7 @@ async function invokeTaskCreatedHookImpl(store, task) {
82799
82999
  }
82800
83000
  async function createBranchGroupImpl(store, input) {
82801
83001
  const layer = store.asyncLayer;
82802
- return createBranchGroup(layer.db, input);
83002
+ return createBranchGroup(layer.db, input, layer.projectId);
82803
83003
  }
82804
83004
  var init_task_id_integrity2 = __esm({
82805
83005
  "../core/src/task-store/task-id-integrity.ts"() {
@@ -82884,22 +83084,23 @@ import { readFile as readFile7, writeFile as writeFile5 } from "node:fs/promises
82884
83084
  import { join as join23 } from "node:path";
82885
83085
  async function getBranchGroupImpl(store, id) {
82886
83086
  const layer = store.asyncLayer;
82887
- return getBranchGroup(layer.db, id);
83087
+ return getBranchGroup(layer.db, id, layer.projectId);
82888
83088
  }
82889
83089
  async function getBranchGroupBySourceImpl(store, sourceType, sourceId) {
82890
83090
  const layer = store.asyncLayer;
82891
- return getBranchGroupBySource(layer.db, sourceType, sourceId);
83091
+ return getBranchGroupBySource(layer.db, sourceType, sourceId, layer.projectId);
82892
83092
  }
82893
83093
  async function getBranchGroupByBranchNameImpl(store, branchName) {
82894
83094
  const layer = store.asyncLayer;
82895
- return getBranchGroupByBranchName(layer.db, branchName);
83095
+ return getBranchGroupByBranchName(layer.db, branchName, layer.projectId);
82896
83096
  }
82897
83097
  async function ensureBranchGroupForSourceImpl(store, sourceType, sourceId, init) {
82898
- return ensureBranchGroupForSource(store.asyncLayer.db, sourceType, sourceId, init);
83098
+ const layer = store.asyncLayer;
83099
+ return ensureBranchGroupForSource(layer.db, sourceType, sourceId, init, layer.projectId);
82899
83100
  }
82900
83101
  async function listBranchGroupsImpl(store, options) {
82901
83102
  const layer = store.asyncLayer;
82902
- return listBranchGroups(layer.db, options);
83103
+ return listBranchGroups(layer.db, options, layer.projectId);
82903
83104
  }
82904
83105
  async function listTasksByBranchGroupImpl(store, groupId) {
82905
83106
  const tasks2 = await store.listTasks({ includeArchived: true, slim: false });
@@ -84441,7 +84642,7 @@ function insertArtifactRowImpl(store, input, id, now, stored) {
84441
84642
  }
84442
84643
  async function getArtifactImpl(store, id) {
84443
84644
  const layer = store.asyncLayer;
84444
- return getArtifact(layer.db, id);
84645
+ return getArtifact(layer.db, id, layer.projectId);
84445
84646
  }
84446
84647
  async function updateArtifactImpl(store, id, updates) {
84447
84648
  const layer = store.asyncLayer;
@@ -89310,6 +89511,7 @@ var init_async_archive_lineage = __esm({
89310
89511
  "use strict";
89311
89512
  init_drizzle_orm();
89312
89513
  init_schema2();
89514
+ init_data_layer();
89313
89515
  init_async_persistence();
89314
89516
  init_async_lifecycle();
89315
89517
  init_lineage_approval_invalidation();
@@ -92777,7 +92979,7 @@ async function _maybeAutoArchiveSameAgentDuplicateBackendImpl(store, task, input
92777
92979
  }
92778
92980
  async function updateBranchGroupImpl(store, id, patch) {
92779
92981
  const layer = store.asyncLayer;
92780
- return updateBranchGroup(layer.db, id, patch);
92982
+ return updateBranchGroup(layer.db, id, patch, layer.projectId);
92781
92983
  }
92782
92984
  async function updatePrEntityImpl(store, id, patch) {
92783
92985
  const layer = store.asyncLayer;
@@ -93287,13 +93489,19 @@ async function deleteAttachmentArtifactRows(store, taskId, filename) {
93287
93489
  const linkedArtifactIds = artifacts2.filter((artifact) => artifact.metadata?.source === "attachment" && artifact.metadata.attachmentFilename === filename).map((artifact) => artifact.id);
93288
93490
  if (linkedArtifactIds.length === 0) return;
93289
93491
  for (const artifactId of linkedArtifactIds) {
93290
- await layer.db.delete(project_exports.artifacts).where(eq(project_exports.artifacts.id, artifactId));
93492
+ await layer.db.delete(project_exports.artifacts).where(and(
93493
+ eq(project_exports.artifacts.id, artifactId),
93494
+ projectScopeFor(project_exports.artifacts.projectId, layer.projectId)
93495
+ ));
93291
93496
  }
93292
93497
  return;
93293
93498
  }
93294
93499
  async function getArtifactsForAttachmentCleanup(store, taskId) {
93295
93500
  const layer = store.asyncLayer;
93296
- const rows = await layer.db.select().from(project_exports.artifacts).where(eq(project_exports.artifacts.taskId, taskId));
93501
+ const rows = await layer.db.select().from(project_exports.artifacts).where(and(
93502
+ eq(project_exports.artifacts.taskId, taskId),
93503
+ projectScopeFor(project_exports.artifacts.projectId, layer.projectId)
93504
+ ));
93297
93505
  return rows;
93298
93506
  }
93299
93507
  async function deleteAttachmentImpl(store, id, filename) {
@@ -95709,7 +95917,7 @@ function clearLinkedAgentTaskIdsImpl(store, taskId, updatedAt = (/* @__PURE__ */
95709
95917
  `).run(updatedAt, updatedAt, taskId);
95710
95918
  }
95711
95919
  async function listArtifactsImpl(store, options) {
95712
- return listArtifacts(store.asyncLayer.db, options);
95920
+ return listArtifacts(store.asyncLayer.db, options, store.asyncLayer.projectId);
95713
95921
  }
95714
95922
  async function rehomeOccupantImpl(store, taskId, targetColumn, reason, metadata) {
95715
95923
  let current;
@@ -96954,8 +97162,8 @@ async function upsertWorkflowWorkItemImpl(store, input, tx) {
96954
97162
  async function replaceActiveTaskWorkflowContinuationImpl(store, input) {
96955
97163
  return replaceActiveTaskWorkflowContinuation(store.asyncLayer, input);
96956
97164
  }
96957
- async function seedStrandedPlanReviewContinuationImpl(store, input) {
96958
- return seedStrandedPlanReviewContinuation(store.asyncLayer, input);
97165
+ async function seedStrandedPlanReviewContinuationImpl(store, input, options = {}) {
97166
+ return seedStrandedPlanReviewContinuation(store.asyncLayer, input, options);
96959
97167
  }
96960
97168
  async function transitionWorkflowWorkItemImpl(store, id, state, patch = {}, tx) {
96961
97169
  return transitionWorkflowWorkItem(store.asyncLayer, id, state, patch, tx);
@@ -101098,6 +101306,37 @@ async function getTaskVerificationRequestAsyncImpl(store, taskId) {
101098
101306
  const row = rows[0];
101099
101307
  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;
101100
101308
  }
101309
+ async function listTaskRecommendationsImpl(store, options) {
101310
+ const layer = store.asyncLayer;
101311
+ const completeColumns = options?.completeColumns ?? await resolveProjectColumnsForRoles(store, ["complete"]);
101312
+ const rawLimit = options?.limit;
101313
+ const rawOffset = options?.offset;
101314
+ const limit2 = typeof rawLimit === "number" && Number.isFinite(rawLimit) ? Math.min(200, Math.max(1, Math.trunc(rawLimit))) : 50;
101315
+ const offset = typeof rawOffset === "number" && Number.isFinite(rawOffset) ? Math.max(0, Math.trunc(rawOffset)) : 0;
101316
+ const columns = [...completeColumns];
101317
+ if (columns.length === 0) return { items: [], rowOffset: offset, rowLimit: limit2, returnedRowCount: 0, totalRowCount: 0, hasMore: false };
101318
+ const filter2 = and(
101319
+ taskProjectScope(layer),
101320
+ isNull(project_exports.tasks.deletedAt),
101321
+ inArray(project_exports.tasks.column, columns),
101322
+ isNotNull(project_exports.tasks.recommendations),
101323
+ sql`jsonb_array_length(${project_exports.tasks.recommendations}) > 0`
101324
+ );
101325
+ const [countRows, rows] = await Promise.all([
101326
+ layer.db.select({ count: sql`count(*)` }).from(project_exports.tasks).where(filter2),
101327
+ 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(filter2).orderBy(desc(project_exports.tasks.updatedAt), desc(project_exports.tasks.id)).limit(limit2).offset(offset)
101328
+ ]);
101329
+ const items = [];
101330
+ for (const row of rows) {
101331
+ if (!Array.isArray(row.recommendations) || row.recommendations.length === 0) continue;
101332
+ for (const recommendation of row.recommendations) {
101333
+ items.push({ taskId: row.id, taskTitle: row.title ?? void 0, taskColumn: row.column, updatedAt: row.updatedAt, recommendation });
101334
+ }
101335
+ }
101336
+ const totalRowCount = Number(countRows[0]?.count ?? 0);
101337
+ const returnedRowCount = rows.length;
101338
+ return { items, rowOffset: offset, rowLimit: limit2, returnedRowCount, totalRowCount, hasMore: offset + returnedRowCount < totalRowCount };
101339
+ }
101101
101340
  var LEGACY_REVIEW_LANES5, LEGACY_ARCHIVE_LANES2;
101102
101341
  var init_reads = __esm({
101103
101342
  "../core/src/task-store/reads.ts"() {
@@ -101120,6 +101359,7 @@ var init_reads = __esm({
101120
101359
  init_retry_summary();
101121
101360
  init_errors3();
101122
101361
  init_project_lane_vocabulary();
101362
+ init_data_layer();
101123
101363
  init_async_persistence();
101124
101364
  init_async_search();
101125
101365
  init_async_archive_db();
@@ -103226,6 +103466,9 @@ var init_store = __esm({
103226
103466
  async clearWorkflowRunStepInstancesAsync(taskId, keepRunId) {
103227
103467
  return clearWorkflowRunStepInstancesAsyncImpl(this, taskId, keepRunId);
103228
103468
  }
103469
+ async listTaskRecommendations(options) {
103470
+ return listTaskRecommendationsImpl(this, options);
103471
+ }
103229
103472
  async listTasksForGithubTrackingReconcile(options) {
103230
103473
  return listTasksForGithubTrackingReconcileImpl(this, options);
103231
103474
  }
@@ -103952,8 +104195,8 @@ var init_store = __esm({
103952
104195
  async replaceActiveTaskWorkflowContinuation(input) {
103953
104196
  return replaceActiveTaskWorkflowContinuationImpl(this, input);
103954
104197
  }
103955
- async seedStrandedPlanReviewContinuation(input) {
103956
- return seedStrandedPlanReviewContinuationImpl(this, input);
104198
+ async seedStrandedPlanReviewContinuation(input, options = {}) {
104199
+ return seedStrandedPlanReviewContinuationImpl(this, input, options);
103957
104200
  }
103958
104201
  async transitionWorkflowWorkItem(id, state, patch = {}, tx) {
103959
104202
  return transitionWorkflowWorkItemImpl(this, id, state, patch, tx);
@@ -104036,7 +104279,11 @@ var init_store = __esm({
104036
104279
  /** PostgreSQL-authoritative audit reader; sync fallback remains for test doubles. */
104037
104280
  async getRunAuditEventsAsync(options = {}) {
104038
104281
  if (this.asyncLayer) {
104039
- const events = await queryRunAuditEvents(this.asyncLayer.db, options);
104282
+ const events = await queryRunAuditEvents(
104283
+ this.asyncLayer.db,
104284
+ options,
104285
+ this.asyncLayer.projectId
104286
+ );
104040
104287
  return events.map((event) => ({
104041
104288
  ...event,
104042
104289
  taskId: event.taskId ?? void 0,
@@ -107614,7 +107861,9 @@ function rangeWhere(query) {
107614
107861
  async function aggregatePluginActivations(dbOrLayer, query = {}) {
107615
107862
  if ("ping" in dbOrLayer) {
107616
107863
  const layer = dbOrLayer;
107617
- const conditions = [];
107864
+ const conditions = [projectScopeFor(project_exports.pluginActivations.projectId, layer.projectId)].filter(
107865
+ (condition) => condition !== void 0
107866
+ );
107618
107867
  if (query.from !== void 0) conditions.push(gte(project_exports.pluginActivations.activatedAt, query.from));
107619
107868
  if (query.to !== void 0) conditions.push(lte(project_exports.pluginActivations.activatedAt, query.to));
107620
107869
  const where2 = conditions.length > 0 ? and(...conditions) : void 0;
@@ -107653,6 +107902,7 @@ async function aggregatePluginActivations(dbOrLayer, query = {}) {
107653
107902
  var init_plugin_activation_analytics = __esm({
107654
107903
  "../core/src/plugins/plugin-activation-analytics.ts"() {
107655
107904
  "use strict";
107905
+ init_data_layer();
107656
107906
  init_drizzle_orm();
107657
107907
  init_schema2();
107658
107908
  }
@@ -130650,6 +130900,18 @@ function rowToMessage2(row) {
130650
130900
  createdAt: row.createdAt
130651
130901
  };
130652
130902
  }
130903
+ function chatMessageProjectConditions(handle, projectId2) {
130904
+ const messageScope = projectScopeFor(project_exports.chatMessages.projectId, projectId2);
130905
+ const sessionScope = projectScopeFor(project_exports.chatSessions.projectId, projectId2);
130906
+ if (!messageScope || !sessionScope) return [];
130907
+ return [
130908
+ messageScope,
130909
+ exists(handle.select({ id: project_exports.chatSessions.id }).from(project_exports.chatSessions).where(and(
130910
+ eq(project_exports.chatSessions.id, project_exports.chatMessages.sessionId),
130911
+ sessionScope
130912
+ )))
130913
+ ];
130914
+ }
130653
130915
  function rowToRoom(row) {
130654
130916
  return {
130655
130917
  id: row.id,
@@ -130821,7 +131083,7 @@ async function replaceChatSessionTags(layer, sessionId, projectId2, tagIds) {
130821
131083
  return attachTags(tx, session);
130822
131084
  });
130823
131085
  }
130824
- async function addChatMessage(handle, message) {
131086
+ async function addChatMessage(handle, message, projectId2) {
130825
131087
  const sanitizedAttachments = message.attachments === void 0 ? void 0 : sanitizeJsonbValue(message.attachments);
130826
131088
  const sanitized = {
130827
131089
  ...message,
@@ -130840,15 +131102,24 @@ async function addChatMessage(handle, message) {
130840
131102
  attachments: sanitizedAttachments ?? null,
130841
131103
  createdAt: sanitized.createdAt
130842
131104
  });
130843
- await handle.update(project_exports.chatSessions).set({ updatedAt: sanitized.createdAt }).where(eq(project_exports.chatSessions.id, sanitized.sessionId));
131105
+ await handle.update(project_exports.chatSessions).set({ updatedAt: sanitized.createdAt }).where(and(
131106
+ eq(project_exports.chatSessions.id, sanitized.sessionId),
131107
+ projectScopeFor(project_exports.chatSessions.projectId, projectId2)
131108
+ ));
130844
131109
  return sanitized;
130845
131110
  }
130846
- async function getChatMessage(handle, id) {
130847
- const rows = await handle.select().from(project_exports.chatMessages).where(eq(project_exports.chatMessages.id, id));
131111
+ async function getChatMessage(handle, id, projectId2) {
131112
+ const rows = await handle.select().from(project_exports.chatMessages).where(and(
131113
+ eq(project_exports.chatMessages.id, id),
131114
+ ...chatMessageProjectConditions(handle, projectId2)
131115
+ ));
130848
131116
  return rows[0] ? rowToMessage2(rows[0]) : void 0;
130849
131117
  }
130850
- async function getChatMessages(handle, sessionId, filter2) {
130851
- const conditions = [eq(project_exports.chatMessages.sessionId, sessionId)];
131118
+ async function getChatMessages(handle, sessionId, filter2, projectId2) {
131119
+ const conditions = [
131120
+ eq(project_exports.chatMessages.sessionId, sessionId),
131121
+ ...chatMessageProjectConditions(handle, projectId2)
131122
+ ];
130852
131123
  if (filter2?.before) {
130853
131124
  conditions.push(lte(project_exports.chatMessages.createdAt, filter2.before));
130854
131125
  }
@@ -130858,9 +131129,12 @@ async function getChatMessages(handle, sessionId, filter2) {
130858
131129
  const rows = await handle.select().from(project_exports.chatMessages).where(and(...conditions)).orderBy(filter2?.order === "desc" ? desc(orderCol) : asc(orderCol)).limit(limit2).offset(offset);
130859
131130
  return rows.map(rowToMessage2);
130860
131131
  }
130861
- async function getLastMessageForSessions(handle, sessionIds) {
131132
+ async function getLastMessageForSessions(handle, sessionIds, projectId2) {
130862
131133
  if (sessionIds.length === 0) return /* @__PURE__ */ new Map();
130863
- const rows = await handle.select().from(project_exports.chatMessages).where(inArray(project_exports.chatMessages.sessionId, sessionIds)).orderBy(
131134
+ const rows = await handle.select().from(project_exports.chatMessages).where(and(
131135
+ inArray(project_exports.chatMessages.sessionId, sessionIds),
131136
+ ...chatMessageProjectConditions(handle, projectId2)
131137
+ )).orderBy(
130864
131138
  desc(project_exports.chatMessages.createdAt),
130865
131139
  desc(project_exports.chatMessages.id)
130866
131140
  );
@@ -131026,8 +131300,8 @@ async function setInFlightGeneration(handle, id, inFlightGeneration) {
131026
131300
  await handle.update(project_exports.chatSessions).set({ inFlightGeneration: sanitizedInFlightGeneration }).where(eq(project_exports.chatSessions.id, id));
131027
131301
  return getChatSession(handle, id);
131028
131302
  }
131029
- async function addChatMessageAttachment(handle, sessionId, messageId, attachment) {
131030
- const message = await getChatMessage(handle, messageId);
131303
+ async function addChatMessageAttachment(handle, sessionId, messageId, attachment, projectId2) {
131304
+ const message = await getChatMessage(handle, messageId, projectId2);
131031
131305
  if (!message || message.sessionId !== sessionId) {
131032
131306
  throw new Error(`Message ${messageId} not found in session ${sessionId}`);
131033
131307
  }
@@ -131035,25 +131309,35 @@ async function addChatMessageAttachment(handle, sessionId, messageId, attachment
131035
131309
  ...message.attachments ?? [],
131036
131310
  attachment
131037
131311
  ]);
131038
- await handle.update(project_exports.chatMessages).set({ attachments: updatedAttachments }).where(eq(project_exports.chatMessages.id, messageId));
131039
- const updated = await getChatMessage(handle, messageId);
131312
+ await handle.update(project_exports.chatMessages).set({ attachments: updatedAttachments }).where(and(
131313
+ eq(project_exports.chatMessages.id, messageId),
131314
+ ...chatMessageProjectConditions(handle, projectId2)
131315
+ ));
131316
+ const updated = await getChatMessage(handle, messageId, projectId2);
131040
131317
  if (!updated) throw new Error(`Failed to update message ${messageId}`);
131041
131318
  return updated;
131042
131319
  }
131043
- async function deleteChatMessage(handle, id) {
131044
- const existing = await getChatMessage(handle, id);
131320
+ async function deleteChatMessage(handle, id, projectId2) {
131321
+ const existing = await getChatMessage(handle, id, projectId2);
131045
131322
  if (!existing) return false;
131046
- await handle.delete(project_exports.chatMessages).where(eq(project_exports.chatMessages.id, id));
131047
- await handle.update(project_exports.chatSessions).set({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(project_exports.chatSessions.id, existing.sessionId));
131323
+ await handle.delete(project_exports.chatMessages).where(and(
131324
+ eq(project_exports.chatMessages.id, id),
131325
+ ...chatMessageProjectConditions(handle, projectId2)
131326
+ ));
131327
+ await handle.update(project_exports.chatSessions).set({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and(
131328
+ eq(project_exports.chatSessions.id, existing.sessionId),
131329
+ projectScopeFor(project_exports.chatSessions.projectId, projectId2)
131330
+ ));
131048
131331
  return true;
131049
131332
  }
131050
- async function searchChatSessionsByMessageContent(handle, query, sessionIds) {
131333
+ async function searchChatSessionsByMessageContent(handle, query, sessionIds, projectId2) {
131051
131334
  const trimmed = query.trim();
131052
131335
  if (!trimmed || sessionIds.length === 0) return /* @__PURE__ */ new Map();
131053
131336
  const escaped = trimmed.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
131054
131337
  const rows = await handle.select().from(project_exports.chatMessages).where(and(
131055
131338
  inArray(project_exports.chatMessages.sessionId, sessionIds),
131056
- ilike(project_exports.chatMessages.content, `%${escaped}%`)
131339
+ ilike(project_exports.chatMessages.content, `%${escaped}%`),
131340
+ ...chatMessageProjectConditions(handle, projectId2)
131057
131341
  )).orderBy(
131058
131342
  desc(project_exports.chatMessages.createdAt),
131059
131343
  desc(project_exports.chatMessages.id)
@@ -131067,13 +131351,16 @@ async function searchChatSessionsByMessageContent(handle, query, sessionIds) {
131067
131351
  }
131068
131352
  return result2;
131069
131353
  }
131070
- async function deleteChatMessagesFrom(handle, sessionId, fromMessageId) {
131071
- const orderedRows = await handle.select().from(project_exports.chatMessages).where(eq(project_exports.chatMessages.sessionId, sessionId)).orderBy(
131354
+ async function deleteChatMessagesFrom(handle, sessionId, fromMessageId, projectId2) {
131355
+ const orderedRows = await handle.select().from(project_exports.chatMessages).where(and(
131356
+ eq(project_exports.chatMessages.sessionId, sessionId),
131357
+ ...chatMessageProjectConditions(handle, projectId2)
131358
+ )).orderBy(
131072
131359
  asc(project_exports.chatMessages.createdAt),
131073
131360
  asc(project_exports.chatMessages.id)
131074
131361
  );
131075
131362
  const ordered2 = orderedRows.map(rowToMessage2);
131076
- const target = await getChatMessage(handle, fromMessageId);
131363
+ const target = await getChatMessage(handle, fromMessageId, projectId2);
131077
131364
  if (!target || target.sessionId !== sessionId) {
131078
131365
  return { deletedIds: [], retained: ordered2 };
131079
131366
  }
@@ -131086,19 +131373,28 @@ async function deleteChatMessagesFrom(handle, sessionId, fromMessageId) {
131086
131373
  if (deletedIds.length === 0) {
131087
131374
  return { deletedIds: [], retained };
131088
131375
  }
131089
- await handle.delete(project_exports.chatMessages).where(inArray(project_exports.chatMessages.id, deletedIds));
131090
- await handle.update(project_exports.chatSessions).set({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(project_exports.chatSessions.id, sessionId));
131376
+ await handle.delete(project_exports.chatMessages).where(and(
131377
+ inArray(project_exports.chatMessages.id, deletedIds),
131378
+ ...chatMessageProjectConditions(handle, projectId2)
131379
+ ));
131380
+ await handle.update(project_exports.chatSessions).set({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and(
131381
+ eq(project_exports.chatSessions.id, sessionId),
131382
+ projectScopeFor(project_exports.chatSessions.projectId, projectId2)
131383
+ ));
131091
131384
  return { deletedIds, retained };
131092
131385
  }
131093
- async function updateChatMessageMetadata(handle, messageId, metadata, options) {
131094
- const existing = await getChatMessage(handle, messageId);
131386
+ async function updateChatMessageMetadata(handle, messageId, metadata, options, projectId2) {
131387
+ const existing = await getChatMessage(handle, messageId, projectId2);
131095
131388
  if (!existing) {
131096
131389
  throw new Error(`Message ${messageId} not found`);
131097
131390
  }
131098
131391
  const merge2 = options?.merge !== false;
131099
131392
  const nextMetadata = metadata === null ? merge2 ? existing.metadata : null : merge2 ? { ...existing.metadata ?? {}, ...metadata } : metadata;
131100
- await handle.update(project_exports.chatMessages).set({ metadata: sanitizeJsonbValue(nextMetadata) ?? null }).where(eq(project_exports.chatMessages.id, messageId));
131101
- const updated = await getChatMessage(handle, messageId);
131393
+ await handle.update(project_exports.chatMessages).set({ metadata: sanitizeJsonbValue(nextMetadata) ?? null }).where(and(
131394
+ eq(project_exports.chatMessages.id, messageId),
131395
+ ...chatMessageProjectConditions(handle, projectId2)
131396
+ ));
131397
+ const updated = await getChatMessage(handle, messageId, projectId2);
131102
131398
  if (!updated) {
131103
131399
  throw new Error(`Failed to update message ${messageId}`);
131104
131400
  }
@@ -131480,7 +131776,7 @@ var init_chat_store = __esm({
131480
131776
  attachments: input.attachments,
131481
131777
  createdAt: now
131482
131778
  };
131483
- const created = await addChatMessage(this.asyncLayer.db, message);
131779
+ const created = await addChatMessage(this.asyncLayer.db, message, this.asyncLayer.projectId);
131484
131780
  this.emit("chat:message:added", created);
131485
131781
  return created;
131486
131782
  }
@@ -131488,7 +131784,7 @@ var init_chat_store = __esm({
131488
131784
  * Append a file attachment metadata record to an existing message.
131489
131785
  */
131490
131786
  async addMessageAttachment(sessionId, messageId, attachment) {
131491
- const updated = await addChatMessageAttachment(this.asyncLayer.db, sessionId, messageId, attachment);
131787
+ const updated = await addChatMessageAttachment(this.asyncLayer.db, sessionId, messageId, attachment, this.asyncLayer.projectId);
131492
131788
  this.emit("chat:message:updated", updated);
131493
131789
  return updated;
131494
131790
  }
@@ -131500,7 +131796,7 @@ var init_chat_store = __esm({
131500
131796
  * @returns Array of messages ordered by createdAt ASC (default) or DESC
131501
131797
  */
131502
131798
  async getMessages(sessionId, filter2) {
131503
- return getChatMessages(this.asyncLayer.db, sessionId, filter2);
131799
+ return getChatMessages(this.asyncLayer.db, sessionId, filter2, this.asyncLayer.projectId);
131504
131800
  }
131505
131801
  /**
131506
131802
  * Get a message by ID.
@@ -131509,7 +131805,7 @@ var init_chat_store = __esm({
131509
131805
  * @returns The message, or undefined if not found
131510
131806
  */
131511
131807
  async getMessage(id) {
131512
- return getChatMessage(this.asyncLayer.db, id);
131808
+ return getChatMessage(this.asyncLayer.db, id, this.asyncLayer.projectId);
131513
131809
  }
131514
131810
  /**
131515
131811
  * Get the latest message for each session in the provided list.
@@ -131519,10 +131815,10 @@ var init_chat_store = __esm({
131519
131815
  * @returns Map of sessionId -> latest ChatMessage for that session
131520
131816
  */
131521
131817
  async getLastMessageForSessions(sessionIds) {
131522
- return getLastMessageForSessions(this.asyncLayer.db, sessionIds);
131818
+ return getLastMessageForSessions(this.asyncLayer.db, sessionIds, this.asyncLayer.projectId);
131523
131819
  }
131524
131820
  async hasMessages(sessionId) {
131525
- return (await getChatMessages(this.asyncLayer.db, sessionId, { limit: 1 })).length > 0;
131821
+ return (await getChatMessages(this.asyncLayer.db, sessionId, { limit: 1 }, this.asyncLayer.projectId)).length > 0;
131526
131822
  }
131527
131823
  /**
131528
131824
  * Search sessions by message content (not just title/agentId).
@@ -131550,7 +131846,7 @@ var init_chat_store = __esm({
131550
131846
  if (!trimmed || !sessionIds || sessionIds.length === 0) {
131551
131847
  return /* @__PURE__ */ new Map();
131552
131848
  }
131553
- return searchChatSessionsByMessageContent(this.asyncLayer.db, trimmed, sessionIds);
131849
+ return searchChatSessionsByMessageContent(this.asyncLayer.db, trimmed, sessionIds, this.asyncLayer.projectId);
131554
131850
  }
131555
131851
  /**
131556
131852
  * Delete a message by ID.
@@ -131559,9 +131855,9 @@ var init_chat_store = __esm({
131559
131855
  * @returns true if deleted, false if not found
131560
131856
  */
131561
131857
  async deleteMessage(id) {
131562
- const existing = await getChatMessage(this.asyncLayer.db, id);
131858
+ const existing = await getChatMessage(this.asyncLayer.db, id, this.asyncLayer.projectId);
131563
131859
  if (!existing) return false;
131564
- const deleted = await deleteChatMessage(this.asyncLayer.db, id);
131860
+ const deleted = await deleteChatMessage(this.asyncLayer.db, id, this.asyncLayer.projectId);
131565
131861
  if (deleted) {
131566
131862
  this.emit("chat:message:deleted", id);
131567
131863
  const updatedSession = await this.getSession(existing.sessionId);
@@ -131584,7 +131880,7 @@ var init_chat_store = __esm({
131584
131880
  * @returns deletedIds (in ASC order) and retained messages (pre-edit history, ASC order)
131585
131881
  */
131586
131882
  async deleteMessagesFrom(sessionId, fromMessageId) {
131587
- const result2 = await deleteChatMessagesFrom(this.asyncLayer.db, sessionId, fromMessageId);
131883
+ const result2 = await deleteChatMessagesFrom(this.asyncLayer.db, sessionId, fromMessageId, this.asyncLayer.projectId);
131588
131884
  if (result2.deletedIds.length > 0) {
131589
131885
  for (const id of result2.deletedIds) {
131590
131886
  this.emit("chat:message:deleted", id);
@@ -131602,7 +131898,7 @@ var init_chat_store = __esm({
131602
131898
  * is what lets a later edit rewind losslessly via SessionManager.branch()/resetLeaf().
131603
131899
  */
131604
131900
  async updateMessageMetadata(messageId, metadata, options) {
131605
- const updated = await updateChatMessageMetadata(this.asyncLayer.db, messageId, metadata, options);
131901
+ const updated = await updateChatMessageMetadata(this.asyncLayer.db, messageId, metadata, options, this.asyncLayer.projectId);
131606
131902
  this.emit("chat:message:updated", updated);
131607
131903
  return updated;
131608
131904
  }
@@ -133038,6 +133334,77 @@ var init_task_timing = __esm({
133038
133334
  }
133039
133335
  });
133040
133336
 
133337
+ // ../core/src/task-recommendation-notice.ts
133338
+ import { createHash as createHash20 } from "node:crypto";
133339
+ function registerTaskRecommendationNoticeMailbox(store, mailbox) {
133340
+ mailboxes2.set(store, mailbox);
133341
+ return () => {
133342
+ if (mailboxes2.get(store) === mailbox) mailboxes2.delete(store);
133343
+ };
133344
+ }
133345
+ function getTaskRecommendationNoticeMailbox(store) {
133346
+ return mailboxes2.get(store);
133347
+ }
133348
+ function buildTaskRecommendationNoticeContent(task, recommendations) {
133349
+ const title2 = task.title?.trim();
133350
+ const heading = title2 ? `## Recommendations from ${task.id} \u2014 ${title2}` : `## Recommendations from ${task.id}`;
133351
+ return [
133352
+ heading,
133353
+ "",
133354
+ "These are optional, non-blocking out-of-scope proposals that were **not** executed.",
133355
+ "",
133356
+ ...recommendations.map((recommendation) => `- **${recommendation.title}** \u2014 \`${recommendation.category}\`: ${recommendation.description}`),
133357
+ "",
133358
+ `Open ${task.id}'s **Recommendations** tab to review them. **Create task** files one through normal guarded intake.`
133359
+ ].join("\n");
133360
+ }
133361
+ function buildTaskRecommendationNoticeIdempotencyKey(taskId, recommendations) {
133362
+ const ids = recommendations.map(({ id }) => id).sort().join("\n");
133363
+ const digest = createHash20("sha256").update(ids, "utf8").digest("hex").slice(0, 16);
133364
+ return `task-recommendation-notice:${taskId}:${digest}`;
133365
+ }
133366
+ async function notifyOperatorOfTaskRecommendations(store, task, recommendations, settings) {
133367
+ try {
133368
+ if (settings.recommendationMailboxNoticeEnabled === false || !recommendations?.length) return false;
133369
+ const mailbox = mailboxes2.get(store);
133370
+ if (!mailbox) return false;
133371
+ await mailbox.sendMessageOnce(
133372
+ {
133373
+ fromId: "system",
133374
+ fromType: "system",
133375
+ toId: DASHBOARD_USER_ID,
133376
+ toType: "user",
133377
+ type: "system",
133378
+ content: buildTaskRecommendationNoticeContent(task, recommendations),
133379
+ metadata: {
133380
+ kind: "task-recommendation-notice",
133381
+ taskId: task.id,
133382
+ recommendationCount: recommendations.length,
133383
+ recommendationIds: recommendations.map(({ id }) => id),
133384
+ categories: recommendations.map(({ category }) => category)
133385
+ }
133386
+ },
133387
+ buildTaskRecommendationNoticeIdempotencyKey(task.id, recommendations)
133388
+ );
133389
+ return true;
133390
+ } catch (error3) {
133391
+ noticeLog2.warn(
133392
+ `Operator recommendation notice failed for ${task.id}: ${error3 instanceof Error ? error3.message : String(error3)}`
133393
+ );
133394
+ return false;
133395
+ }
133396
+ }
133397
+ var noticeLog2, mailboxes2;
133398
+ var init_task_recommendation_notice = __esm({
133399
+ "../core/src/task-recommendation-notice.ts"() {
133400
+ "use strict";
133401
+ init_logger();
133402
+ init_types();
133403
+ noticeLog2 = createLogger("task-recommendation-notice");
133404
+ mailboxes2 = /* @__PURE__ */ new WeakMap();
133405
+ }
133406
+ });
133407
+
133041
133408
  // ../core/src/session-identity-registry.ts
133042
133409
  import { AsyncLocalStorage } from "node:async_hooks";
133043
133410
  import { resolve as resolve24 } from "node:path";
@@ -133887,6 +134254,8 @@ __export(src_exports, {
133887
134254
  buildTaskDeleteNoticeContent: () => buildTaskDeleteNoticeContent,
133888
134255
  buildTaskDeleteNoticeIdempotencyKey: () => buildTaskDeleteNoticeIdempotencyKey,
133889
134256
  buildTaskLineageTrailer: () => buildTaskLineageTrailer,
134257
+ buildTaskRecommendationNoticeContent: () => buildTaskRecommendationNoticeContent,
134258
+ buildTaskRecommendationNoticeIdempotencyKey: () => buildTaskRecommendationNoticeIdempotencyKey,
133890
134259
  buildToolOutputTruncationMarker: () => buildToolOutputTruncationMarker,
133891
134260
  buildTriageHeartbeatGuidance: () => buildTriageHeartbeatGuidance,
133892
134261
  buildTriageMemoryInstructions: () => buildTriageMemoryInstructions,
@@ -134217,6 +134586,7 @@ __export(src_exports, {
134217
134586
  getTaskMergeBlocker: () => getTaskMergeBlocker,
134218
134587
  getTaskMoveDisposer: () => getTaskMoveDisposer,
134219
134588
  getTaskPriorityRank: () => getTaskPriorityRank,
134589
+ getTaskRecommendationNoticeMailbox: () => getTaskRecommendationNoticeMailbox,
134220
134590
  getTemplatesForRole: () => getTemplatesForRole,
134221
134591
  getTotalAgentActiveMs: () => getTotalAgentActiveMs,
134222
134592
  getTrait: () => getTrait,
@@ -134467,6 +134837,7 @@ __export(src_exports, {
134467
134837
  normalizeWorkflowPromptOverrides: () => normalizeWorkflowPromptOverrides,
134468
134838
  normalizeWorkflowReviewFindings: () => normalizeWorkflowReviewFindings,
134469
134839
  notifyOperatorOfNonOperatorDelete: () => notifyOperatorOfNonOperatorDelete,
134840
+ notifyOperatorOfTaskRecommendations: () => notifyOperatorOfTaskRecommendations,
134470
134841
  overseerAdviceSeverityRank: () => overseerAdviceSeverityRank,
134471
134842
  parseAgentManifest: () => parseAgentManifest,
134472
134843
  parseCompanyArchive: () => parseCompanyArchive,
@@ -134576,6 +134947,7 @@ __export(src_exports, {
134576
134947
  registerStepParser: () => registerStepParser,
134577
134948
  registerTaskDeleteNoticeMailbox: () => registerTaskDeleteNoticeMailbox,
134578
134949
  registerTaskMoveDisposer: () => registerTaskMoveDisposer,
134950
+ registerTaskRecommendationNoticeMailbox: () => registerTaskRecommendationNoticeMailbox,
134579
134951
  registerTraitHookImpl: () => registerTraitHookImpl,
134580
134952
  rekeyFallbackProjectPartition: () => rekeyFallbackProjectPartition,
134581
134953
  releaseEmbeddedRuntimeLease: () => releaseEmbeddedRuntimeLease,
@@ -135203,6 +135575,7 @@ var init_src2 = __esm({
135203
135575
  init_task_document_concurrency();
135204
135576
  init_task_delete_attribution();
135205
135577
  init_task_delete_notice();
135578
+ init_task_recommendation_notice();
135206
135579
  init_session_identity_registry();
135207
135580
  init_task_lifecycle_event_retention();
135208
135581
  init_task_lifecycle_consumer_identity();
@@ -136040,7 +136413,7 @@ var init_logger3 = __esm({
136040
136413
  });
136041
136414
 
136042
136415
  // ../engine/src/agents/agent-logger.ts
136043
- import { createHash as createHash20 } from "node:crypto";
136416
+ import { createHash as createHash21 } from "node:crypto";
136044
136417
  function truncateToolResultDetail(value2) {
136045
136418
  if (value2.length <= TOOL_RESULT_DETAIL_LIMIT) return value2;
136046
136419
  return `${value2.slice(0, TOOL_RESULT_DETAIL_LIMIT)}${TOOL_RESULT_DETAIL_TRUNCATION_NOTICE}`;
@@ -136129,7 +136502,7 @@ function summarizeToolArgs(name2, args) {
136129
136502
  const json2 = JSON.stringify(args);
136130
136503
  if (!json2 || json2 === "{}" || json2 === "[]") return void 0;
136131
136504
  if (json2.length <= STRUCTURED_ARG_SUMMARY_MAX) return json2;
136132
- const hash3 = createHash20("sha256").update(json2).digest("hex").slice(0, STRUCTURED_ARG_HASH_HEX_LEN);
136505
+ const hash3 = createHash21("sha256").update(json2).digest("hex").slice(0, STRUCTURED_ARG_HASH_HEX_LEN);
136133
136506
  const suffix = `\u2026#${hash3}`;
136134
136507
  const keep = Math.max(0, STRUCTURED_ARG_SUMMARY_MAX - suffix.length);
136135
136508
  return `${json2.slice(0, keep)}${suffix}`;
@@ -136817,7 +137190,13 @@ var init_gating_classifications = __esm({
136817
137190
  "fn_post_room_message",
136818
137191
  "fn_update_identity",
136819
137192
  "fn_reflect_on_performance",
136820
- "fn_read_evaluations"
137193
+ /*
137194
+ FNXC:ToolGovernance 2026-08-12-22:11:
137195
+ fn_agent_read_evaluations is a subtree-scoped read. Authorization is enforced by
137196
+ the extension tool's getChainOfCommand boundary, not by this action classifier.
137197
+ */
137198
+ "fn_read_evaluations",
137199
+ "fn_agent_read_evaluations"
136821
137200
  ]);
136822
137201
  COORDINATION_EXEMPT_TOOLS = [
136823
137202
  "read",
@@ -136877,6 +137256,8 @@ var init_gating_classifications = __esm({
136877
137256
  "fn_post_room_message",
136878
137257
  "fn_memory_append",
136879
137258
  "fn_read_evaluations",
137259
+ // 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.
137260
+ "fn_agent_read_evaluations",
136880
137261
  "fn_update_identity",
136881
137262
  "fn_reflect_on_performance"
136882
137263
  ];
@@ -136901,7 +137282,7 @@ var init_gating_classifications = __esm({
136901
137282
  });
136902
137283
 
136903
137284
  // ../engine/src/agents/agent-action-gate.ts
136904
- import { createHash as createHash21 } from "node:crypto";
137285
+ import { createHash as createHash22 } from "node:crypto";
136905
137286
  function getExemptTools() {
136906
137287
  if (!_exemptTools) {
136907
137288
  _exemptTools = new Set(DEFAULT_EXEMPT_TOOLS);
@@ -136953,7 +137334,7 @@ function evaluateAgentActionGate(params) {
136953
137334
  if (params.toolName === "bash") {
136954
137335
  const command2 = extractShellCommand(args);
136955
137336
  const git7 = classifyGitCommand(command2);
136956
- resourceId = command2 ? `cmd:${createHash21("sha256").update(command2).digest("hex").slice(0, 16)}` : void 0;
137337
+ resourceId = command2 ? `cmd:${createHash22("sha256").update(command2).digest("hex").slice(0, 16)}` : void 0;
136957
137338
  if (git7?.write) {
136958
137339
  category = "git_write";
136959
137340
  operation = git7.operation;
@@ -140485,11 +140866,11 @@ var init_planning_handoff_recovery = __esm({
140485
140866
  // ../engine/src/plan-review-continuation.ts
140486
140867
  async function seedPreReleasePlanReviewContinuation(store, task, ir2, options = {}) {
140487
140868
  const node = resolvePreReleasePlanReviewNode(ir2);
140488
- if (!node || node.column !== task.column) return { seeded: false };
140869
+ if (!node || node.column !== task.column) return { seeded: false, reason: "no-pre-release-plan-review" };
140489
140870
  if (isTaskBlockedOnApproval(task)) return { seeded: false, reason: "awaiting-approval" };
140490
140871
  if (task.paused === true || task.userPaused === true) return { seeded: false, reason: "paused" };
140491
140872
  const items = await store.listWorkflowWorkItemsForTask(task.id);
140492
- const active2 = items.filter((item2) => ACTIVE_WORKFLOW_WORK_ITEM_STATES.includes(item2.state));
140873
+ const active2 = items.filter((item2) => ACTIVE_WORKFLOW_WORK_ITEM_STATES.includes(item2.state) && item2.id !== options.retirePredecessorId);
140493
140874
  if (active2.length > 0) return { seeded: false, reason: "active-continuation" };
140494
140875
  const continuationSequence = items.length;
140495
140876
  const input = {
@@ -140505,6 +140886,9 @@ async function seedPreReleasePlanReviewContinuation(store, task, ir2, options =
140505
140886
  targetColumn: task.column,
140506
140887
  irHash: computeWorkflowIrPin(ir2, node.id).irHash
140507
140888
  };
140889
+ if (options.retirePredecessorId) {
140890
+ return store.seedStrandedPlanReviewContinuation(input, { retirePredecessorId: options.retirePredecessorId });
140891
+ }
140508
140892
  if (options.atomic) return store.seedStrandedPlanReviewContinuation(input);
140509
140893
  const item = await store.replaceActiveTaskWorkflowContinuation(input);
140510
140894
  return { seeded: true, workItemId: item.id };
@@ -140519,7 +140903,7 @@ async function resumeApprovedPlanReviewHandoff(store, task, ir2) {
140519
140903
  if (seeded.seeded) return { resumed: true, reason: "seeded", workItemId: seeded.workItemId };
140520
140904
  return {
140521
140905
  resumed: false,
140522
- reason: seeded.reason ?? "not-plan-in-place"
140906
+ reason: seeded.reason === void 0 || seeded.reason === "no-pre-release-plan-review" ? "not-plan-in-place" : seeded.reason
140523
140907
  };
140524
140908
  }
140525
140909
  function evaluateStrandedHoldContinuation(input) {
@@ -140557,7 +140941,7 @@ var init_plan_review_continuation = __esm({
140557
140941
  });
140558
140942
 
140559
140943
  // ../engine/src/execution/hold-release.ts
140560
- import { createHash as createHash22 } from "node:crypto";
140944
+ import { createHash as createHash23 } from "node:crypto";
140561
140945
  import { readFile as readFile31 } from "node:fs/promises";
140562
140946
  function findColumn2(ir2, columnId) {
140563
140947
  if (ir2.version !== "v2") return void 0;
@@ -140595,9 +140979,9 @@ async function checkAndRecordUnplannedExecutionBlock(store, task, ir2) {
140595
140979
  promptContent = "";
140596
140980
  }
140597
140981
  }
140598
- const promptMarker = promptContent.length > 0 ? createHash22("sha256").update(promptContent).digest("hex") : "missing";
140982
+ const promptMarker = promptContent.length > 0 ? createHash23("sha256").update(promptContent).digest("hex") : "missing";
140599
140983
  const dependencies = [...task.dependencies ?? []].sort();
140600
- const episode = createHash22("sha256").update(JSON.stringify({
140984
+ const episode = createHash23("sha256").update(JSON.stringify({
140601
140985
  planReviewNode,
140602
140986
  promptMarker,
140603
140987
  dependencies,
@@ -170717,7 +171101,7 @@ ${getErrorStderr(error3) ?? ""}`;
170717
171101
  });
170718
171102
 
170719
171103
  // ../engine/src/worktree/secrets-env-writer.ts
170720
- import { createHash as createHash23 } from "node:crypto";
171104
+ import { createHash as createHash24 } from "node:crypto";
170721
171105
  import { execFile as execFile8 } from "node:child_process";
170722
171106
  import { promises as fs6 } from "node:fs";
170723
171107
  import path2 from "node:path";
@@ -170726,7 +171110,7 @@ function isValidFilename(filename) {
170726
171110
  return !!filename && !filename.includes("/") && !filename.includes("\\") && !filename.includes("..") && !filename.includes("\0") && filename !== FINGERPRINT_FILE;
170727
171111
  }
170728
171112
  function sha2562(content) {
170729
- return createHash23("sha256").update(Buffer.from(content, "utf8")).digest("hex");
171113
+ return createHash24("sha256").update(Buffer.from(content, "utf8")).digest("hex");
170730
171114
  }
170731
171115
  function quote(value2) {
170732
171116
  return `"${value2.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t")}"`;
@@ -178866,32 +179250,36 @@ var init_scheduler = __esm({
178866
179250
  const hierarchy = await missionStore.getMissionWithHierarchy(mission.id);
178867
179251
  if (!hierarchy) continue;
178868
179252
  for (const slice of hierarchy.milestones.flatMap((milestone) => milestone.slices).filter((slice2) => slice2.status === "active")) {
178869
- const superseded = await missionStore.reconcileSupersededGeneratedFixFeatures(slice.id);
178870
- fixed += superseded.supersededCount;
178871
- if (superseded.supersededCount > 0) {
178872
- const refreshed = await missionStore.getSlice(slice.id);
178873
- if (refreshed?.status === "complete") {
178874
- await this.onSliceComplete(refreshed);
178875
- continue;
179253
+ try {
179254
+ const superseded = await missionStore.reconcileSupersededGeneratedFixFeatures(slice.id);
179255
+ fixed += superseded.supersededCount;
179256
+ if (superseded.supersededCount > 0) {
179257
+ const refreshed = await missionStore.getSlice(slice.id);
179258
+ if (refreshed?.status === "complete") {
179259
+ await this.onSliceComplete(refreshed);
179260
+ continue;
179261
+ }
178876
179262
  }
178877
- }
178878
- const listFeatures2 = missionStore.listFeatures;
178879
- const features = listFeatures2 ? await listFeatures2(slice.id) : slice.features;
178880
- const supersededFeatureIds = new Set(superseded.featureIds ?? []);
178881
- const autoTriage = mission.autopilotEnabled === true || mission.autoAdvance === true;
178882
- for (const feature of features) {
178883
- if (supersededFeatureIds.has(feature.id) || feature.taskId || !autoTriage || feature.status === "blocked") continue;
178884
- if (feature.status !== "defined" && this.isGeneratedFixFeature(feature)) {
178885
- await missionStore.updateFeature(feature.id, { status: "blocked", loopState: "blocked", taskId: void 0 });
179263
+ const listFeatures2 = missionStore.listFeatures;
179264
+ const features = listFeatures2 ? await listFeatures2.call(missionStore, slice.id) : slice.features;
179265
+ const supersededFeatureIds = new Set(superseded.featureIds ?? []);
179266
+ const autoTriage = mission.autopilotEnabled === true || mission.autoAdvance === true;
179267
+ for (const feature of features) {
179268
+ if (supersededFeatureIds.has(feature.id) || feature.taskId || !autoTriage || feature.status === "blocked") continue;
179269
+ if (feature.status !== "defined" && this.isGeneratedFixFeature(feature)) {
179270
+ await missionStore.updateFeature(feature.id, { status: "blocked", loopState: "blocked", taskId: void 0 });
179271
+ fixed++;
179272
+ continue;
179273
+ }
179274
+ if (feature.status !== "defined" && feature.status !== "triaged" && feature.status !== "in-progress") continue;
179275
+ const ready = feature.status === "defined" ? feature : await missionStore.updateFeature(feature.id, { status: "defined", loopState: "idle", taskId: void 0 });
179276
+ if (ready !== feature) fixed++;
179277
+ const triaged = await missionStore.triageFeature(ready.id);
178886
179278
  fixed++;
178887
- continue;
179279
+ if (triaged.taskId) await this.emitStrandedFeatureTriageAudit(mission.id, slice.id, triaged.id, triaged.taskId);
178888
179280
  }
178889
- if (feature.status !== "defined" && feature.status !== "triaged" && feature.status !== "in-progress") continue;
178890
- const ready = feature.status === "defined" ? feature : await missionStore.updateFeature(feature.id, { status: "defined", loopState: "idle", taskId: void 0 });
178891
- if (ready !== feature) fixed++;
178892
- const triaged = await missionStore.triageFeature(ready.id);
178893
- fixed++;
178894
- if (triaged.taskId) await this.emitStrandedFeatureTriageAudit(mission.id, slice.id, triaged.id, triaged.taskId);
179281
+ } catch (error3) {
179282
+ schedulerLog.error(`Error during active mission recovery reconciliation for slice ${slice.id}:`, error3);
178895
179283
  }
178896
179284
  }
178897
179285
  }
@@ -179129,7 +179517,7 @@ var init_mission_feature_sync = __esm({
179129
179517
  // ../engine/src/agent-tools.ts
179130
179518
  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";
179131
179519
  import { existsSync as existsSync53 } from "node:fs";
179132
- import { createHash as createHash24, randomUUID as randomUUID47 } from "node:crypto";
179520
+ import { createHash as createHash25, randomUUID as randomUUID47 } from "node:crypto";
179133
179521
  import { tmpdir as tmpdir5 } from "node:os";
179134
179522
  import { extname as extname4, isAbsolute as isAbsolute14, join as join66, relative as relative14, resolve as resolve38, sep as sep10 } from "node:path";
179135
179523
  import { Type as Type2 } from "@earendil-works/pi-ai";
@@ -179173,7 +179561,7 @@ async function readAgentMemoryWorkspaceLongTerm(rootDir, agentId) {
179173
179561
  }
179174
179562
  }
179175
179563
  function qmdAgentMemoryCollectionName2(rootDir, agentId) {
179176
- const hash3 = createHash24("sha1").update(`${rootDir}:${agentId}`).digest("hex").slice(0, 12);
179564
+ const hash3 = createHash25("sha1").update(`${rootDir}:${agentId}`).digest("hex").slice(0, 12);
179177
179565
  return `fusion-agent-memory-${sanitizeAgentMemoryId(agentId).toLowerCase()}-${hash3}`;
179178
179566
  }
179179
179567
  function buildQmdAgentMemoryCollectionAddArgs(rootDir, agentId) {
@@ -190750,7 +191138,7 @@ var init_agent_heartbeat_error_recovery = __esm({
190750
191138
 
190751
191139
  // ../engine/src/agent-heartbeat.ts
190752
191140
  import { Type as Type3 } from "@earendil-works/pi-ai";
190753
- import { createHash as createHash25 } from "node:crypto";
191141
+ import { createHash as createHash26 } from "node:crypto";
190754
191142
  async function resolveHeartbeatReboundColumn(taskStore, taskId) {
190755
191143
  try {
190756
191144
  return resolveReboundTarget(await resolveWorkflowIrForTask(taskStore, taskId)) ?? "todo";
@@ -190905,7 +191293,7 @@ function truncatePrompt(text3, maxChars) {
190905
191293
  ... (truncated, ${text3.length} chars)`;
190906
191294
  }
190907
191295
  function shortContentHash(value2) {
190908
- return createHash25("sha256").update(value2).digest("hex").slice(0, 8);
191296
+ return createHash26("sha256").update(value2).digest("hex").slice(0, 8);
190909
191297
  }
190910
191298
  function buildIdentitySnapshot(args) {
190911
191299
  const { agent, resolvedInstructions, workspaceMemory } = args;
@@ -198794,7 +199182,7 @@ var init_verification_utils = __esm({
198794
199182
 
198795
199183
  // ../engine/src/merge/merge-dependency-sync.ts
198796
199184
  import { exec as exec25 } from "node:child_process";
198797
- import { createHash as createHash26 } from "node:crypto";
199185
+ import { createHash as createHash27 } from "node:crypto";
198798
199186
  import { existsSync as existsSync56, readFileSync as readFileSync18, writeFileSync as writeFileSync6 } from "node:fs";
198799
199187
  import { join as join69 } from "node:path";
198800
199188
  import { promisify as promisify35 } from "node:util";
@@ -198821,7 +199209,7 @@ function computeLockfileHash(rootDir) {
198821
199209
  const p2 = join69(rootDir, name2);
198822
199210
  if (existsSync56(p2)) {
198823
199211
  try {
198824
- return createHash26("sha256").update(readFileSync18(p2)).digest("hex");
199212
+ return createHash27("sha256").update(readFileSync18(p2)).digest("hex");
198825
199213
  } catch {
198826
199214
  return null;
198827
199215
  }
@@ -201363,7 +201751,7 @@ var init_mesh_lease_manager = __esm({
201363
201751
  });
201364
201752
 
201365
201753
  // ../engine/src/merge/merger-integration-worktree.ts
201366
- import { createHash as createHash27 } from "node:crypto";
201754
+ import { createHash as createHash28 } from "node:crypto";
201367
201755
  import { exec as exec31, execFile as execFile22 } from "node:child_process";
201368
201756
  import { promisify as promisify45 } from "node:util";
201369
201757
  function quoteAutostashArg(value2) {
@@ -201486,7 +201874,7 @@ async function gitDirtyFingerprintLocal(rootDir) {
201486
201874
  )
201487
201875
  ]);
201488
201876
  if (!diffOut && !statusOut) return "";
201489
- return createHash27("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
201877
+ return createHash28("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
201490
201878
  } catch {
201491
201879
  return "";
201492
201880
  }
@@ -202472,7 +202860,7 @@ import { execSync as execSync5, exec as exec33 } from "node:child_process";
202472
202860
  import * as childProcess4 from "node:child_process";
202473
202861
  import { promisify as promisify48 } from "node:util";
202474
202862
  import { existsSync as existsSync59, readFileSync as readFileSync21, writeFileSync as writeFileSync8, unlinkSync as unlinkSync3, renameSync as renameSync4 } from "node:fs";
202475
- import { createHash as createHash28 } from "node:crypto";
202863
+ import { createHash as createHash29 } from "node:crypto";
202476
202864
  import { join as join73, resolve as resolve44 } from "node:path";
202477
202865
  import { hostname as hostname2 } from "node:os";
202478
202866
  import { Type as Type4 } from "typebox";
@@ -202720,7 +203108,7 @@ async function gitDirtyFingerprint(rootDir) {
202720
203108
  )
202721
203109
  ]);
202722
203110
  if (!diffOut && !statusOut) return "";
202723
- return createHash28("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
203111
+ return createHash29("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
202724
203112
  } catch {
202725
203113
  return "";
202726
203114
  }
@@ -225143,7 +225531,7 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
225143
225531
  }
225144
225532
  return;
225145
225533
  }
225146
- const duplicateReport = { outcome: "parked" };
225534
+ const duplicateReport = { outcome: "parked", planningWorkItemId };
225147
225535
  if (await this.tryFinalizeExplicitDuplicateMarker(task, written, settings, {
225148
225536
  isReplan,
225149
225537
  feedback
@@ -225162,6 +225550,7 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
225162
225550
  isReplan,
225163
225551
  feedback
225164
225552
  });
225553
+ finalizeReport.planningWorkItemId = planningWorkItemId;
225165
225554
  this.options.onSpecifyComplete?.(task, finalizeReport);
225166
225555
  } finally {
225167
225556
  this.activeSessions.delete(task.id);
@@ -240733,6 +241122,38 @@ var init_execution_block_classifier = __esm({
240733
241122
  }
240734
241123
  });
240735
241124
 
241125
+ // ../engine/src/executor/completion-recommendation-notice.ts
241126
+ function dispatchAcceptedCompletionRecommendationNotice(args) {
241127
+ if (!args.recommendations?.length) return;
241128
+ const pending = (async () => {
241129
+ const settings = args.settings ?? await args.store.getSettings();
241130
+ const task = await args.store.getTask(args.taskId);
241131
+ if (!task || task.deletedAt) return;
241132
+ await notifyOperatorOfTaskRecommendations(
241133
+ args.store,
241134
+ { id: args.taskId, title: args.taskTitle ?? task.title },
241135
+ args.recommendations,
241136
+ settings
241137
+ );
241138
+ })();
241139
+ pendingNotices.add(pending);
241140
+ void pending.catch((error3) => {
241141
+ args.log?.warn(
241142
+ `Failed to dispatch recommendation mailbox notice for ${args.taskId}: ${error3 instanceof Error ? error3.message : String(error3)}`
241143
+ );
241144
+ }).finally(() => {
241145
+ pendingNotices.delete(pending);
241146
+ });
241147
+ }
241148
+ var pendingNotices;
241149
+ var init_completion_recommendation_notice = __esm({
241150
+ "../engine/src/executor/completion-recommendation-notice.ts"() {
241151
+ "use strict";
241152
+ init_src2();
241153
+ pendingNotices = /* @__PURE__ */ new Set();
241154
+ }
241155
+ });
241156
+
240736
241157
  // ../engine/src/executor/create-task-done-tool.ts
240737
241158
  import { Type as Type9 } from "@earendil-works/pi-ai";
240738
241159
  function createTaskDoneTool(deps, taskId, worktreePath, promptContent, codeReviewVerdicts, onDone, audit) {
@@ -241069,6 +241490,16 @@ ${rerunSuffix}`
241069
241490
  bulkCompletionRefusalAt: null
241070
241491
  });
241071
241492
  await store.logEntry(taskId, "Task marked done by agent", void 0, deps.getRunContextFor(taskId));
241493
+ if (completionRecommendations !== void 0) {
241494
+ dispatchAcceptedCompletionRecommendationNotice({
241495
+ store,
241496
+ taskId,
241497
+ taskTitle: task.title,
241498
+ recommendations: completionRecommendations,
241499
+ settings,
241500
+ log: executorLog
241501
+ });
241502
+ }
241072
241503
  const latestTask = await store.getTask(taskId);
241073
241504
  let latestColumn = latestTask.column;
241074
241505
  if (latestColumn === await resolveReboundColumnFor(store, taskId)) {
@@ -241107,6 +241538,7 @@ var init_create_task_done_tool = __esm({
241107
241538
  init_completion_predicates();
241108
241539
  init_task_done_refusal_handler();
241109
241540
  init_validate_completion_recommendations();
241541
+ init_completion_recommendation_notice();
241110
241542
  }
241111
241543
  });
241112
241544
 
@@ -252433,6 +252865,16 @@ ${rerunSuffix}` });
252433
252865
  if (latestColumn === await resolveWipTargetForTask(deps.store, task.id) && !hardPauseActive && beforeWatchdog && !beforeWatchdog.deletedAt && !(rejectIfPaused && (beforeWatchdog.paused || beforeWatchdog.userPaused))) {
252434
252866
  deps.scheduleCompletedTaskWatchdog(task.id, "fn_task_done");
252435
252867
  }
252868
+ if (recommendations !== void 0) {
252869
+ dispatchAcceptedCompletionRecommendationNotice({
252870
+ store: deps.store,
252871
+ taskId: task.id,
252872
+ taskTitle: task.title,
252873
+ recommendations,
252874
+ settings,
252875
+ log: executorLog
252876
+ });
252877
+ }
252436
252878
  return { completed: true, hardPauseActive };
252437
252879
  } catch (error3) {
252438
252880
  await restoreNoCommitsExpected();
@@ -252491,6 +252933,7 @@ var init_plan_review_no_op = __esm({
252491
252933
  init_src2();
252492
252934
  init_logger3();
252493
252935
  init_lifecycle_columns();
252936
+ init_completion_recommendation_notice();
252494
252937
  }
252495
252938
  });
252496
252939
 
@@ -255999,14 +256442,14 @@ ${stdout.trim()}`);
255999
256442
 
256000
256443
  // ../engine/src/missions/mission-execution-loop.ts
256001
256444
  import { EventEmitter as EventEmitter26 } from "node:events";
256002
- import { createHash as createHash29 } from "node:crypto";
256445
+ import { createHash as createHash30 } from "node:crypto";
256003
256446
  import { exec as exec56 } from "node:child_process";
256004
256447
  import { promisify as promisify74 } from "node:util";
256005
256448
  function quoteShellArg7(value2) {
256006
256449
  return `'${value2.replace(/'/g, `'\\''`)}'`;
256007
256450
  }
256008
256451
  function fingerprintMissionValidationInput(landedSha, provider, modelId, systemPrompt, userPrompt) {
256009
- return createHash29("sha256").update(Buffer.from(JSON.stringify(["mission-validation-input-v1", landedSha, provider, modelId, systemPrompt, userPrompt]), "utf8")).digest("hex");
256452
+ return createHash30("sha256").update(Buffer.from(JSON.stringify(["mission-validation-input-v1", landedSha, provider, modelId, systemPrompt, userPrompt]), "utf8")).digest("hex");
256010
256453
  }
256011
256454
  var execAsync53, loopLog, VALIDATION_TIMEOUT_MS, MAX_VALIDATION_RESPONSE_BYTES, MAX_VALIDATION_JSON_CANDIDATES, VALIDATION_FAILURE_BUDGET_PER_FINGERPRINT, MissionExecutionLoop;
256012
256455
  var init_mission_execution_loop = __esm({
@@ -267545,9 +267988,9 @@ var init_routine_scheduler = __esm({
267545
267988
  });
267546
267989
 
267547
267990
  // ../engine/src/healing/stuck-task-detector.ts
267548
- import { createHash as createHash30 } from "node:crypto";
267991
+ import { createHash as createHash31 } from "node:crypto";
267549
267992
  function hashToolDetail(detail) {
267550
- return createHash30("sha256").update(detail).digest("hex").slice(0, TOOL_DETAIL_HASH_HEX_LEN);
267993
+ return createHash31("sha256").update(detail).digest("hex").slice(0, TOOL_DETAIL_HASH_HEX_LEN);
267551
267994
  }
267552
267995
  function buildToolFingerprint(toolName, toolDetail) {
267553
267996
  const name2 = toolName.trim().toLowerCase() || "tool";
@@ -271125,7 +271568,7 @@ var init_node_dispatch_validation = __esm({
271125
271568
  });
271126
271569
 
271127
271570
  // ../engine/src/runtimes/in-process-runtime.ts
271128
- import { createHash as createHash31 } from "node:crypto";
271571
+ import { createHash as createHash32 } from "node:crypto";
271129
271572
  import { EventEmitter as EventEmitter27 } from "node:events";
271130
271573
  import { setImmediate as setImmediateCb4 } from "node:timers";
271131
271574
  function createDiscoveryPluginLoaderOptions(scopedStore) {
@@ -271216,11 +271659,46 @@ async function reactToSpecificationComplete(deps) {
271216
271659
  return;
271217
271660
  }
271218
271661
  deps.log(`Specified ${deps.taskId} \u2192 todo`);
271219
- const live = await deps.getTask(deps.taskId);
271220
- if (!live || live.paused || live.userPaused) return;
271221
- const ir2 = await deps.resolveIr(live.id);
271222
- await deps.seed(live, ir2);
271223
- deps.kick();
271662
+ const sleep10 = deps.sleep ?? ((ms2) => new Promise((resolve110) => setTimeout(resolve110, ms2)));
271663
+ const QUIET_PARK_REASONS = /* @__PURE__ */ new Set([
271664
+ "no-pre-release-plan-review",
271665
+ "awaiting-approval",
271666
+ "paused",
271667
+ "plan-review-passed"
271668
+ ]);
271669
+ const RETRY_DELAYS_MS2 = [1e3, 5e3];
271670
+ for (let attempt = 0; ; attempt++) {
271671
+ let failure = null;
271672
+ try {
271673
+ const live = await deps.getTask(deps.taskId);
271674
+ if (!live || live.paused || live.userPaused) return;
271675
+ if (live.status === "needs-replan") {
271676
+ deps.log(`Plan Review handoff for ${deps.taskId} not armed (needs-replan)`);
271677
+ return;
271678
+ }
271679
+ const ir2 = await deps.resolveIr(live.id);
271680
+ const result2 = await deps.seed(live, ir2);
271681
+ if (result2.seeded) {
271682
+ deps.kick();
271683
+ return;
271684
+ }
271685
+ if (result2.reason && QUIET_PARK_REASONS.has(result2.reason)) {
271686
+ deps.log(`Plan Review handoff for ${deps.taskId} not armed (${result2.reason})`);
271687
+ return;
271688
+ }
271689
+ failure = result2.reason ?? "not-seeded";
271690
+ } catch (error3) {
271691
+ failure = error3 instanceof Error ? error3.message : String(error3);
271692
+ }
271693
+ if (attempt >= RETRY_DELAYS_MS2.length) {
271694
+ deps.warn(
271695
+ `Plan Review handoff for ${deps.taskId} failed after ${attempt + 1} attempts (${failure}) \u2014 stranded-continuation self-healing will recover it`
271696
+ );
271697
+ return;
271698
+ }
271699
+ deps.warn(`Plan Review handoff for ${deps.taskId} did not seed (${failure}) \u2014 retrying`);
271700
+ await sleep10(RETRY_DELAYS_MS2[attempt]);
271701
+ }
271224
271702
  }
271225
271703
  async function drainDuePlanningContinuations(deps) {
271226
271704
  const items = await deps.listDue();
@@ -271355,7 +271833,7 @@ function stableNotificationJson(value2) {
271355
271833
  return `{${fields.join(",")}}`;
271356
271834
  }
271357
271835
  function buildCliAgentNotificationDedupeKey(input) {
271358
- const notificationFingerprint = createHash31("sha256").update(stableNotificationJson(input.info.notification ?? null)).digest("hex").slice(0, 16);
271836
+ const notificationFingerprint = createHash32("sha256").update(stableNotificationJson(input.info.notification ?? null)).digest("hex").slice(0, 16);
271359
271837
  const waitingEpoch = input.session?.updatedAt ?? "unknown-waiting-epoch";
271360
271838
  return [
271361
271839
  "cli-agent",
@@ -271510,6 +271988,8 @@ var init_in_process_runtime = __esm({
271510
271988
  messageStore;
271511
271989
  /** FNXC:TaskDeleteNotice 2026-07-26-16:10: identity-guarded teardown for the delete-notice mailbox seam. */
271512
271990
  unregisterTaskDeleteNoticeMailbox;
271991
+ /** FNXC:TaskRecommendations 2026-08-13-03:56: identity-guarded teardown for the store-scoped recommendation notice seam. */
271992
+ unregisterTaskRecommendationNoticeMailbox;
271513
271993
  chatStore;
271514
271994
  detachAgentLinkSync;
271515
271995
  /**
@@ -271568,7 +272048,8 @@ var init_in_process_runtime = __esm({
271568
272048
  createTaskStoreForBackend: createTaskStoreForBackend2,
271569
272049
  buildConsumerId: buildConsumerId2,
271570
272050
  createProjectScopedPluginMcpProvider: createProjectScopedPluginMcpProvider2,
271571
- registerTaskDeleteNoticeMailbox: registerTaskDeleteNoticeMailbox2
272051
+ registerTaskDeleteNoticeMailbox: registerTaskDeleteNoticeMailbox2,
272052
+ registerTaskRecommendationNoticeMailbox: registerTaskRecommendationNoticeMailbox2
271572
272053
  } = await Promise.resolve().then(() => (init_src2(), src_exports));
271573
272054
  if (this.config.externalTaskStore) {
271574
272055
  this.taskStore = this.config.externalTaskStore;
@@ -271627,6 +272108,10 @@ var init_in_process_runtime = __esm({
271627
272108
  this.taskStore,
271628
272109
  this.messageStore
271629
272110
  );
272111
+ this.unregisterTaskRecommendationNoticeMailbox = registerTaskRecommendationNoticeMailbox2(
272112
+ this.taskStore,
272113
+ this.messageStore
272114
+ );
271630
272115
  await yieldEventLoop4();
271631
272116
  const pluginLayer = this.taskStore.getAsyncLayer();
271632
272117
  if (!pluginLayer) {
@@ -272118,9 +272603,19 @@ var init_in_process_runtime = __esm({
272118
272603
  outcome: report2.outcome,
272119
272604
  getTask: (id) => Promise.resolve(this.taskStore.getTask(id)),
272120
272605
  resolveIr: (id) => resolveWorkflowIrForTask(this.taskStore, id),
272121
- seed: (task, ir2) => seedPreReleasePlanReviewContinuation(this.taskStore, task, ir2),
272606
+ /*
272607
+ FNXC:PlanningHandoffAtomicity 2026-08-13-03:49:
272608
+ The report carries the planning session's own durable work item id so the
272609
+ seeder can retire that exact predecessor row atomically with the successor
272610
+ install. Without it the seeder raced triage's finally-block terminal
272611
+ transition and bailed on its own caller (529 stranded cards in 18 days).
272612
+ */
272613
+ seed: (task, ir2) => seedPreReleasePlanReviewContinuation(this.taskStore, task, ir2, {
272614
+ retirePredecessorId: report2.planningWorkItemId
272615
+ }),
272122
272616
  kick: () => this.kickWorkflowContinuationProcessor(),
272123
- log: (message) => runtimeLog.log(message)
272617
+ log: (message) => runtimeLog.log(message),
272618
+ warn: (message) => runtimeLog.warn(message)
272124
272619
  }).catch((error3) => {
272125
272620
  runtimeLog.error(`Failed to start Todo plan review for ${t.id}:`, error3);
272126
272621
  });
@@ -272408,6 +272903,8 @@ var init_in_process_runtime = __esm({
272408
272903
  this.backendShutdown = void 0;
272409
272904
  this.unregisterTaskDeleteNoticeMailbox?.();
272410
272905
  this.unregisterTaskDeleteNoticeMailbox = void 0;
272906
+ this.unregisterTaskRecommendationNoticeMailbox?.();
272907
+ this.unregisterTaskRecommendationNoticeMailbox = void 0;
272411
272908
  let stopError;
272412
272909
  try {
272413
272910
  if (this.workflowContinuationTimer) {
@@ -280201,9 +280698,9 @@ import { access as access10, unlink as unlink11, writeFile as writeFile33 } from
280201
280698
  import net2 from "node:net";
280202
280699
  import { platform as platform3, tmpdir as tmpdir12 } from "node:os";
280203
280700
  import { join as join91 } from "node:path";
280204
- import { createHash as createHash32 } from "node:crypto";
280701
+ import { createHash as createHash33 } from "node:crypto";
280205
280702
  function projectHash(projectId2) {
280206
- return createHash32("sha1").update(projectId2).digest("hex").slice(0, 16);
280703
+ return createHash33("sha1").update(projectId2).digest("hex").slice(0, 16);
280207
280704
  }
280208
280705
  function computeEngineSocketPath(projectId2) {
280209
280706
  const hash3 = projectHash(projectId2);
@@ -293422,6 +293919,13 @@ function sseDebug(message) {
293422
293919
  if (!isSseDebugEnabled()) return;
293423
293920
  sseLog.debug(message);
293424
293921
  }
293922
+ function resolveAgentActivityPollMs() {
293923
+ const raw = process.env.FUSION_AGENT_ACTIVITY_POLL_MS?.trim();
293924
+ if (!raw) return 2e3;
293925
+ const parsed = Number(raw);
293926
+ if (!Number.isFinite(parsed) || parsed <= 0) return 2e3;
293927
+ return Math.max(10, Math.floor(parsed));
293928
+ }
293425
293929
  function normalizeSSEClientId(value2) {
293426
293930
  const raw = Array.isArray(value2) ? value2[0] : value2;
293427
293931
  if (typeof raw !== "string") return void 0;
@@ -293729,7 +294233,7 @@ data: ${JSON.stringify(payload)}
293729
294233
  }
293730
294234
  void drainAgentActivity();
293731
294235
  };
293732
- const agentActivityPoll = setInterval(onAgentActivityNudge, 2e3);
294236
+ const agentActivityPoll = setInterval(onAgentActivityNudge, resolveAgentActivityPollMs());
293733
294237
  agentActivityPoll.unref?.();
293734
294238
  const onCreated = (task) => {
293735
294239
  send(`event: task:created
@@ -300006,9 +300510,9 @@ __export(import_translate_service_exports, {
300006
300510
  selectEligibleItems: () => selectEligibleItems,
300007
300511
  translateImportItems: () => translateImportItems
300008
300512
  });
300009
- import { createHash as createHash33 } from "node:crypto";
300513
+ import { createHash as createHash34 } from "node:crypto";
300010
300514
  function hashSourceContent(title2, body) {
300011
- return createHash33("sha256").update(`${title2}\0${body}`).digest("hex");
300515
+ return createHash34("sha256").update(`${title2}\0${body}`).digest("hex");
300012
300516
  }
300013
300517
  function resolveTargetLocale(settingsLocale, requestLocale, globalLanguage) {
300014
300518
  if (isLocale(settingsLocale)) return settingsLocale;
@@ -307010,7 +307514,7 @@ var init_auth_middleware = __esm({
307010
307514
  });
307011
307515
 
307012
307516
  // ../dashboard/src/routes/register-task-workflow-routes.ts
307013
- import { createHash as createHash34 } from "node:crypto";
307517
+ import { createHash as createHash35 } from "node:crypto";
307014
307518
  import { createReadStream } from "node:fs";
307015
307519
  import { readFile as readFile53, stat as stat13 } from "node:fs/promises";
307016
307520
  import { join as join96 } from "node:path";
@@ -307416,7 +307920,7 @@ function buildWorkflowReviewItemId(task, result2, findingId, resolution) {
307416
307920
  findingId,
307417
307921
  resolution
307418
307922
  });
307419
- return `workflow-review-${createHash34("sha256").update(identity).digest("hex").slice(0, 24)}`;
307923
+ return `workflow-review-${createHash35("sha256").update(identity).digest("hex").slice(0, 24)}`;
307420
307924
  }
307421
307925
  async function buildWorkflowReviewItems(task, store) {
307422
307926
  const declaredTopLevelReviewResultSources = await getDeclaredTopLevelReviewResultSources(task, store);
@@ -307865,6 +308369,32 @@ function registerTaskWorkflowRoutes(ctx, deps) {
307865
308369
  rethrowAsApiError8(err);
307866
308370
  }
307867
308371
  });
308372
+ router.get("/tasks/recommendations", async (req, res) => {
308373
+ try {
308374
+ const { store: scopedStore } = await getProjectContext2(req);
308375
+ const parsePageNumber = (value2, name2) => {
308376
+ if (value2 === void 0) return void 0;
308377
+ if (typeof value2 !== "string" || value2.trim() === "" || !Number.isInteger(Number(value2)) || Number(value2) < 0) {
308378
+ throw badRequest(`${name2} must be a non-negative integer`);
308379
+ }
308380
+ return Number(value2);
308381
+ };
308382
+ const requestedLimit = parsePageNumber(req.query.limit, "limit");
308383
+ const offset = parsePageNumber(req.query.offset, "offset");
308384
+ if (requestedLimit === 0) throw badRequest("limit must be a positive integer");
308385
+ const limit2 = requestedLimit === void 0 ? void 0 : Math.min(200, requestedLimit);
308386
+ let completeColumns;
308387
+ try {
308388
+ completeColumns = await resolveProjectColumnsForRoles(scopedStore, ["complete"]);
308389
+ } catch {
308390
+ completeColumns = /* @__PURE__ */ new Set(["done"]);
308391
+ }
308392
+ res.json(await scopedStore.listTaskRecommendations({ completeColumns, limit: limit2, offset }));
308393
+ } catch (err) {
308394
+ if (err instanceof ApiError) throw err;
308395
+ rethrowAsApiError8(err);
308396
+ }
308397
+ });
307868
308398
  router.post("/tasks/duplicate-check", async (req, res) => {
307869
308399
  try {
307870
308400
  const { store: scopedStore } = await getProjectContext2(req);
@@ -326179,6 +326709,7 @@ function registerMessagingScriptRoutes(ctx) {
326179
326709
  limit: parseInt(req.query.limit) || 20,
326180
326710
  offset: parseInt(req.query.offset) || 0,
326181
326711
  read: req.query.unreadOnly === "true" ? false : void 0,
326712
+ archived: req.query.archived === "true",
326182
326713
  type: req.query.type
326183
326714
  };
326184
326715
  const messages3 = await msgStore.getInbox(DASHBOARD_USER_ID, "user", filter2);
@@ -326197,6 +326728,7 @@ function registerMessagingScriptRoutes(ctx) {
326197
326728
  const filter2 = {
326198
326729
  limit: parseInt(req.query.limit) || 20,
326199
326730
  offset: parseInt(req.query.offset) || 0,
326731
+ archived: req.query.archived === "true",
326200
326732
  type: req.query.type
326201
326733
  };
326202
326734
  const messages3 = await msgStore.getOutbox(DASHBOARD_USER_ID, "user", filter2);
@@ -326313,7 +326845,8 @@ function registerMessagingScriptRoutes(ctx) {
326313
326845
  const msgStore = await getMessageStore(req);
326314
326846
  const messages3 = await msgStore.getConversation(
326315
326847
  { id: DASHBOARD_USER_ID, type: "user" },
326316
- { id: participantId, type: participantType }
326848
+ { id: participantId, type: participantType },
326849
+ { archived: req.query.archived === "true" }
326317
326850
  );
326318
326851
  res.json(messages3);
326319
326852
  } catch (err) {
@@ -326405,6 +326938,26 @@ function registerMessagingScriptRoutes(ctx) {
326405
326938
  rethrowAsApiError8(err);
326406
326939
  }
326407
326940
  });
326941
+ router.post("/messages/:id/archive", async (req, res) => {
326942
+ try {
326943
+ const message = await (await getMessageStore(req)).archiveMessage(req.params.id);
326944
+ res.json(message);
326945
+ } catch (err) {
326946
+ if (err instanceof ApiError) throw err;
326947
+ if ((err instanceof Error ? err.message : String(err)).includes("not found")) throw notFound(err instanceof Error ? err.message : String(err));
326948
+ rethrowAsApiError8(err);
326949
+ }
326950
+ });
326951
+ router.post("/messages/:id/unarchive", async (req, res) => {
326952
+ try {
326953
+ const message = await (await getMessageStore(req)).unarchiveMessage(req.params.id);
326954
+ res.json(message);
326955
+ } catch (err) {
326956
+ if (err instanceof ApiError) throw err;
326957
+ if ((err instanceof Error ? err.message : String(err)).includes("not found")) throw notFound(err instanceof Error ? err.message : String(err));
326958
+ rethrowAsApiError8(err);
326959
+ }
326960
+ });
326408
326961
  router.post("/messages/:id/read", async (req, res) => {
326409
326962
  try {
326410
326963
  const msgStore = await getMessageStore(req);
@@ -326438,7 +326991,7 @@ function registerMessagingScriptRoutes(ctx) {
326438
326991
  router.get("/agents/mailbox/all", async (req, res) => {
326439
326992
  try {
326440
326993
  const msgStore = await getMessageStore(req);
326441
- const messages3 = await msgStore.getAllAgentToAgentMessages();
326994
+ const messages3 = await msgStore.getAllAgentToAgentMessages({ archived: req.query.archived === "true" });
326442
326995
  const unreadCount = await msgStore.getUnreadAgentToAgentCount();
326443
326996
  res.json({ messages: messages3, total: messages3.length, unreadCount });
326444
326997
  } catch (err) {
@@ -326453,8 +327006,9 @@ function registerMessagingScriptRoutes(ctx) {
326453
327006
  const msgStore = await getMessageStore(req);
326454
327007
  const agentId = req.params.id;
326455
327008
  const mailbox = await msgStore.getMailbox(agentId, "agent");
326456
- const inbox = await msgStore.getInbox(agentId, "agent");
326457
- const outbox = await msgStore.getOutbox(agentId, "agent");
327009
+ const archived = req.query.archived === "true";
327010
+ const inbox = await msgStore.getInbox(agentId, "agent", { archived });
327011
+ const outbox = await msgStore.getOutbox(agentId, "agent", { archived });
326458
327012
  res.json({ ...mailbox, messages: inbox, inbox, outbox });
326459
327013
  } catch (err) {
326460
327014
  if (err instanceof ApiError) {
@@ -357028,8 +357582,8 @@ var init_register_settings_sync_routes = __esm({
357028
357582
  exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
357029
357583
  version: 1
357030
357584
  };
357031
- const { createHash: createHash38 } = await import("node:crypto");
357032
- const checksum = createHash38("sha256").update(JSON.stringify(payloadWithoutChecksum)).digest("hex");
357585
+ const { createHash: createHash39 } = await import("node:crypto");
357586
+ const checksum = createHash39("sha256").update(JSON.stringify(payloadWithoutChecksum)).digest("hex");
357033
357587
  const localPeerInfo = await central.getLocalPeerInfo();
357034
357588
  await fetchFromRemoteNode(node, "/api/settings/sync-receive", {
357035
357589
  method: "POST",
@@ -357100,7 +357654,7 @@ var init_register_settings_sync_routes = __esm({
357100
357654
  });
357101
357655
  return;
357102
357656
  }
357103
- const { createHash: createHash38 } = await import("node:crypto");
357657
+ const { createHash: createHash39 } = await import("node:crypto");
357104
357658
  const exportedAt = (/* @__PURE__ */ new Date()).toISOString();
357105
357659
  const payloadWithoutChecksum = {
357106
357660
  global: remoteSettings.global,
@@ -357109,7 +357663,7 @@ var init_register_settings_sync_routes = __esm({
357109
357663
  exportedAt,
357110
357664
  version: 1
357111
357665
  };
357112
- const checksum = createHash38("sha256").update(JSON.stringify(payloadWithoutChecksum)).digest("hex");
357666
+ const checksum = createHash39("sha256").update(JSON.stringify(payloadWithoutChecksum)).digest("hex");
357113
357667
  const result2 = await central.applyRemoteSettings({
357114
357668
  ...payloadWithoutChecksum,
357115
357669
  checksum
@@ -383863,7 +384417,7 @@ var init_report_scrub = __esm({
383863
384417
  });
383864
384418
 
383865
384419
  // ../dashboard/src/report-pipeline.ts
383866
- import { createHash as createHash35 } from "node:crypto";
384420
+ import { createHash as createHash36 } from "node:crypto";
383867
384421
  function resolveReportMode(actionType, settings) {
383868
384422
  return settings.reportModeByAction?.[actionType] ?? settings.reportMode ?? "draft-review";
383869
384423
  }
@@ -383979,7 +384533,7 @@ async function embedScreenshot(args) {
383979
384533
  try {
383980
384534
  const extension = EXT_BY_MIME[attachment.mimeType];
383981
384535
  if (!extension) return args.report;
383982
- const safeId2 = createHash35("sha256").update(`${args.report.sessionToken ?? ""}:${attachment.artifactId}`).digest("hex").slice(0, 32);
384536
+ const safeId2 = createHash36("sha256").update(`${args.report.sessionToken ?? ""}:${attachment.artifactId}`).digest("hex").slice(0, 32);
383983
384537
  const path14 = `.fusion-reports/${safeId2}/screenshot.${extension}`;
383984
384538
  const uploaded = await args.client.uploadImageAsset({ owner: args.owner, repo: args.repo, path: path14, contentBase64: Buffer.from(attachment.bytes).toString("base64"), message: "chore: add Fusion report screenshot", mimeType: attachment.mimeType });
383985
384539
  const alt = markdownEscapeImageAlt(scrubReportText(attachment.filename, args.scrubContext) || "Report screenshot");
@@ -387093,7 +387647,7 @@ var init_types6 = __esm({
387093
387647
  });
387094
387648
 
387095
387649
  // ../dashboard/src/stt/model-manager.ts
387096
- import { createHash as createHash36, randomUUID as randomUUID68 } from "node:crypto";
387650
+ import { createHash as createHash37, randomUUID as randomUUID68 } from "node:crypto";
387097
387651
  import { mkdir as mkdir40, open as open4, readFile as readFile63, readdir as readdir16, rename as rename12, rm as rm24, stat as stat18, writeFile as writeFile39 } from "node:fs/promises";
387098
387652
  import { join as join125, relative as relative26, resolve as resolve75, sep as sep20 } from "node:path";
387099
387653
  function parseSafeTarListing(listing) {
@@ -387182,7 +387736,7 @@ function createVoiceModelManager(options = {}) {
387182
387736
  if (!response.ok || !response.body) throw Object.assign(new Error("download failed"), { voiceReason: "network" });
387183
387737
  if (!current(g2, signal)) return state;
387184
387738
  const total = Number(response.headers.get("content-length")) || void 0;
387185
- const hash3 = createHash36("sha256");
387739
+ const hash3 = createHash37("sha256");
387186
387740
  let bytes = 0;
387187
387741
  for await (const chunk of response.body) {
387188
387742
  if (!current(g2, signal)) return state;
@@ -400841,7 +401395,7 @@ data: ${JSON.stringify({ type: event.type, data: event.data })}
400841
401395
  const endIso = new Date(nowMs).toISOString();
400842
401396
  const auditFilter = { startTime: startIso, endTime: endIso, limit: 5e4 };
400843
401397
  const asyncLayer = scopedStore.getAsyncLayer();
400844
- const runAuditEventsPromise = asyncLayer ? queryRunAuditEvents(asyncLayer.db, auditFilter).then((events) => events.map((event) => ({
401398
+ const runAuditEventsPromise = asyncLayer ? queryRunAuditEvents(asyncLayer.db, auditFilter, asyncLayer.projectId).then((events) => events.map((event) => ({
400845
401399
  ...event,
400846
401400
  domain: event.domain,
400847
401401
  mutationType: event.mutationType,
@@ -402428,7 +402982,7 @@ var init_extension = __esm({
402428
402982
  });
402429
402983
 
402430
402984
  // src/commands/task-lifecycle.ts
402431
- import { createHash as createHash37 } from "node:crypto";
402985
+ import { createHash as createHash38 } from "node:crypto";
402432
402986
  import { exec as exec63 } from "node:child_process";
402433
402987
  import * as childProcess5 from "node:child_process";
402434
402988
  import { mkdir as mkdir43, realpath as realpath7, rm as rm25 } from "node:fs/promises";
@@ -402601,7 +403155,7 @@ async function refreshAutomatedPrHead(input) {
402601
403155
  await mkdir43(refreshWorktreesDir, { recursive: true });
402602
403156
  temporary = join135(
402603
403157
  refreshWorktreesDir,
402604
- `pr-refresh-${createHash37("sha256").update(input.headBranch).digest("hex").slice(0, 16)}`
403158
+ `pr-refresh-${createHash38("sha256").update(input.headBranch).digest("hex").slice(0, 16)}`
402605
403159
  );
402606
403160
  const reservationDir = join135(root, ".fusion", "pr-refresh-reservations");
402607
403161
  await mkdir43(reservationDir, { recursive: true });