@runfusion/fusion 0.54.0 → 0.55.0
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 +3384 -768
- package/dist/client/.vite/manifest.json +283 -251
- package/dist/client/assets/AgentDetailView-B_VfFSlt.js +18 -0
- package/dist/client/assets/{AgentDetailView-BzSyldiW.css → AgentDetailView-D7UY2Tup.css} +1 -1
- package/dist/client/assets/{AgentPermissionPolicyEditor-D2AQ6swg.js → AgentPermissionPolicyEditor-QZhz41AU.js} +1 -1
- package/dist/client/assets/{AgentsView-DwEFPKuN.js → AgentsView-hkhJ8SUq.js} +6 -6
- package/dist/client/assets/ChatView-B4WnXLYA.js +6 -0
- package/dist/client/assets/{CommandCenter-BABqoSkg.js → CommandCenter-B3fAajUO.js} +16 -16
- package/dist/client/assets/{DevServerView-DlgOzRdC.js → DevServerView-BR0ngFBw.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-BwMSmxYS.js → DirectoryPicker-BiS41Hca.js} +1 -1
- package/dist/client/assets/DocumentsView-BnRvIUpa.js +1 -0
- package/dist/client/assets/{EvalsView-CQBNrO5n.js → EvalsView-Bvx18QyC.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-2SDqP2lc.js → ExperimentalAgentOnboardingModal-CKGJcfXI.js} +1 -1
- package/dist/client/assets/{GoalsView-BgkX-_SB.js → GoalsView-Bj1yVIiv.js} +1 -1
- package/dist/client/assets/{InsightsView-CJY4eZcO.js → InsightsView-DBX8DI60.js} +2 -2
- package/dist/client/assets/{MemoryView-5Crtfanz.js → MemoryView-CqkRH2fa.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-COmzskVa.js → PiExtensionsManager-BMMmGWe2.js} +2 -2
- package/dist/client/assets/PluginManager-C1bdfN2W.js +1 -0
- package/dist/client/assets/{PullRequestView-DAHh0FVM.js → PullRequestView-D0CsXWLl.js} +2 -2
- package/dist/client/assets/{ResearchView-cSUka3sd.js → ResearchView-CVgmXAlj.js} +1 -1
- package/dist/client/assets/{SecretsView-ChFAfFRY.js → SecretsView-Bv_gw5Ix.js} +1 -1
- package/dist/client/assets/{SessionTerminal-D4R3ScvQ.css → SessionTerminal-BvK8A98c.css} +1 -1
- package/dist/client/assets/{SessionTerminal-CtQKDaqI.js → SessionTerminal-VLdVV3Ji.js} +1 -1
- package/dist/client/assets/{SettingsModal-BzZNPNIh.css → SettingsModal-4t8rV7rh.css} +1 -1
- package/dist/client/assets/{SettingsModal-pAz_hOoN.js → SettingsModal-D7YgojbV.js} +1 -1
- package/dist/client/assets/SettingsModal-gcrh5EOG.js +21 -0
- package/dist/client/assets/SetupWizardModal-DvmTlFP_.js +2 -0
- package/dist/client/assets/{ShadcnColorPicker-CsfgWpf6.js → ShadcnColorPicker-DM2fqGwl.js} +1 -1
- package/dist/client/assets/{SkillsView-BSUplEYb.js → SkillsView-Bb-JTo99.js} +1 -1
- package/dist/client/assets/{TodoView-DhBseLvM.js → TodoView-bV-7deOh.js} +1 -1
- package/dist/client/assets/{WorkflowNodeEditor-Bi12CNif.js → WorkflowNodeEditor-Ui1q_fFI.js} +3 -3
- package/dist/client/assets/{agentSkills-qVb0x_G3.js → agentSkills-DGFyuKuP.js} +1 -1
- package/dist/client/assets/{app-DczBmsP4.js → app--OM-ul6R.js} +3 -3
- package/dist/client/assets/{app-BhtYYxin.js → app-BCvqf8qq.js} +3 -3
- package/dist/client/assets/app-BZqqEiWR.js +13 -0
- package/dist/client/assets/{app-5S4z84u5.js → app-DQeH1UDD.js} +3 -3
- package/dist/client/assets/{app-Cts0__Vx.js → app-FiYwRatp.js} +3 -3
- package/dist/client/assets/{app-_GFUqcMK.js → app-rGdYcsAA.js} +3 -3
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-DEt4eIEQ.js → architectureDiagram-3BPJPVTR-DP7vOyXY.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-CJuX0UM_.js → blockDiagram-GPEHLZMM-Di9v1pV8.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-a46Lm113.js → c4Diagram-AAUBKEIU-Dvb8RSfr.js} +1 -1
- package/dist/client/assets/channel-C5g9M3Sk.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-iBhwyS0c.js → chunk-2J33WTMH-gLcAuGjI.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-C7lS5wER.js → chunk-4BX2VUAB-B-B0wjkx.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-CQ6YOncn.js → chunk-55IACEB6-XUDeC0Co.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-CveMaMKK.js → chunk-727SXJPM-1eebiGA2.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-DErjGUEk.js → chunk-AQP2D5EJ-D9VOB5QS.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-DXUvEFfJ.js → chunk-FMBD7UC4-EFNFEaAz.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-w5RqSj1J.js → chunk-ND2GUHAM-CbeQailD.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-CAi0lJ7V.js → chunk-QZHKN3VN-D55N6EBz.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-B-wdlvD6.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-B-wdlvD6.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-Ck2h4uV5.js → cose-bilkent-S5V4N54A-B0zbhkX3.js} +1 -1
- package/dist/client/assets/createLucideIcon-CshVyKO4.js +21 -0
- package/dist/client/assets/{dagre-BM42HDAG-BCRTXNj6.js → dagre-BM42HDAG-BL6peXwl.js} +1 -1
- package/dist/client/assets/dashboard-view-Bsdq3OM4.css +1 -0
- package/dist/client/assets/dashboard-view-CS1H5SQN.js +1 -0
- package/dist/client/assets/dashboard-view-DafOfnxI.js +1 -0
- package/dist/client/assets/{dashboard-view-D9rF_PCq.js → dashboard-view-Db3n_W85.js} +1320 -1335
- package/dist/client/assets/{diagram-2AECGRRQ-DTTk-DYO.js → diagram-2AECGRRQ-cYKwSHk7.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-jDc9kMi2.js → diagram-5GNKFQAL-DHIwTGuM.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-CcLmzlGr.js → diagram-KO2AKTUF-DfHBJ4aX.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-DCW8XKQj.js → diagram-LMA3HP47-pWhYmB6Z.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-wbbKquPs.js → diagram-OG6HWLK6-GzWqbi_m.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-Fo933Mo7.js → erDiagram-TEJ5UH35-CCVm5eiR.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-Dfum7HZP.js → flowDiagram-I6XJVG4X-CUTdnU6S.js} +1 -1
- package/dist/client/assets/{folder-open-B8vOwFeP.js → folder-open-DOCy9GVC.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-BzEBCn01.js → ganttDiagram-6RSMTGT7-CW4ENJaC.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CgovghHs.js → gitGraphDiagram-PVQCEYII-QXXMfSzp.js} +1 -1
- package/dist/client/assets/index-B6ttSchA.js +1336 -0
- package/dist/client/assets/index-CPE2Kwra.css +1 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-BzpU4Wys.js → infoDiagram-5YYISTIA-CZyHbj0n.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-cpp5pozu.js → ishikawaDiagram-YF4QCWOH-Dea84lqZ.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-BVjcw6oo.js → journeyDiagram-JHISSGLW-Db2iKGU5.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-4unAhBzo.js → kanban-definition-UN3LZRKU-CB9loIqR.js} +1 -1
- package/dist/client/assets/loader-circle-CMNcxXXr.js +16 -0
- package/dist/client/assets/{mermaid.core-DzIyUj9t.js → mermaid.core-CoQ8aCb0.js} +4 -4
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-Be_yfVir.js → mindmap-definition-RKZ34NQL-JPb2wF-K.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-Cfnw2aji.js → pieDiagram-4H26LBE5-pdZzOreP.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-BFlC-_Uo.js → quadrantDiagram-W4KKPZXB-Dh9Ids90.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-DpVPzCOx.js → requirementDiagram-4Y6WPE33-BVPOSvHU.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-DM-gKKP0.js → sankeyDiagram-5OEKKPKP-BqSMUpsx.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-DjhZCZhd.js → sequenceDiagram-3UESZ5HK-B8nepQ6X.js} +1 -1
- package/dist/client/assets/{shield-alert-DCfkjjYc.js → shield-alert-CtSaIQNY.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-BCA96rdI.js → stateDiagram-AJRCARHV-CQv9RdRp.js} +1 -1
- package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-DHds08RS.js → stateDiagram-v2-BHNVJYJU-BZ_mlvie.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-CHW2OQRw.js → timeline-definition-PNZ67QCA-CAjwM-Z5.js} +1 -1
- package/dist/client/assets/{upload-EJc5PJPz.js → upload-0_pT1euA.js} +1 -1
- package/dist/client/assets/{users-C-e-99Vp.js → users--1B6mnm8.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-shGh4eBY.js → vennDiagram-CIIHVFJN-BO3Um3rH.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-l0RGrVhn.js → wardley-L42UT6IY-BzqJcqDA.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-C-USmO8A.js → wardleyDiagram-YWT4CUSO-Bctxc6_5.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-CHX9Umta.js → xychartDiagram-2RQKCTM6-CgDuiMFR.js} +1 -1
- package/dist/client/assets/zoom-out-DzDzWQt1.js +21 -0
- package/dist/client/index.html +13 -4
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/package.json +1 -1
- package/dist/extension.js +2066 -271
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/plugin-sdk/index.d.ts +61 -1
- package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
- package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
- package/dist/plugins/fusion-plugin-cursor-runtime/bundled.js +70 -25
- package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
- package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-linear-import/bundled.js +658 -0
- package/dist/plugins/fusion-plugin-linear-import/manifest.json +48 -0
- package/dist/plugins/fusion-plugin-linear-import/package.json +11 -0
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/package.json +1 -1
- package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
- package/package.json +2 -1
- package/skill/fusion/SKILL.md +2 -2
- package/skill/fusion/references/engine-tools.md +1 -0
- package/skill/fusion/references/extension-tools.md +81 -0
- package/skill/fusion/references/fusion-capabilities.md +7 -0
- package/dist/client/assets/AgentDetailView-BJditQat.js +0 -18
- package/dist/client/assets/ChatView-B4bjPXFN.js +0 -6
- package/dist/client/assets/DocumentsView-BfaRXFg5.js +0 -1
- package/dist/client/assets/PluginManager-CGWXqZMa.js +0 -1
- package/dist/client/assets/SettingsModal-BYaotQ2o.js +0 -21
- package/dist/client/assets/SetupWizardModal-eHZHi8VP.js +0 -2
- package/dist/client/assets/app-B1VYocx2.js +0 -13
- package/dist/client/assets/channel-BGI9APMr.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-g6hCcufy.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-g6hCcufy.js +0 -1
- package/dist/client/assets/dashboard-view-BfLJ60Kd.js +0 -1
- package/dist/client/assets/index-BjELA24x.js +0 -1336
- package/dist/client/assets/index-DwGW7OJh.css +0 -1
- package/dist/client/assets/zoom-out-NWbVkreR.js +0 -41
package/dist/extension.js
CHANGED
|
@@ -564,6 +564,66 @@ var init_mcp_config = __esm({
|
|
|
564
564
|
}
|
|
565
565
|
});
|
|
566
566
|
|
|
567
|
+
// ../core/src/gitlab-config.ts
|
|
568
|
+
function resolveGitlabEnabled(input = {}) {
|
|
569
|
+
if (typeof input.project?.gitlabEnabled === "boolean") return input.project.gitlabEnabled;
|
|
570
|
+
if (typeof input.global?.gitlabEnabled === "boolean") return input.global.gitlabEnabled;
|
|
571
|
+
return true;
|
|
572
|
+
}
|
|
573
|
+
function readConfiguredString(value) {
|
|
574
|
+
if (typeof value !== "string") return void 0;
|
|
575
|
+
const trimmed = value.trim();
|
|
576
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
577
|
+
}
|
|
578
|
+
function normalizeHttpUrl(value, label) {
|
|
579
|
+
let parsed;
|
|
580
|
+
try {
|
|
581
|
+
parsed = new URL(value);
|
|
582
|
+
} catch {
|
|
583
|
+
throw new Error(`${label} must be a valid absolute http(s) URL`);
|
|
584
|
+
}
|
|
585
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
586
|
+
throw new Error(`${label} must use http:// or https://`);
|
|
587
|
+
}
|
|
588
|
+
if (parsed.username || parsed.password) {
|
|
589
|
+
throw new Error(`${label} must not include username or password userinfo`);
|
|
590
|
+
}
|
|
591
|
+
if (!parsed.hostname) {
|
|
592
|
+
throw new Error(`${label} must include a hostname`);
|
|
593
|
+
}
|
|
594
|
+
parsed.hash = "";
|
|
595
|
+
parsed.search = "";
|
|
596
|
+
parsed.pathname = parsed.pathname.replace(/\/+$/u, "") || "/";
|
|
597
|
+
const normalized = parsed.toString().replace(/\/$/u, "");
|
|
598
|
+
return normalized;
|
|
599
|
+
}
|
|
600
|
+
function deriveApiBaseUrl(instanceUrl) {
|
|
601
|
+
const parsed = new URL(instanceUrl);
|
|
602
|
+
const basePath = parsed.pathname.replace(/\/+$/u, "");
|
|
603
|
+
parsed.pathname = `${basePath}/api/v4`.replace(/\/+/gu, "/");
|
|
604
|
+
parsed.search = "";
|
|
605
|
+
parsed.hash = "";
|
|
606
|
+
return parsed.toString().replace(/\/$/u, "");
|
|
607
|
+
}
|
|
608
|
+
function resolveGitlabConfig(input = {}) {
|
|
609
|
+
const enabled = resolveGitlabEnabled(input);
|
|
610
|
+
const projectInstanceUrl = readConfiguredString(input.project?.gitlabInstanceUrl);
|
|
611
|
+
const globalInstanceUrl = readConfiguredString(input.global?.gitlabInstanceUrl);
|
|
612
|
+
const projectApiBaseUrl = readConfiguredString(input.project?.gitlabApiBaseUrl);
|
|
613
|
+
const globalApiBaseUrl = readConfiguredString(input.global?.gitlabApiBaseUrl);
|
|
614
|
+
const instanceUrl = normalizeHttpUrl(projectInstanceUrl ?? globalInstanceUrl ?? DEFAULT_GITLAB_INSTANCE_URL, "GitLab instance URL");
|
|
615
|
+
const apiBaseUrl = projectApiBaseUrl ?? globalApiBaseUrl ? normalizeHttpUrl(projectApiBaseUrl ?? globalApiBaseUrl ?? "", "GitLab API base URL") : instanceUrl === DEFAULT_GITLAB_INSTANCE_URL ? DEFAULT_GITLAB_API_BASE_URL : deriveApiBaseUrl(instanceUrl);
|
|
616
|
+
return { enabled, instanceUrl, apiBaseUrl };
|
|
617
|
+
}
|
|
618
|
+
var DEFAULT_GITLAB_INSTANCE_URL, DEFAULT_GITLAB_API_BASE_URL;
|
|
619
|
+
var init_gitlab_config = __esm({
|
|
620
|
+
"../core/src/gitlab-config.ts"() {
|
|
621
|
+
"use strict";
|
|
622
|
+
DEFAULT_GITLAB_INSTANCE_URL = "https://gitlab.com";
|
|
623
|
+
DEFAULT_GITLAB_API_BASE_URL = "https://gitlab.com/api/v4";
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
|
|
567
627
|
// ../core/src/task-helpers.ts
|
|
568
628
|
function getPrimaryPrInfo(task) {
|
|
569
629
|
return task.prInfos?.[0] ?? task.prInfo;
|
|
@@ -1017,7 +1077,11 @@ var init_settings_schema = __esm({
|
|
|
1017
1077
|
"use strict";
|
|
1018
1078
|
init_in_review_stall();
|
|
1019
1079
|
DEFAULT_GLOBAL_SETTINGS = {
|
|
1020
|
-
|
|
1080
|
+
/*
|
|
1081
|
+
FNXC:DashboardTheming 2026-07-03-00:00:
|
|
1082
|
+
Fresh installs must follow the operating system theme until the user explicitly chooses Light, Dark, or System. Keep this global default aligned with dashboard and desktop pre-hydration fallbacks.
|
|
1083
|
+
*/
|
|
1084
|
+
themeMode: "system",
|
|
1021
1085
|
/*
|
|
1022
1086
|
FNXC:DashboardTheming 2026-06-30-00:00:
|
|
1023
1087
|
New users and unset installs should start on Shadcn Ember. Existing users who explicitly stored colorTheme "default", "ocean", or another valid theme must remain on that selection, so the ids stay valid and only the absence/default seed changes to "shadcn-ember".
|
|
@@ -1090,10 +1154,21 @@ var init_settings_schema = __esm({
|
|
|
1090
1154
|
updateCheckFrequency: "daily",
|
|
1091
1155
|
autoReloadOnVersionChange: true,
|
|
1092
1156
|
githubTrackingDefaultRepo: void 0,
|
|
1157
|
+
gitlabEnabled: void 0,
|
|
1158
|
+
gitlabInstanceUrl: void 0,
|
|
1159
|
+
gitlabApiBaseUrl: void 0,
|
|
1160
|
+
gitlabAuthToken: void 0,
|
|
1161
|
+
gitlabAuthTokenType: void 0,
|
|
1093
1162
|
modelOnboardingComplete: void 0,
|
|
1094
1163
|
useClaudeCli: void 0,
|
|
1095
1164
|
useDroidCli: void 0,
|
|
1096
1165
|
useLlamaCpp: void 0,
|
|
1166
|
+
useCursorCli: void 0,
|
|
1167
|
+
/*
|
|
1168
|
+
FNXC:CursorCli 2026-07-02-00:00:
|
|
1169
|
+
Cursor CLI binary overrides are global operator settings because executable locations are machine-local. Blank/undefined preserves PATH auto-detection through cursor-agent and cursor.
|
|
1170
|
+
*/
|
|
1171
|
+
cursorCliBinaryPath: void 0,
|
|
1097
1172
|
// Global baseline lanes for per-role model selection
|
|
1098
1173
|
executionGlobalProvider: void 0,
|
|
1099
1174
|
executionGlobalModelId: void 0,
|
|
@@ -1443,6 +1518,14 @@ var init_settings_schema = __esm({
|
|
|
1443
1518
|
githubTrackingEnabledByDefault: false,
|
|
1444
1519
|
githubLinkImportedIssuesToTracking: false,
|
|
1445
1520
|
githubTrackingDefaultRepo: void 0,
|
|
1521
|
+
gitlabEnabled: void 0,
|
|
1522
|
+
gitlabInstanceUrl: void 0,
|
|
1523
|
+
gitlabApiBaseUrl: void 0,
|
|
1524
|
+
gitlabAuthToken: void 0,
|
|
1525
|
+
gitlabAuthTokenType: void 0,
|
|
1526
|
+
gitlabCommentOnDone: false,
|
|
1527
|
+
gitlabCommentTemplate: void 0,
|
|
1528
|
+
gitlabCloseSourceIssueOnDone: false,
|
|
1446
1529
|
githubTrackingDedupEnabled: true,
|
|
1447
1530
|
githubAuthMode: "gh-cli",
|
|
1448
1531
|
githubAuthToken: void 0,
|
|
@@ -2085,6 +2168,9 @@ function categorizeToolName(toolName) {
|
|
|
2085
2168
|
if (name.startsWith("fn_task_import_github") || name.startsWith("fn_task_browse_github")) {
|
|
2086
2169
|
return "github";
|
|
2087
2170
|
}
|
|
2171
|
+
if (name.startsWith("fn_task_import_gitlab") || name.startsWith("fn_task_browse_gitlab")) {
|
|
2172
|
+
return "gitlab";
|
|
2173
|
+
}
|
|
2088
2174
|
if (name === "fn_web_fetch") return "network";
|
|
2089
2175
|
if (name.startsWith("fn_secret_")) return "secrets";
|
|
2090
2176
|
if (name.startsWith("fn_skills_")) return "skills";
|
|
@@ -2989,12 +3075,13 @@ function validateMessageMetadata(metadata) {
|
|
|
2989
3075
|
throw new Error("metadata.wakeRecipient must be a boolean");
|
|
2990
3076
|
}
|
|
2991
3077
|
}
|
|
2992
|
-
var THINKING_LEVELS, COLUMNS, DEFAULT_COLUMN, TASK_PRIORITIES, DEFAULT_TASK_PRIORITY, WORKFLOW_WORK_ITEM_KINDS, WORKFLOW_WORK_ITEM_STATES, HIGH_FANOUT_BLOCKER_TODO_THRESHOLD, STALE_HIGH_FANOUT_BLOCKER_AGE_THRESHOLD_MS, EXECUTION_MODES, DEFAULT_EXECUTION_MODE, THEME_MODES, COLOR_THEMES, SUPPORTED_LOCALES, DEFAULT_LOCALE, warnedLegacyCwdMain, MERGE_ADVANCE_AUTO_SYNC_MODES, MERGE_STRATEGY_OVERLAP_BEHAVIORS, POST_MERGE_AUDIT_MODES, MERGE_AUDIT_AUTO_RECOVERY_MODES, MERGER_MODES, AUTO_RECOVERY_MODES, NOTIFICATION_EVENTS, DOCUMENT_KEY_RE, REPO_OVERRIDE_RE, CheckoutConflictError, DUPLICATE_OF_METADATA_KEY, WorkspaceTaskMergeError, SHARED_STATE_SNAPSHOT_VERSION, COLUMN_LABELS, COLUMN_DESCRIPTIONS, VALID_TRANSITIONS, AGENT_VALID_TRANSITIONS, AGENT_PERMISSIONS, PERMANENT_AGENT_ACTION_CATEGORIES, AGENT_PERMISSION_POLICY_ACTION_CATEGORIES, AGENT_PERMISSION_POLICY_CATEGORY_TOOL_EXAMPLES, AGENT_PERMISSION_POLICY_EXEMPT_TOOL_EXAMPLES, AGENT_PROVISIONING_APPROVAL_MODES, SECRET_ACCESS_POLICIES, SANDBOX_PROVISIONING_APPROVAL_MODES, AGENT_PERMISSION_POLICY_PRESET_IDS, APPROVAL_REQUEST_STATUSES, APPROVAL_REQUEST_AUDIT_EVENT_TYPES, LEGACY_AGENT_PERMISSION_POLICY_ACTION_CATEGORY_ALIASES, DEFAULT_HEARTBEAT_PROCEDURE_PATH, DASHBOARD_USER_ID, DASHBOARD_USER_ALIASES;
|
|
3078
|
+
var THINKING_LEVELS, COLUMNS, DEFAULT_COLUMN, TASK_PRIORITIES, DEFAULT_TASK_PRIORITY, WORKFLOW_WORK_ITEM_KINDS, WORKFLOW_WORK_ITEM_STATES, HIGH_FANOUT_BLOCKER_TODO_THRESHOLD, STALE_HIGH_FANOUT_BLOCKER_AGE_THRESHOLD_MS, EXECUTION_MODES, DEFAULT_EXECUTION_MODE, THEME_MODES, COLOR_THEMES, SUPPORTED_LOCALES, DEFAULT_LOCALE, warnedLegacyCwdMain, MERGE_ADVANCE_AUTO_SYNC_MODES, MERGE_STRATEGY_OVERLAP_BEHAVIORS, POST_MERGE_AUDIT_MODES, MERGE_AUDIT_AUTO_RECOVERY_MODES, MERGER_MODES, AUTO_RECOVERY_MODES, NOTIFICATION_EVENTS, DOCUMENT_KEY_RE, REPO_OVERRIDE_RE, CheckoutConflictError, DUPLICATE_OF_METADATA_KEY, WorkspaceTaskMergeError, SHARED_STATE_SNAPSHOT_VERSION, COLUMN_LABELS, COLUMN_DESCRIPTIONS, VALID_TRANSITIONS, PLANNING_DEEPEN_CHECKPOINT_QUESTION, PLANNING_DEEPEN_CHECKPOINT_ID, PLANNING_DEEPEN_PROCEED_OPTION_ID, PLANNING_DEEPEN_PROCEED_RESPONSE_KEY, AGENT_VALID_TRANSITIONS, AGENT_PERMISSIONS, PERMANENT_AGENT_ACTION_CATEGORIES, AGENT_PERMISSION_POLICY_ACTION_CATEGORIES, AGENT_PERMISSION_POLICY_CATEGORY_TOOL_EXAMPLES, AGENT_PERMISSION_POLICY_EXEMPT_TOOL_EXAMPLES, AGENT_PROVISIONING_APPROVAL_MODES, SECRET_ACCESS_POLICIES, SANDBOX_PROVISIONING_APPROVAL_MODES, AGENT_PERMISSION_POLICY_PRESET_IDS, APPROVAL_REQUEST_STATUSES, APPROVAL_REQUEST_AUDIT_EVENT_TYPES, LEGACY_AGENT_PERMISSION_POLICY_ACTION_CATEGORY_ALIASES, DEFAULT_HEARTBEAT_PROCEDURE_PATH, DASHBOARD_USER_ID, DASHBOARD_USER_ALIASES;
|
|
2993
3079
|
var init_types = __esm({
|
|
2994
3080
|
"../core/src/types.ts"() {
|
|
2995
3081
|
"use strict";
|
|
2996
3082
|
init_capacity();
|
|
2997
3083
|
init_mcp_config();
|
|
3084
|
+
init_gitlab_config();
|
|
2998
3085
|
init_settings_validation();
|
|
2999
3086
|
init_settings_schema();
|
|
3000
3087
|
init_prompt_overrides();
|
|
@@ -3200,6 +3287,10 @@ var init_types = __esm({
|
|
|
3200
3287
|
done: ["todo", "triage", "archived"],
|
|
3201
3288
|
archived: ["done"]
|
|
3202
3289
|
};
|
|
3290
|
+
PLANNING_DEEPEN_CHECKPOINT_QUESTION = "Would you like to go deeper?";
|
|
3291
|
+
PLANNING_DEEPEN_CHECKPOINT_ID = "__planning_deepen_checkpoint__";
|
|
3292
|
+
PLANNING_DEEPEN_PROCEED_OPTION_ID = "__planning_deepen_proceed_to_final__";
|
|
3293
|
+
PLANNING_DEEPEN_PROCEED_RESPONSE_KEY = "__planning_deepen_proceed__";
|
|
3203
3294
|
AGENT_VALID_TRANSITIONS = {
|
|
3204
3295
|
idle: ["active"],
|
|
3205
3296
|
active: ["idle", "running", "paused", "error"],
|
|
@@ -3272,6 +3363,12 @@ var init_types = __esm({
|
|
|
3272
3363
|
"fn_delegate_task",
|
|
3273
3364
|
"fn_task_import_github",
|
|
3274
3365
|
"fn_task_import_github_issue",
|
|
3366
|
+
"fn_task_import_gitlab_project_issues",
|
|
3367
|
+
"fn_task_import_gitlab_group_issues",
|
|
3368
|
+
"fn_task_import_gitlab_merge_requests",
|
|
3369
|
+
"fn_task_browse_gitlab_project_issues",
|
|
3370
|
+
"fn_task_browse_gitlab_group_issues",
|
|
3371
|
+
"fn_task_browse_gitlab_merge_requests",
|
|
3275
3372
|
"fn_spawn_agent",
|
|
3276
3373
|
"fn_update_agent_config",
|
|
3277
3374
|
"fn_task_update",
|
|
@@ -3586,6 +3683,20 @@ var init_near_duplicate_canonical = __esm({
|
|
|
3586
3683
|
}
|
|
3587
3684
|
});
|
|
3588
3685
|
|
|
3686
|
+
// ../core/src/gitlab-tracking.ts
|
|
3687
|
+
function formatGitLabTrackedItemRef(item) {
|
|
3688
|
+
const marker = item.kind === "merge_request" ? "!" : "#";
|
|
3689
|
+
return `${item.host} ${item.kind} ${marker}${item.iid}`;
|
|
3690
|
+
}
|
|
3691
|
+
function isGitLabTrackingStale(item) {
|
|
3692
|
+
return Boolean(item?.staleAt || item?.staleReason);
|
|
3693
|
+
}
|
|
3694
|
+
var init_gitlab_tracking = __esm({
|
|
3695
|
+
"../core/src/gitlab-tracking.ts"() {
|
|
3696
|
+
"use strict";
|
|
3697
|
+
}
|
|
3698
|
+
});
|
|
3699
|
+
|
|
3589
3700
|
// ../core/src/frontend-ux-policy.ts
|
|
3590
3701
|
function applyFrontendUxCriteria(promptMarkdown, fileScopePaths) {
|
|
3591
3702
|
if (promptMarkdown.includes(FRONTEND_UX_HEADING)) {
|
|
@@ -13032,7 +13143,7 @@ var init_task_title_id_drift = __esm({
|
|
|
13032
13143
|
});
|
|
13033
13144
|
|
|
13034
13145
|
// ../core/src/db.ts
|
|
13035
|
-
import { basename, isAbsolute, join as join2 } from "node:path";
|
|
13146
|
+
import { basename, dirname, isAbsolute, join as join2 } from "node:path";
|
|
13036
13147
|
import { mkdirSync, existsSync, statSync, renameSync, rmSync } from "node:fs";
|
|
13037
13148
|
import { spawn, spawnSync } from "node:child_process";
|
|
13038
13149
|
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
@@ -13302,7 +13413,7 @@ var init_db = __esm({
|
|
|
13302
13413
|
DEFAULT_SQLITE_BUSY_TIMEOUT_MS = 5e3;
|
|
13303
13414
|
DEFAULT_SQLITE_LOCK_RECOVERY_WINDOW_MS = 1e3;
|
|
13304
13415
|
DEFAULT_SQLITE_LOCK_RECOVERY_DELAY_MS = 50;
|
|
13305
|
-
SCHEMA_VERSION =
|
|
13416
|
+
SCHEMA_VERSION = 136;
|
|
13306
13417
|
TASKS_FTS_AUTOMERGE = 8;
|
|
13307
13418
|
TASKS_FTS_CRISISMERGE = 16;
|
|
13308
13419
|
TASKS_FTS_MERGE_PAGES = 16;
|
|
@@ -13396,6 +13507,8 @@ CREATE TABLE IF NOT EXISTS tasks (
|
|
|
13396
13507
|
prInfos TEXT,
|
|
13397
13508
|
issueInfo TEXT,
|
|
13398
13509
|
githubTracking TEXT,
|
|
13510
|
+
-- FNXC:GitLabTracking 2026-07-02-00:00: GitLab item links are a nullable JSON column so project/group issue and merge-request metadata from GitLab.com or self-managed instances round-trip without altering GitHub tracking/source-issue columns.
|
|
13511
|
+
gitlabTracking TEXT,
|
|
13399
13512
|
sourceIssueProvider TEXT,
|
|
13400
13513
|
sourceIssueRepository TEXT,
|
|
13401
13514
|
sourceIssueExternalIssueId TEXT,
|
|
@@ -14520,6 +14633,23 @@ CREATE INDEX IF NOT EXISTS idxIncidentsResolvedAt ON incidents(resolvedAt);
|
|
|
14520
14633
|
createdAt: "TEXT NOT NULL",
|
|
14521
14634
|
attachments: "TEXT"
|
|
14522
14635
|
},
|
|
14636
|
+
chat_token_usage: {
|
|
14637
|
+
id: "TEXT PRIMARY KEY",
|
|
14638
|
+
sourceKind: "TEXT NOT NULL",
|
|
14639
|
+
chatSessionId: "TEXT",
|
|
14640
|
+
roomId: "TEXT",
|
|
14641
|
+
messageId: "TEXT",
|
|
14642
|
+
projectId: "TEXT",
|
|
14643
|
+
agentId: "TEXT",
|
|
14644
|
+
modelProvider: "TEXT",
|
|
14645
|
+
modelId: "TEXT",
|
|
14646
|
+
inputTokens: "INTEGER NOT NULL DEFAULT 0",
|
|
14647
|
+
outputTokens: "INTEGER NOT NULL DEFAULT 0",
|
|
14648
|
+
cachedTokens: "INTEGER NOT NULL DEFAULT 0",
|
|
14649
|
+
cacheWriteTokens: "INTEGER NOT NULL DEFAULT 0",
|
|
14650
|
+
totalTokens: "INTEGER NOT NULL DEFAULT 0",
|
|
14651
|
+
createdAt: "TEXT NOT NULL"
|
|
14652
|
+
},
|
|
14523
14653
|
runAuditEvents: {
|
|
14524
14654
|
id: "TEXT PRIMARY KEY",
|
|
14525
14655
|
timestamp: "TEXT NOT NULL",
|
|
@@ -14682,6 +14812,7 @@ CREATE INDEX IF NOT EXISTS idxIncidentsResolvedAt ON incidents(resolvedAt);
|
|
|
14682
14812
|
static sharedIntegrityChecks = /* @__PURE__ */ new Map();
|
|
14683
14813
|
db;
|
|
14684
14814
|
dbPath;
|
|
14815
|
+
fusionDir;
|
|
14685
14816
|
inMemory;
|
|
14686
14817
|
/** Returns the database file path (or ":memory:" for in-memory databases). */
|
|
14687
14818
|
get path() {
|
|
@@ -14702,6 +14833,7 @@ CREATE INDEX IF NOT EXISTS idxIncidentsResolvedAt ON incidents(resolvedAt);
|
|
|
14702
14833
|
constructor(fusionDir, options) {
|
|
14703
14834
|
const inMemory = options?.inMemory === true;
|
|
14704
14835
|
this.inMemory = inMemory;
|
|
14836
|
+
this.fusionDir = fusionDir;
|
|
14705
14837
|
this.dbPath = inMemory ? ":memory:" : join2(fusionDir, "fusion.db");
|
|
14706
14838
|
this.busyTimeoutMs = Math.max(0, options?.busyTimeoutMs ?? DEFAULT_SQLITE_BUSY_TIMEOUT_MS);
|
|
14707
14839
|
this.lockRecoveryWindowMs = Math.max(0, options?.lockRecoveryWindowMs ?? DEFAULT_SQLITE_LOCK_RECOVERY_WINDOW_MS);
|
|
@@ -17576,6 +17708,39 @@ This means a caller passed a .fusion directory where a project root was expected
|
|
|
17576
17708
|
this.addColumnIfMissing("workflows", "icon", "TEXT");
|
|
17577
17709
|
});
|
|
17578
17710
|
}
|
|
17711
|
+
if (version2 < 135) {
|
|
17712
|
+
this.applyMigration(135, () => {
|
|
17713
|
+
this.addColumnIfMissing("tasks", "gitlabTracking", "TEXT");
|
|
17714
|
+
});
|
|
17715
|
+
}
|
|
17716
|
+
if (version2 < 136) {
|
|
17717
|
+
this.applyMigration(136, () => {
|
|
17718
|
+
this.db.exec(`
|
|
17719
|
+
CREATE TABLE IF NOT EXISTS chat_token_usage (
|
|
17720
|
+
id TEXT PRIMARY KEY,
|
|
17721
|
+
sourceKind TEXT NOT NULL,
|
|
17722
|
+
chatSessionId TEXT,
|
|
17723
|
+
roomId TEXT,
|
|
17724
|
+
messageId TEXT,
|
|
17725
|
+
projectId TEXT,
|
|
17726
|
+
agentId TEXT,
|
|
17727
|
+
modelProvider TEXT,
|
|
17728
|
+
modelId TEXT,
|
|
17729
|
+
inputTokens INTEGER NOT NULL DEFAULT 0,
|
|
17730
|
+
outputTokens INTEGER NOT NULL DEFAULT 0,
|
|
17731
|
+
cachedTokens INTEGER NOT NULL DEFAULT 0,
|
|
17732
|
+
cacheWriteTokens INTEGER NOT NULL DEFAULT 0,
|
|
17733
|
+
totalTokens INTEGER NOT NULL DEFAULT 0,
|
|
17734
|
+
createdAt TEXT NOT NULL
|
|
17735
|
+
);
|
|
17736
|
+
CREATE INDEX IF NOT EXISTS idxChatTokenUsageCreatedAt ON chat_token_usage(createdAt);
|
|
17737
|
+
CREATE INDEX IF NOT EXISTS idxChatTokenUsageProjectCreatedAt ON chat_token_usage(projectId, createdAt);
|
|
17738
|
+
CREATE INDEX IF NOT EXISTS idxChatTokenUsageSessionMessage ON chat_token_usage(chatSessionId, messageId);
|
|
17739
|
+
CREATE INDEX IF NOT EXISTS idxChatTokenUsageRoomMessage ON chat_token_usage(roomId, messageId);
|
|
17740
|
+
CREATE INDEX IF NOT EXISTS idxChatTokenUsageAgentCreatedAt ON chat_token_usage(agentId, createdAt);
|
|
17741
|
+
`);
|
|
17742
|
+
});
|
|
17743
|
+
}
|
|
17579
17744
|
}
|
|
17580
17745
|
/**
|
|
17581
17746
|
* Idempotent schema reconciliation for the PR-entity tables. ensureSchema-
|
|
@@ -17950,23 +18115,61 @@ This means a caller passed a .fusion directory where a project root was expected
|
|
|
17950
18115
|
const value = this.getMetaValue(_Database.PROJECT_IDENTITY_META_KEY);
|
|
17951
18116
|
return fromJson(value);
|
|
17952
18117
|
}
|
|
18118
|
+
parseWorkflowSettingsJson(raw) {
|
|
18119
|
+
if (!raw) return {};
|
|
18120
|
+
try {
|
|
18121
|
+
const parsed = JSON.parse(raw);
|
|
18122
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
18123
|
+
} catch {
|
|
18124
|
+
return {};
|
|
18125
|
+
}
|
|
18126
|
+
}
|
|
18127
|
+
reconcileWorkflowSettingsRootDirProjectId(identityId) {
|
|
18128
|
+
if (this.inMemory) return false;
|
|
18129
|
+
const rootDirProjectId = basename(this.fusionDir) === ".fusion" ? dirname(this.fusionDir) : this.fusionDir;
|
|
18130
|
+
if (!rootDirProjectId || rootDirProjectId === identityId) return false;
|
|
18131
|
+
const rows = this.db.prepare('SELECT workflowId, "values" FROM workflow_settings WHERE projectId = ?').all(rootDirProjectId);
|
|
18132
|
+
if (rows.length === 0) return false;
|
|
18133
|
+
let changed = false;
|
|
18134
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
18135
|
+
for (const row of rows) {
|
|
18136
|
+
const rootValues = this.parseWorkflowSettingsJson(row.values);
|
|
18137
|
+
if (Object.keys(rootValues).length === 0) continue;
|
|
18138
|
+
const identityRow = this.db.prepare('SELECT "values" FROM workflow_settings WHERE workflowId = ? AND projectId = ?').get(row.workflowId, identityId);
|
|
18139
|
+
const identityValues = this.parseWorkflowSettingsJson(identityRow?.values);
|
|
18140
|
+
const carriesEveryRootKey = Object.keys(rootValues).every((key) => Object.prototype.hasOwnProperty.call(identityValues, key));
|
|
18141
|
+
if (carriesEveryRootKey) continue;
|
|
18142
|
+
const next = { ...rootValues, ...identityValues };
|
|
18143
|
+
this.db.prepare(
|
|
18144
|
+
`INSERT INTO workflow_settings (workflowId, projectId, "values", updatedAt)
|
|
18145
|
+
VALUES (?, ?, ?, ?)
|
|
18146
|
+
ON CONFLICT(workflowId, projectId)
|
|
18147
|
+
DO UPDATE SET "values" = excluded."values", updatedAt = excluded.updatedAt`
|
|
18148
|
+
).run(row.workflowId, identityId, JSON.stringify(next), now);
|
|
18149
|
+
changed = true;
|
|
18150
|
+
}
|
|
18151
|
+
return changed;
|
|
18152
|
+
}
|
|
17953
18153
|
setProjectIdentity(identity, options) {
|
|
17954
18154
|
const stored = this.getProjectIdentity();
|
|
17955
18155
|
const force = options?.force === true;
|
|
17956
|
-
if (stored) {
|
|
17957
|
-
|
|
17958
|
-
|
|
17959
|
-
|
|
17960
|
-
|
|
17961
|
-
|
|
17962
|
-
|
|
17963
|
-
storedPath: stored.firstSeenPath,
|
|
17964
|
-
incomingId: identity.id,
|
|
17965
|
-
incomingPath: identity.firstSeenPath
|
|
17966
|
-
});
|
|
17967
|
-
}
|
|
18156
|
+
if (stored && stored.id !== identity.id && !force) {
|
|
18157
|
+
throw new ProjectIdentityConflictError({
|
|
18158
|
+
storedId: stored.id,
|
|
18159
|
+
storedPath: stored.firstSeenPath,
|
|
18160
|
+
incomingId: identity.id,
|
|
18161
|
+
incomingPath: identity.firstSeenPath
|
|
18162
|
+
});
|
|
17968
18163
|
}
|
|
17969
|
-
this.
|
|
18164
|
+
this.transactionImmediate(() => {
|
|
18165
|
+
let changed = false;
|
|
18166
|
+
if (!stored || stored.id !== identity.id || force) {
|
|
18167
|
+
this.setMetaValue(_Database.PROJECT_IDENTITY_META_KEY, JSON.stringify(identity));
|
|
18168
|
+
changed = true;
|
|
18169
|
+
}
|
|
18170
|
+
changed = this.reconcileWorkflowSettingsRootDirProjectId(identity.id) || changed;
|
|
18171
|
+
if (changed) this.bumpLastModified();
|
|
18172
|
+
});
|
|
17970
18173
|
}
|
|
17971
18174
|
clearProjectIdentity() {
|
|
17972
18175
|
this.db.prepare("DELETE FROM __meta WHERE key = ?").run(_Database.PROJECT_IDENTITY_META_KEY);
|
|
@@ -18022,7 +18225,7 @@ This means a caller passed a .fusion directory where a project root was expected
|
|
|
18022
18225
|
// ../core/src/agent-store.ts
|
|
18023
18226
|
import { mkdir, readFile, writeFile, readdir, unlink, rename, access, appendFile } from "node:fs/promises";
|
|
18024
18227
|
import { constants as fsConstants } from "node:fs";
|
|
18025
|
-
import { basename as basename2, dirname, join as join3, resolve as resolve2 } from "node:path";
|
|
18228
|
+
import { basename as basename2, dirname as dirname2, join as join3, resolve as resolve2 } from "node:path";
|
|
18026
18229
|
import { randomUUID as randomUUID2, randomBytes, createHash as createHash3 } from "node:crypto";
|
|
18027
18230
|
import { EventEmitter } from "node:events";
|
|
18028
18231
|
function formatCurrentTaskLine(taskId, linkedTask) {
|
|
@@ -18034,6 +18237,12 @@ function formatCurrentTaskLine(taskId, linkedTask) {
|
|
|
18034
18237
|
}
|
|
18035
18238
|
return `Current Task: ${taskId} (${linkedTask.column})`;
|
|
18036
18239
|
}
|
|
18240
|
+
function normalizeStoredPermissions(raw) {
|
|
18241
|
+
if (!raw) return void 0;
|
|
18242
|
+
const normalized = normalizePermissions(raw);
|
|
18243
|
+
if (normalized.size === 0) return void 0;
|
|
18244
|
+
return Object.fromEntries([...normalized].map((permission) => [permission, true]));
|
|
18245
|
+
}
|
|
18037
18246
|
function resolveCreationRuntimeConfig(incoming, metadata) {
|
|
18038
18247
|
const isEphemeral = isEphemeralAgent({ metadata });
|
|
18039
18248
|
if (isEphemeral) {
|
|
@@ -18159,7 +18368,7 @@ var init_agent_store = __esm({
|
|
|
18159
18368
|
const newAbsPath = join3(this.rootDir, "..", newRelPath);
|
|
18160
18369
|
if (legacyContent !== null) {
|
|
18161
18370
|
try {
|
|
18162
|
-
await mkdir(
|
|
18371
|
+
await mkdir(dirname2(newAbsPath), { recursive: true });
|
|
18163
18372
|
try {
|
|
18164
18373
|
await access(newAbsPath, fsConstants.F_OK);
|
|
18165
18374
|
} catch {
|
|
@@ -18391,7 +18600,7 @@ var init_agent_store = __esm({
|
|
|
18391
18600
|
const agentId = `agent-${randomUUID2().slice(0, 8)}`;
|
|
18392
18601
|
const runtimeConfig = resolveCreationRuntimeConfig(input.runtimeConfig, metadata);
|
|
18393
18602
|
const resolvedHeartbeatProcedurePath = input.heartbeatProcedurePath ?? (ephemeral ? void 0 : getDefaultHeartbeatProcedurePath(agentId, input.name));
|
|
18394
|
-
const normalizedPermissionPolicy =
|
|
18603
|
+
const normalizedPermissionPolicy = input.permissionPolicy ? normalizeAgentPermissionPolicy(input.permissionPolicy) : void 0;
|
|
18395
18604
|
const agent = {
|
|
18396
18605
|
id: agentId,
|
|
18397
18606
|
name: normalizedName,
|
|
@@ -18408,7 +18617,7 @@ var init_agent_store = __esm({
|
|
|
18408
18617
|
...input.imageUrl && { imageUrl: input.imageUrl },
|
|
18409
18618
|
...input.reportsTo && { reportsTo: input.reportsTo },
|
|
18410
18619
|
...runtimeConfig && { runtimeConfig },
|
|
18411
|
-
...input.permissions && { permissions: input.permissions },
|
|
18620
|
+
...normalizeStoredPermissions(input.permissions) && { permissions: normalizeStoredPermissions(input.permissions) },
|
|
18412
18621
|
...normalizedPermissionPolicy && { permissionPolicy: normalizedPermissionPolicy },
|
|
18413
18622
|
...input.instructionsPath && { instructionsPath: input.instructionsPath },
|
|
18414
18623
|
...input.instructionsText && { instructionsText: input.instructionsText },
|
|
@@ -18795,7 +19004,7 @@ var init_agent_store = __esm({
|
|
|
18795
19004
|
..."reportsTo" in updates && { reportsTo: updates.reportsTo },
|
|
18796
19005
|
..."runtimeConfig" in updates && { runtimeConfig: updates.runtimeConfig },
|
|
18797
19006
|
..."pauseReason" in updates && { pauseReason: updates.pauseReason },
|
|
18798
|
-
..."permissions" in updates && { permissions: updates.permissions },
|
|
19007
|
+
..."permissions" in updates && { permissions: normalizeStoredPermissions(updates.permissions) },
|
|
18799
19008
|
..."permissionPolicy" in updates && { permissionPolicy: normalizedUpdatedPermissionPolicy },
|
|
18800
19009
|
..."lastError" in updates && { lastError: updates.lastError },
|
|
18801
19010
|
..."totalInputTokens" in updates && { totalInputTokens: updates.totalInputTokens },
|
|
@@ -20191,8 +20400,8 @@ var init_agent_store = __esm({
|
|
|
20191
20400
|
reportsTo: data.reportsTo,
|
|
20192
20401
|
runtimeConfig: data.runtimeConfig,
|
|
20193
20402
|
pauseReason: data.pauseReason,
|
|
20194
|
-
permissions: data.permissions,
|
|
20195
|
-
permissionPolicy:
|
|
20403
|
+
permissions: normalizeStoredPermissions(data.permissions),
|
|
20404
|
+
permissionPolicy: data.permissionPolicy ? normalizeAgentPermissionPolicy(data.permissionPolicy) : void 0,
|
|
20196
20405
|
totalInputTokens: data.totalInputTokens,
|
|
20197
20406
|
totalOutputTokens: data.totalOutputTokens,
|
|
20198
20407
|
lastError: data.lastError,
|
|
@@ -21809,7 +22018,7 @@ var init_task_priority = __esm({
|
|
|
21809
22018
|
|
|
21810
22019
|
// ../core/src/global-settings.ts
|
|
21811
22020
|
import { homedir } from "node:os";
|
|
21812
|
-
import { basename as basename3, dirname as
|
|
22021
|
+
import { basename as basename3, dirname as dirname3, join as join5, resolve as resolve4 } from "node:path";
|
|
21813
22022
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3, rename as rename2, chmod } from "node:fs/promises";
|
|
21814
22023
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, realpathSync as realpathSync2, renameSync as renameSync2 } from "node:fs";
|
|
21815
22024
|
function getHomeDir() {
|
|
@@ -21823,7 +22032,7 @@ function resolveGlobalDirForHome(homeDir) {
|
|
|
21823
22032
|
const legacyDir = join5(homeDir, ".pi", "fusion");
|
|
21824
22033
|
if (existsSync3(legacyDir)) {
|
|
21825
22034
|
try {
|
|
21826
|
-
mkdirSync2(
|
|
22035
|
+
mkdirSync2(dirname3(preferredDir), { recursive: true });
|
|
21827
22036
|
renameSync2(legacyDir, preferredDir);
|
|
21828
22037
|
return preferredDir;
|
|
21829
22038
|
} catch {
|
|
@@ -21833,7 +22042,7 @@ function resolveGlobalDirForHome(homeDir) {
|
|
|
21833
22042
|
const legacyDirOriginal = join5(homeDir, ".pi", "kb");
|
|
21834
22043
|
if (existsSync3(legacyDirOriginal)) {
|
|
21835
22044
|
try {
|
|
21836
|
-
mkdirSync2(
|
|
22045
|
+
mkdirSync2(dirname3(preferredDir), { recursive: true });
|
|
21837
22046
|
renameSync2(legacyDirOriginal, preferredDir);
|
|
21838
22047
|
return preferredDir;
|
|
21839
22048
|
} catch {
|
|
@@ -21851,7 +22060,7 @@ function resolveGlobalDir(dir) {
|
|
|
21851
22060
|
}
|
|
21852
22061
|
if (hasExplicitDir) {
|
|
21853
22062
|
if (process.env.VITEST !== "true" && process.env.FUSION_ALLOW_PROJECT_LOCAL_GLOBAL_DIR !== "true") {
|
|
21854
|
-
const isFusionDirInsideRepo = basename3(dir) === ".fusion" && existsSync3(join5(
|
|
22063
|
+
const isFusionDirInsideRepo = basename3(dir) === ".fusion" && existsSync3(join5(dirname3(dir), ".git"));
|
|
21855
22064
|
if (isFusionDirInsideRepo) {
|
|
21856
22065
|
const homeGlobalDir = resolveGlobalDirForHome(getHomeDir());
|
|
21857
22066
|
const normalize8 = (p2) => {
|
|
@@ -27801,7 +28010,7 @@ var init_plugin_types = __esm({
|
|
|
27801
28010
|
// ../core/src/project-root-guard.ts
|
|
27802
28011
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
27803
28012
|
import { existsSync as existsSync7 } from "node:fs";
|
|
27804
|
-
import { dirname as
|
|
28013
|
+
import { dirname as dirname4, join as join9, resolve as resolve5 } from "node:path";
|
|
27805
28014
|
function assertProjectRootDir(rootDir, caller) {
|
|
27806
28015
|
if (FUSION_DIR_SUFFIX.test(rootDir)) {
|
|
27807
28016
|
throw new Error(
|
|
@@ -27837,7 +28046,7 @@ function assertNotLinkedWorktreeOfExistingProject(rootDir, _caller) {
|
|
|
27837
28046
|
if (resolvedCommonDir === resolvedGitDir) {
|
|
27838
28047
|
return;
|
|
27839
28048
|
}
|
|
27840
|
-
const parentRoot = resolvedCommonDir.endsWith(`${join9("", ".git")}`) ?
|
|
28049
|
+
const parentRoot = resolvedCommonDir.endsWith(`${join9("", ".git")}`) ? dirname4(resolvedCommonDir) : resolvedCommonDir;
|
|
27841
28050
|
if (!existsSync7(join9(parentRoot, ".fusion", "fusion.db"))) {
|
|
27842
28051
|
return;
|
|
27843
28052
|
}
|
|
@@ -30823,11 +31032,11 @@ var init_eval_store = __esm({
|
|
|
30823
31032
|
|
|
30824
31033
|
// ../core/src/app-version.ts
|
|
30825
31034
|
import { readFileSync } from "node:fs";
|
|
30826
|
-
import { join as join11, dirname as
|
|
31035
|
+
import { join as join11, dirname as dirname5 } from "node:path";
|
|
30827
31036
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
30828
31037
|
function getAppVersion() {
|
|
30829
31038
|
if (cachedVersion !== null) return cachedVersion;
|
|
30830
|
-
const __dirname2 =
|
|
31039
|
+
const __dirname2 = dirname5(fileURLToPath2(import.meta.url));
|
|
30831
31040
|
let currentDir = __dirname2;
|
|
30832
31041
|
for (let i = 0; i < 10; i++) {
|
|
30833
31042
|
try {
|
|
@@ -30839,7 +31048,7 @@ function getAppVersion() {
|
|
|
30839
31048
|
}
|
|
30840
31049
|
} catch {
|
|
30841
31050
|
}
|
|
30842
|
-
const parentDir =
|
|
31051
|
+
const parentDir = dirname5(currentDir);
|
|
30843
31052
|
if (parentDir === currentDir) break;
|
|
30844
31053
|
currentDir = parentDir;
|
|
30845
31054
|
}
|
|
@@ -37566,7 +37775,7 @@ __export(migration_exports, {
|
|
|
37566
37775
|
});
|
|
37567
37776
|
import { existsSync as existsSync11, readFileSync as readFileSync2, realpathSync as realpathSync3 } from "node:fs";
|
|
37568
37777
|
import { homedir as homedir2, tmpdir } from "node:os";
|
|
37569
|
-
import { isAbsolute as isAbsolute3, join as join14, resolve as resolve8, basename as basename6, dirname as
|
|
37778
|
+
import { isAbsolute as isAbsolute3, join as join14, resolve as resolve8, basename as basename6, dirname as dirname6 } from "node:path";
|
|
37570
37779
|
function getHomeDir2() {
|
|
37571
37780
|
return process.env.HOME || process.env.USERPROFILE || homedir2();
|
|
37572
37781
|
}
|
|
@@ -37667,7 +37876,7 @@ var init_migration = __esm({
|
|
|
37667
37876
|
const visited = /* @__PURE__ */ new Set();
|
|
37668
37877
|
let current = resolve8(startDir);
|
|
37669
37878
|
const home = resolve8(getHomeDir2());
|
|
37670
|
-
const root =
|
|
37879
|
+
const root = dirname6(current) === current ? current : "/";
|
|
37671
37880
|
const systemTmp = resolve8(tmpdir());
|
|
37672
37881
|
const normalizePath4 = (path6) => {
|
|
37673
37882
|
try {
|
|
@@ -37704,7 +37913,7 @@ var init_migration = __esm({
|
|
|
37704
37913
|
if (isStopBoundary) {
|
|
37705
37914
|
break;
|
|
37706
37915
|
}
|
|
37707
|
-
const parent =
|
|
37916
|
+
const parent = dirname6(current);
|
|
37708
37917
|
if (parent === current) break;
|
|
37709
37918
|
current = parent;
|
|
37710
37919
|
}
|
|
@@ -48807,7 +49016,7 @@ var init_memory_dreams = __esm({
|
|
|
48807
49016
|
// ../core/src/memory-backend.ts
|
|
48808
49017
|
import { readFile as readFile6, writeFile as writeFile5, mkdir as mkdir6, access as access3, constants, readdir as readdir4, stat as stat2 } from "node:fs/promises";
|
|
48809
49018
|
import { existsSync as existsSync13 } from "node:fs";
|
|
48810
|
-
import { basename as basename7, dirname as
|
|
49019
|
+
import { basename as basename7, dirname as dirname7, isAbsolute as isAbsolute4, join as join18, normalize as normalize3, relative, resolve as resolve9, sep as sep3 } from "node:path";
|
|
48811
49020
|
import { createHash as createHash7 } from "node:crypto";
|
|
48812
49021
|
function shouldSkipBackgroundQmdRefresh() {
|
|
48813
49022
|
return (process.env.VITEST === "true" || process.env.NODE_ENV === "test") && process.env.FUSION_ENABLE_QMD_REFRESH_IN_TESTS !== "1";
|
|
@@ -48973,7 +49182,7 @@ async function readProjectMemoryFileContent(rootDir, path6) {
|
|
|
48973
49182
|
}
|
|
48974
49183
|
async function writeProjectMemoryFile(rootDir, path6, content) {
|
|
48975
49184
|
const { absPath } = resolveMemoryFilePath(rootDir, path6);
|
|
48976
|
-
await mkdir6(
|
|
49185
|
+
await mkdir6(dirname7(absPath), { recursive: true });
|
|
48977
49186
|
const tmpPath = `${absPath}.tmp`;
|
|
48978
49187
|
await writeFile5(tmpPath, content, "utf-8");
|
|
48979
49188
|
const { rename: rename9 } = await import("node:fs/promises");
|
|
@@ -49092,7 +49301,7 @@ async function readAgentMemoryFile(rootDir, agentId, path6) {
|
|
|
49092
49301
|
}
|
|
49093
49302
|
async function writeAgentMemoryFile(rootDir, agentId, path6, content) {
|
|
49094
49303
|
const { absPath } = await resolveAgentMemoryFilePath(rootDir, agentId, path6);
|
|
49095
|
-
await mkdir6(
|
|
49304
|
+
await mkdir6(dirname7(absPath), { recursive: true });
|
|
49096
49305
|
const tmpPath = `${absPath}.tmp`;
|
|
49097
49306
|
await writeFile5(tmpPath, content, "utf-8");
|
|
49098
49307
|
const { rename: rename9 } = await import("node:fs/promises");
|
|
@@ -49238,7 +49447,7 @@ function normalizeQmdSearchResultPath(rootDir, rawPath) {
|
|
|
49238
49447
|
}
|
|
49239
49448
|
const lowerCandidate = candidate.toLowerCase();
|
|
49240
49449
|
const normalizedBaseName = basename7(candidate).toLowerCase();
|
|
49241
|
-
const normalizedDirName =
|
|
49450
|
+
const normalizedDirName = dirname7(lowerCandidate).replace(/\\/g, "/");
|
|
49242
49451
|
if (normalizedBaseName === "memory.md" && (normalizedDirName === ".fusion" || normalizedDirName.endsWith("/.fusion"))) {
|
|
49243
49452
|
return `${MEMORY_WORKSPACE_PATH}/${MEMORY_LONG_TERM_FILENAME}`;
|
|
49244
49453
|
}
|
|
@@ -50387,14 +50596,14 @@ var init_agent_log_constants = __esm({
|
|
|
50387
50596
|
|
|
50388
50597
|
// ../core/src/agent-log-file-store.ts
|
|
50389
50598
|
import { appendFileSync, existsSync as existsSync15, mkdirSync as mkdirSync6, readFileSync as readFileSync3, readdirSync, renameSync as renameSync3, unlinkSync, writeFileSync } from "node:fs";
|
|
50390
|
-
import { dirname as
|
|
50599
|
+
import { dirname as dirname8, join as join19 } from "node:path";
|
|
50391
50600
|
function getAgentLogFilePath(taskDir) {
|
|
50392
50601
|
return join19(taskDir, AGENT_LOG_FILENAME);
|
|
50393
50602
|
}
|
|
50394
50603
|
function appendAgentLogEntriesSync(taskDir, entries) {
|
|
50395
50604
|
if (entries.length === 0) return [];
|
|
50396
50605
|
const filePath = getAgentLogFilePath(taskDir);
|
|
50397
|
-
mkdirSync6(
|
|
50606
|
+
mkdirSync6(dirname8(filePath), { recursive: true });
|
|
50398
50607
|
const startingLineNo = countLineNumbers(filePath);
|
|
50399
50608
|
const payload = entries.map((entry) => serializeEntry(entry)).join("");
|
|
50400
50609
|
appendFileSync(filePath, payload, "utf8");
|
|
@@ -51831,6 +52040,7 @@ var init_store = __esm({
|
|
|
51831
52040
|
defineTaskColumn("prInfos", (task) => toJson(task.prInfos || [])),
|
|
51832
52041
|
defineTaskColumn("issueInfo", (task) => toJsonNullable(task.issueInfo)),
|
|
51833
52042
|
defineTaskColumn("githubTracking", (task) => toJsonNullable(task.githubTracking)),
|
|
52043
|
+
defineTaskColumn("gitlabTracking", (task) => toJsonNullable(task.gitlabTracking)),
|
|
51834
52044
|
defineTaskColumn("sourceIssueProvider", (task) => task.sourceIssue?.provider ?? null),
|
|
51835
52045
|
defineTaskColumn("sourceIssueRepository", (task) => task.sourceIssue?.repository ?? null),
|
|
51836
52046
|
defineTaskColumn("sourceIssueExternalIssueId", (task) => task.sourceIssue?.externalIssueId ?? null),
|
|
@@ -52624,6 +52834,7 @@ var init_store = __esm({
|
|
|
52624
52834
|
})(),
|
|
52625
52835
|
issueInfo: fromJson(row.issueInfo),
|
|
52626
52836
|
githubTracking: fromJson(row.githubTracking) ?? void 0,
|
|
52837
|
+
gitlabTracking: fromJson(row.gitlabTracking) ?? void 0,
|
|
52627
52838
|
sourceIssue: (() => {
|
|
52628
52839
|
if (row.sourceIssueProvider === null || row.sourceIssueRepository === null || row.sourceIssueExternalIssueId === null || row.sourceIssueNumber === null) {
|
|
52629
52840
|
return void 0;
|
|
@@ -52733,6 +52944,7 @@ var init_store = __esm({
|
|
|
52733
52944
|
prInfos: slim ? void 0 : entry.prInfos,
|
|
52734
52945
|
issueInfo: slim ? void 0 : entry.issueInfo,
|
|
52735
52946
|
githubTracking: entry.githubTracking,
|
|
52947
|
+
gitlabTracking: entry.gitlabTracking,
|
|
52736
52948
|
sourceIssue: slim ? void 0 : entry.sourceIssue,
|
|
52737
52949
|
attachments: slim ? void 0 : entry.attachments,
|
|
52738
52950
|
comments: entry.comments,
|
|
@@ -52846,6 +53058,7 @@ ${recentText}` : void 0
|
|
|
52846
53058
|
prInfos: task.prInfos,
|
|
52847
53059
|
issueInfo: task.issueInfo,
|
|
52848
53060
|
githubTracking: task.githubTracking,
|
|
53061
|
+
gitlabTracking: task.gitlabTracking,
|
|
52849
53062
|
sourceIssue: task.sourceIssue,
|
|
52850
53063
|
attachments: task.attachments,
|
|
52851
53064
|
comments: task.comments,
|
|
@@ -53123,6 +53336,7 @@ ${recentText}` : void 0
|
|
|
53123
53336
|
"prInfos",
|
|
53124
53337
|
"issueInfo",
|
|
53125
53338
|
"githubTracking",
|
|
53339
|
+
"gitlabTracking",
|
|
53126
53340
|
"sourceIssueProvider",
|
|
53127
53341
|
"sourceIssueRepository",
|
|
53128
53342
|
"sourceIssueExternalIssueId",
|
|
@@ -53319,6 +53533,7 @@ ${outcome}`;
|
|
|
53319
53533
|
"prInfos",
|
|
53320
53534
|
"issueInfo",
|
|
53321
53535
|
"githubTracking",
|
|
53536
|
+
"gitlabTracking",
|
|
53322
53537
|
"sourceIssueProvider",
|
|
53323
53538
|
"sourceIssueRepository",
|
|
53324
53539
|
"sourceIssueExternalIssueId",
|
|
@@ -55096,6 +55311,7 @@ ${TASK_UPSERT_SQL_ASSIGNMENTS}
|
|
|
55096
55311
|
tokenUsage: input.tokenUsage,
|
|
55097
55312
|
sourceIssue: input.sourceIssue,
|
|
55098
55313
|
githubTracking: input.githubTracking,
|
|
55314
|
+
gitlabTracking: input.gitlabTracking,
|
|
55099
55315
|
sourceType: input.source?.sourceType ?? "unknown",
|
|
55100
55316
|
sourceAgentId: input.source?.sourceAgentId,
|
|
55101
55317
|
sourceRunId: input.source?.sourceRunId,
|
|
@@ -56146,17 +56362,23 @@ ${newTask.description}
|
|
|
56146
56362
|
}
|
|
56147
56363
|
}
|
|
56148
56364
|
async listTasksForGithubTrackingReconcile(options) {
|
|
56365
|
+
return this.listTasksForProviderTrackingReconcile("githubTracking", options);
|
|
56366
|
+
}
|
|
56367
|
+
async listTasksForGitlabTrackingReconcile(options) {
|
|
56368
|
+
return this.listTasksForProviderTrackingReconcile("gitlabTracking", options);
|
|
56369
|
+
}
|
|
56370
|
+
listTasksForProviderTrackingReconcile(trackingColumn, options) {
|
|
56149
56371
|
const reconcileScanLimit = 200;
|
|
56150
56372
|
const offset = Math.max(0, options?.offset ?? 0);
|
|
56151
56373
|
const limit = Math.max(0, options?.limit ?? reconcileScanLimit);
|
|
56152
56374
|
const selectClause = this.getTaskSelectClause(true);
|
|
56153
56375
|
const deletedTotal = this.db.prepare(
|
|
56154
|
-
|
|
56376
|
+
`SELECT COUNT(*) as count FROM tasks WHERE "deletedAt" IS NOT NULL AND "${trackingColumn}" IS NOT NULL`
|
|
56155
56377
|
).get();
|
|
56156
56378
|
const deletedCount = Number(deletedTotal?.count ?? 0);
|
|
56157
56379
|
const deletedOffset = Math.min(offset, deletedCount);
|
|
56158
56380
|
const deletedRows = this.db.prepare(
|
|
56159
|
-
`SELECT ${selectClause} FROM tasks WHERE "deletedAt" IS NOT NULL AND "
|
|
56381
|
+
`SELECT ${selectClause} FROM tasks WHERE "deletedAt" IS NOT NULL AND "${trackingColumn}" IS NOT NULL ORDER BY updatedAt ASC LIMIT ? OFFSET ?`
|
|
56160
56382
|
).all(limit, deletedOffset);
|
|
56161
56383
|
const deletedTasks = deletedRows.map((row) => {
|
|
56162
56384
|
const task = this.rowToTask(row);
|
|
@@ -56167,7 +56389,7 @@ ${newTask.description}
|
|
|
56167
56389
|
let archivedTasks = [];
|
|
56168
56390
|
let archivedCount = 0;
|
|
56169
56391
|
try {
|
|
56170
|
-
const archivedCandidates = this.archiveDb.list().map((entry) => this.archiveEntryToTask(entry, true)).filter((task) => Boolean(task
|
|
56392
|
+
const archivedCandidates = this.archiveDb.list().map((entry) => this.archiveEntryToTask(entry, true)).filter((task) => Boolean(task[trackingColumn]));
|
|
56171
56393
|
archivedCount = archivedCandidates.length;
|
|
56172
56394
|
const archivedOffset = Math.max(0, offset - deletedCount);
|
|
56173
56395
|
const remainingLimit = Math.max(0, limit - deletedTasks.length);
|
|
@@ -58456,6 +58678,38 @@ ${newTask.description}
|
|
|
58456
58678
|
}
|
|
58457
58679
|
task.githubTracking = nextTracking;
|
|
58458
58680
|
}
|
|
58681
|
+
if (updates.gitlabTracking === null) {
|
|
58682
|
+
task.gitlabTracking = void 0;
|
|
58683
|
+
} else if (updates.gitlabTracking !== void 0) {
|
|
58684
|
+
const previousTracking = task.gitlabTracking;
|
|
58685
|
+
const previousItem = previousTracking?.item;
|
|
58686
|
+
const { item: gitlabItemPatch, ...gitlabTrackingPatch } = updates.gitlabTracking;
|
|
58687
|
+
const nextTracking = {
|
|
58688
|
+
...previousTracking ?? {},
|
|
58689
|
+
...gitlabTrackingPatch
|
|
58690
|
+
};
|
|
58691
|
+
if (gitlabItemPatch === null) {
|
|
58692
|
+
if (previousItem) {
|
|
58693
|
+
task.log.push({
|
|
58694
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
58695
|
+
action: "GitLab item unlinked",
|
|
58696
|
+
outcome: `${previousItem.host} ${previousItem.kind} !${previousItem.iid}`,
|
|
58697
|
+
...runContext ? { runContext } : {}
|
|
58698
|
+
});
|
|
58699
|
+
}
|
|
58700
|
+
nextTracking.item = void 0;
|
|
58701
|
+
nextTracking.unlinkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
58702
|
+
} else if (gitlabItemPatch !== void 0) {
|
|
58703
|
+
nextTracking.item = gitlabItemPatch;
|
|
58704
|
+
task.log.push({
|
|
58705
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
58706
|
+
action: "GitLab item linked",
|
|
58707
|
+
outcome: `${gitlabItemPatch.host} ${gitlabItemPatch.kind} !${gitlabItemPatch.iid}`,
|
|
58708
|
+
...runContext ? { runContext } : {}
|
|
58709
|
+
});
|
|
58710
|
+
}
|
|
58711
|
+
task.gitlabTracking = nextTracking;
|
|
58712
|
+
}
|
|
58459
58713
|
if (updates.tokenUsage === null) {
|
|
58460
58714
|
task.tokenUsage = void 0;
|
|
58461
58715
|
} else if (updates.tokenUsage !== void 0) {
|
|
@@ -60191,9 +60445,15 @@ ${newTask.description}
|
|
|
60191
60445
|
throw new TaskSelfDeleteError(id);
|
|
60192
60446
|
}
|
|
60193
60447
|
this.flushAgentLogBuffer();
|
|
60194
|
-
|
|
60448
|
+
let task = this.readTaskFromDb(id, { includeDeleted: true });
|
|
60449
|
+
let restoredColdArchive = false;
|
|
60195
60450
|
if (!task) {
|
|
60196
|
-
|
|
60451
|
+
const archivedEntry = await this.findInArchive(id);
|
|
60452
|
+
if (!archivedEntry) {
|
|
60453
|
+
throw new Error(`Task ${id} not found`);
|
|
60454
|
+
}
|
|
60455
|
+
task = await this.restoreFromArchive(archivedEntry);
|
|
60456
|
+
restoredColdArchive = true;
|
|
60197
60457
|
}
|
|
60198
60458
|
if (task.deletedAt) {
|
|
60199
60459
|
return task;
|
|
@@ -60221,9 +60481,15 @@ ${newTask.description}
|
|
|
60221
60481
|
rewrittenDependents = this.rewriteDependentsForRemoval(id, dependentIds);
|
|
60222
60482
|
rewrittenBlockedByResidueDependents = this.rewriteBlockedByResidueDependentsForRemoval(id, new Set(dependentIds));
|
|
60223
60483
|
rewrittenLineageChildren = this.rewriteLineageChildrenForRemoval(id, lineageChildIds);
|
|
60484
|
+
if (restoredColdArchive) {
|
|
60485
|
+
this.upsertTaskWithFtsRecovery(task);
|
|
60486
|
+
}
|
|
60224
60487
|
const deletedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
60225
60488
|
const allowResurrection = options?.allowResurrection === true ? 1 : 0;
|
|
60226
60489
|
this.db.prepare(`UPDATE tasks SET "column" = 'archived', deletedAt = ?, allowResurrection = ?, updatedAt = ? WHERE id = ?`).run(deletedAt, allowResurrection, deletedAt, id);
|
|
60490
|
+
task.deletedAt = deletedAt;
|
|
60491
|
+
task.allowResurrection = options?.allowResurrection === true ? true : void 0;
|
|
60492
|
+
task.updatedAt = deletedAt;
|
|
60227
60493
|
this.recordRunAuditEvent({
|
|
60228
60494
|
domain: "database",
|
|
60229
60495
|
mutationType: "task:deleted",
|
|
@@ -60247,6 +60513,7 @@ ${newTask.description}
|
|
|
60247
60513
|
if (this.agentLogBuffer.length > 0) {
|
|
60248
60514
|
this.agentLogBuffer = this.agentLogBuffer.filter((entry) => entry.taskId !== id);
|
|
60249
60515
|
}
|
|
60516
|
+
this.archiveDb.delete(id);
|
|
60250
60517
|
if (this.isWatching) this.taskCache.delete(id);
|
|
60251
60518
|
for (const dependentTask of rewrittenDependents) {
|
|
60252
60519
|
this.emit("task:updated", dependentTask);
|
|
@@ -62316,6 +62583,76 @@ ${newTask.description}
|
|
|
62316
62583
|
return task;
|
|
62317
62584
|
});
|
|
62318
62585
|
}
|
|
62586
|
+
async updateGitLabTracking(id, tracking) {
|
|
62587
|
+
return this.withTaskLock(id, async () => {
|
|
62588
|
+
const dir = this.taskDir(id);
|
|
62589
|
+
const task = await this.readTaskJson(dir);
|
|
62590
|
+
const nextTracking = tracking ?? void 0;
|
|
62591
|
+
const previousTracking = task.gitlabTracking;
|
|
62592
|
+
if (JSON.stringify(previousTracking ?? null) === JSON.stringify(nextTracking ?? null)) {
|
|
62593
|
+
return task;
|
|
62594
|
+
}
|
|
62595
|
+
task.gitlabTracking = nextTracking;
|
|
62596
|
+
task.log.push({
|
|
62597
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
62598
|
+
action: nextTracking?.item ? "GitLab item linked" : "GitLab tracking cleared",
|
|
62599
|
+
outcome: nextTracking?.item ? `${nextTracking.item.host} ${nextTracking.item.kind} !${nextTracking.item.iid}` : void 0
|
|
62600
|
+
});
|
|
62601
|
+
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
62602
|
+
await this.atomicWriteTaskJson(dir, task);
|
|
62603
|
+
if (this.isWatching) this.taskCache.set(id, { ...task });
|
|
62604
|
+
this.emit("task:updated", task);
|
|
62605
|
+
return task;
|
|
62606
|
+
});
|
|
62607
|
+
}
|
|
62608
|
+
async linkGitLabItem(id, item) {
|
|
62609
|
+
return this.withTaskLock(id, async () => {
|
|
62610
|
+
const dir = this.taskDir(id);
|
|
62611
|
+
const task = await this.readTaskJson(dir);
|
|
62612
|
+
const previous = task.gitlabTracking ?? {};
|
|
62613
|
+
const nextTracking = { ...previous, item };
|
|
62614
|
+
if (JSON.stringify(previous) === JSON.stringify(nextTracking)) {
|
|
62615
|
+
return task;
|
|
62616
|
+
}
|
|
62617
|
+
task.gitlabTracking = nextTracking;
|
|
62618
|
+
task.log.push({
|
|
62619
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
62620
|
+
action: "GitLab item linked",
|
|
62621
|
+
outcome: `${item.host} ${item.kind} !${item.iid}`
|
|
62622
|
+
});
|
|
62623
|
+
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
62624
|
+
await this.atomicWriteTaskJson(dir, task);
|
|
62625
|
+
if (this.isWatching) this.taskCache.set(id, { ...task });
|
|
62626
|
+
this.emit("task:updated", task);
|
|
62627
|
+
return task;
|
|
62628
|
+
});
|
|
62629
|
+
}
|
|
62630
|
+
async unlinkGitLabItem(id) {
|
|
62631
|
+
return this.withTaskLock(id, async () => {
|
|
62632
|
+
const dir = this.taskDir(id);
|
|
62633
|
+
const task = await this.readTaskJson(dir);
|
|
62634
|
+
const previous = task.gitlabTracking;
|
|
62635
|
+
const previousItem = previous?.item;
|
|
62636
|
+
if (!previousItem || !previous) {
|
|
62637
|
+
return task;
|
|
62638
|
+
}
|
|
62639
|
+
task.gitlabTracking = {
|
|
62640
|
+
...previous,
|
|
62641
|
+
item: void 0,
|
|
62642
|
+
unlinkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
62643
|
+
};
|
|
62644
|
+
task.log.push({
|
|
62645
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
62646
|
+
action: "GitLab item unlinked",
|
|
62647
|
+
outcome: `${previousItem.host} ${previousItem.kind} !${previousItem.iid}`
|
|
62648
|
+
});
|
|
62649
|
+
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
62650
|
+
await this.atomicWriteTaskJson(dir, task);
|
|
62651
|
+
if (this.isWatching) this.taskCache.set(id, { ...task });
|
|
62652
|
+
this.emit("task:updated", task);
|
|
62653
|
+
return task;
|
|
62654
|
+
});
|
|
62655
|
+
}
|
|
62319
62656
|
/**
|
|
62320
62657
|
* Read historical agent log entries for a task from JSONL storage.
|
|
62321
62658
|
* Returns entries in chronological order (oldest first).
|
|
@@ -62801,6 +63138,7 @@ ${newTask.description}
|
|
|
62801
63138
|
review: entry.review,
|
|
62802
63139
|
issueInfo: entry.issueInfo,
|
|
62803
63140
|
githubTracking: entry.githubTracking,
|
|
63141
|
+
gitlabTracking: entry.gitlabTracking,
|
|
62804
63142
|
sourceIssue: entry.sourceIssue,
|
|
62805
63143
|
attachments: entry.attachments,
|
|
62806
63144
|
log: [...entry.log, { timestamp: (/* @__PURE__ */ new Date()).toISOString(), action: "Task restored from archive" }],
|
|
@@ -65495,19 +65833,20 @@ function emptyTotals() {
|
|
|
65495
65833
|
cachedTokens: 0,
|
|
65496
65834
|
cacheWriteTokens: 0,
|
|
65497
65835
|
totalTokens: 0,
|
|
65498
|
-
nTasks: 0
|
|
65836
|
+
nTasks: 0,
|
|
65837
|
+
nChatMessages: 0
|
|
65499
65838
|
};
|
|
65500
65839
|
}
|
|
65501
65840
|
function groupKeyFor(row, groupBy) {
|
|
65502
65841
|
switch (groupBy) {
|
|
65503
65842
|
case "model":
|
|
65504
|
-
return row.tokenUsageModelId ?? row.modelId;
|
|
65843
|
+
return row.contributionKind === "task" ? row.tokenUsageModelId ?? row.modelId : row.modelId;
|
|
65505
65844
|
case "provider":
|
|
65506
|
-
return row.tokenUsageModelProvider ?? row.modelProvider;
|
|
65845
|
+
return row.contributionKind === "task" ? row.tokenUsageModelProvider ?? row.modelProvider : row.modelProvider;
|
|
65507
65846
|
case "node":
|
|
65508
|
-
return row.checkoutNodeId;
|
|
65847
|
+
return row.contributionKind === "task" ? row.checkoutNodeId : null;
|
|
65509
65848
|
case "agent":
|
|
65510
|
-
return row.assignedAgentId;
|
|
65849
|
+
return row.contributionKind === "task" ? row.assignedAgentId : row.agentId;
|
|
65511
65850
|
}
|
|
65512
65851
|
}
|
|
65513
65852
|
function emptyCostAccumulator() {
|
|
@@ -65522,8 +65861,8 @@ function addRowCost(acc, row, now, pricingOverrides) {
|
|
|
65522
65861
|
cacheWriteTokens: row.cacheWriteTokens ?? 0
|
|
65523
65862
|
},
|
|
65524
65863
|
{
|
|
65525
|
-
provider: row.tokenUsageModelProvider ?? row.modelProvider,
|
|
65526
|
-
model: row.tokenUsageModelId ?? row.modelId
|
|
65864
|
+
provider: row.contributionKind === "task" ? row.tokenUsageModelProvider ?? row.modelProvider : row.modelProvider,
|
|
65865
|
+
model: row.contributionKind === "task" ? row.tokenUsageModelId ?? row.modelId : row.modelId
|
|
65527
65866
|
},
|
|
65528
65867
|
now,
|
|
65529
65868
|
pricingOverrides
|
|
@@ -65544,11 +65883,11 @@ function finalizeCost(acc) {
|
|
|
65544
65883
|
};
|
|
65545
65884
|
}
|
|
65546
65885
|
function parsePerModelRows(row) {
|
|
65547
|
-
if (!row.tokenUsagePerModel) return [];
|
|
65886
|
+
if (!row.tokenUsagePerModel) return { valid: false, rows: [] };
|
|
65548
65887
|
try {
|
|
65549
65888
|
const parsed = JSON.parse(row.tokenUsagePerModel);
|
|
65550
|
-
if (!Array.isArray(parsed) || parsed.length === 0) return [];
|
|
65551
|
-
|
|
65889
|
+
if (!Array.isArray(parsed) || parsed.length === 0) return { valid: false, rows: [] };
|
|
65890
|
+
const rows = parsed.filter((entry) => entry !== null && typeof entry === "object").map((entry) => {
|
|
65552
65891
|
const inputTokens = Number.isFinite(entry.inputTokens) ? Number(entry.inputTokens) : 0;
|
|
65553
65892
|
const outputTokens = Number.isFinite(entry.outputTokens) ? Number(entry.outputTokens) : 0;
|
|
65554
65893
|
const cachedTokens = Number.isFinite(entry.cachedTokens) ? Number(entry.cachedTokens) : 0;
|
|
@@ -65562,21 +65901,34 @@ function parsePerModelRows(row) {
|
|
|
65562
65901
|
cacheWriteTokens,
|
|
65563
65902
|
totalTokens,
|
|
65564
65903
|
tokenUsageModelProvider: typeof entry.modelProvider === "string" ? entry.modelProvider : null,
|
|
65565
|
-
tokenUsageModelId: typeof entry.modelId === "string" ? entry.modelId : null
|
|
65904
|
+
tokenUsageModelId: typeof entry.modelId === "string" ? entry.modelId : null,
|
|
65905
|
+
tokenUsageLastUsedAt: typeof entry.lastUsedAt === "string" ? entry.lastUsedAt : row.tokenUsageLastUsedAt,
|
|
65906
|
+
contributionKind: "task"
|
|
65566
65907
|
};
|
|
65567
65908
|
});
|
|
65909
|
+
return { valid: rows.length > 0, rows };
|
|
65568
65910
|
} catch {
|
|
65569
|
-
return [];
|
|
65911
|
+
return { valid: false, rows: [] };
|
|
65570
65912
|
}
|
|
65571
65913
|
}
|
|
65572
|
-
function
|
|
65914
|
+
function isWithinRange(isoTimestamp, from, to2) {
|
|
65915
|
+
return (from === void 0 || isoTimestamp >= from) && (to2 === void 0 || isoTimestamp <= to2);
|
|
65916
|
+
}
|
|
65917
|
+
function addRow(totals, row, ids) {
|
|
65573
65918
|
totals.inputTokens += row.inputTokens ?? 0;
|
|
65574
65919
|
totals.outputTokens += row.outputTokens ?? 0;
|
|
65575
65920
|
totals.cachedTokens += row.cachedTokens ?? 0;
|
|
65576
65921
|
totals.cacheWriteTokens += row.cacheWriteTokens ?? 0;
|
|
65577
65922
|
const persistedTotal = row.totalTokens;
|
|
65578
65923
|
totals.totalTokens += persistedTotal ?? (row.inputTokens ?? 0) + (row.outputTokens ?? 0) + (row.cachedTokens ?? 0) + (row.cacheWriteTokens ?? 0);
|
|
65579
|
-
|
|
65924
|
+
if (!ids || !ids.has(row.id)) {
|
|
65925
|
+
if (row.contributionKind === "task") {
|
|
65926
|
+
totals.nTasks += 1;
|
|
65927
|
+
} else {
|
|
65928
|
+
totals.nChatMessages = (totals.nChatMessages ?? 0) + 1;
|
|
65929
|
+
}
|
|
65930
|
+
ids?.add(row.id);
|
|
65931
|
+
}
|
|
65580
65932
|
}
|
|
65581
65933
|
function isoWeekBucket(isoTimestamp) {
|
|
65582
65934
|
const date5 = new Date(isoTimestamp);
|
|
@@ -65587,30 +65939,39 @@ function isoWeekBucket(isoTimestamp) {
|
|
|
65587
65939
|
const week = Math.ceil(((thursday.getTime() - yearStart.getTime()) / 864e5 + 1) / 7);
|
|
65588
65940
|
return `${thursday.getUTCFullYear()}-W${String(week).padStart(2, "0")}`;
|
|
65589
65941
|
}
|
|
65942
|
+
function contributionTimestamp(row) {
|
|
65943
|
+
return row.contributionKind === "task" ? row.tokenUsageLastUsedAt : row.createdAt;
|
|
65944
|
+
}
|
|
65590
65945
|
function bucketFor(row, granularity) {
|
|
65946
|
+
const timestamp = contributionTimestamp(row);
|
|
65591
65947
|
switch (granularity) {
|
|
65592
65948
|
case "hour":
|
|
65593
|
-
return
|
|
65949
|
+
return timestamp.slice(0, 13);
|
|
65594
65950
|
case "day":
|
|
65595
|
-
return
|
|
65951
|
+
return timestamp.slice(0, 10);
|
|
65596
65952
|
case "week":
|
|
65597
|
-
return isoWeekBucket(
|
|
65953
|
+
return isoWeekBucket(timestamp);
|
|
65598
65954
|
}
|
|
65599
65955
|
}
|
|
65600
65956
|
function aggregateTokenAnalytics(db, query = {}) {
|
|
65601
65957
|
const clauses = ["tokenUsageLastUsedAt IS NOT NULL"];
|
|
65602
65958
|
const params = [];
|
|
65959
|
+
const rangeClauses2 = [];
|
|
65603
65960
|
if (query.from !== void 0) {
|
|
65604
|
-
|
|
65961
|
+
rangeClauses2.push("tokenUsageLastUsedAt >= ?");
|
|
65605
65962
|
params.push(query.from);
|
|
65606
65963
|
}
|
|
65607
65964
|
if (query.to !== void 0) {
|
|
65608
|
-
|
|
65965
|
+
rangeClauses2.push("tokenUsageLastUsedAt <= ?");
|
|
65609
65966
|
params.push(query.to);
|
|
65610
65967
|
}
|
|
65968
|
+
if (rangeClauses2.length > 0) {
|
|
65969
|
+
clauses.push(`((${rangeClauses2.join(" AND ")}) OR tokenUsagePerModel IS NOT NULL)`);
|
|
65970
|
+
}
|
|
65611
65971
|
const where = `WHERE ${clauses.join(" AND ")}`;
|
|
65612
65972
|
const rows = db.prepare(
|
|
65613
65973
|
`SELECT
|
|
65974
|
+
id,
|
|
65614
65975
|
tokenUsageInputTokens AS inputTokens,
|
|
65615
65976
|
tokenUsageOutputTokens AS outputTokens,
|
|
65616
65977
|
tokenUsageCachedTokens AS cachedTokens,
|
|
@@ -65626,6 +65987,36 @@ function aggregateTokenAnalytics(db, query = {}) {
|
|
|
65626
65987
|
tokenUsageLastUsedAt
|
|
65627
65988
|
FROM tasks ${where}`
|
|
65628
65989
|
).all(...params);
|
|
65990
|
+
const chatClauses = [];
|
|
65991
|
+
const chatParams = [];
|
|
65992
|
+
if (query.from !== void 0) {
|
|
65993
|
+
chatClauses.push("createdAt >= ?");
|
|
65994
|
+
chatParams.push(query.from);
|
|
65995
|
+
}
|
|
65996
|
+
if (query.to !== void 0) {
|
|
65997
|
+
chatClauses.push("createdAt <= ?");
|
|
65998
|
+
chatParams.push(query.to);
|
|
65999
|
+
}
|
|
66000
|
+
const chatWhere = chatClauses.length > 0 ? `WHERE ${chatClauses.join(" AND ")}` : "";
|
|
66001
|
+
const chatRows = db.prepare(
|
|
66002
|
+
`SELECT
|
|
66003
|
+
id,
|
|
66004
|
+
sourceKind,
|
|
66005
|
+
chatSessionId,
|
|
66006
|
+
roomId,
|
|
66007
|
+
messageId,
|
|
66008
|
+
projectId,
|
|
66009
|
+
agentId,
|
|
66010
|
+
inputTokens,
|
|
66011
|
+
outputTokens,
|
|
66012
|
+
cachedTokens,
|
|
66013
|
+
cacheWriteTokens,
|
|
66014
|
+
totalTokens,
|
|
66015
|
+
modelProvider,
|
|
66016
|
+
modelId,
|
|
66017
|
+
createdAt
|
|
66018
|
+
FROM chat_token_usage ${chatWhere}`
|
|
66019
|
+
).all(...chatParams);
|
|
65629
66020
|
const totals = emptyTotals();
|
|
65630
66021
|
const totalCost = emptyCostAccumulator();
|
|
65631
66022
|
const groupMap = /* @__PURE__ */ new Map();
|
|
@@ -65636,34 +66027,70 @@ function aggregateTokenAnalytics(db, query = {}) {
|
|
|
65636
66027
|
const granularity = query.granularity;
|
|
65637
66028
|
const now = query.now;
|
|
65638
66029
|
const pricingOverrides = query.pricingOverrides;
|
|
66030
|
+
const totalContributionIds = /* @__PURE__ */ new Set();
|
|
66031
|
+
const groupContributionIds = /* @__PURE__ */ new Map();
|
|
66032
|
+
const seriesContributionIds = /* @__PURE__ */ new Map();
|
|
65639
66033
|
for (const row of rows) {
|
|
65640
|
-
|
|
65641
|
-
|
|
65642
|
-
|
|
65643
|
-
|
|
65644
|
-
|
|
65645
|
-
|
|
65646
|
-
|
|
66034
|
+
const taskRow = { ...row, contributionKind: "task" };
|
|
66035
|
+
const perModel = parsePerModelRows(row);
|
|
66036
|
+
const rowInRange = isWithinRange(row.tokenUsageLastUsedAt, query.from, query.to);
|
|
66037
|
+
const contributionRows = perModel.valid ? perModel.rows.filter((bucketRow) => isWithinRange(contributionTimestamp(bucketRow), query.from, query.to)) : rowInRange ? [taskRow] : [];
|
|
66038
|
+
for (const contributionRow of contributionRows) {
|
|
66039
|
+
addRow(totals, contributionRow, totalContributionIds);
|
|
66040
|
+
addRowCost(totalCost, contributionRow, now, pricingOverrides);
|
|
66041
|
+
if (groupBy) {
|
|
66042
|
+
const key = groupKeyFor(contributionRow, groupBy);
|
|
65647
66043
|
let group = groupMap.get(key);
|
|
65648
66044
|
if (!group) {
|
|
65649
66045
|
group = { key, ...emptyTotals(), cost: { usd: null, unavailable: false, stale: false } };
|
|
65650
66046
|
groupMap.set(key, group);
|
|
65651
66047
|
groupCostMap.set(key, emptyCostAccumulator());
|
|
66048
|
+
groupContributionIds.set(key, /* @__PURE__ */ new Set());
|
|
66049
|
+
}
|
|
66050
|
+
addRow(group, contributionRow, groupContributionIds.get(key));
|
|
66051
|
+
addRowCost(groupCostMap.get(key), contributionRow, now, pricingOverrides);
|
|
66052
|
+
}
|
|
66053
|
+
if (granularity) {
|
|
66054
|
+
const bucket = bucketFor(contributionRow, granularity);
|
|
66055
|
+
let point = seriesMap.get(bucket);
|
|
66056
|
+
if (!point) {
|
|
66057
|
+
point = { bucket, ...emptyTotals(), cost: { usd: null, unavailable: false, stale: false } };
|
|
66058
|
+
seriesMap.set(bucket, point);
|
|
66059
|
+
seriesCostMap.set(bucket, emptyCostAccumulator());
|
|
66060
|
+
seriesContributionIds.set(bucket, /* @__PURE__ */ new Set());
|
|
65652
66061
|
}
|
|
65653
|
-
addRow(
|
|
65654
|
-
addRowCost(
|
|
66062
|
+
addRow(point, contributionRow, seriesContributionIds.get(bucket));
|
|
66063
|
+
addRowCost(seriesCostMap.get(bucket), contributionRow, now, pricingOverrides);
|
|
66064
|
+
}
|
|
66065
|
+
}
|
|
66066
|
+
}
|
|
66067
|
+
for (const row of chatRows) {
|
|
66068
|
+
const contributionRow = { ...row, contributionKind: "chat" };
|
|
66069
|
+
addRow(totals, contributionRow, totalContributionIds);
|
|
66070
|
+
addRowCost(totalCost, contributionRow, now, pricingOverrides);
|
|
66071
|
+
if (groupBy) {
|
|
66072
|
+
const key = groupKeyFor(contributionRow, groupBy);
|
|
66073
|
+
let group = groupMap.get(key);
|
|
66074
|
+
if (!group) {
|
|
66075
|
+
group = { key, ...emptyTotals(), cost: { usd: null, unavailable: false, stale: false } };
|
|
66076
|
+
groupMap.set(key, group);
|
|
66077
|
+
groupCostMap.set(key, emptyCostAccumulator());
|
|
66078
|
+
groupContributionIds.set(key, /* @__PURE__ */ new Set());
|
|
65655
66079
|
}
|
|
66080
|
+
addRow(group, contributionRow, groupContributionIds.get(key));
|
|
66081
|
+
addRowCost(groupCostMap.get(key), contributionRow, now, pricingOverrides);
|
|
65656
66082
|
}
|
|
65657
66083
|
if (granularity) {
|
|
65658
|
-
const bucket = bucketFor(
|
|
66084
|
+
const bucket = bucketFor(contributionRow, granularity);
|
|
65659
66085
|
let point = seriesMap.get(bucket);
|
|
65660
66086
|
if (!point) {
|
|
65661
66087
|
point = { bucket, ...emptyTotals(), cost: { usd: null, unavailable: false, stale: false } };
|
|
65662
66088
|
seriesMap.set(bucket, point);
|
|
65663
66089
|
seriesCostMap.set(bucket, emptyCostAccumulator());
|
|
66090
|
+
seriesContributionIds.set(bucket, /* @__PURE__ */ new Set());
|
|
65664
66091
|
}
|
|
65665
|
-
addRow(point,
|
|
65666
|
-
addRowCost(seriesCostMap.get(bucket),
|
|
66092
|
+
addRow(point, contributionRow, seriesContributionIds.get(bucket));
|
|
66093
|
+
addRowCost(seriesCostMap.get(bucket), contributionRow, now, pricingOverrides);
|
|
65667
66094
|
}
|
|
65668
66095
|
}
|
|
65669
66096
|
for (const [key, group] of groupMap) {
|
|
@@ -66939,6 +67366,113 @@ var init_github_issue_analytics = __esm({
|
|
|
66939
67366
|
}
|
|
66940
67367
|
});
|
|
66941
67368
|
|
|
67369
|
+
// ../core/src/gitlab-issue-analytics.ts
|
|
67370
|
+
function isInRange2(iso, query) {
|
|
67371
|
+
const t = Date.parse(iso);
|
|
67372
|
+
if (!Number.isFinite(t)) return false;
|
|
67373
|
+
if (query.from !== void 0 && t < Date.parse(query.from)) return false;
|
|
67374
|
+
if (query.to !== void 0 && t > Date.parse(query.to)) return false;
|
|
67375
|
+
return true;
|
|
67376
|
+
}
|
|
67377
|
+
function dayKey2(iso) {
|
|
67378
|
+
const t = Date.parse(iso);
|
|
67379
|
+
if (!Number.isFinite(t)) return null;
|
|
67380
|
+
return new Date(t).toISOString().slice(0, 10);
|
|
67381
|
+
}
|
|
67382
|
+
function projectFromItem(item) {
|
|
67383
|
+
const projectPath = typeof item.projectPath === "string" ? item.projectPath.trim() : "";
|
|
67384
|
+
if (projectPath) return projectPath;
|
|
67385
|
+
const groupPath = typeof item.groupPath === "string" ? item.groupPath.trim() : "";
|
|
67386
|
+
if (groupPath) return groupPath;
|
|
67387
|
+
if (typeof item.projectId === "number" && Number.isFinite(item.projectId)) return String(item.projectId);
|
|
67388
|
+
return "(unknown)";
|
|
67389
|
+
}
|
|
67390
|
+
function addDaily2(daily, date5, kind) {
|
|
67391
|
+
const current = daily.get(date5) ?? { filed: 0, fixed: 0 };
|
|
67392
|
+
current[kind] += 1;
|
|
67393
|
+
daily.set(date5, current);
|
|
67394
|
+
}
|
|
67395
|
+
function addProject(byProject, project, kind) {
|
|
67396
|
+
const current = byProject.get(project) ?? { filed: 0, fixed: 0 };
|
|
67397
|
+
current[kind] += 1;
|
|
67398
|
+
byProject.set(project, current);
|
|
67399
|
+
}
|
|
67400
|
+
function aggregateGitlabIssueAnalytics(db, query = {}) {
|
|
67401
|
+
const daily = /* @__PURE__ */ new Map();
|
|
67402
|
+
const byProject = /* @__PURE__ */ new Map();
|
|
67403
|
+
const filedRows = db.prepare(
|
|
67404
|
+
"SELECT gitlabTracking FROM tasks WHERE gitlabTracking IS NOT NULL AND gitlabTracking NOT IN ('', '{}')"
|
|
67405
|
+
).all();
|
|
67406
|
+
let filed = 0;
|
|
67407
|
+
for (const row of filedRows) {
|
|
67408
|
+
if (!row.gitlabTracking) continue;
|
|
67409
|
+
let parsed;
|
|
67410
|
+
try {
|
|
67411
|
+
parsed = JSON.parse(row.gitlabTracking);
|
|
67412
|
+
} catch {
|
|
67413
|
+
continue;
|
|
67414
|
+
}
|
|
67415
|
+
const item = parsed.item;
|
|
67416
|
+
if (!item || typeof item.iid !== "number" || !Number.isFinite(item.iid)) continue;
|
|
67417
|
+
const createdAt = typeof item.createdAt === "string" ? item.createdAt : void 0;
|
|
67418
|
+
const hasUsableDate = createdAt !== void 0 && dayKey2(createdAt) !== null;
|
|
67419
|
+
if (hasUsableDate && !isInRange2(createdAt, query)) continue;
|
|
67420
|
+
filed += 1;
|
|
67421
|
+
const project = projectFromItem(item);
|
|
67422
|
+
addProject(byProject, project, "filed");
|
|
67423
|
+
if (hasUsableDate && createdAt !== void 0) {
|
|
67424
|
+
const day = dayKey2(createdAt);
|
|
67425
|
+
if (day !== null) addDaily2(daily, day, "filed");
|
|
67426
|
+
}
|
|
67427
|
+
}
|
|
67428
|
+
const fixedRows = db.prepare(
|
|
67429
|
+
`SELECT id, title, sourceIssueRepository, sourceIssueNumber, sourceIssueUrl, sourceIssueClosedAt, updatedAt FROM tasks WHERE sourceIssueProvider = 'gitlab' AND "column" = 'done'`
|
|
67430
|
+
).all();
|
|
67431
|
+
let fixed = 0;
|
|
67432
|
+
const resolved = [];
|
|
67433
|
+
for (const row of fixedRows) {
|
|
67434
|
+
const hasExactResolvedAt = row.sourceIssueClosedAt !== null;
|
|
67435
|
+
const fixedDate = row.sourceIssueClosedAt ?? row.updatedAt;
|
|
67436
|
+
if (fixedDate === null || !isInRange2(fixedDate, query)) continue;
|
|
67437
|
+
fixed += 1;
|
|
67438
|
+
const project = row.sourceIssueRepository?.trim() || "(unknown)";
|
|
67439
|
+
addProject(byProject, project, "fixed");
|
|
67440
|
+
const day = dayKey2(fixedDate);
|
|
67441
|
+
if (day !== null) addDaily2(daily, day, "fixed");
|
|
67442
|
+
resolved.push({
|
|
67443
|
+
taskId: row.id,
|
|
67444
|
+
taskTitle: row.title ?? "",
|
|
67445
|
+
project,
|
|
67446
|
+
issueNumber: typeof row.sourceIssueNumber === "number" ? row.sourceIssueNumber : null,
|
|
67447
|
+
url: row.sourceIssueUrl?.trim() || null,
|
|
67448
|
+
resolvedAt: fixedDate,
|
|
67449
|
+
resolvedAtExact: hasExactResolvedAt
|
|
67450
|
+
});
|
|
67451
|
+
}
|
|
67452
|
+
resolved.sort((a, b2) => {
|
|
67453
|
+
const byDate = Date.parse(b2.resolvedAt) - Date.parse(a.resolvedAt);
|
|
67454
|
+
return byDate !== 0 ? byDate : a.taskId.localeCompare(b2.taskId);
|
|
67455
|
+
});
|
|
67456
|
+
return {
|
|
67457
|
+
from: query.from ?? null,
|
|
67458
|
+
to: query.to ?? null,
|
|
67459
|
+
filed,
|
|
67460
|
+
fixed,
|
|
67461
|
+
net: filed - fixed,
|
|
67462
|
+
daily: [...daily.entries()].map(([date5, counts]) => ({ date: date5, filed: counts.filed, fixed: counts.fixed })).sort((a, b2) => a.date.localeCompare(b2.date)),
|
|
67463
|
+
byProject: [...byProject.entries()].map(([project, counts]) => ({ project, filed: counts.filed, fixed: counts.fixed })).sort((a, b2) => {
|
|
67464
|
+
const total = b2.filed + b2.fixed - (a.filed + a.fixed);
|
|
67465
|
+
return total !== 0 ? total : a.project.localeCompare(b2.project);
|
|
67466
|
+
}),
|
|
67467
|
+
resolved
|
|
67468
|
+
};
|
|
67469
|
+
}
|
|
67470
|
+
var init_gitlab_issue_analytics = __esm({
|
|
67471
|
+
"../core/src/gitlab-issue-analytics.ts"() {
|
|
67472
|
+
"use strict";
|
|
67473
|
+
}
|
|
67474
|
+
});
|
|
67475
|
+
|
|
66942
67476
|
// ../core/src/command-center-live.ts
|
|
66943
67477
|
function composeLiveSnapshot(db, now) {
|
|
66944
67478
|
const capturedAt = new Date(now ?? Date.now()).toISOString();
|
|
@@ -67306,7 +67840,7 @@ var init_daemon_token = __esm({
|
|
|
67306
67840
|
// ../core/src/pi-extensions.ts
|
|
67307
67841
|
import { existsSync as existsSync17, mkdirSync as mkdirSync7, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync5, writeFileSync as writeFileSync2 } from "node:fs";
|
|
67308
67842
|
import { homedir as homedir3 } from "node:os";
|
|
67309
|
-
import { basename as basename8, dirname as
|
|
67843
|
+
import { basename as basename8, dirname as dirname9, isAbsolute as isAbsolute5, join as join21, relative as relative2, resolve as resolve10, sep as sep4, win32 } from "node:path";
|
|
67310
67844
|
import { createRequire as createRequire3 } from "node:module";
|
|
67311
67845
|
function getSpawnSync() {
|
|
67312
67846
|
if (!didLoadSpawnSync) {
|
|
@@ -67382,7 +67916,7 @@ function getProjectRootFromGitLinkedWorktree(cwd) {
|
|
|
67382
67916
|
if (!resolvedCommonDir || !resolvedGitDir || resolvedCommonDir === resolvedGitDir) {
|
|
67383
67917
|
return null;
|
|
67384
67918
|
}
|
|
67385
|
-
const parentRoot = resolvedCommonDir.endsWith(`${sep4}.git`) ?
|
|
67919
|
+
const parentRoot = resolvedCommonDir.endsWith(`${sep4}.git`) ? dirname9(resolvedCommonDir) : resolvedCommonDir;
|
|
67386
67920
|
return existsSync17(join21(parentRoot, ".fusion")) ? parentRoot : null;
|
|
67387
67921
|
}
|
|
67388
67922
|
function resolvePiExtensionProjectRoot(cwd) {
|
|
@@ -69097,7 +69631,7 @@ var init_plugin_security_scan = __esm({
|
|
|
69097
69631
|
});
|
|
69098
69632
|
|
|
69099
69633
|
// ../core/src/plugin-loader.ts
|
|
69100
|
-
import { basename as basename9, dirname as
|
|
69634
|
+
import { basename as basename9, dirname as dirname10, extname, isAbsolute as isAbsolute6, join as join23, resolve as resolve11 } from "node:path";
|
|
69101
69635
|
import { existsSync as existsSync19, readdirSync as readdirSync3, statSync as statSync6 } from "node:fs";
|
|
69102
69636
|
import { copyFile, readFile as readFile10, stat as stat4 } from "node:fs/promises";
|
|
69103
69637
|
import { pathToFileURL } from "node:url";
|
|
@@ -69377,7 +69911,7 @@ var init_plugin_loader = __esm({
|
|
|
69377
69911
|
moduleImportVersion += 1;
|
|
69378
69912
|
const ext = extname(path6);
|
|
69379
69913
|
const baseName = basename9(path6, ext);
|
|
69380
|
-
const reloadedPath = resolve11(
|
|
69914
|
+
const reloadedPath = resolve11(dirname10(path6), `.${baseName}.reload-${moduleImportVersion}${ext}`);
|
|
69381
69915
|
await copyFile(path6, reloadedPath);
|
|
69382
69916
|
mod = await import(pathToFileURL(reloadedPath).href);
|
|
69383
69917
|
} else {
|
|
@@ -69869,10 +70403,10 @@ var init_plugin_loader = __esm({
|
|
|
69869
70403
|
}
|
|
69870
70404
|
} catch {
|
|
69871
70405
|
}
|
|
69872
|
-
const entryDir =
|
|
70406
|
+
const entryDir = dirname10(pluginEntryPath);
|
|
69873
70407
|
candidates.add(join23(entryDir, "manifest.json"));
|
|
69874
70408
|
if (PLUGIN_MANIFEST_PARENT_DIR_NAMES.has(basename9(entryDir))) {
|
|
69875
|
-
candidates.add(join23(
|
|
70409
|
+
candidates.add(join23(dirname10(entryDir), "manifest.json"));
|
|
69876
70410
|
}
|
|
69877
70411
|
for (const candidate of candidates) {
|
|
69878
70412
|
try {
|
|
@@ -70682,7 +71216,7 @@ var init_backup = __esm({
|
|
|
70682
71216
|
// ../core/src/memory-backup.ts
|
|
70683
71217
|
import { cp as cp2, mkdir as mkdir9, readdir as readdir7, readFile as readFile11, rename as rename6, rm as rm2, stat as stat6, writeFile as writeFile7 } from "node:fs/promises";
|
|
70684
71218
|
import { existsSync as existsSync21 } from "node:fs";
|
|
70685
|
-
import { dirname as
|
|
71219
|
+
import { dirname as dirname11, join as join25, relative as relative3, resolve as resolve12, sep as sep5 } from "node:path";
|
|
70686
71220
|
function createMemoryBackupManager(fusionDir, settings) {
|
|
70687
71221
|
return new MemoryBackupManager(fusionDir, {
|
|
70688
71222
|
backupDir: sanitizeMemoryBackupDir(fusionDir, canonicalizeMemoryBackupDir(settings?.memoryBackupDir)),
|
|
@@ -70964,7 +71498,7 @@ var init_memory_backup = __esm({
|
|
|
70964
71498
|
for (const file2 of files) {
|
|
70965
71499
|
const rel = relative3(sourceDir, file2);
|
|
70966
71500
|
const destFile = join25(destinationDir, rel);
|
|
70967
|
-
await mkdir9(
|
|
71501
|
+
await mkdir9(dirname11(destFile), { recursive: true });
|
|
70968
71502
|
if (existsSync21(destFile) && !overwrite) {
|
|
70969
71503
|
const [srcBuf, dstBuf] = await Promise.all([readFile11(file2), readFile11(destFile)]);
|
|
70970
71504
|
if (!srcBuf.equals(dstBuf)) {
|
|
@@ -72181,7 +72715,7 @@ var init_docker_provisioning = __esm({
|
|
|
72181
72715
|
// ../core/src/memory-insights.ts
|
|
72182
72716
|
import { readFile as readFile14, writeFile as writeFile9, mkdir as mkdir10, unlink as unlink5 } from "node:fs/promises";
|
|
72183
72717
|
import { existsSync as existsSync22 } from "node:fs";
|
|
72184
|
-
import { dirname as
|
|
72718
|
+
import { dirname as dirname12, join as join26 } from "node:path";
|
|
72185
72719
|
async function readWorkingMemory(rootDir) {
|
|
72186
72720
|
const filePath = join26(rootDir, MEMORY_WORKING_PATH);
|
|
72187
72721
|
if (!existsSync22(filePath)) {
|
|
@@ -72196,7 +72730,7 @@ async function migrateLegacyArtifactIfNeeded(rootDir, canonicalPath, legacyPath)
|
|
|
72196
72730
|
return;
|
|
72197
72731
|
}
|
|
72198
72732
|
const content = await readFile14(legacyFilePath, "utf-8");
|
|
72199
|
-
const canonicalDir =
|
|
72733
|
+
const canonicalDir = dirname12(canonicalFilePath);
|
|
72200
72734
|
if (!existsSync22(canonicalDir)) {
|
|
72201
72735
|
await mkdir10(canonicalDir, { recursive: true });
|
|
72202
72736
|
}
|
|
@@ -72226,7 +72760,7 @@ async function readInsightsMemory(rootDir) {
|
|
|
72226
72760
|
}
|
|
72227
72761
|
async function writeInsightsMemory(rootDir, content) {
|
|
72228
72762
|
const filePath = join26(rootDir, MEMORY_INSIGHTS_PATH);
|
|
72229
|
-
const dir =
|
|
72763
|
+
const dir = dirname12(filePath);
|
|
72230
72764
|
if (!existsSync22(dir)) {
|
|
72231
72765
|
await mkdir10(dir, { recursive: true });
|
|
72232
72766
|
}
|
|
@@ -72235,7 +72769,7 @@ async function writeInsightsMemory(rootDir, content) {
|
|
|
72235
72769
|
}
|
|
72236
72770
|
async function writeWorkingMemory(rootDir, content) {
|
|
72237
72771
|
const filePath = join26(rootDir, MEMORY_WORKING_PATH);
|
|
72238
|
-
const dir =
|
|
72772
|
+
const dir = dirname12(filePath);
|
|
72239
72773
|
if (!existsSync22(dir)) {
|
|
72240
72774
|
await mkdir10(dir, { recursive: true });
|
|
72241
72775
|
}
|
|
@@ -72251,7 +72785,7 @@ async function readMemoryAudit(rootDir) {
|
|
|
72251
72785
|
}
|
|
72252
72786
|
async function writeMemoryAudit(rootDir, content) {
|
|
72253
72787
|
const filePath = join26(rootDir, MEMORY_AUDIT_PATH);
|
|
72254
|
-
const dir =
|
|
72788
|
+
const dir = dirname12(filePath);
|
|
72255
72789
|
if (!existsSync22(dir)) {
|
|
72256
72790
|
await mkdir10(dir, { recursive: true });
|
|
72257
72791
|
}
|
|
@@ -72280,7 +72814,7 @@ async function readMemoryAuditState(rootDir) {
|
|
|
72280
72814
|
}
|
|
72281
72815
|
async function writeMemoryAuditState(rootDir, state) {
|
|
72282
72816
|
const filePath = join26(rootDir, MEMORY_AUDIT_STATE_PATH);
|
|
72283
|
-
const dir =
|
|
72817
|
+
const dir = dirname12(filePath);
|
|
72284
72818
|
if (!existsSync22(dir)) {
|
|
72285
72819
|
await mkdir10(dir, { recursive: true });
|
|
72286
72820
|
}
|
|
@@ -87517,6 +88051,25 @@ var init_chat_store = __esm({
|
|
|
87517
88051
|
createdAt: row.createdAt
|
|
87518
88052
|
};
|
|
87519
88053
|
}
|
|
88054
|
+
rowToTokenUsage(row) {
|
|
88055
|
+
return {
|
|
88056
|
+
id: row.id,
|
|
88057
|
+
sourceKind: row.sourceKind,
|
|
88058
|
+
chatSessionId: row.chatSessionId ?? null,
|
|
88059
|
+
roomId: row.roomId ?? null,
|
|
88060
|
+
messageId: row.messageId ?? null,
|
|
88061
|
+
projectId: row.projectId ?? null,
|
|
88062
|
+
agentId: row.agentId ?? null,
|
|
88063
|
+
modelProvider: row.modelProvider ?? null,
|
|
88064
|
+
modelId: row.modelId ?? null,
|
|
88065
|
+
inputTokens: row.inputTokens ?? 0,
|
|
88066
|
+
outputTokens: row.outputTokens ?? 0,
|
|
88067
|
+
cachedTokens: row.cachedTokens ?? 0,
|
|
88068
|
+
cacheWriteTokens: row.cacheWriteTokens ?? 0,
|
|
88069
|
+
totalTokens: row.totalTokens ?? 0,
|
|
88070
|
+
createdAt: row.createdAt
|
|
88071
|
+
};
|
|
88072
|
+
}
|
|
87520
88073
|
normalizeRoomName(name) {
|
|
87521
88074
|
return name.trim().replace(/^#+/, "").trim();
|
|
87522
88075
|
}
|
|
@@ -88265,6 +88818,62 @@ var init_chat_store = __esm({
|
|
|
88265
88818
|
this.emit("chat:room:message:updated", updated);
|
|
88266
88819
|
return updated;
|
|
88267
88820
|
}
|
|
88821
|
+
recordTokenUsage(input) {
|
|
88822
|
+
const inputTokens = Math.max(0, Math.trunc(input.inputTokens));
|
|
88823
|
+
const outputTokens = Math.max(0, Math.trunc(input.outputTokens));
|
|
88824
|
+
const cachedTokens = Math.max(0, Math.trunc(input.cachedTokens));
|
|
88825
|
+
const cacheWriteTokens = Math.max(0, Math.trunc(input.cacheWriteTokens));
|
|
88826
|
+
const totalTokens = Math.max(0, Math.trunc(input.totalTokens ?? inputTokens + outputTokens + cachedTokens + cacheWriteTokens));
|
|
88827
|
+
if (inputTokens === 0 && outputTokens === 0 && cachedTokens === 0 && cacheWriteTokens === 0 && totalTokens === 0) {
|
|
88828
|
+
return void 0;
|
|
88829
|
+
}
|
|
88830
|
+
const record2 = {
|
|
88831
|
+
id: `chat-tokens-${randomUUID19().slice(0, 12)}`,
|
|
88832
|
+
sourceKind: input.sourceKind,
|
|
88833
|
+
chatSessionId: input.chatSessionId ?? null,
|
|
88834
|
+
roomId: input.roomId ?? null,
|
|
88835
|
+
messageId: input.messageId ?? null,
|
|
88836
|
+
projectId: input.projectId ?? null,
|
|
88837
|
+
agentId: input.agentId ?? null,
|
|
88838
|
+
modelProvider: input.modelProvider ?? null,
|
|
88839
|
+
modelId: input.modelId ?? null,
|
|
88840
|
+
inputTokens,
|
|
88841
|
+
outputTokens,
|
|
88842
|
+
cachedTokens,
|
|
88843
|
+
cacheWriteTokens,
|
|
88844
|
+
totalTokens,
|
|
88845
|
+
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
88846
|
+
};
|
|
88847
|
+
this.db.prepare(`
|
|
88848
|
+
INSERT INTO chat_token_usage (
|
|
88849
|
+
id, sourceKind, chatSessionId, roomId, messageId, projectId, agentId,
|
|
88850
|
+
modelProvider, modelId, inputTokens, outputTokens, cachedTokens,
|
|
88851
|
+
cacheWriteTokens, totalTokens, createdAt
|
|
88852
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
88853
|
+
`).run(
|
|
88854
|
+
record2.id,
|
|
88855
|
+
record2.sourceKind,
|
|
88856
|
+
record2.chatSessionId,
|
|
88857
|
+
record2.roomId,
|
|
88858
|
+
record2.messageId,
|
|
88859
|
+
record2.projectId,
|
|
88860
|
+
record2.agentId,
|
|
88861
|
+
record2.modelProvider,
|
|
88862
|
+
record2.modelId,
|
|
88863
|
+
record2.inputTokens,
|
|
88864
|
+
record2.outputTokens,
|
|
88865
|
+
record2.cachedTokens,
|
|
88866
|
+
record2.cacheWriteTokens,
|
|
88867
|
+
record2.totalTokens,
|
|
88868
|
+
record2.createdAt
|
|
88869
|
+
);
|
|
88870
|
+
this.db.bumpLastModified();
|
|
88871
|
+
return record2;
|
|
88872
|
+
}
|
|
88873
|
+
listTokenUsage() {
|
|
88874
|
+
const rows = this.db.prepare("SELECT * FROM chat_token_usage ORDER BY createdAt ASC").all();
|
|
88875
|
+
return rows.map((row) => this.rowToTokenUsage(row));
|
|
88876
|
+
}
|
|
88268
88877
|
};
|
|
88269
88878
|
}
|
|
88270
88879
|
});
|
|
@@ -88947,6 +89556,8 @@ __export(src_exports, {
|
|
|
88947
89556
|
DEFAULT_DEPENDENCY_BLOCKED_TODO_MIN_COUNT: () => DEFAULT_DEPENDENCY_BLOCKED_TODO_MIN_COUNT,
|
|
88948
89557
|
DEFAULT_DEPENDENCY_BLOCKED_TODO_STALE_MS: () => DEFAULT_DEPENDENCY_BLOCKED_TODO_STALE_MS,
|
|
88949
89558
|
DEFAULT_EXECUTION_MODE: () => DEFAULT_EXECUTION_MODE,
|
|
89559
|
+
DEFAULT_GITLAB_API_BASE_URL: () => DEFAULT_GITLAB_API_BASE_URL,
|
|
89560
|
+
DEFAULT_GITLAB_INSTANCE_URL: () => DEFAULT_GITLAB_INSTANCE_URL,
|
|
88950
89561
|
DEFAULT_GLOBAL_SETTINGS: () => DEFAULT_GLOBAL_SETTINGS,
|
|
88951
89562
|
DEFAULT_HEARTBEAT_PROCEDURE_PATH: () => DEFAULT_HEARTBEAT_PROCEDURE_PATH,
|
|
88952
89563
|
DEFAULT_INSIGHT_SCHEDULE: () => DEFAULT_INSIGHT_SCHEDULE,
|
|
@@ -89099,6 +89710,10 @@ __export(src_exports, {
|
|
|
89099
89710
|
OTEL_METRIC_PREFIX: () => OTEL_METRIC_PREFIX,
|
|
89100
89711
|
OccupiedColumnsError: () => OccupiedColumnsError,
|
|
89101
89712
|
PERMANENT_AGENT_ACTION_CATEGORIES: () => PERMANENT_AGENT_ACTION_CATEGORIES,
|
|
89713
|
+
PLANNING_DEEPEN_CHECKPOINT_ID: () => PLANNING_DEEPEN_CHECKPOINT_ID,
|
|
89714
|
+
PLANNING_DEEPEN_CHECKPOINT_QUESTION: () => PLANNING_DEEPEN_CHECKPOINT_QUESTION,
|
|
89715
|
+
PLANNING_DEEPEN_PROCEED_OPTION_ID: () => PLANNING_DEEPEN_PROCEED_OPTION_ID,
|
|
89716
|
+
PLANNING_DEEPEN_PROCEED_RESPONSE_KEY: () => PLANNING_DEEPEN_PROCEED_RESPONSE_KEY,
|
|
89102
89717
|
PLAN_REVIEW_GROUP_ID: () => PLAN_REVIEW_GROUP_ID,
|
|
89103
89718
|
PLAN_REVIEW_MAX_REVISIONS_SETTING_ID: () => PLAN_REVIEW_MAX_REVISIONS_SETTING_ID,
|
|
89104
89719
|
PLUGIN_TRAIT_ALLOWED_HOOK_POINTS: () => PLUGIN_TRAIT_ALLOWED_HOOK_POINTS,
|
|
@@ -89227,6 +89842,7 @@ __export(src_exports, {
|
|
|
89227
89842
|
aggregateActivityAnalytics: () => aggregateActivityAnalytics,
|
|
89228
89843
|
aggregateAgentTokenUsage: () => aggregateAgentTokenUsage,
|
|
89229
89844
|
aggregateGithubIssueAnalytics: () => aggregateGithubIssueAnalytics,
|
|
89845
|
+
aggregateGitlabIssueAnalytics: () => aggregateGitlabIssueAnalytics,
|
|
89230
89846
|
aggregateMonitorMetrics: () => aggregateMonitorMetrics,
|
|
89231
89847
|
aggregatePluginActivations: () => aggregatePluginActivations,
|
|
89232
89848
|
aggregateProductivityAnalytics: () => aggregateProductivityAnalytics,
|
|
@@ -89401,6 +90017,7 @@ __export(src_exports, {
|
|
|
89401
90017
|
findWorkflowColumn: () => findWorkflowColumn,
|
|
89402
90018
|
formatCurrentTaskLine: () => formatCurrentTaskLine,
|
|
89403
90019
|
formatDistributedTaskId: () => formatDistributedTaskId,
|
|
90020
|
+
formatGitLabTrackedItemRef: () => formatGitLabTrackedItemRef,
|
|
89404
90021
|
formatPiExtensionSource: () => formatPiExtensionSource,
|
|
89405
90022
|
formatRoleMismatchReason: () => formatRoleMismatchReason,
|
|
89406
90023
|
formatTaskListText: () => formatTaskListText,
|
|
@@ -89503,6 +90120,7 @@ __export(src_exports, {
|
|
|
89503
90120
|
isFinalizeRecord: () => isFinalizeRecord,
|
|
89504
90121
|
isGhAuthenticated: () => isGhAuthenticated,
|
|
89505
90122
|
isGhAvailable: () => isGhAvailable,
|
|
90123
|
+
isGitLabTrackingStale: () => isGitLabTrackingStale,
|
|
89506
90124
|
isGlobalSettingsKey: () => isGlobalSettingsKey,
|
|
89507
90125
|
isHookRecord: () => isHookRecord,
|
|
89508
90126
|
isImplementationTask: () => isImplementationTask,
|
|
@@ -89690,6 +90308,8 @@ __export(src_exports, {
|
|
|
89690
90308
|
resolveEntryPointBranchAssignment: () => resolveEntryPointBranchAssignment,
|
|
89691
90309
|
resolveEvalSettings: () => resolveEvalSettings,
|
|
89692
90310
|
resolveExecutionSettingsModel: () => resolveExecutionSettingsModel,
|
|
90311
|
+
resolveGitlabConfig: () => resolveGitlabConfig,
|
|
90312
|
+
resolveGitlabEnabled: () => resolveGitlabEnabled,
|
|
89693
90313
|
resolveGlobalDir: () => resolveGlobalDir,
|
|
89694
90314
|
resolveGlobalDirForHome: () => resolveGlobalDirForHome,
|
|
89695
90315
|
resolveLocalNodeId: () => resolveLocalNodeId,
|
|
@@ -89865,6 +90485,7 @@ var init_src = __esm({
|
|
|
89865
90485
|
init_redact_secrets();
|
|
89866
90486
|
init_plan_approval();
|
|
89867
90487
|
init_near_duplicate_canonical();
|
|
90488
|
+
init_gitlab_tracking();
|
|
89868
90489
|
init_frontend_ux_policy();
|
|
89869
90490
|
init_file_scope_classification();
|
|
89870
90491
|
init_task_list_format();
|
|
@@ -89954,6 +90575,7 @@ var init_src = __esm({
|
|
|
89954
90575
|
init_team_analytics();
|
|
89955
90576
|
init_workflow_analytics();
|
|
89956
90577
|
init_github_issue_analytics();
|
|
90578
|
+
init_gitlab_issue_analytics();
|
|
89957
90579
|
init_activity_analytics();
|
|
89958
90580
|
init_command_center_live();
|
|
89959
90581
|
init_otel_metrics();
|
|
@@ -90616,7 +91238,10 @@ var init_gating_classifications = __esm({
|
|
|
90616
91238
|
...PERMANENT_AND_ACTION_TASK_AGENT_TOOLS,
|
|
90617
91239
|
"fn_delegate_task",
|
|
90618
91240
|
"fn_task_import_github",
|
|
90619
|
-
"fn_task_import_github_issue"
|
|
91241
|
+
"fn_task_import_github_issue",
|
|
91242
|
+
"fn_task_import_gitlab_project_issues",
|
|
91243
|
+
"fn_task_import_gitlab_group_issues",
|
|
91244
|
+
"fn_task_import_gitlab_merge_requests"
|
|
90620
91245
|
];
|
|
90621
91246
|
ACTION_GATE_SHARED_TASK_AGENT_TOOLS = SHARED_TASK_AGENT_TOOLS.filter(
|
|
90622
91247
|
(tool) => !PROVISIONING_TOOLS.includes(tool)
|
|
@@ -90694,6 +91319,12 @@ var init_gating_classifications = __esm({
|
|
|
90694
91319
|
"fn_delegate_task",
|
|
90695
91320
|
"fn_task_import_github",
|
|
90696
91321
|
"fn_task_import_github_issue",
|
|
91322
|
+
"fn_task_import_gitlab_project_issues",
|
|
91323
|
+
"fn_task_import_gitlab_group_issues",
|
|
91324
|
+
"fn_task_import_gitlab_merge_requests",
|
|
91325
|
+
"fn_task_browse_gitlab_project_issues",
|
|
91326
|
+
"fn_task_browse_gitlab_group_issues",
|
|
91327
|
+
"fn_task_browse_gitlab_merge_requests",
|
|
90697
91328
|
"fn_research_list",
|
|
90698
91329
|
"fn_research_get",
|
|
90699
91330
|
"fn_insight_list",
|
|
@@ -90962,7 +91593,7 @@ var init_agent_action_gate = __esm({
|
|
|
90962
91593
|
import { existsSync as existsSync25, readFileSync as readFileSync8 } from "node:fs";
|
|
90963
91594
|
import { homedir as homedir5 } from "node:os";
|
|
90964
91595
|
import { join as join30 } from "node:path";
|
|
90965
|
-
import { AuthStorage } from "@earendil-works/pi-coding-agent";
|
|
91596
|
+
import { AuthStorage, ModelRegistry } from "@earendil-works/pi-coding-agent";
|
|
90966
91597
|
import { getOAuthProvider } from "@earendil-works/pi-ai/oauth";
|
|
90967
91598
|
function getHomeDir5() {
|
|
90968
91599
|
return process.env.HOME || process.env.USERPROFILE || homedir5();
|
|
@@ -91152,6 +91783,9 @@ function readModelsJsonApiKeys(home = getHomeDir5()) {
|
|
|
91152
91783
|
}
|
|
91153
91784
|
return apiKeys;
|
|
91154
91785
|
}
|
|
91786
|
+
function createFusionModelRegistry(authStorage, home) {
|
|
91787
|
+
return ModelRegistry.create(authStorage, getModelRegistryModelsPath(home));
|
|
91788
|
+
}
|
|
91155
91789
|
function createFusionAuthStorage() {
|
|
91156
91790
|
const primary = AuthStorage.create(getFusionAuthPath());
|
|
91157
91791
|
let supplementalCredentials = readSupplementalCredentials();
|
|
@@ -92892,7 +93526,7 @@ var init_github_provider = __esm({
|
|
|
92892
93526
|
|
|
92893
93527
|
// ../engine/src/skill-resolver.ts
|
|
92894
93528
|
import { existsSync as existsSync26, readFileSync as readFileSync9 } from "node:fs";
|
|
92895
|
-
import { dirname as
|
|
93529
|
+
import { dirname as dirname13, join as join31, resolve as resolve15 } from "node:path";
|
|
92896
93530
|
function resolveProjectRoot(cwd) {
|
|
92897
93531
|
const worktreeProjectRoot = getProjectRootFromWorktree(cwd);
|
|
92898
93532
|
if (worktreeProjectRoot && existsSync26(join31(worktreeProjectRoot, ".fusion"))) {
|
|
@@ -92903,7 +93537,7 @@ function resolveProjectRoot(cwd) {
|
|
|
92903
93537
|
if (existsSync26(join31(current, ".fusion"))) {
|
|
92904
93538
|
return current;
|
|
92905
93539
|
}
|
|
92906
|
-
const parent =
|
|
93540
|
+
const parent = dirname13(current);
|
|
92907
93541
|
if (parent === current) {
|
|
92908
93542
|
return resolve15(cwd);
|
|
92909
93543
|
}
|
|
@@ -119000,7 +119634,7 @@ import { existsSync as existsSync27, readFileSync as readFileSync11, realpathSyn
|
|
|
119000
119634
|
import { exec as exec2, execFile as execFile4 } from "node:child_process";
|
|
119001
119635
|
import { promisify as promisify4 } from "node:util";
|
|
119002
119636
|
import { createRequire as createRequire4 } from "node:module";
|
|
119003
|
-
import { basename as basename10, dirname as
|
|
119637
|
+
import { basename as basename10, dirname as dirname14, join as join33, relative as relative4, isAbsolute as isAbsolute8, resolve as resolve16 } from "node:path";
|
|
119004
119638
|
import {
|
|
119005
119639
|
createAgentSession,
|
|
119006
119640
|
createBashTool,
|
|
@@ -119016,7 +119650,7 @@ import {
|
|
|
119016
119650
|
DefaultResourceLoader,
|
|
119017
119651
|
DefaultPackageManager,
|
|
119018
119652
|
discoverAndLoadExtensions,
|
|
119019
|
-
ModelRegistry,
|
|
119653
|
+
ModelRegistry as ModelRegistry2,
|
|
119020
119654
|
SessionManager,
|
|
119021
119655
|
SettingsManager
|
|
119022
119656
|
} from "@earendil-works/pi-coding-agent";
|
|
@@ -119794,7 +120428,7 @@ function siblingAgentDir(agentDir, siblingRoot) {
|
|
|
119794
120428
|
if (basename10(agentDir) !== "agent") {
|
|
119795
120429
|
return void 0;
|
|
119796
120430
|
}
|
|
119797
|
-
return join33(
|
|
120431
|
+
return join33(dirname14(dirname14(agentDir)), siblingRoot, "agent");
|
|
119798
120432
|
}
|
|
119799
120433
|
function createReadOnlyPiSettingsView(cwd, agentDir) {
|
|
119800
120434
|
const projectRoot = resolvePiExtensionProjectRoot(cwd);
|
|
@@ -119838,7 +120472,7 @@ function resolveVendoredClaudeCliEntry() {
|
|
|
119838
120472
|
if (!Array.isArray(extensions) || extensions.length === 0) return null;
|
|
119839
120473
|
const entry = extensions[0];
|
|
119840
120474
|
if (typeof entry !== "string" || entry.length === 0) return null;
|
|
119841
|
-
const path6 = resolve16(
|
|
120475
|
+
const path6 = resolve16(dirname14(pkgJsonPath), entry);
|
|
119842
120476
|
return existsSync27(path6) ? path6 : null;
|
|
119843
120477
|
} catch {
|
|
119844
120478
|
return null;
|
|
@@ -119853,7 +120487,7 @@ function resolveVendoredDroidCliEntry() {
|
|
|
119853
120487
|
if (!Array.isArray(extensions) || extensions.length === 0) return null;
|
|
119854
120488
|
const entry = extensions[0];
|
|
119855
120489
|
if (typeof entry !== "string" || entry.length === 0) return null;
|
|
119856
|
-
const path6 = resolve16(
|
|
120490
|
+
const path6 = resolve16(dirname14(pkgJsonPath), entry);
|
|
119857
120491
|
return existsSync27(path6) ? path6 : null;
|
|
119858
120492
|
} catch {
|
|
119859
120493
|
return null;
|
|
@@ -120237,7 +120871,7 @@ async function createFnAgent2(options) {
|
|
|
120237
120871
|
logMcpForwardingSkipped({ runtimeId: "pi", provider: options.defaultProvider, skippedCount: requestedMcpServers.length, lane: "createFnAgent" });
|
|
120238
120872
|
}
|
|
120239
120873
|
const authStorage = createFusionAuthStorage();
|
|
120240
|
-
const modelRegistry =
|
|
120874
|
+
const modelRegistry = ModelRegistry2.create(authStorage, getModelRegistryModelsPath());
|
|
120241
120875
|
const resolvedProjectRoot = getProjectRootFromWorktree(options.cwd) ?? resolvePiExtensionProjectRoot(options.cwd);
|
|
120242
120876
|
await registerExtensionProviders(resolvedProjectRoot, modelRegistry);
|
|
120243
120877
|
const customProviders = readCustomProviders();
|
|
@@ -120499,8 +121133,25 @@ async function createFnAgent2(options) {
|
|
|
120499
121133
|
throw error2;
|
|
120500
121134
|
}
|
|
120501
121135
|
};
|
|
121136
|
+
const modelDescription = (model) => model ? `${model.provider}/${model.id}` : "unknown model";
|
|
121137
|
+
const configuredFallbackDiffers = Boolean(
|
|
121138
|
+
options.fallbackProvider && options.fallbackModelId && (options.fallbackProvider !== options.defaultProvider || options.fallbackModelId !== options.defaultModelId)
|
|
121139
|
+
);
|
|
121140
|
+
const hasDistinctFallback = Boolean(
|
|
121141
|
+
selectedModel && fallbackModel && (configuredFallbackDiffers || selectedModel.provider !== fallbackModel.provider || selectedModel.id !== fallbackModel.id)
|
|
121142
|
+
);
|
|
121143
|
+
const makeFallbackExhaustedError = (triggerPoint, attempts, underlying) => {
|
|
121144
|
+
const underlyingReason = underlying instanceof Error ? underlying.message : String(underlying);
|
|
121145
|
+
return new ModelFallbackExhaustedError({
|
|
121146
|
+
primaryModel: modelDescription(selectedModel),
|
|
121147
|
+
fallbackModel: hasDistinctFallback ? modelDescription(fallbackModel) : void 0,
|
|
121148
|
+
triggerPoint,
|
|
121149
|
+
attempts,
|
|
121150
|
+
underlyingReason
|
|
121151
|
+
});
|
|
121152
|
+
};
|
|
120502
121153
|
const emitFallbackUsed = async (triggerPoint) => {
|
|
120503
|
-
if (!options.onFallbackModelUsed || !selectedModel || !fallbackModel) {
|
|
121154
|
+
if (!options.onFallbackModelUsed || !selectedModel || !fallbackModel || !hasDistinctFallback) {
|
|
120504
121155
|
return;
|
|
120505
121156
|
}
|
|
120506
121157
|
await options.onFallbackModelUsed({
|
|
@@ -120518,13 +121169,17 @@ async function createFnAgent2(options) {
|
|
|
120518
121169
|
sessionResult = await createSessionWithModel(selectedModel);
|
|
120519
121170
|
piLog.log(`Session created successfully (model=${selectedModel ? `${selectedModel.provider}/${selectedModel.id}` : "default"})`);
|
|
120520
121171
|
} catch (err) {
|
|
120521
|
-
if (!fallbackModel || !selectedModel || !isRetryableModelSelectionError(err?.message || "")) {
|
|
121172
|
+
if (!fallbackModel || !selectedModel || !hasDistinctFallback || !isRetryableModelSelectionError(err?.message || "")) {
|
|
120522
121173
|
piLog.error(`Session creation failed: ${err.message}`);
|
|
120523
121174
|
throw err;
|
|
120524
121175
|
}
|
|
120525
121176
|
piLog.warn(`Primary model failed (${err.message}), trying fallback`);
|
|
120526
121177
|
usingFallback = true;
|
|
120527
|
-
|
|
121178
|
+
try {
|
|
121179
|
+
sessionResult = await createSessionWithModel(fallbackModel);
|
|
121180
|
+
} catch (fallbackErr) {
|
|
121181
|
+
throw makeFallbackExhaustedError("session-creation", 2, fallbackErr);
|
|
121182
|
+
}
|
|
120528
121183
|
await emitFallbackUsed("session-creation");
|
|
120529
121184
|
piLog.log("Fallback session created successfully");
|
|
120530
121185
|
}
|
|
@@ -120652,6 +121307,9 @@ async function createFnAgent2(options) {
|
|
|
120652
121307
|
if (!fallbackModel || usingFallback || !isRetryableModelSelectionError(errorMessage2)) {
|
|
120653
121308
|
throw err;
|
|
120654
121309
|
}
|
|
121310
|
+
if (!hasDistinctFallback) {
|
|
121311
|
+
throw makeFallbackExhaustedError("prompt-time", 1, err);
|
|
121312
|
+
}
|
|
120655
121313
|
usingFallback = true;
|
|
120656
121314
|
const fallbackSession = await swapPromptSession(fallbackModel);
|
|
120657
121315
|
await emitFallbackUsed("prompt-time");
|
|
@@ -120699,7 +121357,7 @@ async function createFnAgent2(options) {
|
|
|
120699
121357
|
throw fallbackErr;
|
|
120700
121358
|
}
|
|
120701
121359
|
}
|
|
120702
|
-
throw fallbackErr;
|
|
121360
|
+
throw makeFallbackExhaustedError("prompt-time", 2, fallbackErr);
|
|
120703
121361
|
}
|
|
120704
121362
|
}
|
|
120705
121363
|
};
|
|
@@ -120726,7 +121384,7 @@ async function createFnAgent2(options) {
|
|
|
120726
121384
|
});
|
|
120727
121385
|
return { session: promptableSession, sessionFile: promptableSession.sessionFile };
|
|
120728
121386
|
}
|
|
120729
|
-
var execAsync, RTK_ACCEPTED_REWRITE_EXIT_CODES, RTK_EXPECTED_PASSTHROUGH_EXIT_CODES, RTK_EXPECTED_FAIL_OPEN_ERROR_CODES, RTK_REWRITE_MAX_BUFFER_BYTES, hostExtensionPaths, FN_MEMORY_APPEND_TOOL_NAME, FUSION_SHUTDOWN_WRAP_FLAG, promptWithFallbackInFlight, COMPACTION_FALLBACK_INSTRUCTIONS, MAX_COMPACTED_PROMPT_MEMORY_CHARS, MAX_COMPACTED_SUBTASK_GUIDANCE_CHARS, MAX_COMPACTED_ATTACHMENTS_CHARS, MAX_COMPACTED_EXISTING_SPEC_CHARS, MAX_COMPACTED_TASK_PROMPT_CHARS, MAX_COMPACTED_USER_COMMENTS_CHARS, GATE_BYPASS_TOOL_NAMES;
|
|
121387
|
+
var execAsync, RTK_ACCEPTED_REWRITE_EXIT_CODES, RTK_EXPECTED_PASSTHROUGH_EXIT_CODES, RTK_EXPECTED_FAIL_OPEN_ERROR_CODES, RTK_REWRITE_MAX_BUFFER_BYTES, hostExtensionPaths, FN_MEMORY_APPEND_TOOL_NAME, FUSION_SHUTDOWN_WRAP_FLAG, promptWithFallbackInFlight, COMPACTION_FALLBACK_INSTRUCTIONS, MAX_COMPACTED_PROMPT_MEMORY_CHARS, MAX_COMPACTED_SUBTASK_GUIDANCE_CHARS, MAX_COMPACTED_ATTACHMENTS_CHARS, MAX_COMPACTED_EXISTING_SPEC_CHARS, MAX_COMPACTED_TASK_PROMPT_CHARS, MAX_COMPACTED_USER_COMMENTS_CHARS, ModelFallbackExhaustedError, GATE_BYPASS_TOOL_NAMES;
|
|
120730
121388
|
var init_pi = __esm({
|
|
120731
121389
|
"../engine/src/pi.ts"() {
|
|
120732
121390
|
"use strict";
|
|
@@ -120766,6 +121424,25 @@ var init_pi = __esm({
|
|
|
120766
121424
|
MAX_COMPACTED_EXISTING_SPEC_CHARS = 4e3;
|
|
120767
121425
|
MAX_COMPACTED_TASK_PROMPT_CHARS = MAX_COMPACTED_EXISTING_SPEC_CHARS;
|
|
120768
121426
|
MAX_COMPACTED_USER_COMMENTS_CHARS = 2e3;
|
|
121427
|
+
ModelFallbackExhaustedError = class extends Error {
|
|
121428
|
+
primaryModel;
|
|
121429
|
+
fallbackModel;
|
|
121430
|
+
triggerPoint;
|
|
121431
|
+
attempts;
|
|
121432
|
+
underlyingReason;
|
|
121433
|
+
constructor(input) {
|
|
121434
|
+
const fallbackClause = input.fallbackModel ? `, fallback ${input.fallbackModel}` : ", no fallback configured";
|
|
121435
|
+
super(
|
|
121436
|
+
`Unable to select a usable model after ${input.attempts} attempt${input.attempts === 1 ? "" : "s"} (primary ${input.primaryModel}${fallbackClause}, trigger: ${input.triggerPoint}): ${input.underlyingReason}`
|
|
121437
|
+
);
|
|
121438
|
+
this.name = "ModelFallbackExhaustedError";
|
|
121439
|
+
this.primaryModel = input.primaryModel;
|
|
121440
|
+
this.fallbackModel = input.fallbackModel;
|
|
121441
|
+
this.triggerPoint = input.triggerPoint;
|
|
121442
|
+
this.attempts = input.attempts;
|
|
121443
|
+
this.underlyingReason = input.underlyingReason;
|
|
121444
|
+
}
|
|
121445
|
+
};
|
|
120769
121446
|
GATE_BYPASS_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
120770
121447
|
"fn_heartbeat_done",
|
|
120771
121448
|
"fn_send_message",
|
|
@@ -122237,11 +122914,17 @@ function normalize6(value) {
|
|
|
122237
122914
|
}
|
|
122238
122915
|
return value.trim().replace(/^refs\/heads\//, "").replace(/^refs\/remotes\/origin\//, "").replace(/^origin\//, "");
|
|
122239
122916
|
}
|
|
122240
|
-
function warnFallback(rootDir, logger3) {
|
|
122917
|
+
function warnFallback(rootDir, logger3, remotes = []) {
|
|
122241
122918
|
if (warnedFallbackRootDirs.has(rootDir)) {
|
|
122242
122919
|
return;
|
|
122243
122920
|
}
|
|
122244
122921
|
warnedFallbackRootDirs.add(rootDir);
|
|
122922
|
+
if (remotes.length > 0) {
|
|
122923
|
+
const remoteList = remotes.join(", ");
|
|
122924
|
+
const originState = remotes.includes("origin") ? "origin/HEAD is unset" : "origin is absent";
|
|
122925
|
+
logger3.warn(`[integration-branch] falling back to 'main' \u2014 auto-detect checks origin/HEAD, but ${originState}; found remote ${remoteList}. Add an origin alias or set integrationBranch manually.`);
|
|
122926
|
+
return;
|
|
122927
|
+
}
|
|
122245
122928
|
logger3.warn("[integration-branch] falling back to 'main' \u2014 origin/HEAD unset and no project override");
|
|
122246
122929
|
}
|
|
122247
122930
|
function resolveFromSettings(settings) {
|
|
@@ -122278,6 +122961,36 @@ function resolveFromOriginHeadSync(rootDir) {
|
|
|
122278
122961
|
return "";
|
|
122279
122962
|
}
|
|
122280
122963
|
}
|
|
122964
|
+
function parseRemotes(stdout) {
|
|
122965
|
+
return [...new Set(stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean))];
|
|
122966
|
+
}
|
|
122967
|
+
async function listGitRemotes(rootDir) {
|
|
122968
|
+
try {
|
|
122969
|
+
const { stdout } = await execAsync2("git remote", {
|
|
122970
|
+
cwd: rootDir,
|
|
122971
|
+
encoding: "utf8",
|
|
122972
|
+
timeout: 5e3,
|
|
122973
|
+
maxBuffer: 1024 * 1024
|
|
122974
|
+
});
|
|
122975
|
+
return parseRemotes(stdout);
|
|
122976
|
+
} catch {
|
|
122977
|
+
return [];
|
|
122978
|
+
}
|
|
122979
|
+
}
|
|
122980
|
+
function listGitRemotesSync(rootDir) {
|
|
122981
|
+
try {
|
|
122982
|
+
const stdout = execSync("git remote", {
|
|
122983
|
+
cwd: rootDir,
|
|
122984
|
+
encoding: "utf8",
|
|
122985
|
+
timeout: 5e3,
|
|
122986
|
+
maxBuffer: 1024 * 1024,
|
|
122987
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
122988
|
+
});
|
|
122989
|
+
return parseRemotes(stdout);
|
|
122990
|
+
} catch {
|
|
122991
|
+
return [];
|
|
122992
|
+
}
|
|
122993
|
+
}
|
|
122281
122994
|
async function resolveIntegrationBranch(rootDir, settings, opts = {}) {
|
|
122282
122995
|
const logger3 = opts.logger ?? console;
|
|
122283
122996
|
const fromSettings = resolveFromSettings(settings);
|
|
@@ -122288,7 +123001,8 @@ async function resolveIntegrationBranch(rootDir, settings, opts = {}) {
|
|
|
122288
123001
|
if (fromOrigin.length > 0) {
|
|
122289
123002
|
return fromOrigin;
|
|
122290
123003
|
}
|
|
122291
|
-
|
|
123004
|
+
const remotes = await listGitRemotes(rootDir);
|
|
123005
|
+
warnFallback(rootDir, logger3, remotes);
|
|
122292
123006
|
return INTEGRATION_BRANCH_FALLBACK;
|
|
122293
123007
|
}
|
|
122294
123008
|
function resolveIntegrationBranchSync(rootDir, settings, opts = {}) {
|
|
@@ -122301,7 +123015,8 @@ function resolveIntegrationBranchSync(rootDir, settings, opts = {}) {
|
|
|
122301
123015
|
if (fromOrigin.length > 0) {
|
|
122302
123016
|
return fromOrigin;
|
|
122303
123017
|
}
|
|
122304
|
-
|
|
123018
|
+
const remotes = listGitRemotesSync(rootDir);
|
|
123019
|
+
warnFallback(rootDir, logger3, remotes);
|
|
122305
123020
|
return INTEGRATION_BRANCH_FALLBACK;
|
|
122306
123021
|
}
|
|
122307
123022
|
function __resetIntegrationBranchCacheForTests() {
|
|
@@ -123543,7 +124258,7 @@ var init_active_session_registry = __esm({
|
|
|
123543
124258
|
// ../engine/src/worktree-hooks.ts
|
|
123544
124259
|
import { exec as exec6 } from "node:child_process";
|
|
123545
124260
|
import * as fs4 from "node:fs/promises";
|
|
123546
|
-
import { dirname as
|
|
124261
|
+
import { dirname as dirname15, resolve as resolve18 } from "node:path";
|
|
123547
124262
|
import { promisify as promisify8 } from "node:util";
|
|
123548
124263
|
function toShellCasePattern(pattern) {
|
|
123549
124264
|
return pattern.replace(/^\^/, "").replace(/\$$/, "").replace(/\\d\+/g, "[0-9]*").replace(/\[a-z0-9-\]\+/g, "[a-z0-9-]*");
|
|
@@ -123733,7 +124448,7 @@ git interpret-trailers --in-place --if-exists doNothing --trailer "$TRAILE
|
|
|
123733
124448
|
`;
|
|
123734
124449
|
}
|
|
123735
124450
|
async function writeFileAtomic(targetPath, content, mode) {
|
|
123736
|
-
await fs4.mkdir(
|
|
124451
|
+
await fs4.mkdir(dirname15(targetPath), { recursive: true });
|
|
123737
124452
|
const tmpPath = `${targetPath}.tmp`;
|
|
123738
124453
|
const current = await fs4.readFile(targetPath, "utf-8").catch(() => null);
|
|
123739
124454
|
if (current === content) return;
|
|
@@ -123889,7 +124604,7 @@ var init_worktree_prune = __esm({
|
|
|
123889
124604
|
// ../engine/src/worktree-stale-lock.ts
|
|
123890
124605
|
import { exec as exec8 } from "node:child_process";
|
|
123891
124606
|
import { readFile as readFile16, stat as stat7, unlink as unlink6 } from "node:fs/promises";
|
|
123892
|
-
import { dirname as
|
|
124607
|
+
import { dirname as dirname16, resolve as resolve19 } from "node:path";
|
|
123893
124608
|
import { promisify as promisify10 } from "node:util";
|
|
123894
124609
|
function parseIndexLockPath(stderr) {
|
|
123895
124610
|
const match = /unable to create ['"]([^'"]*index\.lock)['"]:\s*File exists/i.exec(stderr);
|
|
@@ -123911,15 +124626,15 @@ async function resolveOwningWorktreePath(input) {
|
|
|
123911
124626
|
}
|
|
123912
124627
|
const worktreeName = parseWorktreeNameFromLockPath(input.lockPath);
|
|
123913
124628
|
if (!worktreeName) return void 0;
|
|
123914
|
-
const gitdirFile = resolve19(
|
|
124629
|
+
const gitdirFile = resolve19(dirname16(input.lockPath), "gitdir");
|
|
123915
124630
|
try {
|
|
123916
124631
|
const gitdirRef = (await readFile16(gitdirFile, "utf-8")).trim();
|
|
123917
124632
|
if (gitdirRef) {
|
|
123918
|
-
const resolvedGitdirRef = resolve19(
|
|
124633
|
+
const resolvedGitdirRef = resolve19(dirname16(gitdirFile), gitdirRef);
|
|
123919
124634
|
if (resolvedGitdirRef.endsWith("/.git") || resolvedGitdirRef.endsWith("\\.git")) {
|
|
123920
|
-
return
|
|
124635
|
+
return dirname16(resolvedGitdirRef);
|
|
123921
124636
|
}
|
|
123922
|
-
return
|
|
124637
|
+
return dirname16(resolvedGitdirRef);
|
|
123923
124638
|
}
|
|
123924
124639
|
} catch {
|
|
123925
124640
|
}
|
|
@@ -125207,7 +125922,7 @@ __export(worktree_pool_exports, {
|
|
|
125207
125922
|
import { exec as exec11 } from "node:child_process";
|
|
125208
125923
|
import { promisify as promisify13 } from "node:util";
|
|
125209
125924
|
import { existsSync as existsSync31, lstatSync, readdirSync as readdirSync5, readFileSync as readFileSync12, rmSync as rmSync3, realpathSync as realpathSync6 } from "node:fs";
|
|
125210
|
-
import { basename as basename12, dirname as
|
|
125925
|
+
import { basename as basename12, dirname as dirname17, join as join35, relative as relative6, resolve as resolve22, isAbsolute as isAbsolute9 } from "node:path";
|
|
125211
125926
|
async function getWorktrunkBinary(settings) {
|
|
125212
125927
|
const cacheKey = `${process.env.HOME ?? ""}::${settings.binaryPath ?? ""}`;
|
|
125213
125928
|
const cached2 = _worktrunkBinaryCache.get(cacheKey);
|
|
@@ -125498,7 +126213,7 @@ function dotGitPointerIsDangling(dotGitPath) {
|
|
|
125498
126213
|
const match = /^gitdir:\s*(.+)$/.exec(raw);
|
|
125499
126214
|
if (!match) return false;
|
|
125500
126215
|
const target = match[1].trim();
|
|
125501
|
-
const resolved = isAbsolute9(target) ? target : resolve22(
|
|
126216
|
+
const resolved = isAbsolute9(target) ? target : resolve22(dirname17(dotGitPath), target);
|
|
125502
126217
|
return !existsSync31(resolved);
|
|
125503
126218
|
} catch {
|
|
125504
126219
|
return false;
|
|
@@ -126399,7 +127114,7 @@ var init_worktrunk_failure_handler = __esm({
|
|
|
126399
127114
|
// ../engine/src/worktree-copy-files.ts
|
|
126400
127115
|
import { constants as fsConstants2 } from "node:fs";
|
|
126401
127116
|
import { access as access5, copyFile as copyFile2, lstat, mkdir as mkdir13 } from "node:fs/promises";
|
|
126402
|
-
import { dirname as
|
|
127117
|
+
import { dirname as dirname18, isAbsolute as isAbsolute11, relative as relative8, resolve as resolve24 } from "node:path";
|
|
126403
127118
|
function isInsideRoot(rootDir, candidate) {
|
|
126404
127119
|
const rel = relative8(rootDir, candidate);
|
|
126405
127120
|
return rel === "" || !rel.startsWith("..") && !isAbsolute11(rel);
|
|
@@ -126480,7 +127195,7 @@ async function copyConfiguredWorktreeFiles(options) {
|
|
|
126480
127195
|
continue;
|
|
126481
127196
|
}
|
|
126482
127197
|
try {
|
|
126483
|
-
await mkdir13(
|
|
127198
|
+
await mkdir13(dirname18(destinationPath), { recursive: true });
|
|
126484
127199
|
await copyFile2(sourcePath, destinationPath);
|
|
126485
127200
|
const result = { path: trimmed, sourcePath, destinationPath, outcome: "copied" };
|
|
126486
127201
|
logger3?.log?.(`${taskId ?? "worktree"}: copied configured worktree file ${trimmed}`);
|
|
@@ -132327,7 +133042,7 @@ import {
|
|
|
132327
133042
|
rmSync as rmSync4,
|
|
132328
133043
|
writeFileSync as writeFileSync3
|
|
132329
133044
|
} from "node:fs";
|
|
132330
|
-
import { dirname as
|
|
133045
|
+
import { dirname as dirname19 } from "node:path";
|
|
132331
133046
|
function sanitizeState(parsed) {
|
|
132332
133047
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
132333
133048
|
return {};
|
|
@@ -132420,7 +133135,7 @@ var init_oauth_alert_state = __esm({
|
|
|
132420
133135
|
}
|
|
132421
133136
|
writeState(state) {
|
|
132422
133137
|
const sanitized = sanitizeState(state);
|
|
132423
|
-
const dir =
|
|
133138
|
+
const dir = dirname19(this.statePath);
|
|
132424
133139
|
this.fs.mkdirSync(dir, { recursive: true });
|
|
132425
133140
|
const tempPath = `${this.statePath}.${process.pid}.${this.clock()}.tmp`;
|
|
132426
133141
|
const body = `${JSON.stringify(sanitized, null, 2)}
|
|
@@ -134940,7 +135655,7 @@ var init_agent_memory_index = __esm({
|
|
|
134940
135655
|
// ../engine/src/agent-instructions.ts
|
|
134941
135656
|
import { readFile as readFile20, writeFile as writeFile14, mkdir as mkdir16, access as access7 } from "node:fs/promises";
|
|
134942
135657
|
import { constants as fsConstants3 } from "node:fs";
|
|
134943
|
-
import { isAbsolute as isAbsolute12, resolve as resolve26, relative as relative10, normalize as normalize7, sep as sep6, dirname as
|
|
135658
|
+
import { isAbsolute as isAbsolute12, resolve as resolve26, relative as relative10, normalize as normalize7, sep as sep6, dirname as dirname20 } from "node:path";
|
|
134944
135659
|
function trimAndClamp(value, maxLength, label, agentId) {
|
|
134945
135660
|
const trimmed = value.trim();
|
|
134946
135661
|
if (!trimmed) {
|
|
@@ -135029,7 +135744,7 @@ async function ensureDefaultHeartbeatProcedureFile(rootDir, procedurePathRel, de
|
|
|
135029
135744
|
} catch {
|
|
135030
135745
|
}
|
|
135031
135746
|
try {
|
|
135032
|
-
await mkdir16(
|
|
135747
|
+
await mkdir16(dirname20(filePath), { recursive: true });
|
|
135033
135748
|
await writeFile14(filePath, defaultContent, "utf-8");
|
|
135034
135749
|
log17.log(`Seeded default heartbeat procedure file at ${filePath}`);
|
|
135035
135750
|
return filePath;
|
|
@@ -140211,7 +140926,7 @@ var init_bubblewrap_detect = __esm({
|
|
|
140211
140926
|
});
|
|
140212
140927
|
|
|
140213
140928
|
// ../engine/src/sandbox/bubblewrap-policy.ts
|
|
140214
|
-
import { dirname as
|
|
140929
|
+
import { dirname as dirname21, resolve as resolve27 } from "node:path";
|
|
140215
140930
|
function shouldPassEnv(key) {
|
|
140216
140931
|
return key === "PATH" || key === "HOME" || key === "USER" || key === "LANG" || key.startsWith("LC_") || key.startsWith("NODE_") || key.startsWith("npm_") || key.startsWith("PNPM_") || key === "CI" || key.startsWith("FUSION_");
|
|
140217
140932
|
}
|
|
@@ -140250,7 +140965,7 @@ function policyToBwrapArgs(policy, ctx) {
|
|
|
140250
140965
|
if (!pathExists(path6)) continue;
|
|
140251
140966
|
args.push("--bind", path6, path6);
|
|
140252
140967
|
}
|
|
140253
|
-
const nodeInstallDir =
|
|
140968
|
+
const nodeInstallDir = dirname21(ctx.nodeBinPath);
|
|
140254
140969
|
const readonlyPaths = uniq([
|
|
140255
140970
|
...(policy.allowedReadPaths ?? []).length ? policy.allowedReadPaths ?? [] : [ctx.repoRootPath],
|
|
140256
140971
|
...ctx.repoRootPath !== ctx.worktreePath ? [ctx.repoRootPath] : [],
|
|
@@ -141014,7 +141729,7 @@ var init_sandbox_exec_profile_templates = __esm({
|
|
|
141014
141729
|
});
|
|
141015
141730
|
|
|
141016
141731
|
// ../engine/src/sandbox/sandbox-exec-policy.ts
|
|
141017
|
-
import { dirname as
|
|
141732
|
+
import { dirname as dirname22, resolve as resolve28 } from "node:path";
|
|
141018
141733
|
function uniq2(paths) {
|
|
141019
141734
|
return [...new Set(paths.map((p2) => resolve28(p2)))];
|
|
141020
141735
|
}
|
|
@@ -141054,7 +141769,7 @@ function policyToSbplProfile(policy, ctx) {
|
|
|
141054
141769
|
throw new SandboxPolicyError2("Port 4040 is reserved and cannot be allowed in sandbox policy.");
|
|
141055
141770
|
}
|
|
141056
141771
|
const tmpDir = resolve28(ctx.tmpDirOverride ?? "/private/tmp");
|
|
141057
|
-
const nodeDir =
|
|
141772
|
+
const nodeDir = dirname22(ctx.nodeBinPath);
|
|
141058
141773
|
const writePaths = uniq2([ctx.worktreePath, ctx.pnpmStorePath, ...policy.allowedWritePaths ?? []]);
|
|
141059
141774
|
ensureNoFusionWrites(writePaths, ctx.repoRootPath);
|
|
141060
141775
|
const readPaths = uniq2([
|
|
@@ -141775,6 +142490,22 @@ function throwIfDependencySyncAborted(signal, taskId) {
|
|
|
141775
142490
|
err.name = "AbortError";
|
|
141776
142491
|
throw err;
|
|
141777
142492
|
}
|
|
142493
|
+
function extractCommandErrorDetails(error2) {
|
|
142494
|
+
const maybeCommandError = error2;
|
|
142495
|
+
return String(maybeCommandError.stderr || maybeCommandError.stdout || maybeCommandError.message || error2);
|
|
142496
|
+
}
|
|
142497
|
+
function isOutdatedLockfileError(details) {
|
|
142498
|
+
return /ERR_PNPM_OUTDATED_LOCKFILE|OUTDATED_LOCKFILE|frozen-lockfile|lockfile is frozen|lockfile had changes, but lockfile is frozen|lockfile needs to be updated|Your lockfile needs to be updated/i.test(
|
|
142499
|
+
details
|
|
142500
|
+
);
|
|
142501
|
+
}
|
|
142502
|
+
function buildNonFrozenRetryCommand(installCommand) {
|
|
142503
|
+
if (!installCommand.includes("--frozen-lockfile")) return null;
|
|
142504
|
+
if (/^\s*pnpm\b/.test(installCommand)) {
|
|
142505
|
+
return installCommand.replace(/--frozen-lockfile/g, "--no-frozen-lockfile");
|
|
142506
|
+
}
|
|
142507
|
+
return installCommand.replace(/\s*--frozen-lockfile/g, "").trim();
|
|
142508
|
+
}
|
|
141778
142509
|
async function installWorktreeDependencies(options) {
|
|
141779
142510
|
const { cwd, settings, taskId, signal, log: log27, logger: logger3, context = "merge worktree dependency sync" } = options;
|
|
141780
142511
|
const startedAt = Date.now();
|
|
@@ -141782,7 +142513,7 @@ async function installWorktreeDependencies(options) {
|
|
|
141782
142513
|
const installCommand = getDependencySyncCommand(cwd, settings);
|
|
141783
142514
|
const configured = configuredCommand !== null;
|
|
141784
142515
|
if (!installCommand) {
|
|
141785
|
-
return { installCommand: null, configured: false, skipped: true, skipReason: "no-command", durationMs: Date.now() - startedAt };
|
|
142516
|
+
return { installCommand: null, configured: false, skipped: true, skipReason: "no-command", healed: false, durationMs: Date.now() - startedAt };
|
|
141786
142517
|
}
|
|
141787
142518
|
const shouldUseInstallMarker = !configured;
|
|
141788
142519
|
const lockHash = shouldUseInstallMarker ? computeLockfileHash(cwd) : null;
|
|
@@ -141794,27 +142525,45 @@ async function installWorktreeDependencies(options) {
|
|
|
141794
142525
|
configured,
|
|
141795
142526
|
skipped: true,
|
|
141796
142527
|
skipReason: "lockfile-marker-match",
|
|
142528
|
+
healed: false,
|
|
141797
142529
|
durationMs: Date.now() - startedAt
|
|
141798
142530
|
};
|
|
141799
142531
|
}
|
|
141800
142532
|
throwIfDependencySyncAborted(signal, taskId);
|
|
141801
142533
|
logger3?.log?.(`${taskId}: syncing dependencies ${context}`);
|
|
141802
142534
|
await log27?.(`Syncing dependencies ${context}: ${installCommand}`);
|
|
142535
|
+
const runInstall = (command) => execAsync21(command, {
|
|
142536
|
+
cwd,
|
|
142537
|
+
encoding: "utf-8",
|
|
142538
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
142539
|
+
timeout: INSTALL_TIMEOUT_MS
|
|
142540
|
+
});
|
|
141803
142541
|
try {
|
|
141804
|
-
await
|
|
141805
|
-
cwd,
|
|
141806
|
-
encoding: "utf-8",
|
|
141807
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
141808
|
-
timeout: INSTALL_TIMEOUT_MS
|
|
141809
|
-
});
|
|
142542
|
+
await runInstall(installCommand);
|
|
141810
142543
|
throwIfDependencySyncAborted(signal, taskId);
|
|
141811
142544
|
if (lockHash) writeInstallMarker(cwd, lockHash);
|
|
141812
|
-
return { installCommand, configured, skipped: false, durationMs: Date.now() - startedAt };
|
|
142545
|
+
return { installCommand, configured, skipped: false, healed: false, durationMs: Date.now() - startedAt };
|
|
141813
142546
|
} catch (error2) {
|
|
141814
142547
|
throwIfDependencySyncAborted(signal, taskId);
|
|
141815
|
-
const
|
|
141816
|
-
const
|
|
141817
|
-
|
|
142548
|
+
const details = extractCommandErrorDetails(error2);
|
|
142549
|
+
const retryCommand = configured ? null : buildNonFrozenRetryCommand(installCommand);
|
|
142550
|
+
if (retryCommand && isOutdatedLockfileError(details)) {
|
|
142551
|
+
logger3?.log?.(`${taskId}: lockfile out of date; retrying dependency sync without frozen lockfile`);
|
|
142552
|
+
await log27?.(`Dependency sync hit an outdated lockfile; retrying without frozen lockfile: ${retryCommand}`);
|
|
142553
|
+
try {
|
|
142554
|
+
await runInstall(retryCommand);
|
|
142555
|
+
} catch (retryError) {
|
|
142556
|
+
throwIfDependencySyncAborted(signal, taskId);
|
|
142557
|
+
throw new Error(
|
|
142558
|
+
`Dependency sync failed for ${taskId} (after non-frozen retry): ${extractCommandErrorDetails(retryError)}`.trim()
|
|
142559
|
+
);
|
|
142560
|
+
}
|
|
142561
|
+
throwIfDependencySyncAborted(signal, taskId);
|
|
142562
|
+
const healedHash = computeLockfileHash(cwd);
|
|
142563
|
+
if (healedHash) writeInstallMarker(cwd, healedHash);
|
|
142564
|
+
return { installCommand, configured, skipped: false, healed: true, healedCommand: retryCommand, durationMs: Date.now() - startedAt };
|
|
142565
|
+
}
|
|
142566
|
+
throw new Error(`Dependency sync failed for ${taskId}: ${details}`.trim());
|
|
141818
142567
|
}
|
|
141819
142568
|
}
|
|
141820
142569
|
var execAsync21, INSTALL_MARKER_RELPATH, LOCKFILE_CANDIDATES, INSTALL_TIMEOUT_MS;
|
|
@@ -143954,7 +144703,7 @@ var init_merger_ref_update_advance = __esm({
|
|
|
143954
144703
|
import { execFile as execFile14, spawn as spawn7 } from "node:child_process";
|
|
143955
144704
|
import { promisify as promisify36 } from "node:util";
|
|
143956
144705
|
import { copyFileSync, existsSync as existsSync41, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync2, readFileSync as readFileSync15, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
143957
|
-
import { dirname as
|
|
144706
|
+
import { dirname as dirname23, join as join45 } from "node:path";
|
|
143958
144707
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
143959
144708
|
async function runGit4(args, cwd, timeoutMs) {
|
|
143960
144709
|
const result = await execFileAsync9("git", ["-c", "core.quotePath=false", ...args], {
|
|
@@ -144065,7 +144814,7 @@ async function syncWorktreeToHead(input) {
|
|
|
144065
144814
|
const src = join45(worktreePath, rel);
|
|
144066
144815
|
const dst = join45(untrackedDir, rel);
|
|
144067
144816
|
try {
|
|
144068
|
-
mkdirSync10(
|
|
144817
|
+
mkdirSync10(dirname23(dst), { recursive: true });
|
|
144069
144818
|
copyFileSync(src, dst);
|
|
144070
144819
|
} catch {
|
|
144071
144820
|
}
|
|
@@ -144134,7 +144883,7 @@ async function syncWorktreeToHead(input) {
|
|
|
144134
144883
|
}
|
|
144135
144884
|
const dst = join45(worktreePath, rel);
|
|
144136
144885
|
try {
|
|
144137
|
-
mkdirSync10(
|
|
144886
|
+
mkdirSync10(dirname23(dst), { recursive: true });
|
|
144138
144887
|
const data = readFileSync15(src);
|
|
144139
144888
|
writeFileSync5(dst, data);
|
|
144140
144889
|
restored.push(rel);
|
|
@@ -144484,7 +145233,7 @@ import * as childProcess3 from "node:child_process";
|
|
|
144484
145233
|
import { promisify as promisify38 } from "node:util";
|
|
144485
145234
|
import { existsSync as existsSync42, readFileSync as readFileSync16, readdirSync as readdirSync7, writeFileSync as writeFileSync6, unlinkSync as unlinkSync2, renameSync as renameSync5 } from "node:fs";
|
|
144486
145235
|
import { createHash as createHash12 } from "node:crypto";
|
|
144487
|
-
import { join as join47, dirname as
|
|
145236
|
+
import { join as join47, dirname as dirname24, basename as basename14 } from "node:path";
|
|
144488
145237
|
import { hostname as hostname3 } from "node:os";
|
|
144489
145238
|
import { Type as Type3 } from "typebox";
|
|
144490
145239
|
function mergerCommitEnv() {
|
|
@@ -145075,7 +145824,7 @@ function deriveFileScopedPnpmTestCommand(rootDir, baseBranch, branch) {
|
|
|
145075
145824
|
resolvedTests.add(file2);
|
|
145076
145825
|
continue;
|
|
145077
145826
|
}
|
|
145078
|
-
const dir =
|
|
145827
|
+
const dir = dirname24(file2);
|
|
145079
145828
|
const stem = basename14(file2).replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
145080
145829
|
if (!stem) continue;
|
|
145081
145830
|
const candidates = [
|
|
@@ -153327,8 +154076,8 @@ function optionalStepRevisionLogOutcome(details, key) {
|
|
|
153327
154076
|
return `${details}
|
|
153328
154077
|
${OPTIONAL_STEP_REVISION_KEY_MARKER} ${key}`;
|
|
153329
154078
|
}
|
|
153330
|
-
function extractTaskIdFromTempMergeDir(
|
|
153331
|
-
const match = /^fusion-ai-merge-(fn-\d+)-[a-z0-9]+$/i.exec(
|
|
154079
|
+
function extractTaskIdFromTempMergeDir(dirname31) {
|
|
154080
|
+
const match = /^fusion-ai-merge-(fn-\d+)-[a-z0-9]+$/i.exec(dirname31);
|
|
153332
154081
|
return match?.[1]?.toUpperCase() ?? null;
|
|
153333
154082
|
}
|
|
153334
154083
|
function resolveRepoLocalAiMergeRoot(rootDir, settings) {
|
|
@@ -164847,6 +165596,24 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
|
|
|
164847
165596
|
task.id,
|
|
164848
165597
|
errorMessage2
|
|
164849
165598
|
);
|
|
165599
|
+
} else if (err instanceof ModelFallbackExhaustedError) {
|
|
165600
|
+
const failureMessage = `Triage failed: unable to select a usable model after ${err.attempts} attempt${err.attempts === 1 ? "" : "s"}. ${err.message}`;
|
|
165601
|
+
planLog.error(`\u2717 ${task.id} planner model fallback exhausted: ${failureMessage}`);
|
|
165602
|
+
await this.store.logEntry(task.id, failureMessage).catch((logErr) => {
|
|
165603
|
+
const msg = logErr instanceof Error ? logErr.message : String(logErr);
|
|
165604
|
+
planLog.warn(`${task.id}: failed to log planner fallback exhaustion: ${msg}`);
|
|
165605
|
+
});
|
|
165606
|
+
await this.store.updateTask(task.id, {
|
|
165607
|
+
status: "failed",
|
|
165608
|
+
error: failureMessage,
|
|
165609
|
+
recoveryRetryCount: null,
|
|
165610
|
+
nextRecoveryAt: null
|
|
165611
|
+
}).catch((updateErr) => {
|
|
165612
|
+
const msg = updateErr instanceof Error ? updateErr.message : String(updateErr);
|
|
165613
|
+
planLog.warn(`${task.id}: failed to persist planner fallback exhaustion: ${msg}`);
|
|
165614
|
+
});
|
|
165615
|
+
this.options.onSpecifyError?.(task, err);
|
|
165616
|
+
return;
|
|
164850
165617
|
} else if (isTransientError(errorMessage2)) {
|
|
164851
165618
|
const decision = computeRecoveryDecision({
|
|
164852
165619
|
recoveryRetryCount: task.recoveryRetryCount,
|
|
@@ -170241,7 +171008,7 @@ var init_task_session = __esm({
|
|
|
170241
171008
|
// ../engine/src/pty-native.ts
|
|
170242
171009
|
import * as fs6 from "node:fs";
|
|
170243
171010
|
import { createRequire as createRequire6 } from "node:module";
|
|
170244
|
-
import { join as join53, dirname as
|
|
171011
|
+
import { join as join53, dirname as dirname25 } from "node:path";
|
|
170245
171012
|
function getNativePrebuildName() {
|
|
170246
171013
|
const platform4 = process.platform === "darwin" ? "darwin" : process.platform === "linux" ? "linux" : process.platform === "win32" ? "win32" : "unknown";
|
|
170247
171014
|
const arch = process.arch === "arm64" ? "arm64" : process.arch === "x64" ? "x64" : "unknown";
|
|
@@ -170250,7 +171017,7 @@ function getNativePrebuildName() {
|
|
|
170250
171017
|
function findInstalledNodePtyNativeDir() {
|
|
170251
171018
|
try {
|
|
170252
171019
|
const packageJsonPath = require3.resolve("node-pty/package.json");
|
|
170253
|
-
const pkgRoot =
|
|
171020
|
+
const pkgRoot = dirname25(packageJsonPath);
|
|
170254
171021
|
const releaseDir = join53(pkgRoot, "build", "Release");
|
|
170255
171022
|
if (fs6.existsSync(join53(releaseDir, "pty.node"))) {
|
|
170256
171023
|
return releaseDir;
|
|
@@ -170272,7 +171039,7 @@ function findStagedNativeDir() {
|
|
|
170272
171039
|
return envPath;
|
|
170273
171040
|
}
|
|
170274
171041
|
}
|
|
170275
|
-
const execDir =
|
|
171042
|
+
const execDir = dirname25(process.execPath);
|
|
170276
171043
|
const nextToBinary = join53(execDir, "runtime", prebuildName);
|
|
170277
171044
|
if (fs6.existsSync(join53(nextToBinary, "pty.node"))) {
|
|
170278
171045
|
return nextToBinary;
|
|
@@ -172296,7 +173063,7 @@ import { delimiter, isAbsolute as isAbsolute16, join as join55, relative as rela
|
|
|
172296
173063
|
import { existsSync as existsSync48, lstatSync as lstatSync2, realpathSync as realpathSync9 } from "node:fs";
|
|
172297
173064
|
import { readFile as readFile24, rm as rm9, writeFile as writeFile19 } from "node:fs/promises";
|
|
172298
173065
|
import { Type as Type6 } from "@earendil-works/pi-ai";
|
|
172299
|
-
import { ModelRegistry as
|
|
173066
|
+
import { ModelRegistry as ModelRegistry3, SessionManager as SessionManager2 } from "@earendil-works/pi-coding-agent";
|
|
172300
173067
|
function isAgentBrowserNotFoundError(error2) {
|
|
172301
173068
|
const err = error2;
|
|
172302
173069
|
const code = typeof err?.code === "string" || typeof err?.code === "number" ? String(err.code) : void 0;
|
|
@@ -174063,20 +174830,30 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
174063
174830
|
/** Set of ephemeral spawned agent IDs with in-flight cleanup (prevents duplicate deletion attempts). */
|
|
174064
174831
|
pendingEphemeralDeletions = /* @__PURE__ */ new Set();
|
|
174065
174832
|
workspaceConfig = void 0;
|
|
174833
|
+
/*
|
|
174834
|
+
FNXC:WorkflowLifecycle 2026-07-01-16:20:
|
|
174835
|
+
Breadcrumb task-log writes on the abort/pause/finalize paths are best-effort diagnostics and must NEVER break control flow. FN-7335 wired store.logEntry() straight into the SYNCHRONOUS markPausedAborted() as `void this.store.logEntry(...).catch(...)`; when store.logEntry is absent/throws synchronously (undefined method, store closed mid-abort, corrupted pager) the call throws a TypeError BEFORE the promise exists, so the trailing .catch() never runs and the exception unwinds out of markPausedAborted — aborting hard-cancel/pause and stranding the in-review handoff. Route every breadcrumb write through safeLogEntry() so both synchronous throws and async rejections are swallowed into a warn.
|
|
174836
|
+
*/
|
|
174837
|
+
safeLogEntry(taskId, message) {
|
|
174838
|
+
try {
|
|
174839
|
+
const result = this.store.logEntry(taskId, message, void 0, this.getRunContextFor(taskId));
|
|
174840
|
+
void Promise.resolve(result).catch((error2) => {
|
|
174841
|
+
executorLog.warn(`${taskId}: failed to write task-log breadcrumb: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
174842
|
+
});
|
|
174843
|
+
} catch (error2) {
|
|
174844
|
+
executorLog.warn(`${taskId}: failed to write task-log breadcrumb: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
174845
|
+
}
|
|
174846
|
+
}
|
|
174066
174847
|
markPausedAborted(taskId, provenance = "hard-cancel", source = "unspecified") {
|
|
174067
174848
|
const previousProvenance = this.pausedAbortProvenance.get(taskId);
|
|
174068
174849
|
const alreadyMarked = this.pausedAborted.has(taskId);
|
|
174069
174850
|
this.pausedAborted.add(taskId);
|
|
174070
174851
|
this.pausedAbortProvenance.set(taskId, provenance);
|
|
174071
174852
|
if (!alreadyMarked || previousProvenance !== provenance) {
|
|
174072
|
-
|
|
174853
|
+
this.safeLogEntry(
|
|
174073
174854
|
taskId,
|
|
174074
|
-
`Pause abort marked: provenance=${provenance} source=${source}${previousProvenance && previousProvenance !== provenance ? ` previous=${previousProvenance}` : ""}
|
|
174075
|
-
|
|
174076
|
-
this.getRunContextFor(taskId)
|
|
174077
|
-
).catch((error2) => {
|
|
174078
|
-
executorLog.warn(`${taskId}: failed to log pause-abort marker: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
174079
|
-
});
|
|
174855
|
+
`Pause abort marked: provenance=${provenance} source=${source}${previousProvenance && previousProvenance !== provenance ? ` previous=${previousProvenance}` : ""}`
|
|
174856
|
+
);
|
|
174080
174857
|
}
|
|
174081
174858
|
}
|
|
174082
174859
|
/**
|
|
@@ -174403,7 +175180,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
174403
175180
|
get modelRegistry() {
|
|
174404
175181
|
if (!this._modelRegistry) {
|
|
174405
175182
|
const authStorage = createFusionAuthStorage();
|
|
174406
|
-
this._modelRegistry =
|
|
175183
|
+
this._modelRegistry = ModelRegistry3.create(authStorage, getModelRegistryModelsPath());
|
|
174407
175184
|
this._modelRegistry.refresh();
|
|
174408
175185
|
}
|
|
174409
175186
|
return this._modelRegistry;
|
|
@@ -174415,22 +175192,22 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
174415
175192
|
return this._approvalRequestStore;
|
|
174416
175193
|
}
|
|
174417
175194
|
buildActionGateContext(taskId, agent, projectDefaultPolicy) {
|
|
174418
|
-
|
|
174419
|
-
|
|
174420
|
-
|
|
174421
|
-
const policy = resolveEffectiveAgentPermissionPolicy(agent
|
|
175195
|
+
const actorId = agent?.id ?? `executor-${taskId ?? "unknown"}`;
|
|
175196
|
+
const actorName = agent?.name ?? `Task worker ${taskId ?? "unknown"}`;
|
|
175197
|
+
const isEphemeral = !agent || isEphemeralAgent(agent);
|
|
175198
|
+
const policy = resolveEffectiveAgentPermissionPolicy(agent?.permissionPolicy, projectDefaultPolicy);
|
|
174422
175199
|
return {
|
|
174423
|
-
agentId:
|
|
174424
|
-
agentName:
|
|
174425
|
-
isEphemeral
|
|
175200
|
+
agentId: actorId,
|
|
175201
|
+
agentName: actorName,
|
|
175202
|
+
isEphemeral,
|
|
174426
175203
|
taskId,
|
|
174427
175204
|
runId: taskId ? this.getRunContextFor(taskId)?.runId : void 0,
|
|
174428
175205
|
permissionPolicy: policy,
|
|
174429
175206
|
createApprovalRequest: async (decision, args) => this.approvalRequestStore.create({
|
|
174430
175207
|
requester: {
|
|
174431
|
-
actorId
|
|
175208
|
+
actorId,
|
|
174432
175209
|
actorType: "agent",
|
|
174433
|
-
actorName
|
|
175210
|
+
actorName
|
|
174434
175211
|
},
|
|
174435
175212
|
taskId,
|
|
174436
175213
|
runId: taskId ? this.getRunContextFor(taskId)?.runId : void 0,
|
|
@@ -174449,16 +175226,16 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
174449
175226
|
}
|
|
174450
175227
|
}),
|
|
174451
175228
|
findApprovalByDedupeKey: async (dedupeKey) => {
|
|
174452
|
-
const latest = this.approvalRequestStore.findLatestByDedupeKey({ requesterActorId:
|
|
175229
|
+
const latest = this.approvalRequestStore.findLatestByDedupeKey({ requesterActorId: actorId, taskId, dedupeKey });
|
|
174453
175230
|
return latest ? { id: latest.id, status: latest.status } : null;
|
|
174454
175231
|
},
|
|
174455
175232
|
findPendingApprovalByDedupeKey: async (dedupeKey) => {
|
|
174456
|
-
const latest = this.approvalRequestStore.findLatestByDedupeKey({ requesterActorId:
|
|
175233
|
+
const latest = this.approvalRequestStore.findLatestByDedupeKey({ requesterActorId: actorId, taskId, dedupeKey });
|
|
174457
175234
|
return latest?.status === "pending" ? { id: latest.id } : null;
|
|
174458
175235
|
},
|
|
174459
175236
|
pauseForApproval: async ({ approvalRequestId, decision }) => {
|
|
174460
175237
|
if (taskId) {
|
|
174461
|
-
await this.store.pauseTask(taskId, true, this.getRunContextFor(taskId), { pausedByAgentId:
|
|
175238
|
+
await this.store.pauseTask(taskId, true, this.getRunContextFor(taskId), { pausedByAgentId: actorId });
|
|
174462
175239
|
await this.store.logEntry(
|
|
174463
175240
|
taskId,
|
|
174464
175241
|
`Approval required for ${decision.toolName}. Request ${approvalRequestId} created; task and agent paused awaiting decision.`,
|
|
@@ -174466,55 +175243,54 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
174466
175243
|
this.getRunContextFor(taskId)
|
|
174467
175244
|
);
|
|
174468
175245
|
}
|
|
174469
|
-
if (this.options.agentStore) {
|
|
175246
|
+
if (agent && this.options.agentStore) {
|
|
174470
175247
|
await this.options.agentStore.updateAgentState(agent.id, "paused");
|
|
174471
175248
|
await this.options.agentStore.updateAgent(agent.id, { pauseReason: "awaiting-approval" });
|
|
174472
175249
|
}
|
|
174473
175250
|
},
|
|
174474
175251
|
markApprovalCompleted: async (approvalRequestId) => {
|
|
174475
175252
|
await this.approvalRequestStore.markCompleted(approvalRequestId, {
|
|
174476
|
-
actor: { actorId
|
|
175253
|
+
actor: { actorId, actorType: "agent", actorName },
|
|
174477
175254
|
note: "Tool executed after approval"
|
|
174478
175255
|
});
|
|
174479
175256
|
}
|
|
174480
175257
|
};
|
|
174481
175258
|
}
|
|
174482
175259
|
buildPermanentAgentGatingContext(taskId, agent, projectDefaultPolicy) {
|
|
174483
|
-
|
|
174484
|
-
|
|
174485
|
-
}
|
|
175260
|
+
const actorId = agent?.id ?? `executor-${taskId ?? "unknown"}`;
|
|
175261
|
+
const actorName = agent?.name ?? `Task worker ${taskId ?? "unknown"}`;
|
|
174486
175262
|
return {
|
|
174487
|
-
permissionPolicy: resolveEffectiveAgentPermissionPolicy(agent
|
|
175263
|
+
permissionPolicy: resolveEffectiveAgentPermissionPolicy(agent?.permissionPolicy, projectDefaultPolicy),
|
|
174488
175264
|
requester: {
|
|
174489
|
-
actorId
|
|
175265
|
+
actorId,
|
|
174490
175266
|
actorType: "agent",
|
|
174491
|
-
actorName
|
|
175267
|
+
actorName
|
|
174492
175268
|
},
|
|
174493
175269
|
taskId,
|
|
174494
175270
|
runId: taskId ? this.getRunContextFor(taskId)?.runId : void 0,
|
|
174495
175271
|
createApprovalRequest: async ({ category, toolName, args }) => this.approvalRequestStore.create({
|
|
174496
175272
|
requester: {
|
|
174497
|
-
actorId
|
|
175273
|
+
actorId,
|
|
174498
175274
|
actorType: "agent",
|
|
174499
|
-
actorName
|
|
175275
|
+
actorName
|
|
174500
175276
|
},
|
|
174501
175277
|
taskId,
|
|
174502
175278
|
runId: taskId ? this.getRunContextFor(taskId)?.runId : void 0,
|
|
174503
175279
|
targetAction: {
|
|
174504
175280
|
category,
|
|
174505
175281
|
action: toolName,
|
|
174506
|
-
summary: `
|
|
175282
|
+
summary: `Agent gated action for ${toolName}`,
|
|
174507
175283
|
resourceType: "tool",
|
|
174508
175284
|
resourceId: toolName,
|
|
174509
175285
|
context: {
|
|
174510
175286
|
toolName,
|
|
174511
175287
|
toolArgs: args,
|
|
174512
|
-
source: "
|
|
175288
|
+
source: "agent-gating"
|
|
174513
175289
|
}
|
|
174514
175290
|
}
|
|
174515
175291
|
}),
|
|
174516
175292
|
findPendingApprovalRequest: async (dedupeKey) => {
|
|
174517
|
-
const pending = this.approvalRequestStore.list({ status: "pending", requesterActorId:
|
|
175293
|
+
const pending = this.approvalRequestStore.list({ status: "pending", requesterActorId: actorId, taskId, limit: 100 });
|
|
174518
175294
|
return pending.find((request2) => request2.targetAction.context?.approvalDedupeKey === dedupeKey) ?? null;
|
|
174519
175295
|
}
|
|
174520
175296
|
};
|
|
@@ -174763,14 +175539,10 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
174763
175539
|
this.stuckAborted.delete(taskId);
|
|
174764
175540
|
if (hadActiveSurface) {
|
|
174765
175541
|
executorLog.log(`${taskId}: awaited abort of in-flight work \u2014 ${reason}`);
|
|
174766
|
-
|
|
175542
|
+
this.safeLogEntry(
|
|
174767
175543
|
taskId,
|
|
174768
|
-
`Pause abort cleanup completed: reason=${reason}; surfaces=${abortedSurfaces.join(", ") || "none"}
|
|
174769
|
-
|
|
174770
|
-
this.getRunContextFor(taskId)
|
|
174771
|
-
).catch((error2) => {
|
|
174772
|
-
executorLog.warn(`${taskId}: failed to log pause-abort cleanup: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
174773
|
-
});
|
|
175544
|
+
`Pause abort cleanup completed: reason=${reason}; surfaces=${abortedSurfaces.join(", ") || "none"}`
|
|
175545
|
+
);
|
|
174774
175546
|
}
|
|
174775
175547
|
}
|
|
174776
175548
|
async abortAllInFlight(reason) {
|
|
@@ -178511,11 +179283,9 @@ ${prompt}`;
|
|
|
178511
179283
|
if (pausedAborted || live.paused || live.userPaused || abortProvenance) {
|
|
178512
179284
|
const failedNodeForLog = result.visitedNodeIds[result.visitedNodeIds.length - 1] ?? "unknown";
|
|
178513
179285
|
const failureValueForLog = this.graphFailureValue(result) ?? "none";
|
|
178514
|
-
|
|
179286
|
+
this.safeLogEntry(
|
|
178515
179287
|
task.id,
|
|
178516
|
-
`Pause abort classified: provenance=${abortProvenance ?? "unknown"}; node=${failedNodeForLog}; interrupted=${result.interruptedNodeId ?? "none"}; abortKind=${result.interruptedAbortKind ?? "none"}; column=${live.column}; status=${live.status ?? "none"}; paused=${live.paused === true}; userPaused=${live.userPaused === true}; value=${failureValueForLog}; genuine=${genuinePauseAbort}; mergeSeam=${mergeSeamAborted}; completionSuppressed=${suppressFinalizedCompletionAbort}
|
|
178517
|
-
void 0,
|
|
178518
|
-
this.getRunContextFor(task.id)
|
|
179288
|
+
`Pause abort classified: provenance=${abortProvenance ?? "unknown"}; node=${failedNodeForLog}; interrupted=${result.interruptedNodeId ?? "none"}; abortKind=${result.interruptedAbortKind ?? "none"}; column=${live.column}; status=${live.status ?? "none"}; paused=${live.paused === true}; userPaused=${live.userPaused === true}; value=${failureValueForLog}; genuine=${genuinePauseAbort}; mergeSeam=${mergeSeamAborted}; completionSuppressed=${suppressFinalizedCompletionAbort}`
|
|
178519
179289
|
);
|
|
178520
179290
|
}
|
|
178521
179291
|
if (genuinePauseAbort && await this.isReentrantPausedAbortedInFlightNode(live, result, abortProvenance, pausedAborted, this.userCanceledTaskIds.has(task.id))) {
|
|
@@ -189152,6 +189922,10 @@ async function landOneRepo(repoRootDir, branch, integrationBranch, ctx) {
|
|
|
189152
189922
|
configured: depsSyncResult.configured,
|
|
189153
189923
|
skipped: depsSyncResult.skipped,
|
|
189154
189924
|
skipReason: depsSyncResult.skipReason,
|
|
189925
|
+
// FNXC:AIMerge 2026-07-02-14:05 (lockfile auto-heal): record when an outdated frozen lockfile
|
|
189926
|
+
// was recovered by a non-frozen retry so operators can see deps drifted without failing merge.
|
|
189927
|
+
healed: depsSyncResult.healed,
|
|
189928
|
+
healedCommand: depsSyncResult.healedCommand,
|
|
189155
189929
|
durationMs: depsSyncResult.durationMs
|
|
189156
189930
|
}
|
|
189157
189931
|
});
|
|
@@ -190089,7 +190863,30 @@ var init_workflow_authoritative_driver = __esm({
|
|
|
190089
190863
|
},
|
|
190090
190864
|
primitives: this.deps.executor.createAuthoritativeWorkflowPrimitives?.(settings) ?? primitivesFromLegacySeams(seams),
|
|
190091
190865
|
seams,
|
|
190866
|
+
/*
|
|
190867
|
+
FNXC:WorkflowInterpreterCutover 2026-07-01-21:05:
|
|
190868
|
+
BUILTIN_CODING_WORKFLOW_IR graduated to carry default-on/always-on custom
|
|
190869
|
+
lifecycle nodes: the plan-review/code-review/browser-verification/post-merge
|
|
190870
|
+
optional groups (bypassed at their group node when a task disables them via
|
|
190871
|
+
enabledWorkflowSteps) and an ALWAYS-ON `completion-summary` prompt node
|
|
190872
|
+
(kind:"prompt", summaryTarget:"task") that records the pre-review summary.
|
|
190873
|
+
The transitional authoritative driver validates SEAM-LEVEL parity
|
|
190874
|
+
(planning/execute/review/merge/schedule) against the legacy executor; it does
|
|
190875
|
+
not reproduce custom-node side effects. Previously runCustomNode threw on any
|
|
190876
|
+
custom node, so the always-on completion-summary aborted every clean run
|
|
190877
|
+
before it could reach review→merge. Pass built-in auxiliary custom nodes
|
|
190878
|
+
(task-summary nodes and bypassable optional groups) through as success so the
|
|
190879
|
+
seam pipeline completes, mirroring the main executor's completion path for
|
|
190880
|
+
parity purposes. Genuinely unexpected custom nodes still fail loudly.
|
|
190881
|
+
*/
|
|
190092
190882
|
runCustomNode: async (node) => {
|
|
190883
|
+
const isBuiltinAuxiliaryNode = node.config?.summaryTarget === "task" || node.kind === "optional-group";
|
|
190884
|
+
if (isBuiltinAuxiliaryNode) {
|
|
190885
|
+
executorLog.log(
|
|
190886
|
+
`[workflow-authoritative] ${task.id}: passing auxiliary custom node '${node.id}' through as success (seam-parity run)`
|
|
190887
|
+
);
|
|
190888
|
+
return { outcome: "success" };
|
|
190889
|
+
}
|
|
190093
190890
|
throw new Error(`unexpected custom node in builtin authoritative workflow: ${node.id}`);
|
|
190094
190891
|
},
|
|
190095
190892
|
onEvent: (event) => {
|
|
@@ -192512,14 +193309,11 @@ Critical: a heartbeat without observable progress (or an explicit no-op reason)
|
|
|
192512
193309
|
return this.approvalRequestStore;
|
|
192513
193310
|
}
|
|
192514
193311
|
buildActionGateContext(agent, taskId, runId, projectDefaultPolicy) {
|
|
192515
|
-
if (isEphemeralAgent(agent)) {
|
|
192516
|
-
return void 0;
|
|
192517
|
-
}
|
|
192518
193312
|
const policy = resolveEffectiveAgentPermissionPolicy(agent.permissionPolicy, projectDefaultPolicy);
|
|
192519
193313
|
return {
|
|
192520
193314
|
agentId: agent.id,
|
|
192521
193315
|
agentName: agent.name,
|
|
192522
|
-
isEphemeral:
|
|
193316
|
+
isEphemeral: isEphemeralAgent(agent),
|
|
192523
193317
|
taskId,
|
|
192524
193318
|
runId,
|
|
192525
193319
|
permissionPolicy: policy,
|
|
@@ -192564,9 +193358,6 @@ Critical: a heartbeat without observable progress (or an explicit no-op reason)
|
|
|
192564
193358
|
};
|
|
192565
193359
|
}
|
|
192566
193360
|
buildPermanentAgentGatingContext(agent, taskId, runId, projectDefaultPolicy) {
|
|
192567
|
-
if (isEphemeralAgent(agent)) {
|
|
192568
|
-
return void 0;
|
|
192569
|
-
}
|
|
192570
193361
|
return {
|
|
192571
193362
|
permissionPolicy: resolveEffectiveAgentPermissionPolicy(agent.permissionPolicy, projectDefaultPolicy),
|
|
192572
193363
|
requester: { actorId: agent.id, actorType: "agent", actorName: agent.name },
|
|
@@ -192579,13 +193370,13 @@ Critical: a heartbeat without observable progress (or an explicit no-op reason)
|
|
|
192579
193370
|
targetAction: {
|
|
192580
193371
|
category,
|
|
192581
193372
|
action: toolName,
|
|
192582
|
-
summary: `
|
|
193373
|
+
summary: `Agent gated action for ${toolName}`,
|
|
192583
193374
|
resourceType: "tool",
|
|
192584
193375
|
resourceId: toolName,
|
|
192585
193376
|
context: {
|
|
192586
193377
|
toolName,
|
|
192587
193378
|
toolArgs: args,
|
|
192588
|
-
source: "
|
|
193379
|
+
source: "agent-gating"
|
|
192589
193380
|
}
|
|
192590
193381
|
}
|
|
192591
193382
|
}),
|
|
@@ -203987,7 +204778,7 @@ __export(child_process_runtime_exports, {
|
|
|
203987
204778
|
import { EventEmitter as EventEmitter23 } from "node:events";
|
|
203988
204779
|
import { fork } from "node:child_process";
|
|
203989
204780
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
203990
|
-
import { dirname as
|
|
204781
|
+
import { dirname as dirname26, join as join58 } from "node:path";
|
|
203991
204782
|
var HealthMonitor, ChildProcessRuntime;
|
|
203992
204783
|
var init_child_process_runtime = __esm({
|
|
203993
204784
|
"../engine/src/runtimes/child-process-runtime.ts"() {
|
|
@@ -204147,7 +204938,7 @@ var init_child_process_runtime = __esm({
|
|
|
204147
204938
|
*/
|
|
204148
204939
|
getWorkerPath() {
|
|
204149
204940
|
const isCompiled = !import.meta.url.endsWith(".ts");
|
|
204150
|
-
const currentDir =
|
|
204941
|
+
const currentDir = dirname26(fileURLToPath3(import.meta.url));
|
|
204151
204942
|
const workerFile = isCompiled ? "child-process-worker.js" : "child-process-worker.ts";
|
|
204152
204943
|
return join58(currentDir, workerFile);
|
|
204153
204944
|
}
|
|
@@ -212450,6 +213241,7 @@ __export(src_exports2, {
|
|
|
212450
213241
|
createDefaultNodeHandlers: () => createDefaultNodeHandlers,
|
|
212451
213242
|
createFnAgent: () => createFnAgent2,
|
|
212452
213243
|
createFusionAuthStorage: () => createFusionAuthStorage,
|
|
213244
|
+
createFusionModelRegistry: () => createFusionModelRegistry,
|
|
212453
213245
|
createInteractiveAiSessionWith: () => createInteractiveAiSessionWith,
|
|
212454
213246
|
createLogger: () => createLogger2,
|
|
212455
213247
|
createNoopLegacySeams: () => createNoopLegacySeams,
|
|
@@ -213139,6 +213931,13 @@ var init_planning_board_tools = __esm({
|
|
|
213139
213931
|
// ../dashboard/src/planning.ts
|
|
213140
213932
|
import { randomUUID as randomUUID23 } from "node:crypto";
|
|
213141
213933
|
import { EventEmitter as EventEmitter28 } from "node:events";
|
|
213934
|
+
async function resolvePlanningMcpServers(store) {
|
|
213935
|
+
const resolved = await resolveMcpServersForStore(store);
|
|
213936
|
+
if (!resolved || !Array.isArray(resolved.servers)) {
|
|
213937
|
+
return [];
|
|
213938
|
+
}
|
|
213939
|
+
return resolved.servers;
|
|
213940
|
+
}
|
|
213142
213941
|
function ensureEngineReady() {
|
|
213143
213942
|
return Promise.resolve();
|
|
213144
213943
|
}
|
|
@@ -213207,6 +214006,68 @@ function normalizePlanningSummaryPayload(summaryInput, fallback) {
|
|
|
213207
214006
|
keyDeliverables: normalizeStringArray(summary.keyDeliverables)
|
|
213208
214007
|
};
|
|
213209
214008
|
}
|
|
214009
|
+
function slugifyCheckpointTheme(label) {
|
|
214010
|
+
return label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "topic";
|
|
214011
|
+
}
|
|
214012
|
+
function collectCheckpointThemeText(history, summary) {
|
|
214013
|
+
return [
|
|
214014
|
+
summary.title,
|
|
214015
|
+
summary.description,
|
|
214016
|
+
summary.keyDeliverables.join("\n"),
|
|
214017
|
+
summary.suggestedDependencies.join("\n"),
|
|
214018
|
+
...history.flatMap((entry) => [
|
|
214019
|
+
entry.question.question,
|
|
214020
|
+
entry.question.description ?? "",
|
|
214021
|
+
JSON.stringify(entry.response)
|
|
214022
|
+
])
|
|
214023
|
+
].join("\n");
|
|
214024
|
+
}
|
|
214025
|
+
function buildDeepeningCheckpointOptions(history, summary) {
|
|
214026
|
+
const text = collectCheckpointThemeText(history, summary);
|
|
214027
|
+
const matched = CHECKPOINT_THEME_CANDIDATES.filter(
|
|
214028
|
+
(candidate) => candidate.patterns.some((pattern) => pattern.test(text))
|
|
214029
|
+
);
|
|
214030
|
+
const selected = matched.length > 0 ? matched : CHECKPOINT_THEME_CANDIDATES.filter((candidate) => FALLBACK_CHECKPOINT_THEME_OPTIONS.includes(candidate.id));
|
|
214031
|
+
const seen = /* @__PURE__ */ new Set();
|
|
214032
|
+
const options = selected.flatMap((candidate) => {
|
|
214033
|
+
const id = `theme-${slugifyCheckpointTheme(candidate.id)}`;
|
|
214034
|
+
if (seen.has(id) || candidate.label === "Proceed to final plan") {
|
|
214035
|
+
return [];
|
|
214036
|
+
}
|
|
214037
|
+
seen.add(id);
|
|
214038
|
+
return [{ id, label: candidate.label, description: candidate.description }];
|
|
214039
|
+
});
|
|
214040
|
+
return [
|
|
214041
|
+
{ id: PLANNING_DEEPEN_PROCEED_OPTION_ID, label: "Proceed to final plan", description: "The plan is detailed enough; show the final editable summary." },
|
|
214042
|
+
...options
|
|
214043
|
+
];
|
|
214044
|
+
}
|
|
214045
|
+
function buildDeepeningCheckpointQuestion(history, summary) {
|
|
214046
|
+
return {
|
|
214047
|
+
id: PLANNING_DEEPEN_CHECKPOINT_ID,
|
|
214048
|
+
type: "multi_select",
|
|
214049
|
+
question: PLANNING_DEEPEN_CHECKPOINT_QUESTION,
|
|
214050
|
+
description: "Select any areas you want to explore further, write an unlisted topic, or proceed to the final plan.",
|
|
214051
|
+
options: buildDeepeningCheckpointOptions(history, summary)
|
|
214052
|
+
};
|
|
214053
|
+
}
|
|
214054
|
+
function classifyDeepeningCheckpointResponse(question, responses) {
|
|
214055
|
+
const rawSelected = responses[question.id];
|
|
214056
|
+
const selectedIds = Array.isArray(rawSelected) ? rawSelected.filter((id) => typeof id === "string") : [];
|
|
214057
|
+
const customTopic = typeof responses._other === "string" && responses._other.trim().length > 0 ? responses._other.trim() : void 0;
|
|
214058
|
+
const proceed = responses[PLANNING_DEEPEN_PROCEED_RESPONSE_KEY] === true || selectedIds.includes(PLANNING_DEEPEN_PROCEED_OPTION_ID);
|
|
214059
|
+
const themeIds = selectedIds.filter((id) => id !== PLANNING_DEEPEN_PROCEED_OPTION_ID);
|
|
214060
|
+
const selectedThemeLabels = themeIds.map((id) => question.options?.find((option) => option.id === id)?.label || id);
|
|
214061
|
+
return {
|
|
214062
|
+
proceed,
|
|
214063
|
+
selectedThemeIds: themeIds,
|
|
214064
|
+
selectedThemeLabels,
|
|
214065
|
+
...customTopic ? { customTopic } : {}
|
|
214066
|
+
};
|
|
214067
|
+
}
|
|
214068
|
+
function isDeepeningCheckpointQuestion(question) {
|
|
214069
|
+
return question?.id === PLANNING_DEEPEN_CHECKPOINT_ID && question.question === PLANNING_DEEPEN_CHECKPOINT_QUESTION;
|
|
214070
|
+
}
|
|
213210
214071
|
function safeParseJson(text, fallback, options) {
|
|
213211
214072
|
if (!text) {
|
|
213212
214073
|
return fallback;
|
|
@@ -213260,7 +214121,8 @@ function persistSession(session, status, error2) {
|
|
|
213260
214121
|
initialPlan: session.initialPlan,
|
|
213261
214122
|
...session.draftModelProvider ? { modelProvider: session.draftModelProvider } : {},
|
|
213262
214123
|
...session.draftModelId ? { modelId: session.draftModelId } : {},
|
|
213263
|
-
...session.draftSummarizedFor ? { summarizedFor: session.draftSummarizedFor } : {}
|
|
214124
|
+
...session.draftSummarizedFor ? { summarizedFor: session.draftSummarizedFor } : {},
|
|
214125
|
+
...session.pendingSummary ? { pendingSummary: session.pendingSummary } : {}
|
|
213264
214126
|
}),
|
|
213265
214127
|
conversationHistory: JSON.stringify(session.history),
|
|
213266
214128
|
currentQuestion: session.currentQuestion ? JSON.stringify(session.currentQuestion) : null,
|
|
@@ -213317,6 +214179,7 @@ function buildSessionFromRow(row) {
|
|
|
213317
214179
|
}),
|
|
213318
214180
|
{ title: row.title, description: row.title }
|
|
213319
214181
|
) : void 0,
|
|
214182
|
+
pendingSummary: payload.pendingSummary ? normalizePlanningSummaryPayload(payload.pendingSummary, { title: row.title, description: row.title }) : void 0,
|
|
213320
214183
|
thinkingOutput: row.thinkingOutput,
|
|
213321
214184
|
lastGeneratedThinking: row.thinkingOutput || "",
|
|
213322
214185
|
error: row.error ?? void 0,
|
|
@@ -213424,7 +214287,7 @@ ${depthPromptSuffix}` : baseSystemPrompt;
|
|
|
213424
214287
|
builtinToolsAllowlist: [...PLANNING_BUILTIN_WEB_TOOLS],
|
|
213425
214288
|
// FNXC:McpConfig 2026-06-25-22:31: Planning/chat session creation resolves trusted MCP servers through the dashboard-scoped store and forwards only the materialized in-memory set to the engine runtime guard.
|
|
213426
214289
|
// FNXC:McpConfig 2026-06-29-00:00: Planning sessions are intentionally read-only but still need configured MCP documentation/context tools; opt in at the session boundary while preserving engine-side namespacing, filtering, wrappers, and disposal.
|
|
213427
|
-
mcpServers:
|
|
214290
|
+
mcpServers: await resolvePlanningMcpServers(store),
|
|
213428
214291
|
allowMcpToolsInReadonly: true,
|
|
213429
214292
|
customTools: [
|
|
213430
214293
|
...createPlanningBoardTools(store),
|
|
@@ -213537,14 +214400,7 @@ async function getFirstQuestionFromAgent(session, message) {
|
|
|
213537
214400
|
title: session.title || session.initialPlan,
|
|
213538
214401
|
description: session.initialPlan
|
|
213539
214402
|
});
|
|
213540
|
-
session
|
|
213541
|
-
persistSession(session, "complete");
|
|
213542
|
-
return {
|
|
213543
|
-
id: "q-direct-summary",
|
|
213544
|
-
type: "confirm",
|
|
213545
|
-
question: `The AI has generated a plan: "${summary.title}". Proceed with this?`,
|
|
213546
|
-
description: summary.description
|
|
213547
|
-
};
|
|
214403
|
+
return setPendingSummaryCheckpoint(session, summary);
|
|
213548
214404
|
}
|
|
213549
214405
|
return parsed.data;
|
|
213550
214406
|
}
|
|
@@ -213564,7 +214420,7 @@ ${depthPromptSuffix}` : baseSystemPrompt;
|
|
|
213564
214420
|
builtinToolsAllowlist: [...PLANNING_BUILTIN_WEB_TOOLS],
|
|
213565
214421
|
// FNXC:McpConfig 2026-06-25-22:31: Streaming planning uses the same dashboard-scoped MCP resolution seam as non-streaming planning so no planning lane silently drops enabled servers.
|
|
213566
214422
|
// FNXC:McpConfig 2026-06-29-00:00: Streaming planning uses the explicit read-only MCP opt-in; non-planning read-only lanes remain denied unless they set the same reviewed policy flag.
|
|
213567
|
-
mcpServers:
|
|
214423
|
+
mcpServers: await resolvePlanningMcpServers(store),
|
|
213568
214424
|
allowMcpToolsInReadonly: true,
|
|
213569
214425
|
customTools: [
|
|
213570
214426
|
...createPlanningBoardTools(store),
|
|
@@ -213770,6 +214626,54 @@ async function runGenerationWithTimeout(session, operation) {
|
|
|
213770
214626
|
}
|
|
213771
214627
|
}
|
|
213772
214628
|
}
|
|
214629
|
+
function finalizePendingSummary(session) {
|
|
214630
|
+
const summary = normalizePlanningSummaryPayload(session.pendingSummary ?? session.summary, {
|
|
214631
|
+
title: session.title || session.initialPlan,
|
|
214632
|
+
description: session.initialPlan
|
|
214633
|
+
});
|
|
214634
|
+
session.summary = summary;
|
|
214635
|
+
session.pendingSummary = void 0;
|
|
214636
|
+
session.currentQuestion = void 0;
|
|
214637
|
+
session.error = void 0;
|
|
214638
|
+
session.updatedAt = /* @__PURE__ */ new Date();
|
|
214639
|
+
persistSession(session, "complete");
|
|
214640
|
+
planningStreamManager.broadcast(session.id, {
|
|
214641
|
+
type: "summary",
|
|
214642
|
+
data: summary
|
|
214643
|
+
});
|
|
214644
|
+
planningStreamManager.broadcast(session.id, { type: "complete" });
|
|
214645
|
+
return summary;
|
|
214646
|
+
}
|
|
214647
|
+
function setPendingSummaryCheckpoint(session, summary) {
|
|
214648
|
+
const checkpoint = buildDeepeningCheckpointQuestion(session.history, summary);
|
|
214649
|
+
session.pendingSummary = summary;
|
|
214650
|
+
session.summary = void 0;
|
|
214651
|
+
session.currentQuestion = checkpoint;
|
|
214652
|
+
session.error = void 0;
|
|
214653
|
+
session.lastGeneratedThinking = session.thinkingOutput;
|
|
214654
|
+
session.updatedAt = /* @__PURE__ */ new Date();
|
|
214655
|
+
persistSession(session, "awaiting_input");
|
|
214656
|
+
void maybeNotifyPlanningAwaitingInput(session, checkpoint);
|
|
214657
|
+
planningStreamManager.broadcast(session.id, {
|
|
214658
|
+
type: "question",
|
|
214659
|
+
data: checkpoint
|
|
214660
|
+
});
|
|
214661
|
+
return checkpoint;
|
|
214662
|
+
}
|
|
214663
|
+
function formatDeepeningRequestForAgent(decision, pendingSummary) {
|
|
214664
|
+
const requestedTopics = [
|
|
214665
|
+
...decision.selectedThemeLabels,
|
|
214666
|
+
...decision.customTopic ? [decision.customTopic] : []
|
|
214667
|
+
];
|
|
214668
|
+
return [
|
|
214669
|
+
"The user chose to go deeper before accepting the final planning summary.",
|
|
214670
|
+
"Continue the planning interview and explore these specific topics in more detail before producing another completion summary:",
|
|
214671
|
+
requestedTopics.map((topic) => `- ${topic}`).join("\n"),
|
|
214672
|
+
"Do not skip directly to the final summary unless the additional details are addressed; when you next complete, return the normal JSON complete payload.",
|
|
214673
|
+
"Pending summary that was withheld from the user:",
|
|
214674
|
+
JSON.stringify(pendingSummary)
|
|
214675
|
+
].join("\n\n");
|
|
214676
|
+
}
|
|
213773
214677
|
async function continueAgentConversation(session, message) {
|
|
213774
214678
|
if (!session.agent) {
|
|
213775
214679
|
throw new InvalidSessionStateError("AI agent not initialized");
|
|
@@ -213862,6 +214766,7 @@ async function continueAgentConversation(session, message) {
|
|
|
213862
214766
|
if (parsed.type === "question") {
|
|
213863
214767
|
session.currentQuestion = parsed.data;
|
|
213864
214768
|
session.summary = void 0;
|
|
214769
|
+
session.pendingSummary = void 0;
|
|
213865
214770
|
session.error = void 0;
|
|
213866
214771
|
session.lastGeneratedThinking = session.thinkingOutput;
|
|
213867
214772
|
session.updatedAt = /* @__PURE__ */ new Date();
|
|
@@ -213876,16 +214781,7 @@ async function continueAgentConversation(session, message) {
|
|
|
213876
214781
|
title: session.title || session.initialPlan,
|
|
213877
214782
|
description: session.initialPlan
|
|
213878
214783
|
});
|
|
213879
|
-
session
|
|
213880
|
-
session.currentQuestion = void 0;
|
|
213881
|
-
session.error = void 0;
|
|
213882
|
-
session.updatedAt = /* @__PURE__ */ new Date();
|
|
213883
|
-
persistSession(session, "complete");
|
|
213884
|
-
planningStreamManager.broadcast(session.id, {
|
|
213885
|
-
type: "summary",
|
|
213886
|
-
data: summary
|
|
213887
|
-
});
|
|
213888
|
-
planningStreamManager.broadcast(session.id, { type: "complete" });
|
|
214784
|
+
setPendingSummaryCheckpoint(session, summary);
|
|
213889
214785
|
}
|
|
213890
214786
|
});
|
|
213891
214787
|
} catch (err) {
|
|
@@ -214091,6 +214987,7 @@ async function submitResponse(sessionId, responses, rootDir, promptOverrides, st
|
|
|
214091
214987
|
throw new InvalidSessionStateError("No active question in session");
|
|
214092
214988
|
}
|
|
214093
214989
|
session.error = void 0;
|
|
214990
|
+
session.pendingSummary = void 0;
|
|
214094
214991
|
persistSession(session, "generating");
|
|
214095
214992
|
await ensureSessionAgent(session, rootDir, session.history, promptOverrides, store);
|
|
214096
214993
|
const refineMessage = formatRefineRequestForAgent(session.summary);
|
|
@@ -214104,12 +215001,35 @@ async function submitResponse(sessionId, responses, rootDir, promptOverrides, st
|
|
|
214104
215001
|
};
|
|
214105
215002
|
session.error = void 0;
|
|
214106
215003
|
session.history.push(historyEntry);
|
|
214107
|
-
|
|
214108
|
-
|
|
214109
|
-
|
|
215004
|
+
if (isDeepeningCheckpointQuestion(currentQuestion)) {
|
|
215005
|
+
const pendingSummary = session.pendingSummary;
|
|
215006
|
+
if (!pendingSummary) {
|
|
215007
|
+
throw new InvalidSessionStateError("Planning checkpoint is missing its pending summary");
|
|
215008
|
+
}
|
|
215009
|
+
const decision = classifyDeepeningCheckpointResponse(currentQuestion, responses);
|
|
215010
|
+
if (decision.proceed) {
|
|
215011
|
+
finalizePendingSummary(session);
|
|
215012
|
+
} else {
|
|
215013
|
+
const hasDeepeningTopic = decision.selectedThemeLabels.length > 0 || Boolean(decision.customTopic);
|
|
215014
|
+
if (!hasDeepeningTopic) {
|
|
215015
|
+
session.history.pop();
|
|
215016
|
+
throw new InvalidSessionStateError("Select a topic to explore or proceed to the final plan");
|
|
215017
|
+
}
|
|
215018
|
+
session.pendingSummary = void 0;
|
|
215019
|
+
persistSession(session, "generating");
|
|
215020
|
+
if (!session.agent) {
|
|
215021
|
+
await ensureSessionAgent(session, rootDir, session.history.slice(0, -1), promptOverrides, store);
|
|
215022
|
+
}
|
|
215023
|
+
await continueAgentConversation(session, formatDeepeningRequestForAgent(decision, pendingSummary));
|
|
215024
|
+
}
|
|
215025
|
+
} else {
|
|
215026
|
+
persistSession(session, "generating");
|
|
215027
|
+
if (!session.agent) {
|
|
215028
|
+
await ensureSessionAgent(session, rootDir, session.history.slice(0, -1), promptOverrides, store);
|
|
215029
|
+
}
|
|
215030
|
+
const message = formatResponseForAgent(currentQuestion, responses);
|
|
215031
|
+
await continueAgentConversation(session, message);
|
|
214110
215032
|
}
|
|
214111
|
-
const message = formatResponseForAgent(currentQuestion, responses);
|
|
214112
|
-
await continueAgentConversation(session, message);
|
|
214113
215033
|
}
|
|
214114
215034
|
if (session.summary) {
|
|
214115
215035
|
return { type: "complete", data: session.summary };
|
|
@@ -214266,7 +215186,7 @@ function getSession(sessionId) {
|
|
|
214266
215186
|
return void 0;
|
|
214267
215187
|
}
|
|
214268
215188
|
}
|
|
214269
|
-
var PLANNING_NO_AMBIENT_TASK_ID, PLANNING_BUILTIN_WEB_TOOLS, createFnAgent4, planningNtfyHelpers, diagnostics2, PLANNING_SYSTEM_PROMPT, SESSION_TTL_MS, CLEANUP_INTERVAL_MS2, MAX_SESSIONS_PER_IP_PER_HOUR, RATE_LIMIT_WINDOW_MS2, GENERATION_TIMEOUT_MS, GENERATION_LOOP_REPEAT_LIMIT, PLANNING_STUCK_ERROR_MESSAGE, PLANNING_LOOP_ERROR_MESSAGE, PLANNING_USER_STOP_ERROR_MESSAGE, PLANNING_DEPTH_PROMPT_SUFFIX, sessions, rateLimits2, activeGenerations, _aiSessionStore, cleanupInterval2, PlanningStreamManager, planningStreamManager, MAX_PARSE_RETRIES2, RateLimitError2, SessionNotFoundError, InvalidSessionStateError, GenerationInProgressError;
|
|
215189
|
+
var PLANNING_NO_AMBIENT_TASK_ID, PLANNING_BUILTIN_WEB_TOOLS, createFnAgent4, planningNtfyHelpers, diagnostics2, PLANNING_SYSTEM_PROMPT, SESSION_TTL_MS, CLEANUP_INTERVAL_MS2, MAX_SESSIONS_PER_IP_PER_HOUR, RATE_LIMIT_WINDOW_MS2, GENERATION_TIMEOUT_MS, GENERATION_LOOP_REPEAT_LIMIT, PLANNING_STUCK_ERROR_MESSAGE, PLANNING_LOOP_ERROR_MESSAGE, PLANNING_USER_STOP_ERROR_MESSAGE, PLANNING_DEPTH_PROMPT_SUFFIX, sessions, rateLimits2, activeGenerations, _aiSessionStore, CHECKPOINT_THEME_CANDIDATES, FALLBACK_CHECKPOINT_THEME_OPTIONS, cleanupInterval2, PlanningStreamManager, planningStreamManager, MAX_PARSE_RETRIES2, RateLimitError2, SessionNotFoundError, InvalidSessionStateError, GenerationInProgressError;
|
|
214270
215190
|
var init_planning = __esm({
|
|
214271
215191
|
"../dashboard/src/planning.ts"() {
|
|
214272
215192
|
"use strict";
|
|
@@ -214360,6 +215280,15 @@ For completion:
|
|
|
214360
215280
|
sessions = /* @__PURE__ */ new Map();
|
|
214361
215281
|
rateLimits2 = /* @__PURE__ */ new Map();
|
|
214362
215282
|
activeGenerations = /* @__PURE__ */ new Map();
|
|
215283
|
+
CHECKPOINT_THEME_CANDIDATES = [
|
|
215284
|
+
{ id: "scope", label: "Scope and non-goals", description: "Clarify boundaries, trade-offs, and what should stay out of this task.", patterns: [/\bscope\b/i, /non[- ]?goal/i, /boundary/i, /trade[- ]?off/i] },
|
|
215285
|
+
{ id: "edge-cases", label: "Edge cases and data states", description: "Explore empty, duplicate, malformed, missing, or unusual states before implementation.", patterns: [/edge case/i, /empty/i, /undefined/i, /duplicate/i, /malformed/i, /data state/i] },
|
|
215286
|
+
{ id: "ux", label: "UX and interaction details", description: "Tighten user-facing copy, responsive behavior, accessibility, and interaction flow.", patterns: [/\bux\b/i, /user/i, /mobile/i, /responsive/i, /accessibility/i, /keyboard/i, /button/i] },
|
|
215287
|
+
{ id: "dependencies", label: "Dependencies and integrations", description: "Identify prerequisite tasks, third-party systems, and integration constraints.", patterns: [/dependenc/i, /integration/i, /api\b/i, /external/i, /provider/i, /service/i] },
|
|
215288
|
+
{ id: "testing", label: "Testing and verification", description: "Deepen acceptance criteria, regression coverage, and validation commands.", patterns: [/test/i, /verify/i, /validation/i, /acceptance/i, /regression/i] },
|
|
215289
|
+
{ id: "rollout", label: "Rollout and operations", description: "Discuss migration, release, observability, documentation, or support considerations.", patterns: [/rollout/i, /migration/i, /release/i, /observability/i, /docs?\b/i, /operator/i] }
|
|
215290
|
+
];
|
|
215291
|
+
FALLBACK_CHECKPOINT_THEME_OPTIONS = ["scope", "edge-cases", "ux", "testing"];
|
|
214363
215292
|
cleanupInterval2 = setInterval(cleanupExpiredSessions, CLEANUP_INTERVAL_MS2);
|
|
214364
215293
|
cleanupInterval2.unref?.();
|
|
214365
215294
|
registerBeforeExitCleanup(() => {
|
|
@@ -215229,7 +216158,7 @@ import {
|
|
|
215229
216158
|
unlinkSync as unlinkSync3
|
|
215230
216159
|
} from "node:fs";
|
|
215231
216160
|
import { homedir as homedir8 } from "node:os";
|
|
215232
|
-
import { basename as basename16, dirname as
|
|
216161
|
+
import { basename as basename16, dirname as dirname27, join as join60, resolve as resolve31 } from "node:path";
|
|
215233
216162
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
215234
216163
|
function resolveHermesHome(profile) {
|
|
215235
216164
|
const base = process.env.HERMES_HOME ?? join60(homedir8(), ".hermes");
|
|
@@ -215238,7 +216167,7 @@ function resolveHermesHome(profile) {
|
|
|
215238
216167
|
}
|
|
215239
216168
|
function getFusionSkillSourceCandidates(moduleUrl = import.meta.url) {
|
|
215240
216169
|
const here = fileURLToPath4(moduleUrl);
|
|
215241
|
-
const moduleDir =
|
|
216170
|
+
const moduleDir = dirname27(here);
|
|
215242
216171
|
return [
|
|
215243
216172
|
resolve31(moduleDir, "..", "..", "..", "..", "packages", "cli", "skill", FUSION_SKILL_NAME),
|
|
215244
216173
|
resolve31(moduleDir, "..", "..", "..", "skill", FUSION_SKILL_NAME),
|
|
@@ -215265,16 +216194,16 @@ function installFusionSkillIntoHermesHome(options = {}) {
|
|
|
215265
216194
|
};
|
|
215266
216195
|
}
|
|
215267
216196
|
try {
|
|
215268
|
-
mkdirSync14(
|
|
216197
|
+
mkdirSync14(dirname27(targetDir), { recursive: true });
|
|
215269
216198
|
let replaced = false;
|
|
215270
216199
|
if (existsSync51(targetDir) || isBrokenSymlink(targetDir)) {
|
|
215271
216200
|
const stat12 = lstatSync3(targetDir);
|
|
215272
216201
|
if (stat12.isSymbolicLink()) {
|
|
215273
216202
|
const currentTarget = safeReadlink(targetDir);
|
|
215274
|
-
if (currentTarget && resolve31(
|
|
216203
|
+
if (currentTarget && resolve31(dirname27(targetDir), currentTarget) === resolve31(sourceDir)) {
|
|
215275
216204
|
return { outcome: "already-installed", sourceDir, targetDir };
|
|
215276
216205
|
}
|
|
215277
|
-
if (!looksLikeFusionSkillTarget(resolve31(
|
|
216206
|
+
if (!looksLikeFusionSkillTarget(resolve31(dirname27(targetDir), currentTarget ?? ""))) {
|
|
215278
216207
|
return {
|
|
215279
216208
|
outcome: "skipped",
|
|
215280
216209
|
sourceDir,
|
|
@@ -215741,7 +216670,7 @@ import { writeFileSync as writeFileSync10 } from "node:fs";
|
|
|
215741
216670
|
import { tmpdir as tmpdir10 } from "node:os";
|
|
215742
216671
|
import { join as join61 } from "node:path";
|
|
215743
216672
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
215744
|
-
import { dirname as
|
|
216673
|
+
import { dirname as dirname28 } from "node:path";
|
|
215745
216674
|
function toolsToMcpToolDefs(tools) {
|
|
215746
216675
|
if (!Array.isArray(tools)) return [];
|
|
215747
216676
|
return tools.filter((tool) => tool && typeof tool.name === "string" && !BUILT_IN_TOOL_NAMES.has(tool.name)).map((tool) => ({
|
|
@@ -215755,7 +216684,7 @@ function writeOpenClawMcpBridgeFiles(toolDefs, cacheKey) {
|
|
|
215755
216684
|
const schemaPath = join61(tmpdir10(), `openclaw-runtime-mcp-schemas-${suffix}.json`);
|
|
215756
216685
|
writeFileSync10(schemaPath, JSON.stringify(toolDefs));
|
|
215757
216686
|
const __filename = fileURLToPath5(import.meta.url);
|
|
215758
|
-
const __dirname2 =
|
|
216687
|
+
const __dirname2 = dirname28(__filename);
|
|
215759
216688
|
const serverPath = join61(__dirname2, "mcp-schema-server.cjs");
|
|
215760
216689
|
const serverConfig = {
|
|
215761
216690
|
command: "node",
|
|
@@ -219575,6 +220504,430 @@ var init_github_source_issue_close = __esm({
|
|
|
219575
220504
|
}
|
|
219576
220505
|
});
|
|
219577
220506
|
|
|
220507
|
+
// ../dashboard/src/gitlab.ts
|
|
220508
|
+
function clampLimit(limit) {
|
|
220509
|
+
if (typeof limit !== "number" || !Number.isFinite(limit)) return DEFAULT_LIMIT3;
|
|
220510
|
+
return Math.max(1, Math.min(MAX_LIMIT, Math.floor(limit)));
|
|
220511
|
+
}
|
|
220512
|
+
function encodeGitLabPathId(id) {
|
|
220513
|
+
return encodeURIComponent(String(id).trim());
|
|
220514
|
+
}
|
|
220515
|
+
function appendListParams(params, options) {
|
|
220516
|
+
const state = typeof options.state === "string" && options.state.trim() ? options.state.trim() : "opened";
|
|
220517
|
+
params.set("state", state);
|
|
220518
|
+
if (options.labels && options.labels.length > 0) {
|
|
220519
|
+
params.set("labels", options.labels.map((label) => label.trim()).filter(Boolean).join(","));
|
|
220520
|
+
}
|
|
220521
|
+
}
|
|
220522
|
+
function asRecord2(value) {
|
|
220523
|
+
return value && typeof value === "object" ? value : {};
|
|
220524
|
+
}
|
|
220525
|
+
function normalizeAuthor(author) {
|
|
220526
|
+
const raw = asRecord2(author);
|
|
220527
|
+
if (Object.keys(raw).length === 0) return null;
|
|
220528
|
+
return {
|
|
220529
|
+
...typeof raw.id === "number" ? { id: raw.id } : {},
|
|
220530
|
+
...typeof raw.username === "string" ? { username: raw.username } : {},
|
|
220531
|
+
...typeof raw.name === "string" ? { name: raw.name } : {},
|
|
220532
|
+
...typeof raw.avatar_url === "string" ? { avatarUrl: raw.avatar_url } : {},
|
|
220533
|
+
...typeof raw.web_url === "string" ? { webUrl: raw.web_url } : {}
|
|
220534
|
+
};
|
|
220535
|
+
}
|
|
220536
|
+
function normalizeLabels(labels) {
|
|
220537
|
+
return Array.isArray(labels) ? labels.filter((label) => typeof label === "string") : [];
|
|
220538
|
+
}
|
|
220539
|
+
function readProjectPath(input) {
|
|
220540
|
+
const raw = asRecord2(input);
|
|
220541
|
+
if (typeof raw.project_path_with_namespace === "string") return raw.project_path_with_namespace;
|
|
220542
|
+
const references = asRecord2(raw.references);
|
|
220543
|
+
if (typeof references.full === "string") {
|
|
220544
|
+
const full = references.full;
|
|
220545
|
+
const webUrl = typeof raw.web_url === "string" ? raw.web_url : "";
|
|
220546
|
+
const marker = webUrl.includes("/-/merge_requests/") ? "!" : "#";
|
|
220547
|
+
const idx = full.lastIndexOf(marker);
|
|
220548
|
+
return idx > 0 ? full.slice(0, idx) : void 0;
|
|
220549
|
+
}
|
|
220550
|
+
if (typeof raw?.web_url === "string") {
|
|
220551
|
+
const marker = raw.web_url.includes("/-/merge_requests/") ? "/-/merge_requests/" : "/-/issues/";
|
|
220552
|
+
try {
|
|
220553
|
+
const url2 = new URL(raw.web_url);
|
|
220554
|
+
const idx = url2.pathname.indexOf(marker);
|
|
220555
|
+
return idx > 0 ? decodeURIComponent(url2.pathname.slice(1, idx)) : void 0;
|
|
220556
|
+
} catch {
|
|
220557
|
+
return void 0;
|
|
220558
|
+
}
|
|
220559
|
+
}
|
|
220560
|
+
return void 0;
|
|
220561
|
+
}
|
|
220562
|
+
function normalizeIssue(input, resourceKind, extras = {}) {
|
|
220563
|
+
const raw = asRecord2(input);
|
|
220564
|
+
return {
|
|
220565
|
+
resourceKind,
|
|
220566
|
+
...typeof raw.id === "number" ? { id: raw.id } : {},
|
|
220567
|
+
iid: Number(raw.iid),
|
|
220568
|
+
...typeof raw.project_id === "number" ? { projectId: raw.project_id } : {},
|
|
220569
|
+
...readProjectPath(raw) ? { projectPath: readProjectPath(raw) } : {},
|
|
220570
|
+
title: String(raw.title ?? ""),
|
|
220571
|
+
description: typeof raw.description === "string" ? raw.description : null,
|
|
220572
|
+
webUrl: String(raw.web_url ?? ""),
|
|
220573
|
+
state: String(raw.state ?? "unknown"),
|
|
220574
|
+
author: normalizeAuthor(raw.author),
|
|
220575
|
+
labels: normalizeLabels(raw.labels),
|
|
220576
|
+
...typeof raw.created_at === "string" ? { createdAt: raw.created_at } : {},
|
|
220577
|
+
...typeof raw.updated_at === "string" ? { updatedAt: raw.updated_at } : {},
|
|
220578
|
+
...typeof raw.closed_at === "string" ? { closedAt: raw.closed_at } : {},
|
|
220579
|
+
...typeof raw.user_notes_count === "number" ? { commentsCount: raw.user_notes_count } : {},
|
|
220580
|
+
...extras
|
|
220581
|
+
};
|
|
220582
|
+
}
|
|
220583
|
+
function normalizeMergeRequest(input, extras = {}) {
|
|
220584
|
+
const raw = asRecord2(input);
|
|
220585
|
+
return {
|
|
220586
|
+
resourceKind: "merge_request",
|
|
220587
|
+
...typeof raw.id === "number" ? { id: raw.id } : {},
|
|
220588
|
+
iid: Number(raw.iid),
|
|
220589
|
+
...typeof raw.project_id === "number" ? { projectId: raw.project_id } : {},
|
|
220590
|
+
...readProjectPath(raw) ? { projectPath: readProjectPath(raw) } : {},
|
|
220591
|
+
title: String(raw.title ?? ""),
|
|
220592
|
+
description: typeof raw.description === "string" ? raw.description : null,
|
|
220593
|
+
webUrl: String(raw.web_url ?? ""),
|
|
220594
|
+
state: String(raw.state ?? "unknown"),
|
|
220595
|
+
author: normalizeAuthor(raw.author),
|
|
220596
|
+
labels: normalizeLabels(raw.labels),
|
|
220597
|
+
...typeof raw.created_at === "string" ? { createdAt: raw.created_at } : {},
|
|
220598
|
+
...typeof raw.updated_at === "string" ? { updatedAt: raw.updated_at } : {},
|
|
220599
|
+
...typeof raw.closed_at === "string" ? { closedAt: raw.closed_at } : {},
|
|
220600
|
+
...typeof raw.merged_at === "string" ? { mergedAt: raw.merged_at } : {},
|
|
220601
|
+
...typeof raw.user_notes_count === "number" ? { commentsCount: raw.user_notes_count } : {},
|
|
220602
|
+
...typeof raw.source_branch === "string" ? { sourceBranch: raw.source_branch } : {},
|
|
220603
|
+
...typeof raw.target_branch === "string" ? { targetBranch: raw.target_branch } : {},
|
|
220604
|
+
...typeof raw.draft === "boolean" ? { draft: raw.draft } : {},
|
|
220605
|
+
...extras
|
|
220606
|
+
};
|
|
220607
|
+
}
|
|
220608
|
+
function mapGitLabError(status) {
|
|
220609
|
+
switch (status) {
|
|
220610
|
+
case 401:
|
|
220611
|
+
return "GitLab authentication failed. Check gitlabAuthToken and required read_api scope.";
|
|
220612
|
+
case 403:
|
|
220613
|
+
return "GitLab authorization failed. Check token scope and project/group access.";
|
|
220614
|
+
case 404:
|
|
220615
|
+
return "GitLab project, group, issue, or merge request was not found.";
|
|
220616
|
+
case 429:
|
|
220617
|
+
return "GitLab rate limit exceeded. Try again later.";
|
|
220618
|
+
default:
|
|
220619
|
+
return status >= 500 ? "GitLab is unavailable. Try again later." : `GitLab API request failed with status ${status}.`;
|
|
220620
|
+
}
|
|
220621
|
+
}
|
|
220622
|
+
function projectIdentity(item) {
|
|
220623
|
+
return item.projectPath ?? (item.projectId !== void 0 ? String(item.projectId) : "unknown-project");
|
|
220624
|
+
}
|
|
220625
|
+
function buildGitLabTaskProvenance(args) {
|
|
220626
|
+
const { auth: auth2, resourceType, item } = args;
|
|
220627
|
+
const groupPathFromInput = typeof args.groupInput === "string" ? args.groupInput : void 0;
|
|
220628
|
+
const groupIdFromInput = typeof args.groupInput === "number" ? args.groupInput : void 0;
|
|
220629
|
+
const repository = projectIdentity(item);
|
|
220630
|
+
const externalIssueId = resourceType === "merge_request" ? `gitlab:mr:${item.projectId ?? repository}:${item.id ?? item.iid}` : String(item.id ?? `${item.projectId ?? repository}:${item.iid}`);
|
|
220631
|
+
const url2 = new URL(item.webUrl);
|
|
220632
|
+
return {
|
|
220633
|
+
sourceIssue: {
|
|
220634
|
+
provider: "gitlab",
|
|
220635
|
+
repository,
|
|
220636
|
+
externalIssueId,
|
|
220637
|
+
issueNumber: item.iid,
|
|
220638
|
+
url: item.webUrl
|
|
220639
|
+
},
|
|
220640
|
+
gitlabTracking: {
|
|
220641
|
+
item: {
|
|
220642
|
+
kind: resourceType,
|
|
220643
|
+
url: item.webUrl,
|
|
220644
|
+
instanceUrl: auth2.webBaseUrl,
|
|
220645
|
+
host: url2.host,
|
|
220646
|
+
iid: item.iid,
|
|
220647
|
+
...typeof item.id === "number" ? { id: item.id } : {},
|
|
220648
|
+
...typeof item.projectId === "number" ? { projectId: item.projectId } : {},
|
|
220649
|
+
...typeof item.projectPath === "string" ? { projectPath: item.projectPath } : {},
|
|
220650
|
+
..."groupId" in item && item.groupId !== void 0 ? { groupId: item.groupId } : groupIdFromInput !== void 0 ? { groupId: groupIdFromInput } : {},
|
|
220651
|
+
..."groupPath" in item && item.groupPath !== void 0 ? { groupPath: item.groupPath } : groupPathFromInput !== void 0 ? { groupPath: groupPathFromInput } : {},
|
|
220652
|
+
title: item.title,
|
|
220653
|
+
state: item.state,
|
|
220654
|
+
createdAt: item.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
220655
|
+
linkedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
220656
|
+
...item.updatedAt ? { lastSyncedAt: item.updatedAt } : {}
|
|
220657
|
+
}
|
|
220658
|
+
},
|
|
220659
|
+
sourceMetadata: {
|
|
220660
|
+
provider: "gitlab",
|
|
220661
|
+
resourceType,
|
|
220662
|
+
instanceUrl: auth2.webBaseUrl,
|
|
220663
|
+
apiBaseUrl: auth2.apiBaseUrl,
|
|
220664
|
+
projectId: item.projectId,
|
|
220665
|
+
projectPath: item.projectPath,
|
|
220666
|
+
groupId: "groupId" in item && item.groupId !== void 0 ? item.groupId : groupIdFromInput,
|
|
220667
|
+
groupPath: "groupPath" in item && item.groupPath !== void 0 ? item.groupPath : groupPathFromInput,
|
|
220668
|
+
projectInput: args.projectInput,
|
|
220669
|
+
groupInput: args.groupInput,
|
|
220670
|
+
iid: item.iid,
|
|
220671
|
+
webUrl: item.webUrl,
|
|
220672
|
+
...resourceType === "merge_request" ? {
|
|
220673
|
+
mergeRequestId: item.id,
|
|
220674
|
+
mergeRequestIid: item.iid,
|
|
220675
|
+
sourceBranch: item.sourceBranch,
|
|
220676
|
+
targetBranch: item.targetBranch,
|
|
220677
|
+
draft: item.draft
|
|
220678
|
+
} : {
|
|
220679
|
+
issueId: item.id,
|
|
220680
|
+
issueIid: item.iid
|
|
220681
|
+
}
|
|
220682
|
+
}
|
|
220683
|
+
};
|
|
220684
|
+
}
|
|
220685
|
+
function isGitLabAlreadyImported(task, provenance) {
|
|
220686
|
+
const sourceUrl = provenance.sourceIssue.url;
|
|
220687
|
+
if (sourceUrl && task.description?.includes(sourceUrl)) return true;
|
|
220688
|
+
if (task.sourceIssue?.provider === "gitlab" && task.sourceIssue.externalIssueId === provenance.sourceIssue.externalIssueId) return true;
|
|
220689
|
+
const metadata = task.source?.sourceMetadata;
|
|
220690
|
+
if (task.source?.sourceType === "gitlab_import" && metadata && typeof metadata === "object") {
|
|
220691
|
+
const meta3 = metadata;
|
|
220692
|
+
return meta3.resourceType === provenance.sourceMetadata.resourceType && meta3.iid === provenance.sourceMetadata.iid && (meta3.projectId === provenance.sourceMetadata.projectId || meta3.projectPath === provenance.sourceMetadata.projectPath) && (meta3.webUrl === sourceUrl || sourceUrl === void 0);
|
|
220693
|
+
}
|
|
220694
|
+
return false;
|
|
220695
|
+
}
|
|
220696
|
+
function buildGitLabTaskDescription(item) {
|
|
220697
|
+
const body = item.description?.trim() || "(no description)";
|
|
220698
|
+
return `${body}
|
|
220699
|
+
|
|
220700
|
+
Source: ${item.webUrl}`;
|
|
220701
|
+
}
|
|
220702
|
+
var GitLabApiError, DEFAULT_LIMIT3, MAX_LIMIT, PAGE_SIZE, GitLabClient;
|
|
220703
|
+
var init_gitlab = __esm({
|
|
220704
|
+
"../dashboard/src/gitlab.ts"() {
|
|
220705
|
+
"use strict";
|
|
220706
|
+
GitLabApiError = class extends Error {
|
|
220707
|
+
constructor(status, message) {
|
|
220708
|
+
super(message);
|
|
220709
|
+
this.status = status;
|
|
220710
|
+
this.name = "GitLabApiError";
|
|
220711
|
+
}
|
|
220712
|
+
};
|
|
220713
|
+
DEFAULT_LIMIT3 = 30;
|
|
220714
|
+
MAX_LIMIT = 100;
|
|
220715
|
+
PAGE_SIZE = 100;
|
|
220716
|
+
GitLabClient = class {
|
|
220717
|
+
constructor(auth2, fetchImpl = fetch) {
|
|
220718
|
+
this.auth = auth2;
|
|
220719
|
+
this.fetchImpl = fetchImpl;
|
|
220720
|
+
}
|
|
220721
|
+
async request(path6, init) {
|
|
220722
|
+
const url2 = `${this.auth.apiBaseUrl.replace(/\/+$/u, "")}/${path6.replace(/^\/+/, "")}`;
|
|
220723
|
+
const response = await this.fetchImpl(url2, {
|
|
220724
|
+
...init,
|
|
220725
|
+
headers: {
|
|
220726
|
+
Accept: "application/json",
|
|
220727
|
+
[this.auth.headerName]: this.auth.token,
|
|
220728
|
+
...init?.body ? { "Content-Type": "application/json" } : {},
|
|
220729
|
+
...init?.headers ?? {}
|
|
220730
|
+
}
|
|
220731
|
+
});
|
|
220732
|
+
if (!response.ok) {
|
|
220733
|
+
throw new GitLabApiError(response.status, mapGitLabError(response.status));
|
|
220734
|
+
}
|
|
220735
|
+
if (response.status === 204) return void 0;
|
|
220736
|
+
return await response.json();
|
|
220737
|
+
}
|
|
220738
|
+
async listPaginated(path6, options, normalize8) {
|
|
220739
|
+
const limit = clampLimit(options.limit);
|
|
220740
|
+
const out = [];
|
|
220741
|
+
let page = 1;
|
|
220742
|
+
while (out.length < limit && page <= Math.ceil(limit / PAGE_SIZE) + 1) {
|
|
220743
|
+
const params = new URLSearchParams();
|
|
220744
|
+
params.set("per_page", String(Math.min(PAGE_SIZE, limit - out.length)));
|
|
220745
|
+
params.set("page", String(page));
|
|
220746
|
+
appendListParams(params, options);
|
|
220747
|
+
const rows = await this.request(`${path6}?${params.toString()}`);
|
|
220748
|
+
if (!Array.isArray(rows) || rows.length === 0) break;
|
|
220749
|
+
out.push(...rows.map(normalize8));
|
|
220750
|
+
if (rows.length < Number(params.get("per_page"))) break;
|
|
220751
|
+
page += 1;
|
|
220752
|
+
}
|
|
220753
|
+
return out.slice(0, limit);
|
|
220754
|
+
}
|
|
220755
|
+
listProjectIssues(project, options = {}) {
|
|
220756
|
+
return this.listPaginated(`projects/${encodeGitLabPathId(project)}/issues`, options, (raw) => normalizeIssue(raw, "project_issue"));
|
|
220757
|
+
}
|
|
220758
|
+
listGroupIssues(group, options = {}) {
|
|
220759
|
+
return this.listPaginated(`groups/${encodeGitLabPathId(group)}/issues`, options, (raw) => normalizeIssue(raw, "group_issue", {
|
|
220760
|
+
groupPath: typeof group === "string" ? group : void 0,
|
|
220761
|
+
groupId: typeof group === "number" ? group : void 0
|
|
220762
|
+
}));
|
|
220763
|
+
}
|
|
220764
|
+
listMergeRequests(project, options = {}) {
|
|
220765
|
+
return this.listPaginated(`projects/${encodeGitLabPathId(project)}/merge_requests`, options, normalizeMergeRequest);
|
|
220766
|
+
}
|
|
220767
|
+
async getProjectIssue(project, iid) {
|
|
220768
|
+
return normalizeIssue(await this.request(`projects/${encodeGitLabPathId(project)}/issues/${iid}`), "project_issue");
|
|
220769
|
+
}
|
|
220770
|
+
async getMergeRequest(project, iid) {
|
|
220771
|
+
return normalizeMergeRequest(await this.request(`projects/${encodeGitLabPathId(project)}/merge_requests/${iid}`));
|
|
220772
|
+
}
|
|
220773
|
+
/*
|
|
220774
|
+
FNXC:GitLabLifecycle 2026-07-02-00:00:
|
|
220775
|
+
GitLab lifecycle side effects must use REST notes and state_event APIs for GitLab.com and self-managed instances. Keep project identifiers URL-encoded, token auth header-based, and never introduce a local GitLab CLI dependency.
|
|
220776
|
+
*/
|
|
220777
|
+
async commentOnProjectIssue(project, iid, body) {
|
|
220778
|
+
await this.request(`projects/${encodeGitLabPathId(project)}/issues/${iid}/notes`, {
|
|
220779
|
+
method: "POST",
|
|
220780
|
+
body: JSON.stringify({ body })
|
|
220781
|
+
});
|
|
220782
|
+
}
|
|
220783
|
+
async commentOnMergeRequest(project, iid, body) {
|
|
220784
|
+
await this.request(`projects/${encodeGitLabPathId(project)}/merge_requests/${iid}/notes`, {
|
|
220785
|
+
method: "POST",
|
|
220786
|
+
body: JSON.stringify({ body })
|
|
220787
|
+
});
|
|
220788
|
+
}
|
|
220789
|
+
async setProjectIssueState(project, iid, state) {
|
|
220790
|
+
const params = new URLSearchParams({ state_event: state === "closed" ? "close" : "reopen" });
|
|
220791
|
+
return normalizeIssue(await this.request(`projects/${encodeGitLabPathId(project)}/issues/${iid}?${params.toString()}`, { method: "PUT" }), "project_issue");
|
|
220792
|
+
}
|
|
220793
|
+
async setMergeRequestState(project, iid, state) {
|
|
220794
|
+
const params = new URLSearchParams({ state_event: state === "closed" ? "close" : "reopen" });
|
|
220795
|
+
return normalizeMergeRequest(await this.request(`projects/${encodeGitLabPathId(project)}/merge_requests/${iid}?${params.toString()}`, { method: "PUT" }));
|
|
220796
|
+
}
|
|
220797
|
+
};
|
|
220798
|
+
}
|
|
220799
|
+
});
|
|
220800
|
+
|
|
220801
|
+
// ../dashboard/src/gitlab-auth.ts
|
|
220802
|
+
function pickString2(source, key) {
|
|
220803
|
+
const value = source?.[key];
|
|
220804
|
+
return typeof value === "string" ? value : void 0;
|
|
220805
|
+
}
|
|
220806
|
+
function readConfiguredString2(value) {
|
|
220807
|
+
if (typeof value !== "string") return void 0;
|
|
220808
|
+
const trimmed = value.trim();
|
|
220809
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
220810
|
+
}
|
|
220811
|
+
function normalizeTokenType(value) {
|
|
220812
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
220813
|
+
if (typeof value !== "string") return "invalid";
|
|
220814
|
+
const trimmed = value.trim();
|
|
220815
|
+
if (trimmed === "") return void 0;
|
|
220816
|
+
return GITLAB_AUTH_TOKEN_TYPES.includes(trimmed) ? trimmed : "invalid";
|
|
220817
|
+
}
|
|
220818
|
+
function firstConfiguredTokenType(...values) {
|
|
220819
|
+
for (const value of values) {
|
|
220820
|
+
const normalized = normalizeTokenType(value);
|
|
220821
|
+
if (normalized !== void 0) return normalized;
|
|
220822
|
+
}
|
|
220823
|
+
return void 0;
|
|
220824
|
+
}
|
|
220825
|
+
function resolveGitlabAuth(deps = {}) {
|
|
220826
|
+
if (!resolveGitlabEnabled({ project: deps.projectSettings ?? void 0, global: deps.globalSettings })) {
|
|
220827
|
+
return { ok: false, reason: "disabled", message: "GitLab integration is disabled in Settings." };
|
|
220828
|
+
}
|
|
220829
|
+
let config2;
|
|
220830
|
+
try {
|
|
220831
|
+
config2 = resolveGitlabConfig({
|
|
220832
|
+
project: deps.projectSettings ?? void 0,
|
|
220833
|
+
global: deps.globalSettings
|
|
220834
|
+
});
|
|
220835
|
+
} catch (error2) {
|
|
220836
|
+
return {
|
|
220837
|
+
ok: false,
|
|
220838
|
+
reason: "invalid_config",
|
|
220839
|
+
message: error2 instanceof Error ? error2.message : "Invalid GitLab configuration."
|
|
220840
|
+
};
|
|
220841
|
+
}
|
|
220842
|
+
const project = deps.projectSettings;
|
|
220843
|
+
const global2 = deps.globalSettings;
|
|
220844
|
+
const env = deps.env ?? process.env;
|
|
220845
|
+
const token = readConfiguredString2(project?.gitlabAuthToken) ?? readConfiguredString2(global2?.gitlabAuthToken) ?? readConfiguredString2(pickString2(global2, "projectGitlabAuthToken")) ?? readConfiguredString2(env.GITLAB_TOKEN) ?? "";
|
|
220846
|
+
const tokenType = firstConfiguredTokenType(
|
|
220847
|
+
project?.gitlabAuthTokenType,
|
|
220848
|
+
global2?.gitlabAuthTokenType,
|
|
220849
|
+
pickString2(global2, "projectGitlabAuthTokenType")
|
|
220850
|
+
);
|
|
220851
|
+
if (tokenType === "invalid") {
|
|
220852
|
+
return {
|
|
220853
|
+
ok: false,
|
|
220854
|
+
reason: "invalid_token_type",
|
|
220855
|
+
message: 'Invalid gitlabAuthTokenType. Expected "personal", "project", or "group".'
|
|
220856
|
+
};
|
|
220857
|
+
}
|
|
220858
|
+
if (!token) {
|
|
220859
|
+
return {
|
|
220860
|
+
ok: false,
|
|
220861
|
+
reason: "token_missing",
|
|
220862
|
+
message: "GitLab auth requires gitlabAuthToken or GITLAB_TOKEN."
|
|
220863
|
+
};
|
|
220864
|
+
}
|
|
220865
|
+
return {
|
|
220866
|
+
ok: true,
|
|
220867
|
+
auth: {
|
|
220868
|
+
apiBaseUrl: config2.apiBaseUrl,
|
|
220869
|
+
webBaseUrl: config2.instanceUrl,
|
|
220870
|
+
token,
|
|
220871
|
+
tokenType: tokenType ?? "personal",
|
|
220872
|
+
headerName: GITLAB_AUTH_HEADER_NAME
|
|
220873
|
+
}
|
|
220874
|
+
};
|
|
220875
|
+
}
|
|
220876
|
+
var GITLAB_AUTH_HEADER_NAME, GITLAB_AUTH_TOKEN_TYPES;
|
|
220877
|
+
var init_gitlab_auth = __esm({
|
|
220878
|
+
"../dashboard/src/gitlab-auth.ts"() {
|
|
220879
|
+
"use strict";
|
|
220880
|
+
init_src();
|
|
220881
|
+
GITLAB_AUTH_HEADER_NAME = "PRIVATE-TOKEN";
|
|
220882
|
+
GITLAB_AUTH_TOKEN_TYPES = ["personal", "project", "group"];
|
|
220883
|
+
}
|
|
220884
|
+
});
|
|
220885
|
+
|
|
220886
|
+
// ../dashboard/src/gitlab-lifecycle.ts
|
|
220887
|
+
var init_gitlab_lifecycle = __esm({
|
|
220888
|
+
"../dashboard/src/gitlab-lifecycle.ts"() {
|
|
220889
|
+
"use strict";
|
|
220890
|
+
init_gitlab();
|
|
220891
|
+
init_gitlab_auth();
|
|
220892
|
+
}
|
|
220893
|
+
});
|
|
220894
|
+
|
|
220895
|
+
// ../dashboard/src/gitlab-issue-comment.ts
|
|
220896
|
+
var init_gitlab_issue_comment = __esm({
|
|
220897
|
+
"../dashboard/src/gitlab-issue-comment.ts"() {
|
|
220898
|
+
"use strict";
|
|
220899
|
+
init_gitlab_lifecycle();
|
|
220900
|
+
}
|
|
220901
|
+
});
|
|
220902
|
+
|
|
220903
|
+
// ../dashboard/src/gitlab-tracking-comments.ts
|
|
220904
|
+
var init_gitlab_tracking_comments = __esm({
|
|
220905
|
+
"../dashboard/src/gitlab-tracking-comments.ts"() {
|
|
220906
|
+
"use strict";
|
|
220907
|
+
init_gitlab_lifecycle();
|
|
220908
|
+
}
|
|
220909
|
+
});
|
|
220910
|
+
|
|
220911
|
+
// ../dashboard/src/gitlab-tracking-state.ts
|
|
220912
|
+
var init_gitlab_tracking_state = __esm({
|
|
220913
|
+
"../dashboard/src/gitlab-tracking-state.ts"() {
|
|
220914
|
+
"use strict";
|
|
220915
|
+
init_gitlab();
|
|
220916
|
+
init_gitlab_lifecycle();
|
|
220917
|
+
init_github_tracking_state();
|
|
220918
|
+
}
|
|
220919
|
+
});
|
|
220920
|
+
|
|
220921
|
+
// ../dashboard/src/gitlab-source-issue-close.ts
|
|
220922
|
+
var init_gitlab_source_issue_close = __esm({
|
|
220923
|
+
"../dashboard/src/gitlab-source-issue-close.ts"() {
|
|
220924
|
+
"use strict";
|
|
220925
|
+
init_gitlab_lifecycle();
|
|
220926
|
+
init_gitlab_tracking_state();
|
|
220927
|
+
init_github_tracking_state();
|
|
220928
|
+
}
|
|
220929
|
+
});
|
|
220930
|
+
|
|
219578
220931
|
// ../dashboard/src/knowledge-index.ts
|
|
219579
220932
|
var init_knowledge_index = __esm({
|
|
219580
220933
|
"../dashboard/src/knowledge-index.ts"() {
|
|
@@ -220244,6 +221597,10 @@ var init_register_git_github = __esm({
|
|
|
220244
221597
|
init_github_tracking_state();
|
|
220245
221598
|
init_github_tracking_reconciler();
|
|
220246
221599
|
init_github_source_issue_close();
|
|
221600
|
+
init_gitlab_issue_comment();
|
|
221601
|
+
init_gitlab_tracking_comments();
|
|
221602
|
+
init_gitlab_tracking_state();
|
|
221603
|
+
init_gitlab_source_issue_close();
|
|
220247
221604
|
init_knowledge_index_refresh();
|
|
220248
221605
|
init_github_poll();
|
|
220249
221606
|
init_project_store_resolver();
|
|
@@ -225194,6 +226551,25 @@ var init_register_messaging_scripts = __esm({
|
|
|
225194
226551
|
}
|
|
225195
226552
|
});
|
|
225196
226553
|
|
|
226554
|
+
// ../dashboard/src/gitlab-source-issue-reconciler.ts
|
|
226555
|
+
var init_gitlab_source_issue_reconciler = __esm({
|
|
226556
|
+
"../dashboard/src/gitlab-source-issue-reconciler.ts"() {
|
|
226557
|
+
"use strict";
|
|
226558
|
+
init_gitlab_lifecycle();
|
|
226559
|
+
}
|
|
226560
|
+
});
|
|
226561
|
+
|
|
226562
|
+
// ../dashboard/src/routes/register-gitlab.ts
|
|
226563
|
+
var init_register_gitlab = __esm({
|
|
226564
|
+
"../dashboard/src/routes/register-gitlab.ts"() {
|
|
226565
|
+
"use strict";
|
|
226566
|
+
init_api_error();
|
|
226567
|
+
init_gitlab_auth();
|
|
226568
|
+
init_gitlab();
|
|
226569
|
+
init_gitlab_source_issue_reconciler();
|
|
226570
|
+
}
|
|
226571
|
+
});
|
|
226572
|
+
|
|
225197
226573
|
// ../dashboard/src/terminal.ts
|
|
225198
226574
|
import { spawn as spawn14 } from "node:child_process";
|
|
225199
226575
|
import { randomUUID as randomUUID26 } from "node:crypto";
|
|
@@ -225667,7 +227043,7 @@ var init_register_secrets_sync_inbound_routes = __esm({
|
|
|
225667
227043
|
});
|
|
225668
227044
|
|
|
225669
227045
|
// ../dashboard/src/routes/register-agent-core-routes.ts
|
|
225670
|
-
var MAX_AVATAR_BYTES;
|
|
227046
|
+
var MAX_AVATAR_BYTES, VALID_AGENT_PERMISSION_KEYS;
|
|
225671
227047
|
var init_register_agent_core_routes = __esm({
|
|
225672
227048
|
"../dashboard/src/routes/register-agent-core-routes.ts"() {
|
|
225673
227049
|
"use strict";
|
|
@@ -225675,6 +227051,7 @@ var init_register_agent_core_routes = __esm({
|
|
|
225675
227051
|
init_api_error();
|
|
225676
227052
|
init_src4();
|
|
225677
227053
|
MAX_AVATAR_BYTES = 2 * 1024 * 1024;
|
|
227054
|
+
VALID_AGENT_PERMISSION_KEYS = new Set(AGENT_PERMISSIONS);
|
|
225678
227055
|
}
|
|
225679
227056
|
});
|
|
225680
227057
|
|
|
@@ -225896,6 +227273,14 @@ var init_pagerduty = __esm({
|
|
|
225896
227273
|
}
|
|
225897
227274
|
});
|
|
225898
227275
|
|
|
227276
|
+
// ../dashboard/src/signal-sources/gitlab.ts
|
|
227277
|
+
var init_gitlab2 = __esm({
|
|
227278
|
+
"../dashboard/src/signal-sources/gitlab.ts"() {
|
|
227279
|
+
"use strict";
|
|
227280
|
+
init_signal_source();
|
|
227281
|
+
}
|
|
227282
|
+
});
|
|
227283
|
+
|
|
225899
227284
|
// ../dashboard/src/routes/register-signal-routes.ts
|
|
225900
227285
|
var init_register_signal_routes = __esm({
|
|
225901
227286
|
"../dashboard/src/routes/register-signal-routes.ts"() {
|
|
@@ -225907,6 +227292,7 @@ var init_register_signal_routes = __esm({
|
|
|
225907
227292
|
init_sentry();
|
|
225908
227293
|
init_datadog();
|
|
225909
227294
|
init_pagerduty();
|
|
227295
|
+
init_gitlab2();
|
|
225910
227296
|
}
|
|
225911
227297
|
});
|
|
225912
227298
|
|
|
@@ -225979,17 +227365,32 @@ var init_droid_cli_probe = __esm({
|
|
|
225979
227365
|
|
|
225980
227366
|
// ../../plugins/fusion-plugin-cursor-runtime/src/cli-spawn.ts
|
|
225981
227367
|
import { spawn as spawn15 } from "node:child_process";
|
|
227368
|
+
function formatSpawnError(error2) {
|
|
227369
|
+
const code = typeof error2.code === "string" ? `${error2.code}: ` : "";
|
|
227370
|
+
return `spawn error: ${code}${error2.message}`.trim();
|
|
227371
|
+
}
|
|
225982
227372
|
async function runCursorCommand(binary, args, timeoutMs) {
|
|
225983
227373
|
return new Promise((resolve34) => {
|
|
225984
|
-
const child = spawn15(binary, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
225985
227374
|
let stdout = "";
|
|
225986
227375
|
let stderr = "";
|
|
225987
|
-
|
|
227376
|
+
let settled = false;
|
|
227377
|
+
let timer;
|
|
227378
|
+
const finish = (result) => {
|
|
227379
|
+
if (settled) return;
|
|
227380
|
+
settled = true;
|
|
227381
|
+
if (timer) clearTimeout(timer);
|
|
227382
|
+
resolve34(result);
|
|
227383
|
+
};
|
|
227384
|
+
const child = spawn15(binary, args, {
|
|
227385
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
227386
|
+
shell: process.platform === "win32"
|
|
227387
|
+
});
|
|
227388
|
+
timer = setTimeout(() => {
|
|
225988
227389
|
try {
|
|
225989
227390
|
child.kill("SIGKILL");
|
|
225990
227391
|
} catch {
|
|
225991
227392
|
}
|
|
225992
|
-
|
|
227393
|
+
finish({ code: 124, stdout, stderr });
|
|
225993
227394
|
}, timeoutMs);
|
|
225994
227395
|
child.stdout?.on("data", (c) => {
|
|
225995
227396
|
stdout += c.toString("utf-8");
|
|
@@ -225997,13 +227398,14 @@ async function runCursorCommand(binary, args, timeoutMs) {
|
|
|
225997
227398
|
child.stderr?.on("data", (c) => {
|
|
225998
227399
|
stderr += c.toString("utf-8");
|
|
225999
227400
|
});
|
|
226000
|
-
child.once("error", () => {
|
|
226001
|
-
|
|
226002
|
-
|
|
227401
|
+
child.once("error", (error2) => {
|
|
227402
|
+
const diagnostic = formatSpawnError(error2);
|
|
227403
|
+
stderr = stderr ? `${stderr}
|
|
227404
|
+
${diagnostic}` : diagnostic;
|
|
227405
|
+
finish({ code: 127, stdout, stderr });
|
|
226003
227406
|
});
|
|
226004
227407
|
child.once("close", (code) => {
|
|
226005
|
-
|
|
226006
|
-
resolve34({ code, stdout, stderr });
|
|
227408
|
+
finish({ code, stdout, stderr });
|
|
226007
227409
|
});
|
|
226008
227410
|
});
|
|
226009
227411
|
}
|
|
@@ -226014,20 +227416,40 @@ var init_cli_spawn2 = __esm({
|
|
|
226014
227416
|
});
|
|
226015
227417
|
|
|
226016
227418
|
// ../../plugins/fusion-plugin-cursor-runtime/src/probe.ts
|
|
227419
|
+
function buildCandidates(binaryPath) {
|
|
227420
|
+
const configuredBinaryPath = binaryPath?.trim() || void 0;
|
|
227421
|
+
const ordered = configuredBinaryPath ? [configuredBinaryPath, ...CANDIDATES2] : [...CANDIDATES2];
|
|
227422
|
+
return { candidates: Array.from(new Set(ordered)), configuredBinaryPath };
|
|
227423
|
+
}
|
|
227424
|
+
function summarizeFailure(binary, stdout, stderr) {
|
|
227425
|
+
const detail = `${stderr || stdout}`.replace(/\s+/g, " ").trim();
|
|
227426
|
+
if (!detail) return void 0;
|
|
227427
|
+
const truncated = detail.length > MAX_FAILURE_DETAIL_LENGTH ? `${detail.slice(0, MAX_FAILURE_DETAIL_LENGTH - 1)}\u2026` : detail;
|
|
227428
|
+
return `${binary}: ${truncated}`;
|
|
227429
|
+
}
|
|
226017
227430
|
async function probeCursorBinary(options) {
|
|
226018
227431
|
const startedAt = Date.now();
|
|
226019
227432
|
const timeoutMs = options?.timeoutMs ?? 3e3;
|
|
226020
|
-
const candidates = options?.binaryPath
|
|
227433
|
+
const { candidates, configuredBinaryPath } = buildCandidates(options?.binaryPath);
|
|
227434
|
+
const failureDetails = [];
|
|
226021
227435
|
for (const binary of candidates) {
|
|
226022
227436
|
const version2 = await runCursorCommand(binary, ["--version"], timeoutMs);
|
|
227437
|
+
const failureDetail = summarizeFailure(binary, version2.stdout, version2.stderr);
|
|
227438
|
+
if (failureDetail) failureDetails.push(failureDetail);
|
|
227439
|
+
const common = {
|
|
227440
|
+
binaryName: binary,
|
|
227441
|
+
binaryPath: binary,
|
|
227442
|
+
configuredBinaryPath,
|
|
227443
|
+
usingConfiguredBinaryPath: configuredBinaryPath === binary,
|
|
227444
|
+
diagnostics: failureDetails.length > 0 ? [...failureDetails] : void 0,
|
|
227445
|
+
probeDurationMs: Date.now() - startedAt
|
|
227446
|
+
};
|
|
226023
227447
|
if (version2.code === 0) {
|
|
226024
227448
|
return {
|
|
226025
227449
|
available: true,
|
|
226026
227450
|
authenticated: true,
|
|
226027
|
-
|
|
226028
|
-
|
|
226029
|
-
version: version2.stdout.trim() || void 0,
|
|
226030
|
-
probeDurationMs: Date.now() - startedAt
|
|
227451
|
+
...common,
|
|
227452
|
+
version: version2.stdout.trim() || void 0
|
|
226031
227453
|
};
|
|
226032
227454
|
}
|
|
226033
227455
|
const combined = `${version2.stdout}
|
|
@@ -226036,36 +227458,37 @@ ${version2.stderr}`.toLowerCase();
|
|
|
226036
227458
|
return {
|
|
226037
227459
|
available: true,
|
|
226038
227460
|
authenticated: false,
|
|
226039
|
-
|
|
226040
|
-
|
|
226041
|
-
reason: "macOS login keychain is locked",
|
|
226042
|
-
probeDurationMs: Date.now() - startedAt
|
|
227461
|
+
...common,
|
|
227462
|
+
reason: "macOS login keychain is locked"
|
|
226043
227463
|
};
|
|
226044
227464
|
}
|
|
226045
227465
|
if (combined.includes("no cursor ide installation found")) {
|
|
226046
227466
|
return {
|
|
226047
227467
|
available: true,
|
|
226048
227468
|
authenticated: false,
|
|
226049
|
-
|
|
226050
|
-
|
|
226051
|
-
reason: "Cursor IDE installation not found",
|
|
226052
|
-
probeDurationMs: Date.now() - startedAt
|
|
227469
|
+
...common,
|
|
227470
|
+
reason: "Cursor IDE installation not found"
|
|
226053
227471
|
};
|
|
226054
227472
|
}
|
|
226055
227473
|
}
|
|
227474
|
+
const baseReason = configuredBinaryPath ? `Configured Cursor CLI binary '${configuredBinaryPath}' failed; PATH fallback cursor-agent/cursor also failed` : "cursor-agent/cursor not found on PATH";
|
|
226056
227475
|
return {
|
|
226057
227476
|
available: false,
|
|
226058
227477
|
authenticated: false,
|
|
226059
|
-
|
|
227478
|
+
configuredBinaryPath,
|
|
227479
|
+
usingConfiguredBinaryPath: false,
|
|
227480
|
+
diagnostics: failureDetails.length > 0 ? failureDetails : void 0,
|
|
227481
|
+
reason: failureDetails.length > 0 ? `${baseReason} (${failureDetails.join("; ")})` : baseReason,
|
|
226060
227482
|
probeDurationMs: Date.now() - startedAt
|
|
226061
227483
|
};
|
|
226062
227484
|
}
|
|
226063
|
-
var CANDIDATES2;
|
|
227485
|
+
var CANDIDATES2, MAX_FAILURE_DETAIL_LENGTH;
|
|
226064
227486
|
var init_probe4 = __esm({
|
|
226065
227487
|
"../../plugins/fusion-plugin-cursor-runtime/src/probe.ts"() {
|
|
226066
227488
|
"use strict";
|
|
226067
227489
|
init_cli_spawn2();
|
|
226068
227490
|
CANDIDATES2 = ["cursor-agent", "cursor"];
|
|
227491
|
+
MAX_FAILURE_DETAIL_LENGTH = 180;
|
|
226069
227492
|
}
|
|
226070
227493
|
});
|
|
226071
227494
|
|
|
@@ -226109,12 +227532,20 @@ var init_process_manager = __esm({
|
|
|
226109
227532
|
});
|
|
226110
227533
|
|
|
226111
227534
|
// ../../plugins/fusion-plugin-cursor-runtime/src/provider.ts
|
|
226112
|
-
|
|
226113
|
-
|
|
227535
|
+
function normalizeDiscoveryOptions(options) {
|
|
227536
|
+
if (!options || typeof options !== "object") return {};
|
|
227537
|
+
const record2 = options;
|
|
227538
|
+
return {
|
|
227539
|
+
binaryPath: typeof record2.binaryPath === "string" ? record2.binaryPath : void 0,
|
|
227540
|
+
timeoutMs: typeof record2.timeoutMs === "number" ? record2.timeoutMs : void 0
|
|
227541
|
+
};
|
|
227542
|
+
}
|
|
227543
|
+
async function discoverCursorProviderModels(options) {
|
|
227544
|
+
const probe = await probeCursorBinary(normalizeDiscoveryOptions(options));
|
|
226114
227545
|
if (!probe.available || !probe.binaryName) {
|
|
226115
227546
|
return { models: [], source: "probe", fallbackUsed: true, reason: probe.reason ?? "binary unavailable" };
|
|
226116
227547
|
}
|
|
226117
|
-
const result = await discoverCursorModels(probe.binaryName);
|
|
227548
|
+
const result = await discoverCursorModels(probe.binaryPath ?? probe.binaryName);
|
|
226118
227549
|
return {
|
|
226119
227550
|
models: result.models.map((id) => ({ id, label: id })),
|
|
226120
227551
|
source: result.source,
|
|
@@ -229072,6 +230503,7 @@ var init_routes = __esm({
|
|
|
229072
230503
|
init_register_secrets_routes();
|
|
229073
230504
|
init_register_messaging_scripts();
|
|
229074
230505
|
init_register_git_github();
|
|
230506
|
+
init_register_gitlab();
|
|
229075
230507
|
init_register_files_terminal_workspaces();
|
|
229076
230508
|
init_register_agents_projects_nodes();
|
|
229077
230509
|
init_register_project_routes();
|
|
@@ -229356,7 +230788,7 @@ var init_otel_exporter = __esm({
|
|
|
229356
230788
|
|
|
229357
230789
|
// ../dashboard/src/server.ts
|
|
229358
230790
|
import express2 from "express";
|
|
229359
|
-
import { join as join64, dirname as
|
|
230791
|
+
import { join as join64, dirname as dirname29 } from "node:path";
|
|
229360
230792
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
229361
230793
|
import { WebSocketServer as WebSocketServer2 } from "ws";
|
|
229362
230794
|
function clearAiSessionCleanupInterval() {
|
|
@@ -229402,7 +230834,7 @@ var init_server2 = __esm({
|
|
|
229402
230834
|
init_reliability_metrics();
|
|
229403
230835
|
init_view_chunk_manifest();
|
|
229404
230836
|
init_otel_exporter();
|
|
229405
|
-
__dirname =
|
|
230837
|
+
__dirname = dirname29(fileURLToPath6(import.meta.url));
|
|
229406
230838
|
MIN_AI_SESSION_TTL_MS = 10 * 60 * 1e3;
|
|
229407
230839
|
MAX_AI_SESSION_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
229408
230840
|
MIN_AI_SESSION_CLEANUP_INTERVAL_MS = 60 * 1e3;
|
|
@@ -229470,6 +230902,8 @@ var init_src11 = __esm({
|
|
|
229470
230902
|
init_github_tracking2();
|
|
229471
230903
|
init_github_tracking_dedup();
|
|
229472
230904
|
init_github_tracking_hook();
|
|
230905
|
+
init_gitlab();
|
|
230906
|
+
init_gitlab_auth();
|
|
229473
230907
|
init_github_auth();
|
|
229474
230908
|
init_rate_limit();
|
|
229475
230909
|
init_github_poll();
|
|
@@ -229493,7 +230927,7 @@ var init_src11 = __esm({
|
|
|
229493
230927
|
});
|
|
229494
230928
|
|
|
229495
230929
|
// src/project-context.ts
|
|
229496
|
-
import { resolve as resolve32, dirname as
|
|
230930
|
+
import { resolve as resolve32, dirname as dirname30, basename as basename17 } from "node:path";
|
|
229497
230931
|
async function resolveProject(projectNameFlag, cwd = process.cwd(), globalDir) {
|
|
229498
230932
|
const central = new CentralCore(globalDir);
|
|
229499
230933
|
await central.init();
|
|
@@ -229586,7 +231020,7 @@ async function detectProjectFromCwd(cwd, central) {
|
|
|
229586
231020
|
};
|
|
229587
231021
|
}
|
|
229588
231022
|
}
|
|
229589
|
-
const parentDir =
|
|
231023
|
+
const parentDir = dirname30(currentDir);
|
|
229590
231024
|
if (parentDir === currentDir) {
|
|
229591
231025
|
break;
|
|
229592
231026
|
}
|
|
@@ -229818,6 +231252,7 @@ __export(task_exports, {
|
|
|
229818
231252
|
runTaskDeps: () => runTaskDeps,
|
|
229819
231253
|
runTaskDuplicate: () => runTaskDuplicate,
|
|
229820
231254
|
runTaskImportFromGitHub: () => runTaskImportFromGitHub,
|
|
231255
|
+
runTaskImportFromGitLab: () => runTaskImportFromGitLab,
|
|
229821
231256
|
runTaskImportGitHubInteractive: () => runTaskImportGitHubInteractive,
|
|
229822
231257
|
runTaskList: () => runTaskList,
|
|
229823
231258
|
runTaskLog: () => runTaskLog,
|
|
@@ -229889,6 +231324,8 @@ function formatTaskSource(task) {
|
|
|
229889
231324
|
const issueUrl = getGitHubIssueUrl(task.sourceMetadata);
|
|
229890
231325
|
return issueUrl ? `GitHub Import (${issueUrl})` : "GitHub Import";
|
|
229891
231326
|
}
|
|
231327
|
+
case "gitlab_import":
|
|
231328
|
+
return "GitLab Import";
|
|
229892
231329
|
case "research": {
|
|
229893
231330
|
const context = getResearchSourceContext(task.sourceMetadata);
|
|
229894
231331
|
return context ? `Research (${context})` : "Research";
|
|
@@ -230939,6 +232376,55 @@ Source: ${issue2.html_url}`;
|
|
|
230939
232376
|
console.log(` \u2713 Imported ${created} tasks from ${owner}/${repo}${skipped > 0 ? ` (${skipped} skipped)` : ""}`);
|
|
230940
232377
|
console.log();
|
|
230941
232378
|
}
|
|
232379
|
+
async function createGitLabClientForStore(store) {
|
|
232380
|
+
const projectSettings = await store.getSettings();
|
|
232381
|
+
const globalSettings = await store.getGlobalSettingsStore().getSettings();
|
|
232382
|
+
const auth2 = resolveGitlabAuth({ projectSettings, globalSettings });
|
|
232383
|
+
if (!auth2.ok) throw new Error(auth2.message);
|
|
232384
|
+
return new GitLabClient(auth2.auth);
|
|
232385
|
+
}
|
|
232386
|
+
async function runTaskImportFromGitLab(target, options = {}, projectName) {
|
|
232387
|
+
const resource = options.resource ?? "project-issues";
|
|
232388
|
+
const store = await getStore(projectName);
|
|
232389
|
+
const client = await createGitLabClientForStore(store);
|
|
232390
|
+
console.log(`
|
|
232391
|
+
Importing GitLab ${resource} from ${target}...
|
|
232392
|
+
`);
|
|
232393
|
+
const labelArray = options.labels ?? [];
|
|
232394
|
+
const items = resource === "project-issues" ? await client.listProjectIssues(target, { limit: options.limit, labels: labelArray }) : resource === "group-issues" ? await client.listGroupIssues(target, { limit: options.limit, labels: labelArray }) : await client.listMergeRequests(target, { limit: options.limit, labels: labelArray });
|
|
232395
|
+
if (items.length === 0) {
|
|
232396
|
+
console.log(" No GitLab resources found.\n");
|
|
232397
|
+
return;
|
|
232398
|
+
}
|
|
232399
|
+
const existingTasks = await store.listTasks({ slim: false, includeArchived: false });
|
|
232400
|
+
let created = 0;
|
|
232401
|
+
let skipped = 0;
|
|
232402
|
+
for (const item of items) {
|
|
232403
|
+
const provenance = buildGitLabTaskProvenance({ auth: client.auth, resourceType: resource === "merge-requests" ? "merge_request" : resource === "group-issues" ? "group_issue" : "project_issue", item, projectInput: resource !== "group-issues" ? target : void 0, groupInput: resource === "group-issues" ? target : void 0 });
|
|
232404
|
+
if (existingTasks.some((task2) => isGitLabAlreadyImported(task2, provenance))) {
|
|
232405
|
+
console.log(` \u2192 Skipping #${item.iid}: already imported`);
|
|
232406
|
+
skipped += 1;
|
|
232407
|
+
continue;
|
|
232408
|
+
}
|
|
232409
|
+
const title = resource === "merge-requests" ? `Review MR !${item.iid}: ${item.title.slice(0, 180)}` : item.title.slice(0, 200);
|
|
232410
|
+
const task = await store.createTask({
|
|
232411
|
+
title: title || void 0,
|
|
232412
|
+
description: buildGitLabTaskDescription(item),
|
|
232413
|
+
column: "triage",
|
|
232414
|
+
dependencies: [],
|
|
232415
|
+
sourceIssue: provenance.sourceIssue,
|
|
232416
|
+
gitlabTracking: provenance.gitlabTracking,
|
|
232417
|
+
source: { sourceType: "gitlab_import", sourceMetadata: provenance.sourceMetadata }
|
|
232418
|
+
});
|
|
232419
|
+
await store.logEntry(task.id, resource === "merge-requests" ? "Imported merge request from GitLab" : "Imported from GitLab", item.webUrl);
|
|
232420
|
+
existingTasks.push(task);
|
|
232421
|
+
created += 1;
|
|
232422
|
+
console.log(` \u2713 Created ${task.id}: ${task.title}`);
|
|
232423
|
+
}
|
|
232424
|
+
console.log(`
|
|
232425
|
+
\u2713 Imported ${created} GitLab tasks${skipped > 0 ? ` (${skipped} skipped)` : ""}
|
|
232426
|
+
`);
|
|
232427
|
+
}
|
|
230942
232428
|
async function runTaskComment(id, message, author = "user", projectName) {
|
|
230943
232429
|
const store = await getStore(projectName);
|
|
230944
232430
|
let text = message;
|
|
@@ -232837,6 +234323,107 @@ ${sourceUrl}`
|
|
|
232837
234323
|
};
|
|
232838
234324
|
}
|
|
232839
234325
|
});
|
|
234326
|
+
async function createGitLabClient(ctx) {
|
|
234327
|
+
const store = await getStore2(ctx.cwd);
|
|
234328
|
+
const projectSettings = await store.getSettings();
|
|
234329
|
+
const globalSettings = await store.getGlobalSettingsStore().getSettings();
|
|
234330
|
+
const auth2 = resolveGitlabAuth({ projectSettings, globalSettings });
|
|
234331
|
+
if (!auth2.ok) throw new Error(auth2.message);
|
|
234332
|
+
return { store, client: new GitLabClient(auth2.auth) };
|
|
234333
|
+
}
|
|
234334
|
+
async function importGitLabItems(ctx, resourceType, target, items) {
|
|
234335
|
+
const { store, client } = await createGitLabClient(ctx);
|
|
234336
|
+
const existingTasks = await store.listTasks({ slim: false, includeArchived: false });
|
|
234337
|
+
const createdTasks = [];
|
|
234338
|
+
for (const item of items) {
|
|
234339
|
+
const provenance = buildGitLabTaskProvenance({ auth: client.auth, resourceType, item, projectInput: resourceType !== "group_issue" ? target : void 0, groupInput: resourceType === "group_issue" ? target : void 0 });
|
|
234340
|
+
if (existingTasks.some((task2) => isGitLabAlreadyImported(task2, provenance))) continue;
|
|
234341
|
+
const title = resourceType === "merge_request" ? `Review MR !${item.iid}: ${item.title.slice(0, 180)}` : item.title.slice(0, 200);
|
|
234342
|
+
const task = await store.createTask({ title: title || void 0, description: buildGitLabTaskDescription(item), column: "triage", dependencies: [], sourceIssue: provenance.sourceIssue, gitlabTracking: provenance.gitlabTracking, source: { sourceType: "gitlab_import", sourceMetadata: provenance.sourceMetadata } });
|
|
234343
|
+
await store.logEntry(task.id, resourceType === "merge_request" ? "Imported merge request from GitLab" : "Imported from GitLab", item.webUrl);
|
|
234344
|
+
existingTasks.push(task);
|
|
234345
|
+
createdTasks.push({ id: task.id, title: task.title || item.title });
|
|
234346
|
+
}
|
|
234347
|
+
return createdTasks;
|
|
234348
|
+
}
|
|
234349
|
+
pi2.registerTool({
|
|
234350
|
+
name: "fn_task_browse_gitlab_project_issues",
|
|
234351
|
+
label: "fn: Browse GitLab Project Issues",
|
|
234352
|
+
description: "List GitLab project issues from the configured GitLab instance.",
|
|
234353
|
+
promptSnippet: "Browse GitLab project issues",
|
|
234354
|
+
parameters: Type9.Object({ project: Type9.String({ description: "GitLab project path or numeric ID" }), limit: Type9.Optional(Type9.Number({ minimum: 1, maximum: 100 })), labels: Type9.Optional(Type9.Array(Type9.String())) }),
|
|
234355
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
234356
|
+
const { client } = await createGitLabClient(ctx);
|
|
234357
|
+
const issues = await client.listProjectIssues(params.project, { limit: params.limit, labels: params.labels });
|
|
234358
|
+
return { content: [{ type: "text", text: issues.map((issue2) => `#${issue2.iid}: ${issue2.title}
|
|
234359
|
+
${issue2.webUrl}`).join("\n") || `No GitLab project issues found in ${params.project}.` }], details: { count: issues.length, issues } };
|
|
234360
|
+
}
|
|
234361
|
+
});
|
|
234362
|
+
pi2.registerTool({
|
|
234363
|
+
name: "fn_task_import_gitlab_project_issues",
|
|
234364
|
+
label: "fn: Import GitLab Project Issues",
|
|
234365
|
+
description: "Import GitLab project issues as Fusion tasks using configured GitLab HTTP API auth.",
|
|
234366
|
+
promptSnippet: "Import GitLab project issues",
|
|
234367
|
+
parameters: Type9.Object({ project: Type9.String({ description: "GitLab project path or numeric ID" }), limit: Type9.Optional(Type9.Number({ minimum: 1, maximum: 100 })), labels: Type9.Optional(Type9.Array(Type9.String())) }),
|
|
234368
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
234369
|
+
const { client } = await createGitLabClient(ctx);
|
|
234370
|
+
const issues = await client.listProjectIssues(params.project, { limit: params.limit, labels: params.labels });
|
|
234371
|
+
const createdTasks = await importGitLabItems(ctx, "project_issue", params.project, issues);
|
|
234372
|
+
return { content: [{ type: "text", text: `Imported ${createdTasks.length} GitLab project issue tasks.` }], details: { createdTasks } };
|
|
234373
|
+
}
|
|
234374
|
+
});
|
|
234375
|
+
pi2.registerTool({
|
|
234376
|
+
name: "fn_task_browse_gitlab_group_issues",
|
|
234377
|
+
label: "fn: Browse GitLab Group Issues",
|
|
234378
|
+
description: "List GitLab group issues while preserving each issue's originating project identity.",
|
|
234379
|
+
promptSnippet: "Browse GitLab group issues",
|
|
234380
|
+
parameters: Type9.Object({ group: Type9.String({ description: "GitLab group path or numeric ID" }), limit: Type9.Optional(Type9.Number({ minimum: 1, maximum: 100 })), labels: Type9.Optional(Type9.Array(Type9.String())) }),
|
|
234381
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
234382
|
+
const { client } = await createGitLabClient(ctx);
|
|
234383
|
+
const issues = await client.listGroupIssues(params.group, { limit: params.limit, labels: params.labels });
|
|
234384
|
+
return { content: [{ type: "text", text: issues.map((issue2) => `#${issue2.iid}: ${issue2.projectPath ?? issue2.projectId} \u2014 ${issue2.title}
|
|
234385
|
+
${issue2.webUrl}`).join("\n") || `No GitLab group issues found in ${params.group}.` }], details: { count: issues.length, issues } };
|
|
234386
|
+
}
|
|
234387
|
+
});
|
|
234388
|
+
pi2.registerTool({
|
|
234389
|
+
name: "fn_task_import_gitlab_group_issues",
|
|
234390
|
+
label: "fn: Import GitLab Group Issues",
|
|
234391
|
+
description: "Import GitLab group issues as Fusion tasks using each issue's originating project identity.",
|
|
234392
|
+
promptSnippet: "Import GitLab group issues",
|
|
234393
|
+
parameters: Type9.Object({ group: Type9.String({ description: "GitLab group path or numeric ID" }), limit: Type9.Optional(Type9.Number({ minimum: 1, maximum: 100 })), labels: Type9.Optional(Type9.Array(Type9.String())) }),
|
|
234394
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
234395
|
+
const { client } = await createGitLabClient(ctx);
|
|
234396
|
+
const issues = await client.listGroupIssues(params.group, { limit: params.limit, labels: params.labels });
|
|
234397
|
+
const createdTasks = await importGitLabItems(ctx, "group_issue", params.group, issues);
|
|
234398
|
+
return { content: [{ type: "text", text: `Imported ${createdTasks.length} GitLab group issue tasks.` }], details: { createdTasks } };
|
|
234399
|
+
}
|
|
234400
|
+
});
|
|
234401
|
+
pi2.registerTool({
|
|
234402
|
+
name: "fn_task_browse_gitlab_merge_requests",
|
|
234403
|
+
label: "fn: Browse GitLab Merge Requests",
|
|
234404
|
+
description: "List GitLab project merge requests from the configured GitLab instance.",
|
|
234405
|
+
promptSnippet: "Browse GitLab merge requests",
|
|
234406
|
+
parameters: Type9.Object({ project: Type9.String({ description: "GitLab project path or numeric ID" }), limit: Type9.Optional(Type9.Number({ minimum: 1, maximum: 100 })), labels: Type9.Optional(Type9.Array(Type9.String())) }),
|
|
234407
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
234408
|
+
const { client } = await createGitLabClient(ctx);
|
|
234409
|
+
const mergeRequests = await client.listMergeRequests(params.project, { limit: params.limit, labels: params.labels });
|
|
234410
|
+
return { content: [{ type: "text", text: mergeRequests.map((mr2) => `!${mr2.iid}: ${mr2.title}
|
|
234411
|
+
${mr2.webUrl}`).join("\n") || `No GitLab merge requests found in ${params.project}.` }], details: { count: mergeRequests.length, mergeRequests } };
|
|
234412
|
+
}
|
|
234413
|
+
});
|
|
234414
|
+
pi2.registerTool({
|
|
234415
|
+
name: "fn_task_import_gitlab_merge_requests",
|
|
234416
|
+
label: "fn: Import GitLab Merge Requests",
|
|
234417
|
+
description: "Import GitLab project merge requests as Fusion review tasks using configured GitLab HTTP API auth.",
|
|
234418
|
+
promptSnippet: "Import GitLab merge requests",
|
|
234419
|
+
parameters: Type9.Object({ project: Type9.String({ description: "GitLab project path or numeric ID" }), limit: Type9.Optional(Type9.Number({ minimum: 1, maximum: 100 })), labels: Type9.Optional(Type9.Array(Type9.String())) }),
|
|
234420
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
234421
|
+
const { client } = await createGitLabClient(ctx);
|
|
234422
|
+
const mergeRequests = await client.listMergeRequests(params.project, { limit: params.limit, labels: params.labels });
|
|
234423
|
+
const createdTasks = await importGitLabItems(ctx, "merge_request", params.project, mergeRequests);
|
|
234424
|
+
return { content: [{ type: "text", text: `Imported ${createdTasks.length} GitLab merge request tasks.` }], details: { createdTasks } };
|
|
234425
|
+
}
|
|
234426
|
+
});
|
|
232840
234427
|
pi2.registerTool({
|
|
232841
234428
|
name: "fn_task_plan",
|
|
232842
234429
|
label: "fn: Plan Task",
|
|
@@ -234777,6 +236364,214 @@ Status: ${updated.status}`
|
|
|
234777
236364
|
};
|
|
234778
236365
|
}
|
|
234779
236366
|
});
|
|
236367
|
+
pi2.registerTool({
|
|
236368
|
+
name: "fn_agent_update",
|
|
236369
|
+
label: "fn: Update Agent",
|
|
236370
|
+
description: "Update editable configuration for an existing non-ephemeral agent. Agent callers can only update direct or indirect reports inside their management subtree; user/operator calls are privileged.",
|
|
236371
|
+
promptSnippet: "Update an existing Fusion agent without deleting and recreating it",
|
|
236372
|
+
promptGuidelines: [
|
|
236373
|
+
"Use to update editable agent configuration such as role, instructions, manager, and heartbeat settings",
|
|
236374
|
+
"Agent callers can only target direct or indirect reports, never themselves, peers, ancestors, or unrelated agents",
|
|
236375
|
+
'Use reportsTo as an agent ID/name for a new manager; privileged user/operator calls may pass reportsTo: "" to clear the manager'
|
|
236376
|
+
],
|
|
236377
|
+
parameters: Type9.Object({
|
|
236378
|
+
agent_id: Type9.String({ description: "Target agent ID or name to update" }),
|
|
236379
|
+
name: Type9.Optional(Type9.String({ description: "New display name" })),
|
|
236380
|
+
role: Type9.Optional(Type9.Union([
|
|
236381
|
+
Type9.Literal("triage"),
|
|
236382
|
+
Type9.Literal("executor"),
|
|
236383
|
+
Type9.Literal("reviewer"),
|
|
236384
|
+
Type9.Literal("merger"),
|
|
236385
|
+
Type9.Literal("engineer"),
|
|
236386
|
+
Type9.Literal("custom")
|
|
236387
|
+
], { description: "Agent role/capability" })),
|
|
236388
|
+
title: Type9.Optional(Type9.String({ description: "Optional title shown for the agent" })),
|
|
236389
|
+
icon: Type9.Optional(Type9.String({ description: "Optional compact icon/emoji" })),
|
|
236390
|
+
soul: Type9.Optional(Type9.String({ description: "Agent personality/identity text", maxLength: 1e4 })),
|
|
236391
|
+
instructions_text: Type9.Optional(Type9.String({ description: "Inline custom instructions", maxLength: 5e4 })),
|
|
236392
|
+
instructions_path: Type9.Optional(Type9.String({ description: "Path to instructions markdown", maxLength: 500 })),
|
|
236393
|
+
heartbeat_procedure_path: Type9.Optional(Type9.String({ description: "Path to heartbeat procedure markdown", maxLength: 500 })),
|
|
236394
|
+
reportsTo: Type9.Optional(Type9.String({ description: "Manager agent ID/name. Pass empty string to clear for privileged user/operator calls." })),
|
|
236395
|
+
heartbeat_interval_ms: Type9.Optional(Type9.Number({ minimum: 1e3, description: "Heartbeat polling interval in ms" })),
|
|
236396
|
+
heartbeat_timeout_ms: Type9.Optional(Type9.Number({ minimum: 5e3, description: "Heartbeat timeout in ms" })),
|
|
236397
|
+
max_concurrent_runs: Type9.Optional(Type9.Number({ minimum: 1, description: "Max concurrent heartbeat runs" })),
|
|
236398
|
+
message_response_mode: Type9.Optional(Type9.Union([
|
|
236399
|
+
Type9.Literal("immediate"),
|
|
236400
|
+
Type9.Literal("on-heartbeat")
|
|
236401
|
+
], { description: "How agent responds to messages" }))
|
|
236402
|
+
}),
|
|
236403
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
236404
|
+
const { AgentStore: AgentStore2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
236405
|
+
const agentStore = new AgentStore2({ rootDir: getFusionDir(ctx.cwd) });
|
|
236406
|
+
await agentStore.init();
|
|
236407
|
+
const updateParamKeys = [
|
|
236408
|
+
"name",
|
|
236409
|
+
"role",
|
|
236410
|
+
"title",
|
|
236411
|
+
"icon",
|
|
236412
|
+
"soul",
|
|
236413
|
+
"instructions_text",
|
|
236414
|
+
"instructions_path",
|
|
236415
|
+
"heartbeat_procedure_path",
|
|
236416
|
+
"reportsTo",
|
|
236417
|
+
"heartbeat_interval_ms",
|
|
236418
|
+
"heartbeat_timeout_ms",
|
|
236419
|
+
"max_concurrent_runs",
|
|
236420
|
+
"message_response_mode"
|
|
236421
|
+
];
|
|
236422
|
+
const providedKeys = updateParamKeys.filter((key) => params[key] !== void 0);
|
|
236423
|
+
const invalid = (field, message, extra = {}) => ({
|
|
236424
|
+
content: [{ type: "text", text: `ERROR: ${message}` }],
|
|
236425
|
+
isError: true,
|
|
236426
|
+
details: { outcome: "invalid", field, error: message, ...extra }
|
|
236427
|
+
});
|
|
236428
|
+
const denied = (message, extra = {}) => ({
|
|
236429
|
+
content: [{ type: "text", text: `ERROR: ${message}` }],
|
|
236430
|
+
isError: true,
|
|
236431
|
+
details: { outcome: "denied", error: message, ...extra }
|
|
236432
|
+
});
|
|
236433
|
+
if (providedKeys.length === 0) {
|
|
236434
|
+
return invalid("fields", "Provide at least one field to update");
|
|
236435
|
+
}
|
|
236436
|
+
if (params.soul !== void 0 && params.soul.length > 1e4) {
|
|
236437
|
+
return invalid("soul", "soul exceeds 10000 character limit");
|
|
236438
|
+
}
|
|
236439
|
+
if (params.instructions_text !== void 0 && params.instructions_text.length > 5e4) {
|
|
236440
|
+
return invalid("instructions_text", "instructions_text exceeds 50000 character limit");
|
|
236441
|
+
}
|
|
236442
|
+
if (params.instructions_path !== void 0 && params.instructions_path.length > 500) {
|
|
236443
|
+
return invalid("instructions_path", "instructions_path exceeds 500 character limit");
|
|
236444
|
+
}
|
|
236445
|
+
if (params.heartbeat_procedure_path !== void 0 && params.heartbeat_procedure_path.length > 500) {
|
|
236446
|
+
return invalid("heartbeat_procedure_path", "heartbeat_procedure_path exceeds 500 character limit");
|
|
236447
|
+
}
|
|
236448
|
+
if (params.heartbeat_interval_ms !== void 0 && params.heartbeat_interval_ms < 1e3) {
|
|
236449
|
+
return invalid("heartbeat_interval_ms", "heartbeat_interval_ms must be at least 1000");
|
|
236450
|
+
}
|
|
236451
|
+
if (params.heartbeat_timeout_ms !== void 0 && params.heartbeat_timeout_ms < 5e3) {
|
|
236452
|
+
return invalid("heartbeat_timeout_ms", "heartbeat_timeout_ms must be at least 5000");
|
|
236453
|
+
}
|
|
236454
|
+
if (params.max_concurrent_runs !== void 0 && params.max_concurrent_runs < 1) {
|
|
236455
|
+
return invalid("max_concurrent_runs", "max_concurrent_runs must be at least 1");
|
|
236456
|
+
}
|
|
236457
|
+
const target = await agentStore.getAgent(params.agent_id) ?? await agentStore.resolveAgent(params.agent_id);
|
|
236458
|
+
if (!target) {
|
|
236459
|
+
return {
|
|
236460
|
+
content: [{ type: "text", text: `Agent '${params.agent_id}' not found` }],
|
|
236461
|
+
isError: true,
|
|
236462
|
+
details: { outcome: "not_found", error: "Agent not found", agentId: params.agent_id }
|
|
236463
|
+
};
|
|
236464
|
+
}
|
|
236465
|
+
if (isEphemeralAgent(target)) {
|
|
236466
|
+
return invalid("agent_id", `Cannot update ephemeral/runtime agent ${target.id}`, { agentId: target.id });
|
|
236467
|
+
}
|
|
236468
|
+
const fnCtx = ctx;
|
|
236469
|
+
const callerAgentId = fnCtx.agentId;
|
|
236470
|
+
const targetChain = await agentStore.getChainOfCommand(target.id);
|
|
236471
|
+
if (callerAgentId) {
|
|
236472
|
+
if (callerAgentId === target.id) {
|
|
236473
|
+
return denied("You can only update your own direct or indirect reports, not yourself.", {
|
|
236474
|
+
agentId: target.id,
|
|
236475
|
+
callerAgentId,
|
|
236476
|
+
rule: "direct-or-indirect-reports-only"
|
|
236477
|
+
});
|
|
236478
|
+
}
|
|
236479
|
+
const callerIndex = targetChain.findIndex((agent) => agent.id === callerAgentId);
|
|
236480
|
+
if (callerIndex < 1) {
|
|
236481
|
+
return denied("You can only update your own direct or indirect reports.", {
|
|
236482
|
+
agentId: target.id,
|
|
236483
|
+
callerAgentId,
|
|
236484
|
+
rule: "direct-or-indirect-reports-only"
|
|
236485
|
+
});
|
|
236486
|
+
}
|
|
236487
|
+
}
|
|
236488
|
+
let resolvedReportsTo;
|
|
236489
|
+
let managerForCycleCheck;
|
|
236490
|
+
if (params.reportsTo !== void 0) {
|
|
236491
|
+
if (params.reportsTo === "") {
|
|
236492
|
+
if (callerAgentId) {
|
|
236493
|
+
return denied("Only privileged user/operator calls can clear an agent's manager.", {
|
|
236494
|
+
agentId: target.id,
|
|
236495
|
+
callerAgentId,
|
|
236496
|
+
rule: "privileged-clear-manager-only"
|
|
236497
|
+
});
|
|
236498
|
+
}
|
|
236499
|
+
resolvedReportsTo = void 0;
|
|
236500
|
+
} else {
|
|
236501
|
+
const manager = await agentStore.resolveAgent(params.reportsTo);
|
|
236502
|
+
if (!manager) {
|
|
236503
|
+
return invalid("reportsTo", `Manager '${params.reportsTo}' not found`, { agentId: target.id });
|
|
236504
|
+
}
|
|
236505
|
+
if (manager.id === target.id) {
|
|
236506
|
+
return invalid("reportsTo", "An agent cannot report to itself", { agentId: target.id });
|
|
236507
|
+
}
|
|
236508
|
+
const managerChain = await agentStore.getChainOfCommand(manager.id);
|
|
236509
|
+
if (managerChain.some((agent) => agent.id === target.id)) {
|
|
236510
|
+
return invalid("reportsTo", "reportsTo would create a management cycle", {
|
|
236511
|
+
agentId: target.id,
|
|
236512
|
+
managerId: manager.id
|
|
236513
|
+
});
|
|
236514
|
+
}
|
|
236515
|
+
if (callerAgentId) {
|
|
236516
|
+
const managerCallerIndex = managerChain.findIndex((agent) => agent.id === callerAgentId);
|
|
236517
|
+
if (manager.id !== callerAgentId && managerCallerIndex < 1) {
|
|
236518
|
+
return denied("You can only reparent reports to yourself or another agent in your management subtree.", {
|
|
236519
|
+
agentId: target.id,
|
|
236520
|
+
callerAgentId,
|
|
236521
|
+
managerId: manager.id,
|
|
236522
|
+
rule: "reparent-within-subtree-only"
|
|
236523
|
+
});
|
|
236524
|
+
}
|
|
236525
|
+
}
|
|
236526
|
+
resolvedReportsTo = manager.id;
|
|
236527
|
+
managerForCycleCheck = manager.id;
|
|
236528
|
+
}
|
|
236529
|
+
}
|
|
236530
|
+
const hasRuntimeConfigUpdates = [
|
|
236531
|
+
params.heartbeat_interval_ms,
|
|
236532
|
+
params.heartbeat_timeout_ms,
|
|
236533
|
+
params.max_concurrent_runs,
|
|
236534
|
+
params.message_response_mode
|
|
236535
|
+
].some((value) => value !== void 0);
|
|
236536
|
+
const updateInput = {};
|
|
236537
|
+
const updatedFields = [];
|
|
236538
|
+
const setField = (field, value) => {
|
|
236539
|
+
updateInput[field] = value;
|
|
236540
|
+
updatedFields.push(String(field));
|
|
236541
|
+
};
|
|
236542
|
+
if (params.name !== void 0) setField("name", params.name);
|
|
236543
|
+
if (params.role !== void 0) setField("role", params.role);
|
|
236544
|
+
if (params.title !== void 0) setField("title", params.title);
|
|
236545
|
+
if (params.icon !== void 0) setField("icon", params.icon);
|
|
236546
|
+
if (params.soul !== void 0) setField("soul", params.soul);
|
|
236547
|
+
if (params.instructions_text !== void 0) setField("instructionsText", params.instructions_text);
|
|
236548
|
+
if (params.instructions_path !== void 0) setField("instructionsPath", params.instructions_path);
|
|
236549
|
+
if (params.heartbeat_procedure_path !== void 0) setField("heartbeatProcedurePath", params.heartbeat_procedure_path);
|
|
236550
|
+
if (params.reportsTo !== void 0) {
|
|
236551
|
+
setField("reportsTo", resolvedReportsTo);
|
|
236552
|
+
}
|
|
236553
|
+
if (hasRuntimeConfigUpdates) {
|
|
236554
|
+
setField("runtimeConfig", {
|
|
236555
|
+
...target.runtimeConfig ?? {},
|
|
236556
|
+
...params.heartbeat_interval_ms !== void 0 ? { heartbeatIntervalMs: params.heartbeat_interval_ms } : {},
|
|
236557
|
+
...params.heartbeat_timeout_ms !== void 0 ? { heartbeatTimeoutMs: params.heartbeat_timeout_ms } : {},
|
|
236558
|
+
...params.max_concurrent_runs !== void 0 ? { maxConcurrentRuns: params.max_concurrent_runs } : {},
|
|
236559
|
+
...params.message_response_mode !== void 0 ? { messageResponseMode: params.message_response_mode } : {}
|
|
236560
|
+
});
|
|
236561
|
+
}
|
|
236562
|
+
if (managerForCycleCheck && managerForCycleCheck === target.id) {
|
|
236563
|
+
return invalid("reportsTo", "An agent cannot report to itself", { agentId: target.id });
|
|
236564
|
+
}
|
|
236565
|
+
const updated = await agentStore.updateAgent(target.id, updateInput);
|
|
236566
|
+
return {
|
|
236567
|
+
content: [{
|
|
236568
|
+
type: "text",
|
|
236569
|
+
text: `Updated ${updated.name} (${updated.id}): ${updatedFields.join(", ")}`
|
|
236570
|
+
}],
|
|
236571
|
+
details: { outcome: "updated", agentId: updated.id, updatedFields, agent: updated }
|
|
236572
|
+
};
|
|
236573
|
+
}
|
|
236574
|
+
});
|
|
234780
236575
|
pi2.registerTool({
|
|
234781
236576
|
name: "fn_agent_set_instructions",
|
|
234782
236577
|
label: "fn: Set Agent Instructions",
|