@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
@@ -2145,6 +2145,8 @@ var init_settings_schema = __esm({
2145
2145
  DEFAULT_PROJECT_SETTINGS = {
2146
2146
  // FNXC:TaskRecommendations 2026-08-08-05:02: completion follows-ups stay bounded by default; 0 disables writing them.
2147
2147
  maxRecommendationsPerTask: 3,
2148
+ // FNXC:TaskRecommendations 2026-08-13-03:56: surface completed-task proposals by default; operators can suppress the notice without suppressing capture.
2149
+ recommendationMailboxNoticeEnabled: true,
2148
2150
  globalPause: false,
2149
2151
  globalPauseReason: void 0,
2150
2152
  defaultWorkflowId: void 0,
@@ -68718,20 +68720,32 @@ async function replaceActiveTaskWorkflowContinuation(layer, input) {
68718
68720
  return upsertWorkflowWorkItem(layer, input, tx);
68719
68721
  }));
68720
68722
  }
68721
- async function seedStrandedPlanReviewContinuation(layer, input) {
68723
+ async function seedStrandedPlanReviewContinuation(layer, input, options = {}) {
68722
68724
  return layer.transactionImmediate(async (tx) => withTaskWorkflowSerialization(tx, layer.projectId, input.taskId, async () => {
68723
68725
  const active2 = await tx.select({ id: project_exports.workflowWorkItems.id }).from(project_exports.workflowWorkItems).where(and(
68724
68726
  projectScopeFor(project_exports.workflowWorkItems.projectId, layer.projectId),
68725
68727
  eq(project_exports.workflowWorkItems.taskId, input.taskId),
68726
68728
  inArray(project_exports.workflowWorkItems.state, [...ACTIVE_WORKFLOW_WORK_ITEM_STATES])
68727
68729
  ));
68728
- if (active2.length > 0) return { seeded: false, reason: "active-continuation" };
68730
+ const blocking = options.retirePredecessorId ? active2.filter((row) => row.id !== options.retirePredecessorId) : active2;
68731
+ if (blocking.length > 0) return { seeded: false, reason: "active-continuation" };
68729
68732
  const taskRows = await tx.select({ workflowStepResults: project_exports.tasks.workflowStepResults }).from(project_exports.tasks).where(and(
68730
68733
  projectScopeFor(project_exports.tasks.projectId, layer.projectId),
68731
68734
  eq(project_exports.tasks.id, input.taskId)
68732
68735
  )).limit(1);
68733
68736
  const results = taskRows[0]?.workflowStepResults;
68734
68737
  if (results?.some(isPlanReviewSatisfied)) return { seeded: false, reason: "plan-review-passed" };
68738
+ if (options.retirePredecessorId) {
68739
+ const predRows = await tx.select().from(project_exports.workflowWorkItems).where(and(
68740
+ projectScopeFor(project_exports.workflowWorkItems.projectId, layer.projectId),
68741
+ eq(project_exports.workflowWorkItems.id, options.retirePredecessorId),
68742
+ eq(project_exports.workflowWorkItems.taskId, input.taskId)
68743
+ )).limit(1);
68744
+ const pred = predRows[0];
68745
+ if (pred && !isTerminalWorkflowWorkItemState(normalizeWorkflowWorkItemState(pred.state))) {
68746
+ await transitionWorkflowWorkItem(layer, pred.id, "succeeded", { leaseOwner: null, leaseExpiresAt: null, lastError: null }, tx);
68747
+ }
68748
+ }
68735
68749
  const item = await upsertWorkflowWorkItem(layer, input, tx);
68736
68750
  return { seeded: true, workItemId: item.id };
68737
68751
  }));
@@ -97109,8 +97123,8 @@ async function upsertWorkflowWorkItemImpl(store, input, tx) {
97109
97123
  async function replaceActiveTaskWorkflowContinuationImpl(store, input) {
97110
97124
  return replaceActiveTaskWorkflowContinuation(store.asyncLayer, input);
97111
97125
  }
97112
- async function seedStrandedPlanReviewContinuationImpl(store, input) {
97113
- return seedStrandedPlanReviewContinuation(store.asyncLayer, input);
97126
+ async function seedStrandedPlanReviewContinuationImpl(store, input, options = {}) {
97127
+ return seedStrandedPlanReviewContinuation(store.asyncLayer, input, options);
97114
97128
  }
97115
97129
  async function transitionWorkflowWorkItemImpl(store, id, state, patch = {}, tx) {
97116
97130
  return transitionWorkflowWorkItem(store.asyncLayer, id, state, patch, tx);
@@ -101253,6 +101267,37 @@ async function getTaskVerificationRequestAsyncImpl(store, taskId) {
101253
101267
  const row = rows[0];
101254
101268
  return row ? { taskId: row.taskId, requestId: row.requestId, status: row.status, profile: row.profile, command: row.command, scope: row.scope, requestedBy: row.requestedBy, requestedAt: row.requestedAt, ...row.startedAt ? { startedAt: row.startedAt } : {}, ...row.completedAt ? { completedAt: row.completedAt } : {}, ...row.result ? { result: row.result } : {}, ...row.rejectionReason ? { rejectionReason: row.rejectionReason } : {} } : null;
101255
101269
  }
101270
+ async function listTaskRecommendationsImpl(store, options) {
101271
+ const layer = store.asyncLayer;
101272
+ const completeColumns = options?.completeColumns ?? await resolveProjectColumnsForRoles(store, ["complete"]);
101273
+ const rawLimit = options?.limit;
101274
+ const rawOffset = options?.offset;
101275
+ const limit = typeof rawLimit === "number" && Number.isFinite(rawLimit) ? Math.min(200, Math.max(1, Math.trunc(rawLimit))) : 50;
101276
+ const offset = typeof rawOffset === "number" && Number.isFinite(rawOffset) ? Math.max(0, Math.trunc(rawOffset)) : 0;
101277
+ const columns = [...completeColumns];
101278
+ if (columns.length === 0) return { items: [], rowOffset: offset, rowLimit: limit, returnedRowCount: 0, totalRowCount: 0, hasMore: false };
101279
+ const filter = and(
101280
+ taskProjectScope(layer),
101281
+ isNull(project_exports.tasks.deletedAt),
101282
+ inArray(project_exports.tasks.column, columns),
101283
+ isNotNull(project_exports.tasks.recommendations),
101284
+ sql`jsonb_array_length(${project_exports.tasks.recommendations}) > 0`
101285
+ );
101286
+ const [countRows, rows] = await Promise.all([
101287
+ layer.db.select({ count: sql`count(*)` }).from(project_exports.tasks).where(filter),
101288
+ layer.db.select({ id: project_exports.tasks.id, title: project_exports.tasks.title, column: project_exports.tasks.column, updatedAt: project_exports.tasks.updatedAt, recommendations: project_exports.tasks.recommendations }).from(project_exports.tasks).where(filter).orderBy(desc(project_exports.tasks.updatedAt), desc(project_exports.tasks.id)).limit(limit).offset(offset)
101289
+ ]);
101290
+ const items = [];
101291
+ for (const row of rows) {
101292
+ if (!Array.isArray(row.recommendations) || row.recommendations.length === 0) continue;
101293
+ for (const recommendation of row.recommendations) {
101294
+ items.push({ taskId: row.id, taskTitle: row.title ?? void 0, taskColumn: row.column, updatedAt: row.updatedAt, recommendation });
101295
+ }
101296
+ }
101297
+ const totalRowCount = Number(countRows[0]?.count ?? 0);
101298
+ const returnedRowCount = rows.length;
101299
+ return { items, rowOffset: offset, rowLimit: limit, returnedRowCount, totalRowCount, hasMore: offset + returnedRowCount < totalRowCount };
101300
+ }
101256
101301
  var LEGACY_REVIEW_LANES5, LEGACY_ARCHIVE_LANES2;
101257
101302
  var init_reads = __esm({
101258
101303
  "../core/src/task-store/reads.ts"() {
@@ -101275,6 +101320,7 @@ var init_reads = __esm({
101275
101320
  init_retry_summary();
101276
101321
  init_errors3();
101277
101322
  init_project_lane_vocabulary();
101323
+ init_data_layer();
101278
101324
  init_async_persistence();
101279
101325
  init_async_search();
101280
101326
  init_async_archive_db();
@@ -103381,6 +103427,9 @@ var init_store = __esm({
103381
103427
  async clearWorkflowRunStepInstancesAsync(taskId, keepRunId) {
103382
103428
  return clearWorkflowRunStepInstancesAsyncImpl(this, taskId, keepRunId);
103383
103429
  }
103430
+ async listTaskRecommendations(options) {
103431
+ return listTaskRecommendationsImpl(this, options);
103432
+ }
103384
103433
  async listTasksForGithubTrackingReconcile(options) {
103385
103434
  return listTasksForGithubTrackingReconcileImpl(this, options);
103386
103435
  }
@@ -104107,8 +104156,8 @@ var init_store = __esm({
104107
104156
  async replaceActiveTaskWorkflowContinuation(input) {
104108
104157
  return replaceActiveTaskWorkflowContinuationImpl(this, input);
104109
104158
  }
104110
- async seedStrandedPlanReviewContinuation(input) {
104111
- return seedStrandedPlanReviewContinuationImpl(this, input);
104159
+ async seedStrandedPlanReviewContinuation(input, options = {}) {
104160
+ return seedStrandedPlanReviewContinuationImpl(this, input, options);
104112
104161
  }
104113
104162
  async transitionWorkflowWorkItem(id, state, patch = {}, tx) {
104114
104163
  return transitionWorkflowWorkItemImpl(this, id, state, patch, tx);
@@ -133246,6 +133295,77 @@ var init_task_timing = __esm({
133246
133295
  }
133247
133296
  });
133248
133297
 
133298
+ // ../core/src/task-recommendation-notice.ts
133299
+ import { createHash as createHash20 } from "node:crypto";
133300
+ function registerTaskRecommendationNoticeMailbox(store, mailbox) {
133301
+ mailboxes2.set(store, mailbox);
133302
+ return () => {
133303
+ if (mailboxes2.get(store) === mailbox) mailboxes2.delete(store);
133304
+ };
133305
+ }
133306
+ function getTaskRecommendationNoticeMailbox(store) {
133307
+ return mailboxes2.get(store);
133308
+ }
133309
+ function buildTaskRecommendationNoticeContent(task, recommendations) {
133310
+ const title = task.title?.trim();
133311
+ const heading = title ? `## Recommendations from ${task.id} \u2014 ${title}` : `## Recommendations from ${task.id}`;
133312
+ return [
133313
+ heading,
133314
+ "",
133315
+ "These are optional, non-blocking out-of-scope proposals that were **not** executed.",
133316
+ "",
133317
+ ...recommendations.map((recommendation) => `- **${recommendation.title}** \u2014 \`${recommendation.category}\`: ${recommendation.description}`),
133318
+ "",
133319
+ `Open ${task.id}'s **Recommendations** tab to review them. **Create task** files one through normal guarded intake.`
133320
+ ].join("\n");
133321
+ }
133322
+ function buildTaskRecommendationNoticeIdempotencyKey(taskId, recommendations) {
133323
+ const ids = recommendations.map(({ id }) => id).sort().join("\n");
133324
+ const digest = createHash20("sha256").update(ids, "utf8").digest("hex").slice(0, 16);
133325
+ return `task-recommendation-notice:${taskId}:${digest}`;
133326
+ }
133327
+ async function notifyOperatorOfTaskRecommendations(store, task, recommendations, settings) {
133328
+ try {
133329
+ if (settings.recommendationMailboxNoticeEnabled === false || !recommendations?.length) return false;
133330
+ const mailbox = mailboxes2.get(store);
133331
+ if (!mailbox) return false;
133332
+ await mailbox.sendMessageOnce(
133333
+ {
133334
+ fromId: "system",
133335
+ fromType: "system",
133336
+ toId: DASHBOARD_USER_ID,
133337
+ toType: "user",
133338
+ type: "system",
133339
+ content: buildTaskRecommendationNoticeContent(task, recommendations),
133340
+ metadata: {
133341
+ kind: "task-recommendation-notice",
133342
+ taskId: task.id,
133343
+ recommendationCount: recommendations.length,
133344
+ recommendationIds: recommendations.map(({ id }) => id),
133345
+ categories: recommendations.map(({ category }) => category)
133346
+ }
133347
+ },
133348
+ buildTaskRecommendationNoticeIdempotencyKey(task.id, recommendations)
133349
+ );
133350
+ return true;
133351
+ } catch (error2) {
133352
+ noticeLog2.warn(
133353
+ `Operator recommendation notice failed for ${task.id}: ${error2 instanceof Error ? error2.message : String(error2)}`
133354
+ );
133355
+ return false;
133356
+ }
133357
+ }
133358
+ var noticeLog2, mailboxes2;
133359
+ var init_task_recommendation_notice = __esm({
133360
+ "../core/src/task-recommendation-notice.ts"() {
133361
+ "use strict";
133362
+ init_logger2();
133363
+ init_types();
133364
+ noticeLog2 = createLogger2("task-recommendation-notice");
133365
+ mailboxes2 = /* @__PURE__ */ new WeakMap();
133366
+ }
133367
+ });
133368
+
133249
133369
  // ../core/src/session-identity-registry.ts
133250
133370
  import { AsyncLocalStorage } from "node:async_hooks";
133251
133371
  import { resolve as resolve23 } from "node:path";
@@ -134095,6 +134215,8 @@ __export(src_exports, {
134095
134215
  buildTaskDeleteNoticeContent: () => buildTaskDeleteNoticeContent,
134096
134216
  buildTaskDeleteNoticeIdempotencyKey: () => buildTaskDeleteNoticeIdempotencyKey,
134097
134217
  buildTaskLineageTrailer: () => buildTaskLineageTrailer,
134218
+ buildTaskRecommendationNoticeContent: () => buildTaskRecommendationNoticeContent,
134219
+ buildTaskRecommendationNoticeIdempotencyKey: () => buildTaskRecommendationNoticeIdempotencyKey,
134098
134220
  buildToolOutputTruncationMarker: () => buildToolOutputTruncationMarker,
134099
134221
  buildTriageHeartbeatGuidance: () => buildTriageHeartbeatGuidance,
134100
134222
  buildTriageMemoryInstructions: () => buildTriageMemoryInstructions,
@@ -134425,6 +134547,7 @@ __export(src_exports, {
134425
134547
  getTaskMergeBlocker: () => getTaskMergeBlocker,
134426
134548
  getTaskMoveDisposer: () => getTaskMoveDisposer,
134427
134549
  getTaskPriorityRank: () => getTaskPriorityRank,
134550
+ getTaskRecommendationNoticeMailbox: () => getTaskRecommendationNoticeMailbox,
134428
134551
  getTemplatesForRole: () => getTemplatesForRole,
134429
134552
  getTotalAgentActiveMs: () => getTotalAgentActiveMs,
134430
134553
  getTrait: () => getTrait,
@@ -134675,6 +134798,7 @@ __export(src_exports, {
134675
134798
  normalizeWorkflowPromptOverrides: () => normalizeWorkflowPromptOverrides,
134676
134799
  normalizeWorkflowReviewFindings: () => normalizeWorkflowReviewFindings,
134677
134800
  notifyOperatorOfNonOperatorDelete: () => notifyOperatorOfNonOperatorDelete,
134801
+ notifyOperatorOfTaskRecommendations: () => notifyOperatorOfTaskRecommendations,
134678
134802
  overseerAdviceSeverityRank: () => overseerAdviceSeverityRank,
134679
134803
  parseAgentManifest: () => parseAgentManifest,
134680
134804
  parseCompanyArchive: () => parseCompanyArchive,
@@ -134784,6 +134908,7 @@ __export(src_exports, {
134784
134908
  registerStepParser: () => registerStepParser,
134785
134909
  registerTaskDeleteNoticeMailbox: () => registerTaskDeleteNoticeMailbox,
134786
134910
  registerTaskMoveDisposer: () => registerTaskMoveDisposer,
134911
+ registerTaskRecommendationNoticeMailbox: () => registerTaskRecommendationNoticeMailbox,
134787
134912
  registerTraitHookImpl: () => registerTraitHookImpl,
134788
134913
  rekeyFallbackProjectPartition: () => rekeyFallbackProjectPartition,
134789
134914
  releaseEmbeddedRuntimeLease: () => releaseEmbeddedRuntimeLease,
@@ -135411,6 +135536,7 @@ var init_src2 = __esm({
135411
135536
  init_task_document_concurrency();
135412
135537
  init_task_delete_attribution();
135413
135538
  init_task_delete_notice();
135539
+ init_task_recommendation_notice();
135414
135540
  init_session_identity_registry();
135415
135541
  init_task_lifecycle_event_retention();
135416
135542
  init_task_lifecycle_consumer_identity();
@@ -138781,7 +138907,7 @@ ${getErrorStderr(error2) ?? ""}`;
138781
138907
  });
138782
138908
 
138783
138909
  // ../engine/src/worktree/secrets-env-writer.ts
138784
- import { createHash as createHash20 } from "node:crypto";
138910
+ import { createHash as createHash21 } from "node:crypto";
138785
138911
  import { execFile as execFile7 } from "node:child_process";
138786
138912
  import { promises as fs5 } from "node:fs";
138787
138913
  import path2 from "node:path";
@@ -138790,7 +138916,7 @@ function isValidFilename(filename) {
138790
138916
  return !!filename && !filename.includes("/") && !filename.includes("\\") && !filename.includes("..") && !filename.includes("\0") && filename !== FINGERPRINT_FILE;
138791
138917
  }
138792
138918
  function sha2562(content) {
138793
- return createHash20("sha256").update(Buffer.from(content, "utf8")).digest("hex");
138919
+ return createHash21("sha256").update(Buffer.from(content, "utf8")).digest("hex");
138794
138920
  }
138795
138921
  function quote(value) {
138796
138922
  return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t")}"`;
@@ -141453,11 +141579,11 @@ var init_planning_handoff_recovery = __esm({
141453
141579
  // ../engine/src/plan-review-continuation.ts
141454
141580
  async function seedPreReleasePlanReviewContinuation(store, task, ir2, options = {}) {
141455
141581
  const node = resolvePreReleasePlanReviewNode(ir2);
141456
- if (!node || node.column !== task.column) return { seeded: false };
141582
+ if (!node || node.column !== task.column) return { seeded: false, reason: "no-pre-release-plan-review" };
141457
141583
  if (isTaskBlockedOnApproval(task)) return { seeded: false, reason: "awaiting-approval" };
141458
141584
  if (task.paused === true || task.userPaused === true) return { seeded: false, reason: "paused" };
141459
141585
  const items = await store.listWorkflowWorkItemsForTask(task.id);
141460
- const active2 = items.filter((item2) => ACTIVE_WORKFLOW_WORK_ITEM_STATES.includes(item2.state));
141586
+ const active2 = items.filter((item2) => ACTIVE_WORKFLOW_WORK_ITEM_STATES.includes(item2.state) && item2.id !== options.retirePredecessorId);
141461
141587
  if (active2.length > 0) return { seeded: false, reason: "active-continuation" };
141462
141588
  const continuationSequence = items.length;
141463
141589
  const input = {
@@ -141473,6 +141599,9 @@ async function seedPreReleasePlanReviewContinuation(store, task, ir2, options =
141473
141599
  targetColumn: task.column,
141474
141600
  irHash: computeWorkflowIrPin(ir2, node.id).irHash
141475
141601
  };
141602
+ if (options.retirePredecessorId) {
141603
+ return store.seedStrandedPlanReviewContinuation(input, { retirePredecessorId: options.retirePredecessorId });
141604
+ }
141476
141605
  if (options.atomic) return store.seedStrandedPlanReviewContinuation(input);
141477
141606
  const item = await store.replaceActiveTaskWorkflowContinuation(input);
141478
141607
  return { seeded: true, workItemId: item.id };
@@ -141512,7 +141641,7 @@ var init_plan_review_continuation = __esm({
141512
141641
  });
141513
141642
 
141514
141643
  // ../engine/src/execution/hold-release.ts
141515
- import { createHash as createHash21 } from "node:crypto";
141644
+ import { createHash as createHash22 } from "node:crypto";
141516
141645
  import { readFile as readFile33 } from "node:fs/promises";
141517
141646
  function findColumn2(ir2, columnId) {
141518
141647
  if (ir2.version !== "v2") return void 0;
@@ -141550,9 +141679,9 @@ async function checkAndRecordUnplannedExecutionBlock(store, task, ir2) {
141550
141679
  promptContent = "";
141551
141680
  }
141552
141681
  }
141553
- const promptMarker = promptContent.length > 0 ? createHash21("sha256").update(promptContent).digest("hex") : "missing";
141682
+ const promptMarker = promptContent.length > 0 ? createHash22("sha256").update(promptContent).digest("hex") : "missing";
141554
141683
  const dependencies = [...task.dependencies ?? []].sort();
141555
- const episode = createHash21("sha256").update(JSON.stringify({
141684
+ const episode = createHash22("sha256").update(JSON.stringify({
141556
141685
  planReviewNode,
141557
141686
  promptMarker,
141558
141687
  dependencies,
@@ -144593,7 +144722,7 @@ var init_scheduler = __esm({
144593
144722
  });
144594
144723
 
144595
144724
  // ../engine/src/agents/agent-logger.ts
144596
- import { createHash as createHash22 } from "node:crypto";
144725
+ import { createHash as createHash23 } from "node:crypto";
144597
144726
  function truncateToolResultDetail(value) {
144598
144727
  if (value.length <= TOOL_RESULT_DETAIL_LIMIT) return value;
144599
144728
  return `${value.slice(0, TOOL_RESULT_DETAIL_LIMIT)}${TOOL_RESULT_DETAIL_TRUNCATION_NOTICE}`;
@@ -144682,7 +144811,7 @@ function summarizeToolArgs(name2, args) {
144682
144811
  const json2 = JSON.stringify(args);
144683
144812
  if (!json2 || json2 === "{}" || json2 === "[]") return void 0;
144684
144813
  if (json2.length <= STRUCTURED_ARG_SUMMARY_MAX) return json2;
144685
- const hash3 = createHash22("sha256").update(json2).digest("hex").slice(0, STRUCTURED_ARG_HASH_HEX_LEN);
144814
+ const hash3 = createHash23("sha256").update(json2).digest("hex").slice(0, STRUCTURED_ARG_HASH_HEX_LEN);
144686
144815
  const suffix = `\u2026#${hash3}`;
144687
144816
  const keep = Math.max(0, STRUCTURED_ARG_SUMMARY_MAX - suffix.length);
144688
144817
  return `${json2.slice(0, keep)}${suffix}`;
@@ -149126,7 +149255,7 @@ var init_gating_classifications = __esm({
149126
149255
  });
149127
149256
 
149128
149257
  // ../engine/src/agents/agent-action-gate.ts
149129
- import { createHash as createHash23 } from "node:crypto";
149258
+ import { createHash as createHash24 } from "node:crypto";
149130
149259
  function getExemptTools() {
149131
149260
  if (!_exemptTools) {
149132
149261
  _exemptTools = new Set(DEFAULT_EXEMPT_TOOLS);
@@ -149160,7 +149289,7 @@ function evaluateAgentActionGate(params) {
149160
149289
  if (params.toolName === "bash") {
149161
149290
  const command = extractShellCommand(args);
149162
149291
  const git7 = classifyGitCommand(command);
149163
- resourceId = command ? `cmd:${createHash23("sha256").update(command).digest("hex").slice(0, 16)}` : void 0;
149292
+ resourceId = command ? `cmd:${createHash24("sha256").update(command).digest("hex").slice(0, 16)}` : void 0;
149164
149293
  if (git7?.write) {
149165
149294
  category = "git_write";
149166
149295
  operation = git7.operation;
@@ -177682,7 +177811,7 @@ var init_code_node_runner = __esm({
177682
177811
  // ../engine/src/agent-tools.ts
177683
177812
  import { appendFile as appendFile3, mkdir as mkdir28, readFile as readFile38, readdir as readdir11, realpath as realpath2, stat as stat11, writeFile as writeFile26 } from "node:fs/promises";
177684
177813
  import { existsSync as existsSync52 } from "node:fs";
177685
- import { createHash as createHash24, randomUUID as randomUUID46 } from "node:crypto";
177814
+ import { createHash as createHash25, randomUUID as randomUUID46 } from "node:crypto";
177686
177815
  import { tmpdir as tmpdir5 } from "node:os";
177687
177816
  import { extname as extname4, isAbsolute as isAbsolute14, join as join66, relative as relative14, resolve as resolve37, sep as sep10 } from "node:path";
177688
177817
  import { Type as Type2 } from "@earendil-works/pi-ai";
@@ -177726,7 +177855,7 @@ async function readAgentMemoryWorkspaceLongTerm(rootDir, agentId) {
177726
177855
  }
177727
177856
  }
177728
177857
  function qmdAgentMemoryCollectionName2(rootDir, agentId) {
177729
- const hash3 = createHash24("sha1").update(`${rootDir}:${agentId}`).digest("hex").slice(0, 12);
177858
+ const hash3 = createHash25("sha1").update(`${rootDir}:${agentId}`).digest("hex").slice(0, 12);
177730
177859
  return `fusion-agent-memory-${sanitizeAgentMemoryId(agentId).toLowerCase()}-${hash3}`;
177731
177860
  }
177732
177861
  function buildQmdAgentMemoryCollectionAddArgs(rootDir, agentId) {
@@ -187841,7 +187970,7 @@ var init_verification_utils = __esm({
187841
187970
 
187842
187971
  // ../engine/src/merge/merge-dependency-sync.ts
187843
187972
  import { exec as exec25 } from "node:child_process";
187844
- import { createHash as createHash26 } from "node:crypto";
187973
+ import { createHash as createHash27 } from "node:crypto";
187845
187974
  import { existsSync as existsSync54, readFileSync as readFileSync17, writeFileSync as writeFileSync6 } from "node:fs";
187846
187975
  import { join as join68 } from "node:path";
187847
187976
  import { promisify as promisify35 } from "node:util";
@@ -187868,7 +187997,7 @@ function computeLockfileHash(rootDir) {
187868
187997
  const p2 = join68(rootDir, name2);
187869
187998
  if (existsSync54(p2)) {
187870
187999
  try {
187871
- return createHash26("sha256").update(readFileSync17(p2)).digest("hex");
188000
+ return createHash27("sha256").update(readFileSync17(p2)).digest("hex");
187872
188001
  } catch {
187873
188002
  return null;
187874
188003
  }
@@ -190405,7 +190534,7 @@ var init_mesh_lease_manager = __esm({
190405
190534
  });
190406
190535
 
190407
190536
  // ../engine/src/merge/merger-integration-worktree.ts
190408
- import { createHash as createHash27 } from "node:crypto";
190537
+ import { createHash as createHash28 } from "node:crypto";
190409
190538
  import { exec as exec31, execFile as execFile22 } from "node:child_process";
190410
190539
  import { promisify as promisify45 } from "node:util";
190411
190540
  function quoteAutostashArg(value) {
@@ -190528,7 +190657,7 @@ async function gitDirtyFingerprintLocal(rootDir) {
190528
190657
  )
190529
190658
  ]);
190530
190659
  if (!diffOut && !statusOut) return "";
190531
- return createHash27("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
190660
+ return createHash28("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
190532
190661
  } catch {
190533
190662
  return "";
190534
190663
  }
@@ -191514,7 +191643,7 @@ import { execSync as execSync5, exec as exec33 } from "node:child_process";
191514
191643
  import * as childProcess4 from "node:child_process";
191515
191644
  import { promisify as promisify48 } from "node:util";
191516
191645
  import { existsSync as existsSync57, readFileSync as readFileSync20, writeFileSync as writeFileSync8, unlinkSync as unlinkSync3, renameSync as renameSync4 } from "node:fs";
191517
- import { createHash as createHash28 } from "node:crypto";
191646
+ import { createHash as createHash29 } from "node:crypto";
191518
191647
  import { join as join72, resolve as resolve43 } from "node:path";
191519
191648
  import { hostname as hostname2 } from "node:os";
191520
191649
  import { Type as Type4 } from "typebox";
@@ -191762,7 +191891,7 @@ async function gitDirtyFingerprint(rootDir) {
191762
191891
  )
191763
191892
  ]);
191764
191893
  if (!diffOut && !statusOut) return "";
191765
- return createHash28("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
191894
+ return createHash29("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
191766
191895
  } catch {
191767
191896
  return "";
191768
191897
  }
@@ -200649,7 +200778,7 @@ import { promisify as promisify79 } from "node:util";
200649
200778
  // ../engine/src/runtimes/in-process-runtime.ts
200650
200779
  init_src2();
200651
200780
  init_scheduler();
200652
- import { createHash as createHash31 } from "node:crypto";
200781
+ import { createHash as createHash32 } from "node:crypto";
200653
200782
  import { EventEmitter as EventEmitter27 } from "node:events";
200654
200783
 
200655
200784
  // ../engine/src/executor/public-reexports.ts
@@ -203376,7 +203505,7 @@ init_agent_tools();
203376
203505
  init_agent_logger();
203377
203506
  init_agent_usage_telemetry();
203378
203507
  import { Type as Type3 } from "@earendil-works/pi-ai";
203379
- import { createHash as createHash25 } from "node:crypto";
203508
+ import { createHash as createHash26 } from "node:crypto";
203380
203509
 
203381
203510
  // ../engine/src/agents/approval-mail.ts
203382
203511
  init_src2();
@@ -205004,7 +205133,7 @@ function truncatePrompt(text2, maxChars) {
205004
205133
  ... (truncated, ${text2.length} chars)`;
205005
205134
  }
205006
205135
  function shortContentHash(value) {
205007
- return createHash25("sha256").update(value).digest("hex").slice(0, 8);
205136
+ return createHash26("sha256").update(value).digest("hex").slice(0, 8);
205008
205137
  }
205009
205138
  function buildIdentitySnapshot(args) {
205010
205139
  const { agent, resolvedInstructions, workspaceMemory } = args;
@@ -233492,6 +233621,34 @@ init_effective_settings();
233492
233621
  init_run_audit();
233493
233622
  init_logger();
233494
233623
  init_lifecycle_columns();
233624
+
233625
+ // ../engine/src/executor/completion-recommendation-notice.ts
233626
+ init_src2();
233627
+ var pendingNotices = /* @__PURE__ */ new Set();
233628
+ function dispatchAcceptedCompletionRecommendationNotice(args) {
233629
+ if (!args.recommendations?.length) return;
233630
+ const pending = (async () => {
233631
+ const settings = args.settings ?? await args.store.getSettings();
233632
+ const task = await args.store.getTask(args.taskId);
233633
+ if (!task || task.deletedAt) return;
233634
+ await notifyOperatorOfTaskRecommendations(
233635
+ args.store,
233636
+ { id: args.taskId, title: args.taskTitle ?? task.title },
233637
+ args.recommendations,
233638
+ settings
233639
+ );
233640
+ })();
233641
+ pendingNotices.add(pending);
233642
+ void pending.catch((error2) => {
233643
+ args.log?.warn(
233644
+ `Failed to dispatch recommendation mailbox notice for ${args.taskId}: ${error2 instanceof Error ? error2.message : String(error2)}`
233645
+ );
233646
+ }).finally(() => {
233647
+ pendingNotices.delete(pending);
233648
+ });
233649
+ }
233650
+
233651
+ // ../engine/src/executor/create-task-done-tool.ts
233495
233652
  function createTaskDoneTool(deps, taskId, worktreePath, promptContent, codeReviewVerdicts, onDone, audit) {
233496
233653
  const store = deps.store;
233497
233654
  return {
@@ -233826,6 +233983,16 @@ ${rerunSuffix}`
233826
233983
  bulkCompletionRefusalAt: null
233827
233984
  });
233828
233985
  await store.logEntry(taskId, "Task marked done by agent", void 0, deps.getRunContextFor(taskId));
233986
+ if (completionRecommendations !== void 0) {
233987
+ dispatchAcceptedCompletionRecommendationNotice({
233988
+ store,
233989
+ taskId,
233990
+ taskTitle: task.title,
233991
+ recommendations: completionRecommendations,
233992
+ settings,
233993
+ log: executorLog
233994
+ });
233995
+ }
233829
233996
  const latestTask = await store.getTask(taskId);
233830
233997
  let latestColumn = latestTask.column;
233831
233998
  if (latestColumn === await resolveReboundColumnFor(store, taskId)) {
@@ -245658,6 +245825,16 @@ ${rerunSuffix}` });
245658
245825
  if (latestColumn === await resolveWipTargetForTask(deps.store, task.id) && !hardPauseActive && beforeWatchdog && !beforeWatchdog.deletedAt && !(rejectIfPaused && (beforeWatchdog.paused || beforeWatchdog.userPaused))) {
245659
245826
  deps.scheduleCompletedTaskWatchdog(task.id, "fn_task_done");
245660
245827
  }
245828
+ if (recommendations !== void 0) {
245829
+ dispatchAcceptedCompletionRecommendationNotice({
245830
+ store: deps.store,
245831
+ taskId: task.id,
245832
+ taskTitle: task.title,
245833
+ recommendations,
245834
+ settings,
245835
+ log: executorLog
245836
+ });
245837
+ }
245661
245838
  return { completed: true, hardPauseActive };
245662
245839
  } catch (error2) {
245663
245840
  await restoreNoCommitsExpected();
@@ -250871,7 +251048,7 @@ init_notifier();
250871
251048
 
250872
251049
  // ../engine/src/healing/stuck-task-detector.ts
250873
251050
  init_logger();
250874
- import { createHash as createHash29 } from "node:crypto";
251051
+ import { createHash as createHash30 } from "node:crypto";
250875
251052
  var stuckLog = createLogger("stuck-detector");
250876
251053
  var LOOP_ACTIVITY_THRESHOLD = 60;
250877
251054
  var LOOP_IGNORED_STEP_MIN = 10;
@@ -250882,7 +251059,7 @@ var TOOL_DETAIL_HASH_HEX_LEN = 16;
250882
251059
  var NO_PROGRESS_CHURN_THRESHOLD = 25;
250883
251060
  var VERIFICATION_DEADLINE_GRACE_MS = 5e3;
250884
251061
  function hashToolDetail(detail) {
250885
- return createHash29("sha256").update(detail).digest("hex").slice(0, TOOL_DETAIL_HASH_HEX_LEN);
251062
+ return createHash30("sha256").update(detail).digest("hex").slice(0, TOOL_DETAIL_HASH_HEX_LEN);
250886
251063
  }
250887
251064
  function buildToolFingerprint(toolName, toolDetail) {
250888
251065
  const name2 = toolName.trim().toLowerCase() || "tool";
@@ -253632,7 +253809,7 @@ var MissionAutopilot = class {
253632
253809
  // ../engine/src/missions/mission-execution-loop.ts
253633
253810
  init_src2();
253634
253811
  import { EventEmitter as EventEmitter26 } from "node:events";
253635
- import { createHash as createHash30 } from "node:crypto";
253812
+ import { createHash as createHash31 } from "node:crypto";
253636
253813
 
253637
253814
  // ../engine/src/missions/mission-verification.ts
253638
253815
  init_sandbox();
@@ -253704,7 +253881,7 @@ var MAX_VALIDATION_RESPONSE_BYTES = 256 * 1024;
253704
253881
  var MAX_VALIDATION_JSON_CANDIDATES = 8;
253705
253882
  var VALIDATION_FAILURE_BUDGET_PER_FINGERPRINT = 3;
253706
253883
  function fingerprintMissionValidationInput(landedSha, provider, modelId, systemPrompt, userPrompt) {
253707
- return createHash30("sha256").update(Buffer.from(JSON.stringify(["mission-validation-input-v1", landedSha, provider, modelId, systemPrompt, userPrompt]), "utf8")).digest("hex");
253884
+ return createHash31("sha256").update(Buffer.from(JSON.stringify(["mission-validation-input-v1", landedSha, provider, modelId, systemPrompt, userPrompt]), "utf8")).digest("hex");
253708
253885
  }
253709
253886
  var MissionExecutionLoop = class extends EventEmitter26 {
253710
253887
  running = false;
@@ -257420,7 +257597,7 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
257420
257597
  }
257421
257598
  return;
257422
257599
  }
257423
- const duplicateReport = { outcome: "parked" };
257600
+ const duplicateReport = { outcome: "parked", planningWorkItemId };
257424
257601
  if (await this.tryFinalizeExplicitDuplicateMarker(task, written, settings, {
257425
257602
  isReplan,
257426
257603
  feedback
@@ -257439,6 +257616,7 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
257439
257616
  isReplan,
257440
257617
  feedback
257441
257618
  });
257619
+ finalizeReport.planningWorkItemId = planningWorkItemId;
257442
257620
  this.options.onSpecifyComplete?.(task, finalizeReport);
257443
257621
  } finally {
257444
257622
  this.activeSessions.delete(task.id);
@@ -259013,11 +259191,46 @@ async function reactToSpecificationComplete(deps) {
259013
259191
  return;
259014
259192
  }
259015
259193
  deps.log(`Specified ${deps.taskId} \u2192 todo`);
259016
- const live = await deps.getTask(deps.taskId);
259017
- if (!live || live.paused || live.userPaused) return;
259018
- const ir2 = await deps.resolveIr(live.id);
259019
- await deps.seed(live, ir2);
259020
- deps.kick();
259194
+ const sleep7 = deps.sleep ?? ((ms2) => new Promise((resolve47) => setTimeout(resolve47, ms2)));
259195
+ const QUIET_PARK_REASONS = /* @__PURE__ */ new Set([
259196
+ "no-pre-release-plan-review",
259197
+ "awaiting-approval",
259198
+ "paused",
259199
+ "plan-review-passed"
259200
+ ]);
259201
+ const RETRY_DELAYS_MS2 = [1e3, 5e3];
259202
+ for (let attempt = 0; ; attempt++) {
259203
+ let failure = null;
259204
+ try {
259205
+ const live = await deps.getTask(deps.taskId);
259206
+ if (!live || live.paused || live.userPaused) return;
259207
+ if (live.status === "needs-replan") {
259208
+ deps.log(`Plan Review handoff for ${deps.taskId} not armed (needs-replan)`);
259209
+ return;
259210
+ }
259211
+ const ir2 = await deps.resolveIr(live.id);
259212
+ const result = await deps.seed(live, ir2);
259213
+ if (result.seeded) {
259214
+ deps.kick();
259215
+ return;
259216
+ }
259217
+ if (result.reason && QUIET_PARK_REASONS.has(result.reason)) {
259218
+ deps.log(`Plan Review handoff for ${deps.taskId} not armed (${result.reason})`);
259219
+ return;
259220
+ }
259221
+ failure = result.reason ?? "not-seeded";
259222
+ } catch (error2) {
259223
+ failure = error2 instanceof Error ? error2.message : String(error2);
259224
+ }
259225
+ if (attempt >= RETRY_DELAYS_MS2.length) {
259226
+ deps.warn(
259227
+ `Plan Review handoff for ${deps.taskId} failed after ${attempt + 1} attempts (${failure}) \u2014 stranded-continuation self-healing will recover it`
259228
+ );
259229
+ return;
259230
+ }
259231
+ deps.warn(`Plan Review handoff for ${deps.taskId} did not seed (${failure}) \u2014 retrying`);
259232
+ await sleep7(RETRY_DELAYS_MS2[attempt]);
259233
+ }
259021
259234
  }
259022
259235
  async function drainDuePlanningContinuations(deps) {
259023
259236
  const items = await deps.listDue();
@@ -259154,7 +259367,7 @@ function stableNotificationJson(value) {
259154
259367
  return `{${fields.join(",")}}`;
259155
259368
  }
259156
259369
  function buildCliAgentNotificationDedupeKey(input) {
259157
- const notificationFingerprint = createHash31("sha256").update(stableNotificationJson(input.info.notification ?? null)).digest("hex").slice(0, 16);
259370
+ const notificationFingerprint = createHash32("sha256").update(stableNotificationJson(input.info.notification ?? null)).digest("hex").slice(0, 16);
259158
259371
  const waitingEpoch = input.session?.updatedAt ?? "unknown-waiting-epoch";
259159
259372
  return [
259160
259373
  "cli-agent",
@@ -259266,6 +259479,8 @@ var InProcessRuntime = class extends EventEmitter27 {
259266
259479
  messageStore;
259267
259480
  /** FNXC:TaskDeleteNotice 2026-07-26-16:10: identity-guarded teardown for the delete-notice mailbox seam. */
259268
259481
  unregisterTaskDeleteNoticeMailbox;
259482
+ /** FNXC:TaskRecommendations 2026-08-13-03:56: identity-guarded teardown for the store-scoped recommendation notice seam. */
259483
+ unregisterTaskRecommendationNoticeMailbox;
259269
259484
  chatStore;
259270
259485
  detachAgentLinkSync;
259271
259486
  /**
@@ -259324,7 +259539,8 @@ var InProcessRuntime = class extends EventEmitter27 {
259324
259539
  createTaskStoreForBackend: createTaskStoreForBackend2,
259325
259540
  buildConsumerId: buildConsumerId2,
259326
259541
  createProjectScopedPluginMcpProvider: createProjectScopedPluginMcpProvider2,
259327
- registerTaskDeleteNoticeMailbox: registerTaskDeleteNoticeMailbox2
259542
+ registerTaskDeleteNoticeMailbox: registerTaskDeleteNoticeMailbox2,
259543
+ registerTaskRecommendationNoticeMailbox: registerTaskRecommendationNoticeMailbox2
259328
259544
  } = await Promise.resolve().then(() => (init_src2(), src_exports));
259329
259545
  if (this.config.externalTaskStore) {
259330
259546
  this.taskStore = this.config.externalTaskStore;
@@ -259383,6 +259599,10 @@ var InProcessRuntime = class extends EventEmitter27 {
259383
259599
  this.taskStore,
259384
259600
  this.messageStore
259385
259601
  );
259602
+ this.unregisterTaskRecommendationNoticeMailbox = registerTaskRecommendationNoticeMailbox2(
259603
+ this.taskStore,
259604
+ this.messageStore
259605
+ );
259386
259606
  await yieldEventLoop4();
259387
259607
  const pluginLayer = this.taskStore.getAsyncLayer();
259388
259608
  if (!pluginLayer) {
@@ -259874,9 +260094,19 @@ var InProcessRuntime = class extends EventEmitter27 {
259874
260094
  outcome: report2.outcome,
259875
260095
  getTask: (id) => Promise.resolve(this.taskStore.getTask(id)),
259876
260096
  resolveIr: (id) => resolveWorkflowIrForTask(this.taskStore, id),
259877
- seed: (task, ir2) => seedPreReleasePlanReviewContinuation(this.taskStore, task, ir2),
260097
+ /*
260098
+ FNXC:PlanningHandoffAtomicity 2026-08-13-03:49:
260099
+ The report carries the planning session's own durable work item id so the
260100
+ seeder can retire that exact predecessor row atomically with the successor
260101
+ install. Without it the seeder raced triage's finally-block terminal
260102
+ transition and bailed on its own caller (529 stranded cards in 18 days).
260103
+ */
260104
+ seed: (task, ir2) => seedPreReleasePlanReviewContinuation(this.taskStore, task, ir2, {
260105
+ retirePredecessorId: report2.planningWorkItemId
260106
+ }),
259878
260107
  kick: () => this.kickWorkflowContinuationProcessor(),
259879
- log: (message) => runtimeLog.log(message)
260108
+ log: (message) => runtimeLog.log(message),
260109
+ warn: (message) => runtimeLog.warn(message)
259880
260110
  }).catch((error2) => {
259881
260111
  runtimeLog.error(`Failed to start Todo plan review for ${t.id}:`, error2);
259882
260112
  });
@@ -260164,6 +260394,8 @@ var InProcessRuntime = class extends EventEmitter27 {
260164
260394
  this.backendShutdown = void 0;
260165
260395
  this.unregisterTaskDeleteNoticeMailbox?.();
260166
260396
  this.unregisterTaskDeleteNoticeMailbox = void 0;
260397
+ this.unregisterTaskRecommendationNoticeMailbox?.();
260398
+ this.unregisterTaskRecommendationNoticeMailbox = void 0;
260167
260399
  let stopError;
260168
260400
  try {
260169
260401
  if (this.workflowContinuationTimer) {