@runfusion/fusion 0.73.0-beta.3 → 0.73.0-beta.4

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 (279) hide show
  1. package/dist/bin.js +994 -292
  2. package/dist/child-process-worker.js +632 -153
  3. package/dist/client/.vite/manifest.json +256 -256
  4. package/dist/client/assets/{AgentDetailView-lJqxsPOR.js → AgentDetailView-BBOL6AgZ.js} +1 -1
  5. package/dist/client/assets/{AgentPermissionPolicyEditor-De4wK_UT.js → AgentPermissionPolicyEditor-DV7OPUhR.js} +1 -1
  6. package/dist/client/assets/{AgentsView-CwizRdcA.js → AgentsView-B6xoAHyV.js} +3 -3
  7. package/dist/client/assets/{ChatView-BeQXaz7_.js → ChatView-DwVjnxM8.js} +1 -1
  8. package/dist/client/assets/{CommandCenter-BJZCaPLj.js → CommandCenter-DYWaoYFD.js} +1 -1
  9. package/dist/client/assets/{DevServerView-DQgwQXKx.js → DevServerView-BY5up-NA.js} +1 -1
  10. package/dist/client/assets/{DirectoryPicker-B2UVwp6F.js → DirectoryPicker-fM8MJa2r.js} +1 -1
  11. package/dist/client/assets/{DocumentsView-DOeYrhvg.js → DocumentsView-D2KxsPG_.js} +1 -1
  12. package/dist/client/assets/{EvalsView-LwepJ9Ud.js → EvalsView-U8dOvTRM.js} +1 -1
  13. package/dist/client/assets/{ExperimentalAgentOnboardingModal-DVNSzflw.js → ExperimentalAgentOnboardingModal-C27y8Y-1.js} +1 -1
  14. package/dist/client/assets/{GoalsView-__LpXw4S.js → GoalsView-D-2wmy-O.js} +1 -1
  15. package/dist/client/assets/{InsightsView-Dv388uaO.js → InsightsView-zLyuQL_l.js} +1 -1
  16. package/dist/client/assets/{MemoryView-ChCbEfZT.js → MemoryView-D-tSn48u.js} +1 -1
  17. package/dist/client/assets/{PiExtensionsManager-DehAjrqN.js → PiExtensionsManager-DvwmvGEY.js} +1 -1
  18. package/dist/client/assets/PluginManager-BACOwQAN.js +1 -0
  19. package/dist/client/assets/{PullRequestView-BvmajsBq.js → PullRequestView-DULyv21u.js} +1 -1
  20. package/dist/client/assets/{ReportModal-DM9B_h_w.js → ReportModal-CUlKMFWa.js} +1 -1
  21. package/dist/client/assets/{ResearchView-BzXSgUhp.js → ResearchView-DM_O3IFc.js} +1 -1
  22. package/dist/client/assets/{SecretsView-COBqEdJj.js → SecretsView-Bq3u_nAf.js} +1 -1
  23. package/dist/client/assets/{SessionTerminal-BUw_FTlh.js → SessionTerminal-D00ByR6U.js} +1 -1
  24. package/dist/client/assets/SettingsModal-Bb3pIxiX.js +21 -0
  25. package/dist/client/assets/SettingsModal-CMLHZBhX.css +1 -0
  26. package/dist/client/assets/{SettingsModal-CsLaIrqt.js → SettingsModal-QRaBE1ds.js} +1 -1
  27. package/dist/client/assets/{SettingsTextareaRow-Bv3aZ9aU.js → SettingsTextareaRow-CHOJ-qHz.js} +1 -1
  28. package/dist/client/assets/{SetupWizardModal-C-yMHTlW.js → SetupWizardModal-DriQyd81.js} +2 -2
  29. package/dist/client/assets/{SkillsView-kSHgkBar.js → SkillsView-BtDyujiZ.js} +1 -1
  30. package/dist/client/assets/{TodoView-DYoicJNY.js → TodoView-BNHUnz50.js} +1 -1
  31. package/dist/client/assets/{WorkflowNodeEditor-BtWrziOX.css → WorkflowNodeEditor-BNgkFJ_P.css} +1 -1
  32. package/dist/client/assets/WorkflowNodeEditor-BXikFpra.js +8 -0
  33. package/dist/client/assets/{agent-import-generation-D6P8OUuV.js → agent-import-generation-B2kYEm1O.js} +1 -1
  34. package/dist/client/assets/{app-DegRNirF.js → app-B_HrdDXZ.js} +1 -1
  35. package/dist/client/assets/{app-BsIXfnu-.js → app-C6yo-M_n.js} +1 -1
  36. package/dist/client/assets/{app-B-IdUeIu.js → app-CH8ZgPm4.js} +1 -1
  37. package/dist/client/assets/{app-D9ktpVhR.js → app-D4DpgDss.js} +1 -1
  38. package/dist/client/assets/{app-nBTNvNKK.js → app-Qv0blCyY.js} +1 -1
  39. package/dist/client/assets/{app-C8muVNUU.js → app-kFdtajPy.js} +1 -1
  40. package/dist/client/assets/{architectureDiagram-3BPJPVTR-DLqKCckA.js → architectureDiagram-3BPJPVTR-B-Efjj4Z.js} +1 -1
  41. package/dist/client/assets/{blockDiagram-GPEHLZMM-DRz6F8wZ.js → blockDiagram-GPEHLZMM-CaOVxrlM.js} +1 -1
  42. package/dist/client/assets/{c4Diagram-AAUBKEIU-CJ9glQAp.js → c4Diagram-AAUBKEIU-D8aYt5F1.js} +1 -1
  43. package/dist/client/assets/channel-5bPK6pTS.js +1 -0
  44. package/dist/client/assets/{chunk-2J33WTMH-CjIBb3ln.js → chunk-2J33WTMH-VSDT0J0r.js} +1 -1
  45. package/dist/client/assets/{chunk-4BX2VUAB-DrcoEr1E.js → chunk-4BX2VUAB-Chx1wQgD.js} +1 -1
  46. package/dist/client/assets/{chunk-55IACEB6-DU-WcuA9.js → chunk-55IACEB6-MlqjhIJg.js} +1 -1
  47. package/dist/client/assets/{chunk-727SXJPM-C5Lklr3l.js → chunk-727SXJPM-BheQNUi8.js} +1 -1
  48. package/dist/client/assets/{chunk-AQP2D5EJ-DlyTTghY.js → chunk-AQP2D5EJ-C5EoJhfJ.js} +1 -1
  49. package/dist/client/assets/{chunk-FMBD7UC4-D3C65m5Y.js → chunk-FMBD7UC4-B8_8qP3j.js} +1 -1
  50. package/dist/client/assets/{chunk-ND2GUHAM-BZY-4WZF.js → chunk-ND2GUHAM-BuglCGRx.js} +1 -1
  51. package/dist/client/assets/{chunk-QZHKN3VN-CKUtTYy5.js → chunk-QZHKN3VN-B7_06dxp.js} +1 -1
  52. package/dist/client/assets/classDiagram-4FO5ZUOK-Dv9RQDqG.js +1 -0
  53. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Dv9RQDqG.js +1 -0
  54. package/dist/client/assets/{cose-bilkent-S5V4N54A-DbpsF4N5.js → cose-bilkent-S5V4N54A-Cm-ZOycx.js} +1 -1
  55. package/dist/client/assets/{dagre-BM42HDAG-BwIo4me1.js → dagre-BM42HDAG-Dj_Gwjpv.js} +1 -1
  56. package/dist/client/assets/{dashboard-view-C94UCci5.js → dashboard-view-B4CRL5Fy.js} +1 -1
  57. package/dist/client/assets/{dashboard-view-BvAsW6UZ.js → dashboard-view-noD9p0Zs.js} +1 -1
  58. package/dist/client/assets/{dashboard-view-BMQm7Lt_.js → dashboard-view-pXXSUxG9.js} +1 -1
  59. package/dist/client/assets/{diagram-2AECGRRQ-C-d8XY1c.js → diagram-2AECGRRQ-C_9BfShy.js} +1 -1
  60. package/dist/client/assets/{diagram-5GNKFQAL-ByRrlL9w.js → diagram-5GNKFQAL-Cmg2qpCj.js} +1 -1
  61. package/dist/client/assets/{diagram-KO2AKTUF-BK7jb_J-.js → diagram-KO2AKTUF-2FNo2HXb.js} +1 -1
  62. package/dist/client/assets/{diagram-LMA3HP47-u2o4bYk6.js → diagram-LMA3HP47-DgnVeCp-.js} +1 -1
  63. package/dist/client/assets/{diagram-OG6HWLK6-Dakk1V3D.js → diagram-OG6HWLK6-iAIR50HH.js} +1 -1
  64. package/dist/client/assets/{erDiagram-TEJ5UH35-Yp33v-Cf.js → erDiagram-TEJ5UH35-Da4I04eN.js} +1 -1
  65. package/dist/client/assets/{flowDiagram-I6XJVG4X-C4-dR2TA.js → flowDiagram-I6XJVG4X-Bv9r2T0m.js} +1 -1
  66. package/dist/client/assets/{folder-open-C9qq38qV.js → folder-open-CwWtrDh6.js} +1 -1
  67. package/dist/client/assets/{ganttDiagram-6RSMTGT7-C0CTCkFN.js → ganttDiagram-6RSMTGT7-BMeO84U_.js} +1 -1
  68. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-DK0Dd5mm.js → gitGraphDiagram-PVQCEYII-CWDh_RIb.js} +1 -1
  69. package/dist/client/assets/index-CB3mYxAB.css +1 -0
  70. package/dist/client/assets/{index-BqLVwYoA.js → index-CE7C_XsS.js} +294 -294
  71. package/dist/client/assets/{infoDiagram-5YYISTIA-u5MwFb7j.js → infoDiagram-5YYISTIA-BAE4KtCL.js} +1 -1
  72. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-C7R6TziI.js → ishikawaDiagram-YF4QCWOH-C_iXAuOy.js} +1 -1
  73. package/dist/client/assets/{journeyDiagram-JHISSGLW-Dom8W5Ab.js → journeyDiagram-JHISSGLW-BnxSHwDo.js} +1 -1
  74. package/dist/client/assets/{kanban-definition-UN3LZRKU-_47C02t3.js → kanban-definition-UN3LZRKU-DYNRm3Nu.js} +1 -1
  75. package/dist/client/assets/{mermaid.core-Cd3LKosp.js → mermaid.core-B3hvDDep.js} +4 -4
  76. package/dist/client/assets/{mindmap-definition-RKZ34NQL-D-qVOEin.js → mindmap-definition-RKZ34NQL-s7KBEuPD.js} +1 -1
  77. package/dist/client/assets/{pieDiagram-4H26LBE5-B0TXvVbM.js → pieDiagram-4H26LBE5-Cy1_IPUD.js} +1 -1
  78. package/dist/client/assets/{puzzle-BBYTjDQK.js → puzzle-DWc6gFQ7.js} +1 -1
  79. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-KcFvcZPm.js → quadrantDiagram-W4KKPZXB-DqgVGp41.js} +1 -1
  80. package/dist/client/assets/{requirementDiagram-4Y6WPE33-DEObokVc.js → requirementDiagram-4Y6WPE33-CA5-TDeF.js} +1 -1
  81. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-BGYnEp22.js → sankeyDiagram-5OEKKPKP-Cuvi3RgE.js} +1 -1
  82. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BcYgyJRU.js → sequenceDiagram-3UESZ5HK-Da3GfmGP.js} +1 -1
  83. package/dist/client/assets/{shield-alert-CZg8yaFA.js → shield-alert-_iY63ED4.js} +1 -1
  84. package/dist/client/assets/{standing-instructions-template-BsTtbEqp.js → standing-instructions-template-CCd2YY9c.js} +1 -1
  85. package/dist/client/assets/{stateDiagram-AJRCARHV-CfZQIqmX.js → stateDiagram-AJRCARHV-CZ_I9ENR.js} +1 -1
  86. package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-nBEPRSH7.js → stateDiagram-v2-BHNVJYJU-BomoRVhY.js} +1 -1
  87. package/dist/client/assets/{timeline-definition-PNZ67QCA-DySyabS_.js → timeline-definition-PNZ67QCA-C3CYvIuR.js} +1 -1
  88. package/dist/client/assets/{upload-DMd3zMLx.js → upload-D0RrO65v.js} +1 -1
  89. package/dist/client/assets/{users-9Pyvnn8K.js → users-CGszBY2v.js} +1 -1
  90. package/dist/client/assets/{vennDiagram-CIIHVFJN-DPomfxp_.js → vennDiagram-CIIHVFJN-By9fi8NW.js} +1 -1
  91. package/dist/client/assets/{wardley-L42UT6IY-BPfzngS8.js → wardley-L42UT6IY-DErnXPkI.js} +1 -1
  92. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-Yp4nidAP.js → wardleyDiagram-YWT4CUSO-DFfXZVPk.js} +1 -1
  93. package/dist/client/assets/{xychartDiagram-2RQKCTM6-REgng603.js → xychartDiagram-2RQKCTM6-9Y5oZ5mi.js} +1 -1
  94. package/dist/client/index.html +4 -2
  95. package/dist/client/version.json +1 -1
  96. package/dist/extension.js +752 -207
  97. package/dist/migrations/0000_initial.sql +2 -0
  98. package/dist/migrations/0033_fn-8505_wedge_notification.sql +5 -0
  99. package/dist/plugin-sdk/index.js +1 -0
  100. package/dist/plugins/.fusion-ce-agents/.fusion-ce-upstream-provenance.json +7 -0
  101. package/dist/plugins/.fusion-ce-agents/ce-adversarial-document-reviewer.md +115 -0
  102. package/dist/plugins/.fusion-ce-agents/ce-adversarial-reviewer.md +111 -0
  103. package/dist/plugins/.fusion-ce-agents/ce-agent-native-planning-strategist.md +71 -0
  104. package/dist/plugins/.fusion-ce-agents/ce-agent-native-reviewer.md +181 -0
  105. package/dist/plugins/.fusion-ce-agents/ce-ankane-readme-writer.md +50 -0
  106. package/dist/plugins/.fusion-ce-agents/ce-api-contract-reviewer.md +52 -0
  107. package/dist/plugins/.fusion-ce-agents/ce-architecture-strategist.md +53 -0
  108. package/dist/plugins/.fusion-ce-agents/ce-best-practices-researcher.md +122 -0
  109. package/dist/plugins/.fusion-ce-agents/ce-code-simplicity-reviewer.md +87 -0
  110. package/dist/plugins/.fusion-ce-agents/ce-coherence-reviewer.md +73 -0
  111. package/dist/plugins/.fusion-ce-agents/ce-correctness-reviewer.md +52 -0
  112. package/dist/plugins/.fusion-ce-agents/ce-data-integrity-guardian.md +75 -0
  113. package/dist/plugins/.fusion-ce-agents/ce-data-migration-reviewer.md +119 -0
  114. package/dist/plugins/.fusion-ce-agents/ce-deployment-verification-agent.md +164 -0
  115. package/dist/plugins/.fusion-ce-agents/ce-design-implementation-reviewer.md +94 -0
  116. package/dist/plugins/.fusion-ce-agents/ce-design-iterator.md +197 -0
  117. package/dist/plugins/.fusion-ce-agents/ce-design-lens-reviewer.md +56 -0
  118. package/dist/plugins/.fusion-ce-agents/ce-feasibility-reviewer.md +65 -0
  119. package/dist/plugins/.fusion-ce-agents/ce-figma-design-sync.md +172 -0
  120. package/dist/plugins/.fusion-ce-agents/ce-framework-docs-researcher.md +100 -0
  121. package/dist/plugins/.fusion-ce-agents/ce-git-history-analyzer.md +47 -0
  122. package/dist/plugins/.fusion-ce-agents/ce-issue-intelligence-analyst.md +207 -0
  123. package/dist/plugins/.fusion-ce-agents/ce-julik-frontend-races-reviewer.md +52 -0
  124. package/dist/plugins/.fusion-ce-agents/ce-learnings-researcher.md +254 -0
  125. package/dist/plugins/.fusion-ce-agents/ce-maintainability-reviewer.md +77 -0
  126. package/dist/plugins/.fusion-ce-agents/ce-pattern-recognition-specialist.md +62 -0
  127. package/dist/plugins/.fusion-ce-agents/ce-performance-oracle.md +115 -0
  128. package/dist/plugins/.fusion-ce-agents/ce-performance-reviewer.md +54 -0
  129. package/dist/plugins/.fusion-ce-agents/ce-pr-comment-resolver.md +63 -0
  130. package/dist/plugins/.fusion-ce-agents/ce-previous-comments-reviewer.md +68 -0
  131. package/dist/plugins/.fusion-ce-agents/ce-product-lens-reviewer.md +92 -0
  132. package/dist/plugins/.fusion-ce-agents/ce-project-standards-reviewer.md +84 -0
  133. package/dist/plugins/.fusion-ce-agents/ce-reliability-reviewer.md +52 -0
  134. package/dist/plugins/.fusion-ce-agents/ce-repo-research-analyst.md +263 -0
  135. package/dist/plugins/.fusion-ce-agents/ce-scope-guardian-reviewer.md +79 -0
  136. package/dist/plugins/.fusion-ce-agents/ce-security-lens-reviewer.md +48 -0
  137. package/dist/plugins/.fusion-ce-agents/ce-security-reviewer.md +54 -0
  138. package/dist/plugins/.fusion-ce-agents/ce-security-sentinel.md +98 -0
  139. package/dist/plugins/.fusion-ce-agents/ce-session-historian.md +89 -0
  140. package/dist/plugins/.fusion-ce-agents/ce-slack-researcher.md +133 -0
  141. package/dist/plugins/.fusion-ce-agents/ce-spec-flow-analyzer.md +87 -0
  142. package/dist/plugins/.fusion-ce-agents/ce-swift-ios-reviewer.md +107 -0
  143. package/dist/plugins/.fusion-ce-agents/ce-testing-reviewer.md +52 -0
  144. package/dist/plugins/.fusion-ce-agents/ce-web-researcher.md +127 -0
  145. package/dist/plugins/.fusion-ce-skills/.fusion-ce-upstream-provenance.json +7 -0
  146. package/dist/plugins/.fusion-ce-skills/ce-brainstorm/SKILL.md +318 -0
  147. package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/agents/slack-researcher.md +127 -0
  148. package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/brainstorm-sections.md +354 -0
  149. package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/handoff.md +172 -0
  150. package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/html-rendering.md +662 -0
  151. package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/markdown-rendering.md +236 -0
  152. package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/synthesis-summary.md +271 -0
  153. package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/universal-brainstorming.md +71 -0
  154. package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/visual-probes.md +128 -0
  155. package/dist/plugins/.fusion-ce-skills/ce-brainstorm/scripts/visual-probe-server.js +419 -0
  156. package/dist/plugins/.fusion-ce-skills/ce-code-review/SKILL.md +821 -0
  157. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/action-class-rubric.md +26 -0
  158. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/bulk-preview.md +112 -0
  159. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/cross-model-review.md +63 -0
  160. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/diff-scope.md +41 -0
  161. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/findings-schema.json +137 -0
  162. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/persona-catalog.md +63 -0
  163. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/adversarial-reviewer.md +102 -0
  164. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/agent-native-reviewer.md +173 -0
  165. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/api-contract-reviewer.md +43 -0
  166. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/correctness-reviewer.md +43 -0
  167. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/data-migration-reviewer.md +111 -0
  168. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/deployment-verification-agent.md +157 -0
  169. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/julik-frontend-races-reviewer.md +44 -0
  170. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/learnings-researcher.md +247 -0
  171. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/maintainability-reviewer.md +68 -0
  172. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/performance-reviewer.md +45 -0
  173. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/previous-comments-reviewer.md +59 -0
  174. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/project-standards-reviewer.md +75 -0
  175. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/reliability-reviewer.md +43 -0
  176. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/security-reviewer.md +45 -0
  177. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/swift-ios-reviewer.md +99 -0
  178. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/testing-reviewer.md +43 -0
  179. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/review-output-template.md +170 -0
  180. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/subagent-template.md +199 -0
  181. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/tracker-defer.md +149 -0
  182. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/validator-template.md +89 -0
  183. package/dist/plugins/.fusion-ce-skills/ce-code-review/references/walkthrough.md +249 -0
  184. package/dist/plugins/.fusion-ce-skills/ce-code-review/scripts/cross-model-adversarial-review.sh +218 -0
  185. package/dist/plugins/.fusion-ce-skills/ce-commit/SKILL.md +105 -0
  186. package/dist/plugins/.fusion-ce-skills/ce-commit-push-pr/SKILL.md +134 -0
  187. package/dist/plugins/.fusion-ce-skills/ce-commit-push-pr/references/branch-creation.md +55 -0
  188. package/dist/plugins/.fusion-ce-skills/ce-commit-push-pr/references/pr-description-writing.md +115 -0
  189. package/dist/plugins/.fusion-ce-skills/ce-compound/SKILL.md +712 -0
  190. package/dist/plugins/.fusion-ce-skills/ce-compound/assets/resolution-template.md +94 -0
  191. package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/best-practices-researcher.md +115 -0
  192. package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/data-integrity-guardian.md +68 -0
  193. package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/framework-docs-researcher.md +93 -0
  194. package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/pattern-recognition-specialist.md +55 -0
  195. package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/performance-oracle.md +108 -0
  196. package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/security-sentinel.md +91 -0
  197. package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/session-historian.md +83 -0
  198. package/dist/plugins/.fusion-ce-skills/ce-compound/references/concepts-vocabulary.md +78 -0
  199. package/dist/plugins/.fusion-ce-skills/ce-compound/references/schema.yaml +231 -0
  200. package/dist/plugins/.fusion-ce-skills/ce-compound/references/yaml-schema.md +118 -0
  201. package/dist/plugins/.fusion-ce-skills/ce-compound/scripts/session-history/discover-sessions.sh +130 -0
  202. package/dist/plugins/.fusion-ce-skills/ce-compound/scripts/session-history/extract-errors.py +254 -0
  203. package/dist/plugins/.fusion-ce-skills/ce-compound/scripts/session-history/extract-metadata.py +456 -0
  204. package/dist/plugins/.fusion-ce-skills/ce-compound/scripts/session-history/extract-skeleton.py +570 -0
  205. package/dist/plugins/.fusion-ce-skills/ce-compound/scripts/validate-frontmatter.py +137 -0
  206. package/dist/plugins/.fusion-ce-skills/ce-debug/SKILL.md +257 -0
  207. package/dist/plugins/.fusion-ce-skills/ce-debug/references/anti-patterns.md +91 -0
  208. package/dist/plugins/.fusion-ce-skills/ce-debug/references/defense-in-depth.md +35 -0
  209. package/dist/plugins/.fusion-ce-skills/ce-debug/references/investigation-techniques.md +374 -0
  210. package/dist/plugins/.fusion-ce-skills/ce-doc-review/SKILL.md +70 -0
  211. package/dist/plugins/.fusion-ce-skills/ce-ideate/SKILL.md +401 -0
  212. package/dist/plugins/.fusion-ce-skills/ce-ideate/references/agents/issue-intelligence-analyst.md +200 -0
  213. package/dist/plugins/.fusion-ce-skills/ce-ideate/references/agents/learnings-researcher.md +247 -0
  214. package/dist/plugins/.fusion-ce-skills/ce-ideate/references/agents/slack-researcher.md +127 -0
  215. package/dist/plugins/.fusion-ce-skills/ce-ideate/references/agents/web-researcher.md +121 -0
  216. package/dist/plugins/.fusion-ce-skills/ce-ideate/references/divergent-ideation.md +89 -0
  217. package/dist/plugins/.fusion-ce-skills/ce-ideate/references/html-rendering.md +662 -0
  218. package/dist/plugins/.fusion-ce-skills/ce-ideate/references/ideation-sections.md +191 -0
  219. package/dist/plugins/.fusion-ce-skills/ce-ideate/references/markdown-rendering.md +236 -0
  220. package/dist/plugins/.fusion-ce-skills/ce-ideate/references/post-ideation-workflow.md +166 -0
  221. package/dist/plugins/.fusion-ce-skills/ce-ideate/references/universal-ideation.md +107 -0
  222. package/dist/plugins/.fusion-ce-skills/ce-ideate/references/web-research-cache.md +55 -0
  223. package/dist/plugins/.fusion-ce-skills/ce-plan/SKILL.md +858 -0
  224. package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/agent-native-planning-strategist.md +62 -0
  225. package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/architecture-strategist.md +46 -0
  226. package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/best-practices-researcher.md +114 -0
  227. package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/data-integrity-guardian.md +68 -0
  228. package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/data-migration-reviewer.md +103 -0
  229. package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/deployment-verification-agent.md +157 -0
  230. package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/framework-docs-researcher.md +93 -0
  231. package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/git-history-analyzer.md +40 -0
  232. package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/learnings-researcher.md +247 -0
  233. package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/pattern-recognition-specialist.md +55 -0
  234. package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/performance-oracle.md +108 -0
  235. package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/repo-research-analyst.md +256 -0
  236. package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/security-sentinel.md +91 -0
  237. package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/slack-researcher.md +127 -0
  238. package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/spec-flow-analyzer.md +80 -0
  239. package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/web-researcher.md +121 -0
  240. package/dist/plugins/.fusion-ce-skills/ce-plan/references/approach-altitude.md +55 -0
  241. package/dist/plugins/.fusion-ce-skills/ce-plan/references/deepening-workflow.md +259 -0
  242. package/dist/plugins/.fusion-ce-skills/ce-plan/references/html-rendering.md +668 -0
  243. package/dist/plugins/.fusion-ce-skills/ce-plan/references/markdown-rendering.md +236 -0
  244. package/dist/plugins/.fusion-ce-skills/ce-plan/references/plan-handoff.md +126 -0
  245. package/dist/plugins/.fusion-ce-skills/ce-plan/references/plan-sections.md +405 -0
  246. package/dist/plugins/.fusion-ce-skills/ce-plan/references/synthesis-summary.md +396 -0
  247. package/dist/plugins/.fusion-ce-skills/ce-plan/references/universal-planning.md +168 -0
  248. package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/SKILL.md +53 -0
  249. package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/references/agents/pr-comment-resolver.md +56 -0
  250. package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/references/evaluation-rubric.md +106 -0
  251. package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/references/full-mode.md +283 -0
  252. package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/references/targeted-mode.md +45 -0
  253. package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/scripts/get-pr-comments +159 -0
  254. package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/scripts/get-thread-for-comment +76 -0
  255. package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/scripts/reply-to-pr-thread +33 -0
  256. package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/scripts/resolve-pr-thread +23 -0
  257. package/dist/plugins/.fusion-ce-skills/ce-strategy/SKILL.md +97 -0
  258. package/dist/plugins/.fusion-ce-skills/ce-strategy/references/interview.md +143 -0
  259. package/dist/plugins/.fusion-ce-skills/ce-strategy/references/strategy-template.md +89 -0
  260. package/dist/plugins/.fusion-ce-skills/ce-work/SKILL.md +429 -0
  261. package/dist/plugins/.fusion-ce-skills/ce-work/references/agents/figma-design-sync.md +165 -0
  262. package/dist/plugins/.fusion-ce-skills/ce-work/references/execution-engines.md +85 -0
  263. package/dist/plugins/.fusion-ce-skills/ce-work/references/non-code-execution.md +23 -0
  264. package/dist/plugins/.fusion-ce-skills/ce-work/references/review-findings-followup.md +104 -0
  265. package/dist/plugins/.fusion-ce-skills/ce-work/references/shipping-workflow.md +133 -0
  266. package/dist/plugins/.fusion-ce-skills/ce-work/references/tracker-defer.md +149 -0
  267. package/dist/plugins/fusion-plugin-compound-engineering/.bundled.reload-3.js +11266 -0
  268. package/dist/plugins/fusion-plugin-compound-engineering/.bundled.reload-4.js +11266 -0
  269. package/dist/plugins/fusion-plugin-dependency-graph/.bundled.reload-1.js +8206 -0
  270. package/dist/plugins/fusion-plugin-grok-runtime/.bundled.reload-2.js +26623 -0
  271. package/package.json +1 -1
  272. package/dist/client/assets/PluginManager-DBhJ1i27.js +0 -1
  273. package/dist/client/assets/SettingsModal-BDgH9XID.js +0 -21
  274. package/dist/client/assets/SettingsModal-BXDnzDkJ.css +0 -1
  275. package/dist/client/assets/WorkflowNodeEditor-DLno6bSJ.js +0 -8
  276. package/dist/client/assets/channel-Dj4nOgJO.js +0 -1
  277. package/dist/client/assets/classDiagram-4FO5ZUOK-DnkFo5Qy.js +0 -1
  278. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DnkFo5Qy.js +0 -1
  279. package/dist/client/assets/index-6FBJPKkr.css +0 -1
package/dist/extension.js CHANGED
@@ -906,6 +906,7 @@ var init_workflow_steps = __esm({
906
906
  "in-review",
907
907
  "merged",
908
908
  "failed",
909
+ "task-wedged",
909
910
  "awaiting-approval",
910
911
  "awaiting-user-review",
911
912
  "planning-awaiting-input",
@@ -979,6 +980,50 @@ var init_documents_artifacts = __esm({
979
980
  }
980
981
  });
981
982
 
983
+ // ../core/src/types/task-core.ts
984
+ function assertNotWorkspaceTaskMerge(task) {
985
+ if (isWorkspaceTask(task)) {
986
+ throw new WorkspaceTaskMergeError(
987
+ `Workspace task ${task.id} cannot merge until per-repo merge support (master-plan U6) lands`
988
+ );
989
+ }
990
+ }
991
+ function isWorkspaceTask(task) {
992
+ const worktrees = task.workspaceWorktrees;
993
+ return !!worktrees && Object.keys(worktrees).length > 0;
994
+ }
995
+ var CheckoutConflictError, DUPLICATE_OF_METADATA_KEY, WorkspaceTaskMergeError;
996
+ var init_task_core = __esm({
997
+ "../core/src/types/task-core.ts"() {
998
+ "use strict";
999
+ CheckoutConflictError = class extends Error {
1000
+ constructor(taskId, currentHolderId, requestedById) {
1001
+ super(`Task ${taskId} is already checked out by agent ${currentHolderId}`);
1002
+ this.taskId = taskId;
1003
+ this.currentHolderId = currentHolderId;
1004
+ this.requestedById = requestedById;
1005
+ this.name = "CheckoutConflictError";
1006
+ }
1007
+ };
1008
+ DUPLICATE_OF_METADATA_KEY = "duplicateOfTaskIds";
1009
+ WorkspaceTaskMergeError = class extends Error {
1010
+ constructor(message) {
1011
+ super(message);
1012
+ this.name = "WorkspaceTaskMergeError";
1013
+ }
1014
+ };
1015
+ }
1016
+ });
1017
+
1018
+ // ../core/src/types/todo-list.ts
1019
+ var SHARED_STATE_SNAPSHOT_VERSION;
1020
+ var init_todo_list = __esm({
1021
+ "../core/src/types/todo-list.ts"() {
1022
+ "use strict";
1023
+ SHARED_STATE_SNAPSHOT_VERSION = 1;
1024
+ }
1025
+ });
1026
+
982
1027
  // ../core/src/task-helpers.ts
983
1028
  function getPrimaryPrInfo(task) {
984
1029
  return task.prInfos?.[0] ?? task.prInfo;
@@ -1468,10 +1513,18 @@ var init_settings_schema = __esm({
1468
1513
  "use strict";
1469
1514
  init_in_review_stall();
1470
1515
  DEFAULT_GLOBAL_SETTINGS = {
1471
- // Embedded PostgreSQL is shared by all local Fusion projects and processes.
1472
- // Keep this well above the conservative external-pool budget while still
1473
- // bounded for a local machine.
1474
- embeddedPostgresMaxConnections: 500,
1516
+ /*
1517
+ FNXC:PostgresEmbedded 2026-07-22-23:55:
1518
+ Embedded PostgreSQL is shared by all local Fusion projects and processes.
1519
+ Deliberately undefined (not 500): getSettings() merges these defaults, so a
1520
+ concrete value here would be indistinguishable from an operator choice and
1521
+ would mask the platform-aware server-side default in
1522
+ resolveEmbeddedMaxConnections (win32 150, else 500). Issue #2411: Windows
1523
+ backends are separate processes; a 500-connection cap exhausts the desktop
1524
+ heap under load and forked backends die with 0xC0000142, taking the embedded
1525
+ cluster and dashboard down.
1526
+ */
1527
+ embeddedPostgresMaxConnections: void 0,
1475
1528
  /*
1476
1529
  FNXC:DashboardTheming 2026-07-03-00:00:
1477
1530
  Fresh installs must follow the operating system theme until the user explicitly chooses Light, Dark, or System. Keep this global default aligned with dashboard and desktop pre-hydration fallbacks.
@@ -2234,6 +2287,53 @@ var init_settings_schema = __esm({
2234
2287
  }
2235
2288
  });
2236
2289
 
2290
+ // ../core/src/types/settings-scope.ts
2291
+ function isMcpSecretRef(value) {
2292
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
2293
+ const candidate = value;
2294
+ return typeof candidate.secretRef === "string" && candidate.secretRef.trim().length > 0 && (candidate.scope === "project" || candidate.scope === "global");
2295
+ }
2296
+ var init_settings_scope = __esm({
2297
+ "../core/src/types/settings-scope.ts"() {
2298
+ "use strict";
2299
+ init_settings_schema();
2300
+ }
2301
+ });
2302
+
2303
+ // ../core/src/types/board-config.ts
2304
+ var COLUMN_LABELS, COLUMN_DESCRIPTIONS, VALID_TRANSITIONS;
2305
+ var init_board_config = __esm({
2306
+ "../core/src/types/board-config.ts"() {
2307
+ "use strict";
2308
+ COLUMN_LABELS = {
2309
+ triage: "Planning",
2310
+ todo: "Todo",
2311
+ "in-progress": "In Progress",
2312
+ "in-review": "In Review",
2313
+ done: "Done",
2314
+ archived: "Archived"
2315
+ };
2316
+ COLUMN_DESCRIPTIONS = {
2317
+ triage: "Raw ideas \u2014 AI will plan these",
2318
+ "in-progress": "AI is working on this in a worktree",
2319
+ done: "Merged and closed",
2320
+ archived: "Completed and archived"
2321
+ };
2322
+ VALID_TRANSITIONS = {
2323
+ // FN-4892: intake-side heuristics may cold-archive tasks before execution starts.
2324
+ triage: ["todo", "archived"],
2325
+ // FN-4892: allow direct archival for newly specified intake tasks.
2326
+ todo: ["in-progress", "triage", "archived"],
2327
+ // NOTE: "in-progress" → "done" is enabled for mission validation tasks that complete directly.
2328
+ // Regular implementation tasks should move through "in-review" before "done".
2329
+ "in-progress": ["in-review", "todo", "triage", "done"],
2330
+ "in-review": ["done", "in-progress", "todo", "triage"],
2331
+ done: ["todo", "triage", "archived"],
2332
+ archived: ["done"]
2333
+ };
2334
+ }
2335
+ });
2336
+
2237
2337
  // ../core/src/types/docker-nodes.ts
2238
2338
  function validateDockerNodeConfig(config3) {
2239
2339
  const errors = [];
@@ -2344,6 +2444,41 @@ var init_docker_nodes = __esm({
2344
2444
  }
2345
2445
  });
2346
2446
 
2447
+ // ../core/src/types/archive-planning.ts
2448
+ function formatPlanningPlanMd(summary) {
2449
+ const normalizeListItem = (item) => item.replace(/\s+/g, " ").trim();
2450
+ const list = (items) => items && items.length > 0 ? items.map((item) => `- ${normalizeListItem(item)}`).join("\n") : "_None_";
2451
+ const proposedChanges = list(summary.proposedChanges);
2452
+ const acceptanceCriteria = list(summary.acceptanceCriteria);
2453
+ const dependencies = list(summary.suggestedDependencies);
2454
+ const deliverables = list(summary.keyDeliverables);
2455
+ return `# ${summary.title}
2456
+
2457
+ ${summary.description}
2458
+
2459
+ ## What to change
2460
+ ${proposedChanges}
2461
+
2462
+ ## Acceptance criteria
2463
+ ${acceptanceCriteria}
2464
+
2465
+ ## Size
2466
+ ${summary.suggestedSize}
2467
+
2468
+ ## Suggested dependencies
2469
+ ${dependencies}
2470
+
2471
+ ## Key deliverables
2472
+ ${deliverables}
2473
+ `;
2474
+ }
2475
+ var init_archive_planning = __esm({
2476
+ "../core/src/types/archive-planning.ts"() {
2477
+ "use strict";
2478
+ init_docker_nodes();
2479
+ }
2480
+ });
2481
+
2347
2482
  // ../core/src/types/agent-state.ts
2348
2483
  function isEphemeralAgent(agent) {
2349
2484
  const metadata = agent.metadata ?? {};
@@ -4069,49 +4204,6 @@ var init_workflow_lifecycle_validation = __esm({
4069
4204
  });
4070
4205
 
4071
4206
  // ../core/src/types.ts
4072
- function assertNotWorkspaceTaskMerge(task) {
4073
- if (isWorkspaceTask(task)) {
4074
- throw new WorkspaceTaskMergeError(
4075
- `Workspace task ${task.id} cannot merge until per-repo merge support (master-plan U6) lands`
4076
- );
4077
- }
4078
- }
4079
- function isWorkspaceTask(task) {
4080
- const worktrees = task.workspaceWorktrees;
4081
- return !!worktrees && Object.keys(worktrees).length > 0;
4082
- }
4083
- function isMcpSecretRef(value) {
4084
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
4085
- const candidate = value;
4086
- return typeof candidate.secretRef === "string" && candidate.secretRef.trim().length > 0 && (candidate.scope === "project" || candidate.scope === "global");
4087
- }
4088
- function formatPlanningPlanMd(summary) {
4089
- const normalizeListItem = (item) => item.replace(/\s+/g, " ").trim();
4090
- const list = (items) => items && items.length > 0 ? items.map((item) => `- ${normalizeListItem(item)}`).join("\n") : "_None_";
4091
- const proposedChanges = list(summary.proposedChanges);
4092
- const acceptanceCriteria = list(summary.acceptanceCriteria);
4093
- const dependencies = list(summary.suggestedDependencies);
4094
- const deliverables = list(summary.keyDeliverables);
4095
- return `# ${summary.title}
4096
-
4097
- ${summary.description}
4098
-
4099
- ## What to change
4100
- ${proposedChanges}
4101
-
4102
- ## Acceptance criteria
4103
- ${acceptanceCriteria}
4104
-
4105
- ## Size
4106
- ${summary.suggestedSize}
4107
-
4108
- ## Suggested dependencies
4109
- ${dependencies}
4110
-
4111
- ## Key deliverables
4112
- ${deliverables}
4113
- `;
4114
- }
4115
4207
  function validateMessageMetadata(metadata) {
4116
4208
  if (!metadata) {
4117
4209
  return;
@@ -4163,7 +4255,7 @@ function validateMessageMetadata(metadata) {
4163
4255
  if (metadata.proposalStatus === "pending" && (metadata.claimOwnerToken !== void 0 || metadata.claimStartedAt !== void 0)) throw new Error("pending proposal cannot have a creation lease");
4164
4256
  }
4165
4257
  }
4166
- var DEPRECATED_BUILTIN_WORKFLOW_IDS, CheckoutConflictError, DUPLICATE_OF_METADATA_KEY, WorkspaceTaskMergeError, SHARED_STATE_SNAPSHOT_VERSION, COLUMN_LABELS, COLUMN_DESCRIPTIONS, VALID_TRANSITIONS;
4258
+ var DEPRECATED_BUILTIN_WORKFLOW_IDS;
4167
4259
  var init_types = __esm({
4168
4260
  "../core/src/types.ts"() {
4169
4261
  "use strict";
@@ -4177,8 +4269,11 @@ var init_types = __esm({
4177
4269
  init_merge_policy();
4178
4270
  init_workflow_steps();
4179
4271
  init_documents_artifacts();
4180
- init_settings_schema();
4181
- init_docker_nodes();
4272
+ init_task_core();
4273
+ init_todo_list();
4274
+ init_settings_scope();
4275
+ init_board_config();
4276
+ init_archive_planning();
4182
4277
  init_agent_state();
4183
4278
  init_planner_intervention();
4184
4279
  init_agents();
@@ -4193,49 +4288,6 @@ var init_types = __esm({
4193
4288
  DEPRECATED_BUILTIN_WORKFLOW_IDS = /* @__PURE__ */ new Set([
4194
4289
  "builtin:brainstorming"
4195
4290
  ]);
4196
- CheckoutConflictError = class extends Error {
4197
- constructor(taskId, currentHolderId, requestedById) {
4198
- super(`Task ${taskId} is already checked out by agent ${currentHolderId}`);
4199
- this.taskId = taskId;
4200
- this.currentHolderId = currentHolderId;
4201
- this.requestedById = requestedById;
4202
- this.name = "CheckoutConflictError";
4203
- }
4204
- };
4205
- DUPLICATE_OF_METADATA_KEY = "duplicateOfTaskIds";
4206
- WorkspaceTaskMergeError = class extends Error {
4207
- constructor(message) {
4208
- super(message);
4209
- this.name = "WorkspaceTaskMergeError";
4210
- }
4211
- };
4212
- SHARED_STATE_SNAPSHOT_VERSION = 1;
4213
- COLUMN_LABELS = {
4214
- triage: "Planning",
4215
- todo: "Todo",
4216
- "in-progress": "In Progress",
4217
- "in-review": "In Review",
4218
- done: "Done",
4219
- archived: "Archived"
4220
- };
4221
- COLUMN_DESCRIPTIONS = {
4222
- triage: "Raw ideas \u2014 AI will plan these",
4223
- "in-progress": "AI is working on this in a worktree",
4224
- done: "Merged and closed",
4225
- archived: "Completed and archived"
4226
- };
4227
- VALID_TRANSITIONS = {
4228
- // FN-4892: intake-side heuristics may cold-archive tasks before execution starts.
4229
- triage: ["todo", "archived"],
4230
- // FN-4892: allow direct archival for newly specified intake tasks.
4231
- todo: ["in-progress", "triage", "archived"],
4232
- // NOTE: "in-progress" → "done" is enabled for mission validation tasks that complete directly.
4233
- // Regular implementation tasks should move through "in-review" before "done".
4234
- "in-progress": ["in-review", "todo", "triage", "done"],
4235
- "in-review": ["done", "in-progress", "todo", "triage"],
4236
- done: ["todo", "triage", "archived"],
4237
- archived: ["done"]
4238
- };
4239
4291
  }
4240
4292
  });
4241
4293
 
@@ -12856,6 +12908,7 @@ var init_project = __esm({
12856
12908
  paused: integer("paused").default(0),
12857
12909
  userPaused: integer("user_paused").default(0),
12858
12910
  pausedReason: text("paused_reason"),
12911
+ wedgeNotification: text("wedge_notification"),
12859
12912
  baseBranch: text("base_branch"),
12860
12913
  branch: text("branch"),
12861
12914
  autoMerge: integer("auto_merge"),
@@ -21220,6 +21273,9 @@ function validateColumns(ir2) {
21220
21273
  throw new WorkflowIrError(`Workflow IR has duplicate column id '${column.id}'`);
21221
21274
  }
21222
21275
  seen.add(column.id);
21276
+ if (column.description !== void 0 && typeof column.description !== "string") {
21277
+ throw new WorkflowIrError(`Workflow IR column '${column.id}' description must be a string`);
21278
+ }
21223
21279
  if (!Array.isArray(column.traits)) {
21224
21280
  throw new WorkflowIrError(`Workflow IR column '${column.id}' traits must be an array`);
21225
21281
  }
@@ -36443,6 +36499,7 @@ async function applySchemaBaseline(db, options = {}) {
36443
36499
  const legacyAdoptionDrainedMarkerRuntimeGrantsAlreadyApplied = applied.includes(
36444
36500
  LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_VERSION
36445
36501
  );
36502
+ const taskWedgeNotificationAlreadyApplied = applied.includes(TASK_WEDGE_NOTIFICATION_VERSION);
36446
36503
  assertBinaryNotOlderThanDatabase(applied);
36447
36504
  let schemaChanged = false;
36448
36505
  if (!baselineAlreadyApplied) {
@@ -36766,17 +36823,25 @@ async function applySchemaBaseline(db, options = {}) {
36766
36823
  );
36767
36824
  schemaChanged = true;
36768
36825
  }
36826
+ if (!taskWedgeNotificationAlreadyApplied) {
36827
+ const migrationSql = await readFile4(TASK_WEDGE_NOTIFICATION_MIGRATION_PATH, "utf8");
36828
+ await tx.execute(sql.raw(migrationSql));
36829
+ await tx.execute(
36830
+ sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${TASK_WEDGE_NOTIFICATION_VERSION}) ON CONFLICT (version) DO NOTHING`
36831
+ );
36832
+ schemaChanged = true;
36833
+ }
36769
36834
  return { applied: schemaChanged, pluginHooksRun: pluginHooks.length };
36770
36835
  });
36771
36836
  }
36772
- var SCHEMA_BASELINE_VERSION, TASK_DECLARED_SYMBOLS_VERSION, INITIAL_SCHEMA_VERSION, AUTOMATION_ISOLATION_SCHEMA_VERSION, ANALYTICS_ISOLATION_SCHEMA_VERSION, MONITOR_APPROVAL_ISOLATION_SCHEMA_VERSION, LEGACY_CUTOVER_PRESERVATION_SCHEMA_VERSION, MULTI_PROJECT_CUTOVER_SCHEMA_VERSION, PROJECT_OWNERSHIP_SCHEMA_VERSION, SQLITE_SCHEMA_PARITY_VERSION, SESSION_ADVISOR_ENABLED_SCHEMA_VERSION, MISSION_FIX_IDEMPOTENCY_VERSION, IMPORT_TRANSLATION_CACHE_VERSION, IMPORT_TRANSLATION_CACHE_SCOPE_FIX_VERSION, IMPORT_TRANSLATION_CACHE_LEGACY_PARTITION_BACKFILL_VERSION, OWNER_PROJECT_ID_SPLIT_VERSION, CHAT_SESSION_PINS_VERSION, EXECUTOR_TOOL_FAILURE_RETRY_VERSION, EXECUTOR_ESCALATION_ATTEMPT_VERSION, GLOBAL_ROUTINES_SCHEMA_VERSION, TASK_MERGER_MODEL_LANE_VERSION, BULK_COMPLETION_REFUSAL_AT_VERSION, TASK_PROPOSAL_CLAIM_VERSION, CONFIGURATION_REVISIONS_VERSION, IDEATION_SCHEMA_VERSION, RESEARCH_FEATURE_PROVENANCE_VERSION, TASK_VERIFICATION_REQUEST_VERSION, SYMBOL_LOCKS_SCHEMA_VERSION, BIGINT_COUNTERS_VERSION, PLANNING_ACTIVE_TIMING_VERSION, SQLITE_MIGRATION_RUNTIME_READ_VERSION, WORKFLOW_TASK_CONTINUATIONS_VERSION, LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_VERSION, LEGACY_ADOPTION_DRAINED_MARKER_FUNCTION, StaleBinarySchemaError, MIGRATION_BOOKKEEPING_TABLE, LEGACY_ADOPTION_DRAINED_MARKER, __dirname, MIGRATIONS_DIR, BASELINE_MIGRATION_PATH, AUTOMATION_ISOLATION_MIGRATION_PATH, ANALYTICS_ISOLATION_MIGRATION_PATH, MONITOR_APPROVAL_ISOLATION_MIGRATION_PATH, LEGACY_CUTOVER_PRESERVATION_MIGRATION_PATH, MULTI_PROJECT_CUTOVER_MIGRATION_PATH, PROJECT_OWNERSHIP_MIGRATION_PATH, SQLITE_SCHEMA_PARITY_MIGRATION_PATH, SESSION_ADVISOR_ENABLED_MIGRATION_PATH, MISSION_FIX_IDEMPOTENCY_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_SCOPE_FIX_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_LEGACY_PARTITION_BACKFILL_MIGRATION_PATH, OWNER_PROJECT_ID_SPLIT_MIGRATION_PATH, CHAT_SESSION_PINS_MIGRATION_PATH, EXECUTOR_TOOL_FAILURE_RETRY_MIGRATION_PATH, EXECUTOR_ESCALATION_ATTEMPT_MIGRATION_PATH, GLOBAL_ROUTINES_MIGRATION_PATH, TASK_MERGER_MODEL_LANE_MIGRATION_PATH, BULK_COMPLETION_REFUSAL_AT_MIGRATION_PATH, TASK_PROPOSAL_CLAIM_MIGRATION_PATH, CONFIGURATION_REVISIONS_MIGRATION_PATH, IDEATION_MIGRATION_PATH, RESEARCH_FEATURE_PROVENANCE_MIGRATION_PATH, TASK_VERIFICATION_REQUEST_MIGRATION_PATH, SYMBOL_LOCKS_MIGRATION_PATH, BIGINT_COUNTERS_MIGRATION_PATH, WORKFLOW_IR_PIN_AND_LEGACY_ADOPTION_VERSION, WORKFLOW_IR_PIN_AND_LEGACY_ADOPTION_MIGRATION_PATH, PLANNING_ACTIVE_TIMING_MIGRATION_PATH, TASK_DECLARED_SYMBOLS_MIGRATION_PATH, SQLITE_MIGRATION_RUNTIME_READ_PATH, WORKFLOW_TASK_CONTINUATIONS_PATH, LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_PATH;
36837
+ var SCHEMA_BASELINE_VERSION, TASK_DECLARED_SYMBOLS_VERSION, INITIAL_SCHEMA_VERSION, AUTOMATION_ISOLATION_SCHEMA_VERSION, ANALYTICS_ISOLATION_SCHEMA_VERSION, MONITOR_APPROVAL_ISOLATION_SCHEMA_VERSION, LEGACY_CUTOVER_PRESERVATION_SCHEMA_VERSION, MULTI_PROJECT_CUTOVER_SCHEMA_VERSION, PROJECT_OWNERSHIP_SCHEMA_VERSION, SQLITE_SCHEMA_PARITY_VERSION, SESSION_ADVISOR_ENABLED_SCHEMA_VERSION, MISSION_FIX_IDEMPOTENCY_VERSION, IMPORT_TRANSLATION_CACHE_VERSION, IMPORT_TRANSLATION_CACHE_SCOPE_FIX_VERSION, IMPORT_TRANSLATION_CACHE_LEGACY_PARTITION_BACKFILL_VERSION, OWNER_PROJECT_ID_SPLIT_VERSION, CHAT_SESSION_PINS_VERSION, EXECUTOR_TOOL_FAILURE_RETRY_VERSION, EXECUTOR_ESCALATION_ATTEMPT_VERSION, GLOBAL_ROUTINES_SCHEMA_VERSION, TASK_MERGER_MODEL_LANE_VERSION, BULK_COMPLETION_REFUSAL_AT_VERSION, TASK_PROPOSAL_CLAIM_VERSION, CONFIGURATION_REVISIONS_VERSION, IDEATION_SCHEMA_VERSION, RESEARCH_FEATURE_PROVENANCE_VERSION, TASK_VERIFICATION_REQUEST_VERSION, SYMBOL_LOCKS_SCHEMA_VERSION, BIGINT_COUNTERS_VERSION, PLANNING_ACTIVE_TIMING_VERSION, SQLITE_MIGRATION_RUNTIME_READ_VERSION, WORKFLOW_TASK_CONTINUATIONS_VERSION, LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_VERSION, TASK_WEDGE_NOTIFICATION_VERSION, LEGACY_ADOPTION_DRAINED_MARKER_FUNCTION, StaleBinarySchemaError, MIGRATION_BOOKKEEPING_TABLE, LEGACY_ADOPTION_DRAINED_MARKER, __dirname, MIGRATIONS_DIR, BASELINE_MIGRATION_PATH, AUTOMATION_ISOLATION_MIGRATION_PATH, ANALYTICS_ISOLATION_MIGRATION_PATH, MONITOR_APPROVAL_ISOLATION_MIGRATION_PATH, LEGACY_CUTOVER_PRESERVATION_MIGRATION_PATH, MULTI_PROJECT_CUTOVER_MIGRATION_PATH, PROJECT_OWNERSHIP_MIGRATION_PATH, SQLITE_SCHEMA_PARITY_MIGRATION_PATH, SESSION_ADVISOR_ENABLED_MIGRATION_PATH, MISSION_FIX_IDEMPOTENCY_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_SCOPE_FIX_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_LEGACY_PARTITION_BACKFILL_MIGRATION_PATH, OWNER_PROJECT_ID_SPLIT_MIGRATION_PATH, CHAT_SESSION_PINS_MIGRATION_PATH, EXECUTOR_TOOL_FAILURE_RETRY_MIGRATION_PATH, EXECUTOR_ESCALATION_ATTEMPT_MIGRATION_PATH, GLOBAL_ROUTINES_MIGRATION_PATH, TASK_MERGER_MODEL_LANE_MIGRATION_PATH, BULK_COMPLETION_REFUSAL_AT_MIGRATION_PATH, TASK_PROPOSAL_CLAIM_MIGRATION_PATH, CONFIGURATION_REVISIONS_MIGRATION_PATH, IDEATION_MIGRATION_PATH, RESEARCH_FEATURE_PROVENANCE_MIGRATION_PATH, TASK_VERIFICATION_REQUEST_MIGRATION_PATH, SYMBOL_LOCKS_MIGRATION_PATH, BIGINT_COUNTERS_MIGRATION_PATH, WORKFLOW_IR_PIN_AND_LEGACY_ADOPTION_VERSION, WORKFLOW_IR_PIN_AND_LEGACY_ADOPTION_MIGRATION_PATH, PLANNING_ACTIVE_TIMING_MIGRATION_PATH, TASK_DECLARED_SYMBOLS_MIGRATION_PATH, SQLITE_MIGRATION_RUNTIME_READ_PATH, WORKFLOW_TASK_CONTINUATIONS_PATH, LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_PATH, TASK_WEDGE_NOTIFICATION_MIGRATION_PATH;
36773
36838
  var init_schema_applier = __esm({
36774
36839
  "../core/src/postgres/schema-applier.ts"() {
36775
36840
  "use strict";
36776
36841
  init_drizzle_orm();
36777
36842
  init_plugin_schema_hook();
36778
36843
  init_advisory_locks();
36779
- SCHEMA_BASELINE_VERSION = "0032";
36844
+ SCHEMA_BASELINE_VERSION = "0033";
36780
36845
  TASK_DECLARED_SYMBOLS_VERSION = "0028";
36781
36846
  INITIAL_SCHEMA_VERSION = "0000";
36782
36847
  AUTOMATION_ISOLATION_SCHEMA_VERSION = "0001";
@@ -36809,6 +36874,7 @@ var init_schema_applier = __esm({
36809
36874
  SQLITE_MIGRATION_RUNTIME_READ_VERSION = "0030";
36810
36875
  WORKFLOW_TASK_CONTINUATIONS_VERSION = "0031";
36811
36876
  LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_VERSION = "0032";
36877
+ TASK_WEDGE_NOTIFICATION_VERSION = "0033";
36812
36878
  LEGACY_ADOPTION_DRAINED_MARKER_FUNCTION = "fusion_mark_legacy_adoption_drained";
36813
36879
  StaleBinarySchemaError = class extends Error {
36814
36880
  constructor(databaseVersion, binaryVersion) {
@@ -36915,6 +36981,10 @@ var init_schema_applier = __esm({
36915
36981
  MIGRATIONS_DIR,
36916
36982
  "0032_legacy_adoption_drained_marker_runtime_grants.sql"
36917
36983
  );
36984
+ TASK_WEDGE_NOTIFICATION_MIGRATION_PATH = join8(
36985
+ MIGRATIONS_DIR,
36986
+ "0033_fn-8505_wedge_notification.sql"
36987
+ );
36918
36988
  }
36919
36989
  });
36920
36990
 
@@ -37305,6 +37375,7 @@ __export(sqlite_migrator_exports, {
37305
37375
  isSqliteMigrationComplete: () => isSqliteMigrationComplete,
37306
37376
  migrateLegacyProjectPluginRows: () => migrateLegacyProjectPluginRows,
37307
37377
  migrateSqliteToPostgres: () => migrateSqliteToPostgres,
37378
+ projectPluginSqliteMigrationKey: () => projectPluginSqliteMigrationKey,
37308
37379
  recordSqliteMigrationComplete: () => recordSqliteMigrationComplete,
37309
37380
  toSnakeCase: () => toSnakeCase2
37310
37381
  });
@@ -37847,7 +37918,12 @@ function normalizeLegacyJson(value, fallback) {
37847
37918
  return fallback;
37848
37919
  }
37849
37920
  }
37921
+ function projectPluginSqliteMigrationKey(projectPath) {
37922
+ return `project-plugins:${resolve6(projectPath)}`;
37923
+ }
37850
37924
  async function migrateLegacyProjectPluginRows(db, sqlitePath, projectPath) {
37925
+ const migrationKey = projectPluginSqliteMigrationKey(projectPath);
37926
+ if (await isSqliteMigrationComplete(db, migrationKey)) return;
37851
37927
  await db.transaction(async (tx) => {
37852
37928
  await migrateLegacyProjectPluginRowsOnSession(
37853
37929
  tx,
@@ -37857,19 +37933,14 @@ async function migrateLegacyProjectPluginRows(db, sqlitePath, projectPath) {
37857
37933
  });
37858
37934
  }
37859
37935
  async function migrateLegacyProjectPluginRowsOnSession(db, sqlitePath, projectPath) {
37860
- if (!sqliteTableExists(sqlitePath, "plugins")) return;
37861
- await acquireSqliteMigrationStateLock(db);
37862
37936
  const canonicalProjectPath = resolve6(projectPath);
37863
- const migrationKey = `project-plugins:${canonicalProjectPath}`;
37937
+ const migrationKey = projectPluginSqliteMigrationKey(projectPath);
37938
+ await acquireSqliteMigrationStateLock(db);
37864
37939
  await ensureMigrationStateTable(db);
37865
37940
  await db.execute(sql`SELECT pg_advisory_xact_lock(hashtextextended(${migrationKey}, 0))`);
37866
- const completed = await db.execute(sql`
37867
- SELECT 1 AS complete
37868
- FROM public.${sql.identifier(SQLITE_MIGRATION_STATE_TABLE)}
37869
- WHERE migration_key = ${migrationKey} AND status = 'complete'
37870
- LIMIT 1
37871
- `);
37872
- if (completed.length > 0) return;
37941
+ const completed = await isSqliteMigrationComplete(db, migrationKey);
37942
+ if (completed) return;
37943
+ if (!sqliteTableExists(sqlitePath, "plugins")) return;
37873
37944
  const sqlite = openSqlite(sqlitePath);
37874
37945
  let rows;
37875
37946
  try {
@@ -38955,6 +39026,17 @@ function buildPgCtlOptionsString(port, flags) {
38955
39026
  function buildPgCtlStartArgs(dataDir, logFile, optionsString) {
38956
39027
  return ["-D", dataDir, "-o", optionsString, "-l", logFile, "-W", "start"];
38957
39028
  }
39029
+ function withWindowsNativeBinPath(environment, nativeRoot, platform4 = process.platform) {
39030
+ if (platform4 !== "win32") return environment;
39031
+ const pathKey = Object.keys(environment).find((key) => key.toLowerCase() === "path") ?? "PATH";
39032
+ const bin = nativeRoot.replace(/[\\/]+$/, "") + "\\bin";
39033
+ const normalize8 = (value) => value.replace(/\//g, "\\").toLowerCase();
39034
+ const inherited = environment[pathKey] ?? "";
39035
+ const segments = inherited.split(";").filter(Boolean);
39036
+ const normalizedBin = normalize8(bin);
39037
+ const deduplicated = segments.filter((segment) => normalize8(segment) !== normalizedBin);
39038
+ return { ...environment, [pathKey]: [bin, ...deduplicated].join(";") };
39039
+ }
38958
39040
  function readPostgresPid(dataDir) {
38959
39041
  try {
38960
39042
  const lines = readFileSync3(join9(dataDir, "postmaster.pid"), "utf-8").split("\n");
@@ -39009,7 +39091,13 @@ async function startServerElevatedRestricted(opts) {
39009
39091
  `embedded postgres: elevated start via pg_ctl restricted token (no helper account); log ${logFile}`
39010
39092
  );
39011
39093
  let stopped = false;
39094
+ let logMonitor = null;
39095
+ const stopMonitoring = () => {
39096
+ if (logMonitor) clearInterval(logMonitor);
39097
+ logMonitor = null;
39098
+ };
39012
39099
  const killAll = () => {
39100
+ stopMonitoring();
39013
39101
  if (stopped) return;
39014
39102
  stopped = true;
39015
39103
  const r = spawnSync(pgCtl, ["-D", opts.dataDir, "-m", "fast", "-t", "30", "-w", "stop"], {
@@ -39043,14 +39131,21 @@ async function startServerElevatedRestricted(opts) {
39043
39131
  killAll();
39044
39132
  await waitForDown();
39045
39133
  },
39134
+ stopMonitoring,
39046
39135
  async stopWrapperOnly() {
39136
+ stopMonitoring();
39047
39137
  stopped = true;
39048
39138
  }
39049
39139
  };
39050
39140
  opts.onLaunched?.(handle);
39051
39141
  const launch = await new Promise(
39052
39142
  (resolve51, reject) => {
39053
- const child = spawn(pgCtl, args, { windowsHide: true });
39143
+ const child = spawn(pgCtl, args, {
39144
+ windowsHide: true,
39145
+ // Do not mutate process.env: sibling dashboard/engine children must
39146
+ // retain their inherited environment unchanged.
39147
+ env: withWindowsNativeBinPath(process.env, opts.nativeRoot)
39148
+ });
39054
39149
  let output = "";
39055
39150
  child.stdout.on("data", (d) => output += d.toString());
39056
39151
  child.stderr.on("data", (d) => output += d.toString());
@@ -39075,16 +39170,14 @@ ${readTail(logFile, 2e3)}`
39075
39170
  killAll();
39076
39171
  throw new Error("embedded postgres: elevated launch cancelled before ready.");
39077
39172
  }
39173
+ if (await probeTcpPort(opts.port, 500)) {
39174
+ ready = true;
39175
+ break;
39176
+ }
39078
39177
  const tail = readTail(logFile, 3e3);
39079
39178
  if (tail !== lastSnapshot) {
39080
39179
  lastSnapshot = tail;
39081
- opts.onLog(`elevated poll pg={${tail.slice(-400)}}`);
39082
- }
39083
- if (/database system is ready to accept connections/.test(tail)) {
39084
- if (await probeTcpPort(opts.port, 500)) {
39085
- ready = true;
39086
- break;
39087
- }
39180
+ opts.onLog(`elevated diagnostic pg={${tail.slice(-400)}}`);
39088
39181
  }
39089
39182
  if (/\bFATAL\b|\bPANIC\b|could not (bind|start|create|access|connect|load)|not permitted|Permission denied|is not the owner/i.test(tail)) {
39090
39183
  killAll();
@@ -39116,6 +39209,18 @@ ${tail}`
39116
39209
  opts.onLog(
39117
39210
  `embedded postgres: elevated server ready on 127.0.0.1:${opts.port} (pid ${postgresPid ?? 0}, restricted token)`
39118
39211
  );
39212
+ let lastLogSnapshot = "";
39213
+ const observePostReadyLog = () => {
39214
+ if (stopped) return;
39215
+ const snapshot = readTail(logFile, 16384);
39216
+ if (snapshot === "(no log file)" || snapshot === lastLogSnapshot) return;
39217
+ const next = snapshot.startsWith(lastLogSnapshot) ? snapshot.slice(lastLogSnapshot.length) : snapshot;
39218
+ lastLogSnapshot = snapshot;
39219
+ if (next) opts.onLog(next);
39220
+ };
39221
+ observePostReadyLog();
39222
+ logMonitor = setInterval(observePostReadyLog, 250);
39223
+ if (typeof logMonitor.unref === "function") logMonitor.unref();
39119
39224
  return handle;
39120
39225
  }
39121
39226
  function probeTcpPort(port, timeoutMs) {
@@ -39135,10 +39240,26 @@ function probeTcpPort(port, timeoutMs) {
39135
39240
  socket.once("error", () => finish(false));
39136
39241
  });
39137
39242
  }
39138
- var elevatedCache;
39243
+ var WindowsPostgresFatalDetector, elevatedCache;
39139
39244
  var init_embedded_windows_elevated = __esm({
39140
39245
  "../core/src/postgres/embedded-windows-elevated.ts"() {
39141
39246
  "use strict";
39247
+ WindowsPostgresFatalDetector = class {
39248
+ buffer = "";
39249
+ matched = false;
39250
+ push(chunk) {
39251
+ if (this.matched || !chunk) return false;
39252
+ this.buffer = (this.buffer + chunk).slice(-16384);
39253
+ const text2 = this.buffer.toLowerCase();
39254
+ if (/server process(?:\s+\(pid\s+\d+\))?\s+was terminated by exception\s+0xc0000142[\s\S]*?terminating any other active server processes[\s\S]*?shutting down due to startup process failure[\s\S]*?database system is shut down/.test(
39255
+ text2
39256
+ )) {
39257
+ this.matched = true;
39258
+ return true;
39259
+ }
39260
+ return false;
39261
+ }
39262
+ };
39142
39263
  elevatedCache = null;
39143
39264
  }
39144
39265
  });
@@ -39148,10 +39269,14 @@ var embedded_lifecycle_exports = {};
39148
39269
  __export(embedded_lifecycle_exports, {
39149
39270
  DEFAULT_EMBEDDED_DATABASE: () => DEFAULT_EMBEDDED_DATABASE,
39150
39271
  DEFAULT_EMBEDDED_INITDB_FLAGS: () => DEFAULT_EMBEDDED_INITDB_FLAGS,
39272
+ DEFAULT_EMBEDDED_MAX_CONNECTIONS: () => DEFAULT_EMBEDDED_MAX_CONNECTIONS,
39273
+ DEFAULT_EMBEDDED_MAX_CONNECTIONS_WIN32: () => DEFAULT_EMBEDDED_MAX_CONNECTIONS_WIN32,
39151
39274
  DEFAULT_EMBEDDED_PASSWORD: () => DEFAULT_EMBEDDED_PASSWORD,
39152
39275
  DEFAULT_EMBEDDED_POSTGRES_FLAGS: () => DEFAULT_EMBEDDED_POSTGRES_FLAGS,
39153
39276
  DEFAULT_EMBEDDED_USER: () => DEFAULT_EMBEDDED_USER,
39154
39277
  DEFAULT_START_TIMEOUT_MS: () => DEFAULT_START_TIMEOUT_MS,
39278
+ EMBEDDED_MAX_CONNECTIONS_MAX: () => EMBEDDED_MAX_CONNECTIONS_MAX,
39279
+ EMBEDDED_MAX_CONNECTIONS_MIN: () => EMBEDDED_MAX_CONNECTIONS_MIN,
39155
39280
  EmbeddedPostgresLifecycle: () => EmbeddedPostgresLifecycle,
39156
39281
  EmbeddedStartTimeoutError: () => EmbeddedStartTimeoutError,
39157
39282
  __setEmbeddedPostgresCtorForTests: () => __setEmbeddedPostgresCtorForTests,
@@ -39170,6 +39295,7 @@ __export(embedded_lifecycle_exports, {
39170
39295
  normalizeMacosEmbeddedPostgresDylibSymlinks: () => normalizeMacosEmbeddedPostgresDylibSymlinks,
39171
39296
  readPortFromPostmasterPid: () => readPortFromPostmasterPid,
39172
39297
  resolveElectronAsarUnpackedPath: () => resolveElectronAsarUnpackedPath,
39298
+ resolveEmbeddedMaxConnections: () => resolveEmbeddedMaxConnections,
39173
39299
  uninstallElectronAsarNativePathPatchForTests: () => uninstallElectronAsarNativePathPatchForTests
39174
39300
  });
39175
39301
  import {
@@ -39379,6 +39505,23 @@ function materializeEmbeddedPostgresRuntimeBinaries(nativeRoot, options) {
39379
39505
  writeFileSync2(marker, sourceMarker, "utf8");
39380
39506
  return destRoot;
39381
39507
  }
39508
+ function isWindowsEmbeddedPostgresBinary(command) {
39509
+ return process.platform === "win32" && typeof command === "string" && /[\\/]bin[\\/](?:postgres|initdb|pg_ctl)\.exe$/i.test(command);
39510
+ }
39511
+ function withWindowsPostgresSpawnEnvironment(command, rest) {
39512
+ if (!isWindowsEmbeddedPostgresBinary(command)) return rest;
39513
+ const args = [...rest];
39514
+ const optionIndex = args.length - 1;
39515
+ const existing = args[optionIndex];
39516
+ if (!existing || typeof existing !== "object" || Array.isArray(existing)) return args;
39517
+ const binDir = dirname6(command);
39518
+ const nativeRoot = dirname6(binDir);
39519
+ args[optionIndex] = {
39520
+ ...existing,
39521
+ env: withWindowsNativeBinPath(existing.env ?? process.env, nativeRoot)
39522
+ };
39523
+ return args;
39524
+ }
39382
39525
  function installElectronAsarNativePathPatch() {
39383
39526
  if (electronAsarNativePathPatchInstalled) return;
39384
39527
  electronAsarNativePathPatchInstalled = true;
@@ -39396,7 +39539,10 @@ function installElectronAsarNativePathPatch() {
39396
39539
  const originalSpawn = childProcessMod.spawn.bind(childProcessMod);
39397
39540
  childProcessMod.spawn = (command, ...rest) => {
39398
39541
  const fixedCommand = typeof command === "string" ? resolveElectronAsarUnpackedPath(command) : command;
39399
- return originalSpawn(fixedCommand, ...rest);
39542
+ return originalSpawn(
39543
+ fixedCommand,
39544
+ ...withWindowsPostgresSpawnEnvironment(fixedCommand, rest)
39545
+ );
39400
39546
  };
39401
39547
  const fsPromisesMod = require2("fs/promises");
39402
39548
  const originalStat = fsPromisesMod.stat.bind(fsPromisesMod);
@@ -39445,6 +39591,12 @@ function getEmbeddedPostgresCtor() {
39445
39591
  function defaultEmbeddedPostgresFlagsFor(platform4) {
39446
39592
  return platform4 === "win32" ? [] : ["-c", "shared_memory_type=mmap"];
39447
39593
  }
39594
+ function resolveEmbeddedMaxConnections(configured, platform4 = process.platform) {
39595
+ if (typeof configured === "number" && Number.isInteger(configured)) {
39596
+ return Math.min(EMBEDDED_MAX_CONNECTIONS_MAX, Math.max(EMBEDDED_MAX_CONNECTIONS_MIN, configured));
39597
+ }
39598
+ return platform4 === "win32" ? DEFAULT_EMBEDDED_MAX_CONNECTIONS_WIN32 : DEFAULT_EMBEDDED_MAX_CONNECTIONS;
39599
+ }
39448
39600
  function defaultEmbeddedDataDir() {
39449
39601
  const home = process.env.HOME || process.env.USERPROFILE || process.cwd();
39450
39602
  return join10(home, ".fusion", "embedded-postgres", "default");
@@ -39631,7 +39783,7 @@ function signalNumber(signal) {
39631
39783
  return 0;
39632
39784
  }
39633
39785
  }
39634
- var require2, stagedEmbeddedPgBundleCache, EMBEDDED_PG_BIN_NAMES, MATERIALIZATION_MARKER_VERSION, electronAsarNativePathPatchInstalled, electronAsarNativePathPatchRestore, embeddedPostgresCtorCache, embeddedPostgresCtorIsTestOverride, windowsElevatedAdminForTests, windowsNativeRootForTests, windowsLauncherForTests, log4, DEFAULT_EMBEDDED_USER, DEFAULT_EMBEDDED_PASSWORD, DEFAULT_EMBEDDED_DATABASE, DEFAULT_EMBEDDED_POSTGRES_FLAGS, DEFAULT_EMBEDDED_INITDB_FLAGS, DEFAULT_START_TIMEOUT_MS, PG_VERSION_FILENAME, MACOS_EMBEDDED_DYLIB_SYMLINKS, runningInstances, POSTMASTER_PID_READ_ATTEMPTS, POSTMASTER_PID_READ_RETRY_MS, EmbeddedPostgresLifecycle, EmbeddedStartCancelledError, EmbeddedStartTimeoutError;
39786
+ var require2, stagedEmbeddedPgBundleCache, EMBEDDED_PG_BIN_NAMES, MATERIALIZATION_MARKER_VERSION, electronAsarNativePathPatchInstalled, electronAsarNativePathPatchRestore, embeddedPostgresCtorCache, embeddedPostgresCtorIsTestOverride, windowsElevatedAdminForTests, windowsNativeRootForTests, windowsLauncherForTests, log4, DEFAULT_EMBEDDED_USER, DEFAULT_EMBEDDED_PASSWORD, DEFAULT_EMBEDDED_DATABASE, DEFAULT_EMBEDDED_POSTGRES_FLAGS, DEFAULT_EMBEDDED_MAX_CONNECTIONS, DEFAULT_EMBEDDED_MAX_CONNECTIONS_WIN32, EMBEDDED_MAX_CONNECTIONS_MIN, EMBEDDED_MAX_CONNECTIONS_MAX, DEFAULT_EMBEDDED_INITDB_FLAGS, DEFAULT_START_TIMEOUT_MS, PG_VERSION_FILENAME, MACOS_EMBEDDED_DYLIB_SYMLINKS, runningInstances, POSTMASTER_PID_READ_ATTEMPTS, POSTMASTER_PID_READ_RETRY_MS, EmbeddedPostgresLifecycle, EmbeddedStartCancelledError, EmbeddedStartTimeoutError;
39635
39787
  var init_embedded_lifecycle = __esm({
39636
39788
  "../core/src/postgres/embedded-lifecycle.ts"() {
39637
39789
  "use strict";
@@ -39662,6 +39814,10 @@ var init_embedded_lifecycle = __esm({
39662
39814
  DEFAULT_EMBEDDED_PASSWORD = "password";
39663
39815
  DEFAULT_EMBEDDED_DATABASE = "fusion";
39664
39816
  DEFAULT_EMBEDDED_POSTGRES_FLAGS = defaultEmbeddedPostgresFlagsFor(process.platform);
39817
+ DEFAULT_EMBEDDED_MAX_CONNECTIONS = 500;
39818
+ DEFAULT_EMBEDDED_MAX_CONNECTIONS_WIN32 = 150;
39819
+ EMBEDDED_MAX_CONNECTIONS_MIN = 32;
39820
+ EMBEDDED_MAX_CONNECTIONS_MAX = 2e3;
39665
39821
  DEFAULT_EMBEDDED_INITDB_FLAGS = [
39666
39822
  "--encoding=UTF8",
39667
39823
  "--locale=C"
@@ -39704,6 +39860,10 @@ var init_embedded_lifecycle = __esm({
39704
39860
  * on a failure that is handled before the timeout fires.
39705
39861
  */
39706
39862
  startTimer = null;
39863
+ windowsFatalDetector = new WindowsPostgresFatalDetector();
39864
+ recoveryAttempts = 0;
39865
+ recoveryInFlight = null;
39866
+ stopRequested = false;
39707
39867
  constructor(opts) {
39708
39868
  this.options = {
39709
39869
  dataDir: opts.dataDir,
@@ -39718,6 +39878,15 @@ var init_embedded_lifecycle = __esm({
39718
39878
  onError: opts.onError ?? ((err) => log4.error(String(err)))
39719
39879
  };
39720
39880
  }
39881
+ /**
39882
+ * Forward native process output to the existing sink, then schedule recovery
39883
+ * only for a confirmed owned Windows fatal shutdown sequence.
39884
+ */
39885
+ forwardPostgresLog = (message) => {
39886
+ this.options.onLog(message);
39887
+ if (process.platform !== "win32" || !this.running || !this.ownsProcess) return;
39888
+ if (this.windowsFatalDetector.push(message)) void this.recoverWindowsFatalOnce();
39889
+ };
39721
39890
  /** The configured or discovered port. Undefined until assigned (explicit or discovered in `start()`). */
39722
39891
  getPort() {
39723
39892
  return this.options.port ?? this.resolvedPort;
@@ -39814,11 +39983,19 @@ var init_embedded_lifecycle = __esm({
39814
39983
  migrationUrlOverridden: false
39815
39984
  };
39816
39985
  }
39986
+ return this.startBounded();
39987
+ }
39988
+ /**
39989
+ * Start an owned postmaster with the same cancellation and timeout contract
39990
+ * used by public startup. Recovery calls this directly because it must not
39991
+ * join a stale pid file or allocate a new endpoint between pool reconnects.
39992
+ */
39993
+ async startBounded(preferredPort) {
39817
39994
  if (this.options.startTimeoutMs <= 0) {
39818
- return this.startInternal();
39995
+ return this.startInternal(void 0, preferredPort);
39819
39996
  }
39820
39997
  const controller = new AbortController();
39821
- const startAttempt = this.startInternal(controller.signal);
39998
+ const startAttempt = this.startInternal(controller.signal, preferredPort);
39822
39999
  let timer2;
39823
40000
  const timeout = new Promise((_resolve, reject) => {
39824
40001
  timer2 = setTimeout(() => {
@@ -39844,12 +40021,9 @@ var init_embedded_lifecycle = __esm({
39844
40021
  this.startTimer = null;
39845
40022
  }
39846
40023
  }
39847
- /**
39848
- * The actual start sequence, with no timeout wrapper. Called by {@link start}
39849
- * either directly (timeout disabled) or via Promise.race with the timeout.
39850
- */
39851
- async startInternal(signal) {
39852
- const port = this.options.port ?? await findFreePort();
40024
+ /** The actual start sequence, invoked only through {@link startBounded}. */
40025
+ async startInternal(signal, preferredPort) {
40026
+ const port = this.options.port ?? preferredPort ?? this.resolvedPort ?? await findFreePort();
39853
40027
  if (signal?.aborted) throw new EmbeddedStartCancelledError(this.options.dataDir);
39854
40028
  this.resolvedPort = port;
39855
40029
  const alreadyInitialized = isDataDirInitialized(this.options.dataDir);
@@ -39863,7 +40037,7 @@ var init_embedded_lifecycle = __esm({
39863
40037
  authMethod: "password",
39864
40038
  initdbFlags: [...this.options.initdbFlags],
39865
40039
  postgresFlags: [...this.options.postgresFlags],
39866
- onLog: this.options.onLog,
40040
+ onLog: this.forwardPostgresLog,
39867
40041
  onError: this.options.onError
39868
40042
  });
39869
40043
  this.pg = pg;
@@ -39895,7 +40069,7 @@ var init_embedded_lifecycle = __esm({
39895
40069
  dataDir: this.options.dataDir,
39896
40070
  port,
39897
40071
  postgresFlags: this.options.postgresFlags,
39898
- onLog: this.options.onLog,
40072
+ onLog: this.forwardPostgresLog,
39899
40073
  onError: this.options.onError,
39900
40074
  startTimeoutMs: this.options.startTimeoutMs,
39901
40075
  signal,
@@ -39973,6 +40147,43 @@ var init_embedded_lifecycle = __esm({
39973
40147
  migrationUrlOverridden: false
39974
40148
  };
39975
40149
  }
40150
+ /**
40151
+ * FNXC:PostgresEmbedded 2026-07-22-16:25:
40152
+ * A 0xC0000142 backend crash shuts down its whole PostgreSQL cluster. One
40153
+ * lifecycle-owned retry reuses the initialized directory and same resolved
40154
+ * port; joiners, stop/detach, and a second incident are deliberately inert.
40155
+ */
40156
+ async recoverWindowsFatalOnce() {
40157
+ if (this.recoveryInFlight || this.recoveryAttempts >= 1 || this.stopRequested || !this.ownsProcess) return;
40158
+ this.recoveryAttempts += 1;
40159
+ this.recoveryInFlight = (async () => {
40160
+ this.options.onLog("embedded postgres: detected Windows DLL initialization shutdown; attempting one owned-cluster recovery");
40161
+ try {
40162
+ if (this.nonAdminHandle) await this.nonAdminHandle.stop();
40163
+ else await this.pg?.stop();
40164
+ this.pg = null;
40165
+ this.nonAdminHandle = null;
40166
+ this.running = false;
40167
+ runningInstances.delete(this.options.dataDir);
40168
+ if (this.stopRequested || !this.ownsProcess) return;
40169
+ const recoveryPort = this.resolvedPort;
40170
+ if (recoveryPort === void 0) {
40171
+ throw new Error("embedded postgres: recovery lost its resolved port");
40172
+ }
40173
+ await this.startBounded(recoveryPort);
40174
+ this.options.onLog("embedded postgres: Windows owned-cluster recovery completed; existing pools may reconnect");
40175
+ } catch (error2) {
40176
+ this.running = false;
40177
+ runningInstances.delete(this.options.dataDir);
40178
+ this.options.onError(
40179
+ `embedded postgres: Windows DLL initialization recovery failed after one retry; restart Fusion and inspect the System log. ${error2 instanceof Error ? error2.message : String(error2)}`
40180
+ );
40181
+ } finally {
40182
+ this.recoveryInFlight = null;
40183
+ }
40184
+ })();
40185
+ await this.recoveryInFlight;
40186
+ }
39976
40187
  async settleCancelledStart(pg) {
39977
40188
  if (this.nonAdminHandle) {
39978
40189
  try {
@@ -40119,6 +40330,7 @@ var init_embedded_lifecycle = __esm({
40119
40330
  */
40120
40331
  detachWithoutStop() {
40121
40332
  this.uninstallShutdownHook();
40333
+ this.nonAdminHandle?.stopMonitoring();
40122
40334
  this.pg = null;
40123
40335
  this.nonAdminHandle = null;
40124
40336
  this.running = false;
@@ -40126,6 +40338,7 @@ var init_embedded_lifecycle = __esm({
40126
40338
  runningInstances.delete(this.options.dataDir);
40127
40339
  }
40128
40340
  async stop() {
40341
+ this.stopRequested = true;
40129
40342
  this.uninstallShutdownHook();
40130
40343
  if (!this.ownsProcess) {
40131
40344
  this.running = false;
@@ -52150,10 +52363,10 @@ async function bootSchemaBackendOnce(options, bypassProjectIsolation = false) {
52150
52363
  let resolvedBackend = backend;
52151
52364
  let embeddedDataDir = null;
52152
52365
  if (backend.mode === "embedded") {
52153
- const { EmbeddedPostgresLifecycle: EmbeddedPostgresLifecycle2, defaultEmbeddedDataDir: defaultEmbeddedDataDir2, DEFAULT_EMBEDDED_DATABASE: DEFAULT_EMBEDDED_DATABASE2 } = await Promise.resolve().then(() => (init_embedded_lifecycle(), embedded_lifecycle_exports));
52366
+ const { EmbeddedPostgresLifecycle: EmbeddedPostgresLifecycle2, defaultEmbeddedDataDir: defaultEmbeddedDataDir2, DEFAULT_EMBEDDED_DATABASE: DEFAULT_EMBEDDED_DATABASE2, resolveEmbeddedMaxConnections: resolveEmbeddedMaxConnections2 } = await Promise.resolve().then(() => (init_embedded_lifecycle(), embedded_lifecycle_exports));
52154
52367
  const { GlobalSettingsStore: GlobalSettingsStore2 } = await Promise.resolve().then(() => (init_global_settings(), global_settings_exports));
52155
52368
  const configuredMaxConnections = process.env.VITEST === "true" && !options.globalSettingsDir ? void 0 : (await new GlobalSettingsStore2(options.globalSettingsDir).getSettings()).embeddedPostgresMaxConnections;
52156
- const maxConnections = Number.isInteger(configuredMaxConnections) ? Math.min(2e3, Math.max(32, configuredMaxConnections)) : 500;
52369
+ const maxConnections = resolveEmbeddedMaxConnections2(configuredMaxConnections);
52157
52370
  const dataDir = resolve8(options.embeddedDataDir ?? defaultEmbeddedDataDir2());
52158
52371
  embeddedDataDir = dataDir;
52159
52372
  log5.log(`startup-factory: starting embedded PostgreSQL (data dir ${dataDir})`);
@@ -52390,7 +52603,20 @@ async function createTaskStoreForBackend(options) {
52390
52603
  try {
52391
52604
  const fusionDir = join16(rootDir, ".fusion");
52392
52605
  const legacySqlitePath = join16(fusionDir, "fusion.db");
52393
- if (existsSync11(legacySqlitePath)) {
52606
+ const {
52607
+ migrateSqliteToPostgres: migrateSqliteToPostgres2,
52608
+ defaultMigrationSources: defaultMigrationSources2,
52609
+ formatMigrationProgress: formatMigrationProgress2,
52610
+ isSqliteMigrationComplete: isSqliteMigrationComplete2,
52611
+ completeSqliteMigration: completeSqliteMigration2,
52612
+ recordSqliteMigrationComplete: recordSqliteMigrationComplete2,
52613
+ CENTRAL_SQLITE_MIGRATION_KEY: CENTRAL_SQLITE_MIGRATION_KEY2
52614
+ } = await Promise.resolve().then(() => (init_sqlite_migrator(), sqlite_migrator_exports));
52615
+ const fallbackProjectId = fallbackProjectIdForRoot(rootDir);
52616
+ let migrationProjectId = options.projectId ?? await lookupRegisteredProjectIdByPath(connections.migration, rootDir) ?? fallbackProjectId;
52617
+ let migrationKey = `project:${migrationProjectId}`;
52618
+ let migrationComplete = await isSqliteMigrationComplete2(connections.migration, migrationKey);
52619
+ if (!migrationComplete && existsSync11(legacySqlitePath)) {
52394
52620
  let globalDir = options.globalSettingsDir;
52395
52621
  if (!globalDir) {
52396
52622
  try {
@@ -52400,21 +52626,24 @@ async function createTaskStoreForBackend(options) {
52400
52626
  globalDir = void 0;
52401
52627
  }
52402
52628
  }
52403
- let migrationProjectId = options.projectId ?? await lookupRegisteredProjectIdByPath(connections.migration, rootDir);
52629
+ let resolvedFromLegacyCentral = false;
52630
+ const centralMigrationComplete = await isSqliteMigrationComplete2(
52631
+ connections.migration,
52632
+ CENTRAL_SQLITE_MIGRATION_KEY2
52633
+ );
52404
52634
  const legacyCentralPath = globalDir ? join16(globalDir, "fusion-central.db") : void 0;
52405
- if (!migrationProjectId && legacyCentralPath && existsSync11(legacyCentralPath) && isValidSqliteDatabaseFile(legacyCentralPath)) {
52635
+ if (!centralMigrationComplete && migrationProjectId === fallbackProjectId && legacyCentralPath && existsSync11(legacyCentralPath) && isValidSqliteDatabaseFile(legacyCentralPath)) {
52406
52636
  const { DatabaseSync: DatabaseSync2 } = await Promise.resolve().then(() => (init_sqlite_adapter(), sqlite_adapter_exports));
52407
52637
  const legacyCentral = new DatabaseSync2(legacyCentralPath, { readOnly: true });
52408
52638
  try {
52409
52639
  const row = legacyCentral.prepare(`SELECT id FROM projects WHERE path = ? LIMIT 1`).get(rootDir);
52410
- migrationProjectId = row?.id;
52640
+ migrationProjectId = row?.id ?? migrationProjectId;
52641
+ resolvedFromLegacyCentral = Boolean(row?.id);
52411
52642
  } catch {
52412
52643
  } finally {
52413
52644
  legacyCentral.close();
52414
52645
  }
52415
52646
  }
52416
- const fallbackProjectId = fallbackProjectIdForRoot(rootDir);
52417
- migrationProjectId ??= fallbackProjectId;
52418
52647
  if (migrationProjectId !== fallbackProjectId) {
52419
52648
  try {
52420
52649
  await rekeyFallbackProjectPartition(connections.migration, fallbackProjectId, migrationProjectId);
@@ -52430,14 +52659,9 @@ async function createTaskStoreForBackend(options) {
52430
52659
  }
52431
52660
  }
52432
52661
  }
52433
- const migrationKey = `project:${migrationProjectId ?? rootDir}`;
52434
- const { migrateSqliteToPostgres: migrateSqliteToPostgres2, migrateLegacyProjectPluginRows: migrateLegacyProjectPluginRows2, defaultMigrationSources: defaultMigrationSources2, formatMigrationProgress: formatMigrationProgress2, isSqliteMigrationComplete: isSqliteMigrationComplete2, completeSqliteMigration: completeSqliteMigration2, recordSqliteMigrationComplete: recordSqliteMigrationComplete2, CENTRAL_SQLITE_MIGRATION_KEY: CENTRAL_SQLITE_MIGRATION_KEY2 } = await Promise.resolve().then(() => (init_sqlite_migrator(), sqlite_migrator_exports));
52435
- const migrationComplete = await isSqliteMigrationComplete2(connections.migration, migrationKey);
52662
+ migrationKey = `project:${migrationProjectId}`;
52663
+ migrationComplete = await isSqliteMigrationComplete2(connections.migration, migrationKey);
52436
52664
  if (!migrationComplete && isValidSqliteDatabaseFile(legacySqlitePath)) {
52437
- const centralMigrationComplete = await isSqliteMigrationComplete2(
52438
- connections.migration,
52439
- CENTRAL_SQLITE_MIGRATION_KEY2
52440
- );
52441
52665
  const sources = defaultMigrationSources2(fusionDir, globalDir ?? join16(fusionDir, "__no-global-dir__")).filter((source) => !centralMigrationComplete || source.pgSchema !== "central").filter((source) => existsSync11(source.sqlitePath) && isValidSqliteDatabaseFile(source.sqlitePath));
52442
52666
  if (sources.length > 0) {
52443
52667
  log5.log(`startup-factory: empty PostgreSQL database with legacy SQLite data present \u2014 auto-migrating ${sources.length} source(s) (SQLite files are kept as backups)`);
@@ -52472,6 +52696,12 @@ async function createTaskStoreForBackend(options) {
52472
52696
  rootDir
52473
52697
  });
52474
52698
  }
52699
+ if (resolvedFromLegacyCentral) {
52700
+ const registeredId = await lookupRegisteredProjectIdByPath(connections.migration, rootDir);
52701
+ if (registeredId !== migrationProjectId) {
52702
+ throw new Error(`legacy project identity ${migrationProjectId} was not materialized for ${rootDir} before migration completion`);
52703
+ }
52704
+ }
52475
52705
  await completeSqliteMigration2(connections.migration, migrationKey);
52476
52706
  if (sources.some((source) => source.pgSchema === "central")) {
52477
52707
  await recordSqliteMigrationComplete2(
@@ -52489,14 +52719,9 @@ async function createTaskStoreForBackend(options) {
52489
52719
  log5.log(`startup-factory: SQLite \u2192 PostgreSQL auto-migration complete (${migratedRows} row(s) across ${report.tables.length} table(s))`);
52490
52720
  }
52491
52721
  }
52492
- if (isValidSqliteDatabaseFile(legacySqlitePath)) {
52493
- await migrateLegacyProjectPluginRows2(
52494
- connections.migration,
52495
- legacySqlitePath,
52496
- rootDir
52497
- );
52498
- }
52499
52722
  }
52723
+ const { migrateLegacyProjectPluginRows: migrateLegacyProjectPluginRows2 } = await Promise.resolve().then(() => (init_sqlite_migrator(), sqlite_migrator_exports));
52724
+ await migrateLegacyProjectPluginRows2(connections.migration, legacySqlitePath, rootDir);
52500
52725
  } catch (err) {
52501
52726
  await connections.close().catch(() => void 0);
52502
52727
  await stopEmbeddedRuntime(
@@ -62163,6 +62388,7 @@ var init_persistence = __esm({
62163
62388
  defineTaskColumn("overlapBlockedBy", (task) => task.overlapBlockedBy ?? null),
62164
62389
  defineTaskColumn("paused", (task) => task.paused ? 1 : 0),
62165
62390
  defineTaskColumn("pausedReason", (task) => task.pausedReason ?? null),
62391
+ defineTaskColumn("wedgeNotification", (task) => toJsonNullable(task.wedgeNotification)),
62166
62392
  defineTaskColumn("userPaused", (task) => task.userPaused ? 1 : 0),
62167
62393
  defineTaskColumn("baseBranch", (task) => task.baseBranch ?? null),
62168
62394
  defineTaskColumn("branch", (task) => task.branch ?? null),
@@ -62572,6 +62798,7 @@ function rowToTask(row) {
62572
62798
  overlapBlockedBy: row.overlapBlockedBy || void 0,
62573
62799
  paused: row.paused ? true : void 0,
62574
62800
  pausedReason: row.pausedReason || void 0,
62801
+ wedgeNotification: fromJson(row.wedgeNotification) ?? void 0,
62575
62802
  userPaused: row.userPaused ? true : void 0,
62576
62803
  baseBranch: row.baseBranch || void 0,
62577
62804
  executionStartBranch: row.executionStartBranch || void 0,
@@ -84827,6 +85054,7 @@ function getTaskSelectClauseImpl2(store, slim, tableAlias) {
84827
85054
  "overlapBlockedBy",
84828
85055
  "paused",
84829
85056
  "pausedReason",
85057
+ "wedgeNotification",
84830
85058
  "userPaused",
84831
85059
  "baseBranch",
84832
85060
  "branch",
@@ -91545,7 +91773,7 @@ var init_async_eval_store = __esm({
91545
91773
  }
91546
91774
  });
91547
91775
 
91548
- // ../core/src/task-store/remaining-ops-1.ts
91776
+ // ../core/src/task-store/project-store-ops.ts
91549
91777
  import { randomUUID as randomUUID33 } from "node:crypto";
91550
91778
  import { mkdir as mkdir13, writeFile as writeFile12 } from "node:fs/promises";
91551
91779
  import { join as join33 } from "node:path";
@@ -92413,8 +92641,8 @@ function getEvalStoreImpl(store) {
92413
92641
  return store.evalStore;
92414
92642
  }
92415
92643
  var workflowDefinitionBeforeInsertForTesting;
92416
- var init_remaining_ops_1 = __esm({
92417
- "../core/src/task-store/remaining-ops-1.ts"() {
92644
+ var init_project_store_ops = __esm({
92645
+ "../core/src/task-store/project-store-ops.ts"() {
92418
92646
  "use strict";
92419
92647
  init_store();
92420
92648
  init_errors3();
@@ -99114,6 +99342,11 @@ async function updateTaskUnlockedImpl(store, id, updates, runContext) {
99114
99342
  } else if (updates.pausedReason !== void 0) {
99115
99343
  task.pausedReason = updates.pausedReason;
99116
99344
  }
99345
+ if (updates.wedgeNotification === null) {
99346
+ task.wedgeNotification = void 0;
99347
+ } else if (updates.wedgeNotification !== void 0) {
99348
+ task.wedgeNotification = updates.wedgeNotification;
99349
+ }
99117
99350
  if (updates.tokenBudgetSoftAlertedAt === null) {
99118
99351
  task.tokenBudgetSoftAlertedAt = void 0;
99119
99352
  } else if (updates.tokenBudgetSoftAlertedAt !== void 0) {
@@ -99776,7 +100009,7 @@ var init_store = __esm({
99776
100009
  init_task_store_helpers();
99777
100010
  init_task_row_mappers();
99778
100011
  init_task_mutation_ops();
99779
- init_remaining_ops_1();
100012
+ init_project_store_ops();
99780
100013
  init_workflow_integrity();
99781
100014
  init_branch_group_ops();
99782
100015
  init_archive_lifecycle_2();
@@ -100640,6 +100873,26 @@ var init_store = __esm({
100640
100873
  async updateTask(id, updates, runContext) {
100641
100874
  return updateTaskImpl(this, id, updates, runContext);
100642
100875
  }
100876
+ /**
100877
+ * Atomically opens or resolves the durable task-wedge episode. The task lock is
100878
+ * shared by SQLite and PostgreSQL persistence writes, so concurrent engine
100879
+ * observers cannot allocate two episodes for one active normalized reason.
100880
+ */
100881
+ async claimTaskWedgeNotificationEpisode(taskId, reasonKey) {
100882
+ let result = { claimed: false };
100883
+ await this.updateTaskAtomic(taskId, (current) => {
100884
+ const prior = current.wedgeNotification;
100885
+ if (reasonKey === null) {
100886
+ if (!prior || prior.status === "resolved") return null;
100887
+ return { wedgeNotification: { ...prior, status: "resolved", transitionedAt: (/* @__PURE__ */ new Date()).toISOString() } };
100888
+ }
100889
+ if (prior?.status === "active" && prior.reasonKey === reasonKey) return null;
100890
+ const episodeId = randomUUID37();
100891
+ result = { episodeId, claimed: true };
100892
+ return { wedgeNotification: { reasonKey, episodeId, status: "active", transitionedAt: (/* @__PURE__ */ new Date()).toISOString() } };
100893
+ });
100894
+ return result;
100895
+ }
100643
100896
  async claimNextToolFailureRetry(taskId, expectedCursor, maxRetries) {
100644
100897
  return claimNextToolFailureRetryImpl(this, taskId, expectedCursor, maxRetries);
100645
100898
  }
@@ -172025,6 +172278,7 @@ var init_ntfy_provider = __esm({
172025
172278
  "in-review",
172026
172279
  "merged",
172027
172280
  "failed",
172281
+ "task-wedged",
172028
172282
  "awaiting-approval",
172029
172283
  "awaiting-user-review",
172030
172284
  "planning-awaiting-input",
@@ -172128,6 +172382,15 @@ var init_ntfy_provider = __esm({
172128
172382
  message: `Task "${identifier}" has failed and needs attention`,
172129
172383
  priority: "high"
172130
172384
  },
172385
+ "task-wedged": {
172386
+ title: `Task ${taskId} needs operator action`,
172387
+ message: [
172388
+ `Task "${identifier}" is wedged${typeof payload.metadata?.reason === "string" ? `: ${payload.metadata.reason}` : " and needs operator action"}`,
172389
+ typeof payload.metadata?.gate === "string" ? `Gate: ${payload.metadata.gate}` : null,
172390
+ typeof payload.metadata?.action === "string" ? `Recommended action: ${payload.metadata.action}` : null
172391
+ ].filter((part) => part !== null).join("\n"),
172392
+ priority: "high"
172393
+ },
172131
172394
  "awaiting-approval": {
172132
172395
  title: payload.metadata?.awaitingApprovalReason === "plan-review-replan-cap" ? `Plan Review cap reached for ${taskId}` : `Plan needs approval for ${taskId}`,
172133
172396
  /*
@@ -172482,6 +172745,132 @@ var init_transient_merge_error_classifier = __esm({
172482
172745
  }
172483
172746
  });
172484
172747
 
172748
+ // ../engine/src/notification/task-wedge-notification.ts
172749
+ function describeSelfHealingNoActionWedge(task, stage, metadata) {
172750
+ const description = SELF_HEALING_NO_ACTIONS[stage];
172751
+ if (!description || task.userPaused || task.paused || task.autoMerge === false) return null;
172752
+ const proof = metadata ?? {};
172753
+ if (proof.taskActive === true || proof.hasExecutingTaskLock === true || proof.mergePending === true) return null;
172754
+ if (Array.isArray(proof.livePaths) && proof.livePaths.length > 0) return null;
172755
+ return { reasonKey: `self-healing-no-action:${stage}`, ...description };
172756
+ }
172757
+ function describeTaskWedge(task) {
172758
+ const error2 = task.error ?? "";
172759
+ if (task.pausedReason === "completed-blocked") {
172760
+ return { reasonKey: "completion-blocked", reason: "Completed work is blocked from advancing to review.", action: "Clear the blocker or reset the task to todo." };
172761
+ }
172762
+ if (task.pausedReason === "error-retry-exhausted") {
172763
+ return { reasonKey: "heartbeat-retry-exhausted", reason: "The assigned agent exhausted its heartbeat recovery budget.", action: "Repair the agent configuration, then retry the task." };
172764
+ }
172765
+ if (task.pausedReason === "error-unrecoverable") {
172766
+ return { reasonKey: "heartbeat-error-unrecoverable", reason: "The assigned agent needs operator repair before it can resume.", action: "Repair credentials, access, or configuration, then retry the task." };
172767
+ }
172768
+ const pausedDescriptors = {
172769
+ "branch-cross-contamination": { reasonKey: "branch-cross-contamination", reason: "Branch contamination recovery requires operator intervention.", action: "Inspect the branch history, repair the contamination, then retry or reset to todo." },
172770
+ "branch-conflict-tripwire": { reasonKey: "branch-conflict-tripwire", reason: "A repeated branch conflict stopped automatic recovery.", action: "Resolve the branch conflict, then retry or reset to todo." },
172771
+ "branch-conflict-recovery-exhausted": { reasonKey: "branch-conflict-recovery-exhausted", reason: "Branch conflict recovery retries were exhausted.", action: "Resolve the conflict, then retry or reset to todo." },
172772
+ "branch-conflict-unrecoverable": { reasonKey: "branch-conflict-unrecoverable", reason: "An unrecoverable branch conflict needs operator intervention.", action: "Resolve the conflict or reset the task to todo and retry." },
172773
+ "stuck-loop-exhausted-manual-intervention-required": { reasonKey: "stuck-loop-exhausted", reason: "Self-healing exhausted its stalled-task recovery loop.", action: "Inspect the task state, then retry or reset to todo." },
172774
+ "non-retryable-provider-error": { reasonKey: "non-retryable-provider-error", reason: "A non-retryable provider error stopped the task.", action: "Repair provider access or configuration, then retry the task." },
172775
+ "in-review-stall-deadlock": { reasonKey: "in-review-stall-deadlock", reason: "Review stalled in a deadlock that needs operator intervention.", action: "Inspect review ownership and retry or reset to todo." }
172776
+ };
172777
+ if (task.pausedReason && pausedDescriptors[task.pausedReason]) return pausedDescriptors[task.pausedReason];
172778
+ if (task.status !== "failed") return null;
172779
+ if (error2.startsWith("EXECUTION_DISPATCH_LOOP_EXHAUSTED")) {
172780
+ return { reasonKey: "execution-dispatch-loop-exhausted", reason: "Execution re-queued without progress until its retry budget was exhausted.", action: "Retry, decompose, or rescope the task." };
172781
+ }
172782
+ if (error2.includes("tool failure") || error2.includes("Tool failure")) {
172783
+ return { reasonKey: "tool-failure-retry-exhausted", reason: "Execution tool-failure retries were exhausted.", action: "Inspect the failing tool and retry the task." };
172784
+ }
172785
+ if (/escalat(?:ion|ed).*exhaust|exhaust.*escalat/i.test(error2)) {
172786
+ return { reasonKey: "execution-escalation-exhausted", reason: "The alternate execution escalation was exhausted.", action: "Inspect the failure and retry or rescope the task." };
172787
+ }
172788
+ if (/review.*(?:retry|rework|revision).*(?:exhaust|limit)|(?:exhaust|limit).*review/i.test(error2)) {
172789
+ return { reasonKey: "review-retry-exhausted", reason: "The configured review recovery budget was exhausted.", action: "Review the feedback, fix the task, or use the approved review recovery action." };
172790
+ }
172791
+ const gate = error2.match(/check:([\w:-]+)/)?.[1];
172792
+ if (gate) {
172793
+ return {
172794
+ reasonKey: `merge-blocked:${gate}`,
172795
+ reason: `Merge is blocked by the check:${gate} verification gate.`,
172796
+ action: gate === "changeset-format" ? "Fix the changeset format, then retry the task." : "Fix the failing gate or reset the task to todo and retry.",
172797
+ gate: `check:${gate}`
172798
+ };
172799
+ }
172800
+ if (error2.startsWith("BLOCKED:")) {
172801
+ return { reasonKey: "execution-blocked", reason: "Execution was parked because an external blocker requires operator action.", action: "Resolve the dependency or blocker, then retry the task." };
172802
+ }
172803
+ if (/merge.*(?:blocked|verification|gate)|(?:verification|gate).*(?:failed|exhaust)/i.test(error2)) {
172804
+ return { reasonKey: "merge-blocked", reason: "Merge verification cannot progress without operator action.", action: "Fix the failing verification, then retry the task." };
172805
+ }
172806
+ if (!error2 && (task.mergeRetries ?? 0) < 3) return null;
172807
+ return {
172808
+ reasonKey: "terminal-failed",
172809
+ reason: "The task entered a terminal failed state and needs operator intervention.",
172810
+ action: "Inspect the task error, fix the underlying issue, then retry or reset to todo."
172811
+ };
172812
+ }
172813
+ var SELF_HEALING_NO_ACTIONS;
172814
+ var init_task_wedge_notification = __esm({
172815
+ "../engine/src/notification/task-wedge-notification.ts"() {
172816
+ "use strict";
172817
+ SELF_HEALING_NO_ACTIONS = {
172818
+ "reclaim-pr-conflict": {
172819
+ reason: "Self-healing could not reclaim a stalled pull-request conflict.",
172820
+ action: "Inspect the branch conflict and retry or reset the task to todo."
172821
+ },
172822
+ "reclaim-self-owned-branch-conflict": {
172823
+ reason: "Self-healing could not recover a stalled branch conflict.",
172824
+ action: "Inspect the branch conflict and retry or reset the task to todo."
172825
+ },
172826
+ "reconcile-in-review-unmet-dependencies": {
172827
+ reason: "Unmet dependencies are preventing review from progressing.",
172828
+ action: "Resolve the dependencies, then retry or reset the task to todo."
172829
+ },
172830
+ "reconcile-dependency-blocking-lease": {
172831
+ reason: "A dependency-blocking task lease could not be safely reclaimed.",
172832
+ action: "Inspect the task owner and lease, then retry or reset the task to todo."
172833
+ },
172834
+ "auto-rebound-paused-scope-decay": {
172835
+ reason: "A paused task with stale scope could not be safely resumed.",
172836
+ action: "Inspect the task scope and retry or reset the task to todo."
172837
+ },
172838
+ "stuck-merge-deadlock": {
172839
+ reason: "A merge deadlock needs operator intervention.",
172840
+ action: "Inspect merge ownership and retry or reset the task to todo."
172841
+ },
172842
+ "missing-worktree-merge-active": {
172843
+ reason: "An active merge has an unusable worktree and could not be recovered.",
172844
+ action: "Repair the worktree or reset the task to todo and retry."
172845
+ },
172846
+ "missing-worktree-review": {
172847
+ reason: "Review has an unusable worktree and could not be recovered.",
172848
+ action: "Repair the worktree or reset the task to todo and retry."
172849
+ },
172850
+ "finalize-no-op-review": {
172851
+ reason: "A no-op review task could not be safely finalized.",
172852
+ action: "Inspect merge state and retry or reset the task to todo."
172853
+ },
172854
+ "stale-incomplete-review": {
172855
+ reason: "Incomplete review work could not be safely resumed.",
172856
+ action: "Inspect incomplete steps and retry or reset the task to todo."
172857
+ },
172858
+ "ghost-review": {
172859
+ reason: "A review task has no recoverable workflow owner.",
172860
+ action: "Inspect workflow state and retry or reset the task to todo."
172861
+ },
172862
+ "no-progress-no-task-done": {
172863
+ reason: "Execution stopped without progress and could not be safely requeued.",
172864
+ action: "Inspect the task worktree and retry or reset the task to todo."
172865
+ },
172866
+ "partial-progress-no-task-done": {
172867
+ reason: "Partial execution progress could not be safely resumed.",
172868
+ action: "Inspect partial work and retry or reset the task to todo."
172869
+ }
172870
+ };
172871
+ }
172872
+ });
172873
+
172485
172874
  // ../engine/src/notification/notification-service.ts
172486
172875
  var NotificationService;
172487
172876
  var init_notification_service = __esm({
@@ -172493,6 +172882,7 @@ var init_notification_service = __esm({
172493
172882
  init_transient_merge_error_classifier();
172494
172883
  init_ntfy_provider();
172495
172884
  init_webhook_provider();
172885
+ init_task_wedge_notification();
172496
172886
  NotificationService = class {
172497
172887
  constructor(store, options = {}) {
172498
172888
  this.store = store;
@@ -172523,6 +172913,8 @@ var init_notification_service = __esm({
172523
172913
  failureNotificationSuppressedCount = 0;
172524
172914
  failureNotificationDelayMs = 6e4;
172525
172915
  failureNotificationMode = "sticky-only";
172916
+ /** Compatibility fallback for lightweight test stores without the durable TaskStore CAS. */
172917
+ activeWedgeReasons = /* @__PURE__ */ new Map();
172526
172918
  attachChatStore(chatStore) {
172527
172919
  if (this.chatStore && this.chatStore !== chatStore) {
172528
172920
  this.detachChatStoreListener(this.chatStore);
@@ -172634,6 +173026,10 @@ var init_notification_service = __esm({
172634
173026
  }
172635
173027
  }
172636
173028
  handleTaskUpdated = (task) => {
173029
+ const transientFailure = task.status === "failed" ? classifyTransientMergeError(task.error) : null;
173030
+ const wedge = transientFailure ? null : describeTaskWedge(task);
173031
+ if (wedge) this.cancelPendingFailureNotification(task.id, "classified-terminal-wedge");
173032
+ if (!transientFailure) void this.maybeNotifyTaskWedge(task, wedge);
172637
173033
  void this.maybeSuppressTransientFailedNotification(task, `status=${task.status ?? "undefined"}`);
172638
173034
  if (task.status === "awaiting-approval") {
172639
173035
  void this.writeAwaitingApprovalMailboxMessage(task);
@@ -172644,7 +173040,7 @@ var init_notification_service = __esm({
172644
173040
  if (!this.notificationsEnabled) {
172645
173041
  return;
172646
173042
  }
172647
- if (task.status === "failed") {
173043
+ if (task.status === "failed" && !wedge) {
172648
173044
  const transientClass = classifyTransientMergeError(task.error);
172649
173045
  if (transientClass) {
172650
173046
  this.failureNotificationSuppressedCount += 1;
@@ -172730,6 +173126,69 @@ var init_notification_service = __esm({
172730
173126
  );
172731
173127
  }
172732
173128
  }
173129
+ /*
173130
+ FNXC:TaskWedgeNotifications 2026-07-22-12:00:
173131
+ Terminal task updates are the shared seam for merger, executor, heartbeat, and
173132
+ self-healing parks. Clear only on a non-wedge lifecycle update, allowing a
173133
+ resolved-then-reparked reason to begin a new actionable episode.
173134
+ */
173135
+ /** Delivers a self-healing no-action escalation through the durable wedge episode seam. */
173136
+ async notifyTaskWedge(task, descriptor) {
173137
+ await this.maybeNotifyTaskWedge(task, descriptor);
173138
+ }
173139
+ async maybeNotifyTaskWedge(task, suppliedDescriptor) {
173140
+ const descriptor = suppliedDescriptor ?? describeTaskWedge(task);
173141
+ let episode;
173142
+ if (!descriptor) {
173143
+ const isActiveSelfHealingNoAction = task.wedgeNotification?.status === "active" && task.wedgeNotification.reasonKey.startsWith("self-healing-no-action:");
173144
+ const hasProgressed = task.column === "todo" || task.column === "in-progress" || task.column === "done" || task.column === "archived" || !isActiveSelfHealingNoAction && typeof task.status === "string" && task.status !== "failed" || isActiveSelfHealingNoAction && ["queued", "planning", "in-progress", "merging", "merging-pr", "merged", "done"].includes(task.status ?? "");
173145
+ if (hasProgressed) {
173146
+ this.activeWedgeReasons.delete(task.id);
173147
+ await this.store.claimTaskWedgeNotificationEpisode?.(task.id, null);
173148
+ }
173149
+ return;
173150
+ }
173151
+ if (this.store.claimTaskWedgeNotificationEpisode) {
173152
+ const claim = await this.store.claimTaskWedgeNotificationEpisode(task.id, descriptor.reasonKey);
173153
+ if (!claim.claimed || !claim.episodeId) return;
173154
+ episode = claim.episodeId;
173155
+ } else {
173156
+ if (this.activeWedgeReasons.get(task.id) === descriptor.reasonKey) return;
173157
+ this.activeWedgeReasons.set(task.id, descriptor.reasonKey);
173158
+ episode = `${task.id}:${descriptor.reasonKey}:${task.updatedAt}`;
173159
+ }
173160
+ const link = buildNtfyClickUrl({ dashboardHost: this.dashboardHost, projectId: this.options.projectId, taskId: task.id });
173161
+ const content = [
173162
+ `**${formatTaskIdentifier(task)} needs operator action**`,
173163
+ "",
173164
+ descriptor.reason,
173165
+ ...descriptor.gate ? [`Gate: \`${descriptor.gate}\`.`] : [],
173166
+ `Recommended action: ${descriptor.action}`,
173167
+ ...link ? ["", `[Open ${task.id}](${link})`] : []
173168
+ ].join("\n");
173169
+ const payload = {
173170
+ taskId: task.id,
173171
+ taskTitle: task.title,
173172
+ taskDescription: task.description,
173173
+ event: "task-wedged",
173174
+ // Bounded descriptor text is operator-facing provider content, never audit metadata.
173175
+ metadata: { wedgeReason: descriptor.reasonKey, reason: descriptor.reason, action: descriptor.action, ...descriptor.gate ? { gate: descriptor.gate } : {}, notificationDedupeKey: `task-wedge:${episode}` }
173176
+ };
173177
+ void this.dispatch("task-wedged", payload);
173178
+ try {
173179
+ await this.options.messageStore?.sendMessageOnce?.({
173180
+ fromId: "system",
173181
+ fromType: "system",
173182
+ toId: DASHBOARD_USER_ID,
173183
+ toType: "user",
173184
+ type: "system",
173185
+ content,
173186
+ metadata: { taskId: task.id, kind: "task-wedge", wedgeReason: descriptor.reasonKey, ...descriptor.gate ? { gate: descriptor.gate } : {} }
173187
+ }, `task-wedge:${episode}`);
173188
+ } catch (error2) {
173189
+ schedulerLog.log(`[notify] ${task.id} wedge mailbox message failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
173190
+ }
173191
+ }
172733
173192
  isTriageDuplicateDecision(task) {
172734
173193
  return task.paused === true && task.pausedReason === "duplicate-decision-required" && task.sourceMetadata?.duplicateSource === "triage-marker" && typeof task.sourceMetadata.nearDuplicateOf === "string";
172735
173194
  }
@@ -173095,6 +173554,11 @@ var init_notification_service = __esm({
173095
173554
  );
173096
173555
  return;
173097
173556
  }
173557
+ if (describeTaskWedge(task)) {
173558
+ this.failureNotificationSuppressedCount += 1;
173559
+ schedulerLog.log(`[notify] ${taskId} classified terminal wedge at dispatch time \u2014 suppressed generic failed notification`);
173560
+ return;
173561
+ }
173098
173562
  const isTerminal2 = task.paused === true || task.column === "in-review";
173099
173563
  if (this.failureNotificationMode === "terminal-only" && !isTerminal2) {
173100
173564
  this.failureNotificationSuppressedCount += 1;
@@ -173923,7 +174387,10 @@ var init_notifier = __esm({
173923
174387
  DEFAULT_NTFY_EVENTS = [
173924
174388
  "in-review",
173925
174389
  "merged",
174390
+ // FNXC:TaskWedgeNotifications 2026-07-22-19:00: Wedge episodes are operator
174391
+ // escalation events, so normal Ntfy defaults must not silently filter them.
173926
174392
  "failed",
174393
+ "task-wedged",
173927
174394
  "awaiting-approval",
173928
174395
  "awaiting-user-review",
173929
174396
  "planning-awaiting-input",
@@ -200471,6 +200938,7 @@ var init_self_healing = __esm({
200471
200938
  init_dependency_blocked_todo_reporter();
200472
200939
  init_scheduler();
200473
200940
  init_task_agent_sync();
200941
+ init_task_wedge_notification();
200474
200942
  init_self_healing_constants();
200475
200943
  init_self_healing_constants();
200476
200944
  init_self_healing_branch();
@@ -200778,6 +201246,15 @@ var init_self_healing = __esm({
200778
201246
  const message = error2 instanceof Error ? error2.message : String(error2);
200779
201247
  log28.warn(`[${stage}] ${task.id}: no-action audit emission failed: ${message}`);
200780
201248
  }
201249
+ const descriptor = describeSelfHealingNoActionWedge(task, stage, proof.metadata);
201250
+ if (descriptor) {
201251
+ try {
201252
+ await getActiveNotificationService()?.notifyTaskWedge(task, descriptor);
201253
+ } catch (error2) {
201254
+ const message = error2 instanceof Error ? error2.message : String(error2);
201255
+ log28.warn(`[${stage}] ${task.id}: wedge notification failed: ${message}`);
201256
+ }
201257
+ }
200781
201258
  log28.log(`[${stage}] ${task.id}: triple-proof not satisfied \u2014 no action (operator-decides)`);
200782
201259
  }
200783
201260
  /*
@@ -261747,6 +262224,40 @@ async function resolvePlanningModeSystemPrompt(store, promptOverrides, workflowI
261747
262224
 
261748
262225
  ${PLANNING_SYSTEM_PROMPT}`;
261749
262226
  }
262227
+ function isPlanningTurnActive(sessionId) {
262228
+ return activeGenerations.has(sessionId) || pendingTurnReservations.has(sessionId);
262229
+ }
262230
+ function reservePlanningTurn(sessionId) {
262231
+ if (isPlanningTurnActive(sessionId)) {
262232
+ throw new GenerationInProgressError("Generation already in progress");
262233
+ }
262234
+ let resolveDone;
262235
+ const done = new Promise((resolve51) => {
262236
+ resolveDone = resolve51;
262237
+ });
262238
+ const reservation = { done, resolveDone };
262239
+ pendingTurnReservations.set(sessionId, reservation);
262240
+ return () => {
262241
+ if (pendingTurnReservations.get(sessionId) === reservation) {
262242
+ pendingTurnReservations.delete(sessionId);
262243
+ }
262244
+ reservation.resolveDone();
262245
+ };
262246
+ }
262247
+ function trackTurnOperationSettled(sessionId, operationPromise) {
262248
+ const settled = operationPromise.then(
262249
+ () => {
262250
+ },
262251
+ () => {
262252
+ }
262253
+ );
262254
+ settlingTurnOperations.set(sessionId, settled);
262255
+ void settled.then(() => {
262256
+ if (settlingTurnOperations.get(sessionId) === settled) {
262257
+ settlingTurnOperations.delete(sessionId);
262258
+ }
262259
+ });
262260
+ }
261750
262261
  function isTaskPriority2(value) {
261751
262262
  return typeof value === "string" && TASK_PRIORITIES.includes(value);
261752
262263
  }
@@ -262090,7 +262601,7 @@ async function getFirstQuestionFromAgent(session, message) {
262090
262601
  if (!session.agent) {
262091
262602
  throw new InvalidSessionStateError("AI agent not initialized");
262092
262603
  }
262093
- await session.agent.session.prompt(message);
262604
+ await promptPlanningAgent(session.agent.session, message);
262094
262605
  const lastMessage = session.agent.session.state.messages.filter((m2) => m2.role === "assistant").pop();
262095
262606
  let responseText = "";
262096
262607
  if (lastMessage?.content) {
@@ -262129,7 +262640,8 @@ async function getFirstQuestionFromAgent(session, message) {
262129
262640
  lastError = err instanceof Error ? err : new Error(String(err));
262130
262641
  if (attempt < MAX_PARSE_RETRIES2) {
262131
262642
  try {
262132
- await session.agent.session.prompt(
262643
+ await promptPlanningAgent(
262644
+ session.agent.session,
262133
262645
  'Your previous response could not be parsed as JSON. Please respond with ONLY a valid JSON object: {"type":"question","data":{"runningPlan":{...},...}}. No markdown, no explanation, just the JSON.'
262134
262646
  );
262135
262647
  const retryMessage = session.agent.session.state.messages.filter((m2) => m2.role === "assistant").pop();
@@ -262189,7 +262701,8 @@ function buildMandatoryFirstPlanningQuestion(userInput = "") {
262189
262701
  }
262190
262702
  async function requestMandatoryFirstPlanningQuestion(session, abortSignal) {
262191
262703
  try {
262192
- await session.agent.session.prompt(
262704
+ await promptPlanningAgent(
262705
+ session.agent.session,
262193
262706
  'Before producing a plan, ask one clarifying question. Return ONLY valid JSON: {"type":"question","data":{...}}.',
262194
262707
  { signal: abortSignal }
262195
262708
  );
@@ -262212,6 +262725,8 @@ async function createPlanningAgent(session, rootDir, store, modelProvider, model
262212
262725
  await ensureEngineReady();
262213
262726
  const systemPrompt = await resolvePlanningModeSystemPrompt(store, promptOverrides, session.workflowId);
262214
262727
  const skillContext = buildSessionSkillContextSync(null, "executor", rootDir, pluginRunner);
262728
+ const callbackEpoch = session.agentCallbackEpoch ?? 0;
262729
+ const callbacksInvalidated = () => (session.agentCallbackEpoch ?? 0) !== callbackEpoch;
262215
262730
  return createFnAgent4({
262216
262731
  cwd: rootDir,
262217
262732
  systemPrompt,
@@ -262235,6 +262750,7 @@ async function createPlanningAgent(session, rootDir, store, modelProvider, model
262235
262750
  } : {},
262236
262751
  ...thinkingLevel ? { defaultThinkingLevel: thinkingLevel } : {},
262237
262752
  onThinking: (delta) => {
262753
+ if (callbacksInvalidated()) return;
262238
262754
  markPlanningGenerationProgress(session.id, delta);
262239
262755
  session.thinkingOutput += delta;
262240
262756
  persistThinking(session.id, session.thinkingOutput);
@@ -262244,6 +262760,7 @@ async function createPlanningAgent(session, rootDir, store, modelProvider, model
262244
262760
  });
262245
262761
  },
262246
262762
  onText: (delta) => {
262763
+ if (callbacksInvalidated()) return;
262247
262764
  markPlanningGenerationProgress(session.id, delta);
262248
262765
  session.thinkingOutput += delta;
262249
262766
  persistThinking(session.id, session.thinkingOutput);
@@ -262290,7 +262807,7 @@ async function ensureSessionAgent(session, rootDir, historyForReplay, promptOver
262290
262807
  if (abortSignal.aborted) {
262291
262808
  throw createAbortError2();
262292
262809
  }
262293
- await session.agent.session.prompt(contextMessage, {
262810
+ await promptPlanningAgent(session.agent.session, contextMessage, {
262294
262811
  signal: abortSignal
262295
262812
  });
262296
262813
  if (abortSignal.aborted) {
@@ -262328,7 +262845,7 @@ async function maybeNotifyPlanningAwaitingInput(session, question, proactiveClar
262328
262845
  }
262329
262846
  }
262330
262847
  function buildRetryableParseErrorMessage(error2) {
262331
- const baseMessage = (error2?.message || "Failed to parse AI response").replace(/\s*Please try again\.?\s*$/i, "").trim();
262848
+ const baseMessage = (error2?.message || "Failed to parse AI response").replace(/\s*Please try again\.?\s*$/i, "").replace(/[.\s]+$/, "").trim();
262332
262849
  return `${baseMessage}. Retry this planning session or start a new one.`;
262333
262850
  }
262334
262851
  function setSessionError(session, message) {
@@ -262345,6 +262862,19 @@ function createAbortError2() {
262345
262862
  error2.name = "AbortError";
262346
262863
  return error2;
262347
262864
  }
262865
+ async function promptPlanningAgent(agentSession, message, options) {
262866
+ let promptWithFallback2;
262867
+ try {
262868
+ promptWithFallback2 = promptWithFallback;
262869
+ } catch {
262870
+ promptWithFallback2 = void 0;
262871
+ }
262872
+ if (typeof promptWithFallback2 === "function") {
262873
+ await promptWithFallback2(agentSession, message, options);
262874
+ return;
262875
+ }
262876
+ await agentSession.prompt(message, options);
262877
+ }
262348
262878
  function normalizeGenerationProgress(output) {
262349
262879
  return output.replace(/\s+/g, " ").trim();
262350
262880
  }
@@ -262420,7 +262950,9 @@ async function runGenerationWithTimeout(session, operation) {
262420
262950
  );
262421
262951
  });
262422
262952
  try {
262423
- return await Promise.race([operation(abortController.signal), abortPromise]);
262953
+ const operationPromise = operation(abortController.signal);
262954
+ trackTurnOperationSettled(session.id, operationPromise);
262955
+ return await Promise.race([operationPromise, abortPromise]);
262424
262956
  } finally {
262425
262957
  clearTimeout(generationRecord.timer);
262426
262958
  if (activeGenerations.get(session.id) === generationRecord) {
@@ -262572,7 +263104,7 @@ async function continueAgentConversation(session, message) {
262572
263104
  if (abortSignal.aborted) {
262573
263105
  throw createAbortError2();
262574
263106
  }
262575
- await session.agent.session.prompt(message, {
263107
+ await promptPlanningAgent(session.agent.session, message, {
262576
263108
  signal: abortSignal
262577
263109
  });
262578
263110
  if (abortSignal.aborted) {
@@ -262622,7 +263154,8 @@ async function continueAgentConversation(session, message) {
262622
263154
  if (abortSignal.aborted) {
262623
263155
  throw createAbortError2();
262624
263156
  }
262625
- await session.agent.session.prompt(
263157
+ await promptPlanningAgent(
263158
+ session.agent.session,
262626
263159
  'Your previous response could not be parsed as JSON. Please respond with ONLY a valid JSON object: {"type":"question","data":{"runningPlan":{...},...}}. No markdown, no explanation, just the JSON.',
262627
263160
  { signal: abortSignal }
262628
263161
  );
@@ -262898,57 +263431,62 @@ async function submitResponse(sessionId, responses, rootDir, promptOverrides, st
262898
263431
  }
262899
263432
  if (store && !session.store) session.store = store;
262900
263433
  if (rootDir && !session.rootDir) session.rootDir = rootDir;
262901
- if (activeGenerations.has(session.id)) {
263434
+ if (isPlanningTurnActive(session.id)) {
262902
263435
  if (didSubmitSameAnswer(session, responses)) {
262903
263436
  throw new GenerationInProgressError("Generation already in progress for this response");
262904
263437
  }
262905
263438
  throw new GenerationInProgressError("Generation already in progress");
262906
263439
  }
263440
+ const releaseTurn = reservePlanningTurn(session.id);
262907
263441
  let answeredQuestion;
262908
- if (isRefineRequest(responses) && session.summary) {
262909
- beginPlanningGeneration(session, "question");
262910
- session.currentQuestion = void 0;
262911
- session.error = void 0;
262912
- await persistSession(session, "generating");
262913
- await ensureSessionAgent(session, rootDir, session.history, promptOverrides, store);
262914
- const focus = typeof responses.focus === "string" ? responses.focus.trim() : void 0;
262915
- const refineMessage = formatRefineRequestForAgent(session.summary, focus);
262916
- await continueAgentConversation(session, refineMessage);
262917
- } else if (!session.currentQuestion) {
262918
- throw new InvalidSessionStateError("No active question in session");
262919
- } else {
262920
- const currentQuestion = captureOtherCustomText(session.currentQuestion, responses);
262921
- const historyEntry = {
262922
- question: currentQuestion,
262923
- response: responses,
262924
- thinkingOutput: session.lastGeneratedThinking || ""
262925
- };
262926
- session.error = void 0;
262927
- const editIndex = session.editingQuestionId ? session.history.findIndex((entry) => entry.question.id === session.editingQuestionId) : -1;
262928
- const isEditingPriorAnswer = editIndex >= 0;
262929
- if (isEditingPriorAnswer) {
262930
- session.history[editIndex] = historyEntry;
262931
- session.editingQuestionId = void 0;
262932
- session.summary = buildRunningSummary(session.initialPlan, session.history);
262933
- disposeSessionAgentForRetry(session);
263442
+ try {
263443
+ if (isRefineRequest(responses) && session.summary) {
263444
+ beginPlanningGeneration(session, "question");
263445
+ session.currentQuestion = void 0;
263446
+ session.error = void 0;
263447
+ await persistSession(session, "generating");
263448
+ await ensureSessionAgent(session, rootDir, session.history, promptOverrides, store);
263449
+ const focus = typeof responses.focus === "string" ? responses.focus.trim() : void 0;
263450
+ const refineMessage = formatRefineRequestForAgent(session.summary, focus);
263451
+ await continueAgentConversation(session, refineMessage);
263452
+ } else if (!session.currentQuestion) {
263453
+ throw new InvalidSessionStateError("No active question in session");
262934
263454
  } else {
262935
- session.history.push(historyEntry);
262936
- }
262937
- answeredQuestion = currentQuestion;
262938
- beginPlanningGeneration(session, "plan_update");
262939
- session.currentQuestion = void 0;
262940
- await persistSession(session, "generating");
262941
- if (!session.agent) {
262942
- await ensureSessionAgent(
262943
- session,
262944
- rootDir,
262945
- isEditingPriorAnswer ? session.history : session.history.slice(0, -1),
262946
- promptOverrides,
262947
- store
262948
- );
263455
+ const currentQuestion = captureOtherCustomText(session.currentQuestion, responses);
263456
+ const historyEntry = {
263457
+ question: currentQuestion,
263458
+ response: responses,
263459
+ thinkingOutput: session.lastGeneratedThinking || ""
263460
+ };
263461
+ session.error = void 0;
263462
+ const editIndex = session.editingQuestionId ? session.history.findIndex((entry) => entry.question.id === session.editingQuestionId) : -1;
263463
+ const isEditingPriorAnswer = editIndex >= 0;
263464
+ if (isEditingPriorAnswer) {
263465
+ session.history[editIndex] = historyEntry;
263466
+ session.editingQuestionId = void 0;
263467
+ session.summary = buildRunningSummary(session.initialPlan, session.history);
263468
+ disposeSessionAgentForRetry(session);
263469
+ } else {
263470
+ session.history.push(historyEntry);
263471
+ }
263472
+ answeredQuestion = currentQuestion;
263473
+ beginPlanningGeneration(session, "plan_update");
263474
+ session.currentQuestion = void 0;
263475
+ await persistSession(session, "generating");
263476
+ if (!session.agent) {
263477
+ await ensureSessionAgent(
263478
+ session,
263479
+ rootDir,
263480
+ isEditingPriorAnswer ? session.history : session.history.slice(0, -1),
263481
+ promptOverrides,
263482
+ store
263483
+ );
263484
+ }
263485
+ const message = isEditingPriorAnswer ? "An earlier answer was edited. Use the complete preserved interview context above, regenerate the running plan, and ask exactly one next question." : formatResponseForAgent(currentQuestion, responses);
263486
+ await continueAgentConversation(session, message);
262949
263487
  }
262950
- const message = isEditingPriorAnswer ? "An earlier answer was edited. Use the complete preserved interview context above, regenerate the running plan, and ask exactly one next question." : formatResponseForAgent(currentQuestion, responses);
262951
- await continueAgentConversation(session, message);
263488
+ } finally {
263489
+ releaseTurn();
262952
263490
  }
262953
263491
  if (session.currentQuestion) {
262954
263492
  return { type: "question", data: session.currentQuestion };
@@ -263029,6 +263567,7 @@ Additional context: ${comment}` : formatted;
263029
263567
  Regenerate the runningPlan fields (title, description, concrete proposedChanges, observable acceptanceCriteria, suggestedSize, optional priority, suggestedDependencies, concrete keyDeliverables, and all distinct high-value suggestedRefinements) informed by this answer; do not cap suggestedRefinements at three. Author the operator-facing plan in Markdown: use concise GitHub-flavored Markdown in the description, with the structured fields supplying its Markdown sections and lists. Never list interview questions as deliverables or PROMPT.md sections such as Mission, Steps, File Scope, Review Level, Completion Criteria, or Do NOT. Return type:"question" with that complete runningPlan and ask exactly one next question. Do not validate the plan; only the user can proceed with it.`;
263030
263568
  }
263031
263569
  function disposeSessionAgentForRetry(session) {
263570
+ session.agentCallbackEpoch = (session.agentCallbackEpoch ?? 0) + 1;
263032
263571
  if (!session.agent) {
263033
263572
  return;
263034
263573
  }
@@ -263111,7 +263650,7 @@ async function getSession(sessionId) {
263111
263650
  return void 0;
263112
263651
  }
263113
263652
  }
263114
- var PLANNING_NO_AMBIENT_TASK_ID, PLANNING_BUILTIN_WEB_TOOLS, createFnAgent4, planningNtfyHelpers, diagnostics2, PLANNING_SYSTEM_PROMPT, SESSION_TTL_MS, CLEANUP_INTERVAL_MS2, MAX_SESSIONS_PER_IP_PER_HOUR, RATE_LIMIT_WINDOW_MS2, GENERATION_TIMEOUT_MS, GENERATION_LOOP_REPEAT_LIMIT, PLANNING_STUCK_ERROR_MESSAGE, PLANNING_LOOP_ERROR_MESSAGE, sessions, rateLimits2, activeGenerations, _aiSessionStore, sessionPersistenceQueues, cleanupInterval2, PlanningStreamManager, planningStreamManager, MAX_PARSE_RETRIES2, TruncatedPlanningCompletionError, RateLimitError3, SessionNotFoundError, InvalidSessionStateError, GenerationInProgressError;
263653
+ var PLANNING_NO_AMBIENT_TASK_ID, PLANNING_BUILTIN_WEB_TOOLS, createFnAgent4, planningNtfyHelpers, diagnostics2, PLANNING_SYSTEM_PROMPT, SESSION_TTL_MS, CLEANUP_INTERVAL_MS2, MAX_SESSIONS_PER_IP_PER_HOUR, RATE_LIMIT_WINDOW_MS2, GENERATION_TIMEOUT_MS, GENERATION_LOOP_REPEAT_LIMIT, PLANNING_STUCK_ERROR_MESSAGE, PLANNING_LOOP_ERROR_MESSAGE, sessions, rateLimits2, activeGenerations, pendingTurnReservations, settlingTurnOperations, _aiSessionStore, sessionPersistenceQueues, cleanupInterval2, PlanningStreamManager, planningStreamManager, MAX_PARSE_RETRIES2, TruncatedPlanningCompletionError, RateLimitError3, SessionNotFoundError, InvalidSessionStateError, GenerationInProgressError;
263115
263654
  var init_planning = __esm({
263116
263655
  "../dashboard/src/planning.ts"() {
263117
263656
  "use strict";
@@ -263146,6 +263685,8 @@ Every turn must include the running-plan fields: only title, description, concre
263146
263685
  sessions = /* @__PURE__ */ new Map();
263147
263686
  rateLimits2 = /* @__PURE__ */ new Map();
263148
263687
  activeGenerations = /* @__PURE__ */ new Map();
263688
+ pendingTurnReservations = /* @__PURE__ */ new Map();
263689
+ settlingTurnOperations = /* @__PURE__ */ new Map();
263149
263690
  sessionPersistenceQueues = /* @__PURE__ */ new Map();
263150
263691
  cleanupInterval2 = setInterval(cleanupExpiredSessions, CLEANUP_INTERVAL_MS2);
263151
263692
  cleanupInterval2.unref?.();
@@ -263219,6 +263760,10 @@ Every turn must include the running-plan fields: only title, description, concre
263219
263760
  }
263220
263761
  this.pendingInitialTurns.set(sessionId, start);
263221
263762
  }
263763
+ /** True while a registered initial turn has not been consumed by a stream connection yet. */
263764
+ hasPendingInitialTurn(sessionId) {
263765
+ return this.pendingInitialTurns.has(sessionId);
263766
+ }
263222
263767
  consumeInitialTurn(sessionId) {
263223
263768
  const start = this.pendingInitialTurns.get(sessionId);
263224
263769
  if (!start) {
@@ -263258,7 +263803,7 @@ Every turn must include the running-plan fields: only title, description, concre
263258
263803
  this.removeAllListeners();
263259
263804
  }
263260
263805
  };
263261
- planningStreamManager = new PlanningStreamManager();
263806
+ planningStreamManager = new PlanningStreamManager(2e3);
263262
263807
  MAX_PARSE_RETRIES2 = 1;
263263
263808
  TruncatedPlanningCompletionError = class extends Error {
263264
263809
  constructor() {
@@ -270646,7 +271191,7 @@ var init_chat = __esm({
270646
271191
  };
270647
271192
  CHAT_SYSTEM_PROMPT = `${FUSION_RUNTIME_SELF_AWARENESS}
270648
271193
 
270649
- You are a helpful AI assistant integrated into the fn task board system. You help users with questions about their project, code, architecture, and tasks. You have coding workspace tools on the project checkout: \`read\`, \`write\`, \`edit\`, \`bash\`, \`grep\`, \`find\`, and \`ls\`. Use \`write\`, \`edit\`, and \`bash\` for user-requested code changes, file edits, or shell investigation; prefer minimal, user-directed mutations and respect any pending-approval or blocked tool result. Do not claim that you only have read access. Do not change the branch the working directory is checked out on: do not run \`git checkout <branch>\` or \`git switch <branch>\` to a different branch unless the user explicitly asks. Read-only Git and branch inspection, such as \`git status\`, \`git branch\`, and \`git log\`, is allowed. Response length policy: default to a short, crisp reply (a few sentences or a short bulleted list) that directly answers the user; avoid preamble, restating the question, and filler. If a thorough answer genuinely needs long-form content (for example multi-step plans, design proposals, deep analyses, or long file excerpts), keep the chat reply brief with a one- or two-sentence summary and then send the full write-up via \`fn_send_message\` using \`type: "agent-to-user"\` and \`to_id: "dashboard"\`. That mailbox follow-up must add new substantive detail and must not duplicate the chat reply.`;
271194
+ You are a helpful AI assistant integrated into the fn task board system. You help users with questions about their project, code, architecture, and tasks. You have coding workspace tools on the project checkout: \`read\`, \`write\`, \`edit\`, \`bash\`, \`grep\`, \`find\`, and \`ls\`. Use \`write\`, \`edit\`, and \`bash\` for user-requested code changes, file edits, or shell investigation; prefer minimal, user-directed mutations and respect any pending-approval or blocked tool result. Do not claim that you only have read access. Do not change the branch the working directory is checked out on: do not run \`git checkout <branch>\` or \`git switch <branch>\` to a different branch unless the user explicitly asks. Read-only Git and branch inspection, such as \`git status\`, \`git branch\`, and \`git log\`, is allowed. Response length policy: default to a short, crisp reply (a few sentences or a short bulleted list) that directly answers the user; avoid preamble, restating the question, and filler. For questions about this repository's code or architecture, prioritize correctness and cite real paths/symbols over maximum brevity \u2014 still lead short, but do not omit the evidence needed to be accurate. If a thorough answer genuinely needs long-form content (for example multi-step plans, design proposals, deep multi-file traces, deep analyses, or long file excerpts), keep the chat reply brief with a one- or two-sentence summary plus key citations. When \`fn_send_message\` is available in this session, send the full write-up via \`fn_send_message\` using \`type: "agent-to-user"\` and \`to_id: "dashboard"\` (additive detail that must not duplicate the chat reply). When that tool is not available, put the necessary detail in the chat reply itself rather than inventing a mailbox path.`;
270650
271195
  RATE_LIMIT_WINDOW_MS5 = 60 * 1e3;
270651
271196
  MAX_REFERENCED_FILE_SIZE = 50 * 1024;
270652
271197
  ChatStreamManager = class extends EventEmitter39 {