@runfusion/fusion 0.76.0-beta.2 → 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 (144) hide show
  1. package/dist/bin.js +323 -59
  2. package/dist/child-process-worker.js +274 -42
  3. package/dist/client/.vite/manifest.json +273 -273
  4. package/dist/client/assets/{AgentDetailView-gak4EWTI.js → AgentDetailView-T767kKTg.js} +1 -1
  5. package/dist/client/assets/{AgentPermissionPolicyEditor-DP1QbZL-.js → AgentPermissionPolicyEditor--u9jyUf2.js} +1 -1
  6. package/dist/client/assets/{AgentsView-DAMRTKzI.js → AgentsView-6Ifu7qDr.js} +5 -5
  7. package/dist/client/assets/{ChatView-iE-2gf5P.js → ChatView-BV3lixQi.js} +1 -1
  8. package/dist/client/assets/{CommandCenter-B6RnkLYm.js → CommandCenter-CSRYoveB.js} +1 -1
  9. package/dist/client/assets/{DevServerView-DcFL_LzK.js → DevServerView-B9So_oYx.js} +1 -1
  10. package/dist/client/assets/{DirectoryPicker-CtjqrYoO.js → DirectoryPicker-BhyDL_2m.js} +1 -1
  11. package/dist/client/assets/{DocumentsView-BkAe2S4H.js → DocumentsView-pbERA7O-.js} +1 -1
  12. package/dist/client/assets/{EvalsView-CbGA9dMn.js → EvalsView-DJQ4ltCg.js} +1 -1
  13. package/dist/client/assets/{ExperimentalAgentOnboardingModal-CMgO_S8T.js → ExperimentalAgentOnboardingModal-BwJuzkzB.js} +1 -1
  14. package/dist/client/assets/{GoalsView-_3P9J5sf.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-DwJas8vi.js → MemoryView-BHq1AHPu.js} +1 -1
  18. package/dist/client/assets/{PiExtensionsManager-B_LiW_e9.js → PiExtensionsManager-B8At6fO8.js} +1 -1
  19. package/dist/client/assets/{PluginManager-BkB6WLQc.js → PluginManager-k7hV7_9e.js} +1 -1
  20. package/dist/client/assets/{PullRequestView-DcH5tmYc.js → PullRequestView-B1hZFrWo.js} +1 -1
  21. package/dist/client/assets/{ReportModal-BOEbqtqG.js → ReportModal-C46V_zEa.js} +1 -1
  22. package/dist/client/assets/{ResearchView-Dp6V42pI.js → ResearchView-D8G85BYN.js} +2 -2
  23. package/dist/client/assets/{SecretsView-BCqXHB2L.js → SecretsView-CTri9LRK.js} +1 -1
  24. package/dist/client/assets/{SessionTerminal-CkdoQv4y.js → SessionTerminal-BRf6r4Vp.js} +1 -1
  25. package/dist/client/assets/SettingsModal-D0RBeZik.js +21 -0
  26. package/dist/client/assets/{SettingsModal-Dc7YG6KB.js → SettingsModal-rUQcSTJp.js} +1 -1
  27. package/dist/client/assets/{SettingsTextareaRow-WSkBoAOO.js → SettingsTextareaRow-BirlBcem.js} +1 -1
  28. package/dist/client/assets/{SetupWizardModal-VTrJAsKd.js → SetupWizardModal-xnCBM9EL.js} +2 -2
  29. package/dist/client/assets/{SkillsView-CXeXOxfF.js → SkillsView-O5U0ep50.js} +1 -1
  30. package/dist/client/assets/{WorkflowNodeEditor-DoxLGPRF.js → WorkflowNodeEditor-BX4d2qqF.js} +3 -3
  31. package/dist/client/assets/{agent-import-generation-upc0yw-M.js → agent-import-generation-DnqVkPkz.js} +1 -1
  32. package/dist/client/assets/{agentsOrgChartLayout-C3iumMrW.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-D6jqc3fJ.js → architectureDiagram-3BPJPVTR-gi467xY0.js} +1 -1
  41. package/dist/client/assets/{blockDiagram-GPEHLZMM-DY3AKuPD.js → blockDiagram-GPEHLZMM-B_gpcYiR.js} +1 -1
  42. package/dist/client/assets/{c4Diagram-AAUBKEIU-CpD52V4g.js → c4Diagram-AAUBKEIU-DeylEYsQ.js} +1 -1
  43. package/dist/client/assets/channel-BoE8DeM4.js +1 -0
  44. package/dist/client/assets/{chunk-2J33WTMH-CPeIHyAX.js → chunk-2J33WTMH-BL2WY0HN.js} +1 -1
  45. package/dist/client/assets/{chunk-4BX2VUAB-ivAwI4xM.js → chunk-4BX2VUAB-BkLExGMe.js} +1 -1
  46. package/dist/client/assets/{chunk-55IACEB6-BtVQ5XaZ.js → chunk-55IACEB6-CSTvCF5T.js} +1 -1
  47. package/dist/client/assets/{chunk-727SXJPM-1eap1vDU.js → chunk-727SXJPM-9J8pKk4x.js} +1 -1
  48. package/dist/client/assets/{chunk-AQP2D5EJ-B2NB3BW9.js → chunk-AQP2D5EJ-DXC1vPWr.js} +1 -1
  49. package/dist/client/assets/{chunk-FMBD7UC4-Hk_smNYv.js → chunk-FMBD7UC4-CFRUb-zD.js} +1 -1
  50. package/dist/client/assets/{chunk-ND2GUHAM-BmGCAojk.js → chunk-ND2GUHAM-B87HFYS8.js} +1 -1
  51. package/dist/client/assets/{chunk-QZHKN3VN-D0aXZ8CV.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-DAbu_gnF.js → cose-bilkent-S5V4N54A-D3Edq3L9.js} +1 -1
  55. package/dist/client/assets/{dagre-BM42HDAG-BdmLlrDJ.js → dagre-BM42HDAG-CwwbeQfN.js} +1 -1
  56. package/dist/client/assets/{dashboard-view-CULL1yhL.js → dashboard-view-BLAoW3D4.js} +1 -1
  57. package/dist/client/assets/{dashboard-view-PRCWCSLm.js → dashboard-view-DAlJxqiJ.js} +1 -1
  58. package/dist/client/assets/{dashboard-view-BoFBSXUn.js → dashboard-view-QxQwJFLz.js} +1 -1
  59. package/dist/client/assets/{dashboard-view-CxXMDfNY.js → dashboard-view-aPzfv99T.js} +1 -1
  60. package/dist/client/assets/{diagram-2AECGRRQ-PpqLbVUA.js → diagram-2AECGRRQ-koJWWXE4.js} +1 -1
  61. package/dist/client/assets/{diagram-5GNKFQAL-9rkGmTwh.js → diagram-5GNKFQAL-Yob5cOKT.js} +1 -1
  62. package/dist/client/assets/{diagram-KO2AKTUF-BT-efOMo.js → diagram-KO2AKTUF-DelvFSkK.js} +1 -1
  63. package/dist/client/assets/{diagram-LMA3HP47-w79OWFxg.js → diagram-LMA3HP47-CMput2h2.js} +1 -1
  64. package/dist/client/assets/{diagram-OG6HWLK6-Bye9I5IO.js → diagram-OG6HWLK6-04VXDl79.js} +1 -1
  65. package/dist/client/assets/{erDiagram-TEJ5UH35-aodREgDf.js → erDiagram-TEJ5UH35-C9iP8Uyp.js} +1 -1
  66. package/dist/client/assets/{flowDiagram-I6XJVG4X-D0DKItY3.js → flowDiagram-I6XJVG4X-DENQvu_t.js} +1 -1
  67. package/dist/client/assets/{folder-open-CqvaMJvx.js → folder-open-D-554oji.js} +1 -1
  68. package/dist/client/assets/{ganttDiagram-6RSMTGT7-CZVApkvw.js → ganttDiagram-6RSMTGT7-BqOttZAT.js} +1 -1
  69. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-D_ynwJ4p.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-DWRJyaa3.js → index-CtYRoouR.js} +264 -264
  72. package/dist/client/assets/{infoDiagram-5YYISTIA-pJG6zxjk.js → infoDiagram-5YYISTIA-B5LQQnxt.js} +1 -1
  73. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-nFZ5laUo.js → ishikawaDiagram-YF4QCWOH-DpFEt7D2.js} +1 -1
  74. package/dist/client/assets/{journeyDiagram-JHISSGLW-CWpCaAIe.js → journeyDiagram-JHISSGLW-MELtgu22.js} +1 -1
  75. package/dist/client/assets/{kanban-definition-UN3LZRKU-ATkPspKa.js → kanban-definition-UN3LZRKU-h33PLATd.js} +1 -1
  76. package/dist/client/assets/{mermaid.core-CDXPNs7a.js → mermaid.core-Dk2mUPOq.js} +4 -4
  77. package/dist/client/assets/{mindmap-definition-RKZ34NQL-BkqS9tpe.js → mindmap-definition-RKZ34NQL-B_T4O2-B.js} +1 -1
  78. package/dist/client/assets/{pieDiagram-4H26LBE5-DEUyVLs-.js → pieDiagram-4H26LBE5-B1YIFkRl.js} +1 -1
  79. package/dist/client/assets/{puzzle-DQ0y1-XK.js → puzzle-hvecxlpJ.js} +1 -1
  80. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-ESBJNK5c.js → quadrantDiagram-W4KKPZXB-BoGQR_hO.js} +1 -1
  81. package/dist/client/assets/{requirementDiagram-4Y6WPE33-CqPs7zcv.js → requirementDiagram-4Y6WPE33-BSDmUBIx.js} +1 -1
  82. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-CkFN0m14.js → sankeyDiagram-5OEKKPKP-Dz_GqOa5.js} +1 -1
  83. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-Dx2nDuMO.js → sequenceDiagram-3UESZ5HK-B6twjYUH.js} +1 -1
  84. package/dist/client/assets/{shield-alert-BS05s4RM.js → shield-alert-BX4WRJOv.js} +1 -1
  85. package/dist/client/assets/{standing-instructions-template-xprxYNHm.js → standing-instructions-template-BJY317CC.js} +1 -1
  86. package/dist/client/assets/{stateDiagram-AJRCARHV-EUPmRFGd.js → stateDiagram-AJRCARHV-DuwqWLx-.js} +1 -1
  87. package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-DcQ2mhi5.js → stateDiagram-v2-BHNVJYJU-Bu8Sk187.js} +1 -1
  88. package/dist/client/assets/{timeline-definition-PNZ67QCA-D4YO_3V-.js → timeline-definition-PNZ67QCA-BS6VnqSI.js} +1 -1
  89. package/dist/client/assets/{upload-BjyBtxo7.js → upload-CaDoyaMW.js} +1 -1
  90. package/dist/client/assets/{users-D5ogZ7WQ.js → users-C5uCLwVM.js} +1 -1
  91. package/dist/client/assets/{vennDiagram-CIIHVFJN-BT5_XuQL.js → vennDiagram-CIIHVFJN-CUbzqT5Y.js} +1 -1
  92. package/dist/client/assets/{wardley-L42UT6IY-X_ZMW457.js → wardley-L42UT6IY-Mv-NotFS.js} +1 -1
  93. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-GK9nONHU.js → wardleyDiagram-YWT4CUSO-DuYpvLZX.js} +1 -1
  94. package/dist/client/assets/{xychartDiagram-2RQKCTM6-Dw3zXHAq.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 +285 -47
  99. package/dist/pi-claude-cli/package.json +1 -1
  100. package/dist/plugin-sdk/index.d.ts +23 -0
  101. package/dist/plugin-sdk/index.js +2 -0
  102. package/dist/plugins/fusion-plugin-acp-runtime/bundled.js +2 -0
  103. package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
  104. package/dist/plugins/fusion-plugin-claude-runtime/bundled.js +2 -0
  105. package/dist/plugins/fusion-plugin-claude-runtime/package.json +1 -1
  106. package/dist/plugins/fusion-plugin-cli-printing-press/bundled.js +2 -0
  107. package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
  108. package/dist/plugins/fusion-plugin-compound-engineering/bundled.js +2 -0
  109. package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
  110. package/dist/plugins/fusion-plugin-cursor-runtime/bundled.js +2 -0
  111. package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
  112. package/dist/plugins/fusion-plugin-dependency-graph/bundled.js +2 -0
  113. package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
  114. package/dist/plugins/fusion-plugin-droid-runtime/bundled.js +2 -0
  115. package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
  116. package/dist/plugins/fusion-plugin-grok-runtime/bundled.js +2 -0
  117. package/dist/plugins/fusion-plugin-grok-runtime/package.json +1 -1
  118. package/dist/plugins/fusion-plugin-hermes-runtime/bundled.js +2 -0
  119. package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
  120. package/dist/plugins/fusion-plugin-linear-import/bundled.js +2 -0
  121. package/dist/plugins/fusion-plugin-linear-import/package.json +1 -1
  122. package/dist/plugins/fusion-plugin-omp-runtime/bundled.js +2 -0
  123. package/dist/plugins/fusion-plugin-omp-runtime/package.json +1 -1
  124. package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +2 -0
  125. package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
  126. package/dist/plugins/fusion-plugin-paperclip-runtime/bundled.js +2 -0
  127. package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
  128. package/dist/plugins/fusion-plugin-quality/bundled.js +2 -0
  129. package/dist/plugins/fusion-plugin-quality/package.json +1 -1
  130. package/dist/plugins/fusion-plugin-reports/bundled.js +2 -0
  131. package/dist/plugins/fusion-plugin-reports/package.json +1 -1
  132. package/dist/plugins/fusion-plugin-roadmap/bundled.js +2 -0
  133. package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
  134. package/dist/plugins/fusion-plugin-todos/bundled.js +2 -0
  135. package/dist/plugins/fusion-plugin-todos/package.json +1 -1
  136. package/dist/plugins/fusion-plugin-whatsapp-chat/bundled.js +2 -0
  137. package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
  138. package/package.json +1 -1
  139. package/dist/client/assets/InsightsView-DOhO7xH5.js +0 -11
  140. package/dist/client/assets/SettingsModal-D4Fn4GlS.js +0 -21
  141. package/dist/client/assets/app-DBCGrz-f.js +0 -5
  142. package/dist/client/assets/channel-CaxOu15c.js +0 -1
  143. package/dist/client/assets/classDiagram-4FO5ZUOK-DXIkGbsl.js +0 -1
  144. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DXIkGbsl.js +0 -1
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,
@@ -68640,20 +68642,32 @@ async function replaceActiveTaskWorkflowContinuation(layer, input) {
68640
68642
  return upsertWorkflowWorkItem(layer, input, tx);
68641
68643
  }));
68642
68644
  }
68643
- async function seedStrandedPlanReviewContinuation(layer, input) {
68645
+ async function seedStrandedPlanReviewContinuation(layer, input, options = {}) {
68644
68646
  return layer.transactionImmediate(async (tx) => withTaskWorkflowSerialization(tx, layer.projectId, input.taskId, async () => {
68645
68647
  const active2 = await tx.select({ id: project_exports.workflowWorkItems.id }).from(project_exports.workflowWorkItems).where(and(
68646
68648
  projectScopeFor(project_exports.workflowWorkItems.projectId, layer.projectId),
68647
68649
  eq(project_exports.workflowWorkItems.taskId, input.taskId),
68648
68650
  inArray(project_exports.workflowWorkItems.state, [...ACTIVE_WORKFLOW_WORK_ITEM_STATES])
68649
68651
  ));
68650
- 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" };
68651
68654
  const taskRows = await tx.select({ workflowStepResults: project_exports.tasks.workflowStepResults }).from(project_exports.tasks).where(and(
68652
68655
  projectScopeFor(project_exports.tasks.projectId, layer.projectId),
68653
68656
  eq(project_exports.tasks.id, input.taskId)
68654
68657
  )).limit(1);
68655
68658
  const results = taskRows[0]?.workflowStepResults;
68656
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
+ }
68657
68671
  const item = await upsertWorkflowWorkItem(layer, input, tx);
68658
68672
  return { seeded: true, workItemId: item.id };
68659
68673
  }));
@@ -97031,8 +97045,8 @@ async function upsertWorkflowWorkItemImpl(store, input, tx) {
97031
97045
  async function replaceActiveTaskWorkflowContinuationImpl(store, input) {
97032
97046
  return replaceActiveTaskWorkflowContinuation(store.asyncLayer, input);
97033
97047
  }
97034
- async function seedStrandedPlanReviewContinuationImpl(store, input) {
97035
- return seedStrandedPlanReviewContinuation(store.asyncLayer, input);
97048
+ async function seedStrandedPlanReviewContinuationImpl(store, input, options = {}) {
97049
+ return seedStrandedPlanReviewContinuation(store.asyncLayer, input, options);
97036
97050
  }
97037
97051
  async function transitionWorkflowWorkItemImpl(store, id, state, patch = {}, tx) {
97038
97052
  return transitionWorkflowWorkItem(store.asyncLayer, id, state, patch, tx);
@@ -101175,6 +101189,37 @@ async function getTaskVerificationRequestAsyncImpl(store, taskId) {
101175
101189
  const row = rows[0];
101176
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;
101177
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
+ }
101178
101223
  var LEGACY_REVIEW_LANES5, LEGACY_ARCHIVE_LANES2;
101179
101224
  var init_reads = __esm({
101180
101225
  "../core/src/task-store/reads.ts"() {
@@ -101197,6 +101242,7 @@ var init_reads = __esm({
101197
101242
  init_retry_summary();
101198
101243
  init_errors3();
101199
101244
  init_project_lane_vocabulary();
101245
+ init_data_layer();
101200
101246
  init_async_persistence();
101201
101247
  init_async_search();
101202
101248
  init_async_archive_db();
@@ -103303,6 +103349,9 @@ var init_store = __esm({
103303
103349
  async clearWorkflowRunStepInstancesAsync(taskId, keepRunId) {
103304
103350
  return clearWorkflowRunStepInstancesAsyncImpl(this, taskId, keepRunId);
103305
103351
  }
103352
+ async listTaskRecommendations(options) {
103353
+ return listTaskRecommendationsImpl(this, options);
103354
+ }
103306
103355
  async listTasksForGithubTrackingReconcile(options) {
103307
103356
  return listTasksForGithubTrackingReconcileImpl(this, options);
103308
103357
  }
@@ -104029,8 +104078,8 @@ var init_store = __esm({
104029
104078
  async replaceActiveTaskWorkflowContinuation(input) {
104030
104079
  return replaceActiveTaskWorkflowContinuationImpl(this, input);
104031
104080
  }
104032
- async seedStrandedPlanReviewContinuation(input) {
104033
- return seedStrandedPlanReviewContinuationImpl(this, input);
104081
+ async seedStrandedPlanReviewContinuation(input, options = {}) {
104082
+ return seedStrandedPlanReviewContinuationImpl(this, input, options);
104034
104083
  }
104035
104084
  async transitionWorkflowWorkItem(id, state, patch = {}, tx) {
104036
104085
  return transitionWorkflowWorkItemImpl(this, id, state, patch, tx);
@@ -133168,6 +133217,77 @@ var init_task_timing = __esm({
133168
133217
  }
133169
133218
  });
133170
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
+
133171
133291
  // ../core/src/session-identity-registry.ts
133172
133292
  import { AsyncLocalStorage } from "node:async_hooks";
133173
133293
  import { resolve as resolve23 } from "node:path";
@@ -134017,6 +134137,8 @@ __export(src_exports, {
134017
134137
  buildTaskDeleteNoticeContent: () => buildTaskDeleteNoticeContent,
134018
134138
  buildTaskDeleteNoticeIdempotencyKey: () => buildTaskDeleteNoticeIdempotencyKey,
134019
134139
  buildTaskLineageTrailer: () => buildTaskLineageTrailer,
134140
+ buildTaskRecommendationNoticeContent: () => buildTaskRecommendationNoticeContent,
134141
+ buildTaskRecommendationNoticeIdempotencyKey: () => buildTaskRecommendationNoticeIdempotencyKey,
134020
134142
  buildToolOutputTruncationMarker: () => buildToolOutputTruncationMarker,
134021
134143
  buildTriageHeartbeatGuidance: () => buildTriageHeartbeatGuidance,
134022
134144
  buildTriageMemoryInstructions: () => buildTriageMemoryInstructions,
@@ -134347,6 +134469,7 @@ __export(src_exports, {
134347
134469
  getTaskMergeBlocker: () => getTaskMergeBlocker,
134348
134470
  getTaskMoveDisposer: () => getTaskMoveDisposer,
134349
134471
  getTaskPriorityRank: () => getTaskPriorityRank,
134472
+ getTaskRecommendationNoticeMailbox: () => getTaskRecommendationNoticeMailbox,
134350
134473
  getTemplatesForRole: () => getTemplatesForRole,
134351
134474
  getTotalAgentActiveMs: () => getTotalAgentActiveMs,
134352
134475
  getTrait: () => getTrait,
@@ -134597,6 +134720,7 @@ __export(src_exports, {
134597
134720
  normalizeWorkflowPromptOverrides: () => normalizeWorkflowPromptOverrides,
134598
134721
  normalizeWorkflowReviewFindings: () => normalizeWorkflowReviewFindings,
134599
134722
  notifyOperatorOfNonOperatorDelete: () => notifyOperatorOfNonOperatorDelete,
134723
+ notifyOperatorOfTaskRecommendations: () => notifyOperatorOfTaskRecommendations,
134600
134724
  overseerAdviceSeverityRank: () => overseerAdviceSeverityRank,
134601
134725
  parseAgentManifest: () => parseAgentManifest,
134602
134726
  parseCompanyArchive: () => parseCompanyArchive,
@@ -134706,6 +134830,7 @@ __export(src_exports, {
134706
134830
  registerStepParser: () => registerStepParser,
134707
134831
  registerTaskDeleteNoticeMailbox: () => registerTaskDeleteNoticeMailbox,
134708
134832
  registerTaskMoveDisposer: () => registerTaskMoveDisposer,
134833
+ registerTaskRecommendationNoticeMailbox: () => registerTaskRecommendationNoticeMailbox,
134709
134834
  registerTraitHookImpl: () => registerTraitHookImpl,
134710
134835
  rekeyFallbackProjectPartition: () => rekeyFallbackProjectPartition,
134711
134836
  releaseEmbeddedRuntimeLease: () => releaseEmbeddedRuntimeLease,
@@ -135333,6 +135458,7 @@ var init_src2 = __esm({
135333
135458
  init_task_document_concurrency();
135334
135459
  init_task_delete_attribution();
135335
135460
  init_task_delete_notice();
135461
+ init_task_recommendation_notice();
135336
135462
  init_session_identity_registry();
135337
135463
  init_task_lifecycle_event_retention();
135338
135464
  init_task_lifecycle_consumer_identity();
@@ -135424,7 +135550,7 @@ var init_logger3 = __esm({
135424
135550
  });
135425
135551
 
135426
135552
  // ../engine/src/agents/agent-logger.ts
135427
- import { createHash as createHash20 } from "node:crypto";
135553
+ import { createHash as createHash21 } from "node:crypto";
135428
135554
  function truncateToolResultDetail(value) {
135429
135555
  if (value.length <= TOOL_RESULT_DETAIL_LIMIT) return value;
135430
135556
  return `${value.slice(0, TOOL_RESULT_DETAIL_LIMIT)}${TOOL_RESULT_DETAIL_TRUNCATION_NOTICE}`;
@@ -135513,7 +135639,7 @@ function summarizeToolArgs(name2, args) {
135513
135639
  const json2 = JSON.stringify(args);
135514
135640
  if (!json2 || json2 === "{}" || json2 === "[]") return void 0;
135515
135641
  if (json2.length <= STRUCTURED_ARG_SUMMARY_MAX) return json2;
135516
- 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);
135517
135643
  const suffix = `\u2026#${hash3}`;
135518
135644
  const keep = Math.max(0, STRUCTURED_ARG_SUMMARY_MAX - suffix.length);
135519
135645
  return `${json2.slice(0, keep)}${suffix}`;
@@ -136293,7 +136419,7 @@ var init_gating_classifications = __esm({
136293
136419
  });
136294
136420
 
136295
136421
  // ../engine/src/agents/agent-action-gate.ts
136296
- import { createHash as createHash21 } from "node:crypto";
136422
+ import { createHash as createHash22 } from "node:crypto";
136297
136423
  function getExemptTools() {
136298
136424
  if (!_exemptTools) {
136299
136425
  _exemptTools = new Set(DEFAULT_EXEMPT_TOOLS);
@@ -136345,7 +136471,7 @@ function evaluateAgentActionGate(params) {
136345
136471
  if (params.toolName === "bash") {
136346
136472
  const command = extractShellCommand(args);
136347
136473
  const git7 = classifyGitCommand(command);
136348
- 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;
136349
136475
  if (git7?.write) {
136350
136476
  category = "git_write";
136351
136477
  operation = git7.operation;
@@ -139877,11 +140003,11 @@ var init_planning_handoff_recovery = __esm({
139877
140003
  // ../engine/src/plan-review-continuation.ts
139878
140004
  async function seedPreReleasePlanReviewContinuation(store, task, ir2, options = {}) {
139879
140005
  const node = resolvePreReleasePlanReviewNode(ir2);
139880
- 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" };
139881
140007
  if (isTaskBlockedOnApproval(task)) return { seeded: false, reason: "awaiting-approval" };
139882
140008
  if (task.paused === true || task.userPaused === true) return { seeded: false, reason: "paused" };
139883
140009
  const items = await store.listWorkflowWorkItemsForTask(task.id);
139884
- 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);
139885
140011
  if (active2.length > 0) return { seeded: false, reason: "active-continuation" };
139886
140012
  const continuationSequence = items.length;
139887
140013
  const input = {
@@ -139897,6 +140023,9 @@ async function seedPreReleasePlanReviewContinuation(store, task, ir2, options =
139897
140023
  targetColumn: task.column,
139898
140024
  irHash: computeWorkflowIrPin(ir2, node.id).irHash
139899
140025
  };
140026
+ if (options.retirePredecessorId) {
140027
+ return store.seedStrandedPlanReviewContinuation(input, { retirePredecessorId: options.retirePredecessorId });
140028
+ }
139900
140029
  if (options.atomic) return store.seedStrandedPlanReviewContinuation(input);
139901
140030
  const item = await store.replaceActiveTaskWorkflowContinuation(input);
139902
140031
  return { seeded: true, workItemId: item.id };
@@ -139911,7 +140040,7 @@ async function resumeApprovedPlanReviewHandoff(store, task, ir2) {
139911
140040
  if (seeded.seeded) return { resumed: true, reason: "seeded", workItemId: seeded.workItemId };
139912
140041
  return {
139913
140042
  resumed: false,
139914
- 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
139915
140044
  };
139916
140045
  }
139917
140046
  function evaluateStrandedHoldContinuation(input) {
@@ -139949,7 +140078,7 @@ var init_plan_review_continuation = __esm({
139949
140078
  });
139950
140079
 
139951
140080
  // ../engine/src/execution/hold-release.ts
139952
- import { createHash as createHash22 } from "node:crypto";
140081
+ import { createHash as createHash23 } from "node:crypto";
139953
140082
  import { readFile as readFile31 } from "node:fs/promises";
139954
140083
  function findColumn2(ir2, columnId) {
139955
140084
  if (ir2.version !== "v2") return void 0;
@@ -139987,9 +140116,9 @@ async function checkAndRecordUnplannedExecutionBlock(store, task, ir2) {
139987
140116
  promptContent = "";
139988
140117
  }
139989
140118
  }
139990
- 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";
139991
140120
  const dependencies = [...task.dependencies ?? []].sort();
139992
- const episode = createHash22("sha256").update(JSON.stringify({
140121
+ const episode = createHash23("sha256").update(JSON.stringify({
139993
140122
  planReviewNode,
139994
140123
  promptMarker,
139995
140124
  dependencies,
@@ -170109,7 +170238,7 @@ ${getErrorStderr(error3) ?? ""}`;
170109
170238
  });
170110
170239
 
170111
170240
  // ../engine/src/worktree/secrets-env-writer.ts
170112
- import { createHash as createHash23 } from "node:crypto";
170241
+ import { createHash as createHash24 } from "node:crypto";
170113
170242
  import { execFile as execFile8 } from "node:child_process";
170114
170243
  import { promises as fs6 } from "node:fs";
170115
170244
  import path2 from "node:path";
@@ -170118,7 +170247,7 @@ function isValidFilename(filename) {
170118
170247
  return !!filename && !filename.includes("/") && !filename.includes("\\") && !filename.includes("..") && !filename.includes("\0") && filename !== FINGERPRINT_FILE;
170119
170248
  }
170120
170249
  function sha2562(content) {
170121
- return createHash23("sha256").update(Buffer.from(content, "utf8")).digest("hex");
170250
+ return createHash24("sha256").update(Buffer.from(content, "utf8")).digest("hex");
170122
170251
  }
170123
170252
  function quote(value) {
170124
170253
  return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t")}"`;
@@ -178525,7 +178654,7 @@ var init_mission_feature_sync = __esm({
178525
178654
  // ../engine/src/agent-tools.ts
178526
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";
178527
178656
  import { existsSync as existsSync52 } from "node:fs";
178528
- import { createHash as createHash24, randomUUID as randomUUID46 } from "node:crypto";
178657
+ import { createHash as createHash25, randomUUID as randomUUID46 } from "node:crypto";
178529
178658
  import { tmpdir as tmpdir5 } from "node:os";
178530
178659
  import { extname as extname4, isAbsolute as isAbsolute14, join as join66, relative as relative14, resolve as resolve37, sep as sep10 } from "node:path";
178531
178660
  import { Type as Type2 } from "@earendil-works/pi-ai";
@@ -178569,7 +178698,7 @@ async function readAgentMemoryWorkspaceLongTerm(rootDir, agentId) {
178569
178698
  }
178570
178699
  }
178571
178700
  function qmdAgentMemoryCollectionName2(rootDir, agentId) {
178572
- 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);
178573
178702
  return `fusion-agent-memory-${sanitizeAgentMemoryId(agentId).toLowerCase()}-${hash3}`;
178574
178703
  }
178575
178704
  function buildQmdAgentMemoryCollectionAddArgs(rootDir, agentId) {
@@ -190146,7 +190275,7 @@ var init_agent_heartbeat_error_recovery = __esm({
190146
190275
 
190147
190276
  // ../engine/src/agent-heartbeat.ts
190148
190277
  import { Type as Type3 } from "@earendil-works/pi-ai";
190149
- import { createHash as createHash25 } from "node:crypto";
190278
+ import { createHash as createHash26 } from "node:crypto";
190150
190279
  async function resolveHeartbeatReboundColumn(taskStore, taskId) {
190151
190280
  try {
190152
190281
  return resolveReboundTarget(await resolveWorkflowIrForTask(taskStore, taskId)) ?? "todo";
@@ -190301,7 +190430,7 @@ function truncatePrompt(text2, maxChars) {
190301
190430
  ... (truncated, ${text2.length} chars)`;
190302
190431
  }
190303
190432
  function shortContentHash(value) {
190304
- return createHash25("sha256").update(value).digest("hex").slice(0, 8);
190433
+ return createHash26("sha256").update(value).digest("hex").slice(0, 8);
190305
190434
  }
190306
190435
  function buildIdentitySnapshot(args) {
190307
190436
  const { agent, resolvedInstructions, workspaceMemory } = args;
@@ -198190,7 +198319,7 @@ var init_verification_utils = __esm({
198190
198319
 
198191
198320
  // ../engine/src/merge/merge-dependency-sync.ts
198192
198321
  import { exec as exec25 } from "node:child_process";
198193
- import { createHash as createHash26 } from "node:crypto";
198322
+ import { createHash as createHash27 } from "node:crypto";
198194
198323
  import { existsSync as existsSync55, readFileSync as readFileSync17, writeFileSync as writeFileSync6 } from "node:fs";
198195
198324
  import { join as join69 } from "node:path";
198196
198325
  import { promisify as promisify35 } from "node:util";
@@ -198217,7 +198346,7 @@ function computeLockfileHash(rootDir) {
198217
198346
  const p2 = join69(rootDir, name2);
198218
198347
  if (existsSync55(p2)) {
198219
198348
  try {
198220
- return createHash26("sha256").update(readFileSync17(p2)).digest("hex");
198349
+ return createHash27("sha256").update(readFileSync17(p2)).digest("hex");
198221
198350
  } catch {
198222
198351
  return null;
198223
198352
  }
@@ -200759,7 +200888,7 @@ var init_mesh_lease_manager = __esm({
200759
200888
  });
200760
200889
 
200761
200890
  // ../engine/src/merge/merger-integration-worktree.ts
200762
- import { createHash as createHash27 } from "node:crypto";
200891
+ import { createHash as createHash28 } from "node:crypto";
200763
200892
  import { exec as exec31, execFile as execFile22 } from "node:child_process";
200764
200893
  import { promisify as promisify45 } from "node:util";
200765
200894
  function quoteAutostashArg(value) {
@@ -200882,7 +201011,7 @@ async function gitDirtyFingerprintLocal(rootDir) {
200882
201011
  )
200883
201012
  ]);
200884
201013
  if (!diffOut && !statusOut) return "";
200885
- return createHash27("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
201014
+ return createHash28("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
200886
201015
  } catch {
200887
201016
  return "";
200888
201017
  }
@@ -201868,7 +201997,7 @@ import { execSync as execSync5, exec as exec33 } from "node:child_process";
201868
201997
  import * as childProcess4 from "node:child_process";
201869
201998
  import { promisify as promisify48 } from "node:util";
201870
201999
  import { existsSync as existsSync58, readFileSync as readFileSync20, writeFileSync as writeFileSync8, unlinkSync as unlinkSync3, renameSync as renameSync4 } from "node:fs";
201871
- import { createHash as createHash28 } from "node:crypto";
202000
+ import { createHash as createHash29 } from "node:crypto";
201872
202001
  import { join as join73, resolve as resolve43 } from "node:path";
201873
202002
  import { hostname as hostname2 } from "node:os";
201874
202003
  import { Type as Type4 } from "typebox";
@@ -202116,7 +202245,7 @@ async function gitDirtyFingerprint(rootDir) {
202116
202245
  )
202117
202246
  ]);
202118
202247
  if (!diffOut && !statusOut) return "";
202119
- return createHash28("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
202248
+ return createHash29("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
202120
202249
  } catch {
202121
202250
  return "";
202122
202251
  }
@@ -224539,7 +224668,7 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
224539
224668
  }
224540
224669
  return;
224541
224670
  }
224542
- const duplicateReport = { outcome: "parked" };
224671
+ const duplicateReport = { outcome: "parked", planningWorkItemId };
224543
224672
  if (await this.tryFinalizeExplicitDuplicateMarker(task, written, settings, {
224544
224673
  isReplan,
224545
224674
  feedback
@@ -224558,6 +224687,7 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
224558
224687
  isReplan,
224559
224688
  feedback
224560
224689
  });
224690
+ finalizeReport.planningWorkItemId = planningWorkItemId;
224561
224691
  this.options.onSpecifyComplete?.(task, finalizeReport);
224562
224692
  } finally {
224563
224693
  this.activeSessions.delete(task.id);
@@ -240129,6 +240259,38 @@ var init_execution_block_classifier = __esm({
240129
240259
  }
240130
240260
  });
240131
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
+
240132
240294
  // ../engine/src/executor/create-task-done-tool.ts
240133
240295
  import { Type as Type9 } from "@earendil-works/pi-ai";
240134
240296
  function createTaskDoneTool(deps, taskId, worktreePath, promptContent, codeReviewVerdicts, onDone, audit) {
@@ -240465,6 +240627,16 @@ ${rerunSuffix}`
240465
240627
  bulkCompletionRefusalAt: null
240466
240628
  });
240467
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
+ }
240468
240640
  const latestTask = await store.getTask(taskId);
240469
240641
  let latestColumn = latestTask.column;
240470
240642
  if (latestColumn === await resolveReboundColumnFor(store, taskId)) {
@@ -240503,6 +240675,7 @@ var init_create_task_done_tool = __esm({
240503
240675
  init_completion_predicates();
240504
240676
  init_task_done_refusal_handler();
240505
240677
  init_validate_completion_recommendations();
240678
+ init_completion_recommendation_notice();
240506
240679
  }
240507
240680
  });
240508
240681
 
@@ -251829,6 +252002,16 @@ ${rerunSuffix}` });
251829
252002
  if (latestColumn === await resolveWipTargetForTask(deps.store, task.id) && !hardPauseActive && beforeWatchdog && !beforeWatchdog.deletedAt && !(rejectIfPaused && (beforeWatchdog.paused || beforeWatchdog.userPaused))) {
251830
252003
  deps.scheduleCompletedTaskWatchdog(task.id, "fn_task_done");
251831
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
+ }
251832
252015
  return { completed: true, hardPauseActive };
251833
252016
  } catch (error3) {
251834
252017
  await restoreNoCommitsExpected();
@@ -251887,6 +252070,7 @@ var init_plan_review_no_op = __esm({
251887
252070
  init_src2();
251888
252071
  init_logger3();
251889
252072
  init_lifecycle_columns();
252073
+ init_completion_recommendation_notice();
251890
252074
  }
251891
252075
  });
251892
252076
 
@@ -255395,14 +255579,14 @@ ${stdout.trim()}`);
255395
255579
 
255396
255580
  // ../engine/src/missions/mission-execution-loop.ts
255397
255581
  import { EventEmitter as EventEmitter25 } from "node:events";
255398
- import { createHash as createHash29 } from "node:crypto";
255582
+ import { createHash as createHash30 } from "node:crypto";
255399
255583
  import { exec as exec56 } from "node:child_process";
255400
255584
  import { promisify as promisify74 } from "node:util";
255401
255585
  function quoteShellArg7(value) {
255402
255586
  return `'${value.replace(/'/g, `'\\''`)}'`;
255403
255587
  }
255404
255588
  function fingerprintMissionValidationInput(landedSha, provider, modelId, systemPrompt, userPrompt) {
255405
- 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");
255406
255590
  }
255407
255591
  var execAsync53, loopLog, VALIDATION_TIMEOUT_MS, MAX_VALIDATION_RESPONSE_BYTES, MAX_VALIDATION_JSON_CANDIDATES, VALIDATION_FAILURE_BUDGET_PER_FINGERPRINT, MissionExecutionLoop;
255408
255592
  var init_mission_execution_loop = __esm({
@@ -266941,9 +267125,9 @@ var init_routine_scheduler = __esm({
266941
267125
  });
266942
267126
 
266943
267127
  // ../engine/src/healing/stuck-task-detector.ts
266944
- import { createHash as createHash30 } from "node:crypto";
267128
+ import { createHash as createHash31 } from "node:crypto";
266945
267129
  function hashToolDetail(detail) {
266946
- 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);
266947
267131
  }
266948
267132
  function buildToolFingerprint(toolName, toolDetail) {
266949
267133
  const name2 = toolName.trim().toLowerCase() || "tool";
@@ -270521,7 +270705,7 @@ var init_node_dispatch_validation = __esm({
270521
270705
  });
270522
270706
 
270523
270707
  // ../engine/src/runtimes/in-process-runtime.ts
270524
- import { createHash as createHash31 } from "node:crypto";
270708
+ import { createHash as createHash32 } from "node:crypto";
270525
270709
  import { EventEmitter as EventEmitter26 } from "node:events";
270526
270710
  import { setImmediate as setImmediateCb4 } from "node:timers";
270527
270711
  function createDiscoveryPluginLoaderOptions(scopedStore) {
@@ -270612,11 +270796,46 @@ async function reactToSpecificationComplete(deps) {
270612
270796
  return;
270613
270797
  }
270614
270798
  deps.log(`Specified ${deps.taskId} \u2192 todo`);
270615
- const live = await deps.getTask(deps.taskId);
270616
- if (!live || live.paused || live.userPaused) return;
270617
- const ir2 = await deps.resolveIr(live.id);
270618
- await deps.seed(live, ir2);
270619
- 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
+ }
270620
270839
  }
270621
270840
  async function drainDuePlanningContinuations(deps) {
270622
270841
  const items = await deps.listDue();
@@ -270751,7 +270970,7 @@ function stableNotificationJson(value) {
270751
270970
  return `{${fields.join(",")}}`;
270752
270971
  }
270753
270972
  function buildCliAgentNotificationDedupeKey(input) {
270754
- 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);
270755
270974
  const waitingEpoch = input.session?.updatedAt ?? "unknown-waiting-epoch";
270756
270975
  return [
270757
270976
  "cli-agent",
@@ -270906,6 +271125,8 @@ var init_in_process_runtime = __esm({
270906
271125
  messageStore;
270907
271126
  /** FNXC:TaskDeleteNotice 2026-07-26-16:10: identity-guarded teardown for the delete-notice mailbox seam. */
270908
271127
  unregisterTaskDeleteNoticeMailbox;
271128
+ /** FNXC:TaskRecommendations 2026-08-13-03:56: identity-guarded teardown for the store-scoped recommendation notice seam. */
271129
+ unregisterTaskRecommendationNoticeMailbox;
270909
271130
  chatStore;
270910
271131
  detachAgentLinkSync;
270911
271132
  /**
@@ -270964,7 +271185,8 @@ var init_in_process_runtime = __esm({
270964
271185
  createTaskStoreForBackend: createTaskStoreForBackend2,
270965
271186
  buildConsumerId: buildConsumerId2,
270966
271187
  createProjectScopedPluginMcpProvider: createProjectScopedPluginMcpProvider2,
270967
- registerTaskDeleteNoticeMailbox: registerTaskDeleteNoticeMailbox2
271188
+ registerTaskDeleteNoticeMailbox: registerTaskDeleteNoticeMailbox2,
271189
+ registerTaskRecommendationNoticeMailbox: registerTaskRecommendationNoticeMailbox2
270968
271190
  } = await Promise.resolve().then(() => (init_src2(), src_exports));
270969
271191
  if (this.config.externalTaskStore) {
270970
271192
  this.taskStore = this.config.externalTaskStore;
@@ -271023,6 +271245,10 @@ var init_in_process_runtime = __esm({
271023
271245
  this.taskStore,
271024
271246
  this.messageStore
271025
271247
  );
271248
+ this.unregisterTaskRecommendationNoticeMailbox = registerTaskRecommendationNoticeMailbox2(
271249
+ this.taskStore,
271250
+ this.messageStore
271251
+ );
271026
271252
  await yieldEventLoop4();
271027
271253
  const pluginLayer = this.taskStore.getAsyncLayer();
271028
271254
  if (!pluginLayer) {
@@ -271514,9 +271740,19 @@ var init_in_process_runtime = __esm({
271514
271740
  outcome: report2.outcome,
271515
271741
  getTask: (id) => Promise.resolve(this.taskStore.getTask(id)),
271516
271742
  resolveIr: (id) => resolveWorkflowIrForTask(this.taskStore, id),
271517
- 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
+ }),
271518
271753
  kick: () => this.kickWorkflowContinuationProcessor(),
271519
- log: (message) => runtimeLog.log(message)
271754
+ log: (message) => runtimeLog.log(message),
271755
+ warn: (message) => runtimeLog.warn(message)
271520
271756
  }).catch((error3) => {
271521
271757
  runtimeLog.error(`Failed to start Todo plan review for ${t.id}:`, error3);
271522
271758
  });
@@ -271804,6 +272040,8 @@ var init_in_process_runtime = __esm({
271804
272040
  this.backendShutdown = void 0;
271805
272041
  this.unregisterTaskDeleteNoticeMailbox?.();
271806
272042
  this.unregisterTaskDeleteNoticeMailbox = void 0;
272043
+ this.unregisterTaskRecommendationNoticeMailbox?.();
272044
+ this.unregisterTaskRecommendationNoticeMailbox = void 0;
271807
272045
  let stopError;
271808
272046
  try {
271809
272047
  if (this.workflowContinuationTimer) {
@@ -279597,9 +279835,9 @@ import { access as access10, unlink as unlink11, writeFile as writeFile33 } from
279597
279835
  import net2 from "node:net";
279598
279836
  import { platform as platform3, tmpdir as tmpdir12 } from "node:os";
279599
279837
  import { join as join91 } from "node:path";
279600
- import { createHash as createHash32 } from "node:crypto";
279838
+ import { createHash as createHash33 } from "node:crypto";
279601
279839
  function projectHash(projectId2) {
279602
- return createHash32("sha1").update(projectId2).digest("hex").slice(0, 16);
279840
+ return createHash33("sha1").update(projectId2).digest("hex").slice(0, 16);
279603
279841
  }
279604
279842
  function computeEngineSocketPath(projectId2) {
279605
279843
  const hash3 = projectHash(projectId2);
@@ -308333,7 +308571,7 @@ var init_types5 = __esm({
308333
308571
  });
308334
308572
 
308335
308573
  // ../dashboard/src/stt/model-manager.ts
308336
- import { createHash as createHash33, randomUUID as randomUUID57 } from "node:crypto";
308574
+ import { createHash as createHash34, randomUUID as randomUUID57 } from "node:crypto";
308337
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";
308338
308576
  import { join as join109, relative as relative23, resolve as resolve57, sep as sep17 } from "node:path";
308339
308577
  function parseSafeTarListing(listing) {
@@ -308422,7 +308660,7 @@ function createVoiceModelManager(options = {}) {
308422
308660
  if (!response.ok || !response.body) throw Object.assign(new Error("download failed"), { voiceReason: "network" });
308423
308661
  if (!current(g2, signal)) return state;
308424
308662
  const total = Number(response.headers.get("content-length")) || void 0;
308425
- const hash3 = createHash33("sha256");
308663
+ const hash3 = createHash34("sha256");
308426
308664
  let bytes = 0;
308427
308665
  for await (const chunk of response.body) {
308428
308666
  if (!current(g2, signal)) return state;