@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.
- package/dist/bin.js +994 -292
- package/dist/child-process-worker.js +632 -153
- package/dist/client/.vite/manifest.json +256 -256
- package/dist/client/assets/{AgentDetailView-lJqxsPOR.js → AgentDetailView-BBOL6AgZ.js} +1 -1
- package/dist/client/assets/{AgentPermissionPolicyEditor-De4wK_UT.js → AgentPermissionPolicyEditor-DV7OPUhR.js} +1 -1
- package/dist/client/assets/{AgentsView-CwizRdcA.js → AgentsView-B6xoAHyV.js} +3 -3
- package/dist/client/assets/{ChatView-BeQXaz7_.js → ChatView-DwVjnxM8.js} +1 -1
- package/dist/client/assets/{CommandCenter-BJZCaPLj.js → CommandCenter-DYWaoYFD.js} +1 -1
- package/dist/client/assets/{DevServerView-DQgwQXKx.js → DevServerView-BY5up-NA.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-B2UVwp6F.js → DirectoryPicker-fM8MJa2r.js} +1 -1
- package/dist/client/assets/{DocumentsView-DOeYrhvg.js → DocumentsView-D2KxsPG_.js} +1 -1
- package/dist/client/assets/{EvalsView-LwepJ9Ud.js → EvalsView-U8dOvTRM.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-DVNSzflw.js → ExperimentalAgentOnboardingModal-C27y8Y-1.js} +1 -1
- package/dist/client/assets/{GoalsView-__LpXw4S.js → GoalsView-D-2wmy-O.js} +1 -1
- package/dist/client/assets/{InsightsView-Dv388uaO.js → InsightsView-zLyuQL_l.js} +1 -1
- package/dist/client/assets/{MemoryView-ChCbEfZT.js → MemoryView-D-tSn48u.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-DehAjrqN.js → PiExtensionsManager-DvwmvGEY.js} +1 -1
- package/dist/client/assets/PluginManager-BACOwQAN.js +1 -0
- package/dist/client/assets/{PullRequestView-BvmajsBq.js → PullRequestView-DULyv21u.js} +1 -1
- package/dist/client/assets/{ReportModal-DM9B_h_w.js → ReportModal-CUlKMFWa.js} +1 -1
- package/dist/client/assets/{ResearchView-BzXSgUhp.js → ResearchView-DM_O3IFc.js} +1 -1
- package/dist/client/assets/{SecretsView-COBqEdJj.js → SecretsView-Bq3u_nAf.js} +1 -1
- package/dist/client/assets/{SessionTerminal-BUw_FTlh.js → SessionTerminal-D00ByR6U.js} +1 -1
- package/dist/client/assets/SettingsModal-Bb3pIxiX.js +21 -0
- package/dist/client/assets/SettingsModal-CMLHZBhX.css +1 -0
- package/dist/client/assets/{SettingsModal-CsLaIrqt.js → SettingsModal-QRaBE1ds.js} +1 -1
- package/dist/client/assets/{SettingsTextareaRow-Bv3aZ9aU.js → SettingsTextareaRow-CHOJ-qHz.js} +1 -1
- package/dist/client/assets/{SetupWizardModal-C-yMHTlW.js → SetupWizardModal-DriQyd81.js} +2 -2
- package/dist/client/assets/{SkillsView-kSHgkBar.js → SkillsView-BtDyujiZ.js} +1 -1
- package/dist/client/assets/{TodoView-DYoicJNY.js → TodoView-BNHUnz50.js} +1 -1
- package/dist/client/assets/{WorkflowNodeEditor-BtWrziOX.css → WorkflowNodeEditor-BNgkFJ_P.css} +1 -1
- package/dist/client/assets/WorkflowNodeEditor-BXikFpra.js +8 -0
- package/dist/client/assets/{agent-import-generation-D6P8OUuV.js → agent-import-generation-B2kYEm1O.js} +1 -1
- package/dist/client/assets/{app-DegRNirF.js → app-B_HrdDXZ.js} +1 -1
- package/dist/client/assets/{app-BsIXfnu-.js → app-C6yo-M_n.js} +1 -1
- package/dist/client/assets/{app-B-IdUeIu.js → app-CH8ZgPm4.js} +1 -1
- package/dist/client/assets/{app-D9ktpVhR.js → app-D4DpgDss.js} +1 -1
- package/dist/client/assets/{app-nBTNvNKK.js → app-Qv0blCyY.js} +1 -1
- package/dist/client/assets/{app-C8muVNUU.js → app-kFdtajPy.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-DLqKCckA.js → architectureDiagram-3BPJPVTR-B-Efjj4Z.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-DRz6F8wZ.js → blockDiagram-GPEHLZMM-CaOVxrlM.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-CJ9glQAp.js → c4Diagram-AAUBKEIU-D8aYt5F1.js} +1 -1
- package/dist/client/assets/channel-5bPK6pTS.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-CjIBb3ln.js → chunk-2J33WTMH-VSDT0J0r.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-DrcoEr1E.js → chunk-4BX2VUAB-Chx1wQgD.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-DU-WcuA9.js → chunk-55IACEB6-MlqjhIJg.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-C5Lklr3l.js → chunk-727SXJPM-BheQNUi8.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-DlyTTghY.js → chunk-AQP2D5EJ-C5EoJhfJ.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-D3C65m5Y.js → chunk-FMBD7UC4-B8_8qP3j.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-BZY-4WZF.js → chunk-ND2GUHAM-BuglCGRx.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-CKUtTYy5.js → chunk-QZHKN3VN-B7_06dxp.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-Dv9RQDqG.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Dv9RQDqG.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-DbpsF4N5.js → cose-bilkent-S5V4N54A-Cm-ZOycx.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-BwIo4me1.js → dagre-BM42HDAG-Dj_Gwjpv.js} +1 -1
- package/dist/client/assets/{dashboard-view-C94UCci5.js → dashboard-view-B4CRL5Fy.js} +1 -1
- package/dist/client/assets/{dashboard-view-BvAsW6UZ.js → dashboard-view-noD9p0Zs.js} +1 -1
- package/dist/client/assets/{dashboard-view-BMQm7Lt_.js → dashboard-view-pXXSUxG9.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-C-d8XY1c.js → diagram-2AECGRRQ-C_9BfShy.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-ByRrlL9w.js → diagram-5GNKFQAL-Cmg2qpCj.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-BK7jb_J-.js → diagram-KO2AKTUF-2FNo2HXb.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-u2o4bYk6.js → diagram-LMA3HP47-DgnVeCp-.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-Dakk1V3D.js → diagram-OG6HWLK6-iAIR50HH.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-Yp33v-Cf.js → erDiagram-TEJ5UH35-Da4I04eN.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-C4-dR2TA.js → flowDiagram-I6XJVG4X-Bv9r2T0m.js} +1 -1
- package/dist/client/assets/{folder-open-C9qq38qV.js → folder-open-CwWtrDh6.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-C0CTCkFN.js → ganttDiagram-6RSMTGT7-BMeO84U_.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-DK0Dd5mm.js → gitGraphDiagram-PVQCEYII-CWDh_RIb.js} +1 -1
- package/dist/client/assets/index-CB3mYxAB.css +1 -0
- package/dist/client/assets/{index-BqLVwYoA.js → index-CE7C_XsS.js} +294 -294
- package/dist/client/assets/{infoDiagram-5YYISTIA-u5MwFb7j.js → infoDiagram-5YYISTIA-BAE4KtCL.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-C7R6TziI.js → ishikawaDiagram-YF4QCWOH-C_iXAuOy.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-Dom8W5Ab.js → journeyDiagram-JHISSGLW-BnxSHwDo.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-_47C02t3.js → kanban-definition-UN3LZRKU-DYNRm3Nu.js} +1 -1
- package/dist/client/assets/{mermaid.core-Cd3LKosp.js → mermaid.core-B3hvDDep.js} +4 -4
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-D-qVOEin.js → mindmap-definition-RKZ34NQL-s7KBEuPD.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-B0TXvVbM.js → pieDiagram-4H26LBE5-Cy1_IPUD.js} +1 -1
- package/dist/client/assets/{puzzle-BBYTjDQK.js → puzzle-DWc6gFQ7.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-KcFvcZPm.js → quadrantDiagram-W4KKPZXB-DqgVGp41.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-DEObokVc.js → requirementDiagram-4Y6WPE33-CA5-TDeF.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-BGYnEp22.js → sankeyDiagram-5OEKKPKP-Cuvi3RgE.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BcYgyJRU.js → sequenceDiagram-3UESZ5HK-Da3GfmGP.js} +1 -1
- package/dist/client/assets/{shield-alert-CZg8yaFA.js → shield-alert-_iY63ED4.js} +1 -1
- package/dist/client/assets/{standing-instructions-template-BsTtbEqp.js → standing-instructions-template-CCd2YY9c.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-CfZQIqmX.js → stateDiagram-AJRCARHV-CZ_I9ENR.js} +1 -1
- package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-nBEPRSH7.js → stateDiagram-v2-BHNVJYJU-BomoRVhY.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-DySyabS_.js → timeline-definition-PNZ67QCA-C3CYvIuR.js} +1 -1
- package/dist/client/assets/{upload-DMd3zMLx.js → upload-D0RrO65v.js} +1 -1
- package/dist/client/assets/{users-9Pyvnn8K.js → users-CGszBY2v.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-DPomfxp_.js → vennDiagram-CIIHVFJN-By9fi8NW.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-BPfzngS8.js → wardley-L42UT6IY-DErnXPkI.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-Yp4nidAP.js → wardleyDiagram-YWT4CUSO-DFfXZVPk.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-REgng603.js → xychartDiagram-2RQKCTM6-9Y5oZ5mi.js} +1 -1
- package/dist/client/index.html +4 -2
- package/dist/client/version.json +1 -1
- package/dist/extension.js +752 -207
- package/dist/migrations/0000_initial.sql +2 -0
- package/dist/migrations/0033_fn-8505_wedge_notification.sql +5 -0
- package/dist/plugin-sdk/index.js +1 -0
- package/dist/plugins/.fusion-ce-agents/.fusion-ce-upstream-provenance.json +7 -0
- package/dist/plugins/.fusion-ce-agents/ce-adversarial-document-reviewer.md +115 -0
- package/dist/plugins/.fusion-ce-agents/ce-adversarial-reviewer.md +111 -0
- package/dist/plugins/.fusion-ce-agents/ce-agent-native-planning-strategist.md +71 -0
- package/dist/plugins/.fusion-ce-agents/ce-agent-native-reviewer.md +181 -0
- package/dist/plugins/.fusion-ce-agents/ce-ankane-readme-writer.md +50 -0
- package/dist/plugins/.fusion-ce-agents/ce-api-contract-reviewer.md +52 -0
- package/dist/plugins/.fusion-ce-agents/ce-architecture-strategist.md +53 -0
- package/dist/plugins/.fusion-ce-agents/ce-best-practices-researcher.md +122 -0
- package/dist/plugins/.fusion-ce-agents/ce-code-simplicity-reviewer.md +87 -0
- package/dist/plugins/.fusion-ce-agents/ce-coherence-reviewer.md +73 -0
- package/dist/plugins/.fusion-ce-agents/ce-correctness-reviewer.md +52 -0
- package/dist/plugins/.fusion-ce-agents/ce-data-integrity-guardian.md +75 -0
- package/dist/plugins/.fusion-ce-agents/ce-data-migration-reviewer.md +119 -0
- package/dist/plugins/.fusion-ce-agents/ce-deployment-verification-agent.md +164 -0
- package/dist/plugins/.fusion-ce-agents/ce-design-implementation-reviewer.md +94 -0
- package/dist/plugins/.fusion-ce-agents/ce-design-iterator.md +197 -0
- package/dist/plugins/.fusion-ce-agents/ce-design-lens-reviewer.md +56 -0
- package/dist/plugins/.fusion-ce-agents/ce-feasibility-reviewer.md +65 -0
- package/dist/plugins/.fusion-ce-agents/ce-figma-design-sync.md +172 -0
- package/dist/plugins/.fusion-ce-agents/ce-framework-docs-researcher.md +100 -0
- package/dist/plugins/.fusion-ce-agents/ce-git-history-analyzer.md +47 -0
- package/dist/plugins/.fusion-ce-agents/ce-issue-intelligence-analyst.md +207 -0
- package/dist/plugins/.fusion-ce-agents/ce-julik-frontend-races-reviewer.md +52 -0
- package/dist/plugins/.fusion-ce-agents/ce-learnings-researcher.md +254 -0
- package/dist/plugins/.fusion-ce-agents/ce-maintainability-reviewer.md +77 -0
- package/dist/plugins/.fusion-ce-agents/ce-pattern-recognition-specialist.md +62 -0
- package/dist/plugins/.fusion-ce-agents/ce-performance-oracle.md +115 -0
- package/dist/plugins/.fusion-ce-agents/ce-performance-reviewer.md +54 -0
- package/dist/plugins/.fusion-ce-agents/ce-pr-comment-resolver.md +63 -0
- package/dist/plugins/.fusion-ce-agents/ce-previous-comments-reviewer.md +68 -0
- package/dist/plugins/.fusion-ce-agents/ce-product-lens-reviewer.md +92 -0
- package/dist/plugins/.fusion-ce-agents/ce-project-standards-reviewer.md +84 -0
- package/dist/plugins/.fusion-ce-agents/ce-reliability-reviewer.md +52 -0
- package/dist/plugins/.fusion-ce-agents/ce-repo-research-analyst.md +263 -0
- package/dist/plugins/.fusion-ce-agents/ce-scope-guardian-reviewer.md +79 -0
- package/dist/plugins/.fusion-ce-agents/ce-security-lens-reviewer.md +48 -0
- package/dist/plugins/.fusion-ce-agents/ce-security-reviewer.md +54 -0
- package/dist/plugins/.fusion-ce-agents/ce-security-sentinel.md +98 -0
- package/dist/plugins/.fusion-ce-agents/ce-session-historian.md +89 -0
- package/dist/plugins/.fusion-ce-agents/ce-slack-researcher.md +133 -0
- package/dist/plugins/.fusion-ce-agents/ce-spec-flow-analyzer.md +87 -0
- package/dist/plugins/.fusion-ce-agents/ce-swift-ios-reviewer.md +107 -0
- package/dist/plugins/.fusion-ce-agents/ce-testing-reviewer.md +52 -0
- package/dist/plugins/.fusion-ce-agents/ce-web-researcher.md +127 -0
- package/dist/plugins/.fusion-ce-skills/.fusion-ce-upstream-provenance.json +7 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/SKILL.md +318 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/agents/slack-researcher.md +127 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/brainstorm-sections.md +354 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/handoff.md +172 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/html-rendering.md +662 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/markdown-rendering.md +236 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/synthesis-summary.md +271 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/universal-brainstorming.md +71 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/visual-probes.md +128 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/scripts/visual-probe-server.js +419 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/SKILL.md +821 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/action-class-rubric.md +26 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/bulk-preview.md +112 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/cross-model-review.md +63 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/diff-scope.md +41 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/findings-schema.json +137 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/persona-catalog.md +63 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/adversarial-reviewer.md +102 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/agent-native-reviewer.md +173 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/api-contract-reviewer.md +43 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/correctness-reviewer.md +43 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/data-migration-reviewer.md +111 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/deployment-verification-agent.md +157 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/julik-frontend-races-reviewer.md +44 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/learnings-researcher.md +247 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/maintainability-reviewer.md +68 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/performance-reviewer.md +45 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/previous-comments-reviewer.md +59 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/project-standards-reviewer.md +75 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/reliability-reviewer.md +43 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/security-reviewer.md +45 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/swift-ios-reviewer.md +99 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/testing-reviewer.md +43 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/review-output-template.md +170 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/subagent-template.md +199 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/tracker-defer.md +149 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/validator-template.md +89 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/walkthrough.md +249 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/scripts/cross-model-adversarial-review.sh +218 -0
- package/dist/plugins/.fusion-ce-skills/ce-commit/SKILL.md +105 -0
- package/dist/plugins/.fusion-ce-skills/ce-commit-push-pr/SKILL.md +134 -0
- package/dist/plugins/.fusion-ce-skills/ce-commit-push-pr/references/branch-creation.md +55 -0
- package/dist/plugins/.fusion-ce-skills/ce-commit-push-pr/references/pr-description-writing.md +115 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/SKILL.md +712 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/assets/resolution-template.md +94 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/best-practices-researcher.md +115 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/data-integrity-guardian.md +68 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/framework-docs-researcher.md +93 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/pattern-recognition-specialist.md +55 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/performance-oracle.md +108 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/security-sentinel.md +91 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/session-historian.md +83 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/concepts-vocabulary.md +78 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/schema.yaml +231 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/yaml-schema.md +118 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/scripts/session-history/discover-sessions.sh +130 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/scripts/session-history/extract-errors.py +254 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/scripts/session-history/extract-metadata.py +456 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/scripts/session-history/extract-skeleton.py +570 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/scripts/validate-frontmatter.py +137 -0
- package/dist/plugins/.fusion-ce-skills/ce-debug/SKILL.md +257 -0
- package/dist/plugins/.fusion-ce-skills/ce-debug/references/anti-patterns.md +91 -0
- package/dist/plugins/.fusion-ce-skills/ce-debug/references/defense-in-depth.md +35 -0
- package/dist/plugins/.fusion-ce-skills/ce-debug/references/investigation-techniques.md +374 -0
- package/dist/plugins/.fusion-ce-skills/ce-doc-review/SKILL.md +70 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/SKILL.md +401 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/agents/issue-intelligence-analyst.md +200 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/agents/learnings-researcher.md +247 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/agents/slack-researcher.md +127 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/agents/web-researcher.md +121 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/divergent-ideation.md +89 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/html-rendering.md +662 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/ideation-sections.md +191 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/markdown-rendering.md +236 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/post-ideation-workflow.md +166 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/universal-ideation.md +107 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/web-research-cache.md +55 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/SKILL.md +858 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/agent-native-planning-strategist.md +62 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/architecture-strategist.md +46 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/best-practices-researcher.md +114 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/data-integrity-guardian.md +68 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/data-migration-reviewer.md +103 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/deployment-verification-agent.md +157 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/framework-docs-researcher.md +93 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/git-history-analyzer.md +40 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/learnings-researcher.md +247 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/pattern-recognition-specialist.md +55 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/performance-oracle.md +108 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/repo-research-analyst.md +256 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/security-sentinel.md +91 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/slack-researcher.md +127 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/spec-flow-analyzer.md +80 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/web-researcher.md +121 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/approach-altitude.md +55 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/deepening-workflow.md +259 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/html-rendering.md +668 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/markdown-rendering.md +236 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/plan-handoff.md +126 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/plan-sections.md +405 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/synthesis-summary.md +396 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/universal-planning.md +168 -0
- package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/SKILL.md +53 -0
- package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/references/agents/pr-comment-resolver.md +56 -0
- package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/references/evaluation-rubric.md +106 -0
- package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/references/full-mode.md +283 -0
- package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/references/targeted-mode.md +45 -0
- package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/scripts/get-pr-comments +159 -0
- package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/scripts/get-thread-for-comment +76 -0
- package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/scripts/reply-to-pr-thread +33 -0
- package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/scripts/resolve-pr-thread +23 -0
- package/dist/plugins/.fusion-ce-skills/ce-strategy/SKILL.md +97 -0
- package/dist/plugins/.fusion-ce-skills/ce-strategy/references/interview.md +143 -0
- package/dist/plugins/.fusion-ce-skills/ce-strategy/references/strategy-template.md +89 -0
- package/dist/plugins/.fusion-ce-skills/ce-work/SKILL.md +429 -0
- package/dist/plugins/.fusion-ce-skills/ce-work/references/agents/figma-design-sync.md +165 -0
- package/dist/plugins/.fusion-ce-skills/ce-work/references/execution-engines.md +85 -0
- package/dist/plugins/.fusion-ce-skills/ce-work/references/non-code-execution.md +23 -0
- package/dist/plugins/.fusion-ce-skills/ce-work/references/review-findings-followup.md +104 -0
- package/dist/plugins/.fusion-ce-skills/ce-work/references/shipping-workflow.md +133 -0
- package/dist/plugins/.fusion-ce-skills/ce-work/references/tracker-defer.md +149 -0
- package/dist/plugins/fusion-plugin-compound-engineering/.bundled.reload-3.js +11266 -0
- package/dist/plugins/fusion-plugin-compound-engineering/.bundled.reload-4.js +11266 -0
- package/dist/plugins/fusion-plugin-dependency-graph/.bundled.reload-1.js +8206 -0
- package/dist/plugins/fusion-plugin-grok-runtime/.bundled.reload-2.js +26623 -0
- package/package.json +1 -1
- package/dist/client/assets/PluginManager-DBhJ1i27.js +0 -1
- package/dist/client/assets/SettingsModal-BDgH9XID.js +0 -21
- package/dist/client/assets/SettingsModal-BXDnzDkJ.css +0 -1
- package/dist/client/assets/WorkflowNodeEditor-DLno6bSJ.js +0 -8
- package/dist/client/assets/channel-Dj4nOgJO.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-DnkFo5Qy.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DnkFo5Qy.js +0 -1
- package/dist/client/assets/index-6FBJPKkr.css +0 -1
package/dist/bin.js
CHANGED
|
@@ -908,6 +908,7 @@ var init_workflow_steps = __esm({
|
|
|
908
908
|
"in-review",
|
|
909
909
|
"merged",
|
|
910
910
|
"failed",
|
|
911
|
+
"task-wedged",
|
|
911
912
|
"awaiting-approval",
|
|
912
913
|
"awaiting-user-review",
|
|
913
914
|
"planning-awaiting-input",
|
|
@@ -981,6 +982,50 @@ var init_documents_artifacts = __esm({
|
|
|
981
982
|
}
|
|
982
983
|
});
|
|
983
984
|
|
|
985
|
+
// ../core/src/types/task-core.ts
|
|
986
|
+
function assertNotWorkspaceTaskMerge(task) {
|
|
987
|
+
if (isWorkspaceTask(task)) {
|
|
988
|
+
throw new WorkspaceTaskMergeError(
|
|
989
|
+
`Workspace task ${task.id} cannot merge until per-repo merge support (master-plan U6) lands`
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
function isWorkspaceTask(task) {
|
|
994
|
+
const worktrees = task.workspaceWorktrees;
|
|
995
|
+
return !!worktrees && Object.keys(worktrees).length > 0;
|
|
996
|
+
}
|
|
997
|
+
var CheckoutConflictError, DUPLICATE_OF_METADATA_KEY, WorkspaceTaskMergeError;
|
|
998
|
+
var init_task_core = __esm({
|
|
999
|
+
"../core/src/types/task-core.ts"() {
|
|
1000
|
+
"use strict";
|
|
1001
|
+
CheckoutConflictError = class extends Error {
|
|
1002
|
+
constructor(taskId, currentHolderId, requestedById) {
|
|
1003
|
+
super(`Task ${taskId} is already checked out by agent ${currentHolderId}`);
|
|
1004
|
+
this.taskId = taskId;
|
|
1005
|
+
this.currentHolderId = currentHolderId;
|
|
1006
|
+
this.requestedById = requestedById;
|
|
1007
|
+
this.name = "CheckoutConflictError";
|
|
1008
|
+
}
|
|
1009
|
+
};
|
|
1010
|
+
DUPLICATE_OF_METADATA_KEY = "duplicateOfTaskIds";
|
|
1011
|
+
WorkspaceTaskMergeError = class extends Error {
|
|
1012
|
+
constructor(message) {
|
|
1013
|
+
super(message);
|
|
1014
|
+
this.name = "WorkspaceTaskMergeError";
|
|
1015
|
+
}
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
// ../core/src/types/todo-list.ts
|
|
1021
|
+
var SHARED_STATE_SNAPSHOT_VERSION;
|
|
1022
|
+
var init_todo_list = __esm({
|
|
1023
|
+
"../core/src/types/todo-list.ts"() {
|
|
1024
|
+
"use strict";
|
|
1025
|
+
SHARED_STATE_SNAPSHOT_VERSION = 1;
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
|
|
984
1029
|
// ../core/src/task-helpers.ts
|
|
985
1030
|
function getPrimaryPrInfo(task) {
|
|
986
1031
|
return task.prInfos?.[0] ?? task.prInfo;
|
|
@@ -1470,10 +1515,18 @@ var init_settings_schema = __esm({
|
|
|
1470
1515
|
"use strict";
|
|
1471
1516
|
init_in_review_stall();
|
|
1472
1517
|
DEFAULT_GLOBAL_SETTINGS = {
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1518
|
+
/*
|
|
1519
|
+
FNXC:PostgresEmbedded 2026-07-22-23:55:
|
|
1520
|
+
Embedded PostgreSQL is shared by all local Fusion projects and processes.
|
|
1521
|
+
Deliberately undefined (not 500): getSettings() merges these defaults, so a
|
|
1522
|
+
concrete value here would be indistinguishable from an operator choice and
|
|
1523
|
+
would mask the platform-aware server-side default in
|
|
1524
|
+
resolveEmbeddedMaxConnections (win32 150, else 500). Issue #2411: Windows
|
|
1525
|
+
backends are separate processes; a 500-connection cap exhausts the desktop
|
|
1526
|
+
heap under load and forked backends die with 0xC0000142, taking the embedded
|
|
1527
|
+
cluster and dashboard down.
|
|
1528
|
+
*/
|
|
1529
|
+
embeddedPostgresMaxConnections: void 0,
|
|
1477
1530
|
/*
|
|
1478
1531
|
FNXC:DashboardTheming 2026-07-03-00:00:
|
|
1479
1532
|
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.
|
|
@@ -2236,6 +2289,53 @@ var init_settings_schema = __esm({
|
|
|
2236
2289
|
}
|
|
2237
2290
|
});
|
|
2238
2291
|
|
|
2292
|
+
// ../core/src/types/settings-scope.ts
|
|
2293
|
+
function isMcpSecretRef(value) {
|
|
2294
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2295
|
+
const candidate = value;
|
|
2296
|
+
return typeof candidate.secretRef === "string" && candidate.secretRef.trim().length > 0 && (candidate.scope === "project" || candidate.scope === "global");
|
|
2297
|
+
}
|
|
2298
|
+
var init_settings_scope = __esm({
|
|
2299
|
+
"../core/src/types/settings-scope.ts"() {
|
|
2300
|
+
"use strict";
|
|
2301
|
+
init_settings_schema();
|
|
2302
|
+
}
|
|
2303
|
+
});
|
|
2304
|
+
|
|
2305
|
+
// ../core/src/types/board-config.ts
|
|
2306
|
+
var COLUMN_LABELS, COLUMN_DESCRIPTIONS, VALID_TRANSITIONS;
|
|
2307
|
+
var init_board_config = __esm({
|
|
2308
|
+
"../core/src/types/board-config.ts"() {
|
|
2309
|
+
"use strict";
|
|
2310
|
+
COLUMN_LABELS = {
|
|
2311
|
+
triage: "Planning",
|
|
2312
|
+
todo: "Todo",
|
|
2313
|
+
"in-progress": "In Progress",
|
|
2314
|
+
"in-review": "In Review",
|
|
2315
|
+
done: "Done",
|
|
2316
|
+
archived: "Archived"
|
|
2317
|
+
};
|
|
2318
|
+
COLUMN_DESCRIPTIONS = {
|
|
2319
|
+
triage: "Raw ideas \u2014 AI will plan these",
|
|
2320
|
+
"in-progress": "AI is working on this in a worktree",
|
|
2321
|
+
done: "Merged and closed",
|
|
2322
|
+
archived: "Completed and archived"
|
|
2323
|
+
};
|
|
2324
|
+
VALID_TRANSITIONS = {
|
|
2325
|
+
// FN-4892: intake-side heuristics may cold-archive tasks before execution starts.
|
|
2326
|
+
triage: ["todo", "archived"],
|
|
2327
|
+
// FN-4892: allow direct archival for newly specified intake tasks.
|
|
2328
|
+
todo: ["in-progress", "triage", "archived"],
|
|
2329
|
+
// NOTE: "in-progress" → "done" is enabled for mission validation tasks that complete directly.
|
|
2330
|
+
// Regular implementation tasks should move through "in-review" before "done".
|
|
2331
|
+
"in-progress": ["in-review", "todo", "triage", "done"],
|
|
2332
|
+
"in-review": ["done", "in-progress", "todo", "triage"],
|
|
2333
|
+
done: ["todo", "triage", "archived"],
|
|
2334
|
+
archived: ["done"]
|
|
2335
|
+
};
|
|
2336
|
+
}
|
|
2337
|
+
});
|
|
2338
|
+
|
|
2239
2339
|
// ../core/src/types/docker-nodes.ts
|
|
2240
2340
|
function validateDockerNodeConfig(config3) {
|
|
2241
2341
|
const errors = [];
|
|
@@ -2346,6 +2446,41 @@ var init_docker_nodes = __esm({
|
|
|
2346
2446
|
}
|
|
2347
2447
|
});
|
|
2348
2448
|
|
|
2449
|
+
// ../core/src/types/archive-planning.ts
|
|
2450
|
+
function formatPlanningPlanMd(summary) {
|
|
2451
|
+
const normalizeListItem = (item) => item.replace(/\s+/g, " ").trim();
|
|
2452
|
+
const list = (items) => items && items.length > 0 ? items.map((item) => `- ${normalizeListItem(item)}`).join("\n") : "_None_";
|
|
2453
|
+
const proposedChanges = list(summary.proposedChanges);
|
|
2454
|
+
const acceptanceCriteria = list(summary.acceptanceCriteria);
|
|
2455
|
+
const dependencies = list(summary.suggestedDependencies);
|
|
2456
|
+
const deliverables = list(summary.keyDeliverables);
|
|
2457
|
+
return `# ${summary.title}
|
|
2458
|
+
|
|
2459
|
+
${summary.description}
|
|
2460
|
+
|
|
2461
|
+
## What to change
|
|
2462
|
+
${proposedChanges}
|
|
2463
|
+
|
|
2464
|
+
## Acceptance criteria
|
|
2465
|
+
${acceptanceCriteria}
|
|
2466
|
+
|
|
2467
|
+
## Size
|
|
2468
|
+
${summary.suggestedSize}
|
|
2469
|
+
|
|
2470
|
+
## Suggested dependencies
|
|
2471
|
+
${dependencies}
|
|
2472
|
+
|
|
2473
|
+
## Key deliverables
|
|
2474
|
+
${deliverables}
|
|
2475
|
+
`;
|
|
2476
|
+
}
|
|
2477
|
+
var init_archive_planning = __esm({
|
|
2478
|
+
"../core/src/types/archive-planning.ts"() {
|
|
2479
|
+
"use strict";
|
|
2480
|
+
init_docker_nodes();
|
|
2481
|
+
}
|
|
2482
|
+
});
|
|
2483
|
+
|
|
2349
2484
|
// ../core/src/types/agent-state.ts
|
|
2350
2485
|
function isEphemeralAgent(agent) {
|
|
2351
2486
|
const metadata = agent.metadata ?? {};
|
|
@@ -4071,49 +4206,6 @@ var init_workflow_lifecycle_validation = __esm({
|
|
|
4071
4206
|
});
|
|
4072
4207
|
|
|
4073
4208
|
// ../core/src/types.ts
|
|
4074
|
-
function assertNotWorkspaceTaskMerge(task) {
|
|
4075
|
-
if (isWorkspaceTask(task)) {
|
|
4076
|
-
throw new WorkspaceTaskMergeError(
|
|
4077
|
-
`Workspace task ${task.id} cannot merge until per-repo merge support (master-plan U6) lands`
|
|
4078
|
-
);
|
|
4079
|
-
}
|
|
4080
|
-
}
|
|
4081
|
-
function isWorkspaceTask(task) {
|
|
4082
|
-
const worktrees = task.workspaceWorktrees;
|
|
4083
|
-
return !!worktrees && Object.keys(worktrees).length > 0;
|
|
4084
|
-
}
|
|
4085
|
-
function isMcpSecretRef(value) {
|
|
4086
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
4087
|
-
const candidate = value;
|
|
4088
|
-
return typeof candidate.secretRef === "string" && candidate.secretRef.trim().length > 0 && (candidate.scope === "project" || candidate.scope === "global");
|
|
4089
|
-
}
|
|
4090
|
-
function formatPlanningPlanMd(summary) {
|
|
4091
|
-
const normalizeListItem = (item) => item.replace(/\s+/g, " ").trim();
|
|
4092
|
-
const list = (items) => items && items.length > 0 ? items.map((item) => `- ${normalizeListItem(item)}`).join("\n") : "_None_";
|
|
4093
|
-
const proposedChanges = list(summary.proposedChanges);
|
|
4094
|
-
const acceptanceCriteria = list(summary.acceptanceCriteria);
|
|
4095
|
-
const dependencies = list(summary.suggestedDependencies);
|
|
4096
|
-
const deliverables = list(summary.keyDeliverables);
|
|
4097
|
-
return `# ${summary.title}
|
|
4098
|
-
|
|
4099
|
-
${summary.description}
|
|
4100
|
-
|
|
4101
|
-
## What to change
|
|
4102
|
-
${proposedChanges}
|
|
4103
|
-
|
|
4104
|
-
## Acceptance criteria
|
|
4105
|
-
${acceptanceCriteria}
|
|
4106
|
-
|
|
4107
|
-
## Size
|
|
4108
|
-
${summary.suggestedSize}
|
|
4109
|
-
|
|
4110
|
-
## Suggested dependencies
|
|
4111
|
-
${dependencies}
|
|
4112
|
-
|
|
4113
|
-
## Key deliverables
|
|
4114
|
-
${deliverables}
|
|
4115
|
-
`;
|
|
4116
|
-
}
|
|
4117
4209
|
function validateMessageMetadata(metadata) {
|
|
4118
4210
|
if (!metadata) {
|
|
4119
4211
|
return;
|
|
@@ -4165,7 +4257,7 @@ function validateMessageMetadata(metadata) {
|
|
|
4165
4257
|
if (metadata.proposalStatus === "pending" && (metadata.claimOwnerToken !== void 0 || metadata.claimStartedAt !== void 0)) throw new Error("pending proposal cannot have a creation lease");
|
|
4166
4258
|
}
|
|
4167
4259
|
}
|
|
4168
|
-
var DEPRECATED_BUILTIN_WORKFLOW_IDS
|
|
4260
|
+
var DEPRECATED_BUILTIN_WORKFLOW_IDS;
|
|
4169
4261
|
var init_types = __esm({
|
|
4170
4262
|
"../core/src/types.ts"() {
|
|
4171
4263
|
"use strict";
|
|
@@ -4179,8 +4271,11 @@ var init_types = __esm({
|
|
|
4179
4271
|
init_merge_policy();
|
|
4180
4272
|
init_workflow_steps();
|
|
4181
4273
|
init_documents_artifacts();
|
|
4182
|
-
|
|
4183
|
-
|
|
4274
|
+
init_task_core();
|
|
4275
|
+
init_todo_list();
|
|
4276
|
+
init_settings_scope();
|
|
4277
|
+
init_board_config();
|
|
4278
|
+
init_archive_planning();
|
|
4184
4279
|
init_agent_state();
|
|
4185
4280
|
init_planner_intervention();
|
|
4186
4281
|
init_agents();
|
|
@@ -4195,49 +4290,6 @@ var init_types = __esm({
|
|
|
4195
4290
|
DEPRECATED_BUILTIN_WORKFLOW_IDS = /* @__PURE__ */ new Set([
|
|
4196
4291
|
"builtin:brainstorming"
|
|
4197
4292
|
]);
|
|
4198
|
-
CheckoutConflictError = class extends Error {
|
|
4199
|
-
constructor(taskId, currentHolderId, requestedById) {
|
|
4200
|
-
super(`Task ${taskId} is already checked out by agent ${currentHolderId}`);
|
|
4201
|
-
this.taskId = taskId;
|
|
4202
|
-
this.currentHolderId = currentHolderId;
|
|
4203
|
-
this.requestedById = requestedById;
|
|
4204
|
-
this.name = "CheckoutConflictError";
|
|
4205
|
-
}
|
|
4206
|
-
};
|
|
4207
|
-
DUPLICATE_OF_METADATA_KEY = "duplicateOfTaskIds";
|
|
4208
|
-
WorkspaceTaskMergeError = class extends Error {
|
|
4209
|
-
constructor(message) {
|
|
4210
|
-
super(message);
|
|
4211
|
-
this.name = "WorkspaceTaskMergeError";
|
|
4212
|
-
}
|
|
4213
|
-
};
|
|
4214
|
-
SHARED_STATE_SNAPSHOT_VERSION = 1;
|
|
4215
|
-
COLUMN_LABELS = {
|
|
4216
|
-
triage: "Planning",
|
|
4217
|
-
todo: "Todo",
|
|
4218
|
-
"in-progress": "In Progress",
|
|
4219
|
-
"in-review": "In Review",
|
|
4220
|
-
done: "Done",
|
|
4221
|
-
archived: "Archived"
|
|
4222
|
-
};
|
|
4223
|
-
COLUMN_DESCRIPTIONS = {
|
|
4224
|
-
triage: "Raw ideas \u2014 AI will plan these",
|
|
4225
|
-
"in-progress": "AI is working on this in a worktree",
|
|
4226
|
-
done: "Merged and closed",
|
|
4227
|
-
archived: "Completed and archived"
|
|
4228
|
-
};
|
|
4229
|
-
VALID_TRANSITIONS = {
|
|
4230
|
-
// FN-4892: intake-side heuristics may cold-archive tasks before execution starts.
|
|
4231
|
-
triage: ["todo", "archived"],
|
|
4232
|
-
// FN-4892: allow direct archival for newly specified intake tasks.
|
|
4233
|
-
todo: ["in-progress", "triage", "archived"],
|
|
4234
|
-
// NOTE: "in-progress" → "done" is enabled for mission validation tasks that complete directly.
|
|
4235
|
-
// Regular implementation tasks should move through "in-review" before "done".
|
|
4236
|
-
"in-progress": ["in-review", "todo", "triage", "done"],
|
|
4237
|
-
"in-review": ["done", "in-progress", "todo", "triage"],
|
|
4238
|
-
done: ["todo", "triage", "archived"],
|
|
4239
|
-
archived: ["done"]
|
|
4240
|
-
};
|
|
4241
4293
|
}
|
|
4242
4294
|
});
|
|
4243
4295
|
|
|
@@ -12858,6 +12910,7 @@ var init_project = __esm({
|
|
|
12858
12910
|
paused: integer("paused").default(0),
|
|
12859
12911
|
userPaused: integer("user_paused").default(0),
|
|
12860
12912
|
pausedReason: text("paused_reason"),
|
|
12913
|
+
wedgeNotification: text("wedge_notification"),
|
|
12861
12914
|
baseBranch: text("base_branch"),
|
|
12862
12915
|
branch: text("branch"),
|
|
12863
12916
|
autoMerge: integer("auto_merge"),
|
|
@@ -21222,6 +21275,9 @@ function validateColumns(ir2) {
|
|
|
21222
21275
|
throw new WorkflowIrError(`Workflow IR has duplicate column id '${column.id}'`);
|
|
21223
21276
|
}
|
|
21224
21277
|
seen.add(column.id);
|
|
21278
|
+
if (column.description !== void 0 && typeof column.description !== "string") {
|
|
21279
|
+
throw new WorkflowIrError(`Workflow IR column '${column.id}' description must be a string`);
|
|
21280
|
+
}
|
|
21225
21281
|
if (!Array.isArray(column.traits)) {
|
|
21226
21282
|
throw new WorkflowIrError(`Workflow IR column '${column.id}' traits must be an array`);
|
|
21227
21283
|
}
|
|
@@ -36445,6 +36501,7 @@ async function applySchemaBaseline(db, options = {}) {
|
|
|
36445
36501
|
const legacyAdoptionDrainedMarkerRuntimeGrantsAlreadyApplied = applied.includes(
|
|
36446
36502
|
LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_VERSION
|
|
36447
36503
|
);
|
|
36504
|
+
const taskWedgeNotificationAlreadyApplied = applied.includes(TASK_WEDGE_NOTIFICATION_VERSION);
|
|
36448
36505
|
assertBinaryNotOlderThanDatabase(applied);
|
|
36449
36506
|
let schemaChanged = false;
|
|
36450
36507
|
if (!baselineAlreadyApplied) {
|
|
@@ -36768,17 +36825,25 @@ async function applySchemaBaseline(db, options = {}) {
|
|
|
36768
36825
|
);
|
|
36769
36826
|
schemaChanged = true;
|
|
36770
36827
|
}
|
|
36828
|
+
if (!taskWedgeNotificationAlreadyApplied) {
|
|
36829
|
+
const migrationSql = await readFile4(TASK_WEDGE_NOTIFICATION_MIGRATION_PATH, "utf8");
|
|
36830
|
+
await tx.execute(sql.raw(migrationSql));
|
|
36831
|
+
await tx.execute(
|
|
36832
|
+
sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${TASK_WEDGE_NOTIFICATION_VERSION}) ON CONFLICT (version) DO NOTHING`
|
|
36833
|
+
);
|
|
36834
|
+
schemaChanged = true;
|
|
36835
|
+
}
|
|
36771
36836
|
return { applied: schemaChanged, pluginHooksRun: pluginHooks.length };
|
|
36772
36837
|
});
|
|
36773
36838
|
}
|
|
36774
|
-
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;
|
|
36839
|
+
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;
|
|
36775
36840
|
var init_schema_applier = __esm({
|
|
36776
36841
|
"../core/src/postgres/schema-applier.ts"() {
|
|
36777
36842
|
"use strict";
|
|
36778
36843
|
init_drizzle_orm();
|
|
36779
36844
|
init_plugin_schema_hook();
|
|
36780
36845
|
init_advisory_locks();
|
|
36781
|
-
SCHEMA_BASELINE_VERSION = "
|
|
36846
|
+
SCHEMA_BASELINE_VERSION = "0033";
|
|
36782
36847
|
TASK_DECLARED_SYMBOLS_VERSION = "0028";
|
|
36783
36848
|
INITIAL_SCHEMA_VERSION = "0000";
|
|
36784
36849
|
AUTOMATION_ISOLATION_SCHEMA_VERSION = "0001";
|
|
@@ -36811,6 +36876,7 @@ var init_schema_applier = __esm({
|
|
|
36811
36876
|
SQLITE_MIGRATION_RUNTIME_READ_VERSION = "0030";
|
|
36812
36877
|
WORKFLOW_TASK_CONTINUATIONS_VERSION = "0031";
|
|
36813
36878
|
LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_VERSION = "0032";
|
|
36879
|
+
TASK_WEDGE_NOTIFICATION_VERSION = "0033";
|
|
36814
36880
|
LEGACY_ADOPTION_DRAINED_MARKER_FUNCTION = "fusion_mark_legacy_adoption_drained";
|
|
36815
36881
|
StaleBinarySchemaError = class extends Error {
|
|
36816
36882
|
constructor(databaseVersion, binaryVersion) {
|
|
@@ -36917,6 +36983,10 @@ var init_schema_applier = __esm({
|
|
|
36917
36983
|
MIGRATIONS_DIR,
|
|
36918
36984
|
"0032_legacy_adoption_drained_marker_runtime_grants.sql"
|
|
36919
36985
|
);
|
|
36986
|
+
TASK_WEDGE_NOTIFICATION_MIGRATION_PATH = join8(
|
|
36987
|
+
MIGRATIONS_DIR,
|
|
36988
|
+
"0033_fn-8505_wedge_notification.sql"
|
|
36989
|
+
);
|
|
36920
36990
|
}
|
|
36921
36991
|
});
|
|
36922
36992
|
|
|
@@ -37307,6 +37377,7 @@ __export(sqlite_migrator_exports, {
|
|
|
37307
37377
|
isSqliteMigrationComplete: () => isSqliteMigrationComplete,
|
|
37308
37378
|
migrateLegacyProjectPluginRows: () => migrateLegacyProjectPluginRows,
|
|
37309
37379
|
migrateSqliteToPostgres: () => migrateSqliteToPostgres,
|
|
37380
|
+
projectPluginSqliteMigrationKey: () => projectPluginSqliteMigrationKey,
|
|
37310
37381
|
recordSqliteMigrationComplete: () => recordSqliteMigrationComplete,
|
|
37311
37382
|
toSnakeCase: () => toSnakeCase2
|
|
37312
37383
|
});
|
|
@@ -37849,7 +37920,12 @@ function normalizeLegacyJson(value, fallback2) {
|
|
|
37849
37920
|
return fallback2;
|
|
37850
37921
|
}
|
|
37851
37922
|
}
|
|
37923
|
+
function projectPluginSqliteMigrationKey(projectPath) {
|
|
37924
|
+
return `project-plugins:${resolve6(projectPath)}`;
|
|
37925
|
+
}
|
|
37852
37926
|
async function migrateLegacyProjectPluginRows(db, sqlitePath, projectPath) {
|
|
37927
|
+
const migrationKey = projectPluginSqliteMigrationKey(projectPath);
|
|
37928
|
+
if (await isSqliteMigrationComplete(db, migrationKey)) return;
|
|
37853
37929
|
await db.transaction(async (tx) => {
|
|
37854
37930
|
await migrateLegacyProjectPluginRowsOnSession(
|
|
37855
37931
|
tx,
|
|
@@ -37859,19 +37935,14 @@ async function migrateLegacyProjectPluginRows(db, sqlitePath, projectPath) {
|
|
|
37859
37935
|
});
|
|
37860
37936
|
}
|
|
37861
37937
|
async function migrateLegacyProjectPluginRowsOnSession(db, sqlitePath, projectPath) {
|
|
37862
|
-
if (!sqliteTableExists(sqlitePath, "plugins")) return;
|
|
37863
|
-
await acquireSqliteMigrationStateLock(db);
|
|
37864
37938
|
const canonicalProjectPath = resolve6(projectPath);
|
|
37865
|
-
const migrationKey =
|
|
37939
|
+
const migrationKey = projectPluginSqliteMigrationKey(projectPath);
|
|
37940
|
+
await acquireSqliteMigrationStateLock(db);
|
|
37866
37941
|
await ensureMigrationStateTable(db);
|
|
37867
37942
|
await db.execute(sql`SELECT pg_advisory_xact_lock(hashtextextended(${migrationKey}, 0))`);
|
|
37868
|
-
const completed = await db
|
|
37869
|
-
|
|
37870
|
-
|
|
37871
|
-
WHERE migration_key = ${migrationKey} AND status = 'complete'
|
|
37872
|
-
LIMIT 1
|
|
37873
|
-
`);
|
|
37874
|
-
if (completed.length > 0) return;
|
|
37943
|
+
const completed = await isSqliteMigrationComplete(db, migrationKey);
|
|
37944
|
+
if (completed) return;
|
|
37945
|
+
if (!sqliteTableExists(sqlitePath, "plugins")) return;
|
|
37875
37946
|
const sqlite = openSqlite(sqlitePath);
|
|
37876
37947
|
let rows;
|
|
37877
37948
|
try {
|
|
@@ -38957,6 +39028,17 @@ function buildPgCtlOptionsString(port, flags) {
|
|
|
38957
39028
|
function buildPgCtlStartArgs(dataDir, logFile, optionsString) {
|
|
38958
39029
|
return ["-D", dataDir, "-o", optionsString, "-l", logFile, "-W", "start"];
|
|
38959
39030
|
}
|
|
39031
|
+
function withWindowsNativeBinPath(environment, nativeRoot, platform6 = process.platform) {
|
|
39032
|
+
if (platform6 !== "win32") return environment;
|
|
39033
|
+
const pathKey = Object.keys(environment).find((key) => key.toLowerCase() === "path") ?? "PATH";
|
|
39034
|
+
const bin = nativeRoot.replace(/[\\/]+$/, "") + "\\bin";
|
|
39035
|
+
const normalize10 = (value) => value.replace(/\//g, "\\").toLowerCase();
|
|
39036
|
+
const inherited = environment[pathKey] ?? "";
|
|
39037
|
+
const segments = inherited.split(";").filter(Boolean);
|
|
39038
|
+
const normalizedBin = normalize10(bin);
|
|
39039
|
+
const deduplicated = segments.filter((segment) => normalize10(segment) !== normalizedBin);
|
|
39040
|
+
return { ...environment, [pathKey]: [bin, ...deduplicated].join(";") };
|
|
39041
|
+
}
|
|
38960
39042
|
function readPostgresPid(dataDir) {
|
|
38961
39043
|
try {
|
|
38962
39044
|
const lines = readFileSync3(join9(dataDir, "postmaster.pid"), "utf-8").split("\n");
|
|
@@ -39011,7 +39093,13 @@ async function startServerElevatedRestricted(opts) {
|
|
|
39011
39093
|
`embedded postgres: elevated start via pg_ctl restricted token (no helper account); log ${logFile}`
|
|
39012
39094
|
);
|
|
39013
39095
|
let stopped = false;
|
|
39096
|
+
let logMonitor = null;
|
|
39097
|
+
const stopMonitoring = () => {
|
|
39098
|
+
if (logMonitor) clearInterval(logMonitor);
|
|
39099
|
+
logMonitor = null;
|
|
39100
|
+
};
|
|
39014
39101
|
const killAll = () => {
|
|
39102
|
+
stopMonitoring();
|
|
39015
39103
|
if (stopped) return;
|
|
39016
39104
|
stopped = true;
|
|
39017
39105
|
const r = spawnSync(pgCtl, ["-D", opts.dataDir, "-m", "fast", "-t", "30", "-w", "stop"], {
|
|
@@ -39045,14 +39133,21 @@ async function startServerElevatedRestricted(opts) {
|
|
|
39045
39133
|
killAll();
|
|
39046
39134
|
await waitForDown();
|
|
39047
39135
|
},
|
|
39136
|
+
stopMonitoring,
|
|
39048
39137
|
async stopWrapperOnly() {
|
|
39138
|
+
stopMonitoring();
|
|
39049
39139
|
stopped = true;
|
|
39050
39140
|
}
|
|
39051
39141
|
};
|
|
39052
39142
|
opts.onLaunched?.(handle);
|
|
39053
39143
|
const launch = await new Promise(
|
|
39054
39144
|
(resolve101, reject2) => {
|
|
39055
|
-
const child = spawn(pgCtl, args, {
|
|
39145
|
+
const child = spawn(pgCtl, args, {
|
|
39146
|
+
windowsHide: true,
|
|
39147
|
+
// Do not mutate process.env: sibling dashboard/engine children must
|
|
39148
|
+
// retain their inherited environment unchanged.
|
|
39149
|
+
env: withWindowsNativeBinPath(process.env, opts.nativeRoot)
|
|
39150
|
+
});
|
|
39056
39151
|
let output = "";
|
|
39057
39152
|
child.stdout.on("data", (d) => output += d.toString());
|
|
39058
39153
|
child.stderr.on("data", (d) => output += d.toString());
|
|
@@ -39077,16 +39172,14 @@ ${readTail(logFile, 2e3)}`
|
|
|
39077
39172
|
killAll();
|
|
39078
39173
|
throw new Error("embedded postgres: elevated launch cancelled before ready.");
|
|
39079
39174
|
}
|
|
39175
|
+
if (await probeTcpPort(opts.port, 500)) {
|
|
39176
|
+
ready = true;
|
|
39177
|
+
break;
|
|
39178
|
+
}
|
|
39080
39179
|
const tail = readTail(logFile, 3e3);
|
|
39081
39180
|
if (tail !== lastSnapshot) {
|
|
39082
39181
|
lastSnapshot = tail;
|
|
39083
|
-
opts.onLog(`elevated
|
|
39084
|
-
}
|
|
39085
|
-
if (/database system is ready to accept connections/.test(tail)) {
|
|
39086
|
-
if (await probeTcpPort(opts.port, 500)) {
|
|
39087
|
-
ready = true;
|
|
39088
|
-
break;
|
|
39089
|
-
}
|
|
39182
|
+
opts.onLog(`elevated diagnostic pg={${tail.slice(-400)}}`);
|
|
39090
39183
|
}
|
|
39091
39184
|
if (/\bFATAL\b|\bPANIC\b|could not (bind|start|create|access|connect|load)|not permitted|Permission denied|is not the owner/i.test(tail)) {
|
|
39092
39185
|
killAll();
|
|
@@ -39118,6 +39211,18 @@ ${tail}`
|
|
|
39118
39211
|
opts.onLog(
|
|
39119
39212
|
`embedded postgres: elevated server ready on 127.0.0.1:${opts.port} (pid ${postgresPid ?? 0}, restricted token)`
|
|
39120
39213
|
);
|
|
39214
|
+
let lastLogSnapshot = "";
|
|
39215
|
+
const observePostReadyLog = () => {
|
|
39216
|
+
if (stopped) return;
|
|
39217
|
+
const snapshot = readTail(logFile, 16384);
|
|
39218
|
+
if (snapshot === "(no log file)" || snapshot === lastLogSnapshot) return;
|
|
39219
|
+
const next = snapshot.startsWith(lastLogSnapshot) ? snapshot.slice(lastLogSnapshot.length) : snapshot;
|
|
39220
|
+
lastLogSnapshot = snapshot;
|
|
39221
|
+
if (next) opts.onLog(next);
|
|
39222
|
+
};
|
|
39223
|
+
observePostReadyLog();
|
|
39224
|
+
logMonitor = setInterval(observePostReadyLog, 250);
|
|
39225
|
+
if (typeof logMonitor.unref === "function") logMonitor.unref();
|
|
39121
39226
|
return handle;
|
|
39122
39227
|
}
|
|
39123
39228
|
function probeTcpPort(port, timeoutMs) {
|
|
@@ -39137,10 +39242,26 @@ function probeTcpPort(port, timeoutMs) {
|
|
|
39137
39242
|
socket.once("error", () => finish(false));
|
|
39138
39243
|
});
|
|
39139
39244
|
}
|
|
39140
|
-
var elevatedCache;
|
|
39245
|
+
var WindowsPostgresFatalDetector, elevatedCache;
|
|
39141
39246
|
var init_embedded_windows_elevated = __esm({
|
|
39142
39247
|
"../core/src/postgres/embedded-windows-elevated.ts"() {
|
|
39143
39248
|
"use strict";
|
|
39249
|
+
WindowsPostgresFatalDetector = class {
|
|
39250
|
+
buffer = "";
|
|
39251
|
+
matched = false;
|
|
39252
|
+
push(chunk) {
|
|
39253
|
+
if (this.matched || !chunk) return false;
|
|
39254
|
+
this.buffer = (this.buffer + chunk).slice(-16384);
|
|
39255
|
+
const text3 = this.buffer.toLowerCase();
|
|
39256
|
+
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(
|
|
39257
|
+
text3
|
|
39258
|
+
)) {
|
|
39259
|
+
this.matched = true;
|
|
39260
|
+
return true;
|
|
39261
|
+
}
|
|
39262
|
+
return false;
|
|
39263
|
+
}
|
|
39264
|
+
};
|
|
39144
39265
|
elevatedCache = null;
|
|
39145
39266
|
}
|
|
39146
39267
|
});
|
|
@@ -39150,10 +39271,14 @@ var embedded_lifecycle_exports = {};
|
|
|
39150
39271
|
__export(embedded_lifecycle_exports, {
|
|
39151
39272
|
DEFAULT_EMBEDDED_DATABASE: () => DEFAULT_EMBEDDED_DATABASE,
|
|
39152
39273
|
DEFAULT_EMBEDDED_INITDB_FLAGS: () => DEFAULT_EMBEDDED_INITDB_FLAGS,
|
|
39274
|
+
DEFAULT_EMBEDDED_MAX_CONNECTIONS: () => DEFAULT_EMBEDDED_MAX_CONNECTIONS,
|
|
39275
|
+
DEFAULT_EMBEDDED_MAX_CONNECTIONS_WIN32: () => DEFAULT_EMBEDDED_MAX_CONNECTIONS_WIN32,
|
|
39153
39276
|
DEFAULT_EMBEDDED_PASSWORD: () => DEFAULT_EMBEDDED_PASSWORD,
|
|
39154
39277
|
DEFAULT_EMBEDDED_POSTGRES_FLAGS: () => DEFAULT_EMBEDDED_POSTGRES_FLAGS,
|
|
39155
39278
|
DEFAULT_EMBEDDED_USER: () => DEFAULT_EMBEDDED_USER,
|
|
39156
39279
|
DEFAULT_START_TIMEOUT_MS: () => DEFAULT_START_TIMEOUT_MS,
|
|
39280
|
+
EMBEDDED_MAX_CONNECTIONS_MAX: () => EMBEDDED_MAX_CONNECTIONS_MAX,
|
|
39281
|
+
EMBEDDED_MAX_CONNECTIONS_MIN: () => EMBEDDED_MAX_CONNECTIONS_MIN,
|
|
39157
39282
|
EmbeddedPostgresLifecycle: () => EmbeddedPostgresLifecycle,
|
|
39158
39283
|
EmbeddedStartTimeoutError: () => EmbeddedStartTimeoutError,
|
|
39159
39284
|
__setEmbeddedPostgresCtorForTests: () => __setEmbeddedPostgresCtorForTests,
|
|
@@ -39172,6 +39297,7 @@ __export(embedded_lifecycle_exports, {
|
|
|
39172
39297
|
normalizeMacosEmbeddedPostgresDylibSymlinks: () => normalizeMacosEmbeddedPostgresDylibSymlinks,
|
|
39173
39298
|
readPortFromPostmasterPid: () => readPortFromPostmasterPid,
|
|
39174
39299
|
resolveElectronAsarUnpackedPath: () => resolveElectronAsarUnpackedPath,
|
|
39300
|
+
resolveEmbeddedMaxConnections: () => resolveEmbeddedMaxConnections,
|
|
39175
39301
|
uninstallElectronAsarNativePathPatchForTests: () => uninstallElectronAsarNativePathPatchForTests
|
|
39176
39302
|
});
|
|
39177
39303
|
import {
|
|
@@ -39381,6 +39507,23 @@ function materializeEmbeddedPostgresRuntimeBinaries(nativeRoot, options) {
|
|
|
39381
39507
|
writeFileSync2(marker, sourceMarker, "utf8");
|
|
39382
39508
|
return destRoot;
|
|
39383
39509
|
}
|
|
39510
|
+
function isWindowsEmbeddedPostgresBinary(command) {
|
|
39511
|
+
return process.platform === "win32" && typeof command === "string" && /[\\/]bin[\\/](?:postgres|initdb|pg_ctl)\.exe$/i.test(command);
|
|
39512
|
+
}
|
|
39513
|
+
function withWindowsPostgresSpawnEnvironment(command, rest) {
|
|
39514
|
+
if (!isWindowsEmbeddedPostgresBinary(command)) return rest;
|
|
39515
|
+
const args = [...rest];
|
|
39516
|
+
const optionIndex = args.length - 1;
|
|
39517
|
+
const existing = args[optionIndex];
|
|
39518
|
+
if (!existing || typeof existing !== "object" || Array.isArray(existing)) return args;
|
|
39519
|
+
const binDir = dirname6(command);
|
|
39520
|
+
const nativeRoot = dirname6(binDir);
|
|
39521
|
+
args[optionIndex] = {
|
|
39522
|
+
...existing,
|
|
39523
|
+
env: withWindowsNativeBinPath(existing.env ?? process.env, nativeRoot)
|
|
39524
|
+
};
|
|
39525
|
+
return args;
|
|
39526
|
+
}
|
|
39384
39527
|
function installElectronAsarNativePathPatch() {
|
|
39385
39528
|
if (electronAsarNativePathPatchInstalled) return;
|
|
39386
39529
|
electronAsarNativePathPatchInstalled = true;
|
|
@@ -39398,7 +39541,10 @@ function installElectronAsarNativePathPatch() {
|
|
|
39398
39541
|
const originalSpawn = childProcessMod.spawn.bind(childProcessMod);
|
|
39399
39542
|
childProcessMod.spawn = (command, ...rest) => {
|
|
39400
39543
|
const fixedCommand = typeof command === "string" ? resolveElectronAsarUnpackedPath(command) : command;
|
|
39401
|
-
return originalSpawn(
|
|
39544
|
+
return originalSpawn(
|
|
39545
|
+
fixedCommand,
|
|
39546
|
+
...withWindowsPostgresSpawnEnvironment(fixedCommand, rest)
|
|
39547
|
+
);
|
|
39402
39548
|
};
|
|
39403
39549
|
const fsPromisesMod = require2("fs/promises");
|
|
39404
39550
|
const originalStat = fsPromisesMod.stat.bind(fsPromisesMod);
|
|
@@ -39447,6 +39593,12 @@ function getEmbeddedPostgresCtor() {
|
|
|
39447
39593
|
function defaultEmbeddedPostgresFlagsFor(platform6) {
|
|
39448
39594
|
return platform6 === "win32" ? [] : ["-c", "shared_memory_type=mmap"];
|
|
39449
39595
|
}
|
|
39596
|
+
function resolveEmbeddedMaxConnections(configured, platform6 = process.platform) {
|
|
39597
|
+
if (typeof configured === "number" && Number.isInteger(configured)) {
|
|
39598
|
+
return Math.min(EMBEDDED_MAX_CONNECTIONS_MAX, Math.max(EMBEDDED_MAX_CONNECTIONS_MIN, configured));
|
|
39599
|
+
}
|
|
39600
|
+
return platform6 === "win32" ? DEFAULT_EMBEDDED_MAX_CONNECTIONS_WIN32 : DEFAULT_EMBEDDED_MAX_CONNECTIONS;
|
|
39601
|
+
}
|
|
39450
39602
|
function defaultEmbeddedDataDir() {
|
|
39451
39603
|
const home = process.env.HOME || process.env.USERPROFILE || process.cwd();
|
|
39452
39604
|
return join10(home, ".fusion", "embedded-postgres", "default");
|
|
@@ -39633,7 +39785,7 @@ function signalNumber(signal) {
|
|
|
39633
39785
|
return 0;
|
|
39634
39786
|
}
|
|
39635
39787
|
}
|
|
39636
|
-
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;
|
|
39788
|
+
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;
|
|
39637
39789
|
var init_embedded_lifecycle = __esm({
|
|
39638
39790
|
"../core/src/postgres/embedded-lifecycle.ts"() {
|
|
39639
39791
|
"use strict";
|
|
@@ -39664,6 +39816,10 @@ var init_embedded_lifecycle = __esm({
|
|
|
39664
39816
|
DEFAULT_EMBEDDED_PASSWORD = "password";
|
|
39665
39817
|
DEFAULT_EMBEDDED_DATABASE = "fusion";
|
|
39666
39818
|
DEFAULT_EMBEDDED_POSTGRES_FLAGS = defaultEmbeddedPostgresFlagsFor(process.platform);
|
|
39819
|
+
DEFAULT_EMBEDDED_MAX_CONNECTIONS = 500;
|
|
39820
|
+
DEFAULT_EMBEDDED_MAX_CONNECTIONS_WIN32 = 150;
|
|
39821
|
+
EMBEDDED_MAX_CONNECTIONS_MIN = 32;
|
|
39822
|
+
EMBEDDED_MAX_CONNECTIONS_MAX = 2e3;
|
|
39667
39823
|
DEFAULT_EMBEDDED_INITDB_FLAGS = [
|
|
39668
39824
|
"--encoding=UTF8",
|
|
39669
39825
|
"--locale=C"
|
|
@@ -39706,6 +39862,10 @@ var init_embedded_lifecycle = __esm({
|
|
|
39706
39862
|
* on a failure that is handled before the timeout fires.
|
|
39707
39863
|
*/
|
|
39708
39864
|
startTimer = null;
|
|
39865
|
+
windowsFatalDetector = new WindowsPostgresFatalDetector();
|
|
39866
|
+
recoveryAttempts = 0;
|
|
39867
|
+
recoveryInFlight = null;
|
|
39868
|
+
stopRequested = false;
|
|
39709
39869
|
constructor(opts) {
|
|
39710
39870
|
this.options = {
|
|
39711
39871
|
dataDir: opts.dataDir,
|
|
@@ -39720,6 +39880,15 @@ var init_embedded_lifecycle = __esm({
|
|
|
39720
39880
|
onError: opts.onError ?? ((err) => log4.error(String(err)))
|
|
39721
39881
|
};
|
|
39722
39882
|
}
|
|
39883
|
+
/**
|
|
39884
|
+
* Forward native process output to the existing sink, then schedule recovery
|
|
39885
|
+
* only for a confirmed owned Windows fatal shutdown sequence.
|
|
39886
|
+
*/
|
|
39887
|
+
forwardPostgresLog = (message) => {
|
|
39888
|
+
this.options.onLog(message);
|
|
39889
|
+
if (process.platform !== "win32" || !this.running || !this.ownsProcess) return;
|
|
39890
|
+
if (this.windowsFatalDetector.push(message)) void this.recoverWindowsFatalOnce();
|
|
39891
|
+
};
|
|
39723
39892
|
/** The configured or discovered port. Undefined until assigned (explicit or discovered in `start()`). */
|
|
39724
39893
|
getPort() {
|
|
39725
39894
|
return this.options.port ?? this.resolvedPort;
|
|
@@ -39816,11 +39985,19 @@ var init_embedded_lifecycle = __esm({
|
|
|
39816
39985
|
migrationUrlOverridden: false
|
|
39817
39986
|
};
|
|
39818
39987
|
}
|
|
39988
|
+
return this.startBounded();
|
|
39989
|
+
}
|
|
39990
|
+
/**
|
|
39991
|
+
* Start an owned postmaster with the same cancellation and timeout contract
|
|
39992
|
+
* used by public startup. Recovery calls this directly because it must not
|
|
39993
|
+
* join a stale pid file or allocate a new endpoint between pool reconnects.
|
|
39994
|
+
*/
|
|
39995
|
+
async startBounded(preferredPort) {
|
|
39819
39996
|
if (this.options.startTimeoutMs <= 0) {
|
|
39820
|
-
return this.startInternal();
|
|
39997
|
+
return this.startInternal(void 0, preferredPort);
|
|
39821
39998
|
}
|
|
39822
39999
|
const controller = new AbortController();
|
|
39823
|
-
const startAttempt = this.startInternal(controller.signal);
|
|
40000
|
+
const startAttempt = this.startInternal(controller.signal, preferredPort);
|
|
39824
40001
|
let timer2;
|
|
39825
40002
|
const timeout2 = new Promise((_resolve, reject2) => {
|
|
39826
40003
|
timer2 = setTimeout(() => {
|
|
@@ -39846,12 +40023,9 @@ var init_embedded_lifecycle = __esm({
|
|
|
39846
40023
|
this.startTimer = null;
|
|
39847
40024
|
}
|
|
39848
40025
|
}
|
|
39849
|
-
/**
|
|
39850
|
-
|
|
39851
|
-
|
|
39852
|
-
*/
|
|
39853
|
-
async startInternal(signal) {
|
|
39854
|
-
const port = this.options.port ?? await findFreePort();
|
|
40026
|
+
/** The actual start sequence, invoked only through {@link startBounded}. */
|
|
40027
|
+
async startInternal(signal, preferredPort) {
|
|
40028
|
+
const port = this.options.port ?? preferredPort ?? this.resolvedPort ?? await findFreePort();
|
|
39855
40029
|
if (signal?.aborted) throw new EmbeddedStartCancelledError(this.options.dataDir);
|
|
39856
40030
|
this.resolvedPort = port;
|
|
39857
40031
|
const alreadyInitialized = isDataDirInitialized(this.options.dataDir);
|
|
@@ -39865,7 +40039,7 @@ var init_embedded_lifecycle = __esm({
|
|
|
39865
40039
|
authMethod: "password",
|
|
39866
40040
|
initdbFlags: [...this.options.initdbFlags],
|
|
39867
40041
|
postgresFlags: [...this.options.postgresFlags],
|
|
39868
|
-
onLog: this.
|
|
40042
|
+
onLog: this.forwardPostgresLog,
|
|
39869
40043
|
onError: this.options.onError
|
|
39870
40044
|
});
|
|
39871
40045
|
this.pg = pg;
|
|
@@ -39897,7 +40071,7 @@ var init_embedded_lifecycle = __esm({
|
|
|
39897
40071
|
dataDir: this.options.dataDir,
|
|
39898
40072
|
port,
|
|
39899
40073
|
postgresFlags: this.options.postgresFlags,
|
|
39900
|
-
onLog: this.
|
|
40074
|
+
onLog: this.forwardPostgresLog,
|
|
39901
40075
|
onError: this.options.onError,
|
|
39902
40076
|
startTimeoutMs: this.options.startTimeoutMs,
|
|
39903
40077
|
signal,
|
|
@@ -39975,6 +40149,43 @@ var init_embedded_lifecycle = __esm({
|
|
|
39975
40149
|
migrationUrlOverridden: false
|
|
39976
40150
|
};
|
|
39977
40151
|
}
|
|
40152
|
+
/**
|
|
40153
|
+
* FNXC:PostgresEmbedded 2026-07-22-16:25:
|
|
40154
|
+
* A 0xC0000142 backend crash shuts down its whole PostgreSQL cluster. One
|
|
40155
|
+
* lifecycle-owned retry reuses the initialized directory and same resolved
|
|
40156
|
+
* port; joiners, stop/detach, and a second incident are deliberately inert.
|
|
40157
|
+
*/
|
|
40158
|
+
async recoverWindowsFatalOnce() {
|
|
40159
|
+
if (this.recoveryInFlight || this.recoveryAttempts >= 1 || this.stopRequested || !this.ownsProcess) return;
|
|
40160
|
+
this.recoveryAttempts += 1;
|
|
40161
|
+
this.recoveryInFlight = (async () => {
|
|
40162
|
+
this.options.onLog("embedded postgres: detected Windows DLL initialization shutdown; attempting one owned-cluster recovery");
|
|
40163
|
+
try {
|
|
40164
|
+
if (this.nonAdminHandle) await this.nonAdminHandle.stop();
|
|
40165
|
+
else await this.pg?.stop();
|
|
40166
|
+
this.pg = null;
|
|
40167
|
+
this.nonAdminHandle = null;
|
|
40168
|
+
this.running = false;
|
|
40169
|
+
runningInstances.delete(this.options.dataDir);
|
|
40170
|
+
if (this.stopRequested || !this.ownsProcess) return;
|
|
40171
|
+
const recoveryPort = this.resolvedPort;
|
|
40172
|
+
if (recoveryPort === void 0) {
|
|
40173
|
+
throw new Error("embedded postgres: recovery lost its resolved port");
|
|
40174
|
+
}
|
|
40175
|
+
await this.startBounded(recoveryPort);
|
|
40176
|
+
this.options.onLog("embedded postgres: Windows owned-cluster recovery completed; existing pools may reconnect");
|
|
40177
|
+
} catch (error2) {
|
|
40178
|
+
this.running = false;
|
|
40179
|
+
runningInstances.delete(this.options.dataDir);
|
|
40180
|
+
this.options.onError(
|
|
40181
|
+
`embedded postgres: Windows DLL initialization recovery failed after one retry; restart Fusion and inspect the System log. ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
40182
|
+
);
|
|
40183
|
+
} finally {
|
|
40184
|
+
this.recoveryInFlight = null;
|
|
40185
|
+
}
|
|
40186
|
+
})();
|
|
40187
|
+
await this.recoveryInFlight;
|
|
40188
|
+
}
|
|
39978
40189
|
async settleCancelledStart(pg) {
|
|
39979
40190
|
if (this.nonAdminHandle) {
|
|
39980
40191
|
try {
|
|
@@ -40121,6 +40332,7 @@ var init_embedded_lifecycle = __esm({
|
|
|
40121
40332
|
*/
|
|
40122
40333
|
detachWithoutStop() {
|
|
40123
40334
|
this.uninstallShutdownHook();
|
|
40335
|
+
this.nonAdminHandle?.stopMonitoring();
|
|
40124
40336
|
this.pg = null;
|
|
40125
40337
|
this.nonAdminHandle = null;
|
|
40126
40338
|
this.running = false;
|
|
@@ -40128,6 +40340,7 @@ var init_embedded_lifecycle = __esm({
|
|
|
40128
40340
|
runningInstances.delete(this.options.dataDir);
|
|
40129
40341
|
}
|
|
40130
40342
|
async stop() {
|
|
40343
|
+
this.stopRequested = true;
|
|
40131
40344
|
this.uninstallShutdownHook();
|
|
40132
40345
|
if (!this.ownsProcess) {
|
|
40133
40346
|
this.running = false;
|
|
@@ -52152,10 +52365,10 @@ async function bootSchemaBackendOnce(options, bypassProjectIsolation = false) {
|
|
|
52152
52365
|
let resolvedBackend = backend;
|
|
52153
52366
|
let embeddedDataDir = null;
|
|
52154
52367
|
if (backend.mode === "embedded") {
|
|
52155
|
-
const { EmbeddedPostgresLifecycle: EmbeddedPostgresLifecycle2, defaultEmbeddedDataDir: defaultEmbeddedDataDir2, DEFAULT_EMBEDDED_DATABASE: DEFAULT_EMBEDDED_DATABASE2 } = await Promise.resolve().then(() => (init_embedded_lifecycle(), embedded_lifecycle_exports));
|
|
52368
|
+
const { EmbeddedPostgresLifecycle: EmbeddedPostgresLifecycle2, defaultEmbeddedDataDir: defaultEmbeddedDataDir2, DEFAULT_EMBEDDED_DATABASE: DEFAULT_EMBEDDED_DATABASE2, resolveEmbeddedMaxConnections: resolveEmbeddedMaxConnections2 } = await Promise.resolve().then(() => (init_embedded_lifecycle(), embedded_lifecycle_exports));
|
|
52156
52369
|
const { GlobalSettingsStore: GlobalSettingsStore2 } = await Promise.resolve().then(() => (init_global_settings(), global_settings_exports));
|
|
52157
52370
|
const configuredMaxConnections = process.env.VITEST === "true" && !options.globalSettingsDir ? void 0 : (await new GlobalSettingsStore2(options.globalSettingsDir).getSettings()).embeddedPostgresMaxConnections;
|
|
52158
|
-
const maxConnections =
|
|
52371
|
+
const maxConnections = resolveEmbeddedMaxConnections2(configuredMaxConnections);
|
|
52159
52372
|
const dataDir = resolve8(options.embeddedDataDir ?? defaultEmbeddedDataDir2());
|
|
52160
52373
|
embeddedDataDir = dataDir;
|
|
52161
52374
|
log5.log(`startup-factory: starting embedded PostgreSQL (data dir ${dataDir})`);
|
|
@@ -52392,7 +52605,20 @@ async function createTaskStoreForBackend(options) {
|
|
|
52392
52605
|
try {
|
|
52393
52606
|
const fusionDir = join16(rootDir, ".fusion");
|
|
52394
52607
|
const legacySqlitePath = join16(fusionDir, "fusion.db");
|
|
52395
|
-
|
|
52608
|
+
const {
|
|
52609
|
+
migrateSqliteToPostgres: migrateSqliteToPostgres2,
|
|
52610
|
+
defaultMigrationSources: defaultMigrationSources2,
|
|
52611
|
+
formatMigrationProgress: formatMigrationProgress2,
|
|
52612
|
+
isSqliteMigrationComplete: isSqliteMigrationComplete2,
|
|
52613
|
+
completeSqliteMigration: completeSqliteMigration2,
|
|
52614
|
+
recordSqliteMigrationComplete: recordSqliteMigrationComplete2,
|
|
52615
|
+
CENTRAL_SQLITE_MIGRATION_KEY: CENTRAL_SQLITE_MIGRATION_KEY2
|
|
52616
|
+
} = await Promise.resolve().then(() => (init_sqlite_migrator(), sqlite_migrator_exports));
|
|
52617
|
+
const fallbackProjectId = fallbackProjectIdForRoot(rootDir);
|
|
52618
|
+
let migrationProjectId = options.projectId ?? await lookupRegisteredProjectIdByPath(connections.migration, rootDir) ?? fallbackProjectId;
|
|
52619
|
+
let migrationKey = `project:${migrationProjectId}`;
|
|
52620
|
+
let migrationComplete = await isSqliteMigrationComplete2(connections.migration, migrationKey);
|
|
52621
|
+
if (!migrationComplete && existsSync11(legacySqlitePath)) {
|
|
52396
52622
|
let globalDir = options.globalSettingsDir;
|
|
52397
52623
|
if (!globalDir) {
|
|
52398
52624
|
try {
|
|
@@ -52402,21 +52628,24 @@ async function createTaskStoreForBackend(options) {
|
|
|
52402
52628
|
globalDir = void 0;
|
|
52403
52629
|
}
|
|
52404
52630
|
}
|
|
52405
|
-
let
|
|
52631
|
+
let resolvedFromLegacyCentral = false;
|
|
52632
|
+
const centralMigrationComplete = await isSqliteMigrationComplete2(
|
|
52633
|
+
connections.migration,
|
|
52634
|
+
CENTRAL_SQLITE_MIGRATION_KEY2
|
|
52635
|
+
);
|
|
52406
52636
|
const legacyCentralPath = globalDir ? join16(globalDir, "fusion-central.db") : void 0;
|
|
52407
|
-
if (!migrationProjectId && legacyCentralPath && existsSync11(legacyCentralPath) && isValidSqliteDatabaseFile(legacyCentralPath)) {
|
|
52637
|
+
if (!centralMigrationComplete && migrationProjectId === fallbackProjectId && legacyCentralPath && existsSync11(legacyCentralPath) && isValidSqliteDatabaseFile(legacyCentralPath)) {
|
|
52408
52638
|
const { DatabaseSync: DatabaseSync2 } = await Promise.resolve().then(() => (init_sqlite_adapter(), sqlite_adapter_exports));
|
|
52409
52639
|
const legacyCentral = new DatabaseSync2(legacyCentralPath, { readOnly: true });
|
|
52410
52640
|
try {
|
|
52411
52641
|
const row = legacyCentral.prepare(`SELECT id FROM projects WHERE path = ? LIMIT 1`).get(rootDir);
|
|
52412
|
-
migrationProjectId = row?.id;
|
|
52642
|
+
migrationProjectId = row?.id ?? migrationProjectId;
|
|
52643
|
+
resolvedFromLegacyCentral = Boolean(row?.id);
|
|
52413
52644
|
} catch {
|
|
52414
52645
|
} finally {
|
|
52415
52646
|
legacyCentral.close();
|
|
52416
52647
|
}
|
|
52417
52648
|
}
|
|
52418
|
-
const fallbackProjectId = fallbackProjectIdForRoot(rootDir);
|
|
52419
|
-
migrationProjectId ??= fallbackProjectId;
|
|
52420
52649
|
if (migrationProjectId !== fallbackProjectId) {
|
|
52421
52650
|
try {
|
|
52422
52651
|
await rekeyFallbackProjectPartition(connections.migration, fallbackProjectId, migrationProjectId);
|
|
@@ -52432,14 +52661,9 @@ async function createTaskStoreForBackend(options) {
|
|
|
52432
52661
|
}
|
|
52433
52662
|
}
|
|
52434
52663
|
}
|
|
52435
|
-
|
|
52436
|
-
|
|
52437
|
-
const migrationComplete = await isSqliteMigrationComplete2(connections.migration, migrationKey);
|
|
52664
|
+
migrationKey = `project:${migrationProjectId}`;
|
|
52665
|
+
migrationComplete = await isSqliteMigrationComplete2(connections.migration, migrationKey);
|
|
52438
52666
|
if (!migrationComplete && isValidSqliteDatabaseFile(legacySqlitePath)) {
|
|
52439
|
-
const centralMigrationComplete = await isSqliteMigrationComplete2(
|
|
52440
|
-
connections.migration,
|
|
52441
|
-
CENTRAL_SQLITE_MIGRATION_KEY2
|
|
52442
|
-
);
|
|
52443
52667
|
const sources = defaultMigrationSources2(fusionDir, globalDir ?? join16(fusionDir, "__no-global-dir__")).filter((source) => !centralMigrationComplete || source.pgSchema !== "central").filter((source) => existsSync11(source.sqlitePath) && isValidSqliteDatabaseFile(source.sqlitePath));
|
|
52444
52668
|
if (sources.length > 0) {
|
|
52445
52669
|
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)`);
|
|
@@ -52474,6 +52698,12 @@ async function createTaskStoreForBackend(options) {
|
|
|
52474
52698
|
rootDir
|
|
52475
52699
|
});
|
|
52476
52700
|
}
|
|
52701
|
+
if (resolvedFromLegacyCentral) {
|
|
52702
|
+
const registeredId = await lookupRegisteredProjectIdByPath(connections.migration, rootDir);
|
|
52703
|
+
if (registeredId !== migrationProjectId) {
|
|
52704
|
+
throw new Error(`legacy project identity ${migrationProjectId} was not materialized for ${rootDir} before migration completion`);
|
|
52705
|
+
}
|
|
52706
|
+
}
|
|
52477
52707
|
await completeSqliteMigration2(connections.migration, migrationKey);
|
|
52478
52708
|
if (sources.some((source) => source.pgSchema === "central")) {
|
|
52479
52709
|
await recordSqliteMigrationComplete2(
|
|
@@ -52491,14 +52721,9 @@ async function createTaskStoreForBackend(options) {
|
|
|
52491
52721
|
log5.log(`startup-factory: SQLite \u2192 PostgreSQL auto-migration complete (${migratedRows} row(s) across ${report.tables.length} table(s))`);
|
|
52492
52722
|
}
|
|
52493
52723
|
}
|
|
52494
|
-
if (isValidSqliteDatabaseFile(legacySqlitePath)) {
|
|
52495
|
-
await migrateLegacyProjectPluginRows2(
|
|
52496
|
-
connections.migration,
|
|
52497
|
-
legacySqlitePath,
|
|
52498
|
-
rootDir
|
|
52499
|
-
);
|
|
52500
|
-
}
|
|
52501
52724
|
}
|
|
52725
|
+
const { migrateLegacyProjectPluginRows: migrateLegacyProjectPluginRows2 } = await Promise.resolve().then(() => (init_sqlite_migrator(), sqlite_migrator_exports));
|
|
52726
|
+
await migrateLegacyProjectPluginRows2(connections.migration, legacySqlitePath, rootDir);
|
|
52502
52727
|
} catch (err) {
|
|
52503
52728
|
await connections.close().catch(() => void 0);
|
|
52504
52729
|
await stopEmbeddedRuntime(
|
|
@@ -62165,6 +62390,7 @@ var init_persistence = __esm({
|
|
|
62165
62390
|
defineTaskColumn("overlapBlockedBy", (task) => task.overlapBlockedBy ?? null),
|
|
62166
62391
|
defineTaskColumn("paused", (task) => task.paused ? 1 : 0),
|
|
62167
62392
|
defineTaskColumn("pausedReason", (task) => task.pausedReason ?? null),
|
|
62393
|
+
defineTaskColumn("wedgeNotification", (task) => toJsonNullable(task.wedgeNotification)),
|
|
62168
62394
|
defineTaskColumn("userPaused", (task) => task.userPaused ? 1 : 0),
|
|
62169
62395
|
defineTaskColumn("baseBranch", (task) => task.baseBranch ?? null),
|
|
62170
62396
|
defineTaskColumn("branch", (task) => task.branch ?? null),
|
|
@@ -62574,6 +62800,7 @@ function rowToTask(row) {
|
|
|
62574
62800
|
overlapBlockedBy: row.overlapBlockedBy || void 0,
|
|
62575
62801
|
paused: row.paused ? true : void 0,
|
|
62576
62802
|
pausedReason: row.pausedReason || void 0,
|
|
62803
|
+
wedgeNotification: fromJson(row.wedgeNotification) ?? void 0,
|
|
62577
62804
|
userPaused: row.userPaused ? true : void 0,
|
|
62578
62805
|
baseBranch: row.baseBranch || void 0,
|
|
62579
62806
|
executionStartBranch: row.executionStartBranch || void 0,
|
|
@@ -84829,6 +85056,7 @@ function getTaskSelectClauseImpl2(store, slim, tableAlias) {
|
|
|
84829
85056
|
"overlapBlockedBy",
|
|
84830
85057
|
"paused",
|
|
84831
85058
|
"pausedReason",
|
|
85059
|
+
"wedgeNotification",
|
|
84832
85060
|
"userPaused",
|
|
84833
85061
|
"baseBranch",
|
|
84834
85062
|
"branch",
|
|
@@ -91547,7 +91775,7 @@ var init_async_eval_store = __esm({
|
|
|
91547
91775
|
}
|
|
91548
91776
|
});
|
|
91549
91777
|
|
|
91550
|
-
// ../core/src/task-store/
|
|
91778
|
+
// ../core/src/task-store/project-store-ops.ts
|
|
91551
91779
|
import { randomUUID as randomUUID33 } from "node:crypto";
|
|
91552
91780
|
import { mkdir as mkdir13, writeFile as writeFile12 } from "node:fs/promises";
|
|
91553
91781
|
import { join as join33 } from "node:path";
|
|
@@ -92415,8 +92643,8 @@ function getEvalStoreImpl(store) {
|
|
|
92415
92643
|
return store.evalStore;
|
|
92416
92644
|
}
|
|
92417
92645
|
var workflowDefinitionBeforeInsertForTesting;
|
|
92418
|
-
var
|
|
92419
|
-
"../core/src/task-store/
|
|
92646
|
+
var init_project_store_ops = __esm({
|
|
92647
|
+
"../core/src/task-store/project-store-ops.ts"() {
|
|
92420
92648
|
"use strict";
|
|
92421
92649
|
init_store();
|
|
92422
92650
|
init_errors3();
|
|
@@ -99116,6 +99344,11 @@ async function updateTaskUnlockedImpl(store, id, updates, runContext) {
|
|
|
99116
99344
|
} else if (updates.pausedReason !== void 0) {
|
|
99117
99345
|
task.pausedReason = updates.pausedReason;
|
|
99118
99346
|
}
|
|
99347
|
+
if (updates.wedgeNotification === null) {
|
|
99348
|
+
task.wedgeNotification = void 0;
|
|
99349
|
+
} else if (updates.wedgeNotification !== void 0) {
|
|
99350
|
+
task.wedgeNotification = updates.wedgeNotification;
|
|
99351
|
+
}
|
|
99119
99352
|
if (updates.tokenBudgetSoftAlertedAt === null) {
|
|
99120
99353
|
task.tokenBudgetSoftAlertedAt = void 0;
|
|
99121
99354
|
} else if (updates.tokenBudgetSoftAlertedAt !== void 0) {
|
|
@@ -99778,7 +100011,7 @@ var init_store = __esm({
|
|
|
99778
100011
|
init_task_store_helpers();
|
|
99779
100012
|
init_task_row_mappers();
|
|
99780
100013
|
init_task_mutation_ops();
|
|
99781
|
-
|
|
100014
|
+
init_project_store_ops();
|
|
99782
100015
|
init_workflow_integrity();
|
|
99783
100016
|
init_branch_group_ops();
|
|
99784
100017
|
init_archive_lifecycle_2();
|
|
@@ -100642,6 +100875,26 @@ var init_store = __esm({
|
|
|
100642
100875
|
async updateTask(id, updates, runContext) {
|
|
100643
100876
|
return updateTaskImpl(this, id, updates, runContext);
|
|
100644
100877
|
}
|
|
100878
|
+
/**
|
|
100879
|
+
* Atomically opens or resolves the durable task-wedge episode. The task lock is
|
|
100880
|
+
* shared by SQLite and PostgreSQL persistence writes, so concurrent engine
|
|
100881
|
+
* observers cannot allocate two episodes for one active normalized reason.
|
|
100882
|
+
*/
|
|
100883
|
+
async claimTaskWedgeNotificationEpisode(taskId, reasonKey) {
|
|
100884
|
+
let result = { claimed: false };
|
|
100885
|
+
await this.updateTaskAtomic(taskId, (current) => {
|
|
100886
|
+
const prior = current.wedgeNotification;
|
|
100887
|
+
if (reasonKey === null) {
|
|
100888
|
+
if (!prior || prior.status === "resolved") return null;
|
|
100889
|
+
return { wedgeNotification: { ...prior, status: "resolved", transitionedAt: (/* @__PURE__ */ new Date()).toISOString() } };
|
|
100890
|
+
}
|
|
100891
|
+
if (prior?.status === "active" && prior.reasonKey === reasonKey) return null;
|
|
100892
|
+
const episodeId = randomUUID37();
|
|
100893
|
+
result = { episodeId, claimed: true };
|
|
100894
|
+
return { wedgeNotification: { reasonKey, episodeId, status: "active", transitionedAt: (/* @__PURE__ */ new Date()).toISOString() } };
|
|
100895
|
+
});
|
|
100896
|
+
return result;
|
|
100897
|
+
}
|
|
100645
100898
|
async claimNextToolFailureRetry(taskId, expectedCursor, maxRetries) {
|
|
100646
100899
|
return claimNextToolFailureRetryImpl(this, taskId, expectedCursor, maxRetries);
|
|
100647
100900
|
}
|
|
@@ -172700,6 +172953,7 @@ var init_ntfy_provider = __esm({
|
|
|
172700
172953
|
"in-review",
|
|
172701
172954
|
"merged",
|
|
172702
172955
|
"failed",
|
|
172956
|
+
"task-wedged",
|
|
172703
172957
|
"awaiting-approval",
|
|
172704
172958
|
"awaiting-user-review",
|
|
172705
172959
|
"planning-awaiting-input",
|
|
@@ -172803,6 +173057,15 @@ var init_ntfy_provider = __esm({
|
|
|
172803
173057
|
message: `Task "${identifier}" has failed and needs attention`,
|
|
172804
173058
|
priority: "high"
|
|
172805
173059
|
},
|
|
173060
|
+
"task-wedged": {
|
|
173061
|
+
title: `Task ${taskId} needs operator action`,
|
|
173062
|
+
message: [
|
|
173063
|
+
`Task "${identifier}" is wedged${typeof payload.metadata?.reason === "string" ? `: ${payload.metadata.reason}` : " and needs operator action"}`,
|
|
173064
|
+
typeof payload.metadata?.gate === "string" ? `Gate: ${payload.metadata.gate}` : null,
|
|
173065
|
+
typeof payload.metadata?.action === "string" ? `Recommended action: ${payload.metadata.action}` : null
|
|
173066
|
+
].filter((part) => part !== null).join("\n"),
|
|
173067
|
+
priority: "high"
|
|
173068
|
+
},
|
|
172806
173069
|
"awaiting-approval": {
|
|
172807
173070
|
title: payload.metadata?.awaitingApprovalReason === "plan-review-replan-cap" ? `Plan Review cap reached for ${taskId}` : `Plan needs approval for ${taskId}`,
|
|
172808
173071
|
/*
|
|
@@ -173157,6 +173420,132 @@ var init_transient_merge_error_classifier = __esm({
|
|
|
173157
173420
|
}
|
|
173158
173421
|
});
|
|
173159
173422
|
|
|
173423
|
+
// ../engine/src/notification/task-wedge-notification.ts
|
|
173424
|
+
function describeSelfHealingNoActionWedge(task, stage, metadata) {
|
|
173425
|
+
const description = SELF_HEALING_NO_ACTIONS[stage];
|
|
173426
|
+
if (!description || task.userPaused || task.paused || task.autoMerge === false) return null;
|
|
173427
|
+
const proof = metadata ?? {};
|
|
173428
|
+
if (proof.taskActive === true || proof.hasExecutingTaskLock === true || proof.mergePending === true) return null;
|
|
173429
|
+
if (Array.isArray(proof.livePaths) && proof.livePaths.length > 0) return null;
|
|
173430
|
+
return { reasonKey: `self-healing-no-action:${stage}`, ...description };
|
|
173431
|
+
}
|
|
173432
|
+
function describeTaskWedge(task) {
|
|
173433
|
+
const error2 = task.error ?? "";
|
|
173434
|
+
if (task.pausedReason === "completed-blocked") {
|
|
173435
|
+
return { reasonKey: "completion-blocked", reason: "Completed work is blocked from advancing to review.", action: "Clear the blocker or reset the task to todo." };
|
|
173436
|
+
}
|
|
173437
|
+
if (task.pausedReason === "error-retry-exhausted") {
|
|
173438
|
+
return { reasonKey: "heartbeat-retry-exhausted", reason: "The assigned agent exhausted its heartbeat recovery budget.", action: "Repair the agent configuration, then retry the task." };
|
|
173439
|
+
}
|
|
173440
|
+
if (task.pausedReason === "error-unrecoverable") {
|
|
173441
|
+
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." };
|
|
173442
|
+
}
|
|
173443
|
+
const pausedDescriptors = {
|
|
173444
|
+
"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." },
|
|
173445
|
+
"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." },
|
|
173446
|
+
"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." },
|
|
173447
|
+
"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." },
|
|
173448
|
+
"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." },
|
|
173449
|
+
"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." },
|
|
173450
|
+
"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." }
|
|
173451
|
+
};
|
|
173452
|
+
if (task.pausedReason && pausedDescriptors[task.pausedReason]) return pausedDescriptors[task.pausedReason];
|
|
173453
|
+
if (task.status !== "failed") return null;
|
|
173454
|
+
if (error2.startsWith("EXECUTION_DISPATCH_LOOP_EXHAUSTED")) {
|
|
173455
|
+
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." };
|
|
173456
|
+
}
|
|
173457
|
+
if (error2.includes("tool failure") || error2.includes("Tool failure")) {
|
|
173458
|
+
return { reasonKey: "tool-failure-retry-exhausted", reason: "Execution tool-failure retries were exhausted.", action: "Inspect the failing tool and retry the task." };
|
|
173459
|
+
}
|
|
173460
|
+
if (/escalat(?:ion|ed).*exhaust|exhaust.*escalat/i.test(error2)) {
|
|
173461
|
+
return { reasonKey: "execution-escalation-exhausted", reason: "The alternate execution escalation was exhausted.", action: "Inspect the failure and retry or rescope the task." };
|
|
173462
|
+
}
|
|
173463
|
+
if (/review.*(?:retry|rework|revision).*(?:exhaust|limit)|(?:exhaust|limit).*review/i.test(error2)) {
|
|
173464
|
+
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." };
|
|
173465
|
+
}
|
|
173466
|
+
const gate = error2.match(/check:([\w:-]+)/)?.[1];
|
|
173467
|
+
if (gate) {
|
|
173468
|
+
return {
|
|
173469
|
+
reasonKey: `merge-blocked:${gate}`,
|
|
173470
|
+
reason: `Merge is blocked by the check:${gate} verification gate.`,
|
|
173471
|
+
action: gate === "changeset-format" ? "Fix the changeset format, then retry the task." : "Fix the failing gate or reset the task to todo and retry.",
|
|
173472
|
+
gate: `check:${gate}`
|
|
173473
|
+
};
|
|
173474
|
+
}
|
|
173475
|
+
if (error2.startsWith("BLOCKED:")) {
|
|
173476
|
+
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." };
|
|
173477
|
+
}
|
|
173478
|
+
if (/merge.*(?:blocked|verification|gate)|(?:verification|gate).*(?:failed|exhaust)/i.test(error2)) {
|
|
173479
|
+
return { reasonKey: "merge-blocked", reason: "Merge verification cannot progress without operator action.", action: "Fix the failing verification, then retry the task." };
|
|
173480
|
+
}
|
|
173481
|
+
if (!error2 && (task.mergeRetries ?? 0) < 3) return null;
|
|
173482
|
+
return {
|
|
173483
|
+
reasonKey: "terminal-failed",
|
|
173484
|
+
reason: "The task entered a terminal failed state and needs operator intervention.",
|
|
173485
|
+
action: "Inspect the task error, fix the underlying issue, then retry or reset to todo."
|
|
173486
|
+
};
|
|
173487
|
+
}
|
|
173488
|
+
var SELF_HEALING_NO_ACTIONS;
|
|
173489
|
+
var init_task_wedge_notification = __esm({
|
|
173490
|
+
"../engine/src/notification/task-wedge-notification.ts"() {
|
|
173491
|
+
"use strict";
|
|
173492
|
+
SELF_HEALING_NO_ACTIONS = {
|
|
173493
|
+
"reclaim-pr-conflict": {
|
|
173494
|
+
reason: "Self-healing could not reclaim a stalled pull-request conflict.",
|
|
173495
|
+
action: "Inspect the branch conflict and retry or reset the task to todo."
|
|
173496
|
+
},
|
|
173497
|
+
"reclaim-self-owned-branch-conflict": {
|
|
173498
|
+
reason: "Self-healing could not recover a stalled branch conflict.",
|
|
173499
|
+
action: "Inspect the branch conflict and retry or reset the task to todo."
|
|
173500
|
+
},
|
|
173501
|
+
"reconcile-in-review-unmet-dependencies": {
|
|
173502
|
+
reason: "Unmet dependencies are preventing review from progressing.",
|
|
173503
|
+
action: "Resolve the dependencies, then retry or reset the task to todo."
|
|
173504
|
+
},
|
|
173505
|
+
"reconcile-dependency-blocking-lease": {
|
|
173506
|
+
reason: "A dependency-blocking task lease could not be safely reclaimed.",
|
|
173507
|
+
action: "Inspect the task owner and lease, then retry or reset the task to todo."
|
|
173508
|
+
},
|
|
173509
|
+
"auto-rebound-paused-scope-decay": {
|
|
173510
|
+
reason: "A paused task with stale scope could not be safely resumed.",
|
|
173511
|
+
action: "Inspect the task scope and retry or reset the task to todo."
|
|
173512
|
+
},
|
|
173513
|
+
"stuck-merge-deadlock": {
|
|
173514
|
+
reason: "A merge deadlock needs operator intervention.",
|
|
173515
|
+
action: "Inspect merge ownership and retry or reset the task to todo."
|
|
173516
|
+
},
|
|
173517
|
+
"missing-worktree-merge-active": {
|
|
173518
|
+
reason: "An active merge has an unusable worktree and could not be recovered.",
|
|
173519
|
+
action: "Repair the worktree or reset the task to todo and retry."
|
|
173520
|
+
},
|
|
173521
|
+
"missing-worktree-review": {
|
|
173522
|
+
reason: "Review has an unusable worktree and could not be recovered.",
|
|
173523
|
+
action: "Repair the worktree or reset the task to todo and retry."
|
|
173524
|
+
},
|
|
173525
|
+
"finalize-no-op-review": {
|
|
173526
|
+
reason: "A no-op review task could not be safely finalized.",
|
|
173527
|
+
action: "Inspect merge state and retry or reset the task to todo."
|
|
173528
|
+
},
|
|
173529
|
+
"stale-incomplete-review": {
|
|
173530
|
+
reason: "Incomplete review work could not be safely resumed.",
|
|
173531
|
+
action: "Inspect incomplete steps and retry or reset the task to todo."
|
|
173532
|
+
},
|
|
173533
|
+
"ghost-review": {
|
|
173534
|
+
reason: "A review task has no recoverable workflow owner.",
|
|
173535
|
+
action: "Inspect workflow state and retry or reset the task to todo."
|
|
173536
|
+
},
|
|
173537
|
+
"no-progress-no-task-done": {
|
|
173538
|
+
reason: "Execution stopped without progress and could not be safely requeued.",
|
|
173539
|
+
action: "Inspect the task worktree and retry or reset the task to todo."
|
|
173540
|
+
},
|
|
173541
|
+
"partial-progress-no-task-done": {
|
|
173542
|
+
reason: "Partial execution progress could not be safely resumed.",
|
|
173543
|
+
action: "Inspect partial work and retry or reset the task to todo."
|
|
173544
|
+
}
|
|
173545
|
+
};
|
|
173546
|
+
}
|
|
173547
|
+
});
|
|
173548
|
+
|
|
173160
173549
|
// ../engine/src/notification/notification-service.ts
|
|
173161
173550
|
var NotificationService;
|
|
173162
173551
|
var init_notification_service = __esm({
|
|
@@ -173168,6 +173557,7 @@ var init_notification_service = __esm({
|
|
|
173168
173557
|
init_transient_merge_error_classifier();
|
|
173169
173558
|
init_ntfy_provider();
|
|
173170
173559
|
init_webhook_provider();
|
|
173560
|
+
init_task_wedge_notification();
|
|
173171
173561
|
NotificationService = class {
|
|
173172
173562
|
constructor(store, options = {}) {
|
|
173173
173563
|
this.store = store;
|
|
@@ -173198,6 +173588,8 @@ var init_notification_service = __esm({
|
|
|
173198
173588
|
failureNotificationSuppressedCount = 0;
|
|
173199
173589
|
failureNotificationDelayMs = 6e4;
|
|
173200
173590
|
failureNotificationMode = "sticky-only";
|
|
173591
|
+
/** Compatibility fallback for lightweight test stores without the durable TaskStore CAS. */
|
|
173592
|
+
activeWedgeReasons = /* @__PURE__ */ new Map();
|
|
173201
173593
|
attachChatStore(chatStore) {
|
|
173202
173594
|
if (this.chatStore && this.chatStore !== chatStore) {
|
|
173203
173595
|
this.detachChatStoreListener(this.chatStore);
|
|
@@ -173309,6 +173701,10 @@ var init_notification_service = __esm({
|
|
|
173309
173701
|
}
|
|
173310
173702
|
}
|
|
173311
173703
|
handleTaskUpdated = (task) => {
|
|
173704
|
+
const transientFailure = task.status === "failed" ? classifyTransientMergeError(task.error) : null;
|
|
173705
|
+
const wedge = transientFailure ? null : describeTaskWedge(task);
|
|
173706
|
+
if (wedge) this.cancelPendingFailureNotification(task.id, "classified-terminal-wedge");
|
|
173707
|
+
if (!transientFailure) void this.maybeNotifyTaskWedge(task, wedge);
|
|
173312
173708
|
void this.maybeSuppressTransientFailedNotification(task, `status=${task.status ?? "undefined"}`);
|
|
173313
173709
|
if (task.status === "awaiting-approval") {
|
|
173314
173710
|
void this.writeAwaitingApprovalMailboxMessage(task);
|
|
@@ -173319,7 +173715,7 @@ var init_notification_service = __esm({
|
|
|
173319
173715
|
if (!this.notificationsEnabled) {
|
|
173320
173716
|
return;
|
|
173321
173717
|
}
|
|
173322
|
-
if (task.status === "failed") {
|
|
173718
|
+
if (task.status === "failed" && !wedge) {
|
|
173323
173719
|
const transientClass = classifyTransientMergeError(task.error);
|
|
173324
173720
|
if (transientClass) {
|
|
173325
173721
|
this.failureNotificationSuppressedCount += 1;
|
|
@@ -173405,6 +173801,69 @@ var init_notification_service = __esm({
|
|
|
173405
173801
|
);
|
|
173406
173802
|
}
|
|
173407
173803
|
}
|
|
173804
|
+
/*
|
|
173805
|
+
FNXC:TaskWedgeNotifications 2026-07-22-12:00:
|
|
173806
|
+
Terminal task updates are the shared seam for merger, executor, heartbeat, and
|
|
173807
|
+
self-healing parks. Clear only on a non-wedge lifecycle update, allowing a
|
|
173808
|
+
resolved-then-reparked reason to begin a new actionable episode.
|
|
173809
|
+
*/
|
|
173810
|
+
/** Delivers a self-healing no-action escalation through the durable wedge episode seam. */
|
|
173811
|
+
async notifyTaskWedge(task, descriptor) {
|
|
173812
|
+
await this.maybeNotifyTaskWedge(task, descriptor);
|
|
173813
|
+
}
|
|
173814
|
+
async maybeNotifyTaskWedge(task, suppliedDescriptor) {
|
|
173815
|
+
const descriptor = suppliedDescriptor ?? describeTaskWedge(task);
|
|
173816
|
+
let episode;
|
|
173817
|
+
if (!descriptor) {
|
|
173818
|
+
const isActiveSelfHealingNoAction = task.wedgeNotification?.status === "active" && task.wedgeNotification.reasonKey.startsWith("self-healing-no-action:");
|
|
173819
|
+
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 ?? "");
|
|
173820
|
+
if (hasProgressed) {
|
|
173821
|
+
this.activeWedgeReasons.delete(task.id);
|
|
173822
|
+
await this.store.claimTaskWedgeNotificationEpisode?.(task.id, null);
|
|
173823
|
+
}
|
|
173824
|
+
return;
|
|
173825
|
+
}
|
|
173826
|
+
if (this.store.claimTaskWedgeNotificationEpisode) {
|
|
173827
|
+
const claim = await this.store.claimTaskWedgeNotificationEpisode(task.id, descriptor.reasonKey);
|
|
173828
|
+
if (!claim.claimed || !claim.episodeId) return;
|
|
173829
|
+
episode = claim.episodeId;
|
|
173830
|
+
} else {
|
|
173831
|
+
if (this.activeWedgeReasons.get(task.id) === descriptor.reasonKey) return;
|
|
173832
|
+
this.activeWedgeReasons.set(task.id, descriptor.reasonKey);
|
|
173833
|
+
episode = `${task.id}:${descriptor.reasonKey}:${task.updatedAt}`;
|
|
173834
|
+
}
|
|
173835
|
+
const link = buildNtfyClickUrl({ dashboardHost: this.dashboardHost, projectId: this.options.projectId, taskId: task.id });
|
|
173836
|
+
const content = [
|
|
173837
|
+
`**${formatTaskIdentifier(task)} needs operator action**`,
|
|
173838
|
+
"",
|
|
173839
|
+
descriptor.reason,
|
|
173840
|
+
...descriptor.gate ? [`Gate: \`${descriptor.gate}\`.`] : [],
|
|
173841
|
+
`Recommended action: ${descriptor.action}`,
|
|
173842
|
+
...link ? ["", `[Open ${task.id}](${link})`] : []
|
|
173843
|
+
].join("\n");
|
|
173844
|
+
const payload = {
|
|
173845
|
+
taskId: task.id,
|
|
173846
|
+
taskTitle: task.title,
|
|
173847
|
+
taskDescription: task.description,
|
|
173848
|
+
event: "task-wedged",
|
|
173849
|
+
// Bounded descriptor text is operator-facing provider content, never audit metadata.
|
|
173850
|
+
metadata: { wedgeReason: descriptor.reasonKey, reason: descriptor.reason, action: descriptor.action, ...descriptor.gate ? { gate: descriptor.gate } : {}, notificationDedupeKey: `task-wedge:${episode}` }
|
|
173851
|
+
};
|
|
173852
|
+
void this.dispatch("task-wedged", payload);
|
|
173853
|
+
try {
|
|
173854
|
+
await this.options.messageStore?.sendMessageOnce?.({
|
|
173855
|
+
fromId: "system",
|
|
173856
|
+
fromType: "system",
|
|
173857
|
+
toId: DASHBOARD_USER_ID,
|
|
173858
|
+
toType: "user",
|
|
173859
|
+
type: "system",
|
|
173860
|
+
content,
|
|
173861
|
+
metadata: { taskId: task.id, kind: "task-wedge", wedgeReason: descriptor.reasonKey, ...descriptor.gate ? { gate: descriptor.gate } : {} }
|
|
173862
|
+
}, `task-wedge:${episode}`);
|
|
173863
|
+
} catch (error2) {
|
|
173864
|
+
schedulerLog.log(`[notify] ${task.id} wedge mailbox message failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
173865
|
+
}
|
|
173866
|
+
}
|
|
173408
173867
|
isTriageDuplicateDecision(task) {
|
|
173409
173868
|
return task.paused === true && task.pausedReason === "duplicate-decision-required" && task.sourceMetadata?.duplicateSource === "triage-marker" && typeof task.sourceMetadata.nearDuplicateOf === "string";
|
|
173410
173869
|
}
|
|
@@ -173770,6 +174229,11 @@ var init_notification_service = __esm({
|
|
|
173770
174229
|
);
|
|
173771
174230
|
return;
|
|
173772
174231
|
}
|
|
174232
|
+
if (describeTaskWedge(task)) {
|
|
174233
|
+
this.failureNotificationSuppressedCount += 1;
|
|
174234
|
+
schedulerLog.log(`[notify] ${taskId} classified terminal wedge at dispatch time \u2014 suppressed generic failed notification`);
|
|
174235
|
+
return;
|
|
174236
|
+
}
|
|
173773
174237
|
const isTerminal2 = task.paused === true || task.column === "in-review";
|
|
173774
174238
|
if (this.failureNotificationMode === "terminal-only" && !isTerminal2) {
|
|
173775
174239
|
this.failureNotificationSuppressedCount += 1;
|
|
@@ -174598,7 +175062,10 @@ var init_notifier = __esm({
|
|
|
174598
175062
|
DEFAULT_NTFY_EVENTS = [
|
|
174599
175063
|
"in-review",
|
|
174600
175064
|
"merged",
|
|
175065
|
+
// FNXC:TaskWedgeNotifications 2026-07-22-19:00: Wedge episodes are operator
|
|
175066
|
+
// escalation events, so normal Ntfy defaults must not silently filter them.
|
|
174601
175067
|
"failed",
|
|
175068
|
+
"task-wedged",
|
|
174602
175069
|
"awaiting-approval",
|
|
174603
175070
|
"awaiting-user-review",
|
|
174604
175071
|
"planning-awaiting-input",
|
|
@@ -201146,6 +201613,7 @@ var init_self_healing = __esm({
|
|
|
201146
201613
|
init_dependency_blocked_todo_reporter();
|
|
201147
201614
|
init_scheduler();
|
|
201148
201615
|
init_task_agent_sync();
|
|
201616
|
+
init_task_wedge_notification();
|
|
201149
201617
|
init_self_healing_constants();
|
|
201150
201618
|
init_self_healing_constants();
|
|
201151
201619
|
init_self_healing_branch();
|
|
@@ -201453,6 +201921,15 @@ var init_self_healing = __esm({
|
|
|
201453
201921
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
201454
201922
|
log28.warn(`[${stage}] ${task.id}: no-action audit emission failed: ${message}`);
|
|
201455
201923
|
}
|
|
201924
|
+
const descriptor = describeSelfHealingNoActionWedge(task, stage, proof.metadata);
|
|
201925
|
+
if (descriptor) {
|
|
201926
|
+
try {
|
|
201927
|
+
await getActiveNotificationService()?.notifyTaskWedge(task, descriptor);
|
|
201928
|
+
} catch (error2) {
|
|
201929
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
201930
|
+
log28.warn(`[${stage}] ${task.id}: wedge notification failed: ${message}`);
|
|
201931
|
+
}
|
|
201932
|
+
}
|
|
201456
201933
|
log28.log(`[${stage}] ${task.id}: triple-proof not satisfied \u2014 no action (operator-decides)`);
|
|
201457
201934
|
}
|
|
201458
201935
|
/*
|
|
@@ -262299,6 +262776,70 @@ async function resolvePlanningModeSystemPrompt(store, promptOverrides, workflowI
|
|
|
262299
262776
|
|
|
262300
262777
|
${PLANNING_SYSTEM_PROMPT}`;
|
|
262301
262778
|
}
|
|
262779
|
+
function isPlanningTurnActive(sessionId) {
|
|
262780
|
+
return activeGenerations.has(sessionId) || pendingTurnReservations.has(sessionId);
|
|
262781
|
+
}
|
|
262782
|
+
function reservePlanningTurn(sessionId) {
|
|
262783
|
+
if (isPlanningTurnActive(sessionId)) {
|
|
262784
|
+
throw new GenerationInProgressError("Generation already in progress");
|
|
262785
|
+
}
|
|
262786
|
+
let resolveDone;
|
|
262787
|
+
const done = new Promise((resolve101) => {
|
|
262788
|
+
resolveDone = resolve101;
|
|
262789
|
+
});
|
|
262790
|
+
const reservation = { done, resolveDone };
|
|
262791
|
+
pendingTurnReservations.set(sessionId, reservation);
|
|
262792
|
+
return () => {
|
|
262793
|
+
if (pendingTurnReservations.get(sessionId) === reservation) {
|
|
262794
|
+
pendingTurnReservations.delete(sessionId);
|
|
262795
|
+
}
|
|
262796
|
+
reservation.resolveDone();
|
|
262797
|
+
};
|
|
262798
|
+
}
|
|
262799
|
+
function trackTurnOperationSettled(sessionId, operationPromise) {
|
|
262800
|
+
const settled = operationPromise.then(
|
|
262801
|
+
() => {
|
|
262802
|
+
},
|
|
262803
|
+
() => {
|
|
262804
|
+
}
|
|
262805
|
+
);
|
|
262806
|
+
settlingTurnOperations.set(sessionId, settled);
|
|
262807
|
+
void settled.then(() => {
|
|
262808
|
+
if (settlingTurnOperations.get(sessionId) === settled) {
|
|
262809
|
+
settlingTurnOperations.delete(sessionId);
|
|
262810
|
+
}
|
|
262811
|
+
});
|
|
262812
|
+
}
|
|
262813
|
+
async function waitForTurnOperationSettled(sessionId, timeoutMs = 2e3) {
|
|
262814
|
+
const settling = settlingTurnOperations.get(sessionId);
|
|
262815
|
+
if (!settling) return true;
|
|
262816
|
+
let timer2;
|
|
262817
|
+
const timedOut = await Promise.race([
|
|
262818
|
+
settling.then(() => false),
|
|
262819
|
+
new Promise((resolve101) => {
|
|
262820
|
+
timer2 = setTimeout(() => resolve101(true), timeoutMs);
|
|
262821
|
+
})
|
|
262822
|
+
]);
|
|
262823
|
+
clearTimeout(timer2);
|
|
262824
|
+
return !timedOut;
|
|
262825
|
+
}
|
|
262826
|
+
async function waitForPlanningTurnRelease(sessionId, timeoutMs = 2e3) {
|
|
262827
|
+
const deadline = Date.now() + timeoutMs;
|
|
262828
|
+
while (isPlanningTurnActive(sessionId)) {
|
|
262829
|
+
const remaining = deadline - Date.now();
|
|
262830
|
+
if (remaining <= 0) return false;
|
|
262831
|
+
const reservation = pendingTurnReservations.get(sessionId);
|
|
262832
|
+
if (reservation) {
|
|
262833
|
+
await Promise.race([
|
|
262834
|
+
reservation.done,
|
|
262835
|
+
new Promise((resolve101) => setTimeout(resolve101, Math.min(remaining, 50)))
|
|
262836
|
+
]);
|
|
262837
|
+
} else {
|
|
262838
|
+
await new Promise((resolve101) => setTimeout(resolve101, 10));
|
|
262839
|
+
}
|
|
262840
|
+
}
|
|
262841
|
+
return true;
|
|
262842
|
+
}
|
|
262302
262843
|
function isTaskPriority2(value) {
|
|
262303
262844
|
return typeof value === "string" && TASK_PRIORITIES.includes(value);
|
|
262304
262845
|
}
|
|
@@ -262695,7 +263236,7 @@ async function getFirstQuestionFromAgent(session, message) {
|
|
|
262695
263236
|
if (!session.agent) {
|
|
262696
263237
|
throw new InvalidSessionStateError("AI agent not initialized");
|
|
262697
263238
|
}
|
|
262698
|
-
await session.agent.session
|
|
263239
|
+
await promptPlanningAgent(session.agent.session, message);
|
|
262699
263240
|
const lastMessage = session.agent.session.state.messages.filter((m2) => m2.role === "assistant").pop();
|
|
262700
263241
|
let responseText = "";
|
|
262701
263242
|
if (lastMessage?.content) {
|
|
@@ -262734,7 +263275,8 @@ async function getFirstQuestionFromAgent(session, message) {
|
|
|
262734
263275
|
lastError = err instanceof Error ? err : new Error(String(err));
|
|
262735
263276
|
if (attempt < MAX_PARSE_RETRIES2) {
|
|
262736
263277
|
try {
|
|
262737
|
-
await
|
|
263278
|
+
await promptPlanningAgent(
|
|
263279
|
+
session.agent.session,
|
|
262738
263280
|
'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.'
|
|
262739
263281
|
);
|
|
262740
263282
|
const retryMessage = session.agent.session.state.messages.filter((m2) => m2.role === "assistant").pop();
|
|
@@ -262794,7 +263336,8 @@ function buildMandatoryFirstPlanningQuestion(userInput = "") {
|
|
|
262794
263336
|
}
|
|
262795
263337
|
async function requestMandatoryFirstPlanningQuestion(session, abortSignal) {
|
|
262796
263338
|
try {
|
|
262797
|
-
await
|
|
263339
|
+
await promptPlanningAgent(
|
|
263340
|
+
session.agent.session,
|
|
262798
263341
|
'Before producing a plan, ask one clarifying question. Return ONLY valid JSON: {"type":"question","data":{...}}.',
|
|
262799
263342
|
{ signal: abortSignal }
|
|
262800
263343
|
);
|
|
@@ -262967,8 +263510,14 @@ async function startExistingSession(sessionId, rootDir, store, modelProvider, mo
|
|
|
262967
263510
|
session.clarificationEnabled = runtimeOptions.clarificationEnabled === true;
|
|
262968
263511
|
session.ntfyConfig = runtimeOptions.ntfyConfig;
|
|
262969
263512
|
}
|
|
263513
|
+
if (isPlanningTurnActive(sessionId) || planningStreamManager.hasPendingInitialTurn(sessionId)) {
|
|
263514
|
+
diagnostics2.warn("Ignoring duplicate start for planning session with an active generation", {
|
|
263515
|
+
sessionId,
|
|
263516
|
+
operation: "start-existing-duplicate"
|
|
263517
|
+
});
|
|
263518
|
+
return;
|
|
263519
|
+
}
|
|
262970
263520
|
beginPlanningGeneration(session, "initial_plan");
|
|
262971
|
-
await persistSession(session, "generating");
|
|
262972
263521
|
planningStreamManager.registerInitialTurn(sessionId, () => {
|
|
262973
263522
|
session.pluginRunner = pluginRunner;
|
|
262974
263523
|
initializeAgent(session, rootDir, store, modelProvider, modelId, session.draftThinkingLevel, promptOverrides, pluginRunner).catch((err) => {
|
|
@@ -262980,6 +263529,7 @@ async function startExistingSession(sessionId, rootDir, store, modelProvider, mo
|
|
|
262980
263529
|
});
|
|
262981
263530
|
});
|
|
262982
263531
|
});
|
|
263532
|
+
await persistSession(session, "generating");
|
|
262983
263533
|
}
|
|
262984
263534
|
async function createSessionWithAgent(ip, initialPlan, rootDir, store, modelProvider, modelId, thinkingLevelOrPromptOverrides, promptOverridesOrOptions, optionsMaybe) {
|
|
262985
263535
|
const thinkingLevel = isThinkingLevel(thinkingLevelOrPromptOverrides) ? thinkingLevelOrPromptOverrides : void 0;
|
|
@@ -263042,6 +263592,19 @@ async function createSessionWithAgent(ip, initialPlan, rootDir, store, modelProv
|
|
|
263042
263592
|
return sessionId;
|
|
263043
263593
|
}
|
|
263044
263594
|
async function initializeAgent(session, rootDir, store, modelProvider, modelId, thinkingLevel, promptOverrides, pluginRunner) {
|
|
263595
|
+
let releaseTurn;
|
|
263596
|
+
try {
|
|
263597
|
+
releaseTurn = reservePlanningTurn(session.id);
|
|
263598
|
+
} catch (err) {
|
|
263599
|
+
if (err instanceof GenerationInProgressError) {
|
|
263600
|
+
diagnostics2.warn("Skipping duplicate initial planning turn \u2014 generation already active", {
|
|
263601
|
+
sessionId: session.id,
|
|
263602
|
+
operation: "initialize-agent-duplicate"
|
|
263603
|
+
});
|
|
263604
|
+
return;
|
|
263605
|
+
}
|
|
263606
|
+
throw err;
|
|
263607
|
+
}
|
|
263045
263608
|
try {
|
|
263046
263609
|
await runGenerationWithTimeout(session, async (abortSignal) => {
|
|
263047
263610
|
const agentPromise = createPlanningAgent(
|
|
@@ -263091,12 +263654,16 @@ async function initializeAgent(session, rootDir, store, modelProvider, modelId,
|
|
|
263091
263654
|
type: "error",
|
|
263092
263655
|
data: errorMessage4
|
|
263093
263656
|
});
|
|
263657
|
+
} finally {
|
|
263658
|
+
releaseTurn();
|
|
263094
263659
|
}
|
|
263095
263660
|
}
|
|
263096
263661
|
async function createPlanningAgent(session, rootDir, store, modelProvider, modelId, thinkingLevel, promptOverrides, pluginRunner) {
|
|
263097
263662
|
await ensureEngineReady();
|
|
263098
263663
|
const systemPrompt = await resolvePlanningModeSystemPrompt(store, promptOverrides, session.workflowId);
|
|
263099
263664
|
const skillContext = buildSessionSkillContextSync(null, "executor", rootDir, pluginRunner);
|
|
263665
|
+
const callbackEpoch = session.agentCallbackEpoch ?? 0;
|
|
263666
|
+
const callbacksInvalidated = () => (session.agentCallbackEpoch ?? 0) !== callbackEpoch;
|
|
263100
263667
|
return createFnAgent4({
|
|
263101
263668
|
cwd: rootDir,
|
|
263102
263669
|
systemPrompt,
|
|
@@ -263120,6 +263687,7 @@ async function createPlanningAgent(session, rootDir, store, modelProvider, model
|
|
|
263120
263687
|
} : {},
|
|
263121
263688
|
...thinkingLevel ? { defaultThinkingLevel: thinkingLevel } : {},
|
|
263122
263689
|
onThinking: (delta) => {
|
|
263690
|
+
if (callbacksInvalidated()) return;
|
|
263123
263691
|
markPlanningGenerationProgress(session.id, delta);
|
|
263124
263692
|
session.thinkingOutput += delta;
|
|
263125
263693
|
persistThinking(session.id, session.thinkingOutput);
|
|
@@ -263129,6 +263697,7 @@ async function createPlanningAgent(session, rootDir, store, modelProvider, model
|
|
|
263129
263697
|
});
|
|
263130
263698
|
},
|
|
263131
263699
|
onText: (delta) => {
|
|
263700
|
+
if (callbacksInvalidated()) return;
|
|
263132
263701
|
markPlanningGenerationProgress(session.id, delta);
|
|
263133
263702
|
session.thinkingOutput += delta;
|
|
263134
263703
|
persistThinking(session.id, session.thinkingOutput);
|
|
@@ -263175,7 +263744,7 @@ async function ensureSessionAgent(session, rootDir, historyForReplay, promptOver
|
|
|
263175
263744
|
if (abortSignal.aborted) {
|
|
263176
263745
|
throw createAbortError2();
|
|
263177
263746
|
}
|
|
263178
|
-
await session.agent.session
|
|
263747
|
+
await promptPlanningAgent(session.agent.session, contextMessage, {
|
|
263179
263748
|
signal: abortSignal
|
|
263180
263749
|
});
|
|
263181
263750
|
if (abortSignal.aborted) {
|
|
@@ -263213,7 +263782,7 @@ async function maybeNotifyPlanningAwaitingInput(session, question, proactiveClar
|
|
|
263213
263782
|
}
|
|
263214
263783
|
}
|
|
263215
263784
|
function buildRetryableParseErrorMessage(error2) {
|
|
263216
|
-
const baseMessage = (error2?.message || "Failed to parse AI response").replace(/\s*Please try again\.?\s*$/i, "").trim();
|
|
263785
|
+
const baseMessage = (error2?.message || "Failed to parse AI response").replace(/\s*Please try again\.?\s*$/i, "").replace(/[.\s]+$/, "").trim();
|
|
263217
263786
|
return `${baseMessage}. Retry this planning session or start a new one.`;
|
|
263218
263787
|
}
|
|
263219
263788
|
function setSessionError(session, message) {
|
|
@@ -263230,6 +263799,19 @@ function createAbortError2() {
|
|
|
263230
263799
|
error2.name = "AbortError";
|
|
263231
263800
|
return error2;
|
|
263232
263801
|
}
|
|
263802
|
+
async function promptPlanningAgent(agentSession, message, options) {
|
|
263803
|
+
let promptWithFallback2;
|
|
263804
|
+
try {
|
|
263805
|
+
promptWithFallback2 = promptWithFallback;
|
|
263806
|
+
} catch {
|
|
263807
|
+
promptWithFallback2 = void 0;
|
|
263808
|
+
}
|
|
263809
|
+
if (typeof promptWithFallback2 === "function") {
|
|
263810
|
+
await promptWithFallback2(agentSession, message, options);
|
|
263811
|
+
return;
|
|
263812
|
+
}
|
|
263813
|
+
await agentSession.prompt(message, options);
|
|
263814
|
+
}
|
|
263233
263815
|
function normalizeGenerationProgress(output) {
|
|
263234
263816
|
return output.replace(/\s+/g, " ").trim();
|
|
263235
263817
|
}
|
|
@@ -263305,7 +263887,9 @@ async function runGenerationWithTimeout(session, operation) {
|
|
|
263305
263887
|
);
|
|
263306
263888
|
});
|
|
263307
263889
|
try {
|
|
263308
|
-
|
|
263890
|
+
const operationPromise = operation(abortController.signal);
|
|
263891
|
+
trackTurnOperationSettled(session.id, operationPromise);
|
|
263892
|
+
return await Promise.race([operationPromise, abortPromise]);
|
|
263309
263893
|
} finally {
|
|
263310
263894
|
clearTimeout(generationRecord.timer);
|
|
263311
263895
|
if (activeGenerations.get(session.id) === generationRecord) {
|
|
@@ -263468,7 +264052,7 @@ async function continueAgentConversation(session, message) {
|
|
|
263468
264052
|
if (abortSignal.aborted) {
|
|
263469
264053
|
throw createAbortError2();
|
|
263470
264054
|
}
|
|
263471
|
-
await session.agent.session
|
|
264055
|
+
await promptPlanningAgent(session.agent.session, message, {
|
|
263472
264056
|
signal: abortSignal
|
|
263473
264057
|
});
|
|
263474
264058
|
if (abortSignal.aborted) {
|
|
@@ -263518,7 +264102,8 @@ async function continueAgentConversation(session, message) {
|
|
|
263518
264102
|
if (abortSignal.aborted) {
|
|
263519
264103
|
throw createAbortError2();
|
|
263520
264104
|
}
|
|
263521
|
-
await
|
|
264105
|
+
await promptPlanningAgent(
|
|
264106
|
+
session.agent.session,
|
|
263522
264107
|
'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.',
|
|
263523
264108
|
{ signal: abortSignal }
|
|
263524
264109
|
);
|
|
@@ -263794,57 +264379,62 @@ async function submitResponse(sessionId, responses, rootDir, promptOverrides, st
|
|
|
263794
264379
|
}
|
|
263795
264380
|
if (store && !session.store) session.store = store;
|
|
263796
264381
|
if (rootDir && !session.rootDir) session.rootDir = rootDir;
|
|
263797
|
-
if (
|
|
264382
|
+
if (isPlanningTurnActive(session.id)) {
|
|
263798
264383
|
if (didSubmitSameAnswer(session, responses)) {
|
|
263799
264384
|
throw new GenerationInProgressError("Generation already in progress for this response");
|
|
263800
264385
|
}
|
|
263801
264386
|
throw new GenerationInProgressError("Generation already in progress");
|
|
263802
264387
|
}
|
|
264388
|
+
const releaseTurn = reservePlanningTurn(session.id);
|
|
263803
264389
|
let answeredQuestion;
|
|
263804
|
-
|
|
263805
|
-
|
|
263806
|
-
|
|
263807
|
-
|
|
263808
|
-
|
|
263809
|
-
|
|
263810
|
-
|
|
263811
|
-
|
|
263812
|
-
|
|
263813
|
-
|
|
263814
|
-
|
|
263815
|
-
|
|
263816
|
-
const currentQuestion = captureOtherCustomText(session.currentQuestion, responses);
|
|
263817
|
-
const historyEntry = {
|
|
263818
|
-
question: currentQuestion,
|
|
263819
|
-
response: responses,
|
|
263820
|
-
thinkingOutput: session.lastGeneratedThinking || ""
|
|
263821
|
-
};
|
|
263822
|
-
session.error = void 0;
|
|
263823
|
-
const editIndex = session.editingQuestionId ? session.history.findIndex((entry) => entry.question.id === session.editingQuestionId) : -1;
|
|
263824
|
-
const isEditingPriorAnswer = editIndex >= 0;
|
|
263825
|
-
if (isEditingPriorAnswer) {
|
|
263826
|
-
session.history[editIndex] = historyEntry;
|
|
263827
|
-
session.editingQuestionId = void 0;
|
|
263828
|
-
session.summary = buildRunningSummary(session.initialPlan, session.history);
|
|
263829
|
-
disposeSessionAgentForRetry(session);
|
|
264390
|
+
try {
|
|
264391
|
+
if (isRefineRequest(responses) && session.summary) {
|
|
264392
|
+
beginPlanningGeneration(session, "question");
|
|
264393
|
+
session.currentQuestion = void 0;
|
|
264394
|
+
session.error = void 0;
|
|
264395
|
+
await persistSession(session, "generating");
|
|
264396
|
+
await ensureSessionAgent(session, rootDir, session.history, promptOverrides, store);
|
|
264397
|
+
const focus = typeof responses.focus === "string" ? responses.focus.trim() : void 0;
|
|
264398
|
+
const refineMessage = formatRefineRequestForAgent(session.summary, focus);
|
|
264399
|
+
await continueAgentConversation(session, refineMessage);
|
|
264400
|
+
} else if (!session.currentQuestion) {
|
|
264401
|
+
throw new InvalidSessionStateError("No active question in session");
|
|
263830
264402
|
} else {
|
|
263831
|
-
session.
|
|
263832
|
-
|
|
263833
|
-
|
|
263834
|
-
|
|
263835
|
-
|
|
263836
|
-
|
|
263837
|
-
|
|
263838
|
-
|
|
263839
|
-
|
|
263840
|
-
|
|
263841
|
-
|
|
263842
|
-
|
|
263843
|
-
|
|
263844
|
-
|
|
264403
|
+
const currentQuestion = captureOtherCustomText(session.currentQuestion, responses);
|
|
264404
|
+
const historyEntry = {
|
|
264405
|
+
question: currentQuestion,
|
|
264406
|
+
response: responses,
|
|
264407
|
+
thinkingOutput: session.lastGeneratedThinking || ""
|
|
264408
|
+
};
|
|
264409
|
+
session.error = void 0;
|
|
264410
|
+
const editIndex = session.editingQuestionId ? session.history.findIndex((entry) => entry.question.id === session.editingQuestionId) : -1;
|
|
264411
|
+
const isEditingPriorAnswer = editIndex >= 0;
|
|
264412
|
+
if (isEditingPriorAnswer) {
|
|
264413
|
+
session.history[editIndex] = historyEntry;
|
|
264414
|
+
session.editingQuestionId = void 0;
|
|
264415
|
+
session.summary = buildRunningSummary(session.initialPlan, session.history);
|
|
264416
|
+
disposeSessionAgentForRetry(session);
|
|
264417
|
+
} else {
|
|
264418
|
+
session.history.push(historyEntry);
|
|
264419
|
+
}
|
|
264420
|
+
answeredQuestion = currentQuestion;
|
|
264421
|
+
beginPlanningGeneration(session, "plan_update");
|
|
264422
|
+
session.currentQuestion = void 0;
|
|
264423
|
+
await persistSession(session, "generating");
|
|
264424
|
+
if (!session.agent) {
|
|
264425
|
+
await ensureSessionAgent(
|
|
264426
|
+
session,
|
|
264427
|
+
rootDir,
|
|
264428
|
+
isEditingPriorAnswer ? session.history : session.history.slice(0, -1),
|
|
264429
|
+
promptOverrides,
|
|
264430
|
+
store
|
|
264431
|
+
);
|
|
264432
|
+
}
|
|
264433
|
+
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);
|
|
264434
|
+
await continueAgentConversation(session, message);
|
|
263845
264435
|
}
|
|
263846
|
-
|
|
263847
|
-
|
|
264436
|
+
} finally {
|
|
264437
|
+
releaseTurn();
|
|
263848
264438
|
}
|
|
263849
264439
|
if (session.currentQuestion) {
|
|
263850
264440
|
return { type: "question", data: session.currentQuestion };
|
|
@@ -263875,26 +264465,31 @@ async function retrySession(sessionId, rootDir, promptOverrides, store) {
|
|
|
263875
264465
|
if (!inErrorState) {
|
|
263876
264466
|
throw new InvalidSessionStateError(`Planning session ${sessionId} is not in an error state`);
|
|
263877
264467
|
}
|
|
263878
|
-
|
|
263879
|
-
|
|
263880
|
-
|
|
263881
|
-
|
|
263882
|
-
|
|
263883
|
-
|
|
263884
|
-
|
|
263885
|
-
|
|
263886
|
-
await
|
|
263887
|
-
|
|
263888
|
-
|
|
264468
|
+
const releaseTurn = reservePlanningTurn(session.id);
|
|
264469
|
+
try {
|
|
264470
|
+
disposeSessionAgentForRetry(session);
|
|
264471
|
+
session.error = void 0;
|
|
264472
|
+
session.summary = void 0;
|
|
264473
|
+
session.currentQuestion = void 0;
|
|
264474
|
+
session.updatedAt = /* @__PURE__ */ new Date();
|
|
264475
|
+
beginPlanningGeneration(session, session.history.length === 0 ? "initial_plan" : "plan_update");
|
|
264476
|
+
await persistSession(session, "generating");
|
|
264477
|
+
if (session.history.length === 0) {
|
|
264478
|
+
await ensureSessionAgent(session, rootDir, [], promptOverrides, store);
|
|
264479
|
+
await continueAgentConversation(session, formatInitialRunningPlanRequestForAgent(session.initialPlan));
|
|
264480
|
+
return;
|
|
264481
|
+
}
|
|
264482
|
+
const replayHistory = session.history.slice(0, -1);
|
|
264483
|
+
const lastEntry = session.history[session.history.length - 1];
|
|
264484
|
+
await ensureSessionAgent(session, rootDir, replayHistory, promptOverrides, store);
|
|
264485
|
+
const replayMessage = formatResponseForAgent(
|
|
264486
|
+
lastEntry.question,
|
|
264487
|
+
coerceResponseRecord(lastEntry.question, lastEntry.response)
|
|
264488
|
+
);
|
|
264489
|
+
await continueAgentConversation(session, replayMessage);
|
|
264490
|
+
} finally {
|
|
264491
|
+
releaseTurn();
|
|
263889
264492
|
}
|
|
263890
|
-
const replayHistory = session.history.slice(0, -1);
|
|
263891
|
-
const lastEntry = session.history[session.history.length - 1];
|
|
263892
|
-
await ensureSessionAgent(session, rootDir, replayHistory, promptOverrides, store);
|
|
263893
|
-
const replayMessage = formatResponseForAgent(
|
|
263894
|
-
lastEntry.question,
|
|
263895
|
-
coerceResponseRecord(lastEntry.question, lastEntry.response)
|
|
263896
|
-
);
|
|
263897
|
-
await continueAgentConversation(session, replayMessage);
|
|
263898
264493
|
}
|
|
263899
264494
|
async function rewindSession(sessionId, questionId, rootDir, promptOverrides, store) {
|
|
263900
264495
|
const session = await getSession(sessionId);
|
|
@@ -263909,30 +264504,52 @@ async function rewindSession(sessionId, questionId, rootDir, promptOverrides, st
|
|
|
263909
264504
|
if (session.history.length === 0) {
|
|
263910
264505
|
throw new InvalidSessionStateError("Planning session has no previous question to rewind to");
|
|
263911
264506
|
}
|
|
263912
|
-
const
|
|
263913
|
-
if (
|
|
263914
|
-
|
|
264507
|
+
const activeGeneration = activeGenerations.get(session.id);
|
|
264508
|
+
if (activeGeneration) {
|
|
264509
|
+
activeGeneration.abortReason = "user-stop";
|
|
264510
|
+
clearTimeout(activeGeneration.timer);
|
|
264511
|
+
activeGeneration.abortTeardown();
|
|
264512
|
+
activeGeneration.abortController.abort();
|
|
264513
|
+
activeGenerations.delete(session.id);
|
|
263915
264514
|
}
|
|
263916
|
-
|
|
263917
|
-
|
|
263918
|
-
|
|
263919
|
-
|
|
263920
|
-
|
|
263921
|
-
|
|
263922
|
-
|
|
263923
|
-
|
|
263924
|
-
|
|
263925
|
-
|
|
263926
|
-
|
|
263927
|
-
|
|
264515
|
+
if (!await waitForPlanningTurnRelease(session.id)) {
|
|
264516
|
+
throw new GenerationInProgressError("Generation already in progress");
|
|
264517
|
+
}
|
|
264518
|
+
const releaseTurn = reservePlanningTurn(session.id);
|
|
264519
|
+
try {
|
|
264520
|
+
if (!await waitForTurnOperationSettled(session.id)) {
|
|
264521
|
+
diagnostics2.warn("Rewinding past a provider prompt that has not settled after abort", {
|
|
264522
|
+
sessionId: session.id,
|
|
264523
|
+
operation: "rewind-unsettled-prompt"
|
|
264524
|
+
});
|
|
264525
|
+
}
|
|
264526
|
+
const rewindIndex = questionId ? session.history.findIndex((entry) => entry.question.id === questionId) : session.history.length - 1;
|
|
264527
|
+
if (rewindIndex < 0) {
|
|
264528
|
+
throw new InvalidSessionStateError("Planning question to edit was not found");
|
|
264529
|
+
}
|
|
264530
|
+
const rewindEntry = session.history[rewindIndex];
|
|
264531
|
+
if (!questionId) session.history.pop();
|
|
264532
|
+
disposeSessionAgentForRetry(session);
|
|
264533
|
+
session.currentQuestion = rewindEntry.question;
|
|
264534
|
+
session.editingQuestionId = questionId ? questionId : void 0;
|
|
264535
|
+
session.summary = buildRunningSummary(session.initialPlan, session.history);
|
|
264536
|
+
session.error = void 0;
|
|
264537
|
+
session.lastGeneratedThinking = session.history[session.history.length - 1]?.thinkingOutput ?? "";
|
|
264538
|
+
session.thinkingOutput = "";
|
|
264539
|
+
session.updatedAt = /* @__PURE__ */ new Date();
|
|
264540
|
+
if (!session.agent && rootDir) {
|
|
264541
|
+
await ensureSessionAgent(session, rootDir, session.history, promptOverrides, store);
|
|
264542
|
+
}
|
|
264543
|
+
persistSession(session, "awaiting_input");
|
|
264544
|
+
planningStreamManager.broadcast(session.id, { type: "summary", data: session.summary });
|
|
264545
|
+
planningStreamManager.broadcast(session.id, { type: "question", data: rewindEntry.question });
|
|
264546
|
+
return {
|
|
264547
|
+
currentQuestion: rewindEntry.question,
|
|
264548
|
+
history: [...session.history]
|
|
264549
|
+
};
|
|
264550
|
+
} finally {
|
|
264551
|
+
releaseTurn();
|
|
263928
264552
|
}
|
|
263929
|
-
persistSession(session, "awaiting_input");
|
|
263930
|
-
planningStreamManager.broadcast(session.id, { type: "summary", data: session.summary });
|
|
263931
|
-
planningStreamManager.broadcast(session.id, { type: "question", data: rewindEntry.question });
|
|
263932
|
-
return {
|
|
263933
|
-
currentQuestion: rewindEntry.question,
|
|
263934
|
-
history: [...session.history]
|
|
263935
|
-
};
|
|
263936
264553
|
}
|
|
263937
264554
|
function stopGeneration(sessionId) {
|
|
263938
264555
|
const session = sessions.get(sessionId);
|
|
@@ -264047,6 +264664,7 @@ function coerceResponseRecord(question, response) {
|
|
|
264047
264664
|
};
|
|
264048
264665
|
}
|
|
264049
264666
|
function disposeSessionAgentForRetry(session) {
|
|
264667
|
+
session.agentCallbackEpoch = (session.agentCallbackEpoch ?? 0) + 1;
|
|
264050
264668
|
if (!session.agent) {
|
|
264051
264669
|
return;
|
|
264052
264670
|
}
|
|
@@ -264340,6 +264958,8 @@ function __resetPlanningState() {
|
|
|
264340
264958
|
rateLimits2.clear();
|
|
264341
264959
|
planningStreamManager.reset();
|
|
264342
264960
|
activeGenerations.clear();
|
|
264961
|
+
pendingTurnReservations.clear();
|
|
264962
|
+
settlingTurnOperations.clear();
|
|
264343
264963
|
if (_aiSessionStore && _aiSessionDeletedListener) {
|
|
264344
264964
|
_aiSessionStore.off("ai_session:deleted", _aiSessionDeletedListener);
|
|
264345
264965
|
}
|
|
@@ -264364,7 +264984,7 @@ async function __runGenerationWithTimeoutForTests(sessionId, operation) {
|
|
|
264364
264984
|
}
|
|
264365
264985
|
return runGenerationWithTimeout(session, operation);
|
|
264366
264986
|
}
|
|
264367
|
-
var PLANNING_NO_AMBIENT_TASK_ID, PLANNING_BUILTIN_WEB_TOOLS, createFnAgent4, planningNtfyHelpers, diagnostics2, PLANNING_SYSTEM_PROMPT, DRAFT_PLACEHOLDER_TITLE, 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, _aiSessionDeletedListener, sessionPersistenceQueues, cleanupInterval2, PlanningStreamManager, planningStreamManager, MAX_PARSE_RETRIES2, TruncatedPlanningCompletionError, RateLimitError3, SessionNotFoundError, InvalidSessionStateError, GenerationInProgressError;
|
|
264987
|
+
var PLANNING_NO_AMBIENT_TASK_ID, PLANNING_BUILTIN_WEB_TOOLS, createFnAgent4, planningNtfyHelpers, diagnostics2, PLANNING_SYSTEM_PROMPT, DRAFT_PLACEHOLDER_TITLE, 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, _aiSessionDeletedListener, sessionPersistenceQueues, cleanupInterval2, PlanningStreamManager, planningStreamManager, MAX_PARSE_RETRIES2, TruncatedPlanningCompletionError, RateLimitError3, SessionNotFoundError, InvalidSessionStateError, GenerationInProgressError;
|
|
264368
264988
|
var init_planning = __esm({
|
|
264369
264989
|
"../dashboard/src/planning.ts"() {
|
|
264370
264990
|
"use strict";
|
|
@@ -264400,6 +265020,8 @@ Every turn must include the running-plan fields: only title, description, concre
|
|
|
264400
265020
|
sessions = /* @__PURE__ */ new Map();
|
|
264401
265021
|
rateLimits2 = /* @__PURE__ */ new Map();
|
|
264402
265022
|
activeGenerations = /* @__PURE__ */ new Map();
|
|
265023
|
+
pendingTurnReservations = /* @__PURE__ */ new Map();
|
|
265024
|
+
settlingTurnOperations = /* @__PURE__ */ new Map();
|
|
264403
265025
|
sessionPersistenceQueues = /* @__PURE__ */ new Map();
|
|
264404
265026
|
cleanupInterval2 = setInterval(cleanupExpiredSessions, CLEANUP_INTERVAL_MS2);
|
|
264405
265027
|
cleanupInterval2.unref?.();
|
|
@@ -264473,6 +265095,10 @@ Every turn must include the running-plan fields: only title, description, concre
|
|
|
264473
265095
|
}
|
|
264474
265096
|
this.pendingInitialTurns.set(sessionId, start);
|
|
264475
265097
|
}
|
|
265098
|
+
/** True while a registered initial turn has not been consumed by a stream connection yet. */
|
|
265099
|
+
hasPendingInitialTurn(sessionId) {
|
|
265100
|
+
return this.pendingInitialTurns.has(sessionId);
|
|
265101
|
+
}
|
|
264476
265102
|
consumeInitialTurn(sessionId) {
|
|
264477
265103
|
const start = this.pendingInitialTurns.get(sessionId);
|
|
264478
265104
|
if (!start) {
|
|
@@ -264512,7 +265138,7 @@ Every turn must include the running-plan fields: only title, description, concre
|
|
|
264512
265138
|
this.removeAllListeners();
|
|
264513
265139
|
}
|
|
264514
265140
|
};
|
|
264515
|
-
planningStreamManager = new PlanningStreamManager();
|
|
265141
|
+
planningStreamManager = new PlanningStreamManager(2e3);
|
|
264516
265142
|
MAX_PARSE_RETRIES2 = 1;
|
|
264517
265143
|
TruncatedPlanningCompletionError = class extends Error {
|
|
264518
265144
|
constructor() {
|
|
@@ -269009,7 +269635,7 @@ async function readAndValidateManifest(manifestPath) {
|
|
|
269009
269635
|
}
|
|
269010
269636
|
return manifest;
|
|
269011
269637
|
}
|
|
269012
|
-
function createPluginRouter(pluginStore, pluginLoader, pluginRunner, defaultTaskStore) {
|
|
269638
|
+
function createPluginRouter(pluginStore, pluginLoader, pluginRunner, defaultTaskStore, resolveProjectPluginLoader) {
|
|
269013
269639
|
const router = Router();
|
|
269014
269640
|
router.get("/", catchHandler(async (_req, res) => {
|
|
269015
269641
|
const plugins2 = await pluginStore.listPlugins();
|
|
@@ -269238,24 +269864,37 @@ function createPluginRouter(pluginStore, pluginLoader, pluginRunner, defaultTask
|
|
|
269238
269864
|
throw internalError(err instanceof Error ? err.message : "Failed to update settings");
|
|
269239
269865
|
}
|
|
269240
269866
|
}));
|
|
269241
|
-
const
|
|
269242
|
-
|
|
269243
|
-
|
|
269244
|
-
|
|
269245
|
-
|
|
269867
|
+
const collectDispatchEntries = (resolvedLoader) => {
|
|
269868
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
269869
|
+
const addPluginRoutes = (entries, execLoader) => {
|
|
269870
|
+
if (!entries) return;
|
|
269871
|
+
for (const entry of entries) {
|
|
269872
|
+
const key = `${entry.pluginId}\0${entry.route.method}\0${entry.route.path}`;
|
|
269873
|
+
if (!byKey.has(key)) {
|
|
269874
|
+
byKey.set(key, { ...entry, execLoader });
|
|
269875
|
+
}
|
|
269876
|
+
}
|
|
269877
|
+
};
|
|
269878
|
+
if (resolvedLoader && resolvedLoader !== pluginLoader) {
|
|
269879
|
+
addPluginRoutes(resolvedLoader.getPluginRoutes?.(), resolvedLoader);
|
|
269246
269880
|
}
|
|
269881
|
+
addPluginRoutes(pluginLoader.getPluginRoutes?.(), pluginLoader);
|
|
269882
|
+
if (pluginRunner && typeof pluginRunner.getPluginRoutes === "function") {
|
|
269883
|
+
addPluginRoutes(pluginRunner.getPluginRoutes(), pluginLoader);
|
|
269884
|
+
}
|
|
269885
|
+
return byKey;
|
|
269247
269886
|
};
|
|
269248
|
-
|
|
269249
|
-
|
|
269250
|
-
|
|
269251
|
-
|
|
269252
|
-
|
|
269253
|
-
|
|
269254
|
-
}
|
|
269255
|
-
|
|
269887
|
+
const buildDispatchRouter = (entries) => {
|
|
269888
|
+
const dispatchRouter = Router();
|
|
269889
|
+
for (const { pluginId, route, execLoader } of entries.values()) {
|
|
269890
|
+
registerPluginRoute(dispatchRouter, pluginId, route, execLoader);
|
|
269891
|
+
}
|
|
269892
|
+
return dispatchRouter;
|
|
269893
|
+
};
|
|
269894
|
+
const registerPluginRoute = (targetRouter, pluginId, route, execLoader) => {
|
|
269256
269895
|
const fullPath = `/${pluginId}${route.path.startsWith("/") ? route.path : `/${route.path}`}`;
|
|
269257
269896
|
const handler = catchHandler(async (req, res) => {
|
|
269258
|
-
const plugin9 =
|
|
269897
|
+
const plugin9 = execLoader.getPlugin(pluginId);
|
|
269259
269898
|
if (!plugin9) {
|
|
269260
269899
|
throw notFound(`Plugin "${pluginId}" not loaded`);
|
|
269261
269900
|
}
|
|
@@ -269280,7 +269919,7 @@ function createPluginRouter(pluginStore, pluginLoader, pluginRunner, defaultTask
|
|
|
269280
269919
|
} catch {
|
|
269281
269920
|
}
|
|
269282
269921
|
}
|
|
269283
|
-
const ctx = await
|
|
269922
|
+
const ctx = await execLoader.createRouteContext(pluginId, {
|
|
269284
269923
|
taskStore,
|
|
269285
269924
|
settings,
|
|
269286
269925
|
resolveProjectTaskStore: getOrCreateProjectStore,
|
|
@@ -269320,22 +269959,42 @@ function createPluginRouter(pluginStore, pluginLoader, pluginRunner, defaultTask
|
|
|
269320
269959
|
});
|
|
269321
269960
|
switch (route.method) {
|
|
269322
269961
|
case "GET":
|
|
269323
|
-
|
|
269962
|
+
targetRouter.get(fullPath, handler);
|
|
269324
269963
|
break;
|
|
269325
269964
|
case "POST":
|
|
269326
|
-
|
|
269965
|
+
targetRouter.post(fullPath, handler);
|
|
269327
269966
|
break;
|
|
269328
269967
|
case "PUT":
|
|
269329
|
-
|
|
269968
|
+
targetRouter.put(fullPath, handler);
|
|
269330
269969
|
break;
|
|
269331
269970
|
case "PATCH":
|
|
269332
|
-
|
|
269971
|
+
targetRouter.patch(fullPath, handler);
|
|
269333
269972
|
break;
|
|
269334
269973
|
case "DELETE":
|
|
269335
|
-
|
|
269974
|
+
targetRouter.delete(fullPath, handler);
|
|
269336
269975
|
break;
|
|
269337
269976
|
}
|
|
269338
|
-
}
|
|
269977
|
+
};
|
|
269978
|
+
const dispatchRouterCache = /* @__PURE__ */ new WeakMap();
|
|
269979
|
+
const dispatchSignature = (entries, resolvedLoader) => [...entries.entries()].map(([key, entry]) => `${key}\0${entry.execLoader === resolvedLoader ? "p" : "h"}`).sort().join("\n");
|
|
269980
|
+
router.use((req, res, next) => {
|
|
269981
|
+
void (async () => {
|
|
269982
|
+
const resolvedLoader = resolveProjectPluginLoader ? await resolveProjectPluginLoader(req) : void 0;
|
|
269983
|
+
const entries = collectDispatchEntries(resolvedLoader);
|
|
269984
|
+
if (entries.size === 0) {
|
|
269985
|
+
next();
|
|
269986
|
+
return;
|
|
269987
|
+
}
|
|
269988
|
+
const signature = dispatchSignature(entries, resolvedLoader);
|
|
269989
|
+
const cacheKey = resolvedLoader ?? pluginLoader;
|
|
269990
|
+
let cached2 = dispatchRouterCache.get(cacheKey);
|
|
269991
|
+
if (!cached2 || cached2.signature !== signature) {
|
|
269992
|
+
cached2 = { signature, router: buildDispatchRouter(entries) };
|
|
269993
|
+
dispatchRouterCache.set(cacheKey, cached2);
|
|
269994
|
+
}
|
|
269995
|
+
cached2.router(req, res, next);
|
|
269996
|
+
})().catch(next);
|
|
269997
|
+
});
|
|
269339
269998
|
return router;
|
|
269340
269999
|
}
|
|
269341
270000
|
var registryManifestUrl, cachedRegistryManifest, DIST_DIR_NAMES;
|
|
@@ -269508,6 +270167,24 @@ function createApiRoutesContext(store, options) {
|
|
|
269508
270167
|
return [...projects2].sort((a, b3) => rankProject(b3.path) - rankProject(a.path));
|
|
269509
270168
|
}
|
|
269510
270169
|
const resolveScopedStore2 = (req) => getScopedStore(req, store, options);
|
|
270170
|
+
const fallbackProjectLoaders = /* @__PURE__ */ new WeakMap();
|
|
270171
|
+
const getProjectPluginLoader = async (scopedStore, engine) => {
|
|
270172
|
+
const engineLoader = engine?.getPluginRunner?.()?.getLoader?.();
|
|
270173
|
+
if (engineLoader) return engineLoader;
|
|
270174
|
+
const scopedPluginStore = scopedStore.getPluginStore();
|
|
270175
|
+
if (scopedPluginStore === options?.pluginStore) return options?.pluginLoader;
|
|
270176
|
+
let fallback2 = fallbackProjectLoaders.get(scopedStore);
|
|
270177
|
+
if (!fallback2) {
|
|
270178
|
+
const loader = new PluginLoader({ pluginStore: scopedPluginStore, taskStore: scopedStore });
|
|
270179
|
+
fallback2 = {
|
|
270180
|
+
loader,
|
|
270181
|
+
initialized: loader.loadAllPlugins().then(() => void 0)
|
|
270182
|
+
};
|
|
270183
|
+
fallbackProjectLoaders.set(scopedStore, fallback2);
|
|
270184
|
+
}
|
|
270185
|
+
await fallback2.initialized;
|
|
270186
|
+
return fallback2.loader;
|
|
270187
|
+
};
|
|
269511
270188
|
const fallbackMcpLoaders = /* @__PURE__ */ new WeakMap();
|
|
269512
270189
|
const projectMcpProviders = /* @__PURE__ */ new WeakMap();
|
|
269513
270190
|
const bindProjectScopedPluginMcpProvider = async (context) => {
|
|
@@ -269667,6 +270344,7 @@ function createApiRoutesContext(store, options) {
|
|
|
269667
270344
|
getProjectIdFromRequest,
|
|
269668
270345
|
getScopedStore: resolveScopedStore2,
|
|
269669
270346
|
getProjectContext: resolveProjectContext,
|
|
270347
|
+
getProjectPluginLoader,
|
|
269670
270348
|
emitRemoteRouteDiagnostic: (input) => emitRemoteRouteDiagnostic(runtimeLogger, input),
|
|
269671
270349
|
emitAuthSyncAuditLog,
|
|
269672
270350
|
parseScopeParam,
|
|
@@ -282879,6 +283557,7 @@ function describeColumns(ir2, canonicalizeLifecycle = false) {
|
|
|
282879
283557
|
return v2.columns.map((col) => ({
|
|
282880
283558
|
id: col.id,
|
|
282881
283559
|
name: displayColumnName(col.id, col.name, canonicalizeLifecycle),
|
|
283560
|
+
...col.description ? { description: col.description } : {},
|
|
282882
283561
|
flags: resolveColumnFlags(col)
|
|
282883
283562
|
}));
|
|
282884
283563
|
}
|
|
@@ -291884,6 +292563,7 @@ var chat_exports = {};
|
|
|
291884
292563
|
__export(chat_exports, {
|
|
291885
292564
|
CHAT_AGENT_MESSAGE_ROUTING_GUIDANCE: () => CHAT_AGENT_MESSAGE_ROUTING_GUIDANCE,
|
|
291886
292565
|
CHAT_ASK_QUESTION_GUIDANCE: () => CHAT_ASK_QUESTION_GUIDANCE,
|
|
292566
|
+
CHAT_CODEBASE_ACCURACY_GUIDANCE: () => CHAT_CODEBASE_ACCURACY_GUIDANCE,
|
|
291887
292567
|
CHAT_SYSTEM_PROMPT: () => CHAT_SYSTEM_PROMPT,
|
|
291888
292568
|
ChatManager: () => ChatManager,
|
|
291889
292569
|
ChatStreamManager: () => ChatStreamManager,
|
|
@@ -292535,7 +293215,7 @@ function __resetChatState() {
|
|
|
292535
293215
|
createResolvedAgentSession2 = createResolvedAgentSession;
|
|
292536
293216
|
__setChatDiagnostics(null);
|
|
292537
293217
|
}
|
|
292538
|
-
var createFnAgent10, createResolvedAgentSession2, buildAgentChatPromptFn, defaultDiagnostics, _diagnostics, diagnostics6, SKILL_COMMAND_PATTERN, CHAT_SYSTEM_PROMPT, CHAT_AGENT_MESSAGE_ROUTING_GUIDANCE, CHAT_ASK_QUESTION_GUIDANCE, RATE_LIMIT_WINDOW_MS6, MAX_MESSAGES_PER_IP_PER_MINUTE, MAX_REFERENCED_FILE_SIZE, TASK_PLANNER_CHAT_AGENT_ID_PREFIX2, CHAT_CODING_TOOLS, ROOM_AMBIENT_MAX_RESPONDERS, CHAT_MISSION_READ_TOOL_NAMES, CHAT_IDEATION_READ_TOOL_NAMES, ROOM_SKIP_SENTINEL, DEFAULT_ROOM_THREAD_RECENT_VERBATIM_MESSAGES, DEFAULT_ROOM_THREAD_COMPACTION_FETCH_LIMIT, ROOM_THREAD_CONTEXT_MAX_CHARS, ROOM_THREAD_MESSAGE_CONTENT_MAX_CHARS, DEFAULT_ROOM_THREAD_SUMMARY_MAX_CHARS, IN_FLIGHT_PERSIST_DEBOUNCE_MS, rateLimits6, ChatStreamManager, chatStreamManager, RoomReplyGenerationError, ChatManager;
|
|
293218
|
+
var createFnAgent10, createResolvedAgentSession2, buildAgentChatPromptFn, defaultDiagnostics, _diagnostics, diagnostics6, SKILL_COMMAND_PATTERN, CHAT_SYSTEM_PROMPT, CHAT_AGENT_MESSAGE_ROUTING_GUIDANCE, CHAT_CODEBASE_ACCURACY_GUIDANCE, CHAT_ASK_QUESTION_GUIDANCE, RATE_LIMIT_WINDOW_MS6, MAX_MESSAGES_PER_IP_PER_MINUTE, MAX_REFERENCED_FILE_SIZE, TASK_PLANNER_CHAT_AGENT_ID_PREFIX2, CHAT_CODING_TOOLS, ROOM_AMBIENT_MAX_RESPONDERS, CHAT_MISSION_READ_TOOL_NAMES, CHAT_IDEATION_READ_TOOL_NAMES, ROOM_SKIP_SENTINEL, DEFAULT_ROOM_THREAD_RECENT_VERBATIM_MESSAGES, DEFAULT_ROOM_THREAD_COMPACTION_FETCH_LIMIT, ROOM_THREAD_CONTEXT_MAX_CHARS, ROOM_THREAD_MESSAGE_CONTENT_MAX_CHARS, DEFAULT_ROOM_THREAD_SUMMARY_MAX_CHARS, IN_FLIGHT_PERSIST_DEBOUNCE_MS, rateLimits6, ChatStreamManager, chatStreamManager, RoomReplyGenerationError, ChatManager;
|
|
292539
293219
|
var init_chat = __esm({
|
|
292540
293220
|
"../dashboard/src/chat.ts"() {
|
|
292541
293221
|
"use strict";
|
|
@@ -292575,12 +293255,33 @@ var init_chat = __esm({
|
|
|
292575
293255
|
SKILL_COMMAND_PATTERN = /(^|\s)\/skill:([^\s]+)/gi;
|
|
292576
293256
|
CHAT_SYSTEM_PROMPT = `${FUSION_RUNTIME_SELF_AWARENESS}
|
|
292577
293257
|
|
|
292578
|
-
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
|
|
293258
|
+
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.`;
|
|
292579
293259
|
CHAT_AGENT_MESSAGE_ROUTING_GUIDANCE = `## Messaging Semantics
|
|
292580
293260
|
|
|
292581
293261
|
Your chat reply is the primary response to the user. Do not also call \`fn_send_message\` with the same content just to mirror your chat response into mailbox.
|
|
292582
293262
|
|
|
292583
293263
|
Use \`fn_send_message\` only when either (a) the user explicitly asks for mailbox/inbox/notification delivery (for example: "send me this in mail", "ntfy me when\u2026", or "leave me a note in my inbox"), or (b) you are sending a genuinely longer follow-up that did not fit in a short chat reply. In either case, send with \`type: "agent-to-user"\` and target the dashboard user alias (\`to_id: "dashboard"\` is preferred), and ensure the mailbox message is additive rather than a duplicate of the chat reply. Never route that as a user/CLI \u2192 agent message.`;
|
|
293264
|
+
CHAT_CODEBASE_ACCURACY_GUIDANCE = `## Codebase accuracy
|
|
293265
|
+
|
|
293266
|
+
When the user asks about **this project's** code, architecture, behavior, APIs, files, tests, settings, or "how does X work here", ground the answer in the live checkout **before** you reply. Do not answer from training-data memory or generic framework knowledge as if it were this repo.
|
|
293267
|
+
|
|
293268
|
+
### Investigate first
|
|
293269
|
+
1. Use readonly tools first: \`grep\`, \`find\`, \`ls\`, \`read\` (and readonly git such as \`git status\` / \`git log\` / \`git branch\` when relevant). Prefer \`grep\`/\`find\` to locate symbols, then \`read\` the concrete files. Restrict \`find\` (and directory walks) to the project checkout or a known merge/worktree path with bounded, prefix-filtered scans; never recurse through the OS temp root (\`$TMPDIR\`, \`/tmp\`, macOS \`/var/folders/...\`) or an unbounded path.
|
|
293270
|
+
2. Open the real definitions and call sites you will cite. For behavioral questions, also check tests and docs under \`docs/\` / \`CONCEPTS.md\` when they exist.
|
|
293271
|
+
3. If board/task context matters, use \`fn_task_list\` / \`fn_task_show\` (and search tools if available) rather than inventing task state.
|
|
293272
|
+
4. Only after you have evidence, write the chat reply.
|
|
293273
|
+
|
|
293274
|
+
### How to answer
|
|
293275
|
+
- Name **real** paths, symbols, and modules from the checkout (e.g. \`packages/foo/src/bar.ts\`, \`functionName\`). Prefer evidence over abstraction.
|
|
293276
|
+
- Prefer a short answer that is **correct and cited** over a fluent guess. A crisp 3\u20136 bullet list with file paths is better than a long ungrounded narrative.
|
|
293277
|
+
- If tools conflict with your prior assumptions, **trust the tools**.
|
|
293278
|
+
- If you cannot find something after a reasonable search, say what you searched and that it may not exist \u2014 do not invent modules, routes, tables, or APIs.
|
|
293279
|
+
- Distinguish **verified in this checkout** from **general recommendation**. Label speculation explicitly.
|
|
293280
|
+
|
|
293281
|
+
### When brevity still applies
|
|
293282
|
+
- Purely conversational, product-how-to, or non-code questions: keep the existing short/crisp default.
|
|
293283
|
+
- Code/architecture questions: still lead with a short answer, but include the grounding (paths / symbols). For long excerpts, multi-file traces, or deep designs: lead with a brief chat summary; if \`fn_send_message\` is available in this session, send the full write-up via that tool (\`type: "agent-to-user"\`, \`to_id: "dashboard"\`); if it is not available, keep the necessary detail in the chat reply instead of calling a missing tool.
|
|
293284
|
+
- Do **not** start a Planning Mode interview, write PROMPT.md, or invent steps/file-scope specs unless the user asks to plan or create a task.`;
|
|
292584
293285
|
CHAT_ASK_QUESTION_GUIDANCE = `## Asking the User
|
|
292585
293286
|
|
|
292586
293287
|
When you need structured input, or whenever you present options, choices, or a decision between alternatives, call \`fn_ask_question\` with one or more questions using the right shape (single_select, multi_select, confirm/yes-no, or text) instead of listing options only in prose, then stop and wait for the user's next chat message.`;
|
|
@@ -293200,6 +293901,9 @@ ${mentionContext}`;
|
|
|
293200
293901
|
systemPrompt = `${systemPrompt}
|
|
293201
293902
|
|
|
293202
293903
|
${CHAT_AGENT_MESSAGE_ROUTING_GUIDANCE}`;
|
|
293904
|
+
systemPrompt = `${systemPrompt}
|
|
293905
|
+
|
|
293906
|
+
${CHAT_CODEBASE_ACCURACY_GUIDANCE}`;
|
|
293203
293907
|
const roomCompactionSettings = await this.getRoomCompactionSettings();
|
|
293204
293908
|
const roomMessages = await this.chatStore.getRoomMessages(input.roomId, { limit: roomCompactionSettings.fetchLimit });
|
|
293205
293909
|
const { attachmentContents, imageContents } = await readChatAttachmentContents(
|
|
@@ -293518,6 +294222,9 @@ ${CHAT_AGENT_MESSAGE_ROUTING_GUIDANCE}`;
|
|
|
293518
294222
|
}
|
|
293519
294223
|
systemPrompt = `${systemPrompt}
|
|
293520
294224
|
|
|
294225
|
+
${CHAT_CODEBASE_ACCURACY_GUIDANCE}`;
|
|
294226
|
+
systemPrompt = `${systemPrompt}
|
|
294227
|
+
|
|
293521
294228
|
${CHAT_ASK_QUESTION_GUIDANCE}`;
|
|
293522
294229
|
const taskPlannerChatTaskId = typeof session.agentId === "string" && session.agentId.startsWith(TASK_PLANNER_CHAT_AGENT_ID_PREFIX2) ? session.agentId.slice(TASK_PLANNER_CHAT_AGENT_ID_PREFIX2.length).trim() : "";
|
|
293523
294230
|
let taskPlannerTaskColumn = "";
|
|
@@ -345253,26 +345960,8 @@ var init_plugin_bundled_runtimes = __esm({
|
|
|
345253
345960
|
import { isAbsolute as isAbsolute27, resolve as resolve56 } from "node:path";
|
|
345254
345961
|
import * as nodeFs2 from "node:fs";
|
|
345255
345962
|
function registerPluginsAutomationRoutes(ctx, deps) {
|
|
345256
|
-
const { router, options, parseScopeParam, resolveAutomationStore, resolveRoutineStore, resolveRoutineRunner, getScopedStore: getScopedStore2, getProjectContext: getProjectContext2, rethrowAsApiError: rethrowAsApiError9, runtimeLogger } = ctx;
|
|
345963
|
+
const { router, options, parseScopeParam, resolveAutomationStore, resolveRoutineStore, resolveRoutineRunner, getScopedStore: getScopedStore2, getProjectContext: getProjectContext2, getProjectPluginLoader, rethrowAsApiError: rethrowAsApiError9, runtimeLogger } = ctx;
|
|
345257
345964
|
const makeRunStreamHandler = createAutomationRunStreamHandlerFactory({ parseScopeParam, rethrowAsApiError: rethrowAsApiError9, ...deps });
|
|
345258
|
-
const fallbackProjectLoaders = /* @__PURE__ */ new WeakMap();
|
|
345259
|
-
const getProjectPluginLoader = async (scopedStore, engine) => {
|
|
345260
|
-
const engineLoader = engine?.getPluginRunner?.()?.getLoader?.();
|
|
345261
|
-
if (engineLoader) return engineLoader;
|
|
345262
|
-
const scopedPluginStore = scopedStore.getPluginStore();
|
|
345263
|
-
if (scopedPluginStore === options?.pluginStore) return options?.pluginLoader;
|
|
345264
|
-
let fallback2 = fallbackProjectLoaders.get(scopedStore);
|
|
345265
|
-
if (!fallback2) {
|
|
345266
|
-
const loader = new PluginLoader({ pluginStore: scopedPluginStore, taskStore: scopedStore });
|
|
345267
|
-
fallback2 = {
|
|
345268
|
-
loader,
|
|
345269
|
-
initialized: loader.loadAllPlugins().then(() => void 0)
|
|
345270
|
-
};
|
|
345271
|
-
fallbackProjectLoaders.set(scopedStore, fallback2);
|
|
345272
|
-
}
|
|
345273
|
-
await fallback2.initialized;
|
|
345274
|
-
return fallback2.loader;
|
|
345275
|
-
};
|
|
345276
345965
|
router.get("/automations", async (req, res) => {
|
|
345277
345966
|
if (!options?.automationStore) {
|
|
345278
345967
|
return res.json([]);
|
|
@@ -371669,6 +372358,7 @@ function createApiRoutes(store, options) {
|
|
|
371669
372358
|
getProjectIdFromRequest: getProjectIdFromRequest2,
|
|
371670
372359
|
getScopedStore: getScopedStore2,
|
|
371671
372360
|
getProjectContext: getProjectContext2,
|
|
372361
|
+
getProjectPluginLoader,
|
|
371672
372362
|
emitRemoteRouteDiagnostic: emitRemoteRouteDiagnostic2,
|
|
371673
372363
|
emitAuthSyncAuditLog,
|
|
371674
372364
|
parseScopeParam,
|
|
@@ -371689,6 +372379,7 @@ function createApiRoutes(store, options) {
|
|
|
371689
372379
|
getProjectIdFromRequest: getProjectIdFromRequest2,
|
|
371690
372380
|
getScopedStore: getScopedStore2,
|
|
371691
372381
|
getProjectContext: getProjectContext2,
|
|
372382
|
+
getProjectPluginLoader,
|
|
371692
372383
|
emitRemoteRouteDiagnostic: emitRemoteRouteDiagnostic2,
|
|
371693
372384
|
emitAuthSyncAuditLog,
|
|
371694
372385
|
parseScopeParam,
|
|
@@ -372386,7 +373077,18 @@ function createApiRoutes(store, options) {
|
|
|
372386
373077
|
options.pluginStore,
|
|
372387
373078
|
options.pluginLoader,
|
|
372388
373079
|
pluginRunner,
|
|
372389
|
-
store
|
|
373080
|
+
store,
|
|
373081
|
+
/*
|
|
373082
|
+
FNXC:PluginRoutes 2026-07-22-20:30:
|
|
373083
|
+
Per-request project-scoped loader resolution for plugin-defined routes — the
|
|
373084
|
+
same getProjectPluginLoader cache the plugin management registrar uses — so a
|
|
373085
|
+
plugin enabled after boot, or enabled only in a non-launch project, serves its
|
|
373086
|
+
API routes the moment its dashboard view appears.
|
|
373087
|
+
*/
|
|
373088
|
+
async (req) => {
|
|
373089
|
+
const { store: scopedStore, engine } = await routeContext.getProjectContext(req);
|
|
373090
|
+
return routeContext.getProjectPluginLoader(scopedStore, engine);
|
|
373091
|
+
}
|
|
372390
373092
|
)
|
|
372391
373093
|
);
|
|
372392
373094
|
}
|