@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/extension.js CHANGED
@@ -2067,6 +2067,8 @@ var init_settings_schema = __esm({
2067
2067
  DEFAULT_PROJECT_SETTINGS = {
2068
2068
  // FNXC:TaskRecommendations 2026-08-08-05:02: completion follows-ups stay bounded by default; 0 disables writing them.
2069
2069
  maxRecommendationsPerTask: 3,
2070
+ // FNXC:TaskRecommendations 2026-08-13-03:56: surface completed-task proposals by default; operators can suppress the notice without suppressing capture.
2071
+ recommendationMailboxNoticeEnabled: true,
2070
2072
  globalPause: false,
2071
2073
  globalPauseReason: void 0,
2072
2074
  defaultWorkflowId: void 0,
@@ -21639,9 +21641,14 @@ var init_project = __esm({
21639
21641
  // now keyed per-project on `project_id` (the effective PK). `id` is retained
21640
21642
  // for column-shape parity (always 1) but is no longer the PK and no longer
21641
21643
  // CHECK-constrained. Single-project / SQLite-parity callers leave project_id
21642
- // at its '' default (one row), preserving the pre-isolation behavior.
21644
+ // through the database ownership trigger, preserving the pre-isolation behavior.
21645
+ /*
21646
+ FNXC:MultiProjectIsolation 2026-08-12-15:43:
21647
+ Migration 0006 owns config's project_id default and leaves its catalog PK as (project_id).
21648
+ Drizzle emits DEFAULT while the database GUC/trigger selects the effective ownership partition.
21649
+ */
21643
21650
  id: integer("id").default(1),
21644
- projectId: text("project_id").notNull().default("").primaryKey(),
21651
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`).primaryKey(),
21645
21652
  nextId: integer("next_id").default(1),
21646
21653
  nextWorkflowStepId: integer("next_workflow_step_id").default(1),
21647
21654
  // FNXC:SqliteFinalRemoval 2026-06-28:
@@ -22035,9 +22042,13 @@ var init_project = __esm({
22035
22042
  automations = projectSchema.table("automations", {
22036
22043
  /*
22037
22044
  * FNXC:AutomationIsolation 2026-07-13-22:37:
22038
- * 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.
22045
+ * 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.
22046
+ *
22047
+ * FNXC:MultiProjectIsolation 2026-08-12-15:43:
22048
+ * Migration 0006 owns the default through the database GUC/trigger; Drizzle emits DEFAULT
22049
+ * without replacing the physical legacy fallback or the fail-closed bound writer contract.
22039
22050
  */
22040
- projectId: text("project_id").notNull().default(""),
22051
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
22041
22052
  id: text("id").notNull(),
22042
22053
  name: text("name").notNull(),
22043
22054
  description: text("description"),
@@ -22336,7 +22347,8 @@ var init_project = __esm({
22336
22347
  index("idxTaskDocumentsTaskId").on(t.taskId)
22337
22348
  ]);
22338
22349
  artifacts = projectSchema.table("artifacts", {
22339
- id: text("id").primaryKey(),
22350
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
22351
+ id: text("id").notNull(),
22340
22352
  type: text("type").notNull(),
22341
22353
  title: text("title").notNull(),
22342
22354
  description: text("description"),
@@ -22351,7 +22363,8 @@ var init_project = __esm({
22351
22363
  createdAt: text("created_at").notNull(),
22352
22364
  updatedAt: text("updated_at").notNull()
22353
22365
  }, (t) => [
22354
- foreignKey({ columns: [t.taskId], foreignColumns: [tasks.id] }).onDelete("cascade"),
22366
+ primaryKey({ columns: [t.projectId, t.id] }),
22367
+ foreignKey({ name: "artifacts_task_id_fkey", columns: [t.projectId, t.taskId], foreignColumns: [tasks.projectId, tasks.id] }).onDelete("cascade"),
22355
22368
  index("idxArtifactsTaskId").on(t.taskId),
22356
22369
  index("idxArtifactsAuthorId").on(t.authorId),
22357
22370
  index("idxArtifactsType").on(t.type),
@@ -22541,7 +22554,8 @@ var init_project = __esm({
22541
22554
  index("idxEvalRunEventsRunIdSeq").on(t.runId, t.seq)
22542
22555
  ]);
22543
22556
  secrets = projectSchema.table("secrets", {
22544
- id: text("id").primaryKey(),
22557
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
22558
+ id: text("id").notNull(),
22545
22559
  key: text("key").notNull(),
22546
22560
  valueCiphertext: bytea("value_ciphertext").notNull(),
22547
22561
  nonce: bytea("nonce").notNull(),
@@ -22554,7 +22568,8 @@ var init_project = __esm({
22554
22568
  lastReadAt: text("last_read_at"),
22555
22569
  lastReadBy: text("last_read_by")
22556
22570
  }, (t) => [
22557
- unique("secrets_key_unique").on(t.key),
22571
+ primaryKey({ columns: [t.projectId, t.id] }),
22572
+ unique("secrets_key_unique").on(t.projectId, t.key),
22558
22573
  check("secrets_access_policy_check", sql`${t.accessPolicy} IN ('auto', 'prompt', 'deny')`),
22559
22574
  check("secrets_env_exportable_check", sql`${t.envExportable} IN (0, 1)`)
22560
22575
  ]);
@@ -22596,10 +22611,11 @@ var init_project = __esm({
22596
22611
  updatedAt: text("updated_at").notNull()
22597
22612
  }, (t) => [primaryKey({ columns: [t.projectId, t.id] })]);
22598
22613
  branchGroups = projectSchema.table("branch_groups", {
22599
- id: text("id").primaryKey(),
22614
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
22615
+ id: text("id").notNull(),
22600
22616
  sourceType: text("source_type").notNull(),
22601
22617
  sourceId: text("source_id").notNull(),
22602
- branchName: text("branch_name").notNull().unique(),
22618
+ branchName: text("branch_name").notNull(),
22603
22619
  worktreePath: text("worktree_path"),
22604
22620
  autoMerge: integer("auto_merge").notNull().default(0),
22605
22621
  prState: text("pr_state").notNull().default("none"),
@@ -22615,6 +22631,8 @@ var init_project = __esm({
22615
22631
  updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
22616
22632
  closedAt: bigint("closed_at", { mode: "number" })
22617
22633
  }, (t) => [
22634
+ primaryKey({ columns: [t.projectId, t.id] }),
22635
+ unique("branch_groups_branch_name_key").on(t.projectId, t.branchName),
22618
22636
  check("branch_groups_source_type_check", sql`${t.sourceType} IN ('mission','planning','new-task')`),
22619
22637
  check("branch_groups_pr_state_check", sql`${t.prState} IN ('none','open','merged','closed')`),
22620
22638
  check("branch_groups_status_check", sql`${t.status} IN ('open','finalized','abandoned')`),
@@ -23016,12 +23034,14 @@ var init_project = __esm({
23016
23034
  index("idxUsageEventsKindTs").on(t.kind, t.ts)
23017
23035
  ]);
23018
23036
  pluginActivations = projectSchema.table("plugin_activations", {
23019
- id: integer("id").generatedAlwaysAsIdentity().primaryKey(),
23037
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23038
+ id: integer("id").generatedAlwaysAsIdentity().notNull(),
23020
23039
  pluginId: text("plugin_id").notNull(),
23021
23040
  source: text("source").notNull(),
23022
23041
  pluginVersion: text("plugin_version"),
23023
23042
  activatedAt: text("activated_at").notNull()
23024
23043
  }, (t) => [
23044
+ primaryKey({ columns: [t.projectId, t.id] }),
23025
23045
  index("idxPluginActivationsActivatedAt").on(t.activatedAt),
23026
23046
  index("idxPluginActivationsPluginId").on(t.pluginId)
23027
23047
  ]);
@@ -23046,8 +23066,13 @@ var init_project = __esm({
23046
23066
  index("idxKnowledgePagesUpdatedAt").on(t.updatedAt)
23047
23067
  ]);
23048
23068
  deployments = projectSchema.table("deployments", {
23049
- id: integer("id").generatedAlwaysAsIdentity().primaryKey(),
23050
- projectId: text("project_id").notNull().default(""),
23069
+ id: integer("id").generatedAlwaysAsIdentity().notNull(),
23070
+ /*
23071
+ FNXC:MultiProjectIsolation 2026-08-12-15:43:
23072
+ Migration 0006 rebuilt deployments_pkey as (project_id, id). Drizzle emits DEFAULT while the
23073
+ database GUC/trigger keeps ownership stamping and the physical legacy fallback authoritative.
23074
+ */
23075
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23051
23076
  deploymentId: text("deployment_id").notNull(),
23052
23077
  service: text("service"),
23053
23078
  environment: text("environment"),
@@ -23058,6 +23083,7 @@ var init_project = __esm({
23058
23083
  meta: jsonb("meta"),
23059
23084
  createdAt: text("created_at").notNull()
23060
23085
  }, (t) => [
23086
+ primaryKey({ columns: [t.projectId, t.id], name: "deployments_pkey" }),
23061
23087
  uniqueIndex("idxDeploymentsProjectDeploymentId").on(t.projectId, t.deploymentId),
23062
23088
  index("idxDeploymentsProjectDeployedAt").on(t.projectId, t.deployedAt),
23063
23089
  index("idxDeploymentsDeployedAt").on(t.deployedAt),
@@ -23065,7 +23091,12 @@ var init_project = __esm({
23065
23091
  ]);
23066
23092
  githubCheckStates = projectSchema.table("github_check_states", {
23067
23093
  id: integer("id").generatedAlwaysAsIdentity().notNull(),
23068
- projectId: text("project_id").notNull().default(""),
23094
+ /*
23095
+ FNXC:MultiProjectIsolation 2026-08-12-15:43:
23096
+ Migration 0048 post-dated 0006, so FN-9004 reconciles its physical default to the ownership
23097
+ expression. Drizzle emits DEFAULT while the database GUC/trigger remains the stamping authority.
23098
+ */
23099
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23069
23100
  repo: text("repo").notNull(),
23070
23101
  headSha: text("head_sha").notNull(),
23071
23102
  checkName: text("check_name").notNull(),
@@ -23085,9 +23116,14 @@ var init_project = __esm({
23085
23116
  index("idxGithubCheckStatesProjectReceived").on(t.projectId, t.receivedAt)
23086
23117
  ]);
23087
23118
  incidents = projectSchema.table("incidents", {
23088
- id: integer("id").generatedAlwaysAsIdentity().primaryKey(),
23089
- projectId: text("project_id").notNull().default(""),
23090
- incidentId: text("incident_id").notNull().unique(),
23119
+ id: integer("id").generatedAlwaysAsIdentity().notNull(),
23120
+ /*
23121
+ FNXC:MultiProjectIsolation 2026-08-12-15:43:
23122
+ Migration 0006 rebuilt incident keys with project_id first. Drizzle emits DEFAULT while the
23123
+ database GUC/trigger retains ownership stamping and its physical legacy fallback.
23124
+ */
23125
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23126
+ incidentId: text("incident_id").notNull(),
23091
23127
  groupingKey: text("grouping_key").notNull(),
23092
23128
  title: text("title").notNull(),
23093
23129
  severity: text("severity"),
@@ -23101,6 +23137,8 @@ var init_project = __esm({
23101
23137
  createdAt: text("created_at").notNull(),
23102
23138
  updatedAt: text("updated_at").notNull()
23103
23139
  }, (t) => [
23140
+ primaryKey({ columns: [t.projectId, t.id], name: "incidents_pkey" }),
23141
+ unique("incidents_incident_id_key").on(t.projectId, t.incidentId),
23104
23142
  index("idxIncidentsProjectOpenedAt").on(t.projectId, t.openedAt),
23105
23143
  index("idxIncidentsProjectStatus").on(t.projectId, t.status),
23106
23144
  index("idxIncidentsGroupingKey").on(t.groupingKey),
@@ -23155,12 +23193,14 @@ var init_project = __esm({
23155
23193
  content: text("content").notNull(),
23156
23194
  type: text("type").notNull(),
23157
23195
  read: integer("read").default(0),
23196
+ archived: integer("archived").default(0),
23158
23197
  metadata: jsonb("metadata"),
23159
23198
  createdAt: text("created_at").notNull(),
23160
23199
  updatedAt: text("updated_at").notNull()
23161
23200
  }, (t) => [
23162
23201
  primaryKey({ columns: [t.projectId, t.id] }),
23163
23202
  index("idxMessagesTo").on(t.toId, t.toType, t.read),
23203
+ index("idxMessagesToArchived").on(t.toId, t.toType, t.archived),
23164
23204
  index("idxMessagesFrom").on(t.fromId, t.fromType),
23165
23205
  index("idxMessagesCreatedAt").on(t.createdAt)
23166
23206
  ]);
@@ -23261,7 +23301,8 @@ var init_project = __esm({
23261
23301
  index("idx_cli_sessions_project_state").on(t.projectId, t.agentState)
23262
23302
  ]);
23263
23303
  chatMessages = projectSchema.table("chat_messages", {
23264
- id: text("id").primaryKey(),
23304
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23305
+ id: text("id").notNull(),
23265
23306
  sessionId: text("session_id").notNull(),
23266
23307
  role: text("role").notNull(),
23267
23308
  content: text("content").notNull(),
@@ -23270,6 +23311,7 @@ var init_project = __esm({
23270
23311
  createdAt: text("created_at").notNull(),
23271
23312
  attachments: jsonb("attachments")
23272
23313
  }, (t) => [
23314
+ primaryKey({ columns: [t.projectId, t.id] }),
23273
23315
  index("idxChatMessagesSessionId").on(t.sessionId),
23274
23316
  index("idxChatMessagesCreatedAt").on(t.createdAt)
23275
23317
  ]);
@@ -23295,7 +23337,8 @@ var init_project = __esm({
23295
23337
  index("idxChatTokenUsageCreatedAt").on(t.createdAt)
23296
23338
  ]);
23297
23339
  runAuditEvents = projectSchema.table("run_audit_events", {
23298
- id: text("id").primaryKey(),
23340
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23341
+ id: text("id").notNull(),
23299
23342
  timestamp: text("timestamp").notNull(),
23300
23343
  taskId: text("task_id"),
23301
23344
  agentId: text("agent_id").notNull(),
@@ -23305,6 +23348,7 @@ var init_project = __esm({
23305
23348
  target: text("target").notNull(),
23306
23349
  metadata: jsonb("metadata")
23307
23350
  }, (t) => [
23351
+ primaryKey({ columns: [t.projectId, t.id] }),
23308
23352
  index("idxRunAuditEventsRunIdTimestamp").on(t.runId, t.timestamp),
23309
23353
  index("idxRunAuditEventsTaskIdTimestamp").on(t.taskId, t.timestamp),
23310
23354
  index("idxRunAuditEventsTimestamp").on(t.timestamp)
@@ -23405,13 +23449,14 @@ var init_project = __esm({
23405
23449
  index("idxMissionLineageStopsMissionId").on(t.projectId, t.missionId)
23406
23450
  ]);
23407
23451
  verificationCache = projectSchema.table("verification_cache", {
23452
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23408
23453
  treeSha: text("tree_sha").notNull(),
23409
23454
  testCommand: text("test_command").notNull().default(""),
23410
23455
  buildCommand: text("build_command").notNull().default(""),
23411
23456
  recordedAt: text("recorded_at").notNull(),
23412
23457
  taskId: text("task_id")
23413
23458
  }, (t) => [
23414
- primaryKey({ columns: [t.treeSha, t.testCommand, t.buildCommand] }),
23459
+ primaryKey({ columns: [t.projectId, t.treeSha, t.testCommand, t.buildCommand] }),
23415
23460
  index("idxVerificationCacheRecordedAt").on(t.recordedAt)
23416
23461
  ]);
23417
23462
  importTranslationCache = projectSchema.table("import_translation_cache", {
@@ -23444,7 +23489,8 @@ var init_project = __esm({
23444
23489
  index("idxImportTranslationCacheRecordedAt").on(t.recordedAt)
23445
23490
  ]);
23446
23491
  approvalRequests = projectSchema.table("approval_requests", {
23447
- id: text("id").primaryKey(),
23492
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23493
+ id: text("id").notNull(),
23448
23494
  status: text("status").notNull(),
23449
23495
  requesterActorId: text("requester_actor_id").notNull(),
23450
23496
  requesterActorType: text("requester_actor_type").notNull(),
@@ -23463,13 +23509,14 @@ var init_project = __esm({
23463
23509
  createdAt: text("created_at").notNull(),
23464
23510
  updatedAt: text("updated_at").notNull()
23465
23511
  }, (t) => [
23512
+ primaryKey({ columns: [t.projectId, t.id] }),
23466
23513
  index("idxApprovalRequestsStatusCreatedAt").on(t.status, t.createdAt),
23467
23514
  index("idxApprovalRequestsRequesterCreatedAt").on(t.requesterActorId, t.createdAt),
23468
23515
  index("idxApprovalRequestsTaskCreatedAt").on(t.taskId, t.createdAt)
23469
23516
  ]);
23470
23517
  approvalRequestAuditEvents = projectSchema.table("approval_request_audit_events", {
23471
- projectId: text("project_id").notNull().default(""),
23472
- id: text("id").primaryKey(),
23518
+ projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
23519
+ id: text("id").notNull(),
23473
23520
  requestId: text("request_id").notNull(),
23474
23521
  eventType: text("event_type").notNull(),
23475
23522
  actorId: text("actor_id").notNull(),
@@ -23478,6 +23525,7 @@ var init_project = __esm({
23478
23525
  note: text("note"),
23479
23526
  createdAt: text("created_at").notNull()
23480
23527
  }, (t) => [
23528
+ primaryKey({ columns: [t.projectId, t.id] }),
23481
23529
  index("idxApprovalRequestAuditRequestCreatedAt").on(t.requestId, t.createdAt, t.id),
23482
23530
  index("idxApprovalRequestAuditProjectCreatedAt").on(t.projectId, t.createdAt)
23483
23531
  ]);
@@ -26967,6 +27015,27 @@ var init_grok_provider = __esm({
26967
27015
  apiKey: "$GROK_API_KEY",
26968
27016
  api: "openai-completions",
26969
27017
  models: [
27018
+ /*
27019
+ FNXC:ModelRegistry 2026-08-12-20:17:
27020
+ FN-9008 requires Grok 4.6 to be selectable in every model area. Keep it in this shared
27021
+ catalog because pi's registerProvider() replaces provider model lists wholesale, while every
27022
+ Fusion host seeds and re-merges this export. xAI's model docs verify grok-4.6, its 500k-token
27023
+ context window, and configurable reasoning; max output and per-model image input remain
27024
+ upstream-pending-verification, so retain the conservative text-only and 65536-token defaults.
27025
+ Source: https://docs.x.ai/developers/models
27026
+ */
27027
+ {
27028
+ id: "grok-4.6",
27029
+ name: "Grok 4.6",
27030
+ reasoning: true,
27031
+ input: ["text"],
27032
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
27033
+ contextWindow: 5e5,
27034
+ maxTokens: 65536,
27035
+ compat: {
27036
+ supportsDeveloperRole: false
27037
+ }
27038
+ },
26970
27039
  {
26971
27040
  id: "grok-4.5",
26972
27041
  name: "Grok 4.5",
@@ -34925,6 +34994,7 @@ function rowToMessage(row) {
34925
34994
  content: row.content,
34926
34995
  type: row.type,
34927
34996
  read: (row.read ?? 0) === 1,
34997
+ archived: (row.archived ?? 0) === 1,
34928
34998
  metadata: row.metadata ?? void 0,
34929
34999
  createdAt: row.createdAt,
34930
35000
  updatedAt: row.updatedAt
@@ -34936,6 +35006,11 @@ function participantIdsForLookup(ownerId, ownerType) {
34936
35006
  }
34937
35007
  return [ownerId];
34938
35008
  }
35009
+ function archivedCondition(archived) {
35010
+ if (archived === true) return eq(project_exports.messages.archived, 1);
35011
+ if (archived === false) return or(eq(project_exports.messages.archived, 0), sql`${project_exports.messages.archived} IS NULL`);
35012
+ return or(eq(project_exports.messages.archived, 0), sql`${project_exports.messages.archived} IS NULL`);
35013
+ }
34939
35014
  async function sendMessage(handle, message) {
34940
35015
  const sanitizedContent = sanitizeTextValue(message.content);
34941
35016
  const sanitizedMetadata = sanitizeJsonbValue(message.metadata);
@@ -34948,6 +35023,7 @@ async function sendMessage(handle, message) {
34948
35023
  content: sanitizedContent,
34949
35024
  type: message.type,
34950
35025
  read: message.read ? 1 : 0,
35026
+ archived: message.archived ? 1 : 0,
34951
35027
  metadata: sanitizedMetadata,
34952
35028
  createdAt: message.createdAt,
34953
35029
  updatedAt: message.updatedAt
@@ -34956,7 +35032,8 @@ async function sendMessage(handle, message) {
34956
35032
  ...message,
34957
35033
  content: sanitizedContent,
34958
35034
  metadata: sanitizedMetadata,
34959
- read: message.read ? 1 : 0
35035
+ read: message.read ? 1 : 0,
35036
+ archived: message.archived ? 1 : 0
34960
35037
  });
34961
35038
  }
34962
35039
  async function sendMessageOnce(handle, message) {
@@ -34969,6 +35046,7 @@ async function sendMessageOnce(handle, message) {
34969
35046
  content: message.content,
34970
35047
  type: message.type,
34971
35048
  read: message.read ? 1 : 0,
35049
+ archived: message.archived ? 1 : 0,
34972
35050
  metadata: message.metadata,
34973
35051
  createdAt: message.createdAt,
34974
35052
  updatedAt: message.updatedAt
@@ -35020,6 +35098,7 @@ async function queryMessagesByParticipant(handle, direction, ownerId, ownerType,
35020
35098
  if (filter?.read !== void 0) {
35021
35099
  conditions.push(eq(project_exports.messages.read, filter.read ? 1 : 0));
35022
35100
  }
35101
+ conditions.push(archivedCondition(filter?.archived));
35023
35102
  const limit = filter?.limit ?? 100;
35024
35103
  const offset = filter?.offset ?? 0;
35025
35104
  const rows = await handle.select(messageColumns).from(project_exports.messages).where(and(...conditions)).orderBy(desc(project_exports.messages.createdAt), desc(project_exports.messages.id)).limit(limit).offset(offset);
@@ -35033,6 +35112,13 @@ async function markMessageAsRead(handle, messageId) {
35033
35112
  await handle.update(project_exports.messages).set({ read: 1, updatedAt: now }).where(eq(project_exports.messages.id, messageId));
35034
35113
  return await getMessage(handle, messageId);
35035
35114
  }
35115
+ async function setMessageArchived(handle, id, archived) {
35116
+ const existing = await getMessage(handle, id);
35117
+ if (!existing) return null;
35118
+ if (existing.archived === archived) return existing;
35119
+ 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);
35120
+ return rows[0] ? rowToMessage(rows[0]) : null;
35121
+ }
35036
35122
  async function markAllMessagesAsRead(handle, ownerId, ownerType) {
35037
35123
  const now = (/* @__PURE__ */ new Date()).toISOString();
35038
35124
  const participantIds = participantIdsForLookup(ownerId, ownerType);
@@ -35040,7 +35126,8 @@ async function markAllMessagesAsRead(handle, ownerId, ownerType) {
35040
35126
  and(
35041
35127
  inArray(project_exports.messages.toId, participantIds),
35042
35128
  eq(project_exports.messages.toType, ownerType),
35043
- eq(project_exports.messages.read, 0)
35129
+ eq(project_exports.messages.read, 0),
35130
+ archivedCondition()
35044
35131
  )
35045
35132
  );
35046
35133
  const count2 = countRows[0]?.count ?? 0;
@@ -35048,7 +35135,8 @@ async function markAllMessagesAsRead(handle, ownerId, ownerType) {
35048
35135
  and(
35049
35136
  inArray(project_exports.messages.toId, participantIds),
35050
35137
  eq(project_exports.messages.toType, ownerType),
35051
- eq(project_exports.messages.read, 0)
35138
+ eq(project_exports.messages.read, 0),
35139
+ archivedCondition()
35052
35140
  )
35053
35141
  );
35054
35142
  return count2;
@@ -35070,19 +35158,22 @@ async function getConversation(handle, participantA, participantB, options) {
35070
35158
  const aIds = participantIdsForLookup(participantA.id, participantA.type);
35071
35159
  const bIds = participantIdsForLookup(participantB.id, participantB.type);
35072
35160
  const rows = await handle.select(messageColumns).from(project_exports.messages).where(
35073
- or(
35074
- and(
35075
- inArray(project_exports.messages.fromId, aIds),
35076
- eq(project_exports.messages.fromType, participantA.type),
35077
- inArray(project_exports.messages.toId, bIds),
35078
- eq(project_exports.messages.toType, participantB.type)
35161
+ and(
35162
+ or(
35163
+ and(
35164
+ inArray(project_exports.messages.fromId, aIds),
35165
+ eq(project_exports.messages.fromType, participantA.type),
35166
+ inArray(project_exports.messages.toId, bIds),
35167
+ eq(project_exports.messages.toType, participantB.type)
35168
+ ),
35169
+ and(
35170
+ inArray(project_exports.messages.fromId, bIds),
35171
+ eq(project_exports.messages.fromType, participantB.type),
35172
+ inArray(project_exports.messages.toId, aIds),
35173
+ eq(project_exports.messages.toType, participantA.type)
35174
+ )
35079
35175
  ),
35080
- and(
35081
- inArray(project_exports.messages.fromId, bIds),
35082
- eq(project_exports.messages.fromType, participantB.type),
35083
- inArray(project_exports.messages.toId, aIds),
35084
- eq(project_exports.messages.toType, participantA.type)
35085
- )
35176
+ archivedCondition(options?.archived)
35086
35177
  )
35087
35178
  ).orderBy(desc(project_exports.messages.createdAt)).limit(limit);
35088
35179
  return rows.reverse().map((row) => rowToMessage(row));
@@ -35093,14 +35184,16 @@ async function getMailbox(handle, ownerId, ownerType) {
35093
35184
  and(
35094
35185
  inArray(project_exports.messages.toId, participantIds),
35095
35186
  eq(project_exports.messages.toType, ownerType),
35096
- eq(project_exports.messages.read, 0)
35187
+ eq(project_exports.messages.read, 0),
35188
+ archivedCondition()
35097
35189
  )
35098
35190
  );
35099
35191
  const unreadCount = unreadRows[0]?.count ?? 0;
35100
35192
  const lastRows = await handle.select(messageColumns).from(project_exports.messages).where(
35101
35193
  and(
35102
35194
  inArray(project_exports.messages.toId, participantIds),
35103
- eq(project_exports.messages.toType, ownerType)
35195
+ eq(project_exports.messages.toType, ownerType),
35196
+ archivedCondition()
35104
35197
  )
35105
35198
  ).orderBy(desc(project_exports.messages.createdAt), desc(project_exports.messages.id)).limit(1);
35106
35199
  return {
@@ -35110,15 +35203,16 @@ async function getMailbox(handle, ownerId, ownerType) {
35110
35203
  lastMessage: lastRows[0] ? rowToMessage(lastRows[0]) : void 0
35111
35204
  };
35112
35205
  }
35113
- async function getAllAgentToAgentMessages(handle) {
35114
- 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));
35206
+ async function getAllAgentToAgentMessages(handle, filter) {
35207
+ const rows = await handle.select(messageColumns).from(project_exports.messages).where(and(eq(project_exports.messages.type, "agent-to-agent"), archivedCondition(filter?.archived))).orderBy(desc(project_exports.messages.createdAt), desc(project_exports.messages.id));
35115
35208
  return rows.map((row) => rowToMessage(row));
35116
35209
  }
35117
35210
  async function getUnreadAgentToAgentCount(handle) {
35118
35211
  const rows = await handle.select({ count: sql`count(*)::int` }).from(project_exports.messages).where(
35119
35212
  and(
35120
35213
  eq(project_exports.messages.type, "agent-to-agent"),
35121
- eq(project_exports.messages.read, 0)
35214
+ eq(project_exports.messages.read, 0),
35215
+ archivedCondition()
35122
35216
  )
35123
35217
  );
35124
35218
  return rows[0]?.count ?? 0;
@@ -35140,6 +35234,7 @@ var init_async_message_store = __esm({
35140
35234
  content: project_exports.messages.content,
35141
35235
  type: project_exports.messages.type,
35142
35236
  read: project_exports.messages.read,
35237
+ archived: project_exports.messages.archived,
35143
35238
  metadata: project_exports.messages.metadata,
35144
35239
  createdAt: project_exports.messages.createdAt,
35145
35240
  updatedAt: project_exports.messages.updatedAt
@@ -35325,12 +35420,12 @@ var init_message_store = __esm({
35325
35420
  }
35326
35421
  const sqliteDb = this.db;
35327
35422
  this.stmtInsert = sqliteDb.prepare(`
35328
- INSERT INTO messages (id, fromId, fromType, toId, toType, content, type, read, metadata, createdAt, updatedAt)
35329
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
35423
+ INSERT INTO messages (id, fromId, fromType, toId, toType, content, type, read, archived, metadata, createdAt, updatedAt)
35424
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
35330
35425
  `);
35331
35426
  this.stmtInsertOnce = sqliteDb.prepare(`
35332
- INSERT OR IGNORE INTO messages (id, fromId, fromType, toId, toType, content, type, read, metadata, createdAt, updatedAt)
35333
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
35427
+ INSERT OR IGNORE INTO messages (id, fromId, fromType, toId, toType, content, type, read, archived, metadata, createdAt, updatedAt)
35428
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
35334
35429
  `);
35335
35430
  this.stmtGetById = sqliteDb.prepare(`
35336
35431
  SELECT * FROM messages WHERE id = ?
@@ -35368,6 +35463,7 @@ var init_message_store = __esm({
35368
35463
  content: row.content,
35369
35464
  type: row.type,
35370
35465
  read: row.read === 1,
35466
+ archived: (row.archived ?? 0) === 1,
35371
35467
  metadata: fromJson(row.metadata),
35372
35468
  createdAt: row.createdAt,
35373
35469
  updatedAt: row.updatedAt
@@ -35401,6 +35497,7 @@ var init_message_store = __esm({
35401
35497
  content: sanitizeTextValue(input.content),
35402
35498
  type: input.type,
35403
35499
  read: false,
35500
+ archived: false,
35404
35501
  metadata: sanitizeJsonbValue(input.metadata),
35405
35502
  createdAt: now,
35406
35503
  updatedAt: now
@@ -35416,6 +35513,7 @@ var init_message_store = __esm({
35416
35513
  content: message.content,
35417
35514
  type: message.type,
35418
35515
  read: message.read,
35516
+ archived: message.archived,
35419
35517
  metadata: message.metadata ?? null,
35420
35518
  createdAt: message.createdAt,
35421
35519
  updatedAt: message.updatedAt
@@ -35430,6 +35528,7 @@ var init_message_store = __esm({
35430
35528
  message.content,
35431
35529
  message.type,
35432
35530
  message.read ? 1 : 0,
35531
+ message.archived ? 1 : 0,
35433
35532
  toJsonNullable(message.metadata),
35434
35533
  message.createdAt,
35435
35534
  message.updatedAt
@@ -35488,6 +35587,7 @@ var init_message_store = __esm({
35488
35587
  content: sanitizeTextValue(input.content),
35489
35588
  type: input.type,
35490
35589
  read: false,
35590
+ archived: false,
35491
35591
  metadata: sanitizeJsonbValue(input.metadata),
35492
35592
  createdAt: now,
35493
35593
  updatedAt: now
@@ -35508,6 +35608,7 @@ var init_message_store = __esm({
35508
35608
  message.content,
35509
35609
  message.type,
35510
35610
  0,
35611
+ 0,
35511
35612
  toJsonNullable(message.metadata),
35512
35613
  message.createdAt,
35513
35614
  message.updatedAt
@@ -35639,6 +35740,7 @@ var init_message_store = __esm({
35639
35740
  whereClauses.push("read = ?");
35640
35741
  params.push(filter.read ? 1 : 0);
35641
35742
  }
35743
+ whereClauses.push(filter?.archived === true ? "archived = 1" : "(archived = 0 OR archived IS NULL)");
35642
35744
  const whereSql = whereClauses.join(" AND ");
35643
35745
  const limit = filter?.limit ?? 100;
35644
35746
  const offset = filter?.offset ?? 0;
@@ -35689,15 +35791,39 @@ var init_message_store = __esm({
35689
35791
  const participantIds = this.getParticipantIdsForLookup(ownerId, ownerType);
35690
35792
  const toIdPredicate = participantIds.length === 1 ? "toId = ?" : `toId IN (${participantIds.map(() => "?").join(", ")})`;
35691
35793
  const unreadRow = this.db.prepare(`
35692
- SELECT COUNT(*) as count FROM messages WHERE ${toIdPredicate} AND toType = ? AND read = 0
35794
+ SELECT COUNT(*) as count FROM messages WHERE ${toIdPredicate} AND toType = ? AND read = 0 AND (archived = 0 OR archived IS NULL)
35693
35795
  `).get(...participantIds, ownerType);
35694
35796
  const count2 = unreadRow?.count ?? 0;
35695
35797
  this.db.prepare(`
35696
- UPDATE messages SET read = 1, updatedAt = ? WHERE ${toIdPredicate} AND toType = ? AND read = 0
35798
+ UPDATE messages SET read = 1, updatedAt = ? WHERE ${toIdPredicate} AND toType = ? AND read = 0 AND (archived = 0 OR archived IS NULL)
35697
35799
  `).run(now, ...participantIds, ownerType);
35698
35800
  this.db.bumpLastModified();
35699
35801
  return count2;
35700
35802
  }
35803
+ /** Archive a message without destroying its correspondence history. */
35804
+ async archiveMessage(id) {
35805
+ return this.setMessageArchived(id, true);
35806
+ }
35807
+ /** Restore a previously archived message to default mailbox lists. */
35808
+ async unarchiveMessage(id) {
35809
+ return this.setMessageArchived(id, false);
35810
+ }
35811
+ async setMessageArchived(id, archived) {
35812
+ if (this.asyncLayer) {
35813
+ const updated2 = await setMessageArchived(this.asyncLayer.db, id, archived);
35814
+ if (!updated2) throw new Error(`Message ${id} not found`);
35815
+ this.emit(archived ? "message:archived" : "message:unarchived", updated2);
35816
+ return updated2;
35817
+ }
35818
+ const existing = await this.getMessage(id);
35819
+ if (!existing) throw new Error(`Message ${id} not found`);
35820
+ if (existing.archived === archived) return existing;
35821
+ this.db.prepare("UPDATE messages SET archived = ?, updatedAt = ? WHERE id = ?").run(archived ? 1 : 0, (/* @__PURE__ */ new Date()).toISOString(), id);
35822
+ this.db.bumpLastModified();
35823
+ const updated = await this.getMessage(id);
35824
+ this.emit(archived ? "message:archived" : "message:unarchived", updated);
35825
+ return updated;
35826
+ }
35701
35827
  /**
35702
35828
  * Delete a message by ID.
35703
35829
  * @param id - The message ID
@@ -35788,7 +35914,7 @@ var init_message_store = __esm({
35788
35914
  (${participantAFromPredicate} AND fromType = ? AND ${participantBToPredicate} AND toType = ?)
35789
35915
  OR
35790
35916
  (${participantBFromPredicate} AND fromType = ? AND ${participantAToPredicate} AND toType = ?)
35791
- )
35917
+ ) AND ${options?.archived === true ? "archived = 1" : "(archived = 0 OR archived IS NULL)"}
35792
35918
  ORDER BY createdAt DESC
35793
35919
  LIMIT ?
35794
35920
  `).all(
@@ -35823,11 +35949,11 @@ var init_message_store = __esm({
35823
35949
  const participantIds = this.getParticipantIdsForLookup(ownerId, ownerType);
35824
35950
  const toIdPredicate = participantIds.length === 1 ? "toId = ?" : `toId IN (${participantIds.map(() => "?").join(", ")})`;
35825
35951
  const unreadRow = this.db.prepare(`
35826
- SELECT COUNT(*) as count FROM messages WHERE ${toIdPredicate} AND toType = ? AND read = 0
35952
+ SELECT COUNT(*) as count FROM messages WHERE ${toIdPredicate} AND toType = ? AND read = 0 AND (archived = 0 OR archived IS NULL)
35827
35953
  `).get(...participantIds, ownerType);
35828
35954
  const unreadCount = unreadRow?.count ?? 0;
35829
35955
  const lastRow = this.db.prepare(`
35830
- SELECT * FROM messages WHERE ${toIdPredicate} AND toType = ? ORDER BY createdAt DESC, rowid DESC LIMIT 1
35956
+ SELECT * FROM messages WHERE ${toIdPredicate} AND toType = ? AND (archived = 0 OR archived IS NULL) ORDER BY createdAt DESC, rowid DESC LIMIT 1
35831
35957
  `).get(...participantIds, ownerType);
35832
35958
  const lastMessage = lastRow ? this.rowToMessage(lastRow) : void 0;
35833
35959
  return {
@@ -35841,13 +35967,13 @@ var init_message_store = __esm({
35841
35967
  * Get all agent-to-agent messages across all agents.
35842
35968
  * @returns Array of messages (newest first)
35843
35969
  */
35844
- async getAllAgentToAgentMessages() {
35970
+ async getAllAgentToAgentMessages(filter) {
35845
35971
  if (this.asyncLayer) {
35846
- return getAllAgentToAgentMessages(this.asyncLayer.db);
35972
+ return getAllAgentToAgentMessages(this.asyncLayer.db, filter);
35847
35973
  }
35848
35974
  const rows = this.db.prepare(`
35849
35975
  SELECT * FROM messages
35850
- WHERE type = ?
35976
+ WHERE type = ? AND ${filter?.archived === true ? "archived = 1" : "(archived = 0 OR archived IS NULL)"}
35851
35977
  ORDER BY createdAt DESC, rowid DESC
35852
35978
  `).all("agent-to-agent");
35853
35979
  return rows.map((row) => this.rowToMessage(row));
@@ -35861,7 +35987,7 @@ var init_message_store = __esm({
35861
35987
  }
35862
35988
  const row = this.db.prepare(`
35863
35989
  SELECT COUNT(*) as count FROM messages
35864
- WHERE type = ? AND read = 0
35990
+ WHERE type = ? AND read = 0 AND (archived = 0 OR archived IS NULL)
35865
35991
  `).get("agent-to-agent");
35866
35992
  return row?.count ?? 0;
35867
35993
  }
@@ -35960,6 +36086,7 @@ async function createApprovalRequest(layer, input) {
35960
36086
  };
35961
36087
  await layer.transactionImmediate(async (tx) => {
35962
36088
  await tx.insert(project_exports.approvalRequests).values({
36089
+ ...layer.projectId?.trim() ? { projectId: layer.projectId } : {},
35963
36090
  id: request2.id,
35964
36091
  status: request2.status,
35965
36092
  requesterActorId: request2.requester.actorId,
@@ -35983,12 +36110,12 @@ async function createApprovalRequest(layer, input) {
35983
36110
  });
35984
36111
  return request2;
35985
36112
  }
35986
- async function getApprovalRequest(handle, id) {
35987
- const rows = await handle.select().from(project_exports.approvalRequests).where(eq(project_exports.approvalRequests.id, id));
36113
+ async function getApprovalRequest(handle, id, projectId2) {
36114
+ const rows = await handle.select().from(project_exports.approvalRequests).where(and(eq(project_exports.approvalRequests.id, id), projectScopeFor(project_exports.approvalRequests.projectId, projectId2)));
35988
36115
  return rows[0] ? rowToRequest(rows[0]) : null;
35989
36116
  }
35990
- async function listApprovalRequests(handle, input = {}) {
35991
- const conditions = [];
36117
+ async function listApprovalRequests(handle, input = {}, projectId2) {
36118
+ const conditions = [projectScopeFor(project_exports.approvalRequests.projectId, projectId2)];
35992
36119
  if (input.status) conditions.push(eq(project_exports.approvalRequests.status, input.status));
35993
36120
  if (input.requesterActorId) conditions.push(eq(project_exports.approvalRequests.requesterActorId, input.requesterActorId));
35994
36121
  if (input.taskId) conditions.push(eq(project_exports.approvalRequests.taskId, input.taskId));
@@ -36002,7 +36129,7 @@ async function listApprovalRequests(handle, input = {}) {
36002
36129
  async function decideApprovalRequest(layer, requestId, status, input) {
36003
36130
  const now = (/* @__PURE__ */ new Date()).toISOString();
36004
36131
  return layer.transactionImmediate(async (tx) => {
36005
- const existing = await getApprovalRequest(tx, requestId);
36132
+ const existing = await getApprovalRequest(tx, requestId, layer.projectId);
36006
36133
  if (!existing) throw new Error(`Approval request ${requestId} not found`);
36007
36134
  if (!isValidApprovalRequestTransition(existing.status, status)) {
36008
36135
  throw new Error(`Invalid approval request transition: ${existing.status} -> ${status}`);
@@ -36013,23 +36140,24 @@ async function decideApprovalRequest(layer, requestId, status, input) {
36013
36140
  const updatedRows = await tx.update(project_exports.approvalRequests).set({ status, decidedAt: now, updatedAt: now }).where(
36014
36141
  and(
36015
36142
  eq(project_exports.approvalRequests.id, requestId),
36016
- eq(project_exports.approvalRequests.status, existing.status)
36143
+ eq(project_exports.approvalRequests.status, existing.status),
36144
+ projectScopeFor(project_exports.approvalRequests.projectId, layer.projectId)
36017
36145
  )
36018
36146
  ).returning({ id: project_exports.approvalRequests.id });
36019
36147
  if (updatedRows.length === 0) {
36020
- const raced = await getApprovalRequest(tx, requestId);
36148
+ const raced = await getApprovalRequest(tx, requestId, layer.projectId);
36021
36149
  throw new Error(
36022
36150
  `Invalid approval request transition: ${raced?.status ?? existing.status} -> ${status}`
36023
36151
  );
36024
36152
  }
36025
36153
  await appendAuditEvent(tx, layer.projectId ?? "", requestId, status, input.actor, now, input.note);
36026
- return await getApprovalRequest(tx, requestId);
36154
+ return await getApprovalRequest(tx, requestId, layer.projectId);
36027
36155
  });
36028
36156
  }
36029
36157
  async function markApprovalRequestCompleted(layer, requestId, input) {
36030
36158
  const now = (/* @__PURE__ */ new Date()).toISOString();
36031
36159
  return layer.transactionImmediate(async (tx) => {
36032
- const existing = await getApprovalRequest(tx, requestId);
36160
+ const existing = await getApprovalRequest(tx, requestId, layer.projectId);
36033
36161
  if (!existing) throw new Error(`Approval request ${requestId} not found`);
36034
36162
  if (!isValidApprovalRequestTransition(existing.status, "completed")) {
36035
36163
  throw new Error(`Invalid approval request transition: ${existing.status} -> completed`);
@@ -36043,21 +36171,22 @@ async function markApprovalRequestCompleted(layer, requestId, input) {
36043
36171
  const updatedRows = await tx.update(project_exports.approvalRequests).set({ status: "completed", completedAt: now, updatedAt: now }).where(
36044
36172
  and(
36045
36173
  eq(project_exports.approvalRequests.id, requestId),
36046
- eq(project_exports.approvalRequests.status, existing.status)
36174
+ eq(project_exports.approvalRequests.status, existing.status),
36175
+ projectScopeFor(project_exports.approvalRequests.projectId, layer.projectId)
36047
36176
  )
36048
36177
  ).returning({ id: project_exports.approvalRequests.id });
36049
36178
  if (updatedRows.length === 0) {
36050
- const raced = await getApprovalRequest(tx, requestId);
36179
+ const raced = await getApprovalRequest(tx, requestId, layer.projectId);
36051
36180
  throw new Error(
36052
36181
  `Invalid approval request transition: ${raced?.status ?? existing.status} -> completed`
36053
36182
  );
36054
36183
  }
36055
36184
  await appendAuditEvent(tx, layer.projectId ?? "", requestId, "completed", input.actor, now, input.note);
36056
- return await getApprovalRequest(tx, requestId);
36185
+ return await getApprovalRequest(tx, requestId, layer.projectId);
36057
36186
  });
36058
36187
  }
36059
- async function getApprovalAuditHistory(handle, requestId) {
36060
- const rows = await handle.select().from(project_exports.approvalRequestAuditEvents).where(eq(project_exports.approvalRequestAuditEvents.requestId, requestId)).orderBy(
36188
+ async function getApprovalAuditHistory(handle, requestId, projectId2) {
36189
+ const rows = await handle.select().from(project_exports.approvalRequestAuditEvents).where(and(eq(project_exports.approvalRequestAuditEvents.requestId, requestId), projectScopeFor(project_exports.approvalRequestAuditEvents.projectId, projectId2))).orderBy(
36061
36190
  sql`${project_exports.approvalRequestAuditEvents.createdAt} ASC, ${project_exports.approvalRequestAuditEvents.id} ASC`
36062
36191
  );
36063
36192
  return rows.map((row) => rowToAuditEvent(row));
@@ -36067,6 +36196,7 @@ var init_async_approval_request_store = __esm({
36067
36196
  "use strict";
36068
36197
  init_drizzle_orm();
36069
36198
  init_schema2();
36199
+ init_data_layer();
36070
36200
  init_agents();
36071
36201
  init_types();
36072
36202
  }
@@ -36080,6 +36210,7 @@ var init_approval_request_store = __esm({
36080
36210
  "use strict";
36081
36211
  init_drizzle_orm();
36082
36212
  init_db2();
36213
+ init_data_layer();
36083
36214
  init_async_approval_request_store();
36084
36215
  init_schema2();
36085
36216
  init_async_agent_activity();
@@ -36208,23 +36339,26 @@ var init_approval_request_store = __esm({
36208
36339
  return created;
36209
36340
  }
36210
36341
  async get(id) {
36211
- return getApprovalRequest(this.asyncLayer.db, id);
36342
+ return getApprovalRequest(this.asyncLayer.db, id, this.asyncLayer.projectId);
36212
36343
  }
36213
36344
  async list(input = {}) {
36214
- return listApprovalRequests(this.asyncLayer.db, input);
36345
+ return listApprovalRequests(this.asyncLayer.db, input, this.asyncLayer.projectId);
36215
36346
  }
36216
36347
  async getPendingCountsByActor() {
36217
36348
  const table = project_exports.approvalRequests;
36218
36349
  const rows = await this.asyncLayer.db.select({
36219
36350
  actorId: table.requesterActorId,
36220
36351
  requestCount: count()
36221
- }).from(table).where(eq(table.status, "pending")).groupBy(table.requesterActorId);
36352
+ }).from(table).where(and(eq(table.status, "pending"), projectScopeFor(table.projectId, this.asyncLayer.projectId))).groupBy(table.requesterActorId);
36222
36353
  return new Map(rows.map((row) => [row.actorId, Number(row.requestCount)]));
36223
36354
  }
36224
36355
  async findLatestByDedupeKey(input) {
36225
36356
  if (this.backendMode) {
36226
36357
  const table = project_exports.approvalRequests;
36227
- const conditions = [eq(table.requesterActorId, input.requesterActorId)];
36358
+ const conditions = [
36359
+ eq(table.requesterActorId, input.requesterActorId),
36360
+ projectScopeFor(table.projectId, this.asyncLayer.projectId)
36361
+ ];
36228
36362
  if (input.taskId !== void 0) {
36229
36363
  conditions.push(eq(table.taskId, input.taskId));
36230
36364
  }
@@ -36263,7 +36397,7 @@ var init_approval_request_store = __esm({
36263
36397
  return markApprovalRequestCompleted(this.asyncLayer, requestId, input);
36264
36398
  }
36265
36399
  async getAuditHistory(requestId) {
36266
- return getApprovalAuditHistory(this.asyncLayer.db, requestId);
36400
+ return getApprovalAuditHistory(this.asyncLayer.db, requestId, this.asyncLayer.projectId);
36267
36401
  }
36268
36402
  };
36269
36403
  }
@@ -40449,6 +40583,8 @@ async function applySchemaBaseline(db, options = {}) {
40449
40583
  const agentRatingProjectIsolationAlreadyApplied = applied.includes(AGENT_RATING_PROJECT_ISOLATION_VERSION);
40450
40584
  const agentRatingsProjectPartitionAlreadyApplied = applied.includes(AGENT_RATINGS_PROJECT_PARTITION_VERSION);
40451
40585
  const projectOwnershipDeclarationDriftAlreadyApplied = applied.includes(PROJECT_OWNERSHIP_DECLARATION_DRIFT_VERSION);
40586
+ const projectOwnershipDefaultReconciliationAlreadyApplied = applied.includes(PROJECT_OWNERSHIP_DEFAULT_RECONCILIATION_VERSION);
40587
+ const messageArchiveSchemaAlreadyApplied = applied.includes(MESSAGE_ARCHIVE_SCHEMA_VERSION);
40452
40588
  assertBinaryNotOlderThanDatabase(applied);
40453
40589
  let schemaChanged = false;
40454
40590
  if (!baselineAlreadyApplied) {
@@ -40932,17 +41068,29 @@ async function applySchemaBaseline(db, options = {}) {
40932
41068
  await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${PROJECT_OWNERSHIP_DECLARATION_DRIFT_VERSION}) ON CONFLICT (version) DO NOTHING`);
40933
41069
  schemaChanged = true;
40934
41070
  }
41071
+ if (!projectOwnershipDefaultReconciliationAlreadyApplied) {
41072
+ const migrationSql = await readFile4(PROJECT_OWNERSHIP_DEFAULT_RECONCILIATION_MIGRATION_PATH, "utf8");
41073
+ await tx.execute(sql.raw(migrationSql));
41074
+ await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${PROJECT_OWNERSHIP_DEFAULT_RECONCILIATION_VERSION}) ON CONFLICT (version) DO NOTHING`);
41075
+ schemaChanged = true;
41076
+ }
41077
+ if (!messageArchiveSchemaAlreadyApplied) {
41078
+ const migrationSql = await readFile4(MESSAGE_ARCHIVE_SCHEMA_MIGRATION_PATH, "utf8");
41079
+ await tx.execute(sql.raw(migrationSql));
41080
+ await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${MESSAGE_ARCHIVE_SCHEMA_VERSION}) ON CONFLICT (version) DO NOTHING`);
41081
+ schemaChanged = true;
41082
+ }
40935
41083
  return { applied: schemaChanged, pluginHooksRun: pluginHooks.length };
40936
41084
  });
40937
41085
  }
40938
- 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;
41086
+ 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;
40939
41087
  var init_schema_applier = __esm({
40940
41088
  "../core/src/postgres/schema-applier.ts"() {
40941
41089
  "use strict";
40942
41090
  init_drizzle_orm();
40943
41091
  init_plugin_schema_hook();
40944
41092
  init_advisory_locks();
40945
- SCHEMA_BASELINE_VERSION = "0056";
41093
+ SCHEMA_BASELINE_VERSION = "0058";
40946
41094
  TASK_DECLARED_SYMBOLS_VERSION = "0028";
40947
41095
  INITIAL_SCHEMA_VERSION = "0000";
40948
41096
  AUTOMATION_ISOLATION_SCHEMA_VERSION = "0001";
@@ -40999,6 +41147,8 @@ var init_schema_applier = __esm({
40999
41147
  AGENT_RATING_PROJECT_ISOLATION_VERSION = "0054";
41000
41148
  AGENT_RATINGS_PROJECT_PARTITION_VERSION = "0055";
41001
41149
  PROJECT_OWNERSHIP_DECLARATION_DRIFT_VERSION = "0056";
41150
+ PROJECT_OWNERSHIP_DEFAULT_RECONCILIATION_VERSION = "0057";
41151
+ MESSAGE_ARCHIVE_SCHEMA_VERSION = "0058";
41002
41152
  LEGACY_ADOPTION_DRAINED_MARKER_FUNCTION = "fusion_mark_legacy_adoption_drained";
41003
41153
  StaleBinarySchemaError = class extends Error {
41004
41154
  constructor(databaseVersion, binaryVersion) {
@@ -41135,6 +41285,8 @@ var init_schema_applier = __esm({
41135
41285
  AGENT_RATING_PROJECT_ISOLATION_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0054_fn_8957_agent_rating_project_isolation.sql");
41136
41286
  AGENT_RATINGS_PROJECT_PARTITION_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0055_fn_8988_agent_ratings_project_partition.sql");
41137
41287
  PROJECT_OWNERSHIP_DECLARATION_DRIFT_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0056_fn_8997_project_ownership_declaration_drift.sql");
41288
+ PROJECT_OWNERSHIP_DEFAULT_RECONCILIATION_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0057_fn_9004_project_ownership_default_reconciliation.sql");
41289
+ MESSAGE_ARCHIVE_SCHEMA_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0058_fn_9014_message_archive.sql");
41138
41290
  }
41139
41291
  });
41140
41292
 
@@ -68490,20 +68642,32 @@ async function replaceActiveTaskWorkflowContinuation(layer, input) {
68490
68642
  return upsertWorkflowWorkItem(layer, input, tx);
68491
68643
  }));
68492
68644
  }
68493
- async function seedStrandedPlanReviewContinuation(layer, input) {
68645
+ async function seedStrandedPlanReviewContinuation(layer, input, options = {}) {
68494
68646
  return layer.transactionImmediate(async (tx) => withTaskWorkflowSerialization(tx, layer.projectId, input.taskId, async () => {
68495
68647
  const active2 = await tx.select({ id: project_exports.workflowWorkItems.id }).from(project_exports.workflowWorkItems).where(and(
68496
68648
  projectScopeFor(project_exports.workflowWorkItems.projectId, layer.projectId),
68497
68649
  eq(project_exports.workflowWorkItems.taskId, input.taskId),
68498
68650
  inArray(project_exports.workflowWorkItems.state, [...ACTIVE_WORKFLOW_WORK_ITEM_STATES])
68499
68651
  ));
68500
- if (active2.length > 0) return { seeded: false, reason: "active-continuation" };
68652
+ const blocking = options.retirePredecessorId ? active2.filter((row) => row.id !== options.retirePredecessorId) : active2;
68653
+ if (blocking.length > 0) return { seeded: false, reason: "active-continuation" };
68501
68654
  const taskRows = await tx.select({ workflowStepResults: project_exports.tasks.workflowStepResults }).from(project_exports.tasks).where(and(
68502
68655
  projectScopeFor(project_exports.tasks.projectId, layer.projectId),
68503
68656
  eq(project_exports.tasks.id, input.taskId)
68504
68657
  )).limit(1);
68505
68658
  const results = taskRows[0]?.workflowStepResults;
68506
68659
  if (results?.some(isPlanReviewSatisfied)) return { seeded: false, reason: "plan-review-passed" };
68660
+ if (options.retirePredecessorId) {
68661
+ const predRows = await tx.select().from(project_exports.workflowWorkItems).where(and(
68662
+ projectScopeFor(project_exports.workflowWorkItems.projectId, layer.projectId),
68663
+ eq(project_exports.workflowWorkItems.id, options.retirePredecessorId),
68664
+ eq(project_exports.workflowWorkItems.taskId, input.taskId)
68665
+ )).limit(1);
68666
+ const pred = predRows[0];
68667
+ if (pred && !isTerminalWorkflowWorkItemState(normalizeWorkflowWorkItemState(pred.state))) {
68668
+ await transitionWorkflowWorkItem(layer, pred.id, "succeeded", { leaseOwner: null, leaseExpiresAt: null, lastError: null }, tx);
68669
+ }
68670
+ }
68507
68671
  const item = await upsertWorkflowWorkItem(layer, input, tx);
68508
68672
  return { seeded: true, workItemId: item.id };
68509
68673
  }));
@@ -68667,8 +68831,10 @@ function safeJsonParse(value) {
68667
68831
  return null;
68668
68832
  }
68669
68833
  }
68670
- async function queryRunAuditEvents(db, filter = {}) {
68671
- const conditions = [];
68834
+ async function queryRunAuditEvents(db, filter = {}, projectId2) {
68835
+ const conditions = [projectScopeFor(project_exports.runAuditEvents.projectId, projectId2)].filter(
68836
+ (condition) => condition !== void 0
68837
+ );
68672
68838
  if (filter.runId) {
68673
68839
  conditions.push(eq(project_exports.runAuditEvents.runId, filter.runId));
68674
68840
  }
@@ -68814,6 +68980,7 @@ var init_async_audit = __esm({
68814
68980
  init_drizzle_orm();
68815
68981
  init_schema2();
68816
68982
  init_data_layer();
68983
+ init_data_layer();
68817
68984
  LEGACY_REVIEW_LANES3 = ["in-review"];
68818
68985
  LEGACY_COMPLETE_LANES = ["done"];
68819
68986
  }
@@ -69203,7 +69370,10 @@ async function insertArtifactRow(layer, input, stored, archivedColumns) {
69203
69370
  createdAt: now,
69204
69371
  updatedAt: now
69205
69372
  });
69206
- const rows = await tx.select().from(project_exports.artifacts).where(eq(project_exports.artifacts.id, id)).limit(1);
69373
+ const rows = await tx.select().from(project_exports.artifacts).where(and(
69374
+ eq(project_exports.artifacts.id, id),
69375
+ projectScopeFor(project_exports.artifacts.projectId, layer.projectId)
69376
+ )).limit(1);
69207
69377
  const row = rows[0];
69208
69378
  if (!row) throw new Error(`Failed to register artifact ${id}`);
69209
69379
  return rowToArtifact2(row);
@@ -69211,7 +69381,7 @@ async function insertArtifactRow(layer, input, stored, archivedColumns) {
69211
69381
  }
69212
69382
  async function updateArtifactRow(layer, id, updates, archivedColumns) {
69213
69383
  return layer.transactionImmediate(async (tx) => {
69214
- const existing = await getArtifact(tx, id);
69384
+ const existing = await getArtifact(tx, id, layer.projectId);
69215
69385
  if (!existing) {
69216
69386
  throw new Error(`Artifact ${id} not found`);
69217
69387
  }
@@ -69231,29 +69401,38 @@ async function updateArtifactRow(layer, id, updates, archivedColumns) {
69231
69401
  description: updates.description !== void 0 ? updates.description : existing.description ?? null,
69232
69402
  content: updates.content !== void 0 ? updates.content : existing.content ?? null,
69233
69403
  updatedAt: now
69234
- }).where(eq(project_exports.artifacts.id, id));
69235
- const updated = await getArtifact(tx, id);
69404
+ }).where(and(
69405
+ eq(project_exports.artifacts.id, id),
69406
+ projectScopeFor(project_exports.artifacts.projectId, layer.projectId)
69407
+ ));
69408
+ const updated = await getArtifact(tx, id, layer.projectId);
69236
69409
  if (!updated) {
69237
69410
  throw new Error(`Failed to update artifact ${id}`);
69238
69411
  }
69239
69412
  return updated;
69240
69413
  });
69241
69414
  }
69242
- async function getArtifact(db, id) {
69243
- const rows = await db.select().from(project_exports.artifacts).where(eq(project_exports.artifacts.id, id)).limit(1);
69415
+ async function getArtifact(db, id, projectId2) {
69416
+ const rows = await db.select().from(project_exports.artifacts).where(and(
69417
+ eq(project_exports.artifacts.id, id),
69418
+ projectScopeFor(project_exports.artifacts.projectId, projectId2)
69419
+ )).limit(1);
69244
69420
  const row = rows[0];
69245
69421
  return row ? rowToArtifact2(row) : null;
69246
69422
  }
69247
69423
  async function getArtifacts(db, taskId, projectId2, archivedColumns) {
69248
69424
  const column = await getLiveTaskColumn(db, taskId, projectId2, archivedColumns);
69249
69425
  if (column === null || column === "archived") return [];
69250
- const rows = await db.select().from(project_exports.artifacts).where(eq(project_exports.artifacts.taskId, taskId)).orderBy(desc(project_exports.artifacts.createdAt));
69426
+ const rows = await db.select().from(project_exports.artifacts).where(and(
69427
+ eq(project_exports.artifacts.taskId, taskId),
69428
+ projectScopeFor(project_exports.artifacts.projectId, projectId2)
69429
+ )).orderBy(desc(project_exports.artifacts.createdAt));
69251
69430
  return rows.map((row) => rowToArtifact2(row));
69252
69431
  }
69253
- async function listArtifacts(db, options) {
69432
+ async function listArtifacts(db, options, projectId2) {
69254
69433
  const limit = Math.min(Math.max(1, options?.limit ?? 200), 1e3);
69255
69434
  const offset = Math.max(0, options?.offset ?? 0);
69256
- const conditions = [];
69435
+ const conditions = [projectScopeFor(project_exports.artifacts.projectId, projectId2)];
69257
69436
  if (options?.type) {
69258
69437
  conditions.push(eq(project_exports.artifacts.type, options.type));
69259
69438
  }
@@ -69292,7 +69471,10 @@ async function listArtifacts(db, options) {
69292
69471
  taskColumn: project_exports.tasks.column
69293
69472
  }).from(project_exports.artifacts).leftJoin(
69294
69473
  project_exports.tasks,
69295
- eq(project_exports.artifacts.taskId, project_exports.tasks.id)
69474
+ and(
69475
+ eq(project_exports.artifacts.taskId, project_exports.tasks.id),
69476
+ eq(project_exports.artifacts.projectId, project_exports.tasks.projectId)
69477
+ )
69296
69478
  ).where(and(...conditions)).orderBy(desc(project_exports.artifacts.createdAt)).limit(limit).offset(offset);
69297
69479
  return rows.map((row) => {
69298
69480
  const artifact = rowToArtifact2(row);
@@ -70457,7 +70639,7 @@ function rowToBranchGroup2(row) {
70457
70639
  closedAt: row.closedAt ?? void 0
70458
70640
  };
70459
70641
  }
70460
- async function createBranchGroup(db, input) {
70642
+ async function createBranchGroup(db, input, projectId2) {
70461
70643
  validateBranchGroupBranchName(input.branchName);
70462
70644
  const now = Date.now();
70463
70645
  const id = generateBranchGroupId2();
@@ -70476,44 +70658,52 @@ async function createBranchGroup(db, input) {
70476
70658
  updatedAt: now,
70477
70659
  closedAt: input.closedAt ?? null
70478
70660
  });
70479
- const created = await getBranchGroup(db, id);
70661
+ const created = await getBranchGroup(db, id, projectId2);
70480
70662
  if (!created) throw new Error(`Failed to read branch group ${id} after create`);
70481
70663
  return created;
70482
70664
  }
70483
- async function getBranchGroup(db, id) {
70484
- const rows = await db.select().from(project_exports.branchGroups).where(eq(project_exports.branchGroups.id, id)).limit(1);
70665
+ async function getBranchGroup(db, id, projectId2) {
70666
+ const rows = await db.select().from(project_exports.branchGroups).where(and(
70667
+ eq(project_exports.branchGroups.id, id),
70668
+ projectScopeFor(project_exports.branchGroups.projectId, projectId2)
70669
+ )).limit(1);
70485
70670
  const row = rows[0];
70486
70671
  return row ? rowToBranchGroup2(row) : null;
70487
70672
  }
70488
- async function getBranchGroupBySource(db, sourceType, sourceId) {
70673
+ async function getBranchGroupBySource(db, sourceType, sourceId, projectId2) {
70489
70674
  const rows = await db.select().from(project_exports.branchGroups).where(
70490
70675
  and(
70491
70676
  eq(project_exports.branchGroups.sourceType, sourceType),
70492
- eq(project_exports.branchGroups.sourceId, sourceId)
70677
+ eq(project_exports.branchGroups.sourceId, sourceId),
70678
+ projectScopeFor(project_exports.branchGroups.projectId, projectId2)
70493
70679
  )
70494
70680
  ).limit(1);
70495
70681
  const row = rows[0];
70496
70682
  return row ? rowToBranchGroup2(row) : null;
70497
70683
  }
70498
- async function getBranchGroupByBranchName(db, branchName) {
70499
- 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);
70684
+ async function getBranchGroupByBranchName(db, branchName, projectId2) {
70685
+ const rows = await db.select().from(project_exports.branchGroups).where(and(
70686
+ eq(project_exports.branchGroups.branchName, branchName),
70687
+ projectScopeFor(project_exports.branchGroups.projectId, projectId2)
70688
+ )).orderBy(sql`${project_exports.branchGroups.createdAt} DESC`).limit(1);
70500
70689
  const row = rows[0];
70501
70690
  return row ? rowToBranchGroup2(row) : null;
70502
70691
  }
70503
- async function ensureBranchGroupForSource(db, sourceType, sourceId, init) {
70504
- const existing = await getBranchGroupBySource(db, sourceType, sourceId);
70692
+ async function ensureBranchGroupForSource(db, sourceType, sourceId, init, projectId2) {
70693
+ const existing = await getBranchGroupBySource(db, sourceType, sourceId, projectId2);
70505
70694
  if (existing) return existing;
70506
- const existingByBranch = await getBranchGroupByBranchName(db, init.branchName);
70695
+ const existingByBranch = await getBranchGroupByBranchName(db, init.branchName, projectId2);
70507
70696
  if (existingByBranch) return existingByBranch;
70508
- return createBranchGroup(db, { sourceType, sourceId, ...init });
70697
+ return createBranchGroup(db, { sourceType, sourceId, ...init }, projectId2);
70509
70698
  }
70510
- async function listBranchGroups(db, options) {
70511
- const query = db.select().from(project_exports.branchGroups).orderBy(asc(project_exports.branchGroups.createdAt));
70512
- const rows = options?.status ? await query.where(eq(project_exports.branchGroups.status, options.status)) : await query;
70699
+ async function listBranchGroups(db, options, projectId2) {
70700
+ const conditions = [projectScopeFor(project_exports.branchGroups.projectId, projectId2)];
70701
+ if (options?.status) conditions.push(eq(project_exports.branchGroups.status, options.status));
70702
+ const rows = await db.select().from(project_exports.branchGroups).where(and(...conditions)).orderBy(asc(project_exports.branchGroups.createdAt));
70513
70703
  return rows.map((row) => rowToBranchGroup2(row));
70514
70704
  }
70515
- async function updateBranchGroup(db, id, patch) {
70516
- const current = await getBranchGroup(db, id);
70705
+ async function updateBranchGroup(db, id, patch, projectId2) {
70706
+ const current = await getBranchGroup(db, id, projectId2);
70517
70707
  if (!current) throw new Error(`Branch group ${id} not found`);
70518
70708
  if (patch.branchName !== void 0) {
70519
70709
  validateBranchGroupBranchName(patch.branchName);
@@ -70532,8 +70722,11 @@ async function updateBranchGroup(db, id, patch) {
70532
70722
  status: nextStatus,
70533
70723
  updatedAt: now,
70534
70724
  closedAt: nextClosedAt
70535
- }).where(eq(project_exports.branchGroups.id, id));
70536
- const updated = await getBranchGroup(db, id);
70725
+ }).where(and(
70726
+ eq(project_exports.branchGroups.id, id),
70727
+ projectScopeFor(project_exports.branchGroups.projectId, projectId2)
70728
+ ));
70729
+ const updated = await getBranchGroup(db, id, projectId2);
70537
70730
  if (!updated) throw new Error(`Branch group ${id} disappeared after update`);
70538
70731
  return updated;
70539
70732
  }
@@ -70690,6 +70883,7 @@ var init_async_branch_groups = __esm({
70690
70883
  "use strict";
70691
70884
  init_drizzle_orm();
70692
70885
  init_schema2();
70886
+ init_data_layer();
70693
70887
  init_branch_assignment();
70694
70888
  }
70695
70889
  });
@@ -80503,6 +80697,9 @@ import { randomUUID as randomUUID21 } from "node:crypto";
80503
80697
  function tableForScope(scope) {
80504
80698
  return scope === "project" ? project_exports.secrets : central_exports.secretsGlobal;
80505
80699
  }
80700
+ function scopeForSecretTable(scope, table, projectId2) {
80701
+ return scope === "project" ? projectScopeFor(table.projectId, projectId2) : void 0;
80702
+ }
80506
80703
  function isAccessPolicy(value) {
80507
80704
  return value === "auto" || value === "prompt" || value === "deny";
80508
80705
  }
@@ -80525,23 +80722,23 @@ function rowToRecord2(row, scope) {
80525
80722
  lastReadBy: row.lastReadBy
80526
80723
  };
80527
80724
  }
80528
- async function getSecretMetadata(handle, id, scope) {
80725
+ async function getSecretMetadata(handle, id, scope, projectId2) {
80529
80726
  const table = tableForScope(scope);
80530
- const rows = await handle.select(metadataColumns(table)).from(table).where(eq(table.id, id)).limit(1);
80727
+ const rows = await handle.select(metadataColumns(table)).from(table).where(and(eq(table.id, id), scopeForSecretTable(scope, table, projectId2))).limit(1);
80531
80728
  const row = rows[0];
80532
80729
  return row ? rowToRecord2(row, scope) : null;
80533
80730
  }
80534
- async function listSecrets(handle, scope) {
80731
+ async function listSecrets(handle, scope, projectId2) {
80535
80732
  if (scope) {
80536
80733
  const table = tableForScope(scope);
80537
- const rows = await handle.select(metadataColumns(table)).from(table).orderBy(asc(sql`lower(${table.key})`));
80734
+ const rows = await handle.select(metadataColumns(table)).from(table).where(scopeForSecretTable(scope, table, projectId2)).orderBy(asc(sql`lower(${table.key})`));
80538
80735
  return rows.map((row) => rowToRecord2(row, scope));
80539
80736
  }
80540
- const project = await listSecrets(handle, "project");
80541
- const global2 = await listSecrets(handle, "global");
80737
+ const project = await listSecrets(handle, "project", projectId2);
80738
+ const global2 = await listSecrets(handle, "global", projectId2);
80542
80739
  return [...project, ...global2];
80543
80740
  }
80544
- async function createSecret(handle, cipher, input) {
80741
+ async function createSecret(handle, cipher, input, projectId2) {
80545
80742
  const key = input.key.trim();
80546
80743
  if (!key) {
80547
80744
  throw new SecretsStoreError({ code: "invalid-key", message: "Secret key is required" });
@@ -80555,6 +80752,7 @@ async function createSecret(handle, cipher, input) {
80555
80752
  const table = tableForScope(input.scope);
80556
80753
  try {
80557
80754
  await handle.insert(table).values({
80755
+ ...input.scope === "project" && projectId2?.trim() ? { projectId: projectId2 } : {},
80558
80756
  id,
80559
80757
  key,
80560
80758
  valueCiphertext: encrypted.ciphertext,
@@ -80574,14 +80772,14 @@ async function createSecret(handle, cipher, input) {
80574
80772
  }
80575
80773
  throw error3;
80576
80774
  }
80577
- const created = await getSecretMetadata(handle, id, input.scope);
80775
+ const created = await getSecretMetadata(handle, id, input.scope, projectId2);
80578
80776
  if (!created) {
80579
80777
  throw new SecretsStoreError({ code: "not-found", message: "Secret insert succeeded but row could not be read back" });
80580
80778
  }
80581
80779
  return created;
80582
80780
  }
80583
- async function updateSecret(handle, cipher, id, scope, patch) {
80584
- const existing = await getSecretMetadata(handle, id, scope);
80781
+ async function updateSecret(handle, cipher, id, scope, patch, projectId2) {
80782
+ const existing = await getSecretMetadata(handle, id, scope, projectId2);
80585
80783
  if (!existing) {
80586
80784
  throw new SecretsStoreError({ code: "not-found", message: "Secret not found" });
80587
80785
  }
@@ -80615,34 +80813,34 @@ async function updateSecret(handle, cipher, id, scope, patch) {
80615
80813
  updates.nonce = encrypted.nonce;
80616
80814
  }
80617
80815
  try {
80618
- await handle.update(table).set(updates).where(eq(table.id, id));
80816
+ await handle.update(table).set(updates).where(and(eq(table.id, id), scopeForSecretTable(scope, table, projectId2)));
80619
80817
  } catch (error3) {
80620
80818
  if (isPostgresUniqueError(error3)) {
80621
80819
  throw new SecretsStoreError({ code: "duplicate-key", message: "Secret key already exists" });
80622
80820
  }
80623
80821
  throw error3;
80624
80822
  }
80625
- const updated = await getSecretMetadata(handle, id, scope);
80823
+ const updated = await getSecretMetadata(handle, id, scope, projectId2);
80626
80824
  if (!updated) {
80627
80825
  throw new SecretsStoreError({ code: "not-found", message: "Secret update succeeded but row could not be read back" });
80628
80826
  }
80629
80827
  return updated;
80630
80828
  }
80631
- async function deleteSecret(handle, id, scope) {
80632
- const existing = await getSecretMetadata(handle, id, scope);
80829
+ async function deleteSecret(handle, id, scope, projectId2) {
80830
+ const existing = await getSecretMetadata(handle, id, scope, projectId2);
80633
80831
  if (!existing) {
80634
80832
  throw new SecretsStoreError({ code: "not-found", message: "Secret not found" });
80635
80833
  }
80636
80834
  const table = tableForScope(scope);
80637
- await handle.delete(table).where(eq(table.id, id));
80835
+ await handle.delete(table).where(and(eq(table.id, id), scopeForSecretTable(scope, table, projectId2)));
80638
80836
  }
80639
- async function revealSecret(handle, cipher, id, scope, reader) {
80837
+ async function revealSecret(handle, cipher, id, scope, reader, projectId2) {
80640
80838
  const table = tableForScope(scope);
80641
80839
  const rows = await handle.select({
80642
80840
  ...metadataColumns(table),
80643
80841
  valueCiphertext: table.valueCiphertext,
80644
80842
  nonce: table.nonce
80645
- }).from(table).where(eq(table.id, id)).limit(1);
80843
+ }).from(table).where(and(eq(table.id, id), scopeForSecretTable(scope, table, projectId2))).limit(1);
80646
80844
  const row = rows[0];
80647
80845
  if (!row) {
80648
80846
  throw new SecretsStoreError({ code: "not-found", message: "Secret not found" });
@@ -80661,7 +80859,7 @@ async function revealSecret(handle, cipher, id, scope, reader) {
80661
80859
  }
80662
80860
  const now = (/* @__PURE__ */ new Date()).toISOString();
80663
80861
  const lastReadBy = reader.userId ?? reader.agentId ?? null;
80664
- await handle.update(table).set({ lastReadAt: now, lastReadBy, updatedAt: now }).where(eq(table.id, id));
80862
+ await handle.update(table).set({ lastReadAt: now, lastReadBy, updatedAt: now }).where(and(eq(table.id, id), scopeForSecretTable(scope, table, projectId2)));
80665
80863
  return { key: row.key, plaintextValue };
80666
80864
  }
80667
80865
  var severityAuditLog10, SecretsStoreError, metadataColumns;
@@ -80672,6 +80870,7 @@ var init_async_secrets_store = __esm({
80672
80870
  init_drizzle_orm();
80673
80871
  init_postgres_errors();
80674
80872
  init_schema2();
80873
+ init_data_layer();
80675
80874
  init_secrets_crypto();
80676
80875
  severityAuditLog10 = createLogger("core-async-secrets-store");
80677
80876
  SecretsStoreError = class extends Error {
@@ -80767,7 +80966,7 @@ var init_secrets_store = __esm({
80767
80966
  };
80768
80967
  }
80769
80968
  async listSecrets(scope) {
80770
- return listSecrets(this.asyncLayer.db, scope);
80969
+ return listSecrets(this.asyncLayer.db, scope, this.asyncLayer.projectId);
80771
80970
  }
80772
80971
  async listEnvExportable(opts) {
80773
80972
  const keyPrefix = opts?.keyPrefix;
@@ -80810,7 +81009,7 @@ var init_secrets_store = __esm({
80810
81009
  return [...exported.values()];
80811
81010
  }
80812
81011
  async getSecretMetadata(id, scope) {
80813
- return getSecretMetadata(this.asyncLayer.db, id, scope);
81012
+ return getSecretMetadata(this.asyncLayer.db, id, scope, this.asyncLayer.projectId);
80814
81013
  }
80815
81014
  async createSecret(input) {
80816
81015
  const key = input.key.trim();
@@ -80820,12 +81019,12 @@ var init_secrets_store = __esm({
80820
81019
  if (input.accessPolicy && !isAccessPolicy2(input.accessPolicy)) {
80821
81020
  throw new SecretsStoreError2({ code: "invalid-policy", message: "Invalid access policy" });
80822
81021
  }
80823
- const created = await createSecret(this.asyncLayer.db, this.cipher, input);
81022
+ const created = await createSecret(this.asyncLayer.db, this.cipher, input, this.asyncLayer.projectId);
80824
81023
  this.emitAudit({ mutationType: "secret:create", scope: input.scope, secretId: created.id, key: created.key });
80825
81024
  return created;
80826
81025
  }
80827
81026
  async updateSecret(id, scope, patch) {
80828
- const updated = await updateSecret(this.asyncLayer.db, this.cipher, id, scope, patch);
81027
+ const updated = await updateSecret(this.asyncLayer.db, this.cipher, id, scope, patch, this.asyncLayer.projectId);
80829
81028
  this.emitAudit({ mutationType: "secret:update", scope, secretId: updated.id, key: updated.key });
80830
81029
  return updated;
80831
81030
  }
@@ -80834,12 +81033,12 @@ var init_secrets_store = __esm({
80834
81033
  if (!existing) {
80835
81034
  throw new SecretsStoreError2({ code: "not-found", message: "Secret not found" });
80836
81035
  }
80837
- await deleteSecret(this.asyncLayer.db, id, scope);
81036
+ await deleteSecret(this.asyncLayer.db, id, scope, this.asyncLayer.projectId);
80838
81037
  this.emitAudit({ mutationType: "secret:delete", scope, secretId: id, key: existing.key });
80839
81038
  return;
80840
81039
  }
80841
81040
  async revealSecret(id, scope, reader) {
80842
- const revealed = await revealSecret(this.asyncLayer.db, this.cipher, id, scope, reader);
81041
+ const revealed = await revealSecret(this.asyncLayer.db, this.cipher, id, scope, reader, this.asyncLayer.projectId);
80843
81042
  this.emitAudit({ mutationType: "secret:read", scope, secretId: id, key: revealed.key, actor: reader });
80844
81043
  return revealed;
80845
81044
  }
@@ -81940,6 +82139,7 @@ async function getVerificationCacheHitImpl(store, treeSha, testCommand, buildCom
81940
82139
  const normalizedBuild = buildCommand2 ?? "";
81941
82140
  const table = project_exports.verificationCache;
81942
82141
  const rows = await store.asyncLayer.db.select({ recordedAt: table.recordedAt, taskId: table.taskId }).from(table).where(and(
82142
+ projectScopeFor(table.projectId, store.asyncLayer.projectId),
81943
82143
  eq(table.treeSha, treeSha),
81944
82144
  eq(table.testCommand, normalizedTest),
81945
82145
  eq(table.buildCommand, normalizedBuild)
@@ -82682,7 +82882,7 @@ async function invokeTaskCreatedHookImpl(store, task) {
82682
82882
  }
82683
82883
  async function createBranchGroupImpl(store, input) {
82684
82884
  const layer = store.asyncLayer;
82685
- return createBranchGroup(layer.db, input);
82885
+ return createBranchGroup(layer.db, input, layer.projectId);
82686
82886
  }
82687
82887
  var init_task_id_integrity2 = __esm({
82688
82888
  "../core/src/task-store/task-id-integrity.ts"() {
@@ -82767,22 +82967,23 @@ import { readFile as readFile7, writeFile as writeFile5 } from "node:fs/promises
82767
82967
  import { join as join23 } from "node:path";
82768
82968
  async function getBranchGroupImpl(store, id) {
82769
82969
  const layer = store.asyncLayer;
82770
- return getBranchGroup(layer.db, id);
82970
+ return getBranchGroup(layer.db, id, layer.projectId);
82771
82971
  }
82772
82972
  async function getBranchGroupBySourceImpl(store, sourceType, sourceId) {
82773
82973
  const layer = store.asyncLayer;
82774
- return getBranchGroupBySource(layer.db, sourceType, sourceId);
82974
+ return getBranchGroupBySource(layer.db, sourceType, sourceId, layer.projectId);
82775
82975
  }
82776
82976
  async function getBranchGroupByBranchNameImpl(store, branchName) {
82777
82977
  const layer = store.asyncLayer;
82778
- return getBranchGroupByBranchName(layer.db, branchName);
82978
+ return getBranchGroupByBranchName(layer.db, branchName, layer.projectId);
82779
82979
  }
82780
82980
  async function ensureBranchGroupForSourceImpl(store, sourceType, sourceId, init) {
82781
- return ensureBranchGroupForSource(store.asyncLayer.db, sourceType, sourceId, init);
82981
+ const layer = store.asyncLayer;
82982
+ return ensureBranchGroupForSource(layer.db, sourceType, sourceId, init, layer.projectId);
82782
82983
  }
82783
82984
  async function listBranchGroupsImpl(store, options) {
82784
82985
  const layer = store.asyncLayer;
82785
- return listBranchGroups(layer.db, options);
82986
+ return listBranchGroups(layer.db, options, layer.projectId);
82786
82987
  }
82787
82988
  async function listTasksByBranchGroupImpl(store, groupId) {
82788
82989
  const tasks2 = await store.listTasks({ includeArchived: true, slim: false });
@@ -84324,7 +84525,7 @@ function insertArtifactRowImpl(store, input, id, now, stored) {
84324
84525
  }
84325
84526
  async function getArtifactImpl(store, id) {
84326
84527
  const layer = store.asyncLayer;
84327
- return getArtifact(layer.db, id);
84528
+ return getArtifact(layer.db, id, layer.projectId);
84328
84529
  }
84329
84530
  async function updateArtifactImpl(store, id, updates) {
84330
84531
  const layer = store.asyncLayer;
@@ -89193,6 +89394,7 @@ var init_async_archive_lineage = __esm({
89193
89394
  "use strict";
89194
89395
  init_drizzle_orm();
89195
89396
  init_schema2();
89397
+ init_data_layer();
89196
89398
  init_async_persistence();
89197
89399
  init_async_lifecycle();
89198
89400
  init_lineage_approval_invalidation();
@@ -92660,7 +92862,7 @@ async function _maybeAutoArchiveSameAgentDuplicateBackendImpl(store, task, input
92660
92862
  }
92661
92863
  async function updateBranchGroupImpl(store, id, patch) {
92662
92864
  const layer = store.asyncLayer;
92663
- return updateBranchGroup(layer.db, id, patch);
92865
+ return updateBranchGroup(layer.db, id, patch, layer.projectId);
92664
92866
  }
92665
92867
  async function updatePrEntityImpl(store, id, patch) {
92666
92868
  const layer = store.asyncLayer;
@@ -93170,13 +93372,19 @@ async function deleteAttachmentArtifactRows(store, taskId, filename) {
93170
93372
  const linkedArtifactIds = artifacts2.filter((artifact) => artifact.metadata?.source === "attachment" && artifact.metadata.attachmentFilename === filename).map((artifact) => artifact.id);
93171
93373
  if (linkedArtifactIds.length === 0) return;
93172
93374
  for (const artifactId of linkedArtifactIds) {
93173
- await layer.db.delete(project_exports.artifacts).where(eq(project_exports.artifacts.id, artifactId));
93375
+ await layer.db.delete(project_exports.artifacts).where(and(
93376
+ eq(project_exports.artifacts.id, artifactId),
93377
+ projectScopeFor(project_exports.artifacts.projectId, layer.projectId)
93378
+ ));
93174
93379
  }
93175
93380
  return;
93176
93381
  }
93177
93382
  async function getArtifactsForAttachmentCleanup(store, taskId) {
93178
93383
  const layer = store.asyncLayer;
93179
- const rows = await layer.db.select().from(project_exports.artifacts).where(eq(project_exports.artifacts.taskId, taskId));
93384
+ const rows = await layer.db.select().from(project_exports.artifacts).where(and(
93385
+ eq(project_exports.artifacts.taskId, taskId),
93386
+ projectScopeFor(project_exports.artifacts.projectId, layer.projectId)
93387
+ ));
93180
93388
  return rows;
93181
93389
  }
93182
93390
  async function deleteAttachmentImpl(store, id, filename) {
@@ -95592,7 +95800,7 @@ function clearLinkedAgentTaskIdsImpl(store, taskId, updatedAt = (/* @__PURE__ */
95592
95800
  `).run(updatedAt, updatedAt, taskId);
95593
95801
  }
95594
95802
  async function listArtifactsImpl(store, options) {
95595
- return listArtifacts(store.asyncLayer.db, options);
95803
+ return listArtifacts(store.asyncLayer.db, options, store.asyncLayer.projectId);
95596
95804
  }
95597
95805
  async function rehomeOccupantImpl(store, taskId, targetColumn, reason, metadata) {
95598
95806
  let current;
@@ -96837,8 +97045,8 @@ async function upsertWorkflowWorkItemImpl(store, input, tx) {
96837
97045
  async function replaceActiveTaskWorkflowContinuationImpl(store, input) {
96838
97046
  return replaceActiveTaskWorkflowContinuation(store.asyncLayer, input);
96839
97047
  }
96840
- async function seedStrandedPlanReviewContinuationImpl(store, input) {
96841
- return seedStrandedPlanReviewContinuation(store.asyncLayer, input);
97048
+ async function seedStrandedPlanReviewContinuationImpl(store, input, options = {}) {
97049
+ return seedStrandedPlanReviewContinuation(store.asyncLayer, input, options);
96842
97050
  }
96843
97051
  async function transitionWorkflowWorkItemImpl(store, id, state, patch = {}, tx) {
96844
97052
  return transitionWorkflowWorkItem(store.asyncLayer, id, state, patch, tx);
@@ -100981,6 +101189,37 @@ async function getTaskVerificationRequestAsyncImpl(store, taskId) {
100981
101189
  const row = rows[0];
100982
101190
  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;
100983
101191
  }
101192
+ async function listTaskRecommendationsImpl(store, options) {
101193
+ const layer = store.asyncLayer;
101194
+ const completeColumns = options?.completeColumns ?? await resolveProjectColumnsForRoles(store, ["complete"]);
101195
+ const rawLimit = options?.limit;
101196
+ const rawOffset = options?.offset;
101197
+ const limit = typeof rawLimit === "number" && Number.isFinite(rawLimit) ? Math.min(200, Math.max(1, Math.trunc(rawLimit))) : 50;
101198
+ const offset = typeof rawOffset === "number" && Number.isFinite(rawOffset) ? Math.max(0, Math.trunc(rawOffset)) : 0;
101199
+ const columns = [...completeColumns];
101200
+ if (columns.length === 0) return { items: [], rowOffset: offset, rowLimit: limit, returnedRowCount: 0, totalRowCount: 0, hasMore: false };
101201
+ const filter = and(
101202
+ taskProjectScope(layer),
101203
+ isNull(project_exports.tasks.deletedAt),
101204
+ inArray(project_exports.tasks.column, columns),
101205
+ isNotNull(project_exports.tasks.recommendations),
101206
+ sql`jsonb_array_length(${project_exports.tasks.recommendations}) > 0`
101207
+ );
101208
+ const [countRows, rows] = await Promise.all([
101209
+ layer.db.select({ count: sql`count(*)` }).from(project_exports.tasks).where(filter),
101210
+ layer.db.select({ id: project_exports.tasks.id, title: project_exports.tasks.title, column: project_exports.tasks.column, updatedAt: project_exports.tasks.updatedAt, recommendations: project_exports.tasks.recommendations }).from(project_exports.tasks).where(filter).orderBy(desc(project_exports.tasks.updatedAt), desc(project_exports.tasks.id)).limit(limit).offset(offset)
101211
+ ]);
101212
+ const items = [];
101213
+ for (const row of rows) {
101214
+ if (!Array.isArray(row.recommendations) || row.recommendations.length === 0) continue;
101215
+ for (const recommendation of row.recommendations) {
101216
+ items.push({ taskId: row.id, taskTitle: row.title ?? void 0, taskColumn: row.column, updatedAt: row.updatedAt, recommendation });
101217
+ }
101218
+ }
101219
+ const totalRowCount = Number(countRows[0]?.count ?? 0);
101220
+ const returnedRowCount = rows.length;
101221
+ return { items, rowOffset: offset, rowLimit: limit, returnedRowCount, totalRowCount, hasMore: offset + returnedRowCount < totalRowCount };
101222
+ }
100984
101223
  var LEGACY_REVIEW_LANES5, LEGACY_ARCHIVE_LANES2;
100985
101224
  var init_reads = __esm({
100986
101225
  "../core/src/task-store/reads.ts"() {
@@ -101003,6 +101242,7 @@ var init_reads = __esm({
101003
101242
  init_retry_summary();
101004
101243
  init_errors3();
101005
101244
  init_project_lane_vocabulary();
101245
+ init_data_layer();
101006
101246
  init_async_persistence();
101007
101247
  init_async_search();
101008
101248
  init_async_archive_db();
@@ -103109,6 +103349,9 @@ var init_store = __esm({
103109
103349
  async clearWorkflowRunStepInstancesAsync(taskId, keepRunId) {
103110
103350
  return clearWorkflowRunStepInstancesAsyncImpl(this, taskId, keepRunId);
103111
103351
  }
103352
+ async listTaskRecommendations(options) {
103353
+ return listTaskRecommendationsImpl(this, options);
103354
+ }
103112
103355
  async listTasksForGithubTrackingReconcile(options) {
103113
103356
  return listTasksForGithubTrackingReconcileImpl(this, options);
103114
103357
  }
@@ -103835,8 +104078,8 @@ var init_store = __esm({
103835
104078
  async replaceActiveTaskWorkflowContinuation(input) {
103836
104079
  return replaceActiveTaskWorkflowContinuationImpl(this, input);
103837
104080
  }
103838
- async seedStrandedPlanReviewContinuation(input) {
103839
- return seedStrandedPlanReviewContinuationImpl(this, input);
104081
+ async seedStrandedPlanReviewContinuation(input, options = {}) {
104082
+ return seedStrandedPlanReviewContinuationImpl(this, input, options);
103840
104083
  }
103841
104084
  async transitionWorkflowWorkItem(id, state, patch = {}, tx) {
103842
104085
  return transitionWorkflowWorkItemImpl(this, id, state, patch, tx);
@@ -103919,7 +104162,11 @@ var init_store = __esm({
103919
104162
  /** PostgreSQL-authoritative audit reader; sync fallback remains for test doubles. */
103920
104163
  async getRunAuditEventsAsync(options = {}) {
103921
104164
  if (this.asyncLayer) {
103922
- const events = await queryRunAuditEvents(this.asyncLayer.db, options);
104165
+ const events = await queryRunAuditEvents(
104166
+ this.asyncLayer.db,
104167
+ options,
104168
+ this.asyncLayer.projectId
104169
+ );
103923
104170
  return events.map((event) => ({
103924
104171
  ...event,
103925
104172
  taskId: event.taskId ?? void 0,
@@ -107497,7 +107744,9 @@ function rangeWhere(query) {
107497
107744
  async function aggregatePluginActivations(dbOrLayer, query = {}) {
107498
107745
  if ("ping" in dbOrLayer) {
107499
107746
  const layer = dbOrLayer;
107500
- const conditions = [];
107747
+ const conditions = [projectScopeFor(project_exports.pluginActivations.projectId, layer.projectId)].filter(
107748
+ (condition) => condition !== void 0
107749
+ );
107501
107750
  if (query.from !== void 0) conditions.push(gte(project_exports.pluginActivations.activatedAt, query.from));
107502
107751
  if (query.to !== void 0) conditions.push(lte(project_exports.pluginActivations.activatedAt, query.to));
107503
107752
  const where2 = conditions.length > 0 ? and(...conditions) : void 0;
@@ -107536,6 +107785,7 @@ async function aggregatePluginActivations(dbOrLayer, query = {}) {
107536
107785
  var init_plugin_activation_analytics = __esm({
107537
107786
  "../core/src/plugins/plugin-activation-analytics.ts"() {
107538
107787
  "use strict";
107788
+ init_data_layer();
107539
107789
  init_drizzle_orm();
107540
107790
  init_schema2();
107541
107791
  }
@@ -130533,6 +130783,18 @@ function rowToMessage2(row) {
130533
130783
  createdAt: row.createdAt
130534
130784
  };
130535
130785
  }
130786
+ function chatMessageProjectConditions(handle, projectId2) {
130787
+ const messageScope = projectScopeFor(project_exports.chatMessages.projectId, projectId2);
130788
+ const sessionScope = projectScopeFor(project_exports.chatSessions.projectId, projectId2);
130789
+ if (!messageScope || !sessionScope) return [];
130790
+ return [
130791
+ messageScope,
130792
+ exists(handle.select({ id: project_exports.chatSessions.id }).from(project_exports.chatSessions).where(and(
130793
+ eq(project_exports.chatSessions.id, project_exports.chatMessages.sessionId),
130794
+ sessionScope
130795
+ )))
130796
+ ];
130797
+ }
130536
130798
  function rowToRoom(row) {
130537
130799
  return {
130538
130800
  id: row.id,
@@ -130704,7 +130966,7 @@ async function replaceChatSessionTags(layer, sessionId, projectId2, tagIds) {
130704
130966
  return attachTags(tx, session);
130705
130967
  });
130706
130968
  }
130707
- async function addChatMessage(handle, message) {
130969
+ async function addChatMessage(handle, message, projectId2) {
130708
130970
  const sanitizedAttachments = message.attachments === void 0 ? void 0 : sanitizeJsonbValue(message.attachments);
130709
130971
  const sanitized = {
130710
130972
  ...message,
@@ -130723,15 +130985,24 @@ async function addChatMessage(handle, message) {
130723
130985
  attachments: sanitizedAttachments ?? null,
130724
130986
  createdAt: sanitized.createdAt
130725
130987
  });
130726
- await handle.update(project_exports.chatSessions).set({ updatedAt: sanitized.createdAt }).where(eq(project_exports.chatSessions.id, sanitized.sessionId));
130988
+ await handle.update(project_exports.chatSessions).set({ updatedAt: sanitized.createdAt }).where(and(
130989
+ eq(project_exports.chatSessions.id, sanitized.sessionId),
130990
+ projectScopeFor(project_exports.chatSessions.projectId, projectId2)
130991
+ ));
130727
130992
  return sanitized;
130728
130993
  }
130729
- async function getChatMessage(handle, id) {
130730
- const rows = await handle.select().from(project_exports.chatMessages).where(eq(project_exports.chatMessages.id, id));
130994
+ async function getChatMessage(handle, id, projectId2) {
130995
+ const rows = await handle.select().from(project_exports.chatMessages).where(and(
130996
+ eq(project_exports.chatMessages.id, id),
130997
+ ...chatMessageProjectConditions(handle, projectId2)
130998
+ ));
130731
130999
  return rows[0] ? rowToMessage2(rows[0]) : void 0;
130732
131000
  }
130733
- async function getChatMessages(handle, sessionId, filter) {
130734
- const conditions = [eq(project_exports.chatMessages.sessionId, sessionId)];
131001
+ async function getChatMessages(handle, sessionId, filter, projectId2) {
131002
+ const conditions = [
131003
+ eq(project_exports.chatMessages.sessionId, sessionId),
131004
+ ...chatMessageProjectConditions(handle, projectId2)
131005
+ ];
130735
131006
  if (filter?.before) {
130736
131007
  conditions.push(lte(project_exports.chatMessages.createdAt, filter.before));
130737
131008
  }
@@ -130741,9 +131012,12 @@ async function getChatMessages(handle, sessionId, filter) {
130741
131012
  const rows = await handle.select().from(project_exports.chatMessages).where(and(...conditions)).orderBy(filter?.order === "desc" ? desc(orderCol) : asc(orderCol)).limit(limit).offset(offset);
130742
131013
  return rows.map(rowToMessage2);
130743
131014
  }
130744
- async function getLastMessageForSessions(handle, sessionIds) {
131015
+ async function getLastMessageForSessions(handle, sessionIds, projectId2) {
130745
131016
  if (sessionIds.length === 0) return /* @__PURE__ */ new Map();
130746
- const rows = await handle.select().from(project_exports.chatMessages).where(inArray(project_exports.chatMessages.sessionId, sessionIds)).orderBy(
131017
+ const rows = await handle.select().from(project_exports.chatMessages).where(and(
131018
+ inArray(project_exports.chatMessages.sessionId, sessionIds),
131019
+ ...chatMessageProjectConditions(handle, projectId2)
131020
+ )).orderBy(
130747
131021
  desc(project_exports.chatMessages.createdAt),
130748
131022
  desc(project_exports.chatMessages.id)
130749
131023
  );
@@ -130909,8 +131183,8 @@ async function setInFlightGeneration(handle, id, inFlightGeneration) {
130909
131183
  await handle.update(project_exports.chatSessions).set({ inFlightGeneration: sanitizedInFlightGeneration }).where(eq(project_exports.chatSessions.id, id));
130910
131184
  return getChatSession(handle, id);
130911
131185
  }
130912
- async function addChatMessageAttachment(handle, sessionId, messageId, attachment) {
130913
- const message = await getChatMessage(handle, messageId);
131186
+ async function addChatMessageAttachment(handle, sessionId, messageId, attachment, projectId2) {
131187
+ const message = await getChatMessage(handle, messageId, projectId2);
130914
131188
  if (!message || message.sessionId !== sessionId) {
130915
131189
  throw new Error(`Message ${messageId} not found in session ${sessionId}`);
130916
131190
  }
@@ -130918,25 +131192,35 @@ async function addChatMessageAttachment(handle, sessionId, messageId, attachment
130918
131192
  ...message.attachments ?? [],
130919
131193
  attachment
130920
131194
  ]);
130921
- await handle.update(project_exports.chatMessages).set({ attachments: updatedAttachments }).where(eq(project_exports.chatMessages.id, messageId));
130922
- const updated = await getChatMessage(handle, messageId);
131195
+ await handle.update(project_exports.chatMessages).set({ attachments: updatedAttachments }).where(and(
131196
+ eq(project_exports.chatMessages.id, messageId),
131197
+ ...chatMessageProjectConditions(handle, projectId2)
131198
+ ));
131199
+ const updated = await getChatMessage(handle, messageId, projectId2);
130923
131200
  if (!updated) throw new Error(`Failed to update message ${messageId}`);
130924
131201
  return updated;
130925
131202
  }
130926
- async function deleteChatMessage(handle, id) {
130927
- const existing = await getChatMessage(handle, id);
131203
+ async function deleteChatMessage(handle, id, projectId2) {
131204
+ const existing = await getChatMessage(handle, id, projectId2);
130928
131205
  if (!existing) return false;
130929
- await handle.delete(project_exports.chatMessages).where(eq(project_exports.chatMessages.id, id));
130930
- await handle.update(project_exports.chatSessions).set({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(project_exports.chatSessions.id, existing.sessionId));
131206
+ await handle.delete(project_exports.chatMessages).where(and(
131207
+ eq(project_exports.chatMessages.id, id),
131208
+ ...chatMessageProjectConditions(handle, projectId2)
131209
+ ));
131210
+ await handle.update(project_exports.chatSessions).set({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and(
131211
+ eq(project_exports.chatSessions.id, existing.sessionId),
131212
+ projectScopeFor(project_exports.chatSessions.projectId, projectId2)
131213
+ ));
130931
131214
  return true;
130932
131215
  }
130933
- async function searchChatSessionsByMessageContent(handle, query, sessionIds) {
131216
+ async function searchChatSessionsByMessageContent(handle, query, sessionIds, projectId2) {
130934
131217
  const trimmed = query.trim();
130935
131218
  if (!trimmed || sessionIds.length === 0) return /* @__PURE__ */ new Map();
130936
131219
  const escaped = trimmed.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
130937
131220
  const rows = await handle.select().from(project_exports.chatMessages).where(and(
130938
131221
  inArray(project_exports.chatMessages.sessionId, sessionIds),
130939
- ilike(project_exports.chatMessages.content, `%${escaped}%`)
131222
+ ilike(project_exports.chatMessages.content, `%${escaped}%`),
131223
+ ...chatMessageProjectConditions(handle, projectId2)
130940
131224
  )).orderBy(
130941
131225
  desc(project_exports.chatMessages.createdAt),
130942
131226
  desc(project_exports.chatMessages.id)
@@ -130950,13 +131234,16 @@ async function searchChatSessionsByMessageContent(handle, query, sessionIds) {
130950
131234
  }
130951
131235
  return result2;
130952
131236
  }
130953
- async function deleteChatMessagesFrom(handle, sessionId, fromMessageId) {
130954
- const orderedRows = await handle.select().from(project_exports.chatMessages).where(eq(project_exports.chatMessages.sessionId, sessionId)).orderBy(
131237
+ async function deleteChatMessagesFrom(handle, sessionId, fromMessageId, projectId2) {
131238
+ const orderedRows = await handle.select().from(project_exports.chatMessages).where(and(
131239
+ eq(project_exports.chatMessages.sessionId, sessionId),
131240
+ ...chatMessageProjectConditions(handle, projectId2)
131241
+ )).orderBy(
130955
131242
  asc(project_exports.chatMessages.createdAt),
130956
131243
  asc(project_exports.chatMessages.id)
130957
131244
  );
130958
131245
  const ordered2 = orderedRows.map(rowToMessage2);
130959
- const target = await getChatMessage(handle, fromMessageId);
131246
+ const target = await getChatMessage(handle, fromMessageId, projectId2);
130960
131247
  if (!target || target.sessionId !== sessionId) {
130961
131248
  return { deletedIds: [], retained: ordered2 };
130962
131249
  }
@@ -130969,19 +131256,28 @@ async function deleteChatMessagesFrom(handle, sessionId, fromMessageId) {
130969
131256
  if (deletedIds.length === 0) {
130970
131257
  return { deletedIds: [], retained };
130971
131258
  }
130972
- await handle.delete(project_exports.chatMessages).where(inArray(project_exports.chatMessages.id, deletedIds));
130973
- await handle.update(project_exports.chatSessions).set({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(project_exports.chatSessions.id, sessionId));
131259
+ await handle.delete(project_exports.chatMessages).where(and(
131260
+ inArray(project_exports.chatMessages.id, deletedIds),
131261
+ ...chatMessageProjectConditions(handle, projectId2)
131262
+ ));
131263
+ await handle.update(project_exports.chatSessions).set({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and(
131264
+ eq(project_exports.chatSessions.id, sessionId),
131265
+ projectScopeFor(project_exports.chatSessions.projectId, projectId2)
131266
+ ));
130974
131267
  return { deletedIds, retained };
130975
131268
  }
130976
- async function updateChatMessageMetadata(handle, messageId, metadata, options) {
130977
- const existing = await getChatMessage(handle, messageId);
131269
+ async function updateChatMessageMetadata(handle, messageId, metadata, options, projectId2) {
131270
+ const existing = await getChatMessage(handle, messageId, projectId2);
130978
131271
  if (!existing) {
130979
131272
  throw new Error(`Message ${messageId} not found`);
130980
131273
  }
130981
131274
  const merge2 = options?.merge !== false;
130982
131275
  const nextMetadata = metadata === null ? merge2 ? existing.metadata : null : merge2 ? { ...existing.metadata ?? {}, ...metadata } : metadata;
130983
- await handle.update(project_exports.chatMessages).set({ metadata: sanitizeJsonbValue(nextMetadata) ?? null }).where(eq(project_exports.chatMessages.id, messageId));
130984
- const updated = await getChatMessage(handle, messageId);
131276
+ await handle.update(project_exports.chatMessages).set({ metadata: sanitizeJsonbValue(nextMetadata) ?? null }).where(and(
131277
+ eq(project_exports.chatMessages.id, messageId),
131278
+ ...chatMessageProjectConditions(handle, projectId2)
131279
+ ));
131280
+ const updated = await getChatMessage(handle, messageId, projectId2);
130985
131281
  if (!updated) {
130986
131282
  throw new Error(`Failed to update message ${messageId}`);
130987
131283
  }
@@ -131363,7 +131659,7 @@ var init_chat_store = __esm({
131363
131659
  attachments: input.attachments,
131364
131660
  createdAt: now
131365
131661
  };
131366
- const created = await addChatMessage(this.asyncLayer.db, message);
131662
+ const created = await addChatMessage(this.asyncLayer.db, message, this.asyncLayer.projectId);
131367
131663
  this.emit("chat:message:added", created);
131368
131664
  return created;
131369
131665
  }
@@ -131371,7 +131667,7 @@ var init_chat_store = __esm({
131371
131667
  * Append a file attachment metadata record to an existing message.
131372
131668
  */
131373
131669
  async addMessageAttachment(sessionId, messageId, attachment) {
131374
- const updated = await addChatMessageAttachment(this.asyncLayer.db, sessionId, messageId, attachment);
131670
+ const updated = await addChatMessageAttachment(this.asyncLayer.db, sessionId, messageId, attachment, this.asyncLayer.projectId);
131375
131671
  this.emit("chat:message:updated", updated);
131376
131672
  return updated;
131377
131673
  }
@@ -131383,7 +131679,7 @@ var init_chat_store = __esm({
131383
131679
  * @returns Array of messages ordered by createdAt ASC (default) or DESC
131384
131680
  */
131385
131681
  async getMessages(sessionId, filter) {
131386
- return getChatMessages(this.asyncLayer.db, sessionId, filter);
131682
+ return getChatMessages(this.asyncLayer.db, sessionId, filter, this.asyncLayer.projectId);
131387
131683
  }
131388
131684
  /**
131389
131685
  * Get a message by ID.
@@ -131392,7 +131688,7 @@ var init_chat_store = __esm({
131392
131688
  * @returns The message, or undefined if not found
131393
131689
  */
131394
131690
  async getMessage(id) {
131395
- return getChatMessage(this.asyncLayer.db, id);
131691
+ return getChatMessage(this.asyncLayer.db, id, this.asyncLayer.projectId);
131396
131692
  }
131397
131693
  /**
131398
131694
  * Get the latest message for each session in the provided list.
@@ -131402,10 +131698,10 @@ var init_chat_store = __esm({
131402
131698
  * @returns Map of sessionId -> latest ChatMessage for that session
131403
131699
  */
131404
131700
  async getLastMessageForSessions(sessionIds) {
131405
- return getLastMessageForSessions(this.asyncLayer.db, sessionIds);
131701
+ return getLastMessageForSessions(this.asyncLayer.db, sessionIds, this.asyncLayer.projectId);
131406
131702
  }
131407
131703
  async hasMessages(sessionId) {
131408
- return (await getChatMessages(this.asyncLayer.db, sessionId, { limit: 1 })).length > 0;
131704
+ return (await getChatMessages(this.asyncLayer.db, sessionId, { limit: 1 }, this.asyncLayer.projectId)).length > 0;
131409
131705
  }
131410
131706
  /**
131411
131707
  * Search sessions by message content (not just title/agentId).
@@ -131433,7 +131729,7 @@ var init_chat_store = __esm({
131433
131729
  if (!trimmed || !sessionIds || sessionIds.length === 0) {
131434
131730
  return /* @__PURE__ */ new Map();
131435
131731
  }
131436
- return searchChatSessionsByMessageContent(this.asyncLayer.db, trimmed, sessionIds);
131732
+ return searchChatSessionsByMessageContent(this.asyncLayer.db, trimmed, sessionIds, this.asyncLayer.projectId);
131437
131733
  }
131438
131734
  /**
131439
131735
  * Delete a message by ID.
@@ -131442,9 +131738,9 @@ var init_chat_store = __esm({
131442
131738
  * @returns true if deleted, false if not found
131443
131739
  */
131444
131740
  async deleteMessage(id) {
131445
- const existing = await getChatMessage(this.asyncLayer.db, id);
131741
+ const existing = await getChatMessage(this.asyncLayer.db, id, this.asyncLayer.projectId);
131446
131742
  if (!existing) return false;
131447
- const deleted = await deleteChatMessage(this.asyncLayer.db, id);
131743
+ const deleted = await deleteChatMessage(this.asyncLayer.db, id, this.asyncLayer.projectId);
131448
131744
  if (deleted) {
131449
131745
  this.emit("chat:message:deleted", id);
131450
131746
  const updatedSession = await this.getSession(existing.sessionId);
@@ -131467,7 +131763,7 @@ var init_chat_store = __esm({
131467
131763
  * @returns deletedIds (in ASC order) and retained messages (pre-edit history, ASC order)
131468
131764
  */
131469
131765
  async deleteMessagesFrom(sessionId, fromMessageId) {
131470
- const result2 = await deleteChatMessagesFrom(this.asyncLayer.db, sessionId, fromMessageId);
131766
+ const result2 = await deleteChatMessagesFrom(this.asyncLayer.db, sessionId, fromMessageId, this.asyncLayer.projectId);
131471
131767
  if (result2.deletedIds.length > 0) {
131472
131768
  for (const id of result2.deletedIds) {
131473
131769
  this.emit("chat:message:deleted", id);
@@ -131485,7 +131781,7 @@ var init_chat_store = __esm({
131485
131781
  * is what lets a later edit rewind losslessly via SessionManager.branch()/resetLeaf().
131486
131782
  */
131487
131783
  async updateMessageMetadata(messageId, metadata, options) {
131488
- const updated = await updateChatMessageMetadata(this.asyncLayer.db, messageId, metadata, options);
131784
+ const updated = await updateChatMessageMetadata(this.asyncLayer.db, messageId, metadata, options, this.asyncLayer.projectId);
131489
131785
  this.emit("chat:message:updated", updated);
131490
131786
  return updated;
131491
131787
  }
@@ -132921,6 +133217,77 @@ var init_task_timing = __esm({
132921
133217
  }
132922
133218
  });
132923
133219
 
133220
+ // ../core/src/task-recommendation-notice.ts
133221
+ import { createHash as createHash20 } from "node:crypto";
133222
+ function registerTaskRecommendationNoticeMailbox(store, mailbox) {
133223
+ mailboxes2.set(store, mailbox);
133224
+ return () => {
133225
+ if (mailboxes2.get(store) === mailbox) mailboxes2.delete(store);
133226
+ };
133227
+ }
133228
+ function getTaskRecommendationNoticeMailbox(store) {
133229
+ return mailboxes2.get(store);
133230
+ }
133231
+ function buildTaskRecommendationNoticeContent(task, recommendations) {
133232
+ const title = task.title?.trim();
133233
+ const heading = title ? `## Recommendations from ${task.id} \u2014 ${title}` : `## Recommendations from ${task.id}`;
133234
+ return [
133235
+ heading,
133236
+ "",
133237
+ "These are optional, non-blocking out-of-scope proposals that were **not** executed.",
133238
+ "",
133239
+ ...recommendations.map((recommendation) => `- **${recommendation.title}** \u2014 \`${recommendation.category}\`: ${recommendation.description}`),
133240
+ "",
133241
+ `Open ${task.id}'s **Recommendations** tab to review them. **Create task** files one through normal guarded intake.`
133242
+ ].join("\n");
133243
+ }
133244
+ function buildTaskRecommendationNoticeIdempotencyKey(taskId, recommendations) {
133245
+ const ids = recommendations.map(({ id }) => id).sort().join("\n");
133246
+ const digest = createHash20("sha256").update(ids, "utf8").digest("hex").slice(0, 16);
133247
+ return `task-recommendation-notice:${taskId}:${digest}`;
133248
+ }
133249
+ async function notifyOperatorOfTaskRecommendations(store, task, recommendations, settings) {
133250
+ try {
133251
+ if (settings.recommendationMailboxNoticeEnabled === false || !recommendations?.length) return false;
133252
+ const mailbox = mailboxes2.get(store);
133253
+ if (!mailbox) return false;
133254
+ await mailbox.sendMessageOnce(
133255
+ {
133256
+ fromId: "system",
133257
+ fromType: "system",
133258
+ toId: DASHBOARD_USER_ID,
133259
+ toType: "user",
133260
+ type: "system",
133261
+ content: buildTaskRecommendationNoticeContent(task, recommendations),
133262
+ metadata: {
133263
+ kind: "task-recommendation-notice",
133264
+ taskId: task.id,
133265
+ recommendationCount: recommendations.length,
133266
+ recommendationIds: recommendations.map(({ id }) => id),
133267
+ categories: recommendations.map(({ category }) => category)
133268
+ }
133269
+ },
133270
+ buildTaskRecommendationNoticeIdempotencyKey(task.id, recommendations)
133271
+ );
133272
+ return true;
133273
+ } catch (error3) {
133274
+ noticeLog2.warn(
133275
+ `Operator recommendation notice failed for ${task.id}: ${error3 instanceof Error ? error3.message : String(error3)}`
133276
+ );
133277
+ return false;
133278
+ }
133279
+ }
133280
+ var noticeLog2, mailboxes2;
133281
+ var init_task_recommendation_notice = __esm({
133282
+ "../core/src/task-recommendation-notice.ts"() {
133283
+ "use strict";
133284
+ init_logger();
133285
+ init_types();
133286
+ noticeLog2 = createLogger("task-recommendation-notice");
133287
+ mailboxes2 = /* @__PURE__ */ new WeakMap();
133288
+ }
133289
+ });
133290
+
132924
133291
  // ../core/src/session-identity-registry.ts
132925
133292
  import { AsyncLocalStorage } from "node:async_hooks";
132926
133293
  import { resolve as resolve23 } from "node:path";
@@ -133770,6 +134137,8 @@ __export(src_exports, {
133770
134137
  buildTaskDeleteNoticeContent: () => buildTaskDeleteNoticeContent,
133771
134138
  buildTaskDeleteNoticeIdempotencyKey: () => buildTaskDeleteNoticeIdempotencyKey,
133772
134139
  buildTaskLineageTrailer: () => buildTaskLineageTrailer,
134140
+ buildTaskRecommendationNoticeContent: () => buildTaskRecommendationNoticeContent,
134141
+ buildTaskRecommendationNoticeIdempotencyKey: () => buildTaskRecommendationNoticeIdempotencyKey,
133773
134142
  buildToolOutputTruncationMarker: () => buildToolOutputTruncationMarker,
133774
134143
  buildTriageHeartbeatGuidance: () => buildTriageHeartbeatGuidance,
133775
134144
  buildTriageMemoryInstructions: () => buildTriageMemoryInstructions,
@@ -134100,6 +134469,7 @@ __export(src_exports, {
134100
134469
  getTaskMergeBlocker: () => getTaskMergeBlocker,
134101
134470
  getTaskMoveDisposer: () => getTaskMoveDisposer,
134102
134471
  getTaskPriorityRank: () => getTaskPriorityRank,
134472
+ getTaskRecommendationNoticeMailbox: () => getTaskRecommendationNoticeMailbox,
134103
134473
  getTemplatesForRole: () => getTemplatesForRole,
134104
134474
  getTotalAgentActiveMs: () => getTotalAgentActiveMs,
134105
134475
  getTrait: () => getTrait,
@@ -134350,6 +134720,7 @@ __export(src_exports, {
134350
134720
  normalizeWorkflowPromptOverrides: () => normalizeWorkflowPromptOverrides,
134351
134721
  normalizeWorkflowReviewFindings: () => normalizeWorkflowReviewFindings,
134352
134722
  notifyOperatorOfNonOperatorDelete: () => notifyOperatorOfNonOperatorDelete,
134723
+ notifyOperatorOfTaskRecommendations: () => notifyOperatorOfTaskRecommendations,
134353
134724
  overseerAdviceSeverityRank: () => overseerAdviceSeverityRank,
134354
134725
  parseAgentManifest: () => parseAgentManifest,
134355
134726
  parseCompanyArchive: () => parseCompanyArchive,
@@ -134459,6 +134830,7 @@ __export(src_exports, {
134459
134830
  registerStepParser: () => registerStepParser,
134460
134831
  registerTaskDeleteNoticeMailbox: () => registerTaskDeleteNoticeMailbox,
134461
134832
  registerTaskMoveDisposer: () => registerTaskMoveDisposer,
134833
+ registerTaskRecommendationNoticeMailbox: () => registerTaskRecommendationNoticeMailbox,
134462
134834
  registerTraitHookImpl: () => registerTraitHookImpl,
134463
134835
  rekeyFallbackProjectPartition: () => rekeyFallbackProjectPartition,
134464
134836
  releaseEmbeddedRuntimeLease: () => releaseEmbeddedRuntimeLease,
@@ -135086,6 +135458,7 @@ var init_src2 = __esm({
135086
135458
  init_task_document_concurrency();
135087
135459
  init_task_delete_attribution();
135088
135460
  init_task_delete_notice();
135461
+ init_task_recommendation_notice();
135089
135462
  init_session_identity_registry();
135090
135463
  init_task_lifecycle_event_retention();
135091
135464
  init_task_lifecycle_consumer_identity();
@@ -135177,7 +135550,7 @@ var init_logger3 = __esm({
135177
135550
  });
135178
135551
 
135179
135552
  // ../engine/src/agents/agent-logger.ts
135180
- import { createHash as createHash20 } from "node:crypto";
135553
+ import { createHash as createHash21 } from "node:crypto";
135181
135554
  function truncateToolResultDetail(value) {
135182
135555
  if (value.length <= TOOL_RESULT_DETAIL_LIMIT) return value;
135183
135556
  return `${value.slice(0, TOOL_RESULT_DETAIL_LIMIT)}${TOOL_RESULT_DETAIL_TRUNCATION_NOTICE}`;
@@ -135266,7 +135639,7 @@ function summarizeToolArgs(name2, args) {
135266
135639
  const json2 = JSON.stringify(args);
135267
135640
  if (!json2 || json2 === "{}" || json2 === "[]") return void 0;
135268
135641
  if (json2.length <= STRUCTURED_ARG_SUMMARY_MAX) return json2;
135269
- const hash3 = createHash20("sha256").update(json2).digest("hex").slice(0, STRUCTURED_ARG_HASH_HEX_LEN);
135642
+ const hash3 = createHash21("sha256").update(json2).digest("hex").slice(0, STRUCTURED_ARG_HASH_HEX_LEN);
135270
135643
  const suffix = `\u2026#${hash3}`;
135271
135644
  const keep = Math.max(0, STRUCTURED_ARG_SUMMARY_MAX - suffix.length);
135272
135645
  return `${json2.slice(0, keep)}${suffix}`;
@@ -135954,7 +136327,13 @@ var init_gating_classifications = __esm({
135954
136327
  "fn_post_room_message",
135955
136328
  "fn_update_identity",
135956
136329
  "fn_reflect_on_performance",
135957
- "fn_read_evaluations"
136330
+ /*
136331
+ FNXC:ToolGovernance 2026-08-12-22:11:
136332
+ fn_agent_read_evaluations is a subtree-scoped read. Authorization is enforced by
136333
+ the extension tool's getChainOfCommand boundary, not by this action classifier.
136334
+ */
136335
+ "fn_read_evaluations",
136336
+ "fn_agent_read_evaluations"
135958
136337
  ]);
135959
136338
  COORDINATION_EXEMPT_TOOLS = [
135960
136339
  "read",
@@ -136014,6 +136393,8 @@ var init_gating_classifications = __esm({
136014
136393
  "fn_post_room_message",
136015
136394
  "fn_memory_append",
136016
136395
  "fn_read_evaluations",
136396
+ // 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.
136397
+ "fn_agent_read_evaluations",
136017
136398
  "fn_update_identity",
136018
136399
  "fn_reflect_on_performance"
136019
136400
  ];
@@ -136038,7 +136419,7 @@ var init_gating_classifications = __esm({
136038
136419
  });
136039
136420
 
136040
136421
  // ../engine/src/agents/agent-action-gate.ts
136041
- import { createHash as createHash21 } from "node:crypto";
136422
+ import { createHash as createHash22 } from "node:crypto";
136042
136423
  function getExemptTools() {
136043
136424
  if (!_exemptTools) {
136044
136425
  _exemptTools = new Set(DEFAULT_EXEMPT_TOOLS);
@@ -136090,7 +136471,7 @@ function evaluateAgentActionGate(params) {
136090
136471
  if (params.toolName === "bash") {
136091
136472
  const command = extractShellCommand(args);
136092
136473
  const git7 = classifyGitCommand(command);
136093
- resourceId = command ? `cmd:${createHash21("sha256").update(command).digest("hex").slice(0, 16)}` : void 0;
136474
+ resourceId = command ? `cmd:${createHash22("sha256").update(command).digest("hex").slice(0, 16)}` : void 0;
136094
136475
  if (git7?.write) {
136095
136476
  category = "git_write";
136096
136477
  operation = git7.operation;
@@ -139622,11 +140003,11 @@ var init_planning_handoff_recovery = __esm({
139622
140003
  // ../engine/src/plan-review-continuation.ts
139623
140004
  async function seedPreReleasePlanReviewContinuation(store, task, ir2, options = {}) {
139624
140005
  const node = resolvePreReleasePlanReviewNode(ir2);
139625
- if (!node || node.column !== task.column) return { seeded: false };
140006
+ if (!node || node.column !== task.column) return { seeded: false, reason: "no-pre-release-plan-review" };
139626
140007
  if (isTaskBlockedOnApproval(task)) return { seeded: false, reason: "awaiting-approval" };
139627
140008
  if (task.paused === true || task.userPaused === true) return { seeded: false, reason: "paused" };
139628
140009
  const items = await store.listWorkflowWorkItemsForTask(task.id);
139629
- const active2 = items.filter((item2) => ACTIVE_WORKFLOW_WORK_ITEM_STATES.includes(item2.state));
140010
+ const active2 = items.filter((item2) => ACTIVE_WORKFLOW_WORK_ITEM_STATES.includes(item2.state) && item2.id !== options.retirePredecessorId);
139630
140011
  if (active2.length > 0) return { seeded: false, reason: "active-continuation" };
139631
140012
  const continuationSequence = items.length;
139632
140013
  const input = {
@@ -139642,6 +140023,9 @@ async function seedPreReleasePlanReviewContinuation(store, task, ir2, options =
139642
140023
  targetColumn: task.column,
139643
140024
  irHash: computeWorkflowIrPin(ir2, node.id).irHash
139644
140025
  };
140026
+ if (options.retirePredecessorId) {
140027
+ return store.seedStrandedPlanReviewContinuation(input, { retirePredecessorId: options.retirePredecessorId });
140028
+ }
139645
140029
  if (options.atomic) return store.seedStrandedPlanReviewContinuation(input);
139646
140030
  const item = await store.replaceActiveTaskWorkflowContinuation(input);
139647
140031
  return { seeded: true, workItemId: item.id };
@@ -139656,7 +140040,7 @@ async function resumeApprovedPlanReviewHandoff(store, task, ir2) {
139656
140040
  if (seeded.seeded) return { resumed: true, reason: "seeded", workItemId: seeded.workItemId };
139657
140041
  return {
139658
140042
  resumed: false,
139659
- reason: seeded.reason ?? "not-plan-in-place"
140043
+ reason: seeded.reason === void 0 || seeded.reason === "no-pre-release-plan-review" ? "not-plan-in-place" : seeded.reason
139660
140044
  };
139661
140045
  }
139662
140046
  function evaluateStrandedHoldContinuation(input) {
@@ -139694,7 +140078,7 @@ var init_plan_review_continuation = __esm({
139694
140078
  });
139695
140079
 
139696
140080
  // ../engine/src/execution/hold-release.ts
139697
- import { createHash as createHash22 } from "node:crypto";
140081
+ import { createHash as createHash23 } from "node:crypto";
139698
140082
  import { readFile as readFile31 } from "node:fs/promises";
139699
140083
  function findColumn2(ir2, columnId) {
139700
140084
  if (ir2.version !== "v2") return void 0;
@@ -139732,9 +140116,9 @@ async function checkAndRecordUnplannedExecutionBlock(store, task, ir2) {
139732
140116
  promptContent = "";
139733
140117
  }
139734
140118
  }
139735
- const promptMarker = promptContent.length > 0 ? createHash22("sha256").update(promptContent).digest("hex") : "missing";
140119
+ const promptMarker = promptContent.length > 0 ? createHash23("sha256").update(promptContent).digest("hex") : "missing";
139736
140120
  const dependencies = [...task.dependencies ?? []].sort();
139737
- const episode = createHash22("sha256").update(JSON.stringify({
140121
+ const episode = createHash23("sha256").update(JSON.stringify({
139738
140122
  planReviewNode,
139739
140123
  promptMarker,
139740
140124
  dependencies,
@@ -169854,7 +170238,7 @@ ${getErrorStderr(error3) ?? ""}`;
169854
170238
  });
169855
170239
 
169856
170240
  // ../engine/src/worktree/secrets-env-writer.ts
169857
- import { createHash as createHash23 } from "node:crypto";
170241
+ import { createHash as createHash24 } from "node:crypto";
169858
170242
  import { execFile as execFile8 } from "node:child_process";
169859
170243
  import { promises as fs6 } from "node:fs";
169860
170244
  import path2 from "node:path";
@@ -169863,7 +170247,7 @@ function isValidFilename(filename) {
169863
170247
  return !!filename && !filename.includes("/") && !filename.includes("\\") && !filename.includes("..") && !filename.includes("\0") && filename !== FINGERPRINT_FILE;
169864
170248
  }
169865
170249
  function sha2562(content) {
169866
- return createHash23("sha256").update(Buffer.from(content, "utf8")).digest("hex");
170250
+ return createHash24("sha256").update(Buffer.from(content, "utf8")).digest("hex");
169867
170251
  }
169868
170252
  function quote(value) {
169869
170253
  return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t")}"`;
@@ -178003,32 +178387,36 @@ var init_scheduler = __esm({
178003
178387
  const hierarchy = await missionStore.getMissionWithHierarchy(mission.id);
178004
178388
  if (!hierarchy) continue;
178005
178389
  for (const slice of hierarchy.milestones.flatMap((milestone) => milestone.slices).filter((slice2) => slice2.status === "active")) {
178006
- const superseded = await missionStore.reconcileSupersededGeneratedFixFeatures(slice.id);
178007
- fixed += superseded.supersededCount;
178008
- if (superseded.supersededCount > 0) {
178009
- const refreshed = await missionStore.getSlice(slice.id);
178010
- if (refreshed?.status === "complete") {
178011
- await this.onSliceComplete(refreshed);
178012
- continue;
178390
+ try {
178391
+ const superseded = await missionStore.reconcileSupersededGeneratedFixFeatures(slice.id);
178392
+ fixed += superseded.supersededCount;
178393
+ if (superseded.supersededCount > 0) {
178394
+ const refreshed = await missionStore.getSlice(slice.id);
178395
+ if (refreshed?.status === "complete") {
178396
+ await this.onSliceComplete(refreshed);
178397
+ continue;
178398
+ }
178013
178399
  }
178014
- }
178015
- const listFeatures2 = missionStore.listFeatures;
178016
- const features = listFeatures2 ? await listFeatures2(slice.id) : slice.features;
178017
- const supersededFeatureIds = new Set(superseded.featureIds ?? []);
178018
- const autoTriage = mission.autopilotEnabled === true || mission.autoAdvance === true;
178019
- for (const feature of features) {
178020
- if (supersededFeatureIds.has(feature.id) || feature.taskId || !autoTriage || feature.status === "blocked") continue;
178021
- if (feature.status !== "defined" && this.isGeneratedFixFeature(feature)) {
178022
- await missionStore.updateFeature(feature.id, { status: "blocked", loopState: "blocked", taskId: void 0 });
178400
+ const listFeatures2 = missionStore.listFeatures;
178401
+ const features = listFeatures2 ? await listFeatures2.call(missionStore, slice.id) : slice.features;
178402
+ const supersededFeatureIds = new Set(superseded.featureIds ?? []);
178403
+ const autoTriage = mission.autopilotEnabled === true || mission.autoAdvance === true;
178404
+ for (const feature of features) {
178405
+ if (supersededFeatureIds.has(feature.id) || feature.taskId || !autoTriage || feature.status === "blocked") continue;
178406
+ if (feature.status !== "defined" && this.isGeneratedFixFeature(feature)) {
178407
+ await missionStore.updateFeature(feature.id, { status: "blocked", loopState: "blocked", taskId: void 0 });
178408
+ fixed++;
178409
+ continue;
178410
+ }
178411
+ if (feature.status !== "defined" && feature.status !== "triaged" && feature.status !== "in-progress") continue;
178412
+ const ready = feature.status === "defined" ? feature : await missionStore.updateFeature(feature.id, { status: "defined", loopState: "idle", taskId: void 0 });
178413
+ if (ready !== feature) fixed++;
178414
+ const triaged = await missionStore.triageFeature(ready.id);
178023
178415
  fixed++;
178024
- continue;
178416
+ if (triaged.taskId) await this.emitStrandedFeatureTriageAudit(mission.id, slice.id, triaged.id, triaged.taskId);
178025
178417
  }
178026
- if (feature.status !== "defined" && feature.status !== "triaged" && feature.status !== "in-progress") continue;
178027
- const ready = feature.status === "defined" ? feature : await missionStore.updateFeature(feature.id, { status: "defined", loopState: "idle", taskId: void 0 });
178028
- if (ready !== feature) fixed++;
178029
- const triaged = await missionStore.triageFeature(ready.id);
178030
- fixed++;
178031
- if (triaged.taskId) await this.emitStrandedFeatureTriageAudit(mission.id, slice.id, triaged.id, triaged.taskId);
178418
+ } catch (error3) {
178419
+ schedulerLog.error(`Error during active mission recovery reconciliation for slice ${slice.id}:`, error3);
178032
178420
  }
178033
178421
  }
178034
178422
  }
@@ -178266,7 +178654,7 @@ var init_mission_feature_sync = __esm({
178266
178654
  // ../engine/src/agent-tools.ts
178267
178655
  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";
178268
178656
  import { existsSync as existsSync52 } from "node:fs";
178269
- import { createHash as createHash24, randomUUID as randomUUID46 } from "node:crypto";
178657
+ import { createHash as createHash25, randomUUID as randomUUID46 } from "node:crypto";
178270
178658
  import { tmpdir as tmpdir5 } from "node:os";
178271
178659
  import { extname as extname4, isAbsolute as isAbsolute14, join as join66, relative as relative14, resolve as resolve37, sep as sep10 } from "node:path";
178272
178660
  import { Type as Type2 } from "@earendil-works/pi-ai";
@@ -178310,7 +178698,7 @@ async function readAgentMemoryWorkspaceLongTerm(rootDir, agentId) {
178310
178698
  }
178311
178699
  }
178312
178700
  function qmdAgentMemoryCollectionName2(rootDir, agentId) {
178313
- const hash3 = createHash24("sha1").update(`${rootDir}:${agentId}`).digest("hex").slice(0, 12);
178701
+ const hash3 = createHash25("sha1").update(`${rootDir}:${agentId}`).digest("hex").slice(0, 12);
178314
178702
  return `fusion-agent-memory-${sanitizeAgentMemoryId(agentId).toLowerCase()}-${hash3}`;
178315
178703
  }
178316
178704
  function buildQmdAgentMemoryCollectionAddArgs(rootDir, agentId) {
@@ -189887,7 +190275,7 @@ var init_agent_heartbeat_error_recovery = __esm({
189887
190275
 
189888
190276
  // ../engine/src/agent-heartbeat.ts
189889
190277
  import { Type as Type3 } from "@earendil-works/pi-ai";
189890
- import { createHash as createHash25 } from "node:crypto";
190278
+ import { createHash as createHash26 } from "node:crypto";
189891
190279
  async function resolveHeartbeatReboundColumn(taskStore, taskId) {
189892
190280
  try {
189893
190281
  return resolveReboundTarget(await resolveWorkflowIrForTask(taskStore, taskId)) ?? "todo";
@@ -190042,7 +190430,7 @@ function truncatePrompt(text2, maxChars) {
190042
190430
  ... (truncated, ${text2.length} chars)`;
190043
190431
  }
190044
190432
  function shortContentHash(value) {
190045
- return createHash25("sha256").update(value).digest("hex").slice(0, 8);
190433
+ return createHash26("sha256").update(value).digest("hex").slice(0, 8);
190046
190434
  }
190047
190435
  function buildIdentitySnapshot(args) {
190048
190436
  const { agent, resolvedInstructions, workspaceMemory } = args;
@@ -197931,7 +198319,7 @@ var init_verification_utils = __esm({
197931
198319
 
197932
198320
  // ../engine/src/merge/merge-dependency-sync.ts
197933
198321
  import { exec as exec25 } from "node:child_process";
197934
- import { createHash as createHash26 } from "node:crypto";
198322
+ import { createHash as createHash27 } from "node:crypto";
197935
198323
  import { existsSync as existsSync55, readFileSync as readFileSync17, writeFileSync as writeFileSync6 } from "node:fs";
197936
198324
  import { join as join69 } from "node:path";
197937
198325
  import { promisify as promisify35 } from "node:util";
@@ -197958,7 +198346,7 @@ function computeLockfileHash(rootDir) {
197958
198346
  const p2 = join69(rootDir, name2);
197959
198347
  if (existsSync55(p2)) {
197960
198348
  try {
197961
- return createHash26("sha256").update(readFileSync17(p2)).digest("hex");
198349
+ return createHash27("sha256").update(readFileSync17(p2)).digest("hex");
197962
198350
  } catch {
197963
198351
  return null;
197964
198352
  }
@@ -200500,7 +200888,7 @@ var init_mesh_lease_manager = __esm({
200500
200888
  });
200501
200889
 
200502
200890
  // ../engine/src/merge/merger-integration-worktree.ts
200503
- import { createHash as createHash27 } from "node:crypto";
200891
+ import { createHash as createHash28 } from "node:crypto";
200504
200892
  import { exec as exec31, execFile as execFile22 } from "node:child_process";
200505
200893
  import { promisify as promisify45 } from "node:util";
200506
200894
  function quoteAutostashArg(value) {
@@ -200623,7 +201011,7 @@ async function gitDirtyFingerprintLocal(rootDir) {
200623
201011
  )
200624
201012
  ]);
200625
201013
  if (!diffOut && !statusOut) return "";
200626
- return createHash27("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
201014
+ return createHash28("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
200627
201015
  } catch {
200628
201016
  return "";
200629
201017
  }
@@ -201609,7 +201997,7 @@ import { execSync as execSync5, exec as exec33 } from "node:child_process";
201609
201997
  import * as childProcess4 from "node:child_process";
201610
201998
  import { promisify as promisify48 } from "node:util";
201611
201999
  import { existsSync as existsSync58, readFileSync as readFileSync20, writeFileSync as writeFileSync8, unlinkSync as unlinkSync3, renameSync as renameSync4 } from "node:fs";
201612
- import { createHash as createHash28 } from "node:crypto";
202000
+ import { createHash as createHash29 } from "node:crypto";
201613
202001
  import { join as join73, resolve as resolve43 } from "node:path";
201614
202002
  import { hostname as hostname2 } from "node:os";
201615
202003
  import { Type as Type4 } from "typebox";
@@ -201857,7 +202245,7 @@ async function gitDirtyFingerprint(rootDir) {
201857
202245
  )
201858
202246
  ]);
201859
202247
  if (!diffOut && !statusOut) return "";
201860
- return createHash28("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
202248
+ return createHash29("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
201861
202249
  } catch {
201862
202250
  return "";
201863
202251
  }
@@ -224280,7 +224668,7 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
224280
224668
  }
224281
224669
  return;
224282
224670
  }
224283
- const duplicateReport = { outcome: "parked" };
224671
+ const duplicateReport = { outcome: "parked", planningWorkItemId };
224284
224672
  if (await this.tryFinalizeExplicitDuplicateMarker(task, written, settings, {
224285
224673
  isReplan,
224286
224674
  feedback
@@ -224299,6 +224687,7 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
224299
224687
  isReplan,
224300
224688
  feedback
224301
224689
  });
224690
+ finalizeReport.planningWorkItemId = planningWorkItemId;
224302
224691
  this.options.onSpecifyComplete?.(task, finalizeReport);
224303
224692
  } finally {
224304
224693
  this.activeSessions.delete(task.id);
@@ -239870,6 +240259,38 @@ var init_execution_block_classifier = __esm({
239870
240259
  }
239871
240260
  });
239872
240261
 
240262
+ // ../engine/src/executor/completion-recommendation-notice.ts
240263
+ function dispatchAcceptedCompletionRecommendationNotice(args) {
240264
+ if (!args.recommendations?.length) return;
240265
+ const pending = (async () => {
240266
+ const settings = args.settings ?? await args.store.getSettings();
240267
+ const task = await args.store.getTask(args.taskId);
240268
+ if (!task || task.deletedAt) return;
240269
+ await notifyOperatorOfTaskRecommendations(
240270
+ args.store,
240271
+ { id: args.taskId, title: args.taskTitle ?? task.title },
240272
+ args.recommendations,
240273
+ settings
240274
+ );
240275
+ })();
240276
+ pendingNotices.add(pending);
240277
+ void pending.catch((error3) => {
240278
+ args.log?.warn(
240279
+ `Failed to dispatch recommendation mailbox notice for ${args.taskId}: ${error3 instanceof Error ? error3.message : String(error3)}`
240280
+ );
240281
+ }).finally(() => {
240282
+ pendingNotices.delete(pending);
240283
+ });
240284
+ }
240285
+ var pendingNotices;
240286
+ var init_completion_recommendation_notice = __esm({
240287
+ "../engine/src/executor/completion-recommendation-notice.ts"() {
240288
+ "use strict";
240289
+ init_src2();
240290
+ pendingNotices = /* @__PURE__ */ new Set();
240291
+ }
240292
+ });
240293
+
239873
240294
  // ../engine/src/executor/create-task-done-tool.ts
239874
240295
  import { Type as Type9 } from "@earendil-works/pi-ai";
239875
240296
  function createTaskDoneTool(deps, taskId, worktreePath, promptContent, codeReviewVerdicts, onDone, audit) {
@@ -240206,6 +240627,16 @@ ${rerunSuffix}`
240206
240627
  bulkCompletionRefusalAt: null
240207
240628
  });
240208
240629
  await store.logEntry(taskId, "Task marked done by agent", void 0, deps.getRunContextFor(taskId));
240630
+ if (completionRecommendations !== void 0) {
240631
+ dispatchAcceptedCompletionRecommendationNotice({
240632
+ store,
240633
+ taskId,
240634
+ taskTitle: task.title,
240635
+ recommendations: completionRecommendations,
240636
+ settings,
240637
+ log: executorLog
240638
+ });
240639
+ }
240209
240640
  const latestTask = await store.getTask(taskId);
240210
240641
  let latestColumn = latestTask.column;
240211
240642
  if (latestColumn === await resolveReboundColumnFor(store, taskId)) {
@@ -240244,6 +240675,7 @@ var init_create_task_done_tool = __esm({
240244
240675
  init_completion_predicates();
240245
240676
  init_task_done_refusal_handler();
240246
240677
  init_validate_completion_recommendations();
240678
+ init_completion_recommendation_notice();
240247
240679
  }
240248
240680
  });
240249
240681
 
@@ -251570,6 +252002,16 @@ ${rerunSuffix}` });
251570
252002
  if (latestColumn === await resolveWipTargetForTask(deps.store, task.id) && !hardPauseActive && beforeWatchdog && !beforeWatchdog.deletedAt && !(rejectIfPaused && (beforeWatchdog.paused || beforeWatchdog.userPaused))) {
251571
252003
  deps.scheduleCompletedTaskWatchdog(task.id, "fn_task_done");
251572
252004
  }
252005
+ if (recommendations !== void 0) {
252006
+ dispatchAcceptedCompletionRecommendationNotice({
252007
+ store: deps.store,
252008
+ taskId: task.id,
252009
+ taskTitle: task.title,
252010
+ recommendations,
252011
+ settings,
252012
+ log: executorLog
252013
+ });
252014
+ }
251573
252015
  return { completed: true, hardPauseActive };
251574
252016
  } catch (error3) {
251575
252017
  await restoreNoCommitsExpected();
@@ -251628,6 +252070,7 @@ var init_plan_review_no_op = __esm({
251628
252070
  init_src2();
251629
252071
  init_logger3();
251630
252072
  init_lifecycle_columns();
252073
+ init_completion_recommendation_notice();
251631
252074
  }
251632
252075
  });
251633
252076
 
@@ -255136,14 +255579,14 @@ ${stdout.trim()}`);
255136
255579
 
255137
255580
  // ../engine/src/missions/mission-execution-loop.ts
255138
255581
  import { EventEmitter as EventEmitter25 } from "node:events";
255139
- import { createHash as createHash29 } from "node:crypto";
255582
+ import { createHash as createHash30 } from "node:crypto";
255140
255583
  import { exec as exec56 } from "node:child_process";
255141
255584
  import { promisify as promisify74 } from "node:util";
255142
255585
  function quoteShellArg7(value) {
255143
255586
  return `'${value.replace(/'/g, `'\\''`)}'`;
255144
255587
  }
255145
255588
  function fingerprintMissionValidationInput(landedSha, provider, modelId, systemPrompt, userPrompt) {
255146
- return createHash29("sha256").update(Buffer.from(JSON.stringify(["mission-validation-input-v1", landedSha, provider, modelId, systemPrompt, userPrompt]), "utf8")).digest("hex");
255589
+ return createHash30("sha256").update(Buffer.from(JSON.stringify(["mission-validation-input-v1", landedSha, provider, modelId, systemPrompt, userPrompt]), "utf8")).digest("hex");
255147
255590
  }
255148
255591
  var execAsync53, loopLog, VALIDATION_TIMEOUT_MS, MAX_VALIDATION_RESPONSE_BYTES, MAX_VALIDATION_JSON_CANDIDATES, VALIDATION_FAILURE_BUDGET_PER_FINGERPRINT, MissionExecutionLoop;
255149
255592
  var init_mission_execution_loop = __esm({
@@ -266682,9 +267125,9 @@ var init_routine_scheduler = __esm({
266682
267125
  });
266683
267126
 
266684
267127
  // ../engine/src/healing/stuck-task-detector.ts
266685
- import { createHash as createHash30 } from "node:crypto";
267128
+ import { createHash as createHash31 } from "node:crypto";
266686
267129
  function hashToolDetail(detail) {
266687
- return createHash30("sha256").update(detail).digest("hex").slice(0, TOOL_DETAIL_HASH_HEX_LEN);
267130
+ return createHash31("sha256").update(detail).digest("hex").slice(0, TOOL_DETAIL_HASH_HEX_LEN);
266688
267131
  }
266689
267132
  function buildToolFingerprint(toolName, toolDetail) {
266690
267133
  const name2 = toolName.trim().toLowerCase() || "tool";
@@ -270262,7 +270705,7 @@ var init_node_dispatch_validation = __esm({
270262
270705
  });
270263
270706
 
270264
270707
  // ../engine/src/runtimes/in-process-runtime.ts
270265
- import { createHash as createHash31 } from "node:crypto";
270708
+ import { createHash as createHash32 } from "node:crypto";
270266
270709
  import { EventEmitter as EventEmitter26 } from "node:events";
270267
270710
  import { setImmediate as setImmediateCb4 } from "node:timers";
270268
270711
  function createDiscoveryPluginLoaderOptions(scopedStore) {
@@ -270353,11 +270796,46 @@ async function reactToSpecificationComplete(deps) {
270353
270796
  return;
270354
270797
  }
270355
270798
  deps.log(`Specified ${deps.taskId} \u2192 todo`);
270356
- const live = await deps.getTask(deps.taskId);
270357
- if (!live || live.paused || live.userPaused) return;
270358
- const ir2 = await deps.resolveIr(live.id);
270359
- await deps.seed(live, ir2);
270360
- deps.kick();
270799
+ const sleep9 = deps.sleep ?? ((ms2) => new Promise((resolve60) => setTimeout(resolve60, ms2)));
270800
+ const QUIET_PARK_REASONS = /* @__PURE__ */ new Set([
270801
+ "no-pre-release-plan-review",
270802
+ "awaiting-approval",
270803
+ "paused",
270804
+ "plan-review-passed"
270805
+ ]);
270806
+ const RETRY_DELAYS_MS2 = [1e3, 5e3];
270807
+ for (let attempt = 0; ; attempt++) {
270808
+ let failure = null;
270809
+ try {
270810
+ const live = await deps.getTask(deps.taskId);
270811
+ if (!live || live.paused || live.userPaused) return;
270812
+ if (live.status === "needs-replan") {
270813
+ deps.log(`Plan Review handoff for ${deps.taskId} not armed (needs-replan)`);
270814
+ return;
270815
+ }
270816
+ const ir2 = await deps.resolveIr(live.id);
270817
+ const result2 = await deps.seed(live, ir2);
270818
+ if (result2.seeded) {
270819
+ deps.kick();
270820
+ return;
270821
+ }
270822
+ if (result2.reason && QUIET_PARK_REASONS.has(result2.reason)) {
270823
+ deps.log(`Plan Review handoff for ${deps.taskId} not armed (${result2.reason})`);
270824
+ return;
270825
+ }
270826
+ failure = result2.reason ?? "not-seeded";
270827
+ } catch (error3) {
270828
+ failure = error3 instanceof Error ? error3.message : String(error3);
270829
+ }
270830
+ if (attempt >= RETRY_DELAYS_MS2.length) {
270831
+ deps.warn(
270832
+ `Plan Review handoff for ${deps.taskId} failed after ${attempt + 1} attempts (${failure}) \u2014 stranded-continuation self-healing will recover it`
270833
+ );
270834
+ return;
270835
+ }
270836
+ deps.warn(`Plan Review handoff for ${deps.taskId} did not seed (${failure}) \u2014 retrying`);
270837
+ await sleep9(RETRY_DELAYS_MS2[attempt]);
270838
+ }
270361
270839
  }
270362
270840
  async function drainDuePlanningContinuations(deps) {
270363
270841
  const items = await deps.listDue();
@@ -270492,7 +270970,7 @@ function stableNotificationJson(value) {
270492
270970
  return `{${fields.join(",")}}`;
270493
270971
  }
270494
270972
  function buildCliAgentNotificationDedupeKey(input) {
270495
- const notificationFingerprint = createHash31("sha256").update(stableNotificationJson(input.info.notification ?? null)).digest("hex").slice(0, 16);
270973
+ const notificationFingerprint = createHash32("sha256").update(stableNotificationJson(input.info.notification ?? null)).digest("hex").slice(0, 16);
270496
270974
  const waitingEpoch = input.session?.updatedAt ?? "unknown-waiting-epoch";
270497
270975
  return [
270498
270976
  "cli-agent",
@@ -270647,6 +271125,8 @@ var init_in_process_runtime = __esm({
270647
271125
  messageStore;
270648
271126
  /** FNXC:TaskDeleteNotice 2026-07-26-16:10: identity-guarded teardown for the delete-notice mailbox seam. */
270649
271127
  unregisterTaskDeleteNoticeMailbox;
271128
+ /** FNXC:TaskRecommendations 2026-08-13-03:56: identity-guarded teardown for the store-scoped recommendation notice seam. */
271129
+ unregisterTaskRecommendationNoticeMailbox;
270650
271130
  chatStore;
270651
271131
  detachAgentLinkSync;
270652
271132
  /**
@@ -270705,7 +271185,8 @@ var init_in_process_runtime = __esm({
270705
271185
  createTaskStoreForBackend: createTaskStoreForBackend2,
270706
271186
  buildConsumerId: buildConsumerId2,
270707
271187
  createProjectScopedPluginMcpProvider: createProjectScopedPluginMcpProvider2,
270708
- registerTaskDeleteNoticeMailbox: registerTaskDeleteNoticeMailbox2
271188
+ registerTaskDeleteNoticeMailbox: registerTaskDeleteNoticeMailbox2,
271189
+ registerTaskRecommendationNoticeMailbox: registerTaskRecommendationNoticeMailbox2
270709
271190
  } = await Promise.resolve().then(() => (init_src2(), src_exports));
270710
271191
  if (this.config.externalTaskStore) {
270711
271192
  this.taskStore = this.config.externalTaskStore;
@@ -270764,6 +271245,10 @@ var init_in_process_runtime = __esm({
270764
271245
  this.taskStore,
270765
271246
  this.messageStore
270766
271247
  );
271248
+ this.unregisterTaskRecommendationNoticeMailbox = registerTaskRecommendationNoticeMailbox2(
271249
+ this.taskStore,
271250
+ this.messageStore
271251
+ );
270767
271252
  await yieldEventLoop4();
270768
271253
  const pluginLayer = this.taskStore.getAsyncLayer();
270769
271254
  if (!pluginLayer) {
@@ -271255,9 +271740,19 @@ var init_in_process_runtime = __esm({
271255
271740
  outcome: report2.outcome,
271256
271741
  getTask: (id) => Promise.resolve(this.taskStore.getTask(id)),
271257
271742
  resolveIr: (id) => resolveWorkflowIrForTask(this.taskStore, id),
271258
- seed: (task, ir2) => seedPreReleasePlanReviewContinuation(this.taskStore, task, ir2),
271743
+ /*
271744
+ FNXC:PlanningHandoffAtomicity 2026-08-13-03:49:
271745
+ The report carries the planning session's own durable work item id so the
271746
+ seeder can retire that exact predecessor row atomically with the successor
271747
+ install. Without it the seeder raced triage's finally-block terminal
271748
+ transition and bailed on its own caller (529 stranded cards in 18 days).
271749
+ */
271750
+ seed: (task, ir2) => seedPreReleasePlanReviewContinuation(this.taskStore, task, ir2, {
271751
+ retirePredecessorId: report2.planningWorkItemId
271752
+ }),
271259
271753
  kick: () => this.kickWorkflowContinuationProcessor(),
271260
- log: (message) => runtimeLog.log(message)
271754
+ log: (message) => runtimeLog.log(message),
271755
+ warn: (message) => runtimeLog.warn(message)
271261
271756
  }).catch((error3) => {
271262
271757
  runtimeLog.error(`Failed to start Todo plan review for ${t.id}:`, error3);
271263
271758
  });
@@ -271545,6 +272040,8 @@ var init_in_process_runtime = __esm({
271545
272040
  this.backendShutdown = void 0;
271546
272041
  this.unregisterTaskDeleteNoticeMailbox?.();
271547
272042
  this.unregisterTaskDeleteNoticeMailbox = void 0;
272043
+ this.unregisterTaskRecommendationNoticeMailbox?.();
272044
+ this.unregisterTaskRecommendationNoticeMailbox = void 0;
271548
272045
  let stopError;
271549
272046
  try {
271550
272047
  if (this.workflowContinuationTimer) {
@@ -279338,9 +279835,9 @@ import { access as access10, unlink as unlink11, writeFile as writeFile33 } from
279338
279835
  import net2 from "node:net";
279339
279836
  import { platform as platform3, tmpdir as tmpdir12 } from "node:os";
279340
279837
  import { join as join91 } from "node:path";
279341
- import { createHash as createHash32 } from "node:crypto";
279838
+ import { createHash as createHash33 } from "node:crypto";
279342
279839
  function projectHash(projectId2) {
279343
- return createHash32("sha1").update(projectId2).digest("hex").slice(0, 16);
279840
+ return createHash33("sha1").update(projectId2).digest("hex").slice(0, 16);
279344
279841
  }
279345
279842
  function computeEngineSocketPath(projectId2) {
279346
279843
  const hash3 = projectHash(projectId2);
@@ -308074,7 +308571,7 @@ var init_types5 = __esm({
308074
308571
  });
308075
308572
 
308076
308573
  // ../dashboard/src/stt/model-manager.ts
308077
- import { createHash as createHash33, randomUUID as randomUUID57 } from "node:crypto";
308574
+ import { createHash as createHash34, randomUUID as randomUUID57 } from "node:crypto";
308078
308575
  import { mkdir as mkdir33, open as open4, readFile as readFile53, readdir as readdir13, rename as rename12, rm as rm20, stat as stat14, writeFile as writeFile35 } from "node:fs/promises";
308079
308576
  import { join as join109, relative as relative23, resolve as resolve57, sep as sep17 } from "node:path";
308080
308577
  function parseSafeTarListing(listing) {
@@ -308163,7 +308660,7 @@ function createVoiceModelManager(options = {}) {
308163
308660
  if (!response.ok || !response.body) throw Object.assign(new Error("download failed"), { voiceReason: "network" });
308164
308661
  if (!current(g2, signal)) return state;
308165
308662
  const total = Number(response.headers.get("content-length")) || void 0;
308166
- const hash3 = createHash33("sha256");
308663
+ const hash3 = createHash34("sha256");
308167
308664
  let bytes = 0;
308168
308665
  for await (const chunk of response.body) {
308169
308666
  if (!current(g2, signal)) return state;
@@ -312361,6 +312858,50 @@ async function getAgentStore(cwd) {
312361
312858
  const projectStore = await getStore(cwd);
312362
312859
  return new AgentStore({ rootDir: getFusionDir(cwd), asyncLayer: requireProjectLayer(projectStore, "CLI AgentStore") });
312363
312860
  }
312861
+ async function resolveManagerEvaluationTarget(agentStore, agentId, ctx) {
312862
+ const target = await agentStore.resolveAgent(agentId);
312863
+ if (!target) {
312864
+ return {
312865
+ kind: "error",
312866
+ response: {
312867
+ content: [{ type: "text", text: `Agent '${agentId}' not found` }],
312868
+ isError: true,
312869
+ details: { outcome: "not_found", error: "Agent not found", agentId }
312870
+ }
312871
+ };
312872
+ }
312873
+ if (isEphemeralAgent(target)) {
312874
+ return {
312875
+ kind: "error",
312876
+ response: {
312877
+ content: [{ type: "text", text: `ERROR: Cannot evaluate ephemeral/runtime agent ${target.id}` }],
312878
+ isError: true,
312879
+ details: { outcome: "invalid", field: "agent_id", agentId: target.id }
312880
+ }
312881
+ };
312882
+ }
312883
+ const callerAgentId = typeof ctx.agentId === "string" && ctx.agentId ? ctx.agentId : void 0;
312884
+ if (callerAgentId) {
312885
+ const chain = await agentStore.getChainOfCommand(target.id);
312886
+ const callerIndex = chain.findIndex((agent) => agent.id === callerAgentId);
312887
+ if (callerAgentId === target.id || callerIndex < 1) {
312888
+ return {
312889
+ kind: "error",
312890
+ response: {
312891
+ content: [{ type: "text", text: "ERROR: You can only access evaluations for your own direct or indirect reports." }],
312892
+ isError: true,
312893
+ details: {
312894
+ outcome: "denied",
312895
+ agentId: target.id,
312896
+ callerAgentId,
312897
+ rule: "direct-or-indirect-reports-only"
312898
+ }
312899
+ }
312900
+ };
312901
+ }
312902
+ }
312903
+ return { kind: "allowed", target, callerAgentId };
312904
+ }
312364
312905
  function emitSecretAudit(store, ctx, mutationType, target, metadata) {
312365
312906
  if (!ctx.runId || !ctx.agentId) return;
312366
312907
  try {
@@ -316433,6 +316974,143 @@ Status: ${updated.status}`
316433
316974
  };
316434
316975
  }
316435
316976
  });
316977
+ pi2.registerTool({
316978
+ name: "fn_agent_read_evaluations",
316979
+ label: "fn: Read Report Evaluations",
316980
+ description: "Read ratings, feedback, reflections, and performance data for a direct or indirect report.",
316981
+ promptSnippet: "Review evaluation and reflection results for an agent in your management subtree",
316982
+ promptGuidelines: [
316983
+ "Use to review evaluation data for a direct or indirect report in your management subtree",
316984
+ "Agent callers cannot target themselves, peers, ancestors, or agents outside their subtree",
316985
+ "Operator and CLI calls are privileged and may read any durable agent"
316986
+ ],
316987
+ parameters: Type12.Object({
316988
+ agent_id: Type12.String({ description: "Target agent ID or resolvable name" }),
316989
+ rating_limit: Type12.Optional(Type12.Number({ minimum: 1, maximum: 50, description: "Maximum ratings to return (default 10)" })),
316990
+ reflection_limit: Type12.Optional(Type12.Number({ minimum: 1, maximum: 20, description: "Maximum reflections to return (default 5)" }))
316991
+ }),
316992
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
316993
+ const agentStore = await getAgentStore(ctx.cwd);
316994
+ await agentStore.init();
316995
+ const resolved = await resolveManagerEvaluationTarget(agentStore, params.agent_id, ctx);
316996
+ if (resolved.kind === "error") return resolved.response;
316997
+ const ratingLimit = params.rating_limit ?? 10;
316998
+ const reflectionLimit = params.reflection_limit ?? 5;
316999
+ const reflectionStore = new ReflectionStore({ rootDir: getFusionDir(ctx.cwd) });
317000
+ await reflectionStore.init();
317001
+ const [summary, ratings, latestReflection, reflections, performance4] = await Promise.all([
317002
+ agentStore.getRatingSummary(resolved.target.id),
317003
+ agentStore.getRatings(resolved.target.id, { limit: ratingLimit }),
317004
+ reflectionStore.getLatestReflection(resolved.target.id),
317005
+ reflectionStore.getReflections(resolved.target.id, reflectionLimit),
317006
+ reflectionStore.getPerformanceSummary(resolved.target.id)
317007
+ ]);
317008
+ const hasRatings = ratings.length > 0 || summary.totalRatings > 0;
317009
+ const hasReflections = Boolean(latestReflection) || reflections.length > 0;
317010
+ if (!hasRatings && !hasReflections) {
317011
+ return {
317012
+ content: [{ type: "text", text: `${resolved.target.name} (${resolved.target.id})
317013
+
317014
+ No evaluation data available yet.` }],
317015
+ details: { outcome: "empty", agentId: resolved.target.id, agentName: resolved.target.name, summary, ratings, reflections, latestReflection, performance: performance4 }
317016
+ };
317017
+ }
317018
+ const formatScore2 = (score) => typeof score === "number" && Number.isFinite(score) ? score.toFixed(2) : "n/a";
317019
+ const lines = [
317020
+ `Evaluation Summary: ${resolved.target.name} (${resolved.target.id})`,
317021
+ `- Average score: ${formatScore2(summary.averageScore)}`,
317022
+ `- Trend: ${summary.trend}`,
317023
+ `- Total ratings: ${summary.totalRatings}`
317024
+ ];
317025
+ const categoryEntries = Object.entries(summary.categoryAverages ?? {});
317026
+ if (categoryEntries.length > 0) {
317027
+ lines.push("", "Category averages:");
317028
+ categoryEntries.forEach(([category, score]) => lines.push(`- ${category}: ${formatScore2(score)}`));
317029
+ }
317030
+ const commentedRatings = ratings.filter((rating) => rating.comment?.trim());
317031
+ if (commentedRatings.length > 0) {
317032
+ lines.push("", "Recent rating comments:");
317033
+ commentedRatings.slice(0, 5).forEach((rating) => lines.push(`- [${rating.score}/5] ${rating.comment.trim()}`));
317034
+ }
317035
+ if (latestReflection) {
317036
+ lines.push("", "Latest reflection:", `- Summary: ${latestReflection.summary}`);
317037
+ if (latestReflection.insights.length > 0) {
317038
+ lines.push("- Insights:");
317039
+ latestReflection.insights.forEach((insight) => lines.push(` - ${insight}`));
317040
+ }
317041
+ if (latestReflection.suggestedImprovements.length > 0) {
317042
+ lines.push("- Suggested improvements:");
317043
+ latestReflection.suggestedImprovements.forEach((item) => lines.push(` - ${item}`));
317044
+ }
317045
+ }
317046
+ if (reflections.length > 0) {
317047
+ lines.push("", "Recent reflection history:");
317048
+ reflections.slice(0, 5).forEach((reflection) => lines.push(`- ${reflection.timestamp}: ${reflection.summary}`));
317049
+ }
317050
+ return {
317051
+ content: [{ type: "text", text: lines.join("\n") }],
317052
+ details: { outcome: "read", agentId: resolved.target.id, agentName: resolved.target.name, summary, ratings, reflections, latestReflection, performance: performance4 }
317053
+ };
317054
+ }
317055
+ });
317056
+ pi2.registerTool({
317057
+ name: "fn_agent_evaluation_followup",
317058
+ label: "fn: Record Evaluation Follow-up",
317059
+ description: "Record a coaching evaluation follow-up for a direct or indirect report to read through its self-improvement loop.",
317060
+ promptSnippet: "Record coaching feedback for an agent in your management subtree",
317061
+ promptGuidelines: [
317062
+ "Use to record actionable coaching feedback for a direct or indirect report",
317063
+ "Agent callers cannot target themselves, peers, ancestors, or agents outside their subtree",
317064
+ "Create or delegate separate implementation work with fn_task_create or fn_delegate_task"
317065
+ ],
317066
+ parameters: Type12.Object({
317067
+ agent_id: Type12.String({ description: "Target agent ID or resolvable name" }),
317068
+ score: Type12.Number({ minimum: 1, maximum: 5, description: "Evaluation score from 1 to 5" }),
317069
+ comment: Type12.String({ minLength: 1, maxLength: 4e3, description: "Coaching or follow-up note" }),
317070
+ category: Type12.Optional(Type12.String({ maxLength: 100, description: "Optional evaluation category" })),
317071
+ task_id: Type12.Optional(Type12.String({ description: "Optional related task ID" }))
317072
+ }),
317073
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
317074
+ const gated = await applyAgentPolicyGateForExtensionTool(
317075
+ "fn_agent_evaluation_followup",
317076
+ params,
317077
+ ctx
317078
+ );
317079
+ if (gated) return gated;
317080
+ const agentStore = await getAgentStore(ctx.cwd);
317081
+ await agentStore.init();
317082
+ const resolved = await resolveManagerEvaluationTarget(agentStore, params.agent_id, ctx);
317083
+ if (resolved.kind === "error") return resolved.response;
317084
+ if (!Number.isFinite(params.score) || params.score < 1 || params.score > 5) {
317085
+ return {
317086
+ content: [{ type: "text", text: "ERROR: score must be a finite value from 1 to 5" }],
317087
+ isError: true,
317088
+ details: { outcome: "invalid", field: "score" }
317089
+ };
317090
+ }
317091
+ const comment = params.comment.trim();
317092
+ if (!comment) {
317093
+ return {
317094
+ content: [{ type: "text", text: "ERROR: comment must not be empty" }],
317095
+ isError: true,
317096
+ details: { outcome: "invalid", field: "comment" }
317097
+ };
317098
+ }
317099
+ const raterType = resolved.callerAgentId ? "agent" : "user";
317100
+ const rating = await agentStore.addRating(resolved.target.id, {
317101
+ raterType,
317102
+ ...resolved.callerAgentId ? { raterId: resolved.callerAgentId } : {},
317103
+ score: params.score,
317104
+ ...params.category !== void 0 ? { category: params.category } : {},
317105
+ comment,
317106
+ ...params.task_id !== void 0 ? { taskId: params.task_id } : {}
317107
+ });
317108
+ return {
317109
+ content: [{ type: "text", text: `Recorded evaluation follow-up for ${resolved.target.name} (${resolved.target.id}).` }],
317110
+ details: { outcome: "recorded", agentId: resolved.target.id, ratingId: rating.id, score: rating.score, raterType }
317111
+ };
317112
+ }
317113
+ });
316436
317114
  pi2.registerTool({
316437
317115
  name: "fn_agent_delete",
316438
317116
  label: "fn: Delete Agent",