@runfusion/fusion 0.75.1-beta.0 → 0.75.1-beta.2
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 +523 -212
- package/dist/child-process-worker.js +323 -99
- package/dist/client/.vite/manifest.json +249 -249
- package/dist/client/assets/{AgentDetailView-swTdiyeE.js → AgentDetailView-ltOG8f0-.js} +1 -1
- package/dist/client/assets/{AgentPermissionPolicyEditor-Dq-KuDaA.js → AgentPermissionPolicyEditor-CC_rrbbG.js} +1 -1
- package/dist/client/assets/{AgentsView-D1GEHyH0.js → AgentsView-DWUdi27N.js} +3 -3
- package/dist/client/assets/{ChatView-D1czGvvT.js → ChatView-mFKfoegS.js} +1 -1
- package/dist/client/assets/{CommandCenter-DWZ7OTOB.js → CommandCenter-C2wvDq_N.js} +1 -1
- package/dist/client/assets/{DevServerView-CAcUKOXd.js → DevServerView-0MWEJM7o.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-DVUl5jOk.js → DirectoryPicker-BjMPhCdh.js} +1 -1
- package/dist/client/assets/{DocumentsView-CnexOxtr.js → DocumentsView-ckH3rjmd.js} +1 -1
- package/dist/client/assets/{EvalsView-rTupzcDf.js → EvalsView-NyA644VF.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-CXE6Bj3V.js → ExperimentalAgentOnboardingModal-D0ZfbBRN.js} +1 -1
- package/dist/client/assets/{GoalsView-CjHwnnIi.js → GoalsView-Ctt5P_yo.js} +1 -1
- package/dist/client/assets/{InsightsView-C3XocYP-.js → InsightsView-NDp0bdNB.js} +1 -1
- package/dist/client/assets/{MemoryView-CsNHcx_d.js → MemoryView-BKbA4Bm4.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-BPTPoZBI.js → PiExtensionsManager-V3AFWMHm.js} +1 -1
- package/dist/client/assets/{PluginManager-CXadn8JE.js → PluginManager-D88G_TZh.js} +1 -1
- package/dist/client/assets/{PullRequestView-CC03CtZJ.js → PullRequestView-DQQmFFYC.js} +1 -1
- package/dist/client/assets/{ReportModal-Ci0na1eI.js → ReportModal-Clh1S8RF.js} +1 -1
- package/dist/client/assets/{ResearchView-DGWyAo8p.js → ResearchView-kxvq4zp5.js} +1 -1
- package/dist/client/assets/SecretsView-Dm0Vk8SA.js +1 -0
- package/dist/client/assets/{SessionTerminal-B22RlQeo.js → SessionTerminal-DJO4JTEX.js} +1 -1
- package/dist/client/assets/{SettingsModal-BkgBQZ19.js → SettingsModal-BN5mL_ym.js} +1 -1
- package/dist/client/assets/SettingsModal-CTf5voEW.js +21 -0
- package/dist/client/assets/{SettingsTextareaRow-0LXwMUQR.js → SettingsTextareaRow-CvKszqO-.js} +1 -1
- package/dist/client/assets/{SetupWizardModal-CyW3AlRO.js → SetupWizardModal-D5hVZh1G.js} +2 -2
- package/dist/client/assets/{SkillsView-3oqB1ubq.js → SkillsView-DQVz21w2.js} +1 -1
- package/dist/client/assets/{WorkflowNodeEditor-CfUK_xs1.js → WorkflowNodeEditor-CpKIi6Tb.js} +3 -3
- package/dist/client/assets/{agent-import-generation-C7-Cmo3M.js → agent-import-generation-DGUNF3nd.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-BQ7ZN1r_.js → architectureDiagram-3BPJPVTR-CV83wubP.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-Debw1Xka.js → blockDiagram-GPEHLZMM-CNQsA3dD.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-CiU212s3.js → c4Diagram-AAUBKEIU-CXe7ii5H.js} +1 -1
- package/dist/client/assets/channel-DoX862zl.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-BUJQ86zb.js → chunk-2J33WTMH-BS6W9bWv.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-C6GdEm1z.js → chunk-4BX2VUAB-DbehRhdW.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-CImvrxQL.js → chunk-55IACEB6-DMI5SR42.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-Dt-A3pkM.js → chunk-727SXJPM-D1d-GBVj.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-DDj4oXTx.js → chunk-AQP2D5EJ-BzO6K3Uf.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-BD4r1ukC.js → chunk-FMBD7UC4-BVRLV3lK.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-DFSteefi.js → chunk-ND2GUHAM-DdWByQfn.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-t2zF8tjB.js → chunk-QZHKN3VN-1ia12cRV.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-mb2IeAGN.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-mb2IeAGN.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-tkvDkXQA.js → cose-bilkent-S5V4N54A-BPDT41HR.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-DD2rW7wo.js → dagre-BM42HDAG-Cktn26EH.js} +1 -1
- package/dist/client/assets/{dashboard-view-B1vduc8x.js → dashboard-view-8o0EUcRX.js} +1 -1
- package/dist/client/assets/{dashboard-view-CciuDS5A.js → dashboard-view-CkCerlVJ.js} +1 -1
- package/dist/client/assets/{dashboard-view-DruCruAc.js → dashboard-view-D_HC4lnr.js} +1 -1
- package/dist/client/assets/{dashboard-view-C7jJ1lA4.js → dashboard-view-zd8GxC49.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-DASuxGNc.js → diagram-2AECGRRQ-But4Lvf-.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-f9WtiIUk.js → diagram-5GNKFQAL-2bpQAGFc.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-QivICYCd.js → diagram-KO2AKTUF-Ckn9Qd1u.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-CMwcDFHR.js → diagram-LMA3HP47-Bjn8h84N.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-C-vPMeHZ.js → diagram-OG6HWLK6-BI6541hx.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-CxGtk9vj.js → erDiagram-TEJ5UH35-BqMiwHiM.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-qBZRZoV1.js → flowDiagram-I6XJVG4X-Q_SAB10M.js} +1 -1
- package/dist/client/assets/{folder-open-Dysi9Zzg.js → folder-open-CHiAJbTN.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-DAaXroiu.js → ganttDiagram-6RSMTGT7-CqWFSfPF.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-DNUNQot_.js → gitGraphDiagram-PVQCEYII-B1o4uEjO.js} +1 -1
- package/dist/client/assets/index-Bdw5eNKV.css +1 -0
- package/dist/client/assets/index-kwRywd9M.js +2767 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-BSJmWLwh.js → infoDiagram-5YYISTIA-BzaMa0c2.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-AyVKvoDq.js → ishikawaDiagram-YF4QCWOH-I38yge00.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-972txzzd.js → journeyDiagram-JHISSGLW-WVQSgx4P.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-1xiNvIqK.js → kanban-definition-UN3LZRKU-BWtPQ3AH.js} +1 -1
- package/dist/client/assets/{mermaid.core-oeDA8jOm.js → mermaid.core-D1hnBtbi.js} +4 -4
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-DcktmI8s.js → mindmap-definition-RKZ34NQL-DqiSLkW7.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-ekBwLt-O.js → pieDiagram-4H26LBE5-CcgwbdIN.js} +1 -1
- package/dist/client/assets/{puzzle-I6pr6ZrW.js → puzzle-BhXUaTQl.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-D_wjTsvn.js → quadrantDiagram-W4KKPZXB-MsqQQfDN.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-CKGHyiJ1.js → requirementDiagram-4Y6WPE33-CILGjMMA.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-CtiZFNk5.js → sankeyDiagram-5OEKKPKP-c14fWbtR.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-B6OMrfGk.js → sequenceDiagram-3UESZ5HK-BD5bxMom.js} +1 -1
- package/dist/client/assets/{shield-alert-CVMmviMG.js → shield-alert-C9W2WywS.js} +1 -1
- package/dist/client/assets/{standing-instructions-template-Cg1a3tjW.js → standing-instructions-template-CJdbDSyg.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-CyTY2M_7.js → stateDiagram-AJRCARHV-CZ5YqeOn.js} +1 -1
- package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-NWPCCiqC.js → stateDiagram-v2-BHNVJYJU-mdMN-PNX.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-DU4TVYpw.js → timeline-definition-PNZ67QCA-DHKay9uj.js} +1 -1
- package/dist/client/assets/{upload-VCzeCiFH.js → upload-DxEwowRU.js} +1 -1
- package/dist/client/assets/{users-DB3knZtm.js → users-mNFgW_Zd.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-DX0Ppll6.js → vennDiagram-CIIHVFJN-X29K9hWM.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-CBoWxpDv.js → wardley-L42UT6IY-yEoTrfmN.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-B_eVqdhC.js → wardleyDiagram-YWT4CUSO-ny4k3kG0.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-DeZzEX0f.js → xychartDiagram-2RQKCTM6-DC8ODtRJ.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/package.json +1 -1
- package/dist/extension.js +411 -155
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/plugin-sdk/index.d.ts +159 -141
- package/dist/plugin-sdk/index.js +19 -0
- package/dist/plugins/fusion-plugin-acp-runtime/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-claude-runtime/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-claude-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-cli-printing-press/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
- package/dist/plugins/fusion-plugin-compound-engineering/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
- package/dist/plugins/fusion-plugin-cursor-runtime/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
- package/dist/plugins/fusion-plugin-droid-runtime/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-grok-runtime/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-grok-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-hermes-runtime/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-linear-import/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-linear-import/package.json +1 -1
- package/dist/plugins/fusion-plugin-omp-runtime/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-omp-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-paperclip-runtime/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-quality/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-quality/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-reports/package.json +1 -1
- package/dist/plugins/fusion-plugin-roadmap/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
- package/dist/plugins/fusion-plugin-todos/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-todos/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
- package/package.json +1 -1
- package/dist/client/assets/SecretsView-Cv2IuqFO.js +0 -1
- package/dist/client/assets/SettingsModal-CPRpRK71.js +0 -21
- package/dist/client/assets/channel-D-O4mRXx.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-BhHdSetA.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BhHdSetA.js +0 -1
- package/dist/client/assets/index-D0QgE5F5.js +0 -2767
- package/dist/client/assets/index-DHk_s6rA.css +0 -1
package/dist/extension.js
CHANGED
|
@@ -1103,6 +1103,9 @@ function isFusionSiblingBranch(branch) {
|
|
|
1103
1103
|
function resolveEffectiveAutoMerge(task, settings) {
|
|
1104
1104
|
return task.autoMerge ?? settings.autoMerge;
|
|
1105
1105
|
}
|
|
1106
|
+
function hasUserAutoMergeHold(task) {
|
|
1107
|
+
return task.autoMerge === false && task.autoMergeProvenance === "user";
|
|
1108
|
+
}
|
|
1106
1109
|
function allowsAutoMergeProcessing(task, settings) {
|
|
1107
1110
|
return (settings.autoMerge !== false || task.autoMerge === true) && !taskHasManualOpenPullRequest(task);
|
|
1108
1111
|
}
|
|
@@ -9968,6 +9971,7 @@ function validateForeach(node, topLevelNodeIds, columnIds) {
|
|
|
9968
9971
|
);
|
|
9969
9972
|
}
|
|
9970
9973
|
for (const inner of templateNodes) {
|
|
9974
|
+
validateReviewKind(inner, "nested");
|
|
9971
9975
|
if (inner.kind === "foreach" || inner.kind === "loop") {
|
|
9972
9976
|
throw new WorkflowIrError(
|
|
9973
9977
|
`foreach node '${node.id}' template may not contain nested loop/foreach ('${inner.id}')`
|
|
@@ -10086,6 +10090,7 @@ function validateLoop(node, topLevelNodeIds, columnIds) {
|
|
|
10086
10090
|
);
|
|
10087
10091
|
}
|
|
10088
10092
|
for (const inner of templateNodes) {
|
|
10093
|
+
validateReviewKind(inner, "nested");
|
|
10089
10094
|
if (inner.kind === "loop" || inner.kind === "foreach") {
|
|
10090
10095
|
throw new WorkflowIrError(
|
|
10091
10096
|
`loop node '${node.id}' template may not contain nested loop/foreach ('${inner.id}')`
|
|
@@ -10179,6 +10184,7 @@ function validateOptionalGroup(node, topLevelNodeIds, columnIds) {
|
|
|
10179
10184
|
throw new WorkflowIrError(`optional-group node '${node.id}' template has duplicate node ids`);
|
|
10180
10185
|
}
|
|
10181
10186
|
for (const inner of templateNodes) {
|
|
10187
|
+
validateReviewKind(inner, "nested");
|
|
10182
10188
|
if (inner.kind === "loop" || inner.kind === "foreach" || inner.kind === "optional-group") {
|
|
10183
10189
|
throw new WorkflowIrError(
|
|
10184
10190
|
`optional-group node '${node.id}' template may not contain nested loop/foreach/optional-group ('${inner.id}')`
|
|
@@ -10850,6 +10856,19 @@ function validateColumnRecovery(column) {
|
|
|
10850
10856
|
);
|
|
10851
10857
|
}
|
|
10852
10858
|
}
|
|
10859
|
+
function validateReviewKind(node, placement) {
|
|
10860
|
+
const value = node.config?.reviewKind;
|
|
10861
|
+
if (value === void 0) return;
|
|
10862
|
+
if (value !== "plan" && value !== "code") {
|
|
10863
|
+
throw new WorkflowIrError(`Workflow node '${node.id}' has invalid reviewKind '${String(value)}'; expected 'plan' or 'code'`);
|
|
10864
|
+
}
|
|
10865
|
+
if (placement === "nested") {
|
|
10866
|
+
throw new WorkflowIrError(`Workflow node '${node.id}' has reviewKind in an unsupported nested template placement`);
|
|
10867
|
+
}
|
|
10868
|
+
if (node.kind !== "prompt" && node.kind !== "gate" && node.kind !== "script" && node.kind !== "optional-group") {
|
|
10869
|
+
throw new WorkflowIrError(`Workflow node '${node.id}' has reviewKind on unsupported node kind '${node.kind}'`);
|
|
10870
|
+
}
|
|
10871
|
+
}
|
|
10853
10872
|
function validateV2(ir2) {
|
|
10854
10873
|
validateColumns(ir2);
|
|
10855
10874
|
for (const [index2, column] of ir2.columns.entries()) {
|
|
@@ -10872,6 +10891,7 @@ function validateV2(ir2) {
|
|
|
10872
10891
|
}
|
|
10873
10892
|
const nodesById = new Map(ir2.nodes.map((n) => [n.id, n]));
|
|
10874
10893
|
for (const node of ir2.nodes) {
|
|
10894
|
+
validateReviewKind(node, "top-level");
|
|
10875
10895
|
validateExtensionMetadata(`Workflow node '${node.id}'`, node.extensions);
|
|
10876
10896
|
if (node.column !== void 0 && !columnIds.has(node.column)) {
|
|
10877
10897
|
throw new WorkflowIrError(
|
|
@@ -13335,6 +13355,7 @@ function codeReviewOptionalGroupNode(column, options = {}) {
|
|
|
13335
13355
|
column,
|
|
13336
13356
|
config: {
|
|
13337
13357
|
name: CODE_REVIEW_NAME,
|
|
13358
|
+
reviewKind: "code",
|
|
13338
13359
|
// Default-ON: runs for every coding task by default, but operators can toggle it
|
|
13339
13360
|
// off per task (remove `code-review` from enabledWorkflowSteps).
|
|
13340
13361
|
defaultOn: options.defaultOn ?? true,
|
|
@@ -13480,6 +13501,7 @@ function planReviewOptionalGroupNode(column, options = {}) {
|
|
|
13480
13501
|
...column ? { column } : {},
|
|
13481
13502
|
config: {
|
|
13482
13503
|
name: PLAN_REVIEW_NAME,
|
|
13504
|
+
reviewKind: "plan",
|
|
13483
13505
|
defaultOn: options.defaultOn ?? true,
|
|
13484
13506
|
/*
|
|
13485
13507
|
* FNXC:WorkflowRemediation 2026-06-29-12:14:
|
|
@@ -63324,7 +63346,7 @@ function rowToTask(row) {
|
|
|
63324
63346
|
executionStartBranch: row.executionStartBranch || void 0,
|
|
63325
63347
|
branch: row.branch || void 0,
|
|
63326
63348
|
autoMerge: row.autoMerge === null ? void 0 : row.autoMerge === 1,
|
|
63327
|
-
autoMergeProvenance: row.autoMergeProvenance === "user" || row.autoMergeProvenance === "legacy-stamp" ? row.autoMergeProvenance : void 0,
|
|
63349
|
+
autoMergeProvenance: row.autoMergeProvenance === "user" || row.autoMergeProvenance === "mission" || row.autoMergeProvenance === "legacy-stamp" ? row.autoMergeProvenance : void 0,
|
|
63328
63350
|
baseCommitSha: row.baseCommitSha || void 0,
|
|
63329
63351
|
scopeOverride: row.scopeOverride ? true : void 0,
|
|
63330
63352
|
scopeOverrideReason: row.scopeOverrideReason || void 0,
|
|
@@ -72840,10 +72862,10 @@ ${feature.acceptanceCriteria}`);
|
|
|
72840
72862
|
Apply the selected Missions header workflow atomically during TaskStore.createTask so newly triaged features land in the intended workflow lane. Duplicate-guard reuses skip this create path, preserving existing duplicate tasks without workflow mutation.
|
|
72841
72863
|
*/
|
|
72842
72864
|
/*
|
|
72843
|
-
FNXC:MissionAutoMerge 2026-
|
|
72844
|
-
An autoMerge:false mission stamps each newly triaged task so its shared branch produces one PR instead of per-task auto-merges. Duplicate reuse intentionally bypasses this create-only override.
|
|
72865
|
+
FNXC:MissionAutoMerge 2026-08-05-22:50:
|
|
72866
|
+
An autoMerge:false mission stamps each newly triaged task as mission policy so its shared branch produces one PR instead of per-task auto-merges. Duplicate reuse intentionally bypasses this create-only override; policy must not impersonate an operator manual-hold choice.
|
|
72845
72867
|
*/
|
|
72846
|
-
...mission?.autoMerge === false ? { autoMerge: false } : {},
|
|
72868
|
+
...mission?.autoMerge === false ? { autoMerge: false, autoMergeProvenance: "mission" } : {},
|
|
72847
72869
|
// FNXC:MissionTaskPrefix 2026-07-26-12:00: thread the mission's optional taskPrefix into TaskCreateInput for distributed id minting.
|
|
72848
72870
|
...mission?.taskPrefix ? { taskPrefix: mission.taskPrefix } : {},
|
|
72849
72871
|
...branchOptions?.workflowId !== void 0 ? { workflowId: branchOptions.workflowId } : {}
|
|
@@ -76021,10 +76043,10 @@ ${feature.acceptanceCriteria}`);
|
|
|
76021
76043
|
}
|
|
76022
76044
|
} : {},
|
|
76023
76045
|
/*
|
|
76024
|
-
FNXC:MissionAutoMerge 2026-
|
|
76025
|
-
An autoMerge:false mission stamps each newly triaged task so its shared branch produces one PR instead of per-task auto-merges. Duplicate reuse intentionally bypasses this create-only override.
|
|
76046
|
+
FNXC:MissionAutoMerge 2026-08-05-22:50:
|
|
76047
|
+
An autoMerge:false mission stamps each newly triaged task as mission policy so its shared branch produces one PR instead of per-task auto-merges. Duplicate reuse intentionally bypasses this create-only override; policy must not impersonate an operator manual-hold choice.
|
|
76026
76048
|
*/
|
|
76027
|
-
...mission?.autoMerge === false ? { autoMerge: false } : {},
|
|
76049
|
+
...mission?.autoMerge === false ? { autoMerge: false, autoMergeProvenance: "mission" } : {},
|
|
76028
76050
|
// FNXC:MissionTaskPrefix 2026-07-26-12:00: thread the mission's optional taskPrefix into TaskCreateInput so the distributed allocator mints ERR-N (etc.) instead of the project prefix.
|
|
76029
76051
|
...mission?.taskPrefix ? { taskPrefix: mission.taskPrefix } : {},
|
|
76030
76052
|
...branchOptions?.workflowId !== void 0 ? { workflowId: branchOptions.workflowId } : {}
|
|
@@ -86375,7 +86397,9 @@ async function _createTaskInternalBackendImpl(store, input, title, resolvedWorkf
|
|
|
86375
86397
|
sourceMetadata: withTaskBranchContextInSourceMetadata(input.source?.sourceMetadata, input.branchContext),
|
|
86376
86398
|
branchContext: input.branchContext,
|
|
86377
86399
|
autoMerge: input.autoMerge,
|
|
86378
|
-
|
|
86400
|
+
// FNXC:SharedBranchMemberHold 2026-08-05-22:50: trusted mission creation
|
|
86401
|
+
// preserves policy provenance; operator/API create requests retain user provenance.
|
|
86402
|
+
autoMergeProvenance: input.autoMerge === void 0 ? void 0 : input.autoMergeProvenance ?? "user",
|
|
86379
86403
|
// FNXC:CodingIdeasWorkflow 2026-07-05-19:45: land the task in its
|
|
86380
86404
|
// workflow's manual intake column (e.g. Coding (Ideas) → "ideas") when
|
|
86381
86405
|
// no explicit column is given (main FN-7591 parity).
|
|
@@ -127080,6 +127104,36 @@ var init_async_knowledge = __esm({
|
|
|
127080
127104
|
});
|
|
127081
127105
|
|
|
127082
127106
|
// ../core/src/workflows/workflow-step-results.ts
|
|
127107
|
+
function normalizeWorkflowReviewFindings(raw) {
|
|
127108
|
+
if (!Array.isArray(raw)) return void 0;
|
|
127109
|
+
const normalized = [];
|
|
127110
|
+
const usedIds = /* @__PURE__ */ new Set();
|
|
127111
|
+
for (const candidate of raw) {
|
|
127112
|
+
if (normalized.length >= MAX_WORKFLOW_REVIEW_FINDINGS || !candidate || typeof candidate !== "object") continue;
|
|
127113
|
+
const value = candidate;
|
|
127114
|
+
const title = boundedTrimmedString(value.title, MAX_FINDING_TITLE_LENGTH);
|
|
127115
|
+
const body = boundedTrimmedString(value.body, MAX_FINDING_BODY_LENGTH);
|
|
127116
|
+
if (!title || !body) continue;
|
|
127117
|
+
const baseId = boundedTrimmedString(value.id, MAX_FINDING_ID_LENGTH) || `finding-${normalized.length + 1}`;
|
|
127118
|
+
let id = baseId;
|
|
127119
|
+
let suffix = 2;
|
|
127120
|
+
while (usedIds.has(id)) id = `${baseId.slice(0, Math.max(1, MAX_FINDING_ID_LENGTH - String(suffix).length - 1))}-${suffix++}`;
|
|
127121
|
+
usedIds.add(id);
|
|
127122
|
+
const filePath = boundedTrimmedString(value.filePath, MAX_FINDING_PATH_LENGTH);
|
|
127123
|
+
const line2 = typeof value.line === "number" && Number.isFinite(value.line) && value.line > 0 ? Math.floor(value.line) : void 0;
|
|
127124
|
+
const severity = isWorkflowReviewFindingSeverity(value.severity) ? value.severity : void 0;
|
|
127125
|
+
normalized.push({ id, title, body, ...filePath ? { filePath } : {}, ...line2 ? { line: line2 } : {}, ...severity ? { severity } : {} });
|
|
127126
|
+
}
|
|
127127
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
127128
|
+
}
|
|
127129
|
+
function boundedTrimmedString(value, maxLength) {
|
|
127130
|
+
if (typeof value !== "string") return void 0;
|
|
127131
|
+
const trimmed = value.trim();
|
|
127132
|
+
return trimmed && trimmed.length <= maxLength ? trimmed : void 0;
|
|
127133
|
+
}
|
|
127134
|
+
function isWorkflowReviewFindingSeverity(value) {
|
|
127135
|
+
return typeof value === "string" && WORKFLOW_REVIEW_FINDING_SEVERITIES.includes(value);
|
|
127136
|
+
}
|
|
127083
127137
|
function isTerminalFailure(result2) {
|
|
127084
127138
|
return TERMINAL_FAILURE_STATUSES.has(result2.status);
|
|
127085
127139
|
}
|
|
@@ -127145,10 +127199,16 @@ function makeReviewLeaseRecord(args) {
|
|
|
127145
127199
|
leaseOwner: args.owner
|
|
127146
127200
|
};
|
|
127147
127201
|
}
|
|
127148
|
-
var MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS, TERMINAL_FAILURE_STATUSES, PLAN_REVIEW_LEASE_STALENESS_MS, TERMINAL_STEP_STATUSES;
|
|
127202
|
+
var WORKFLOW_REVIEW_FINDING_SEVERITIES, MAX_WORKFLOW_REVIEW_FINDINGS, MAX_FINDING_ID_LENGTH, MAX_FINDING_TITLE_LENGTH, MAX_FINDING_BODY_LENGTH, MAX_FINDING_PATH_LENGTH, MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS, TERMINAL_FAILURE_STATUSES, PLAN_REVIEW_LEASE_STALENESS_MS, TERMINAL_STEP_STATUSES;
|
|
127149
127203
|
var init_workflow_step_results = __esm({
|
|
127150
127204
|
"../core/src/workflows/workflow-step-results.ts"() {
|
|
127151
127205
|
"use strict";
|
|
127206
|
+
WORKFLOW_REVIEW_FINDING_SEVERITIES = ["low", "medium", "high", "critical"];
|
|
127207
|
+
MAX_WORKFLOW_REVIEW_FINDINGS = 20;
|
|
127208
|
+
MAX_FINDING_ID_LENGTH = 128;
|
|
127209
|
+
MAX_FINDING_TITLE_LENGTH = 240;
|
|
127210
|
+
MAX_FINDING_BODY_LENGTH = 4e3;
|
|
127211
|
+
MAX_FINDING_PATH_LENGTH = 1e3;
|
|
127152
127212
|
MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS = 5;
|
|
127153
127213
|
TERMINAL_FAILURE_STATUSES = /* @__PURE__ */ new Set([
|
|
127154
127214
|
"failed",
|
|
@@ -127217,6 +127277,7 @@ var init_task_timing = __esm({
|
|
|
127217
127277
|
});
|
|
127218
127278
|
|
|
127219
127279
|
// ../core/src/session-identity-registry.ts
|
|
127280
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
127220
127281
|
import { resolve as resolve21 } from "node:path";
|
|
127221
127282
|
import { realpathSync as realpathSync5 } from "node:fs";
|
|
127222
127283
|
function getRegistry() {
|
|
@@ -127228,6 +127289,16 @@ function getRegistry() {
|
|
|
127228
127289
|
}
|
|
127229
127290
|
return registry3;
|
|
127230
127291
|
}
|
|
127292
|
+
function getActiveInvocationStorage() {
|
|
127293
|
+
const holder = globalThis;
|
|
127294
|
+
const existing = holder[ACTIVE_INVOCATION_KEY];
|
|
127295
|
+
if (existing instanceof AsyncLocalStorage) {
|
|
127296
|
+
return existing;
|
|
127297
|
+
}
|
|
127298
|
+
const storage = new AsyncLocalStorage();
|
|
127299
|
+
holder[ACTIVE_INVOCATION_KEY] = storage;
|
|
127300
|
+
return storage;
|
|
127301
|
+
}
|
|
127231
127302
|
function canonicalizeCwd(cwd) {
|
|
127232
127303
|
const resolved = resolve21(cwd);
|
|
127233
127304
|
try {
|
|
@@ -127255,8 +127326,13 @@ function registerFusionSessionIdentity(cwd, identity) {
|
|
|
127255
127326
|
};
|
|
127256
127327
|
}
|
|
127257
127328
|
function resolveFusionSessionPrincipal(cwd) {
|
|
127329
|
+
const canonicalCwd = canonicalizeCwd(cwd);
|
|
127330
|
+
const activeInvocation = getActiveInvocationStorage().getStore();
|
|
127331
|
+
if (activeInvocation?.cwdKeys.has(canonicalCwd)) {
|
|
127332
|
+
return { kind: "agent", identity: activeInvocation.identity };
|
|
127333
|
+
}
|
|
127258
127334
|
const registry3 = getRegistry();
|
|
127259
|
-
const list = registry3.get(
|
|
127335
|
+
const list = registry3.get(canonicalCwd);
|
|
127260
127336
|
if (!list || list.length === 0) {
|
|
127261
127337
|
return { kind: "operator" };
|
|
127262
127338
|
}
|
|
@@ -127265,14 +127341,24 @@ function resolveFusionSessionPrincipal(cwd) {
|
|
|
127265
127341
|
}
|
|
127266
127342
|
return { kind: "ambiguous", identities: [...list] };
|
|
127267
127343
|
}
|
|
127344
|
+
function runWithFusionSessionIdentity(cwdKeys, identity, callback) {
|
|
127345
|
+
const invocation = {
|
|
127346
|
+
identity: { ...identity, registeredAt: Date.now() },
|
|
127347
|
+
cwdKeys: new Set(cwdKeys.map(canonicalizeCwd))
|
|
127348
|
+
};
|
|
127349
|
+
return getActiveInvocationStorage().run(invocation, callback);
|
|
127350
|
+
}
|
|
127268
127351
|
function __clearFusionSessionIdentityRegistryForTests() {
|
|
127269
|
-
|
|
127352
|
+
const holder = globalThis;
|
|
127353
|
+
holder[REGISTRY_KEY] = /* @__PURE__ */ new Map();
|
|
127354
|
+
holder[ACTIVE_INVOCATION_KEY] = new AsyncLocalStorage();
|
|
127270
127355
|
}
|
|
127271
|
-
var REGISTRY_KEY;
|
|
127356
|
+
var REGISTRY_KEY, ACTIVE_INVOCATION_KEY;
|
|
127272
127357
|
var init_session_identity_registry = __esm({
|
|
127273
127358
|
"../core/src/session-identity-registry.ts"() {
|
|
127274
127359
|
"use strict";
|
|
127275
127360
|
REGISTRY_KEY = "__FUSION_SESSION_IDENTITY_REGISTRY_V1__";
|
|
127361
|
+
ACTIVE_INVOCATION_KEY = "__FUSION_SESSION_IDENTITY_ACTIVE_INVOCATION_V1__";
|
|
127276
127362
|
}
|
|
127277
127363
|
});
|
|
127278
127364
|
|
|
@@ -127635,6 +127721,7 @@ __export(src_exports, {
|
|
|
127635
127721
|
MAX_TITLE_LENGTH: () => MAX_TITLE_LENGTH,
|
|
127636
127722
|
MAX_TITLE_SUMMARIZE_INPUT_LENGTH: () => MAX_TITLE_SUMMARIZE_INPUT_LENGTH,
|
|
127637
127723
|
MAX_WORKFLOW_ICON_LENGTH: () => MAX_WORKFLOW_ICON_LENGTH,
|
|
127724
|
+
MAX_WORKFLOW_REVIEW_FINDINGS: () => MAX_WORKFLOW_REVIEW_FINDINGS,
|
|
127638
127725
|
MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS: () => MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS,
|
|
127639
127726
|
MEMORY_AUDIT_PATH: () => MEMORY_AUDIT_PATH,
|
|
127640
127727
|
MEMORY_BACKEND_SETTINGS_KEYS: () => MEMORY_BACKEND_SETTINGS_KEYS,
|
|
@@ -127836,6 +127923,7 @@ __export(src_exports, {
|
|
|
127836
127923
|
WAKE_DELTA_ASSIGNED_TASKS_CAP: () => WAKE_DELTA_ASSIGNED_TASKS_CAP,
|
|
127837
127924
|
WEDGE_RENOTIFY_COOLDOWN_MS: () => WEDGE_RENOTIFY_COOLDOWN_MS,
|
|
127838
127925
|
WORKFLOW_EXTENSION_SCHEMA_VERSION: () => WORKFLOW_EXTENSION_SCHEMA_VERSION,
|
|
127926
|
+
WORKFLOW_REVIEW_FINDING_SEVERITIES: () => WORKFLOW_REVIEW_FINDING_SEVERITIES,
|
|
127839
127927
|
WORKFLOW_SETTING_REJECTION_CODES: () => WORKFLOW_SETTING_REJECTION_CODES,
|
|
127840
127928
|
WORKFLOW_SETTING_TYPES: () => WORKFLOW_SETTING_TYPES,
|
|
127841
127929
|
WORKFLOW_WORK_ITEM_KINDS: () => WORKFLOW_WORK_ITEM_KINDS,
|
|
@@ -128241,6 +128329,7 @@ __export(src_exports, {
|
|
|
128241
128329
|
hasRoleOverrides: () => hasRoleOverrides,
|
|
128242
128330
|
hasSyncPassphraseConfigured: () => hasSyncPassphraseConfigured,
|
|
128243
128331
|
hasTitleIdDrift: () => hasTitleIdDrift,
|
|
128332
|
+
hasUserAutoMergeHold: () => hasUserAutoMergeHold,
|
|
128244
128333
|
hashWorkflowIr: () => hashWorkflowIr,
|
|
128245
128334
|
healSchemaDrift: () => healSchemaDrift,
|
|
128246
128335
|
importMcpServersJson: () => importMcpServersJson,
|
|
@@ -128363,6 +128452,7 @@ __export(src_exports, {
|
|
|
128363
128452
|
isWipColumn: () => isWipColumn,
|
|
128364
128453
|
isWipColumnRole: () => isWipColumnRole,
|
|
128365
128454
|
isWorkflowOptionalGroupEnabled: () => isWorkflowOptionalGroupEnabled,
|
|
128455
|
+
isWorkflowReviewFindingSeverity: () => isWorkflowReviewFindingSeverity,
|
|
128366
128456
|
isWorkspaceTask: () => isWorkspaceTask,
|
|
128367
128457
|
layoutForIr: () => layoutForIr,
|
|
128368
128458
|
listActiveAiSessions: () => listActiveAiSessions,
|
|
@@ -128436,6 +128526,7 @@ __export(src_exports, {
|
|
|
128436
128526
|
normalizeValidationDiagnostics: () => normalizeValidationDiagnostics,
|
|
128437
128527
|
normalizeWorkflowIcon: () => normalizeWorkflowIcon,
|
|
128438
128528
|
normalizeWorkflowPromptOverrides: () => normalizeWorkflowPromptOverrides,
|
|
128529
|
+
normalizeWorkflowReviewFindings: () => normalizeWorkflowReviewFindings,
|
|
128439
128530
|
notifyOperatorOfNonOperatorDelete: () => notifyOperatorOfNonOperatorDelete,
|
|
128440
128531
|
overseerAdviceSeverityRank: () => overseerAdviceSeverityRank,
|
|
128441
128532
|
parseAgentManifest: () => parseAgentManifest,
|
|
@@ -128699,6 +128790,7 @@ __export(src_exports, {
|
|
|
128699
128790
|
runMemoryBackupCommand: () => runMemoryBackupCommand,
|
|
128700
128791
|
runReconciliationAbort: () => runReconciliationAbort,
|
|
128701
128792
|
runScheduledEvalBatch: () => runScheduledEvalBatch,
|
|
128793
|
+
runWithFusionSessionIdentity: () => runWithFusionSessionIdentity,
|
|
128702
128794
|
sanitizeBranchSegment: () => sanitizeBranchSegment,
|
|
128703
128795
|
sanitizeCliAgentSettings: () => sanitizeCliAgentSettings,
|
|
128704
128796
|
sanitizeCliAgentsSettings: () => sanitizeCliAgentsSettings,
|
|
@@ -157348,6 +157440,7 @@ import { existsSync as existsSync43, readFileSync as readFileSync15, realpathSyn
|
|
|
157348
157440
|
import { exec as exec2, execFile as execFile7 } from "node:child_process";
|
|
157349
157441
|
import { promisify as promisify6 } from "node:util";
|
|
157350
157442
|
import { createRequire as createRequire5 } from "node:module";
|
|
157443
|
+
import { randomUUID as randomUUID42 } from "node:crypto";
|
|
157351
157444
|
import { basename as basename13, dirname as dirname21, join as join56, relative as relative6, isAbsolute as isAbsolute8, resolve as resolve24 } from "node:path";
|
|
157352
157445
|
import {
|
|
157353
157446
|
createAgentSession,
|
|
@@ -159058,7 +159151,8 @@ async function createFnAgent2(options) {
|
|
|
159058
159151
|
});
|
|
159059
159152
|
};
|
|
159060
159153
|
const sessionIdentity = (() => {
|
|
159061
|
-
const
|
|
159154
|
+
const namedAgentId = options.actionGateContext?.agentId ?? options.permanentAgentGating?.requester?.actorId;
|
|
159155
|
+
const principalAgentId = namedAgentId ?? `engine-session-${randomUUID42()}`;
|
|
159062
159156
|
const principalAgentName = options.actionGateContext?.agentName ?? options.permanentAgentGating?.requester?.actorName;
|
|
159063
159157
|
return {
|
|
159064
159158
|
agentId: principalAgentId,
|
|
@@ -159070,6 +159164,14 @@ async function createFnAgent2(options) {
|
|
|
159070
159164
|
const sessionIdentityKeys = [...new Set([options.cwd, resolvedProjectRoot].filter((key) => Boolean(key)))];
|
|
159071
159165
|
const attachSessionIdentity = (session) => {
|
|
159072
159166
|
const identityDisposers = sessionIdentityKeys.map((key) => registerFusionSessionIdentity(key, sessionIdentity));
|
|
159167
|
+
const sessionInvocations = session;
|
|
159168
|
+
const wrapInvocation = (methodName) => {
|
|
159169
|
+
const original = sessionInvocations[methodName];
|
|
159170
|
+
if (typeof original !== "function") return;
|
|
159171
|
+
sessionInvocations[methodName] = (...args) => runWithFusionSessionIdentity(sessionIdentityKeys, sessionIdentity, () => original.apply(session, args));
|
|
159172
|
+
};
|
|
159173
|
+
wrapInvocation("prompt");
|
|
159174
|
+
wrapInvocation("promptWithFallback");
|
|
159073
159175
|
const disposeBeforeIdentity = typeof session.dispose === "function" ? session.dispose.bind(session) : () => void 0;
|
|
159074
159176
|
session.dispose = async () => {
|
|
159075
159177
|
for (const disposeIdentity of identityDisposers) {
|
|
@@ -167269,10 +167371,11 @@ function createMergeAttemptHandler(deps) {
|
|
|
167269
167371
|
);
|
|
167270
167372
|
};
|
|
167271
167373
|
}
|
|
167272
|
-
function createMergeGateHandler() {
|
|
167374
|
+
function createMergeGateHandler(deps = {}) {
|
|
167273
167375
|
return async (_node, ctx) => {
|
|
167274
167376
|
const settingsAutoMerge = ctx.settings?.autoMerge;
|
|
167275
|
-
const
|
|
167377
|
+
const settings = { autoMerge: settingsAutoMerge ?? true };
|
|
167378
|
+
const autoMerge = !hasUserAutoMergeHold(ctx.task) && (await deps.isLiveSharedBranchMember?.(ctx.task, settings) === true || ctx.task.autoMerge !== false && settings.autoMerge !== false);
|
|
167276
167379
|
return {
|
|
167277
167380
|
outcome: "success",
|
|
167278
167381
|
value: autoMerge ? "auto-on" : "auto-off"
|
|
@@ -167282,6 +167385,7 @@ function createMergeGateHandler() {
|
|
|
167282
167385
|
var init_merge_runner = __esm({
|
|
167283
167386
|
"../engine/src/workflow-node-runners/merge-runner.ts"() {
|
|
167284
167387
|
"use strict";
|
|
167388
|
+
init_src2();
|
|
167285
167389
|
init_workflow_merge_nodes();
|
|
167286
167390
|
}
|
|
167287
167391
|
});
|
|
@@ -167625,7 +167729,7 @@ function createDefaultNodeHandlers(seams, runCustomNode, deps) {
|
|
|
167625
167729
|
"parse-steps": parseSteps,
|
|
167626
167730
|
code: createCodeNodeHandler(deps?.runCode),
|
|
167627
167731
|
notify: createNotifyHandler(deps?.notifyDispatch),
|
|
167628
|
-
"merge-gate": createMergeGateHandler(),
|
|
167732
|
+
"merge-gate": createMergeGateHandler({ isLiveSharedBranchMember: deps?.isLiveSharedBranchMember }),
|
|
167629
167733
|
"merge-attempt": createMergeAttemptHandler({
|
|
167630
167734
|
primitives: deps?.primitives,
|
|
167631
167735
|
seams,
|
|
@@ -168023,7 +168127,7 @@ var init_code_node_runner = __esm({
|
|
|
168023
168127
|
// ../engine/src/agent-tools.ts
|
|
168024
168128
|
import { appendFile as appendFile3, mkdir as mkdir26, readFile as readFile32, readdir as readdir9, realpath, stat as stat9, writeFile as writeFile24 } from "node:fs/promises";
|
|
168025
168129
|
import { existsSync as existsSync50 } from "node:fs";
|
|
168026
|
-
import { createHash as createHash19, randomUUID as
|
|
168130
|
+
import { createHash as createHash19, randomUUID as randomUUID43 } from "node:crypto";
|
|
168027
168131
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
168028
168132
|
import { extname as extname3, isAbsolute as isAbsolute12, join as join62, relative as relative11, resolve as resolve33, sep as sep9 } from "node:path";
|
|
168029
168133
|
import { Type as Type2 } from "@earendil-works/pi-ai";
|
|
@@ -168669,7 +168773,7 @@ function createTaskCreateTool(store, provenance, options) {
|
|
|
168669
168773
|
toType: "user",
|
|
168670
168774
|
type: "agent-to-user",
|
|
168671
168775
|
content: `Task proposal awaiting validation: ${title}`,
|
|
168672
|
-
metadata: { kind: "task-proposal", proposalStatus: "pending", proposalIdempotencyKey:
|
|
168776
|
+
metadata: { kind: "task-proposal", proposalStatus: "pending", proposalIdempotencyKey: randomUUID43(), taskId: options.sourceTaskId, proposedTask: { title, description: params.description, priority: params.priority, workflowId: params.workflow_id, dependencies: params.dependencies } }
|
|
168673
168777
|
});
|
|
168674
168778
|
return { content: [{ type: "text", text: "Task proposal submitted to the operator for validation; no task was created." }], details: { proposed: true } };
|
|
168675
168779
|
}
|
|
@@ -173094,37 +173198,6 @@ var init_effective_settings = __esm({
|
|
|
173094
173198
|
}
|
|
173095
173199
|
});
|
|
173096
173200
|
|
|
173097
|
-
// ../engine/src/errors/transient-merge-error-classifier.ts
|
|
173098
|
-
function classifyTransientMergeError(error3) {
|
|
173099
|
-
if (!error3) return null;
|
|
173100
|
-
if (/lease-handoff-failed[^a-z]+target-not-queued/i.test(error3)) {
|
|
173101
|
-
return "lease-handoff-target-not-queued";
|
|
173102
|
-
}
|
|
173103
|
-
if (/\bACP turn failed\b/i.test(error3) || /\bacp rpc code -32(?:603|00[0-3])\b/i.test(error3)) {
|
|
173104
|
-
return "ai-provider-turn-failure";
|
|
173105
|
-
}
|
|
173106
|
-
if (/\bspawn(?:\s+\S+)?\s+ENO(?:TDIR|ENT)\b/i.test(error3)) {
|
|
173107
|
-
return "process-spawn-failure";
|
|
173108
|
-
}
|
|
173109
|
-
if (/\bis not a working tree\b/i.test(error3)) {
|
|
173110
|
-
return "process-spawn-failure";
|
|
173111
|
-
}
|
|
173112
|
-
const sameSha = error3.match(/advanced concurrently \(expected ([0-9a-f]{7,40}),\s+observed ([0-9a-f]{7,40})\)/i);
|
|
173113
|
-
if (sameSha && sameSha[1].toLowerCase() === sameSha[2].toLowerCase()) {
|
|
173114
|
-
return "spurious-concurrent-advance-same-sha";
|
|
173115
|
-
}
|
|
173116
|
-
if (isTransientError(error3)) {
|
|
173117
|
-
return "network-transport-failure";
|
|
173118
|
-
}
|
|
173119
|
-
return null;
|
|
173120
|
-
}
|
|
173121
|
-
var init_transient_merge_error_classifier = __esm({
|
|
173122
|
-
"../engine/src/errors/transient-merge-error-classifier.ts"() {
|
|
173123
|
-
"use strict";
|
|
173124
|
-
init_transient_error_patterns();
|
|
173125
|
-
}
|
|
173126
|
-
});
|
|
173127
|
-
|
|
173128
173201
|
// ../engine/src/notification/ntfy-provider.ts
|
|
173129
173202
|
function resolveParticipantLabel(metadata, kind) {
|
|
173130
173203
|
const nameKey = kind === "from" ? "fromName" : "toName";
|
|
@@ -173598,7 +173671,50 @@ var init_webhook_provider = __esm({
|
|
|
173598
173671
|
}
|
|
173599
173672
|
});
|
|
173600
173673
|
|
|
173674
|
+
// ../engine/src/errors/transient-merge-error-classifier.ts
|
|
173675
|
+
function classifyTransientMergeError(error3) {
|
|
173676
|
+
if (!error3) return null;
|
|
173677
|
+
if (/lease-handoff-failed[^a-z]+target-not-queued/i.test(error3)) {
|
|
173678
|
+
return "lease-handoff-target-not-queued";
|
|
173679
|
+
}
|
|
173680
|
+
if (/\bACP turn failed\b/i.test(error3) || /\bacp rpc code -32(?:603|00[0-3])\b/i.test(error3)) {
|
|
173681
|
+
return "ai-provider-turn-failure";
|
|
173682
|
+
}
|
|
173683
|
+
if (/\bspawn(?:\s+\S+)?\s+ENO(?:TDIR|ENT)\b/i.test(error3)) {
|
|
173684
|
+
return "process-spawn-failure";
|
|
173685
|
+
}
|
|
173686
|
+
if (/\bis not a working tree\b/i.test(error3)) {
|
|
173687
|
+
return "process-spawn-failure";
|
|
173688
|
+
}
|
|
173689
|
+
const sameSha = error3.match(/advanced concurrently \(expected ([0-9a-f]{7,40}),\s+observed ([0-9a-f]{7,40})\)/i);
|
|
173690
|
+
if (sameSha && sameSha[1].toLowerCase() === sameSha[2].toLowerCase()) {
|
|
173691
|
+
return "spurious-concurrent-advance-same-sha";
|
|
173692
|
+
}
|
|
173693
|
+
if (isTransientError(error3)) {
|
|
173694
|
+
return "network-transport-failure";
|
|
173695
|
+
}
|
|
173696
|
+
return null;
|
|
173697
|
+
}
|
|
173698
|
+
function hasTransientMergeRecoveryOwner(task) {
|
|
173699
|
+
return classifyTransientMergeError(task.error) !== null && typeof task.mergeTransientRetryCount === "number" && Number.isInteger(task.mergeTransientRetryCount) && task.mergeTransientRetryCount >= 0 && task.mergeTransientRetryCount < MAX_AUTO_MERGE_TRANSIENT_RETRIES;
|
|
173700
|
+
}
|
|
173701
|
+
var MAX_AUTO_MERGE_TRANSIENT_RETRIES;
|
|
173702
|
+
var init_transient_merge_error_classifier = __esm({
|
|
173703
|
+
"../engine/src/errors/transient-merge-error-classifier.ts"() {
|
|
173704
|
+
"use strict";
|
|
173705
|
+
init_transient_error_patterns();
|
|
173706
|
+
MAX_AUTO_MERGE_TRANSIENT_RETRIES = 5;
|
|
173707
|
+
}
|
|
173708
|
+
});
|
|
173709
|
+
|
|
173601
173710
|
// ../engine/src/notification/task-wedge-notification.ts
|
|
173711
|
+
function describeTaskRecoveryOwner(task) {
|
|
173712
|
+
if (hasTransientMergeRecoveryOwner(task)) return { kind: "transient-merge-retry" };
|
|
173713
|
+
if (typeof task.recoveryRetryCount === "number" && Number.isInteger(task.recoveryRetryCount) && task.recoveryRetryCount >= 0 && typeof task.nextRecoveryAt === "string" && Number.isFinite(Date.parse(task.nextRecoveryAt))) {
|
|
173714
|
+
return { kind: "scheduled-recovery" };
|
|
173715
|
+
}
|
|
173716
|
+
return null;
|
|
173717
|
+
}
|
|
173602
173718
|
function describeSelfHealingNoActionWedge(task, stage, metadata) {
|
|
173603
173719
|
const description = SELF_HEALING_NO_ACTIONS[stage];
|
|
173604
173720
|
if (!description || task.userPaused || task.paused || task.autoMerge === false) return null;
|
|
@@ -173656,6 +173772,7 @@ function describeTaskWedge(task) {
|
|
|
173656
173772
|
if (/merge.*(?:blocked|verification|gate)|(?:verification|gate).*(?:failed|exhaust)/i.test(error3)) {
|
|
173657
173773
|
return { reasonKey: "merge-blocked", reason: "Merge verification cannot progress without operator action.", action: "Fix the failing verification, then retry the task." };
|
|
173658
173774
|
}
|
|
173775
|
+
if (describeTaskRecoveryOwner(task)) return null;
|
|
173659
173776
|
if (!error3 && (task.mergeRetries ?? 0) < 3) return null;
|
|
173660
173777
|
return {
|
|
173661
173778
|
reasonKey: "terminal-failed",
|
|
@@ -173667,6 +173784,7 @@ var SELF_HEALING_NO_ACTIONS;
|
|
|
173667
173784
|
var init_task_wedge_notification = __esm({
|
|
173668
173785
|
"../engine/src/notification/task-wedge-notification.ts"() {
|
|
173669
173786
|
"use strict";
|
|
173787
|
+
init_transient_merge_error_classifier();
|
|
173670
173788
|
SELF_HEALING_NO_ACTIONS = {
|
|
173671
173789
|
"reclaim-pr-conflict": {
|
|
173672
173790
|
reason: "Self-healing could not reclaim a stalled pull-request conflict.",
|
|
@@ -173732,7 +173850,6 @@ var init_notification_service = __esm({
|
|
|
173732
173850
|
init_src2();
|
|
173733
173851
|
init_notifier();
|
|
173734
173852
|
init_logger3();
|
|
173735
|
-
init_transient_merge_error_classifier();
|
|
173736
173853
|
init_ntfy_provider();
|
|
173737
173854
|
init_webhook_provider();
|
|
173738
173855
|
init_task_wedge_notification();
|
|
@@ -173958,10 +174075,10 @@ var init_notification_service = __esm({
|
|
|
173958
174075
|
}
|
|
173959
174076
|
}
|
|
173960
174077
|
handleTaskUpdated = (task, meta2) => {
|
|
173961
|
-
const
|
|
173962
|
-
const wedge =
|
|
174078
|
+
const recoveryOwner = task.status === "failed" ? describeTaskRecoveryOwner(task) : null;
|
|
174079
|
+
const wedge = describeTaskWedge(task);
|
|
173963
174080
|
if (wedge) this.cancelPendingFailureNotification(task.id, "classified-terminal-wedge");
|
|
173964
|
-
|
|
174081
|
+
void this.enqueueWedgeHandling(task.id, () => this.maybeNotifyTaskWedge(task, wedge));
|
|
173965
174082
|
void this.maybeSuppressTransientFailedNotification(task, `status=${task.status ?? "undefined"}`);
|
|
173966
174083
|
if (task.status === "awaiting-approval") {
|
|
173967
174084
|
void this.writeAwaitingApprovalMailboxMessage(task);
|
|
@@ -173972,17 +174089,12 @@ var init_notification_service = __esm({
|
|
|
173972
174089
|
if (!this.notificationsEnabled) {
|
|
173973
174090
|
return;
|
|
173974
174091
|
}
|
|
173975
|
-
if (task.status === "failed" &&
|
|
173976
|
-
|
|
173977
|
-
|
|
173978
|
-
|
|
173979
|
-
schedulerLog.debug(
|
|
173980
|
-
`[notify] ${task.id} transient merge failure (${transientClass}) \u2014 suppressed notification (self-heal in flight)`
|
|
173981
|
-
);
|
|
173982
|
-
return;
|
|
173983
|
-
}
|
|
174092
|
+
if (task.status === "failed" && recoveryOwner) {
|
|
174093
|
+
this.failureNotificationSuppressedCount += 1;
|
|
174094
|
+
schedulerLog.debug(`[notify] ${task.id} recovery-owned failure \u2014 suppressed notification`);
|
|
174095
|
+
} else if (task.status === "failed" && !wedge) {
|
|
173984
174096
|
if (this.failureNotificationMode === "all") {
|
|
173985
|
-
|
|
174097
|
+
void this.maybeNotifyImmediateFailure(task);
|
|
173986
174098
|
} else {
|
|
173987
174099
|
this.scheduleFailureNotification(task);
|
|
173988
174100
|
}
|
|
@@ -174069,7 +174181,17 @@ var init_notification_service = __esm({
|
|
|
174069
174181
|
await this.enqueueWedgeHandling(task.id, () => this.maybeNotifyTaskWedge(task, descriptor));
|
|
174070
174182
|
}
|
|
174071
174183
|
async maybeNotifyTaskWedge(task, suppliedDescriptor) {
|
|
174072
|
-
const
|
|
174184
|
+
const liveTask = this.store.getTask ? await this.store.getTask(task.id) ?? task : task;
|
|
174185
|
+
const recoveryOwner = describeTaskRecoveryOwner(liveTask);
|
|
174186
|
+
if (recoveryOwner) {
|
|
174187
|
+
if (this.activeWedgeReasons.has(task.id) || liveTask.wedgeNotification?.status === "active") {
|
|
174188
|
+
this.activeWedgeReasons.delete(task.id);
|
|
174189
|
+
await this.store.claimTaskWedgeNotificationEpisode?.(task.id, null);
|
|
174190
|
+
}
|
|
174191
|
+
return;
|
|
174192
|
+
}
|
|
174193
|
+
const descriptor = suppliedDescriptor ?? describeTaskWedge(liveTask);
|
|
174194
|
+
task = liveTask;
|
|
174073
174195
|
let episode;
|
|
174074
174196
|
if (!descriptor) {
|
|
174075
174197
|
const isActiveSelfHealingNoAction = task.wedgeNotification?.status === "active" && task.wedgeNotification.reasonKey.startsWith("self-healing-no-action:");
|
|
@@ -174436,6 +174558,14 @@ var init_notification_service = __esm({
|
|
|
174436
174558
|
this.failureNotificationDelayMs = typeof settings.failureNotificationDelayMs === "number" && settings.failureNotificationDelayMs >= 0 ? settings.failureNotificationDelayMs : this.failedNotificationGraceMs;
|
|
174437
174559
|
this.failureNotificationMode = settings.failureNotificationMode ?? "sticky-only";
|
|
174438
174560
|
}
|
|
174561
|
+
async maybeNotifyImmediateFailure(task) {
|
|
174562
|
+
const liveTask = this.store.getTask ? await this.store.getTask(task.id) ?? task : task;
|
|
174563
|
+
if (liveTask.status !== "failed" || describeTaskRecoveryOwner(liveTask) || describeTaskWedge(liveTask)) {
|
|
174564
|
+
this.failureNotificationSuppressedCount += 1;
|
|
174565
|
+
return;
|
|
174566
|
+
}
|
|
174567
|
+
this.maybeNotify(liveTask.id, "failed", this.createTaskPayload(liveTask, "failed"));
|
|
174568
|
+
}
|
|
174439
174569
|
scheduleFailureNotification(task) {
|
|
174440
174570
|
if (this.pendingFailureNotifications.has(task.id)) {
|
|
174441
174571
|
return;
|
|
@@ -174492,12 +174622,9 @@ var init_notification_service = __esm({
|
|
|
174492
174622
|
schedulerLog.debug(`[notify] ${taskId} no longer failed at dispatch time \u2014 suppressed notification`);
|
|
174493
174623
|
return;
|
|
174494
174624
|
}
|
|
174495
|
-
|
|
174496
|
-
if (transientClassAtDispatch) {
|
|
174625
|
+
if (describeTaskRecoveryOwner(task)) {
|
|
174497
174626
|
this.failureNotificationSuppressedCount += 1;
|
|
174498
|
-
schedulerLog.debug(
|
|
174499
|
-
`[notify] ${taskId} transient merge failure (${transientClassAtDispatch}) at dispatch time \u2014 suppressed notification (self-heal in flight)`
|
|
174500
|
-
);
|
|
174627
|
+
schedulerLog.debug(`[notify] ${taskId} recovery-owned failure at dispatch time \u2014 suppressed notification`);
|
|
174501
174628
|
return;
|
|
174502
174629
|
}
|
|
174503
174630
|
if (describeTaskWedge(task)) {
|
|
@@ -186152,10 +186279,21 @@ var init_scheduler = __esm({
|
|
|
186152
186279
|
const unmetDeps = getUnmetSchedulingDependencies(task, tasks2, schedulingDependencyOptions);
|
|
186153
186280
|
if (unmetDeps.length > 0) {
|
|
186154
186281
|
const normalizedUnmetDeps = [...new Set(unmetDeps)].sort();
|
|
186282
|
+
let activeOverlapBlockedBy = null;
|
|
186283
|
+
if (activeScopes.size > 0) {
|
|
186284
|
+
try {
|
|
186285
|
+
const taskScope = await getFilteredFileScope(task.id);
|
|
186286
|
+
if (taskScope.length > 0 && !isCoordinationOnlyTask(task, taskScope)) {
|
|
186287
|
+
activeOverlapBlockedBy = Array.from(activeScopes.entries()).sort(([aId], [bId]) => aId.localeCompare(bId)).find(([, activeScope]) => this.pathsOverlap(taskScope, activeScope))?.[0] ?? null;
|
|
186288
|
+
}
|
|
186289
|
+
} catch (error3) {
|
|
186290
|
+
schedulerLog.warn(`Failed to refresh file-scope overlap blocker for dependency-blocked task ${task.id}`, error3);
|
|
186291
|
+
}
|
|
186292
|
+
}
|
|
186155
186293
|
await this.transitionQueuedEpisode(task, {
|
|
186156
186294
|
signature: `dependency:${normalizedUnmetDeps.join(",")}`,
|
|
186157
186295
|
blockedBy: unmetDeps[0] ?? null,
|
|
186158
|
-
overlapBlockedBy:
|
|
186296
|
+
overlapBlockedBy: activeOverlapBlockedBy,
|
|
186159
186297
|
action: `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`
|
|
186160
186298
|
});
|
|
186161
186299
|
this.options.onBlocked?.(task, unmetDeps);
|
|
@@ -202212,16 +202350,29 @@ var init_self_healing = __esm({
|
|
|
202212
202350
|
activeWork: await this.resolveActiveWorkColumnsFor(taskId, cache)
|
|
202213
202351
|
};
|
|
202214
202352
|
}
|
|
202215
|
-
|
|
202353
|
+
/*
|
|
202354
|
+
FNXC:SharedBranchMemberHold 2026-08-05-23:35:
|
|
202355
|
+
Pause-abort recovery must resolve the same live intermediate-group predicate as
|
|
202356
|
+
merge admission. Shared metadata alone is stale after group closure or a
|
|
202357
|
+
default-branch collision, so it cannot suppress that task's standalone hold.
|
|
202358
|
+
*/
|
|
202359
|
+
async isLiveSharedMemberIntegration(task, settings) {
|
|
202360
|
+
const groupId = task.branchContext?.groupId?.trim();
|
|
202361
|
+
const group = groupId ? await this.store.getBranchGroup?.(groupId) : null;
|
|
202362
|
+
const defaultBranch = await resolveIntegrationBranch(this.options.rootDir, settings);
|
|
202363
|
+
return isLiveSharedBranchGroupMemberIntegration(task, group, defaultBranch);
|
|
202364
|
+
}
|
|
202365
|
+
async classifyPausedAbortWorkflowRecovery(task, settings, isExecuting, columns) {
|
|
202216
202366
|
const isPausedAbortPark = task.status === "failed" && typeof task.error === "string" && task.error.includes(PAUSE_ABORT_PARK_OPERATOR_MARKER) && task.error.includes(PAUSE_ABORT_PARK_ERROR_MARKER);
|
|
202217
202367
|
if (!isPausedAbortPark) return { kind: "no-action", reason: "not-pause-abort" };
|
|
202218
202368
|
if (task.paused || task.userPaused || isExecuting) return { kind: "no-action", reason: "unsafe-or-not-routable" };
|
|
202219
202369
|
const errorText = typeof task.error === "string" ? task.error.toLowerCase() : "";
|
|
202220
202370
|
const isTerminalMergePark = errorText.includes("conflict") || errorText.includes("contamination") || errorText.includes("foreign") || errorText.includes("retry-exhausted") || errorText.includes("retries exhausted") || errorText.includes("max retries");
|
|
202221
202371
|
const completedSteps = task.steps.length > 0 && task.steps.every((step) => step.status === "done" || step.status === "skipped");
|
|
202222
|
-
const
|
|
202223
|
-
const
|
|
202224
|
-
const
|
|
202372
|
+
const liveSharedBranchMember = await this.isLiveSharedMemberIntegration(task, settings);
|
|
202373
|
+
const autoMergeProcessing = allowsAutoMergeProcessing(task, settings) || liveSharedBranchMember;
|
|
202374
|
+
const hasReviewProgress = columns.review.has(task.column) && autoMergeProcessing && task.mergeDetails?.mergeConfirmed !== true && !isTerminalMergePark && completedSteps;
|
|
202375
|
+
const hasManualMergeHoldProgress = columns.review.has(task.column) && (hasUserAutoMergeHold(task) || !allowsAutoMergeProcessing(task, settings) || resolveEffectiveAutoMerge(task, settings) === false) && (!liveSharedBranchMember || hasUserAutoMergeHold(task)) && task.mergeDetails?.mergeConfirmed !== true && !isTerminalMergePark && completedSteps;
|
|
202225
202376
|
if (hasManualMergeHoldProgress) {
|
|
202226
202377
|
return { kind: "work-item-resume", reason: "pause-abort-manual-merge-hold" };
|
|
202227
202378
|
}
|
|
@@ -207889,8 +208040,23 @@ var init_self_healing = __esm({
|
|
|
207889
208040
|
return own.length > 0 ? new Set(own) : /* @__PURE__ */ new Set(["in-review"]);
|
|
207890
208041
|
};
|
|
207891
208042
|
const executingIds = this.options.getExecutingTaskIds?.() ?? /* @__PURE__ */ new Set();
|
|
208043
|
+
const canRecoverMergeableReviewTask = async (task) => {
|
|
208044
|
+
if (hasUserAutoMergeHold(task)) return false;
|
|
208045
|
+
const groupId = task.branchContext?.groupId?.trim();
|
|
208046
|
+
const branchGroup = groupId ? await this.store.getBranchGroup(groupId) : null;
|
|
208047
|
+
const projectDefaultBranch = await resolveIntegrationBranch(this.options.rootDir, settings);
|
|
208048
|
+
if (isLiveSharedBranchGroupMemberIntegration(task, branchGroup, projectDefaultBranch)) {
|
|
208049
|
+
return true;
|
|
208050
|
+
}
|
|
208051
|
+
return allowsAutoMergeProcessing(task, settings) && resolveEffectiveAutoMerge(task, settings) !== false;
|
|
208052
|
+
};
|
|
208053
|
+
const mergeAdmission = await Promise.all(tasks2.map(async (task) => [
|
|
208054
|
+
task.id,
|
|
208055
|
+
await canRecoverMergeableReviewTask(task)
|
|
208056
|
+
]));
|
|
208057
|
+
const mergeAdmissionByTaskId = new Map(mergeAdmission);
|
|
207892
208058
|
const mergeable = tasks2.filter(
|
|
207893
|
-
(t) =>
|
|
208059
|
+
(t) => mergeAdmissionByTaskId.get(t.id) === true && !t.paused && !executingIds.has(t.id) && t.status !== "failed" && // Exclude transient merge statuses. Active merges should be left alone;
|
|
207894
208060
|
// stale ones are handled by recoverStaleMergingStatus().
|
|
207895
208061
|
t.status !== "merging" && t.status !== "merging-pr" && // FNXC:Workspace 2026-06-22-09:30 (Phase D U1, KTD1 — admit workspace tasks):
|
|
207896
208062
|
// A workspace task has task.worktree===null (its worktrees live per-repo in
|
|
@@ -210937,7 +211103,7 @@ var init_self_healing = __esm({
|
|
|
210937
211103
|
for (const t of tasks2) {
|
|
210938
211104
|
if (!this.isPauseAbortParkCandidate(t)) continue;
|
|
210939
211105
|
const columns = await this.resolvePauseAbortColumnsFor(t.id, columnCache);
|
|
210940
|
-
if (this.classifyPausedAbortWorkflowRecovery(t, settings, executingIds.has(t.id), columns).kind !== "no-action") {
|
|
211106
|
+
if ((await this.classifyPausedAbortWorkflowRecovery(t, settings, executingIds.has(t.id), columns)).kind !== "no-action") {
|
|
210941
211107
|
parked.push(t);
|
|
210942
211108
|
}
|
|
210943
211109
|
}
|
|
@@ -210954,7 +211120,7 @@ var init_self_healing = __esm({
|
|
|
210954
211120
|
continue;
|
|
210955
211121
|
}
|
|
210956
211122
|
const freshColumns = await this.resolvePauseAbortColumnsFor(fresh.id, columnCache);
|
|
210957
|
-
const route = this.classifyPausedAbortWorkflowRecovery(fresh, settings, latestExecutingIds.has(fresh.id), freshColumns);
|
|
211123
|
+
const route = await this.classifyPausedAbortWorkflowRecovery(fresh, settings, latestExecutingIds.has(fresh.id), freshColumns);
|
|
210958
211124
|
if (route.kind === "no-action") {
|
|
210959
211125
|
continue;
|
|
210960
211126
|
}
|
|
@@ -217957,7 +218123,7 @@ function recoverPassedPlanReviewFromLatestLog(task) {
|
|
|
217957
218123
|
completedAt: latest.timestamp
|
|
217958
218124
|
};
|
|
217959
218125
|
}
|
|
217960
|
-
var PLAN_REVIEW_PROVIDER_FAILURE_HOLD_VALUE, PLAN_REVIEW_LEASE_HELD_VALUE, SESSION_CONTENTION_HOLD_VALUE, WORKFLOW_INTERRUPTED_NODE_ID_CONTEXT_KEY, WORKFLOW_INTERRUPTED_NODE_ABORT_KIND_CONTEXT_KEY, WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY, WORKFLOW_NODE_ENGINE_PAUSE_ABORT_KIND, WORKFLOW_DRIFT_PARK_CONTEXT_KEY, WorkflowGraphSuspended, MERGE_REGION_KINDS, WorkflowGraphExecutor;
|
|
218126
|
+
var PLAN_REVIEW_PROVIDER_FAILURE_HOLD_VALUE, PLAN_REVIEW_LEASE_HELD_VALUE, SESSION_CONTENTION_HOLD_VALUE, WORKFLOW_INTERRUPTED_NODE_ID_CONTEXT_KEY, WORKFLOW_INTERRUPTED_NODE_ABORT_KIND_CONTEXT_KEY, WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY, WORKFLOW_REVIEW_KIND_CONTEXT_KEY, WORKFLOW_NODE_ENGINE_PAUSE_ABORT_KIND, WORKFLOW_DRIFT_PARK_CONTEXT_KEY, WorkflowGraphSuspended, MERGE_REGION_KINDS, WorkflowGraphExecutor;
|
|
217961
218127
|
var init_workflow_graph_executor = __esm({
|
|
217962
218128
|
"../engine/src/workflows/workflow-graph-executor.ts"() {
|
|
217963
218129
|
"use strict";
|
|
@@ -217977,6 +218143,7 @@ var init_workflow_graph_executor = __esm({
|
|
|
217977
218143
|
WORKFLOW_INTERRUPTED_NODE_ID_CONTEXT_KEY = "workflow:interruptedNodeId";
|
|
217978
218144
|
WORKFLOW_INTERRUPTED_NODE_ABORT_KIND_CONTEXT_KEY = "workflow:interruptedNodeAbortKind";
|
|
217979
218145
|
WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY = "workflow:optionalGroupActive";
|
|
218146
|
+
WORKFLOW_REVIEW_KIND_CONTEXT_KEY = "workflow:reviewKind";
|
|
217980
218147
|
WORKFLOW_NODE_ENGINE_PAUSE_ABORT_KIND = "engine-pause";
|
|
217981
218148
|
WORKFLOW_DRIFT_PARK_CONTEXT_KEY = "workflow:driftPark";
|
|
217982
218149
|
WorkflowGraphSuspended = class extends Error {
|
|
@@ -218004,7 +218171,8 @@ var init_workflow_graph_executor = __esm({
|
|
|
218004
218171
|
parseSteps: deps.parseStepsDeps,
|
|
218005
218172
|
runCode: deps.runCode,
|
|
218006
218173
|
notifyDispatch: deps.notifyDispatch,
|
|
218007
|
-
prNodes: deps.prNodes
|
|
218174
|
+
prNodes: deps.prNodes,
|
|
218175
|
+
isLiveSharedBranchMember: deps.isLiveSharedBranchMember
|
|
218008
218176
|
}),
|
|
218009
218177
|
...deps.runnerRegistry?.toHandlers() ?? {},
|
|
218010
218178
|
...deps.handlers ?? {}
|
|
@@ -218225,6 +218393,7 @@ var init_workflow_graph_executor = __esm({
|
|
|
218225
218393
|
phase: stepPhase,
|
|
218226
218394
|
status: "pending",
|
|
218227
218395
|
source: "optional-group",
|
|
218396
|
+
...this.workflowReviewKind(node) ? { reviewKind: this.workflowReviewKind(node) } : {},
|
|
218228
218397
|
startedAt: stepStartedAt,
|
|
218229
218398
|
// U3/KTD-4: stamp the lease owner so a concurrent/crashed re-entry
|
|
218230
218399
|
// adopts this pending gate instead of dispatching a second reviewer.
|
|
@@ -218244,7 +218413,8 @@ var init_workflow_graph_executor = __esm({
|
|
|
218244
218413
|
runTemplateNode: (tNode, sig, contextOverride) => {
|
|
218245
218414
|
const optionalGroupContext = {
|
|
218246
218415
|
...contextOverride ?? context,
|
|
218247
|
-
[WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY]: node.id
|
|
218416
|
+
[WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY]: node.id,
|
|
218417
|
+
...this.workflowReviewKind(node) ? { [WORKFLOW_REVIEW_KIND_CONTEXT_KEY]: this.workflowReviewKind(node) } : {}
|
|
218248
218418
|
};
|
|
218249
218419
|
return this.executeNodeWithRetries(tNode, task, settings, optionalGroupContext, ir2, sig, false);
|
|
218250
218420
|
},
|
|
@@ -218262,6 +218432,7 @@ var init_workflow_graph_executor = __esm({
|
|
|
218262
218432
|
const exitContextPatch = exitResult?.contextPatch;
|
|
218263
218433
|
let stepOutput = typeof exitContextPatch?.output === "string" ? exitContextPatch.output : void 0;
|
|
218264
218434
|
const stepNotes = typeof exitContextPatch?.notes === "string" ? exitContextPatch.notes : void 0;
|
|
218435
|
+
const stepFindings = this.workflowReviewKind(node) && Array.isArray(exitContextPatch?.findings) ? exitContextPatch.findings : void 0;
|
|
218265
218436
|
if (stepStatus === "failed" && !verdict && stepOutput === void 0 && stepNotes === void 0) {
|
|
218266
218437
|
const lastVisited = groupResult.visitedNodeIds[groupResult.visitedNodeIds.length - 1];
|
|
218267
218438
|
const templateNodeId = lastVisited?.includes("::") ? lastVisited.slice(lastVisited.indexOf("::") + 2) : void 0;
|
|
@@ -218279,9 +218450,11 @@ var init_workflow_graph_executor = __esm({
|
|
|
218279
218450
|
phase: stepPhase,
|
|
218280
218451
|
source: "optional-group",
|
|
218281
218452
|
status: stepStatus,
|
|
218453
|
+
...this.workflowReviewKind(node) ? { reviewKind: this.workflowReviewKind(node) } : {},
|
|
218282
218454
|
...verdict ? { verdict } : {},
|
|
218283
218455
|
...stepOutput !== void 0 ? { output: stepOutput } : {},
|
|
218284
218456
|
...stepNotes !== void 0 ? { notes: stepNotes } : {},
|
|
218457
|
+
...stepFindings?.length ? { findings: stepFindings } : {},
|
|
218285
218458
|
startedAt: stepStartedAt,
|
|
218286
218459
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
218287
218460
|
});
|
|
@@ -218741,9 +218914,15 @@ var init_workflow_graph_executor = __esm({
|
|
|
218741
218914
|
}
|
|
218742
218915
|
return failureResult;
|
|
218743
218916
|
}
|
|
218917
|
+
/** FNXC:WorkflowReviewKind 2026-08-05-02:31: Persist only the declared closed
|
|
218918
|
+
* marker; execution never derives review semantics from a label, verdict, or gate mode. */
|
|
218919
|
+
workflowReviewKind(node) {
|
|
218920
|
+
return node.config?.reviewKind === "plan" || node.config?.reviewKind === "code" ? node.config.reviewKind : void 0;
|
|
218921
|
+
}
|
|
218744
218922
|
shouldRecordNodeProgress(node) {
|
|
218745
218923
|
const skillName = typeof node.config?.skillName === "string" ? node.config.skillName.trim() : "";
|
|
218746
|
-
|
|
218924
|
+
const markedReview = this.workflowReviewKind(node) !== void 0;
|
|
218925
|
+
return markedReview && (node.kind === "prompt" || node.kind === "gate" || node.kind === "script") || skillName.length > 0 && (node.kind === "prompt" || node.kind === "gate");
|
|
218747
218926
|
}
|
|
218748
218927
|
workflowNodeProgressName(node) {
|
|
218749
218928
|
const configuredName = typeof node.config?.name === "string" ? node.config.name.trim() : "";
|
|
@@ -218757,6 +218936,7 @@ var init_workflow_graph_executor = __esm({
|
|
|
218757
218936
|
phase: node.config?.phase === "post-merge" ? "post-merge" : "pre-merge",
|
|
218758
218937
|
source: "node",
|
|
218759
218938
|
status: "pending",
|
|
218939
|
+
...this.workflowReviewKind(node) ? { reviewKind: this.workflowReviewKind(node) } : {},
|
|
218760
218940
|
startedAt
|
|
218761
218941
|
};
|
|
218762
218942
|
await this.recordOptionalGroupStepResult(taskId, result2);
|
|
@@ -218767,6 +218947,7 @@ var init_workflow_graph_executor = __esm({
|
|
|
218767
218947
|
const contextPatch = nodeResult.contextPatch ?? {};
|
|
218768
218948
|
let output = typeof contextPatch.output === "string" ? contextPatch.output : void 0;
|
|
218769
218949
|
const notes = typeof contextPatch.notes === "string" ? contextPatch.notes : void 0;
|
|
218950
|
+
const findings = this.workflowReviewKind(node) && Array.isArray(contextPatch.findings) ? contextPatch.findings : void 0;
|
|
218770
218951
|
if (status === "failed" && output === void 0 && notes === void 0) {
|
|
218771
218952
|
output = this.synthesizeNonVerdictFailureOutput({
|
|
218772
218953
|
stepLabel: this.workflowNodeProgressName(node),
|
|
@@ -218781,8 +218962,10 @@ var init_workflow_graph_executor = __esm({
|
|
|
218781
218962
|
phase: started?.phase ?? (node.config?.phase === "post-merge" ? "post-merge" : "pre-merge"),
|
|
218782
218963
|
source: "node",
|
|
218783
218964
|
status,
|
|
218965
|
+
...this.workflowReviewKind(node) ? { reviewKind: this.workflowReviewKind(node) } : {},
|
|
218784
218966
|
...output !== void 0 ? { output } : {},
|
|
218785
218967
|
...notes !== void 0 ? { notes } : {},
|
|
218968
|
+
...findings?.length ? { findings } : {},
|
|
218786
218969
|
startedAt: started?.startedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
218787
218970
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
218788
218971
|
});
|
|
@@ -219225,6 +219408,10 @@ var init_workflow_graph_task_runner = __esm({
|
|
|
219225
219408
|
runCode: this.deps.runCode,
|
|
219226
219409
|
notifyDispatch: this.deps.notifyDispatch,
|
|
219227
219410
|
prNodes: this.deps.prNodes,
|
|
219411
|
+
// FNXC:SharedBranchMemberHold 2026-08-06-00:24: carry the executor's
|
|
219412
|
+
// live group resolver into the graph merge gate; omitting it turns
|
|
219413
|
+
// mission-policy members into manual holds before integration.
|
|
219414
|
+
isLiveSharedBranchMember: this.deps.isLiveSharedBranchMember,
|
|
219228
219415
|
/*
|
|
219229
219416
|
FNXC:WorkflowResume 2026-06-29-08:49:
|
|
219230
219417
|
Production graph runs must fetch live task steps during foreach replay. The runner is the workflow boundary that has store access, so it supplies the fresh projection seam instead of making executor self-healing guess after a stale step node fails.
|
|
@@ -224063,9 +224250,11 @@ function parseWorkflowStepVerdict(rawOutput) {
|
|
|
224063
224250
|
verdict = "REVISE";
|
|
224064
224251
|
}
|
|
224065
224252
|
if (!verdict) continue;
|
|
224253
|
+
const findings = normalizeWorkflowReviewFindings(parsed.findings);
|
|
224066
224254
|
return {
|
|
224067
224255
|
verdict,
|
|
224068
|
-
notes: typeof parsed.notes === "string" ? parsed.notes : ""
|
|
224256
|
+
notes: typeof parsed.notes === "string" ? parsed.notes : "",
|
|
224257
|
+
...findings ? { findings } : {}
|
|
224069
224258
|
};
|
|
224070
224259
|
} catch {
|
|
224071
224260
|
}
|
|
@@ -224097,7 +224286,8 @@ function parseWorkflowStepOutput(rawOutput, options = {}) {
|
|
|
224097
224286
|
return {
|
|
224098
224287
|
output: parsed.notes || "",
|
|
224099
224288
|
verdict: parsed.verdict,
|
|
224100
|
-
notes: parsed.notes
|
|
224289
|
+
notes: parsed.notes,
|
|
224290
|
+
...parsed.findings ? { findings: parsed.findings } : {}
|
|
224101
224291
|
};
|
|
224102
224292
|
}
|
|
224103
224293
|
const inferred = inferWorkflowStepVerdictFromProse(trimmed);
|
|
@@ -227334,6 +227524,7 @@ ${feedback}`,
|
|
|
227334
227524
|
if (info.phase !== "pre-merge") return false;
|
|
227335
227525
|
if (info.status !== "advisory_failure" && info.status !== "failed") return false;
|
|
227336
227526
|
const liveTask = await this.store.getTask(taskId).catch(() => fallbackTask);
|
|
227527
|
+
if (hasUserAutoMergeHold(liveTask)) return false;
|
|
227337
227528
|
const missingArtifactKeys = parseRequiredArtifactMissingValue(info.failureValue);
|
|
227338
227529
|
if (missingArtifactKeys) {
|
|
227339
227530
|
await this.recoverMissingRequiredArtifacts(liveTask, missingArtifactKeys, {
|
|
@@ -227590,6 +227781,7 @@ ${info.feedback}`, revisionKey),
|
|
|
227590
227781
|
*/
|
|
227591
227782
|
async recoverFailedPreMergeWorkflowStep(task) {
|
|
227592
227783
|
try {
|
|
227784
|
+
if (hasUserAutoMergeHold(task)) return false;
|
|
227593
227785
|
const failed = (task.workflowStepResults ?? []).filter((r) => (r.phase || "pre-merge") === "pre-merge" && r.status === "failed").sort((a, b3) => {
|
|
227594
227786
|
const aTs = Date.parse(a.completedAt || a.startedAt || "");
|
|
227595
227787
|
const bTs = Date.parse(b3.completedAt || b3.startedAt || "");
|
|
@@ -228116,6 +228308,7 @@ ${info.feedback}`, revisionKey),
|
|
|
228116
228308
|
getTask: (taskId) => this.store.getTask(taskId)
|
|
228117
228309
|
},
|
|
228118
228310
|
runId: resolvedRunId,
|
|
228311
|
+
isLiveSharedBranchMember: (nodeTask) => this.isLiveSharedBranchGroupMember(nodeTask),
|
|
228119
228312
|
primitives: this.createAuthoritativeWorkflowPrimitives(settings),
|
|
228120
228313
|
seams: this.createAuthoritativeWorkflowSeams(settings),
|
|
228121
228314
|
prepareNodeExecution: (node, nodeTask, requirement) => this.prepareGraphNodeExecution(node, nodeTask, settings, requirement),
|
|
@@ -230358,6 +230551,7 @@ ${info.feedback}`, revisionKey),
|
|
|
230358
230551
|
return this.runCliAgentNode(node, await this.store.getTask(live.id), cfg);
|
|
230359
230552
|
}
|
|
230360
230553
|
const optionalGroupId = typeof graphContext?.[WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY] === "string" ? graphContext[WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY] : void 0;
|
|
230554
|
+
const declaredReviewKind = cfg.reviewKind === "plan" || cfg.reviewKind === "code" ? cfg.reviewKind : graphContext?.[WORKFLOW_REVIEW_KIND_CONTEXT_KEY] === "plan" || graphContext?.[WORKFLOW_REVIEW_KIND_CONTEXT_KEY] === "code" ? graphContext[WORKFLOW_REVIEW_KIND_CONTEXT_KEY] : void 0;
|
|
230361
230555
|
const isCompletionSummaryNode2 = cfg.summaryTarget === "task" || node.id === "completion-summary";
|
|
230362
230556
|
if (live.executionMode === "fast" && !isCompletionSummaryNode2 && !optionalGroupId && !cfg.seam && (node.kind === "prompt" || node.kind === "script" || node.kind === "gate")) {
|
|
230363
230557
|
executorLog.debug(`${live.id}: fast mode \u2014 skipping custom graph node '${node.id}'`);
|
|
@@ -230506,6 +230700,9 @@ ${prompt}`;
|
|
|
230506
230700
|
if (optionalGroupId) {
|
|
230507
230701
|
step.optionalGroupId = optionalGroupId;
|
|
230508
230702
|
}
|
|
230703
|
+
if (declaredReviewKind) {
|
|
230704
|
+
step.reviewKind = declaredReviewKind;
|
|
230705
|
+
}
|
|
230509
230706
|
if (cfg.reviewCanFixInline === true) {
|
|
230510
230707
|
step.reviewCanFixInline = true;
|
|
230511
230708
|
}
|
|
@@ -230518,7 +230715,11 @@ ${prompt}`;
|
|
|
230518
230715
|
executorLog.debug(`${live.id}: graph node '${node.id}' runtime env injected (${injected.pathEntryCount} PATH entries, ${injected.injectedKeyCount} env keys)`);
|
|
230519
230716
|
}
|
|
230520
230717
|
const unattended = this.graphUnattendedRuns.has(live.id);
|
|
230521
|
-
|
|
230718
|
+
let outcome = mode === "script" ? await this.executeScriptWorkflowStep(live, step, worktreePath, settings, nodeEnv) : await this.executeWorkflowStep(live, step, worktreePath, settings, nodeEnv, { unattended });
|
|
230719
|
+
if (declaredReviewKind && typeof outcome.output === "string") {
|
|
230720
|
+
const parsedReviewOutput = parseWorkflowStepOutput(outcome.output, { requireVerdict: false });
|
|
230721
|
+
if (parsedReviewOutput.findings?.length) outcome = { ...outcome, findings: parsedReviewOutput.findings };
|
|
230722
|
+
}
|
|
230522
230723
|
const awaitQuestion = parseAwaitInputSentinel(outcome.output);
|
|
230523
230724
|
if (awaitQuestion) {
|
|
230524
230725
|
await this.store.logEntry(
|
|
@@ -230541,6 +230742,8 @@ ${prompt}`;
|
|
|
230541
230742
|
const contextPatch = {};
|
|
230542
230743
|
if (typeof stepOutput === "string") contextPatch.output = stepOutput;
|
|
230543
230744
|
if (typeof stepNotes === "string" && stepNotes) contextPatch.notes = stepNotes;
|
|
230745
|
+
const stepFindings = outcome.findings;
|
|
230746
|
+
if (stepFindings?.length) contextPatch.findings = stepFindings;
|
|
230544
230747
|
if (cfg.summaryTarget === "task" && typeof stepOutput === "string" && stepOutput.trim()) {
|
|
230545
230748
|
contextPatch.summary = stepOutput.trim();
|
|
230546
230749
|
}
|
|
@@ -231047,6 +231250,7 @@ Graph value: ${failureValue}` : ""}`, budget.key),
|
|
|
231047
231250
|
} catch {
|
|
231048
231251
|
return false;
|
|
231049
231252
|
}
|
|
231253
|
+
if (hasUserAutoMergeHold(live)) return false;
|
|
231050
231254
|
const sharedBranchMember = await this.isLiveSharedBranchGroupMember(live);
|
|
231051
231255
|
if (!sharedBranchMember && !allowsAutoMergeProcessing(live, settings)) return false;
|
|
231052
231256
|
if (!sharedBranchMember && resolveEffectiveAutoMerge(live, settings) === false) return false;
|
|
@@ -231108,8 +231312,8 @@ Graph value: ${failureValue}` : ""}`, budget.key),
|
|
|
231108
231312
|
} catch {
|
|
231109
231313
|
return false;
|
|
231110
231314
|
}
|
|
231111
|
-
if (await this.isLiveSharedBranchGroupMember(live)) return false;
|
|
231112
|
-
return !allowsAutoMergeProcessing(live, settings) || resolveEffectiveAutoMerge(live, settings) === false;
|
|
231315
|
+
if (await this.isLiveSharedBranchGroupMember(live) && !hasUserAutoMergeHold(live)) return false;
|
|
231316
|
+
return hasUserAutoMergeHold(live) || !allowsAutoMergeProcessing(live, settings) || resolveEffectiveAutoMerge(live, settings) === false;
|
|
231113
231317
|
}
|
|
231114
231318
|
async handleStaleInReviewPlanPauseAbortReplay(live, result2, abortProvenance, pausedAborted, userCanceled, resumeLanesMemo) {
|
|
231115
231319
|
if (!pausedAborted) return false;
|
|
@@ -231273,9 +231477,9 @@ Graph value: ${failureValue}` : ""}`, budget.key),
|
|
|
231273
231477
|
if (settings.globalPause === true) return false;
|
|
231274
231478
|
}
|
|
231275
231479
|
if (live.column === resumeLanes.review) {
|
|
231276
|
-
if (live.autoMerge === false) return false;
|
|
231277
231480
|
if (!settings) return false;
|
|
231278
231481
|
const sharedBranchMember = await this.isLiveSharedBranchGroupMember(live);
|
|
231482
|
+
if (hasUserAutoMergeHold(live) || live.autoMerge === false && !sharedBranchMember) return false;
|
|
231279
231483
|
if (!sharedBranchMember && !allowsAutoMergeProcessing(live, settings)) return false;
|
|
231280
231484
|
if (live.mergeDetails?.mergeConfirmed === true) return false;
|
|
231281
231485
|
}
|
|
@@ -237043,13 +237247,14 @@ CRITICAL SCOPING RULES \u2014 read before doing anything else:
|
|
|
237043
237247
|
const isSkillStep = typeof workflowStep.skillName === "string" && workflowStep.skillName.trim().length > 0;
|
|
237044
237248
|
const isSummaryProjectionStep = workflowStep.summaryTarget === "task";
|
|
237045
237249
|
const requireVerdict = !isSummaryProjectionStep && (workflowStep.gateMode === "gate" || !isSkillStep);
|
|
237250
|
+
const reviewFindingsContract = workflowStepMetadata.reviewKind === "plan" || workflowStepMetadata.reviewKind === "code";
|
|
237046
237251
|
const verdictBlock = requireVerdict ? `
|
|
237047
237252
|
|
|
237048
237253
|
## Feedback Format
|
|
237049
237254
|
|
|
237050
237255
|
When your review is complete, your final line MUST be a single JSON object (no markdown fences):
|
|
237051
237256
|
|
|
237052
|
-
{"verdict":"APPROVE|APPROVE_WITH_NOTES|REVISE","notes":"..."}
|
|
237257
|
+
${reviewFindingsContract ? '{"verdict":"APPROVE|APPROVE_WITH_NOTES|REVISE","notes":"...","findings":[{"id":"stable-id","title":"concise issue","body":"actionable detail","filePath":"optional/path","line":1,"severity":"low|medium|high|critical"}]}' : '{"verdict":"APPROVE|APPROVE_WITH_NOTES|REVISE","notes":"..."}'}
|
|
237053
237258
|
|
|
237054
237259
|
Rules:
|
|
237055
237260
|
- Output exactly one trailing JSON object and stop.
|
|
@@ -237359,7 +237564,8 @@ ${detectedQuestion}
|
|
|
237359
237564
|
revisionRequested,
|
|
237360
237565
|
output: parsed.output,
|
|
237361
237566
|
verdict: parsed.verdict,
|
|
237362
|
-
notes: parsed.notes
|
|
237567
|
+
notes: parsed.notes,
|
|
237568
|
+
...parsed.findings ? { findings: parsed.findings } : {}
|
|
237363
237569
|
};
|
|
237364
237570
|
}
|
|
237365
237571
|
if (parsed.malformed) {
|
|
@@ -248348,7 +248554,7 @@ var init_metric_parser = __esm({
|
|
|
248348
248554
|
});
|
|
248349
248555
|
|
|
248350
248556
|
// ../engine/src/eval/experiment-executor.ts
|
|
248351
|
-
import { randomUUID as
|
|
248557
|
+
import { randomUUID as randomUUID44 } from "node:crypto";
|
|
248352
248558
|
var ExperimentMaxIterationsError, ExperimentGitNotConfiguredError, ExperimentExecutor;
|
|
248353
248559
|
var init_experiment_executor = __esm({
|
|
248354
248560
|
"../engine/src/eval/experiment-executor.ts"() {
|
|
@@ -248416,7 +248622,7 @@ var init_experiment_executor = __esm({
|
|
|
248416
248622
|
}
|
|
248417
248623
|
await this.semaphore.acquire();
|
|
248418
248624
|
const controller = new AbortController();
|
|
248419
|
-
const runHandle =
|
|
248625
|
+
const runHandle = randomUUID44();
|
|
248420
248626
|
if (opts?.abortSignal) {
|
|
248421
248627
|
opts.abortSignal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
248422
248628
|
}
|
|
@@ -257423,6 +257629,7 @@ var init_in_process_runtime = __esm({
|
|
|
257423
257629
|
}
|
|
257424
257630
|
}
|
|
257425
257631
|
const prNodeGithubOps = this.config.prNodeGithubOps;
|
|
257632
|
+
const secretsStore = await this.taskStore.getSecretsStore();
|
|
257426
257633
|
const executorOptions = {
|
|
257427
257634
|
/*
|
|
257428
257635
|
FNXC:PlanReviewLease 2026-07-26-21:12:
|
|
@@ -257438,6 +257645,7 @@ var init_in_process_runtime = __esm({
|
|
|
257438
257645
|
cliAgentRuntime: this.cliAgentRuntime?.bundle,
|
|
257439
257646
|
pluginRunner: this.pluginRunner,
|
|
257440
257647
|
messageStore: this.messageStore,
|
|
257648
|
+
secretsStore,
|
|
257441
257649
|
missionStore,
|
|
257442
257650
|
reflectionService,
|
|
257443
257651
|
// PR-entity nodes (U3): assemble the handler deps from the CLI-injected
|
|
@@ -257539,6 +257747,7 @@ var init_in_process_runtime = __esm({
|
|
|
257539
257747
|
reflectionService,
|
|
257540
257748
|
selfImproveService,
|
|
257541
257749
|
credentialRotator: this.credentialRotator,
|
|
257750
|
+
secretsStore,
|
|
257542
257751
|
snapshotManager: autoClaimSnapshotManager,
|
|
257543
257752
|
onMissed: (agentId, reason) => {
|
|
257544
257753
|
runtimeLog.warn(`Agent ${agentId} missed heartbeat: ${reason}`);
|
|
@@ -261400,7 +261609,21 @@ var init_project_engine = __esm({
|
|
|
261400
261609
|
Cleared on the first non-busy outcome (success path resets it).
|
|
261401
261610
|
*/
|
|
261402
261611
|
workspaceBusyReenqueues = /* @__PURE__ */ new Map();
|
|
261612
|
+
workspaceBusyReenqueueTimers = /* @__PURE__ */ new Set();
|
|
261403
261613
|
static WORKSPACE_BUSY_MAX_REENQUEUES = 10;
|
|
261614
|
+
/*
|
|
261615
|
+
FNXC:WorkspaceMergeDispatch 2026-08-05-23:56:
|
|
261616
|
+
Workspace lease-contention retries are engine-owned lifecycle work, not detached callbacks.
|
|
261617
|
+
Track only these busy re-enqueue timers so stop() can cancel them and tests can measure the
|
|
261618
|
+
capped workspace ladder without confusing it with merge-body or maintenance timers.
|
|
261619
|
+
*/
|
|
261620
|
+
scheduleWorkspaceBusyReenqueue(taskId, delayMs) {
|
|
261621
|
+
const timer2 = setTimeout(() => {
|
|
261622
|
+
this.workspaceBusyReenqueueTimers.delete(timer2);
|
|
261623
|
+
if (!this.shuttingDown) this.internalEnqueueMerge(taskId);
|
|
261624
|
+
}, delayMs);
|
|
261625
|
+
this.workspaceBusyReenqueueTimers.add(timer2);
|
|
261626
|
+
}
|
|
261404
261627
|
/**
|
|
261405
261628
|
* Pending manual merge resolvers — keyed by taskId.
|
|
261406
261629
|
* When `onMerge` is called, the task is enqueued like auto-merge but a
|
|
@@ -261461,7 +261684,7 @@ var init_project_engine = __esm({
|
|
|
261461
261684
|
*
|
|
261462
261685
|
* Readable (not private) so tests derive the cap from this single source of truth rather
|
|
261463
261686
|
* than hardcoding it — the FN-8004 bump broke two suites that had baked in the old `3`. */
|
|
261464
|
-
static MAX_AUTO_MERGE_TRANSIENT_RETRIES =
|
|
261687
|
+
static MAX_AUTO_MERGE_TRANSIENT_RETRIES = MAX_AUTO_MERGE_TRANSIENT_RETRIES;
|
|
261465
261688
|
static MERGE_REQUEST_RETRY_EXHAUSTED_AGE_MS = 30 * 60 * 1e3;
|
|
261466
261689
|
/** Cap on outer in-review→in-progress bounces caused by deterministic
|
|
261467
261690
|
* verification failures during auto-merge. After this many failed merges
|
|
@@ -261935,6 +262158,10 @@ ${detail}`);
|
|
|
261935
262158
|
clearInterval(this.mergeActiveReconcileTimer);
|
|
261936
262159
|
this.mergeActiveReconcileTimer = null;
|
|
261937
262160
|
}
|
|
262161
|
+
for (const timer2 of this.workspaceBusyReenqueueTimers) {
|
|
262162
|
+
clearTimeout(timer2);
|
|
262163
|
+
}
|
|
262164
|
+
this.workspaceBusyReenqueueTimers.clear();
|
|
261938
262165
|
for (const [taskId, deferred] of this.capacityDeferredMerges) {
|
|
261939
262166
|
clearTimeout(deferred.timer);
|
|
261940
262167
|
for (const resolver of deferred.resolvers) {
|
|
@@ -263234,16 +263461,15 @@ ${detail}`);
|
|
|
263234
263461
|
* older low-priority task would start before a later urgent one.
|
|
263235
263462
|
*/
|
|
263236
263463
|
async allowInReviewMergeProcessing(task, settings, store = this.runtime.getTaskStore()) {
|
|
263237
|
-
if (
|
|
263238
|
-
return true;
|
|
263239
|
-
}
|
|
263464
|
+
if (hasUserAutoMergeHold(task)) return false;
|
|
263240
263465
|
const groupId = task.branchContext?.groupId?.trim();
|
|
263241
263466
|
const branchGroup = groupId ? await store.getBranchGroup?.(groupId) : null;
|
|
263242
|
-
if (!branchGroup || branchGroup.status !== "open" || !branchGroup.branchName.trim()) {
|
|
263243
|
-
return false;
|
|
263244
|
-
}
|
|
263245
263467
|
const projectDefaultBranch = await resolveIntegrationBranch(this.config.workingDirectory, settings);
|
|
263246
|
-
|
|
263468
|
+
if (isLiveSharedBranchGroupMemberIntegration(task, branchGroup, projectDefaultBranch)) {
|
|
263469
|
+
return true;
|
|
263470
|
+
}
|
|
263471
|
+
if (task.autoMerge === false && groupId) return false;
|
|
263472
|
+
return allowsAutoMergeProcessing(task, settings);
|
|
263247
263473
|
}
|
|
263248
263474
|
async emitLegacyAutoMergeStampAdvisory(store) {
|
|
263249
263475
|
if (this.legacyAutoMergeStampAdvisoryEmitted) {
|
|
@@ -264196,9 +264422,7 @@ ${detail}`);
|
|
|
264196
264422
|
runtimeLog.log(
|
|
264197
264423
|
`Workspace land busy re-enqueue ${busyCount + 1}/${_ProjectEngine.WORKSPACE_BUSY_MAX_REENQUEUES} for ${taskId} in ${delayMs / 1e3}s (no mergeRetry consumed \u2014 pure lease contention)`
|
|
264198
264424
|
);
|
|
264199
|
-
|
|
264200
|
-
if (!this.shuttingDown) this.internalEnqueueMerge(taskId);
|
|
264201
|
-
}, delayMs);
|
|
264425
|
+
this.scheduleWorkspaceBusyReenqueue(taskId, delayMs);
|
|
264202
264426
|
} else {
|
|
264203
264427
|
this.workspaceBusyReenqueues.delete(taskId);
|
|
264204
264428
|
await store.updateTask(taskId, { status: "failed", error: errorMsg }).catch(() => void 0);
|
|
@@ -267690,7 +267914,7 @@ var init_task_lifecycle_lanes = __esm({
|
|
|
267690
267914
|
});
|
|
267691
267915
|
|
|
267692
267916
|
// ../dashboard/src/ai-session-diagnostics.ts
|
|
267693
|
-
import { randomUUID as
|
|
267917
|
+
import { randomUUID as randomUUID45 } from "node:crypto";
|
|
267694
267918
|
function defaultSink(level, scope, message, context) {
|
|
267695
267919
|
const log41 = createLogger(scope);
|
|
267696
267920
|
try {
|
|
@@ -267733,7 +267957,7 @@ function emit(level, scope, message, context) {
|
|
|
267733
267957
|
const fullContext = {
|
|
267734
267958
|
...context,
|
|
267735
267959
|
_emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
267736
|
-
_diagnosticsId:
|
|
267960
|
+
_diagnosticsId: randomUUID45()
|
|
267737
267961
|
};
|
|
267738
267962
|
try {
|
|
267739
267963
|
_sink(level, scope, message, fullContext);
|
|
@@ -268449,7 +268673,7 @@ var init_planning_board_tools = __esm({
|
|
|
268449
268673
|
});
|
|
268450
268674
|
|
|
268451
268675
|
// ../dashboard/src/planning.ts
|
|
268452
|
-
import { randomUUID as
|
|
268676
|
+
import { randomUUID as randomUUID46 } from "node:crypto";
|
|
268453
268677
|
import { EventEmitter as EventEmitter34 } from "node:events";
|
|
268454
268678
|
async function createPlanningRuntimeSession(options) {
|
|
268455
268679
|
const { pluginRunner, runtimeHint, settings, ...runtimeOptions } = options ?? {};
|
|
@@ -268832,7 +269056,7 @@ async function createSession(ip, initialPlan, store, rootDir, promptOverrides, p
|
|
|
268832
269056
|
if (!store) {
|
|
268833
269057
|
throw new Error("store is required for AI-powered planning sessions");
|
|
268834
269058
|
}
|
|
268835
|
-
const sessionId =
|
|
269059
|
+
const sessionId = randomUUID46();
|
|
268836
269060
|
const session = {
|
|
268837
269061
|
id: sessionId,
|
|
268838
269062
|
ip,
|
|
@@ -269402,7 +269626,7 @@ function normalizePlanningQuestion(input, userInput = "") {
|
|
|
269402
269626
|
const type = source.type === "multi_select" || source.type === "single_select" ? source.type : "single_select";
|
|
269403
269627
|
const question = typeof source.question === "string" && source.question.trim() ? source.question.trim() : fallback.question;
|
|
269404
269628
|
const normalized = {
|
|
269405
|
-
id: typeof source.id === "string" && source.id.trim() ? source.id :
|
|
269629
|
+
id: typeof source.id === "string" && source.id.trim() ? source.id : randomUUID46(),
|
|
269406
269630
|
type,
|
|
269407
269631
|
question,
|
|
269408
269632
|
...typeof source.description === "string" && source.description.trim() ? { description: source.description.trim() } : {}
|
|
@@ -269428,7 +269652,7 @@ function normalizePlanningQuestion(input, userInput = "") {
|
|
|
269428
269652
|
}
|
|
269429
269653
|
function coerceQuestionResponse(response, session) {
|
|
269430
269654
|
if (response.type === "question") return normalizePlanningQuestion(response.data, session.initialPlan);
|
|
269431
|
-
return normalizePlanningQuestion({ id:
|
|
269655
|
+
return normalizePlanningQuestion({ id: randomUUID46(), type: "single_select", question: planningFallbackCopy(session.initialPlan).question }, session.initialPlan);
|
|
269432
269656
|
}
|
|
269433
269657
|
async function validateSession(sessionId) {
|
|
269434
269658
|
const session = await getSession(sessionId);
|
|
@@ -270176,7 +270400,7 @@ async function createTaskFromPlanSession(sessionId, store, options) {
|
|
|
270176
270400
|
throw new InvalidSessionStateError("PLANNING_CREATED_TASK_MISSING");
|
|
270177
270401
|
}
|
|
270178
270402
|
}
|
|
270179
|
-
const claimOwnerToken =
|
|
270403
|
+
const claimOwnerToken = randomUUID46();
|
|
270180
270404
|
let claimed = await claimPlanningTaskCreation(sessionId, claimOwnerToken, (/* @__PURE__ */ new Date()).toISOString(), claimEpoch);
|
|
270181
270405
|
if (!claimed) {
|
|
270182
270406
|
session = await getDurablePlanningSession(sessionId).catch(() => void 0) ?? session;
|
|
@@ -282764,7 +282988,7 @@ var init_register_gitlab = __esm({
|
|
|
282764
282988
|
|
|
282765
282989
|
// ../dashboard/src/terminal.ts
|
|
282766
282990
|
import { spawn as spawn11 } from "node:child_process";
|
|
282767
|
-
import { randomUUID as
|
|
282991
|
+
import { randomUUID as randomUUID47 } from "node:crypto";
|
|
282768
282992
|
import { EventEmitter as EventEmitter41 } from "node:events";
|
|
282769
282993
|
function extractBaseCommand(command) {
|
|
282770
282994
|
let trimmed = command.trim();
|
|
@@ -282924,7 +283148,7 @@ var init_terminal = __esm({
|
|
|
282924
283148
|
if (!validation.valid) {
|
|
282925
283149
|
return { sessionId: "", error: validation.error };
|
|
282926
283150
|
}
|
|
282927
|
-
const sessionId =
|
|
283151
|
+
const sessionId = randomUUID47();
|
|
282928
283152
|
const childProcess5 = spawn11(command, [], {
|
|
282929
283153
|
cwd,
|
|
282930
283154
|
shell: true,
|
|
@@ -284396,7 +284620,7 @@ var init_src8 = __esm({
|
|
|
284396
284620
|
|
|
284397
284621
|
// ../../plugins/fusion-plugin-openclaw-runtime/src/pi-module.ts
|
|
284398
284622
|
import { spawn as spawn13 } from "node:child_process";
|
|
284399
|
-
import { randomUUID as
|
|
284623
|
+
import { randomUUID as randomUUID48 } from "node:crypto";
|
|
284400
284624
|
import { readFile as readFile44 } from "node:fs/promises";
|
|
284401
284625
|
function asString2(v2) {
|
|
284402
284626
|
return typeof v2 === "string" && v2.trim() !== "" ? v2.trim() : void 0;
|
|
@@ -284456,7 +284680,7 @@ function extractStderrError(stderr, stdout) {
|
|
|
284456
284680
|
}
|
|
284457
284681
|
function createCliSession(opts) {
|
|
284458
284682
|
return {
|
|
284459
|
-
sessionId:
|
|
284683
|
+
sessionId: randomUUID48(),
|
|
284460
284684
|
agentId: opts.agentId ?? DEFAULT_AGENT_ID,
|
|
284461
284685
|
systemPrompt: opts.systemPrompt,
|
|
284462
284686
|
messages: [{ role: "developer", content: opts.systemPrompt }],
|
|
@@ -284651,7 +284875,7 @@ var init_mcp_config2 = __esm({
|
|
|
284651
284875
|
});
|
|
284652
284876
|
|
|
284653
284877
|
// ../../plugins/fusion-plugin-openclaw-runtime/src/runtime-adapter.ts
|
|
284654
|
-
import { randomUUID as
|
|
284878
|
+
import { randomUUID as randomUUID49 } from "node:crypto";
|
|
284655
284879
|
var OpenClawRuntimeAdapter;
|
|
284656
284880
|
var init_runtime_adapter2 = __esm({
|
|
284657
284881
|
"../../plugins/fusion-plugin-openclaw-runtime/src/runtime-adapter.ts"() {
|
|
@@ -284674,8 +284898,8 @@ var init_runtime_adapter2 = __esm({
|
|
|
284674
284898
|
let mcpProfile;
|
|
284675
284899
|
let mcpConfigPath;
|
|
284676
284900
|
if (toolDefs.length > 0) {
|
|
284677
|
-
const bridge = writeOpenClawMcpBridgeFiles(toolDefs,
|
|
284678
|
-
mcpProfile = `fusion-${
|
|
284901
|
+
const bridge = writeOpenClawMcpBridgeFiles(toolDefs, randomUUID49());
|
|
284902
|
+
mcpProfile = `fusion-${randomUUID49()}`;
|
|
284679
284903
|
mcpConfigPath = bridge.serverConfigPath;
|
|
284680
284904
|
await configureOpenClawMcpServer({
|
|
284681
284905
|
binaryPath: this.config.binaryPath,
|
|
@@ -287149,7 +287373,7 @@ import { existsSync as existsSync73, writeFileSync as writeFileSync15 } from "no
|
|
|
287149
287373
|
import { tmpdir as tmpdir16 } from "node:os";
|
|
287150
287374
|
import { dirname as dirname41, join as join93 } from "node:path";
|
|
287151
287375
|
import { fileURLToPath as fileURLToPath9 } from "node:url";
|
|
287152
|
-
import { randomUUID as
|
|
287376
|
+
import { randomUUID as randomUUID50 } from "node:crypto";
|
|
287153
287377
|
function toolsToMcpToolDefs2(tools) {
|
|
287154
287378
|
if (!Array.isArray(tools)) return [];
|
|
287155
287379
|
return tools.filter((tool) => tool && typeof tool.name === "string" && tool.name.trim().length > 0 && !BUILT_IN_TOOL_NAMES2.has(tool.name)).map((tool) => ({
|
|
@@ -287200,7 +287424,7 @@ async function startFusionToolBridge(tools) {
|
|
|
287200
287424
|
byName.set(tool.name, tool);
|
|
287201
287425
|
}
|
|
287202
287426
|
}
|
|
287203
|
-
const schemaPath = join93(tmpdir16(), `fusion-grok-mcp-schemas-${process.pid}-${
|
|
287427
|
+
const schemaPath = join93(tmpdir16(), `fusion-grok-mcp-schemas-${process.pid}-${randomUUID50()}.json`);
|
|
287204
287428
|
writeFileSync15(schemaPath, JSON.stringify(defs));
|
|
287205
287429
|
const server = createServer2(async (req, res) => {
|
|
287206
287430
|
if (req.method !== "POST" || req.url !== "/tool-call") {
|
|
@@ -287226,7 +287450,7 @@ async function startFusionToolBridge(tools) {
|
|
|
287226
287450
|
return;
|
|
287227
287451
|
}
|
|
287228
287452
|
try {
|
|
287229
|
-
const result2 = await tool.execute(`grok-mcp-${
|
|
287453
|
+
const result2 = await tool.execute(`grok-mcp-${randomUUID50()}`, parsed.arguments ?? {}, void 0, void 0, void 0);
|
|
287230
287454
|
res.statusCode = 200;
|
|
287231
287455
|
res.setHeader("content-type", "application/json");
|
|
287232
287456
|
res.end(
|
|
@@ -289270,7 +289494,7 @@ import { existsSync as existsSync77, rmSync as rmSync13, writeFileSync as writeF
|
|
|
289270
289494
|
import { tmpdir as tmpdir18 } from "node:os";
|
|
289271
289495
|
import { dirname as dirname46, join as join98 } from "node:path";
|
|
289272
289496
|
import { fileURLToPath as fileURLToPath13 } from "node:url";
|
|
289273
|
-
import { randomBytes as randomBytes7, randomUUID as
|
|
289497
|
+
import { randomBytes as randomBytes7, randomUUID as randomUUID51 } from "node:crypto";
|
|
289274
289498
|
function toolsToMcpToolDefs3(tools) {
|
|
289275
289499
|
if (!Array.isArray(tools)) return [];
|
|
289276
289500
|
return tools.filter((tool) => tool && typeof tool.name === "string" && tool.name.trim().length > 0 && !BUILT_IN_TOOL_NAMES3.has(tool.name)).map((tool) => ({
|
|
@@ -289321,7 +289545,7 @@ async function startFusionToolBridge2(tools, options = {}) {
|
|
|
289321
289545
|
byName.set(tool.name, tool);
|
|
289322
289546
|
}
|
|
289323
289547
|
}
|
|
289324
|
-
const schemaPath = join98(tmpdir18(), `fusion-claude-mcp-schemas-${process.pid}-${
|
|
289548
|
+
const schemaPath = join98(tmpdir18(), `fusion-claude-mcp-schemas-${process.pid}-${randomUUID51()}.json`);
|
|
289325
289549
|
writeFileSync17(schemaPath, JSON.stringify(defs));
|
|
289326
289550
|
const capabilityToken = randomBytes7(32).toString("base64url");
|
|
289327
289551
|
const server = createServer3(async (req, res) => {
|
|
@@ -289386,7 +289610,7 @@ async function startFusionToolBridge2(tools, options = {}) {
|
|
|
289386
289610
|
return;
|
|
289387
289611
|
}
|
|
289388
289612
|
try {
|
|
289389
|
-
const result2 = await tool.execute(`claude-mcp-${
|
|
289613
|
+
const result2 = await tool.execute(`claude-mcp-${randomUUID51()}`, parsed.arguments ?? {}, void 0, void 0, void 0);
|
|
289390
289614
|
res.statusCode = 200;
|
|
289391
289615
|
res.setHeader("content-type", "application/json");
|
|
289392
289616
|
res.end(
|
|
@@ -291463,7 +291687,7 @@ import { existsSync as existsSync79, unlinkSync as unlinkSync5, writeFileSync as
|
|
|
291463
291687
|
import { tmpdir as tmpdir19 } from "node:os";
|
|
291464
291688
|
import { dirname as dirname49, join as join101 } from "node:path";
|
|
291465
291689
|
import { fileURLToPath as fileURLToPath15 } from "node:url";
|
|
291466
|
-
import { randomUUID as
|
|
291690
|
+
import { randomUUID as randomUUID52 } from "node:crypto";
|
|
291467
291691
|
function toolsToMcpToolDefs4(tools) {
|
|
291468
291692
|
if (!Array.isArray(tools)) return [];
|
|
291469
291693
|
return tools.filter(
|
|
@@ -291520,7 +291744,7 @@ async function startFusionToolBridge3(tools) {
|
|
|
291520
291744
|
byName.set(tool.name, tool);
|
|
291521
291745
|
}
|
|
291522
291746
|
}
|
|
291523
|
-
const schemaPath = join101(tmpdir19(), `fusion-omp-mcp-schemas-${process.pid}-${
|
|
291747
|
+
const schemaPath = join101(tmpdir19(), `fusion-omp-mcp-schemas-${process.pid}-${randomUUID52()}.json`);
|
|
291524
291748
|
writeFileSync18(schemaPath, JSON.stringify(defs));
|
|
291525
291749
|
const server = createServer4(async (req, res) => {
|
|
291526
291750
|
if (req.method !== "POST" || req.url !== "/tool-call") {
|
|
@@ -291547,7 +291771,7 @@ async function startFusionToolBridge3(tools) {
|
|
|
291547
291771
|
}
|
|
291548
291772
|
try {
|
|
291549
291773
|
const result2 = await tool.execute(
|
|
291550
|
-
`omp-mcp-${
|
|
291774
|
+
`omp-mcp-${randomUUID52()}`,
|
|
291551
291775
|
parsed.arguments ?? {},
|
|
291552
291776
|
void 0,
|
|
291553
291777
|
void 0,
|
|
@@ -292440,7 +292664,7 @@ var init_paperclip_client = __esm({
|
|
|
292440
292664
|
});
|
|
292441
292665
|
|
|
292442
292666
|
// ../../plugins/fusion-plugin-paperclip-runtime/src/runtime-adapter.ts
|
|
292443
|
-
import { randomUUID as
|
|
292667
|
+
import { randomUUID as randomUUID53 } from "node:crypto";
|
|
292444
292668
|
function sleep7(ms2) {
|
|
292445
292669
|
return new Promise((resolve54) => setTimeout(resolve54, ms2));
|
|
292446
292670
|
}
|
|
@@ -292569,7 +292793,7 @@ var init_runtime_adapter10 = __esm({
|
|
|
292569
292793
|
apiKey: effectiveApiKey,
|
|
292570
292794
|
agentId,
|
|
292571
292795
|
companyId,
|
|
292572
|
-
sessionId:
|
|
292796
|
+
sessionId: randomUUID53(),
|
|
292573
292797
|
systemPrompt: options.systemPrompt,
|
|
292574
292798
|
cwd: options.cwd,
|
|
292575
292799
|
mode: normalizeMode3(this.config.mode),
|
|
@@ -293247,7 +293471,7 @@ var init_types5 = __esm({
|
|
|
293247
293471
|
});
|
|
293248
293472
|
|
|
293249
293473
|
// ../dashboard/src/stt/model-manager.ts
|
|
293250
|
-
import { createHash as createHash28, randomUUID as
|
|
293474
|
+
import { createHash as createHash28, randomUUID as randomUUID54 } from "node:crypto";
|
|
293251
293475
|
import { mkdir as mkdir31, open as open4, readFile as readFile46, readdir as readdir11, rename as rename10, rm as rm17, stat as stat12, writeFile as writeFile33 } from "node:fs/promises";
|
|
293252
293476
|
import { join as join102, relative as relative19, resolve as resolve51, sep as sep15 } from "node:path";
|
|
293253
293477
|
function parseSafeTarListing(listing) {
|
|
@@ -293328,8 +293552,8 @@ function createVoiceModelManager(options = {}) {
|
|
|
293328
293552
|
if (!current(g2, signal)) return state;
|
|
293329
293553
|
await mkdir31(cacheDir(), { recursive: true });
|
|
293330
293554
|
if (!current(g2, signal)) return state;
|
|
293331
|
-
partial2 = join102(cacheDir(), `.partial-${g2}-${
|
|
293332
|
-
staging = join102(cacheDir(), `.staging-${g2}-${
|
|
293555
|
+
partial2 = join102(cacheDir(), `.partial-${g2}-${randomUUID54()}`);
|
|
293556
|
+
staging = join102(cacheDir(), `.staging-${g2}-${randomUUID54()}`);
|
|
293333
293557
|
const file = await open4(partial2, "wx");
|
|
293334
293558
|
try {
|
|
293335
293559
|
const response = await (options.fetch ?? globalThis.fetch)(asset.url, { signal });
|
|
@@ -293377,7 +293601,7 @@ function createVoiceModelManager(options = {}) {
|
|
|
293377
293601
|
await writeFile33(join102(staging, "manifest.json"), JSON.stringify({ filename: asset.filename, sha256: asset.sha256, installedAt: (/* @__PURE__ */ new Date()).toISOString(), expectedFiles: asset.expectedFiles }));
|
|
293378
293602
|
await options.beforePromote?.(signal);
|
|
293379
293603
|
if (!current(g2, signal)) return state;
|
|
293380
|
-
const backup = join102(cacheDir(), `.backup-${g2}-${
|
|
293604
|
+
const backup = join102(cacheDir(), `.backup-${g2}-${randomUUID54()}`);
|
|
293381
293605
|
let movedPrevious = false;
|
|
293382
293606
|
try {
|
|
293383
293607
|
try {
|
|
@@ -293598,7 +293822,7 @@ var init_parakeet_service = __esm({
|
|
|
293598
293822
|
});
|
|
293599
293823
|
|
|
293600
293824
|
// ../dashboard/src/routes/register-voice-routes.ts
|
|
293601
|
-
import { randomUUID as
|
|
293825
|
+
import { randomUUID as randomUUID55 } from "node:crypto";
|
|
293602
293826
|
import express from "express";
|
|
293603
293827
|
async function settingsFor(ctx, req) {
|
|
293604
293828
|
const scoped = await ctx.getScopedStore(req);
|
|
@@ -293677,7 +293901,7 @@ function createRegisterVoiceRoutes(deps = {}) {
|
|
|
293677
293901
|
recognizer.close();
|
|
293678
293902
|
return error2(res, 409, { error: "unavailable", reason: "model-removed" });
|
|
293679
293903
|
}
|
|
293680
|
-
const id =
|
|
293904
|
+
const id = randomUUID55();
|
|
293681
293905
|
const now = Date.now();
|
|
293682
293906
|
sessions6.set(id, { projectId, recognizer, next: 0, bytes: 0, last: now, expires: now + 3e5 });
|
|
293683
293907
|
res.status(201).json({ sessionId: id, expiresAt: new Date(now + 3e5).toISOString(), modelId: model.id, language: language.language });
|
|
@@ -297003,14 +297227,14 @@ init_src2();
|
|
|
297003
297227
|
init_gh_cli();
|
|
297004
297228
|
init_src5();
|
|
297005
297229
|
init_src15();
|
|
297006
|
-
import { randomUUID as
|
|
297230
|
+
import { randomUUID as randomUUID56 } from "node:crypto";
|
|
297007
297231
|
import { Type as Type9 } from "typebox";
|
|
297008
297232
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
297009
297233
|
import { resolve as resolve53, relative as relative20, isAbsolute as isAbsolute26, sep as sep16, basename as basename29, extname as extname4, join as join105 } from "node:path";
|
|
297010
297234
|
import { readFile as readFile47 } from "node:fs/promises";
|
|
297011
297235
|
import { existsSync as existsSync82 } from "node:fs";
|
|
297012
297236
|
import { spawn as spawn23 } from "node:child_process";
|
|
297013
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
297237
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
297014
297238
|
function truncateAgentDiagnosticText(value, maxChars) {
|
|
297015
297239
|
return value.length > maxChars ? `${value.slice(0, maxChars)}\u2026` : value;
|
|
297016
297240
|
}
|
|
@@ -297115,7 +297339,7 @@ var storeCache2 = extensionStoreState.cache;
|
|
|
297115
297339
|
var storeBootInflight = extensionStoreState.bootInflight;
|
|
297116
297340
|
var storeBootFailureCooldown = extensionStoreState.bootFailureCooldown;
|
|
297117
297341
|
var BOOT_FAILURE_COOLDOWN_MS = 5e3;
|
|
297118
|
-
var extensionToolSignal = new
|
|
297342
|
+
var extensionToolSignal = new AsyncLocalStorage2();
|
|
297119
297343
|
var EXTENSION_STORE_BOOT_TIMEOUT_MS = 3e4;
|
|
297120
297344
|
var EXTENSION_TOOL_TIMEOUT_MS = 6e4;
|
|
297121
297345
|
var SKILLS_INSTALL_TIMEOUT_MS = 3e5;
|
|
@@ -297417,6 +297641,28 @@ async function isEphemeralCallerAgent(cwd, callerAgentId) {
|
|
|
297417
297641
|
}
|
|
297418
297642
|
}
|
|
297419
297643
|
var AMBIGUOUS_AGENT_PRINCIPAL_ID = "unknown-agent";
|
|
297644
|
+
function resolveSecretAccessPrincipal(ctx) {
|
|
297645
|
+
const principal = resolveExtensionCallerPrincipal(ctx);
|
|
297646
|
+
if (principal.kind === "ambiguous") return { kind: "ambiguous" };
|
|
297647
|
+
if (principal.kind === "operator") {
|
|
297648
|
+
return {
|
|
297649
|
+
kind: "resolved",
|
|
297650
|
+
actor: { actorId: "user", actorType: "user", actorName: "CLI User" },
|
|
297651
|
+
agentId: null
|
|
297652
|
+
};
|
|
297653
|
+
}
|
|
297654
|
+
return {
|
|
297655
|
+
kind: "resolved",
|
|
297656
|
+
actor: {
|
|
297657
|
+
actorId: principal.identity.agentId,
|
|
297658
|
+
actorType: "agent",
|
|
297659
|
+
actorName: principal.identity.agentName ?? principal.identity.agentId
|
|
297660
|
+
},
|
|
297661
|
+
agentId: principal.identity.agentId,
|
|
297662
|
+
...principal.identity.agentName ? { agentName: principal.identity.agentName } : {},
|
|
297663
|
+
...principal.identity.taskId ? { taskId: principal.identity.taskId } : {}
|
|
297664
|
+
};
|
|
297665
|
+
}
|
|
297420
297666
|
function resolveExtensionCallerPrincipal(ctx) {
|
|
297421
297667
|
const explicitAgentId = typeof ctx.agentId === "string" && ctx.agentId.trim().length > 0 ? ctx.agentId.trim() : void 0;
|
|
297422
297668
|
if (explicitAgentId) {
|
|
@@ -297976,7 +298222,7 @@ function kbExtension(pi2) {
|
|
|
297976
298222
|
const layer = store.getAsyncLayer();
|
|
297977
298223
|
const messageStore = layer ? new MessageStore(null, { asyncLayer: layer }) : new MessageStore(store.getDatabase());
|
|
297978
298224
|
const title = params.description.split(/\r?\n/, 1)[0]?.trim().slice(0, 80) || "Follow-up task";
|
|
297979
|
-
await messageStore.sendMessage({ fromId: fnCtx.agentId ?? "ephemeral-worker", fromType: "agent", toId: DASHBOARD_USER_ID, toType: "user", type: "agent-to-user", content: `Task proposal awaiting validation: ${title}`, metadata: { kind: "task-proposal", proposalStatus: "pending", proposalIdempotencyKey:
|
|
298225
|
+
await messageStore.sendMessage({ fromId: fnCtx.agentId ?? "ephemeral-worker", fromType: "agent", toId: DASHBOARD_USER_ID, toType: "user", type: "agent-to-user", content: `Task proposal awaiting validation: ${title}`, metadata: { kind: "task-proposal", proposalStatus: "pending", proposalIdempotencyKey: randomUUID56(), proposedTask: { title, description: params.description, priority: params.priority, workflowId: params.workflow_id, dependencies: params.depends } } });
|
|
297980
298226
|
return { content: [{ type: "text", text: "Task proposal submitted to the operator for validation; no task was created." }], details: { proposed: true } };
|
|
297981
298227
|
}
|
|
297982
298228
|
}
|
|
@@ -299226,6 +299472,20 @@ Planning session completed. Task ${taskId} is now in planning and will be auto-p
|
|
|
299226
299472
|
}),
|
|
299227
299473
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
299228
299474
|
const fnCtx = ctx;
|
|
299475
|
+
const secretPrincipal = resolveSecretAccessPrincipal(fnCtx);
|
|
299476
|
+
if (secretPrincipal.kind === "ambiguous") {
|
|
299477
|
+
return {
|
|
299478
|
+
content: [{ type: "text", text: "Secret access was not requested because the calling agent identity is ambiguous. End one concurrent session and retry." }],
|
|
299479
|
+
isError: true,
|
|
299480
|
+
details: { error: "ambiguous-caller-identity", key: params.key, scope: params.scope ?? null }
|
|
299481
|
+
};
|
|
299482
|
+
}
|
|
299483
|
+
const effectiveCtx = {
|
|
299484
|
+
...secretPrincipal.agentId ? { agentId: secretPrincipal.agentId } : {},
|
|
299485
|
+
...secretPrincipal.agentName ? { agentName: secretPrincipal.agentName } : {},
|
|
299486
|
+
...typeof fnCtx.taskId === "string" ? { taskId: fnCtx.taskId } : secretPrincipal.taskId ? { taskId: secretPrincipal.taskId } : {},
|
|
299487
|
+
...typeof fnCtx.runId === "string" ? { runId: fnCtx.runId } : {}
|
|
299488
|
+
};
|
|
299229
299489
|
const store = await getStore(ctx.cwd);
|
|
299230
299490
|
const secretsStore = await store.getSecretsStore();
|
|
299231
299491
|
const scopes = params.scope ? [params.scope] : ["project", "global"];
|
|
@@ -299248,36 +299508,32 @@ Planning session completed. Task ${taskId} is now in planning and will be auto-p
|
|
|
299248
299508
|
settings: { secretsAccessPolicy: globalSettings.secretsAccessPolicy }
|
|
299249
299509
|
});
|
|
299250
299510
|
if (decision.policy === "deny") {
|
|
299251
|
-
emitSecretAudit(store,
|
|
299511
|
+
emitSecretAudit(store, effectiveCtx, "secret:approval-denied", `${resolvedScope}:${params.key}`);
|
|
299252
299512
|
return { content: [{ type: "text", text: "Secret access denied by policy." }], details: { error: "denied", key: params.key, scope: resolvedScope, policySource: decision.source } };
|
|
299253
299513
|
}
|
|
299254
299514
|
if (decision.policy === "prompt") {
|
|
299255
299515
|
const cliLayer = requireProjectLayer(store, "CLI secret approval store");
|
|
299256
299516
|
const approvalStore = new ApprovalRequestStore(null, { asyncLayer: cliLayer });
|
|
299257
|
-
const
|
|
299258
|
-
const requesterActorId =
|
|
299259
|
-
const
|
|
299260
|
-
|
|
299261
|
-
actorType: "agent",
|
|
299262
|
-
actorName: fnCtx.agentName ?? fnCtx.agentId
|
|
299263
|
-
} : { actorId: "user", actorType: "user", actorName: "CLI User" };
|
|
299264
|
-
const existing = await findLatestApprovalRequestByDedupeKey(approvalStore, { requesterActorId, ...fnCtx.taskId ? { taskId: fnCtx.taskId } : {}, dedupeKey });
|
|
299517
|
+
const requesterSnapshot = secretPrincipal.actor;
|
|
299518
|
+
const requesterActorId = requesterSnapshot.actorId;
|
|
299519
|
+
const dedupeKey = `secret-read:${resolvedScope}:${params.key}:${requesterActorId}`;
|
|
299520
|
+
const existing = await findLatestApprovalRequestByDedupeKey(approvalStore, { requesterActorId, ...effectiveCtx.taskId ? { taskId: effectiveCtx.taskId } : {}, dedupeKey });
|
|
299265
299521
|
if (existing?.status === "pending") {
|
|
299266
|
-
emitSecretAudit(store,
|
|
299522
|
+
emitSecretAudit(store, effectiveCtx, "secret:approval-requested", `${resolvedScope}:${params.key}`);
|
|
299267
299523
|
return {
|
|
299268
299524
|
content: [{ type: "text", text: `Secret access approval request ${existing.id} is still pending. Approve via POST /api/approvals/:id/decision.` }],
|
|
299269
299525
|
details: { outcome: "pending_approval", approvalRequestId: existing.id, key: params.key, scope: resolvedScope }
|
|
299270
299526
|
};
|
|
299271
299527
|
}
|
|
299272
299528
|
if (existing?.status === "denied") {
|
|
299273
|
-
emitSecretAudit(store,
|
|
299529
|
+
emitSecretAudit(store, effectiveCtx, "secret:approval-denied", `${resolvedScope}:${params.key}`);
|
|
299274
299530
|
return {
|
|
299275
299531
|
content: [{ type: "text", text: `Secret access request ${existing.id} was denied by the operator. Do not retry without operator direction.` }],
|
|
299276
299532
|
details: { outcome: "denied", approvalRequestId: existing.id, key: params.key, scope: resolvedScope }
|
|
299277
299533
|
};
|
|
299278
299534
|
}
|
|
299279
299535
|
if (existing?.status === "approved") {
|
|
299280
|
-
const revealedAfterApproval = await secretsStore.revealSecret(record2.id, resolvedScope, { agentId:
|
|
299536
|
+
const revealedAfterApproval = await secretsStore.revealSecret(record2.id, resolvedScope, { agentId: secretPrincipal.agentId });
|
|
299281
299537
|
await approvalStore.markCompleted(existing.id, {
|
|
299282
299538
|
actor: requesterSnapshot,
|
|
299283
299539
|
note: "Secret revealed after approval",
|
|
@@ -299285,7 +299541,7 @@ Planning session completed. Task ${taskId} is now in planning and will be auto-p
|
|
|
299285
299541
|
// get the same expectedRequesterActorId enforcement as the gate path.
|
|
299286
299542
|
expectedRequesterActorId: requesterActorId
|
|
299287
299543
|
});
|
|
299288
|
-
emitSecretAudit(store,
|
|
299544
|
+
emitSecretAudit(store, effectiveCtx, "secret:read", `${resolvedScope}:${params.key}`, { key: params.key, scope: resolvedScope, approvalRequestId: existing.id });
|
|
299289
299545
|
return {
|
|
299290
299546
|
content: [{ type: "text", text: `Loaded secret '${params.key}' from ${resolvedScope} scope (approval ${existing.id} consumed).` }],
|
|
299291
299547
|
details: { key: params.key, value: revealedAfterApproval.plaintextValue, scope: resolvedScope, approvalRequestId: existing.id }
|
|
@@ -299301,17 +299557,17 @@ Planning session completed. Task ${taskId} is now in planning and will be auto-p
|
|
|
299301
299557
|
resourceId: record2.id,
|
|
299302
299558
|
context: { approvalDedupeKey: dedupeKey, key: params.key, scope: resolvedScope }
|
|
299303
299559
|
},
|
|
299304
|
-
...
|
|
299305
|
-
...
|
|
299560
|
+
...effectiveCtx.runId ? { runId: effectiveCtx.runId } : {},
|
|
299561
|
+
...effectiveCtx.taskId ? { taskId: effectiveCtx.taskId } : {}
|
|
299306
299562
|
});
|
|
299307
|
-
emitSecretAudit(store,
|
|
299563
|
+
emitSecretAudit(store, effectiveCtx, "secret:approval-requested", `${resolvedScope}:${params.key}`);
|
|
299308
299564
|
return {
|
|
299309
299565
|
content: [{ type: "text", text: `Secret access requires approval. Request ${request2.id} is pending. Approve via POST /api/approvals/:id/decision.` }],
|
|
299310
299566
|
details: { outcome: "pending_approval", approvalRequestId: request2.id, key: params.key, scope: resolvedScope }
|
|
299311
299567
|
};
|
|
299312
299568
|
}
|
|
299313
|
-
const revealed = await secretsStore.revealSecret(record2.id, resolvedScope, { agentId:
|
|
299314
|
-
emitSecretAudit(store,
|
|
299569
|
+
const revealed = await secretsStore.revealSecret(record2.id, resolvedScope, { agentId: secretPrincipal.agentId });
|
|
299570
|
+
emitSecretAudit(store, effectiveCtx, "secret:read", `${resolvedScope}:${params.key}`, { key: params.key, scope: resolvedScope });
|
|
299315
299571
|
return {
|
|
299316
299572
|
content: [{ type: "text", text: `Loaded secret '${params.key}' from ${resolvedScope} scope.` }],
|
|
299317
299573
|
details: { key: params.key, value: revealed.plaintextValue, scope: resolvedScope }
|