@sideboard-ai/core 0.1.217 → 0.1.218
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/{abletime-QGGYL2G4.js → abletime-BF253OZT.js} +5 -4
- package/dist/{abletime-GDZLZRMY.js → abletime-D6773P33.js} +5 -5
- package/dist/{abletime-oauth-F335TWKY.js → abletime-oauth-H4672PRH.js} +4 -3
- package/dist/{abletime-oauth-3X3BAPTF.js → abletime-oauth-KQPVVBKA.js} +4 -4
- package/dist/{abletime-rest-PLJVY3H3.js → abletime-rest-DAFPXZ7H.js} +4 -4
- package/dist/{abletime-rest-LAGTPAJL.js → abletime-rest-OPEVV3XC.js} +4 -3
- package/dist/agents/cursor-runner.js +5 -5
- package/dist/{agents-WBDDLRBY.js → agents-NWJ7DFJD.js} +8 -7
- package/dist/{agents-HETHRRZJ.js → agents-YABJUV3V.js} +9 -9
- package/dist/{app-settings-XCRNCEEE.js → app-settings-5RQMQKSB.js} +2 -1
- package/dist/{app-settings-2TN3WLZV.js → app-settings-V2BBCX2H.js} +2 -2
- package/dist/{chunk-MN6YUD4O.js → chunk-3NWUHXU3.js} +3 -3
- package/dist/{chunk-G4TANQC3.js → chunk-646I442S.js} +5 -5
- package/dist/{chunk-FNQNJOHY.js → chunk-ABOBTSO6.js} +4 -1
- package/dist/{chunk-YVE4PXCJ.js → chunk-CLI5DVU4.js} +2 -2
- package/dist/{chunk-7MTVRNP4.js → chunk-FSIJGLSV.js} +1173 -1186
- package/dist/{chunk-LS6TSOCE.js → chunk-HSYE5ID6.js} +5 -5
- package/dist/{chunk-UFHNSFAS.js → chunk-HVUVQXZ2.js} +5 -1
- package/dist/{chunk-SALM35KD.js → chunk-IILG7DPX.js} +5 -5
- package/dist/{chunk-D2IKXMF3.js → chunk-JWBPSD7Q.js} +453 -289
- package/dist/{chunk-NI243N27.js → chunk-KTZESHYH.js} +15 -7
- package/dist/{chunk-KFD2OXCE.js → chunk-MW57SKKR.js} +19 -6
- package/dist/{chunk-XYKVRRC2.js → chunk-NFWPDEN4.js} +15 -7
- package/dist/{chunk-FNGXKY5W.js → chunk-OPYBLX4V.js} +19 -6
- package/dist/{chunk-QQKDGPUI.js → chunk-PO4M4JAJ.js} +1 -1
- package/dist/{chunk-2M5B7YNY.js → chunk-RRW2XWAY.js} +2 -2
- package/dist/{chunk-REZRGCKV.js → chunk-SOGGKYFV.js} +3 -3
- package/dist/{chunk-C32SBNA3.js → chunk-SRFFIFW6.js} +457 -294
- package/dist/{chunk-3OHR73VK.js → chunk-SVXIJ2IC.js} +1 -1
- package/dist/{chunk-SYFVUF2C.js → chunk-SZJZEFTY.js} +1 -1
- package/dist/{chunk-42IULNIY.js → chunk-THLVCXLF.js} +3 -3
- package/dist/{chunk-JZHRZ3DR.js → chunk-TQBUXKIB.js} +5 -5
- package/dist/chunk-WWUABK6B.js +56 -0
- package/dist/{chunk-DFTVZEZY.js → chunk-XRHTRXZN.js} +1051 -1050
- package/dist/{chunk-BTC2D6LS.js → chunk-Y5JES2VV.js} +3 -51
- package/dist/{connected-teams-WK47ATT7.js → connected-teams-LX5DLL2I.js} +3 -2
- package/dist/{connected-teams-PU4AMFNA.js → connected-teams-PQETXUWV.js} +3 -2
- package/dist/{coordinator-prompt-FQX6ALAI.js → coordinator-prompt-5V7GEPBG.js} +6 -6
- package/dist/{coordinator-prompt-LE2GQZ2X.js → coordinator-prompt-J4QSNTKE.js} +6 -5
- package/dist/{global-workspace-6P236KKG.js → global-workspace-4TEMLESD.js} +6 -6
- package/dist/{global-workspace-KT3FRHZL.js → global-workspace-XPTFHMKL.js} +6 -5
- package/dist/index.cjs +1027 -821
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +22 -18
- package/dist/mcp/run-stdio.cjs +1838 -1651
- package/dist/mcp/run-stdio.js +16 -14
- package/dist/{orchestrator-WVZPTHBV.js → orchestrator-HKW3VLGX.js} +10 -10
- package/dist/{orchestrator-MI7A5DQZ.js → orchestrator-SWRTYFHX.js} +9 -8
- package/dist/{run-EBWL2Q4N.js → run-QDZCTP4Y.js} +2 -1
- package/dist/{run-CAGKZLHA.js → run-UEQM4JMZ.js} +2 -1
- package/dist/{workspaces-KM7KTLVO.js → workspaces-2HQP654Q.js} +6 -5
- package/dist/{workspaces-R5LJCJRB.js → workspaces-BCBBMMXB.js} +6 -6
- package/dist/{worktree-MDMTCAHX.js → worktree-7V67XTEO.js} +5 -5
- package/dist/{worktree-VXRI7LYM.js → worktree-I573UOPD.js} +5 -4
- package/dist/{worktree-exclude-N5X5I422.js → worktree-exclude-K7QK4RI5.js} +3 -2
- package/dist/{worktree-exclude-X54WSOXM.js → worktree-exclude-L32WEBX6.js} +3 -2
- package/package.json +1 -1
- package/dist/{chunk-L5CIEBLR.js → chunk-EWHJNJHV.js} +3 -3
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isOrchestratorThread
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-OPYBLX4V.js";
|
|
4
4
|
import {
|
|
5
5
|
codexUnattendedGitConfigArgs,
|
|
6
6
|
mergeAgentGitAuthEnv,
|
|
7
7
|
resolveAgentGitAuthEnv,
|
|
8
8
|
resolveCodexGitWritableRoots
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-KTZESHYH.js";
|
|
10
10
|
import {
|
|
11
11
|
applyConnectedTeamToCli,
|
|
12
12
|
brightsyMcpServerName,
|
|
13
13
|
ensureCliTeamTracked,
|
|
14
14
|
ensureConnectedBrightsyTeamTokens
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-IILG7DPX.js";
|
|
16
16
|
import {
|
|
17
17
|
loadBrightsyConfig
|
|
18
18
|
} from "./chunk-AN3THU7F.js";
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
packagedMcpStdioPath,
|
|
34
34
|
parseCursorRunnerLine,
|
|
35
35
|
resolveNodeLaunch
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-TQBUXKIB.js";
|
|
37
37
|
import {
|
|
38
38
|
claudeChromeEnabled,
|
|
39
39
|
fromInclusiveInputUsage,
|
|
@@ -42,11 +42,7 @@ import {
|
|
|
42
42
|
loadAppSettings,
|
|
43
43
|
resolveAgentExecutable,
|
|
44
44
|
resolveClaudeExecutable
|
|
45
|
-
} from "./chunk-
|
|
46
|
-
import {
|
|
47
|
-
isElectronLikeCommand,
|
|
48
|
-
unwrapStrippedElectronLaunch
|
|
49
|
-
} from "./chunk-4TR3HZFT.js";
|
|
45
|
+
} from "./chunk-EWHJNJHV.js";
|
|
50
46
|
import {
|
|
51
47
|
appDataDir
|
|
52
48
|
} from "./chunk-WFUDY6PC.js";
|
|
@@ -56,7 +52,11 @@ import {
|
|
|
56
52
|
resolveCommandBinarySync,
|
|
57
53
|
run,
|
|
58
54
|
withExportedPath
|
|
59
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-HVUVQXZ2.js";
|
|
56
|
+
import {
|
|
57
|
+
isElectronLikeCommand,
|
|
58
|
+
unwrapStrippedElectronLaunch
|
|
59
|
+
} from "./chunk-4TR3HZFT.js";
|
|
60
60
|
|
|
61
61
|
// src/agents/brightsy.ts
|
|
62
62
|
import { existsSync } from "fs";
|
|
@@ -486,7 +486,7 @@ var brightsyAdapter = {
|
|
|
486
486
|
};
|
|
487
487
|
|
|
488
488
|
// src/agents/claude.ts
|
|
489
|
-
import { existsSync as
|
|
489
|
+
import { existsSync as existsSync5 } from "fs";
|
|
490
490
|
|
|
491
491
|
// src/agents/claude-mcp.ts
|
|
492
492
|
function parseMcpList(text) {
|
|
@@ -521,6 +521,11 @@ function mcpAllowTools(servers) {
|
|
|
521
521
|
}
|
|
522
522
|
return out;
|
|
523
523
|
}
|
|
524
|
+
function mcpAllowToolsFromNames(names) {
|
|
525
|
+
return mcpAllowTools(
|
|
526
|
+
[...names].map((name) => name.trim()).filter(Boolean).map((name) => ({ name, connected: true, needsAuth: false }))
|
|
527
|
+
);
|
|
528
|
+
}
|
|
524
529
|
function mcpAuthWarnings(servers) {
|
|
525
530
|
const needing = servers.filter((s) => s.needsAuth).map((s) => s.name);
|
|
526
531
|
if (needing.length === 0) return [];
|
|
@@ -529,964 +534,1142 @@ function mcpAuthWarnings(servers) {
|
|
|
529
534
|
];
|
|
530
535
|
}
|
|
531
536
|
|
|
532
|
-
// src/agents/
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
537
|
+
// src/agents/orch-mcp-isolation.ts
|
|
538
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
539
|
+
import { homedir as homedir2 } from "os";
|
|
540
|
+
import { join as join3 } from "path";
|
|
541
|
+
import { parse as parseToml } from "smol-toml";
|
|
542
|
+
|
|
543
|
+
// src/agents/user-mcp-config.ts
|
|
544
|
+
import { existsSync as existsSync3, mkdirSync, readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
545
|
+
import { homedir } from "os";
|
|
546
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
547
|
+
|
|
548
|
+
// src/agents/injected-mcp.ts
|
|
549
|
+
import { existsSync as existsSync2, mkdtempSync, writeFileSync } from "fs";
|
|
550
|
+
import { createRequire } from "module";
|
|
551
|
+
import { tmpdir } from "os";
|
|
552
|
+
import { dirname, join } from "path";
|
|
553
|
+
import { fileURLToPath } from "url";
|
|
554
|
+
|
|
555
|
+
// src/mcp/profile.ts
|
|
556
|
+
var SIDEBOARD_MCP_PROFILE_ENV = "SIDEBOARD_MCP_PROFILE";
|
|
557
|
+
var WORKTREE_MCP_TOOLS = [
|
|
558
|
+
"present_artifact",
|
|
559
|
+
"ask_user",
|
|
560
|
+
"present_plan",
|
|
561
|
+
"present_schema",
|
|
562
|
+
"present_files",
|
|
563
|
+
"wait_for_job",
|
|
564
|
+
"stop_job",
|
|
565
|
+
"get_viewer_context",
|
|
566
|
+
"update_viewer_context"
|
|
567
|
+
];
|
|
568
|
+
var WORKTREE_GITHUB_MCP_TOOLS = [
|
|
569
|
+
"github_search_issues",
|
|
570
|
+
"github_get_issue",
|
|
571
|
+
"github_comment",
|
|
572
|
+
"github_update_issue",
|
|
573
|
+
"github_create_issue"
|
|
574
|
+
];
|
|
575
|
+
var WORKTREE_LINEAR_MCP_TOOLS = [
|
|
576
|
+
"linear_list_teams",
|
|
577
|
+
"linear_search_issues",
|
|
578
|
+
"linear_get_issue",
|
|
579
|
+
"linear_create_issue",
|
|
580
|
+
"linear_update_issue",
|
|
581
|
+
"linear_comment"
|
|
582
|
+
];
|
|
583
|
+
var WORKTREE_ABLETIME_MCP_TOOLS = [
|
|
584
|
+
"abletime_orientation",
|
|
585
|
+
"abletime_list_projects",
|
|
586
|
+
"abletime_list_tasks",
|
|
587
|
+
"abletime_search_tasks",
|
|
588
|
+
"abletime_get_task",
|
|
589
|
+
"abletime_comment",
|
|
590
|
+
"abletime_update_task",
|
|
591
|
+
"abletime_create_task",
|
|
592
|
+
"abletime_ensure_task"
|
|
593
|
+
];
|
|
594
|
+
function sideboardMcpProfile(env = process.env) {
|
|
595
|
+
return env[SIDEBOARD_MCP_PROFILE_ENV]?.trim().toLowerCase() === "worktree" ? "worktree" : "orchestration";
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// src/agents/injected-mcp.ts
|
|
599
|
+
var SIDEBOARD_MCP_ALLOWED_TOOLS = [
|
|
600
|
+
"mcp__sideboard",
|
|
601
|
+
"mcp__sideboard__*"
|
|
602
|
+
];
|
|
603
|
+
var SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS = [
|
|
604
|
+
"mcp__sideboard__present_artifact",
|
|
605
|
+
"mcp__sideboard__present_schema",
|
|
606
|
+
"mcp__sideboard__present_files",
|
|
607
|
+
"mcp__sideboard__ask_user",
|
|
608
|
+
"mcp__sideboard__present_plan",
|
|
609
|
+
"mcp__sideboard__wait_for_job",
|
|
610
|
+
"mcp__sideboard__stop_job",
|
|
611
|
+
"mcp__sideboard__get_viewer_context",
|
|
612
|
+
"mcp__sideboard__update_viewer_context"
|
|
613
|
+
];
|
|
614
|
+
var SIDEBOARD_GITHUB_MCP_ALLOWED_TOOLS = ["mcp__sideboard__github_*"];
|
|
615
|
+
var SIDEBOARD_LINEAR_MCP_ALLOWED_TOOLS = ["mcp__sideboard__linear_*"];
|
|
616
|
+
var SIDEBOARD_ABLETIME_MCP_ALLOWED_TOOLS = ["mcp__sideboard__abletime_*"];
|
|
617
|
+
function sideboardWorktreeAllowedTools(opts) {
|
|
618
|
+
const out = [...SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS];
|
|
619
|
+
if (opts?.github !== false) out.push(...SIDEBOARD_GITHUB_MCP_ALLOWED_TOOLS);
|
|
620
|
+
if (opts?.linear) out.push(...SIDEBOARD_LINEAR_MCP_ALLOWED_TOOLS);
|
|
621
|
+
if (opts?.abletime) out.push(...SIDEBOARD_ABLETIME_MCP_ALLOWED_TOOLS);
|
|
622
|
+
return out;
|
|
623
|
+
}
|
|
624
|
+
var BRIGHTSY_MCP_ALLOWED_TOOLS = [
|
|
625
|
+
"mcp__brightsy",
|
|
626
|
+
"mcp__brightsy__*"
|
|
627
|
+
];
|
|
628
|
+
var brightsyMcpCommandCache = null;
|
|
629
|
+
async function resolveBrightsyMcpCommand() {
|
|
630
|
+
const now = Date.now();
|
|
631
|
+
if (brightsyMcpCommandCache && now - brightsyMcpCommandCache.at < 6e4 && brightsyMcpCommandCache.command) {
|
|
632
|
+
return brightsyMcpCommandCache.command;
|
|
536
633
|
}
|
|
537
|
-
|
|
634
|
+
const which = await run("which", ["brightsy-mcp"], { reject: false });
|
|
635
|
+
const command = which.exitCode === 0 && which.stdout.trim() ? "brightsy-mcp" : "npx";
|
|
636
|
+
brightsyMcpCommandCache = { at: now, command };
|
|
637
|
+
return command;
|
|
538
638
|
}
|
|
539
|
-
function
|
|
540
|
-
|
|
639
|
+
function isBrightsyConnected() {
|
|
640
|
+
try {
|
|
641
|
+
loadBrightsyConfig();
|
|
642
|
+
return true;
|
|
643
|
+
} catch {
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
541
646
|
}
|
|
542
|
-
var
|
|
543
|
-
function
|
|
544
|
-
|
|
545
|
-
if (line.length <= max) return line;
|
|
546
|
-
return `${line.slice(0, Math.max(1, max - 1))}\u2026`;
|
|
647
|
+
var BRIGHTSY_WORD = /(?<![a-z0-9_-])brightsy(?![a-z0-9_-])/i;
|
|
648
|
+
function promptMentionsBrightsy(text) {
|
|
649
|
+
return BRIGHTSY_WORD.test(text ?? "");
|
|
547
650
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
if (SHELL_BIN_DIR.test(head)) return false;
|
|
552
|
-
return rest.every((tok) => !/^[-/~$]/.test(tok) && !tok.includes("="));
|
|
651
|
+
function isBrightsyMcpToolName(name) {
|
|
652
|
+
const n = name.toLowerCase();
|
|
653
|
+
return n === "brightsy" || n.startsWith("mcp__brightsy") || n.startsWith("brightsy_");
|
|
553
654
|
}
|
|
554
|
-
function
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
const rooted = trimmed.startsWith("/") || trimmed.startsWith("~/") || /^[A-Za-z]:[\\/]/.test(trimmed);
|
|
558
|
-
if (/\s/.test(trimmed)) return rooted && rootedPathWithSpaces(trimmed);
|
|
559
|
-
if (rooted) return true;
|
|
560
|
-
return trimmed.includes("/") || trimmed.includes("\\");
|
|
655
|
+
function toolInputUsesBrightsyDatasource(input) {
|
|
656
|
+
const ds = input?.datasource;
|
|
657
|
+
return typeof ds === "string" && ds.toLowerCase() === "brightsy";
|
|
561
658
|
}
|
|
562
|
-
function
|
|
563
|
-
if (
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
659
|
+
function messageUsedBrightsyMcp(msg) {
|
|
660
|
+
if (msg.role === "user" && promptMentionsBrightsy(msg.text)) return true;
|
|
661
|
+
for (const part of msg.parts ?? []) {
|
|
662
|
+
if (part.type !== "tool") continue;
|
|
663
|
+
if (isBrightsyMcpToolName(part.name)) return true;
|
|
664
|
+
if (/present_(schema|files)$/i.test(part.name) && toolInputUsesBrightsyDatasource(part.input)) {
|
|
665
|
+
return true;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
return false;
|
|
568
669
|
}
|
|
569
|
-
function
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
if (!
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
670
|
+
function threadRequestsBrightsyMcp(thread) {
|
|
671
|
+
return (thread?.messages ?? []).some(messageUsedBrightsyMcp);
|
|
672
|
+
}
|
|
673
|
+
function shouldInjectBrightsyMcp(thread, opts) {
|
|
674
|
+
if (!(opts?.connected ?? isBrightsyConnected())) return false;
|
|
675
|
+
if (opts?.orchestrator) return true;
|
|
676
|
+
const alwaysOn = opts?.alwaysOnWorktree ?? Boolean(loadAppSettings().brightsy?.injectWorktreeMcp);
|
|
677
|
+
if (alwaysOn) return true;
|
|
678
|
+
return threadRequestsBrightsyMcp(thread);
|
|
679
|
+
}
|
|
680
|
+
function mcpLaunch(cmd, name, env) {
|
|
681
|
+
if (cmd === "brightsy-mcp") {
|
|
682
|
+
return { name, command: "brightsy-mcp", ...env ? { env } : {} };
|
|
577
683
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
if (parts.length >= 2) return `\u2026/${parts.slice(-2).join("/")}`;
|
|
585
|
-
return base;
|
|
684
|
+
return {
|
|
685
|
+
name,
|
|
686
|
+
command: "npx",
|
|
687
|
+
args: ["-y", "@brightsy/mcp-server"],
|
|
688
|
+
...env ? { env } : {}
|
|
689
|
+
};
|
|
586
690
|
}
|
|
587
|
-
function
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
691
|
+
function teamEnv(team) {
|
|
692
|
+
const endpoint = (team.endpoint || "https://brightsy.ai").replace(/\/$/, "");
|
|
693
|
+
return {
|
|
694
|
+
BRIGHTSY_API_TOKEN: team.access_token,
|
|
695
|
+
...team.refresh_token ? { BRIGHTSY_REFRESH_TOKEN: team.refresh_token } : {},
|
|
696
|
+
BRIGHTSY_ACCOUNT_ID: team.id,
|
|
697
|
+
BRIGHTSY_API_URL: endpoint
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
function brightsyMcpAllowedTools(serverNames) {
|
|
701
|
+
const out = [];
|
|
702
|
+
for (const name of serverNames) {
|
|
703
|
+
out.push(`mcp__${name}`, `mcp__${name}__*`);
|
|
595
704
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
const
|
|
600
|
-
if (
|
|
705
|
+
return out;
|
|
706
|
+
}
|
|
707
|
+
function corePackageDir() {
|
|
708
|
+
const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
|
|
709
|
+
if (cjsDir) return cjsDir;
|
|
601
710
|
try {
|
|
602
|
-
const
|
|
603
|
-
|
|
711
|
+
const url = import.meta.url;
|
|
712
|
+
if (typeof url === "string" && url.length > 0) {
|
|
713
|
+
return dirname(fileURLToPath(url));
|
|
714
|
+
}
|
|
604
715
|
} catch {
|
|
605
|
-
return name;
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
function toolDescription(name, input) {
|
|
609
|
-
const n = name.replace(/^mcp__/, "").replace(/__/g, " \xB7 ");
|
|
610
|
-
if (/^get_record_types$|recordTypes/i.test(name)) return "List record types";
|
|
611
|
-
if (/connectedAgentRequest/i.test(name)) {
|
|
612
|
-
return str(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
|
|
613
716
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
return
|
|
717
|
+
try {
|
|
718
|
+
const req = createRequire(join(process.cwd(), "package.json"));
|
|
719
|
+
return dirname(req.resolve("@sideboard-ai/core"));
|
|
720
|
+
} catch {
|
|
721
|
+
return process.cwd();
|
|
619
722
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
if (
|
|
624
|
-
|
|
723
|
+
}
|
|
724
|
+
function findSideboardMcpJsEntry() {
|
|
725
|
+
const override = process.env.SIDEBOARD_MCP_ENTRY?.trim() || process.env.SIDEBOARD_CLI?.trim();
|
|
726
|
+
if (override && existsSync2(override)) return override;
|
|
727
|
+
const packaged = packagedMcpStdioPath();
|
|
728
|
+
if (packaged) return packaged;
|
|
729
|
+
let dir = corePackageDir();
|
|
730
|
+
for (let i = 0; i < 10; i++) {
|
|
731
|
+
const candidates = [
|
|
732
|
+
join(dir, "mcp/run-stdio.js"),
|
|
733
|
+
join(dir, "mcp/run-stdio.cjs"),
|
|
734
|
+
join(dir, "dist/mcp/run-stdio.js"),
|
|
735
|
+
join(dir, "dist/mcp/run-stdio.cjs"),
|
|
736
|
+
join(dir, "packages/core/dist/mcp/run-stdio.js"),
|
|
737
|
+
join(dir, "packages/cli/dist/index.js"),
|
|
738
|
+
join(dir, "cli/dist/index.js")
|
|
739
|
+
];
|
|
740
|
+
for (const p of candidates) {
|
|
741
|
+
if (existsSync2(p) && !isAsarPath(p)) return p;
|
|
742
|
+
}
|
|
743
|
+
const parent = dirname(dir);
|
|
744
|
+
if (parent === dir) break;
|
|
745
|
+
dir = parent;
|
|
625
746
|
}
|
|
626
|
-
|
|
627
|
-
|
|
747
|
+
return null;
|
|
748
|
+
}
|
|
749
|
+
async function resolveSideboardMcpServer() {
|
|
750
|
+
const entry = findSideboardMcpJsEntry();
|
|
751
|
+
if (entry) {
|
|
752
|
+
const isCli = /[/\\]cli[/\\]dist[/\\]index\.js$/.test(entry);
|
|
753
|
+
const scriptArgs = isCli ? [entry, "mcp"] : [entry];
|
|
754
|
+
const launch = applyNodeLaunch(await resolveNodeLaunch(entry), scriptArgs);
|
|
755
|
+
if (launch.file !== "/bin/sh" && !isElectronLikeCommand(launch.file)) {
|
|
756
|
+
return {
|
|
757
|
+
name: "sideboard",
|
|
758
|
+
command: launch.file,
|
|
759
|
+
args: launch.args,
|
|
760
|
+
...Object.keys(launch.env).length > 0 ? { env: launch.env } : {}
|
|
761
|
+
};
|
|
762
|
+
}
|
|
628
763
|
}
|
|
629
|
-
|
|
630
|
-
|
|
764
|
+
const which = await run("which", ["sideboard"], { reject: false });
|
|
765
|
+
if (which.exitCode === 0 && which.stdout.trim()) {
|
|
766
|
+
return { name: "sideboard", command: which.stdout.trim(), args: ["mcp"] };
|
|
631
767
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
768
|
+
return { name: "sideboard", command: "sideboard", args: ["mcp"] };
|
|
769
|
+
}
|
|
770
|
+
async function buildInjectedMcpServers(opts) {
|
|
771
|
+
const servers = [];
|
|
772
|
+
if (opts.includeSideboard) {
|
|
773
|
+
const sideboard = await resolveSideboardMcpServer();
|
|
774
|
+
const orchId = opts.orchestratorThreadId?.trim();
|
|
775
|
+
const profile = orchId ? "orchestration" : "worktree";
|
|
776
|
+
sideboard.env = {
|
|
777
|
+
...sideboard.env ?? {},
|
|
778
|
+
SIDEBOARD_APP_DATA: appDataDir(),
|
|
779
|
+
[SIDEBOARD_MCP_PROFILE_ENV]: profile
|
|
780
|
+
};
|
|
781
|
+
if (orchId) {
|
|
782
|
+
sideboard.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID = orchId;
|
|
638
783
|
}
|
|
639
|
-
|
|
640
|
-
|
|
784
|
+
try {
|
|
785
|
+
mergeAgentGitAuthEnv(
|
|
786
|
+
sideboard.env,
|
|
787
|
+
await resolveAgentGitAuthEnv(sideboard.env)
|
|
788
|
+
);
|
|
789
|
+
} catch {
|
|
641
790
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
return `Subagent${subagentLiveSuffix(input)}`;
|
|
645
|
-
}
|
|
646
|
-
if (/^(create|update)_artifact$/i.test(name.replace(/^mcp__[^_]+__/, ""))) {
|
|
647
|
-
return str(input?.title) ? `Artifact ${str(input?.title)}` : "Artifact";
|
|
791
|
+
applyAgentRunnerHeapEnv(sideboard.env);
|
|
792
|
+
servers.push(sideboard);
|
|
648
793
|
}
|
|
649
|
-
if (
|
|
650
|
-
|
|
651
|
-
const
|
|
652
|
-
if (
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
794
|
+
if (opts.includeBrightsy && isBrightsyConnected()) {
|
|
795
|
+
const cmd = await resolveBrightsyMcpCommand();
|
|
796
|
+
const teams = await ensureConnectedBrightsyTeamTokens();
|
|
797
|
+
if (teams.length > 0) {
|
|
798
|
+
const used = /* @__PURE__ */ new Set();
|
|
799
|
+
for (const team of teams) {
|
|
800
|
+
let name = brightsyMcpServerName(team.slug);
|
|
801
|
+
if (used.has(name)) name = `${name}_${team.id.slice(0, 8)}`;
|
|
802
|
+
used.add(name);
|
|
803
|
+
servers.push(mcpLaunch(cmd, name, teamEnv(team)));
|
|
804
|
+
}
|
|
805
|
+
} else {
|
|
806
|
+
servers.push(mcpLaunch(cmd, "brightsy"));
|
|
807
|
+
}
|
|
658
808
|
}
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
809
|
+
return servers;
|
|
810
|
+
}
|
|
811
|
+
function cursorSafeMcpLaunch(command, args) {
|
|
812
|
+
if (process.platform === "win32") {
|
|
813
|
+
return args && args.length > 0 ? { command, args } : { command };
|
|
662
814
|
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
815
|
+
const unwrapped = unwrapStrippedElectronLaunch(command, args);
|
|
816
|
+
const file = unwrapped?.file ?? command;
|
|
817
|
+
const fileArgs = unwrapped?.args ?? args ?? [];
|
|
818
|
+
return fileArgs.length > 0 ? { command: file, args: fileArgs } : { command: file };
|
|
819
|
+
}
|
|
820
|
+
function mcpSpawnEnv(env) {
|
|
821
|
+
if (!env) return void 0;
|
|
822
|
+
const out = { ...env };
|
|
823
|
+
delete out.ELECTRON_RUN_AS_NODE;
|
|
824
|
+
delete out.ELECTRON_RUN_AS_NODE;
|
|
825
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
826
|
+
}
|
|
827
|
+
function toCursorMcpServers(servers) {
|
|
828
|
+
const out = {};
|
|
829
|
+
for (const s of servers) {
|
|
830
|
+
const env = mcpSpawnEnv(s.env);
|
|
831
|
+
const launch = cursorSafeMcpLaunch(s.command, s.args);
|
|
832
|
+
out[s.name] = {
|
|
833
|
+
type: "stdio",
|
|
834
|
+
command: launch.command,
|
|
835
|
+
...launch.args && launch.args.length > 0 ? { args: launch.args } : {},
|
|
836
|
+
...env ? { env } : {}
|
|
837
|
+
};
|
|
666
838
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
839
|
+
return out;
|
|
840
|
+
}
|
|
841
|
+
function toCodexMcpConfigArgs(servers) {
|
|
842
|
+
const args = [];
|
|
843
|
+
for (const s of servers) {
|
|
844
|
+
const prefix = `mcp_servers.${s.name}`;
|
|
845
|
+
args.push("-c", `${prefix}.command=${JSON.stringify(s.command)}`);
|
|
846
|
+
if (s.args?.length) {
|
|
847
|
+
args.push("-c", `${prefix}.args=${JSON.stringify(s.args)}`);
|
|
848
|
+
}
|
|
849
|
+
const env = mcpSpawnEnv(s.env);
|
|
850
|
+
if (env) {
|
|
851
|
+
for (const [key, value] of Object.entries(env)) {
|
|
852
|
+
args.push("-c", `${prefix}.env.${key}=${JSON.stringify(value)}`);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
args.push("-c", `${prefix}.default_tools_approval_mode=${JSON.stringify("approve")}`);
|
|
856
|
+
args.push("-c", `${prefix}.tool_timeout_sec=300`);
|
|
857
|
+
args.push("-c", `${prefix}.startup_timeout_sec=30`);
|
|
672
858
|
}
|
|
673
|
-
|
|
674
|
-
|
|
859
|
+
return args;
|
|
860
|
+
}
|
|
861
|
+
function toOpencodeMcpConfigContent(servers, opts) {
|
|
862
|
+
const mcp = {};
|
|
863
|
+
for (const s of servers) {
|
|
864
|
+
const env = mcpSpawnEnv(s.env);
|
|
865
|
+
mcp[s.name] = {
|
|
866
|
+
type: "local",
|
|
867
|
+
command: [s.command, ...s.args ?? []],
|
|
868
|
+
enabled: true,
|
|
869
|
+
...env ? { environment: env } : {}
|
|
870
|
+
};
|
|
675
871
|
}
|
|
676
|
-
|
|
677
|
-
const
|
|
678
|
-
|
|
872
|
+
for (const name of opts?.disableNames ?? []) {
|
|
873
|
+
const key = name.trim();
|
|
874
|
+
if (!key || mcp[key]?.enabled === true) continue;
|
|
875
|
+
mcp[key] = { ...mcp[key], enabled: false };
|
|
679
876
|
}
|
|
680
|
-
return
|
|
877
|
+
return JSON.stringify({ mcp });
|
|
681
878
|
}
|
|
682
|
-
function
|
|
683
|
-
if (
|
|
684
|
-
const
|
|
685
|
-
const
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
879
|
+
function writeMcpServersConfig(servers) {
|
|
880
|
+
if (servers.length === 0) return null;
|
|
881
|
+
const mcpServers = {};
|
|
882
|
+
for (const s of servers) {
|
|
883
|
+
const env = mcpSpawnEnv(s.env);
|
|
884
|
+
mcpServers[s.name] = {
|
|
885
|
+
command: s.command,
|
|
886
|
+
...s.args ? { args: s.args } : {},
|
|
887
|
+
...env ? { env } : {}
|
|
888
|
+
};
|
|
690
889
|
}
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
890
|
+
const dir = mkdtempSync(join(tmpdir(), "sideboard-mcp-"));
|
|
891
|
+
const cfgPath = join(dir, "mcp.json");
|
|
892
|
+
writeFileSync(cfgPath, JSON.stringify({ mcpServers }, null, 2));
|
|
893
|
+
return cfgPath;
|
|
694
894
|
}
|
|
695
|
-
function
|
|
696
|
-
|
|
697
|
-
if (/^(task|agent|spawn_agent)$/i.test(n)) return true;
|
|
698
|
-
return /connectedAgentRequest/i.test(n);
|
|
895
|
+
async function writeInjectedMcpConfig(opts) {
|
|
896
|
+
return writeMcpServersConfig(await buildInjectedMcpServers(opts));
|
|
699
897
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
if (/^agent is running\b/i.test(t) && t.length < 160) return true;
|
|
705
|
-
return false;
|
|
898
|
+
|
|
899
|
+
// src/agents/user-mcp-config.ts
|
|
900
|
+
function userCursorMcpConfigPath() {
|
|
901
|
+
return join2(homedir(), ".cursor", "mcp.json");
|
|
706
902
|
}
|
|
707
|
-
function
|
|
708
|
-
|
|
709
|
-
return /^(taskoutput|taskstop|sleep)$/i.test(n);
|
|
903
|
+
function userClaudeMcpConfigPath() {
|
|
904
|
+
return join2(homedir(), ".claude.json");
|
|
710
905
|
}
|
|
711
|
-
function
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
if (p.type !== type) continue;
|
|
715
|
-
const text = p.text.trim();
|
|
716
|
-
if (!text || isInternalAgentStatusText(text)) continue;
|
|
717
|
-
return text;
|
|
718
|
-
}
|
|
719
|
-
return "";
|
|
906
|
+
function asObject(value) {
|
|
907
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
908
|
+
return { ...value };
|
|
720
909
|
}
|
|
721
|
-
function
|
|
722
|
-
|
|
910
|
+
function stripElectronSpawnEnv(env) {
|
|
911
|
+
if (!env) return void 0;
|
|
912
|
+
const next = { ...env };
|
|
913
|
+
delete next.ELECTRON_RUN_AS_NODE;
|
|
914
|
+
delete next.ELECTRON_RUN_AS_NODE;
|
|
915
|
+
return Object.keys(next).length > 0 ? next : void 0;
|
|
723
916
|
}
|
|
724
|
-
function
|
|
725
|
-
const
|
|
726
|
-
|
|
917
|
+
function mergeSideboardIntoMcpServersJson(existing, sideboard) {
|
|
918
|
+
const root = asObject(existing);
|
|
919
|
+
const servers = asObject(root.mcpServers);
|
|
920
|
+
const env = stripElectronSpawnEnv(sideboard.env);
|
|
921
|
+
servers.sideboard = {
|
|
922
|
+
type: "stdio",
|
|
923
|
+
command: sideboard.command,
|
|
924
|
+
...sideboard.args && sideboard.args.length > 0 ? { args: sideboard.args } : {},
|
|
925
|
+
...env ? { env } : {}
|
|
926
|
+
};
|
|
927
|
+
return { ...root, mcpServers: servers };
|
|
727
928
|
}
|
|
728
|
-
function
|
|
729
|
-
|
|
730
|
-
if (
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
929
|
+
function writeMergedMcpServersJson(configPath, sideboard) {
|
|
930
|
+
let existing = {};
|
|
931
|
+
if (existsSync3(configPath)) {
|
|
932
|
+
try {
|
|
933
|
+
existing = JSON.parse(readFileSync(configPath, "utf8"));
|
|
934
|
+
} catch {
|
|
935
|
+
existing = {};
|
|
936
|
+
}
|
|
735
937
|
}
|
|
736
|
-
|
|
938
|
+
const next = mergeSideboardIntoMcpServersJson(existing, sideboard);
|
|
939
|
+
mkdirSync(dirname2(configPath), { recursive: true });
|
|
940
|
+
writeFileSync2(configPath, `${JSON.stringify(next, null, 2)}
|
|
941
|
+
`);
|
|
737
942
|
}
|
|
738
|
-
function
|
|
739
|
-
return
|
|
943
|
+
function launchFromResolved(server) {
|
|
944
|
+
return {
|
|
945
|
+
command: server.command,
|
|
946
|
+
args: server.args,
|
|
947
|
+
env: {
|
|
948
|
+
...server.env ?? {},
|
|
949
|
+
SIDEBOARD_APP_DATA: appDataDir()
|
|
950
|
+
}
|
|
951
|
+
};
|
|
740
952
|
}
|
|
741
|
-
function
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
if (tools.length === 0) return null;
|
|
750
|
-
const edited = [];
|
|
751
|
-
let reads = 0;
|
|
752
|
-
let searches = 0;
|
|
753
|
-
let shells = 0;
|
|
754
|
-
let others = 0;
|
|
755
|
-
let additions = 0;
|
|
756
|
-
let deletions = 0;
|
|
757
|
-
for (const tool of tools) {
|
|
758
|
-
const kind = classifyTool(tool.name);
|
|
759
|
-
if (kind === "edit") {
|
|
760
|
-
const path = tool.filePath ?? toolFilePath(tool.input);
|
|
761
|
-
edited.push({
|
|
762
|
-
name: path ? fileBasename(path) : tool.name,
|
|
763
|
-
running: tool.status === "running"
|
|
764
|
-
});
|
|
765
|
-
} else if (kind === "read") reads += 1;
|
|
766
|
-
else if (kind === "search") searches += 1;
|
|
767
|
-
else if (kind === "shell") shells += 1;
|
|
768
|
-
else others += 1;
|
|
769
|
-
if (typeof tool.additions === "number") additions += tool.additions;
|
|
770
|
-
if (typeof tool.deletions === "number") deletions += tool.deletions;
|
|
953
|
+
async function registerPackagedUserMcpClients() {
|
|
954
|
+
const launch = launchFromResolved(await resolveSideboardMcpServer());
|
|
955
|
+
const cursor = userCursorMcpConfigPath();
|
|
956
|
+
writeMergedMcpServersJson(cursor, launch);
|
|
957
|
+
const claude = userClaudeMcpConfigPath();
|
|
958
|
+
if (existsSync3(claude)) {
|
|
959
|
+
writeMergedMcpServersJson(claude, launch);
|
|
960
|
+
return { cursor, claude };
|
|
771
961
|
}
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
962
|
+
return { cursor };
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
// src/agents/orch-mcp-isolation.ts
|
|
966
|
+
function isInjectedOrchMcpName(name) {
|
|
967
|
+
const n = name.trim().toLowerCase();
|
|
968
|
+
return n === "sideboard" || n === "brightsy" || n.startsWith("brightsy_");
|
|
969
|
+
}
|
|
970
|
+
function asObject2(value) {
|
|
971
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
972
|
+
return value;
|
|
973
|
+
}
|
|
974
|
+
function listMcpNamesFromJsonMap(raw, key) {
|
|
975
|
+
return Object.keys(asObject2(asObject2(raw)[key])).filter((n) => n.trim());
|
|
976
|
+
}
|
|
977
|
+
function listMcpNamesFromCodexToml(text) {
|
|
978
|
+
try {
|
|
979
|
+
const parsed = parseToml(text);
|
|
980
|
+
return Object.keys(asObject2(parsed.mcp_servers)).filter((n) => n.trim());
|
|
981
|
+
} catch {
|
|
982
|
+
return [];
|
|
780
983
|
}
|
|
781
|
-
if (reads === 1) bits.push("explored 1 file");
|
|
782
|
-
else if (reads > 1) bits.push(`explored ${reads} files`);
|
|
783
|
-
if (searches === 1) bits.push("1 search");
|
|
784
|
-
else if (searches > 1) bits.push(`${searches} searches`);
|
|
785
|
-
if (shells === 1) bits.push("ran 1 command");
|
|
786
|
-
else if (shells > 1) bits.push(`ran ${shells} commands`);
|
|
787
|
-
if (others === 1) bits.push("1 tool");
|
|
788
|
-
else if (others > 1) bits.push(`${others} tools`);
|
|
789
|
-
if (bits.length === 0) return null;
|
|
790
|
-
return { text: bits.join(", "), additions, deletions };
|
|
791
984
|
}
|
|
792
|
-
function
|
|
793
|
-
|
|
794
|
-
return
|
|
985
|
+
function readJsonObject(path) {
|
|
986
|
+
try {
|
|
987
|
+
return JSON.parse(readFileSync2(path, "utf8"));
|
|
988
|
+
} catch {
|
|
989
|
+
return {};
|
|
795
990
|
}
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
991
|
+
}
|
|
992
|
+
function readText(path) {
|
|
993
|
+
try {
|
|
994
|
+
return readFileSync2(path, "utf8");
|
|
995
|
+
} catch {
|
|
996
|
+
return "";
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
function userCodexConfigPaths(home = homedir2()) {
|
|
1000
|
+
return [join3(home, ".codex", "config.toml"), join3(home, ".config", "codex", "config.toml")];
|
|
1001
|
+
}
|
|
1002
|
+
function userOpencodeConfigPaths(home = homedir2()) {
|
|
1003
|
+
return [
|
|
1004
|
+
join3(home, ".config", "opencode", "opencode.json"),
|
|
1005
|
+
join3(home, ".config", "opencode", "opencode.jsonc")
|
|
1006
|
+
];
|
|
1007
|
+
}
|
|
1008
|
+
function userMcpNamesToDisable(opts) {
|
|
1009
|
+
const keep = new Set(
|
|
1010
|
+
[...opts.injectedNames ?? []].map((n) => n.trim().toLowerCase()).filter(Boolean)
|
|
803
1011
|
);
|
|
804
|
-
const
|
|
805
|
-
const
|
|
806
|
-
const
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
const
|
|
810
|
-
if (
|
|
811
|
-
|
|
1012
|
+
const out = [];
|
|
1013
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1014
|
+
for (const raw of opts.names) {
|
|
1015
|
+
const name = raw.trim();
|
|
1016
|
+
if (!name) continue;
|
|
1017
|
+
const key = name.toLowerCase();
|
|
1018
|
+
if (seen.has(key) || keep.has(key) || isInjectedOrchMcpName(name)) continue;
|
|
1019
|
+
seen.add(key);
|
|
1020
|
+
out.push(name);
|
|
812
1021
|
}
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
const
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
return last.status === "running" ? label : `Finished ${label}`;
|
|
1022
|
+
return out;
|
|
1023
|
+
}
|
|
1024
|
+
function listUserCodexMcpNames(home = homedir2()) {
|
|
1025
|
+
const names = [];
|
|
1026
|
+
for (const path of userCodexConfigPaths(home)) {
|
|
1027
|
+
if (!existsSync4(path)) continue;
|
|
1028
|
+
names.push(...listMcpNamesFromCodexToml(readText(path)));
|
|
821
1029
|
}
|
|
822
|
-
return
|
|
1030
|
+
return names;
|
|
823
1031
|
}
|
|
824
|
-
function
|
|
825
|
-
|
|
826
|
-
|
|
1032
|
+
function listUserOpencodeMcpNames(home = homedir2()) {
|
|
1033
|
+
const names = [];
|
|
1034
|
+
for (const path of userOpencodeConfigPaths(home)) {
|
|
1035
|
+
if (!existsSync4(path)) continue;
|
|
1036
|
+
names.push(...listMcpNamesFromJsonMap(readJsonObject(path), "mcp"));
|
|
827
1037
|
}
|
|
828
|
-
return
|
|
1038
|
+
return names;
|
|
829
1039
|
}
|
|
830
|
-
function
|
|
831
|
-
|
|
1040
|
+
function listUserClaudeMcpNames(home = homedir2()) {
|
|
1041
|
+
const claudePath = home === homedir2() ? userClaudeMcpConfigPath() : join3(home, ".claude.json");
|
|
1042
|
+
if (!existsSync4(claudePath)) return [];
|
|
1043
|
+
return listMcpNamesFromJsonMap(readJsonObject(claudePath), "mcpServers");
|
|
832
1044
|
}
|
|
833
|
-
function
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
1045
|
+
function toCodexDisableUserMcpArgs(names) {
|
|
1046
|
+
const args = [];
|
|
1047
|
+
for (const name of names) {
|
|
1048
|
+
if (!/^[A-Za-z0-9_-]+$/.test(name)) continue;
|
|
1049
|
+
args.push("-c", `mcp_servers.${name}.enabled=false`);
|
|
837
1050
|
}
|
|
838
|
-
return
|
|
1051
|
+
return args;
|
|
839
1052
|
}
|
|
840
|
-
function
|
|
841
|
-
|
|
842
|
-
|
|
1053
|
+
function toCodexUnattendedAppsArgs() {
|
|
1054
|
+
return [
|
|
1055
|
+
"-c",
|
|
1056
|
+
"features.apps=false",
|
|
1057
|
+
"-c",
|
|
1058
|
+
"apps._default.enabled=false",
|
|
1059
|
+
"-c",
|
|
1060
|
+
"apps.posthog.enabled=false",
|
|
1061
|
+
"-c",
|
|
1062
|
+
"features.plugins=false"
|
|
1063
|
+
];
|
|
843
1064
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
1065
|
+
|
|
1066
|
+
// src/agents/message-parts.ts
|
|
1067
|
+
function asRecord(input) {
|
|
1068
|
+
if (input && typeof input === "object" && !Array.isArray(input)) {
|
|
1069
|
+
return input;
|
|
1070
|
+
}
|
|
1071
|
+
return void 0;
|
|
847
1072
|
}
|
|
848
|
-
function
|
|
849
|
-
|
|
850
|
-
return text.split("\n").length;
|
|
1073
|
+
function str(v) {
|
|
1074
|
+
return typeof v === "string" && v.trim() ? v : void 0;
|
|
851
1075
|
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
return {
|
|
858
|
-
additions: countLines(newS),
|
|
859
|
-
deletions: countLines(oldS)
|
|
860
|
-
};
|
|
861
|
-
}
|
|
862
|
-
return {};
|
|
1076
|
+
var CHAT_DETAIL_MAX = 72;
|
|
1077
|
+
function clipChatLine(value, max = CHAT_DETAIL_MAX) {
|
|
1078
|
+
const line = value.trim().split(/\r?\n/, 1)[0] ?? "";
|
|
1079
|
+
if (line.length <= max) return line;
|
|
1080
|
+
return `${line.slice(0, Math.max(1, max - 1))}\u2026`;
|
|
863
1081
|
}
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
const
|
|
867
|
-
if (
|
|
868
|
-
|
|
869
|
-
additions: Number(paired[1]),
|
|
870
|
-
deletions: Number(paired[2])
|
|
871
|
-
};
|
|
872
|
-
}
|
|
873
|
-
const verbose = result.match(
|
|
874
|
-
/(\d+)\s+insertions?(?:,\s*(\d+)\s+deletions?)?/i
|
|
875
|
-
);
|
|
876
|
-
if (verbose) {
|
|
877
|
-
return {
|
|
878
|
-
additions: Number(verbose[1]),
|
|
879
|
-
deletions: verbose[2] != null ? Number(verbose[2]) : void 0
|
|
880
|
-
};
|
|
881
|
-
}
|
|
882
|
-
return {};
|
|
1082
|
+
var SHELL_BIN_DIR = /^(?:\/usr\/local\/bin|\/usr\/bin|\/bin|\/opt\/homebrew\/bin|\/sbin|\/usr\/sbin)\//;
|
|
1083
|
+
function rootedPathWithSpaces(trimmed) {
|
|
1084
|
+
const [head = "", ...rest] = trimmed.split(/\s+/);
|
|
1085
|
+
if (SHELL_BIN_DIR.test(head)) return false;
|
|
1086
|
+
return rest.every((tok) => !/^[-/~$]/.test(tok) && !tok.includes("="));
|
|
883
1087
|
}
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
if (
|
|
887
|
-
const
|
|
888
|
-
|
|
1088
|
+
function looksLikeFilePath(value) {
|
|
1089
|
+
const trimmed = value.trim();
|
|
1090
|
+
if (!trimmed || /[\r\n;|&<>]/.test(trimmed)) return false;
|
|
1091
|
+
const rooted = trimmed.startsWith("/") || trimmed.startsWith("~/") || /^[A-Za-z]:[\\/]/.test(trimmed);
|
|
1092
|
+
if (/\s/.test(trimmed)) return rooted && rootedPathWithSpaces(trimmed);
|
|
1093
|
+
if (rooted) return true;
|
|
1094
|
+
return trimmed.includes("/") || trimmed.includes("\\");
|
|
889
1095
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
if (
|
|
894
|
-
|
|
895
|
-
return
|
|
1096
|
+
function stripWorktreePrefix(path, worktreePath) {
|
|
1097
|
+
if (!worktreePath) return path;
|
|
1098
|
+
const prefix = worktreePath.replace(/[/\\]+$/, "");
|
|
1099
|
+
if (path === prefix || path === `${prefix}/`) return "";
|
|
1100
|
+
if (path.startsWith(`${prefix}/`)) return path.slice(prefix.length + 1);
|
|
1101
|
+
return path;
|
|
896
1102
|
}
|
|
897
|
-
function
|
|
898
|
-
if (
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
return
|
|
1103
|
+
function visibleToolRowDetail(detail, description, worktreePath) {
|
|
1104
|
+
if (!detail?.trim()) return void 0;
|
|
1105
|
+
const raw = detail.trim();
|
|
1106
|
+
const desc = (description ?? "").trim();
|
|
1107
|
+
if (!looksLikeFilePath(raw)) {
|
|
1108
|
+
const clipped = clipChatLine(raw);
|
|
1109
|
+
if (!clipped || desc === clipped || desc === raw) return void 0;
|
|
1110
|
+
return clipped;
|
|
905
1111
|
}
|
|
906
|
-
|
|
907
|
-
|
|
1112
|
+
const rel = stripWorktreePrefix(raw, worktreePath);
|
|
1113
|
+
if (!rel) return void 0;
|
|
1114
|
+
const base = fileBasename(rel);
|
|
1115
|
+
if (desc && (desc === base || desc.endsWith(` ${base}`))) return void 0;
|
|
1116
|
+
if (rel.length <= 42) return rel;
|
|
1117
|
+
const parts = rel.split(/[/\\]/).filter(Boolean);
|
|
1118
|
+
if (parts.length >= 2) return `\u2026/${parts.slice(-2).join("/")}`;
|
|
1119
|
+
return base;
|
|
1120
|
+
}
|
|
1121
|
+
function toolDetail(name, input) {
|
|
1122
|
+
if (!input) return void 0;
|
|
1123
|
+
const command = str(input.command) ?? str(input.cmd);
|
|
1124
|
+
if (command) return clipChatLine(command);
|
|
1125
|
+
const pattern = str(input.pattern) ?? str(input.glob) ?? str(input.glob_pattern);
|
|
1126
|
+
const isSearch = /grep|glob|search|ripgrep|findfiles|semsearch/i.test(name);
|
|
1127
|
+
if (isSearch && pattern) {
|
|
1128
|
+
return pattern.length > 80 ? `${pattern.slice(0, 77)}\u2026` : pattern;
|
|
908
1129
|
}
|
|
909
|
-
|
|
910
|
-
|
|
1130
|
+
const path = str(input.file_path) ?? str(input.path) ?? str(input.filePath) ?? str(input.filename);
|
|
1131
|
+
if (path) return path;
|
|
1132
|
+
if (pattern) return pattern.length > 80 ? `${pattern.slice(0, 77)}\u2026` : pattern;
|
|
1133
|
+
const query = str(input.query) ?? str(input.prompt);
|
|
1134
|
+
if (query) return query.length > 80 ? `${query.slice(0, 77)}\u2026` : query;
|
|
1135
|
+
try {
|
|
1136
|
+
const raw = JSON.stringify(input);
|
|
1137
|
+
return raw.length > 80 ? `${raw.slice(0, 77)}\u2026` : raw;
|
|
1138
|
+
} catch {
|
|
1139
|
+
return name;
|
|
911
1140
|
}
|
|
912
|
-
return event;
|
|
913
1141
|
}
|
|
914
|
-
function
|
|
915
|
-
const
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
function applyAgentEvent(parts, event) {
|
|
920
|
-
if (event.type === "stdout") {
|
|
921
|
-
const data = event.data;
|
|
922
|
-
if (!data) return parts;
|
|
923
|
-
const next = [...parts];
|
|
924
|
-
const last = next[next.length - 1];
|
|
925
|
-
if (last?.type === "text" && sameParentId(last.parentId, event.parentId)) {
|
|
926
|
-
next[next.length - 1] = withPartTimes(
|
|
927
|
-
{
|
|
928
|
-
type: "text",
|
|
929
|
-
text: last.text + data,
|
|
930
|
-
...event.parentId ? { parentId: event.parentId } : {}
|
|
931
|
-
},
|
|
932
|
-
last
|
|
933
|
-
);
|
|
934
|
-
} else {
|
|
935
|
-
next.push(
|
|
936
|
-
withPartTimes({
|
|
937
|
-
type: "text",
|
|
938
|
-
text: data,
|
|
939
|
-
...event.parentId ? { parentId: event.parentId } : {}
|
|
940
|
-
})
|
|
941
|
-
);
|
|
942
|
-
}
|
|
943
|
-
return next;
|
|
1142
|
+
function toolDescription(name, input) {
|
|
1143
|
+
const n = name.replace(/^mcp__/, "").replace(/__/g, " \xB7 ");
|
|
1144
|
+
if (/^get_record_types$|recordTypes/i.test(name)) return "List record types";
|
|
1145
|
+
if (/connectedAgentRequest/i.test(name)) {
|
|
1146
|
+
return str(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
|
|
944
1147
|
}
|
|
945
|
-
if (
|
|
946
|
-
|
|
947
|
-
if (!data) return parts;
|
|
948
|
-
const next = [...parts];
|
|
949
|
-
if (event.replace) {
|
|
950
|
-
for (let i = next.length - 1; i >= 0; i--) {
|
|
951
|
-
const prev = next[i];
|
|
952
|
-
if (prev?.type === "thinking" && sameParentId(prev.parentId, event.parentId)) {
|
|
953
|
-
next[i] = withPartTimes(
|
|
954
|
-
{
|
|
955
|
-
type: "thinking",
|
|
956
|
-
text: clipThinkingForStore(data),
|
|
957
|
-
...event.parentId ? { parentId: event.parentId } : {}
|
|
958
|
-
},
|
|
959
|
-
prev
|
|
960
|
-
);
|
|
961
|
-
return next;
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
next.push(
|
|
965
|
-
withPartTimes({
|
|
966
|
-
type: "thinking",
|
|
967
|
-
text: clipThinkingForStore(data),
|
|
968
|
-
...event.parentId ? { parentId: event.parentId } : {}
|
|
969
|
-
})
|
|
970
|
-
);
|
|
971
|
-
return next;
|
|
972
|
-
}
|
|
973
|
-
const last = next[next.length - 1];
|
|
974
|
-
if (last?.type === "thinking" && sameParentId(last.parentId, event.parentId)) {
|
|
975
|
-
next[next.length - 1] = withPartTimes(
|
|
976
|
-
{
|
|
977
|
-
type: "thinking",
|
|
978
|
-
text: clipThinkingForStore(last.text + data),
|
|
979
|
-
...event.parentId ? { parentId: event.parentId } : {}
|
|
980
|
-
},
|
|
981
|
-
last
|
|
982
|
-
);
|
|
983
|
-
} else {
|
|
984
|
-
next.push(
|
|
985
|
-
withPartTimes({
|
|
986
|
-
type: "thinking",
|
|
987
|
-
text: clipThinkingForStore(data),
|
|
988
|
-
...event.parentId ? { parentId: event.parentId } : {}
|
|
989
|
-
})
|
|
990
|
-
);
|
|
991
|
-
}
|
|
992
|
-
return next;
|
|
1148
|
+
if (/present_artifact$/i.test(name)) {
|
|
1149
|
+
return str(input?.title) ? `Present ${str(input?.title)}` : "Present artifact";
|
|
993
1150
|
}
|
|
994
|
-
if (
|
|
995
|
-
|
|
996
|
-
const diff = diffFromInput(input);
|
|
997
|
-
const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
|
|
998
|
-
if (existing >= 0) {
|
|
999
|
-
const prev = parts[existing];
|
|
1000
|
-
const next = [...parts];
|
|
1001
|
-
const mergedInput = {
|
|
1002
|
-
...prev.input ?? {},
|
|
1003
|
-
...input ?? {}
|
|
1004
|
-
};
|
|
1005
|
-
const mergedRecord = Object.keys(mergedInput).length > 0 ? mergedInput : void 0;
|
|
1006
|
-
next[existing] = withPartTimes(
|
|
1007
|
-
{
|
|
1008
|
-
...prev,
|
|
1009
|
-
name: event.name || prev.name,
|
|
1010
|
-
input: mergedRecord,
|
|
1011
|
-
description: toolDescription(event.name || prev.name, mergedRecord),
|
|
1012
|
-
detail: toolDetail(event.name || prev.name, mergedRecord) ?? prev.detail,
|
|
1013
|
-
filePath: toolFilePath(mergedRecord) ?? prev.filePath,
|
|
1014
|
-
additions: diff.additions ?? prev.additions,
|
|
1015
|
-
deletions: diff.deletions ?? prev.deletions,
|
|
1016
|
-
parentId: event.parentId ?? prev.parentId
|
|
1017
|
-
},
|
|
1018
|
-
prev
|
|
1019
|
-
);
|
|
1020
|
-
return next;
|
|
1021
|
-
}
|
|
1022
|
-
return [
|
|
1023
|
-
...parts,
|
|
1024
|
-
withPartTimes({
|
|
1025
|
-
type: "tool",
|
|
1026
|
-
id: event.id,
|
|
1027
|
-
name: event.name,
|
|
1028
|
-
input,
|
|
1029
|
-
description: toolDescription(event.name, input),
|
|
1030
|
-
detail: toolDetail(event.name, input),
|
|
1031
|
-
status: "running",
|
|
1032
|
-
filePath: toolFilePath(input),
|
|
1033
|
-
...event.parentId ? { parentId: event.parentId } : {},
|
|
1034
|
-
...diff
|
|
1035
|
-
})
|
|
1036
|
-
];
|
|
1151
|
+
if (/present_plan$/i.test(name)) {
|
|
1152
|
+
return str(input?.title) ? `Plan ${str(input?.title)}` : "Present plan";
|
|
1037
1153
|
}
|
|
1038
|
-
if (
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
return parts.map((p, i) => {
|
|
1057
|
-
if (i !== existing || p.type !== "tool") return p;
|
|
1058
|
-
if (p.status === "done" || p.status === "error") return p;
|
|
1059
|
-
return withPartTimes(
|
|
1060
|
-
{
|
|
1061
|
-
...p,
|
|
1062
|
-
result: clipToolResultForStore(event.content) ?? p.result
|
|
1063
|
-
},
|
|
1064
|
-
p
|
|
1065
|
-
);
|
|
1066
|
-
});
|
|
1154
|
+
if (/present_schema$/i.test(name)) {
|
|
1155
|
+
return str(input?.title) ? `Schema ${str(input?.title)}` : "Present schema";
|
|
1156
|
+
}
|
|
1157
|
+
if (/present_files$/i.test(name)) {
|
|
1158
|
+
return str(input?.title) ? `Files ${str(input?.title)}` : "Present files";
|
|
1159
|
+
}
|
|
1160
|
+
if (/wait_for_job$/i.test(name)) {
|
|
1161
|
+
return str(input?.id) ? `Wait for ${str(input?.id)}` : "Wait for job";
|
|
1162
|
+
}
|
|
1163
|
+
if (/stop_job$/i.test(name)) {
|
|
1164
|
+
return str(input?.id) ? `Stop ${str(input?.id)}` : "Stop job";
|
|
1165
|
+
}
|
|
1166
|
+
if (isSubagentToolName(name)) {
|
|
1167
|
+
const desc = str(input?.description);
|
|
1168
|
+
const sub = asRecord(input?.subagentType);
|
|
1169
|
+
const kind = str(sub?.name) ?? str(sub?.kind) ?? str(input?.subagent_type) ?? str(input?.subagentType);
|
|
1170
|
+
if (/^spawn_agent$/i.test(name)) {
|
|
1171
|
+
return str(input?.prompt) ?? desc ?? "Subagent";
|
|
1067
1172
|
}
|
|
1068
|
-
if (
|
|
1069
|
-
return
|
|
1070
|
-
...parts,
|
|
1071
|
-
withPartTimes({
|
|
1072
|
-
type: "tool",
|
|
1073
|
-
id: event.id,
|
|
1074
|
-
name: "tool",
|
|
1075
|
-
description: "tool",
|
|
1076
|
-
status: event.isError ? "error" : "done",
|
|
1077
|
-
result: clipToolResultForStore(event.content),
|
|
1078
|
-
...event.parentId ? { parentId: event.parentId } : {},
|
|
1079
|
-
...fromResult.additions != null ? { additions: fromResult.additions } : {},
|
|
1080
|
-
...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
|
|
1081
|
-
})
|
|
1082
|
-
];
|
|
1173
|
+
if (/connectedAgentRequest/i.test(name)) {
|
|
1174
|
+
return str(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
|
|
1083
1175
|
}
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
{
|
|
1088
|
-
...p,
|
|
1089
|
-
status: event.isError ? "error" : "done",
|
|
1090
|
-
result: clipToolResultForStore(event.content),
|
|
1091
|
-
...fromResult.additions != null ? { additions: fromResult.additions } : {},
|
|
1092
|
-
...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
|
|
1093
|
-
},
|
|
1094
|
-
p
|
|
1095
|
-
);
|
|
1096
|
-
});
|
|
1176
|
+
if (desc && kind) return `${kind}: ${desc}${subagentLiveSuffix(input)}`;
|
|
1177
|
+
if (desc) return `${desc}${subagentLiveSuffix(input)}`;
|
|
1178
|
+
return `Subagent${subagentLiveSuffix(input)}`;
|
|
1097
1179
|
}
|
|
1098
|
-
|
|
1180
|
+
if (/^(create|update)_artifact$/i.test(name.replace(/^mcp__[^_]+__/, ""))) {
|
|
1181
|
+
return str(input?.title) ? `Artifact ${str(input?.title)}` : "Artifact";
|
|
1182
|
+
}
|
|
1183
|
+
if (!input) return n;
|
|
1184
|
+
if (/bash|shell|terminal/i.test(name) && str(input.command)) {
|
|
1185
|
+
const cmd = str(input.command);
|
|
1186
|
+
if (/git\s+fetch/i.test(cmd)) return "Fetch latest from origin and check status";
|
|
1187
|
+
if (/git\s+status/i.test(cmd)) return "Check git status";
|
|
1188
|
+
if (/git\s+log/i.test(cmd)) return "Inspect recent commits";
|
|
1189
|
+
if (/git\s+branch/i.test(cmd)) return "List branches";
|
|
1190
|
+
if (/grep|rg\b/i.test(cmd)) return "Search repository";
|
|
1191
|
+
return "Run shell command";
|
|
1192
|
+
}
|
|
1193
|
+
if (/edit|write|apply/i.test(name)) {
|
|
1194
|
+
const path = str(input.file_path) ?? str(input.path);
|
|
1195
|
+
return path ? `Edit ${path.split("/").pop()}` : "Edit file";
|
|
1196
|
+
}
|
|
1197
|
+
if (/read/i.test(name)) {
|
|
1198
|
+
const path = str(input.file_path) ?? str(input.path);
|
|
1199
|
+
return path ? `Read ${path.split("/").pop()}` : "Read file";
|
|
1200
|
+
}
|
|
1201
|
+
if (/grep/i.test(name)) return "Search files";
|
|
1202
|
+
if (/glob/i.test(name)) return "Find files";
|
|
1203
|
+
const compact = n.replace(/[_-]/g, "");
|
|
1204
|
+
if (/^taskoutput$/i.test(compact)) {
|
|
1205
|
+
return str(input.task_id) ? `Wait for ${str(input.task_id)}` : "Wait for background task";
|
|
1206
|
+
}
|
|
1207
|
+
if (/^taskstop$/i.test(compact)) {
|
|
1208
|
+
return str(input.task_id) ? `Stop ${str(input.task_id)}` : "Stop background task";
|
|
1209
|
+
}
|
|
1210
|
+
if (/^monitor$/i.test(compact)) {
|
|
1211
|
+
const command = str(input.command);
|
|
1212
|
+
return command ? `Watch ${command.length > 48 ? `${command.slice(0, 45)}\u2026` : command}` : "Watch command";
|
|
1213
|
+
}
|
|
1214
|
+
return n;
|
|
1099
1215
|
}
|
|
1100
|
-
function
|
|
1101
|
-
return
|
|
1102
|
-
|
|
1103
|
-
|
|
1216
|
+
function subagentLiveSuffix(input) {
|
|
1217
|
+
if (!input) return "";
|
|
1218
|
+
const bits = [];
|
|
1219
|
+
const status = str(input.live_status);
|
|
1220
|
+
if (status && !/^runn(ing)?$|^working$/i.test(status)) bits.push(status);
|
|
1221
|
+
if (typeof input.live_tool_uses === "number") bits.push(`${input.live_tool_uses} tools`);
|
|
1222
|
+
if (typeof input.live_duration_ms === "number") {
|
|
1223
|
+
bits.push(`${Math.max(0, Math.round(Number(input.live_duration_ms) / 1e3))}s`);
|
|
1224
|
+
}
|
|
1225
|
+
const last = str(input.live_last_tool);
|
|
1226
|
+
if (last) bits.push(last);
|
|
1227
|
+
return bits.length ? ` \xB7 ${bits.join(" \xB7 ")}` : "";
|
|
1104
1228
|
}
|
|
1105
|
-
function
|
|
1106
|
-
|
|
1229
|
+
function isSubagentToolName(name) {
|
|
1230
|
+
const n = (name ?? "").trim();
|
|
1231
|
+
if (/^(task|agent|spawn_agent)$/i.test(n)) return true;
|
|
1232
|
+
return /connectedAgentRequest/i.test(n);
|
|
1233
|
+
}
|
|
1234
|
+
function isInternalAgentStatusText(text) {
|
|
1235
|
+
const t = text.trim();
|
|
1236
|
+
if (!t) return false;
|
|
1237
|
+
if (/waiting for gate to pass/i.test(t)) return true;
|
|
1238
|
+
if (/^agent is running\b/i.test(t) && t.length < 160) return true;
|
|
1239
|
+
return false;
|
|
1240
|
+
}
|
|
1241
|
+
function isPollWrapperToolName(name) {
|
|
1242
|
+
const n = (name ?? "").replace(/[_-]/g, "");
|
|
1243
|
+
return /^(taskoutput|taskstop|sleep)$/i.test(n);
|
|
1244
|
+
}
|
|
1245
|
+
function lastTextPart(parts, type) {
|
|
1107
1246
|
for (let i = parts.length - 1; i >= 0; i--) {
|
|
1108
1247
|
const p = parts[i];
|
|
1109
|
-
if (
|
|
1248
|
+
if (p.type !== type) continue;
|
|
1110
1249
|
const text = p.text.trim();
|
|
1111
1250
|
if (!text || isInternalAgentStatusText(text)) continue;
|
|
1112
1251
|
return text;
|
|
1113
1252
|
}
|
|
1114
1253
|
return "";
|
|
1115
1254
|
}
|
|
1116
|
-
function
|
|
1117
|
-
|
|
1118
|
-
let out = text;
|
|
1119
|
-
out = out.replace(
|
|
1120
|
-
/\{"type":"(?:tool_use|tool_result|tool|thinking|usage|done|error)"[\s\S]*?\}\s*(?=\{"type":"|$|(?=[A-Za-z*#]))/g,
|
|
1121
|
-
""
|
|
1122
|
-
);
|
|
1123
|
-
out = out.replace(/\{"type":"(?:tool_use|tool_result|tool)"[\s\S]*$/g, "");
|
|
1124
|
-
return out.trim();
|
|
1125
|
-
}
|
|
1126
|
-
function isBrightsyNdjsonLine(line) {
|
|
1127
|
-
const t = line.trim();
|
|
1128
|
-
if (!t.startsWith("{")) return false;
|
|
1129
|
-
return /"type"\s*:\s*"(tool_use|tool_result|tool|thinking|usage|done|error|text)"/.test(t);
|
|
1130
|
-
}
|
|
1131
|
-
function finalizeParts(parts) {
|
|
1132
|
-
return parts.map(
|
|
1133
|
-
(p) => p.type === "tool" && p.status === "running" ? { ...p, status: "done" } : p
|
|
1134
|
-
);
|
|
1135
|
-
}
|
|
1136
|
-
function normalizeParseResult(parsed) {
|
|
1137
|
-
if (!parsed) return [];
|
|
1138
|
-
return Array.isArray(parsed) ? parsed : [parsed];
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
// src/agents/injected-mcp.ts
|
|
1142
|
-
import { existsSync as existsSync2, mkdtempSync, writeFileSync } from "fs";
|
|
1143
|
-
import { createRequire } from "module";
|
|
1144
|
-
import { tmpdir } from "os";
|
|
1145
|
-
import { dirname, join } from "path";
|
|
1146
|
-
import { fileURLToPath } from "url";
|
|
1147
|
-
|
|
1148
|
-
// src/mcp/profile.ts
|
|
1149
|
-
var SIDEBOARD_MCP_PROFILE_ENV = "SIDEBOARD_MCP_PROFILE";
|
|
1150
|
-
var WORKTREE_MCP_TOOLS = [
|
|
1151
|
-
"present_artifact",
|
|
1152
|
-
"ask_user",
|
|
1153
|
-
"present_plan",
|
|
1154
|
-
"present_schema",
|
|
1155
|
-
"present_files",
|
|
1156
|
-
"wait_for_job",
|
|
1157
|
-
"stop_job",
|
|
1158
|
-
"get_viewer_context",
|
|
1159
|
-
"update_viewer_context"
|
|
1160
|
-
];
|
|
1161
|
-
var WORKTREE_GITHUB_MCP_TOOLS = [
|
|
1162
|
-
"github_search_issues",
|
|
1163
|
-
"github_get_issue",
|
|
1164
|
-
"github_comment",
|
|
1165
|
-
"github_update_issue",
|
|
1166
|
-
"github_create_issue"
|
|
1167
|
-
];
|
|
1168
|
-
var WORKTREE_LINEAR_MCP_TOOLS = [
|
|
1169
|
-
"linear_list_teams",
|
|
1170
|
-
"linear_search_issues",
|
|
1171
|
-
"linear_get_issue",
|
|
1172
|
-
"linear_create_issue",
|
|
1173
|
-
"linear_update_issue",
|
|
1174
|
-
"linear_comment"
|
|
1175
|
-
];
|
|
1176
|
-
var WORKTREE_ABLETIME_MCP_TOOLS = [
|
|
1177
|
-
"abletime_orientation",
|
|
1178
|
-
"abletime_list_projects",
|
|
1179
|
-
"abletime_list_tasks",
|
|
1180
|
-
"abletime_search_tasks",
|
|
1181
|
-
"abletime_get_task",
|
|
1182
|
-
"abletime_comment",
|
|
1183
|
-
"abletime_update_task",
|
|
1184
|
-
"abletime_create_task",
|
|
1185
|
-
"abletime_ensure_task"
|
|
1186
|
-
];
|
|
1187
|
-
function sideboardMcpProfile(env = process.env) {
|
|
1188
|
-
return env[SIDEBOARD_MCP_PROFILE_ENV]?.trim().toLowerCase() === "worktree" ? "worktree" : "orchestration";
|
|
1255
|
+
function toolLabel(tool) {
|
|
1256
|
+
return tool.description || toolDescription(tool.name, tool.input) || tool.name;
|
|
1189
1257
|
}
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
"mcp__sideboard",
|
|
1194
|
-
"mcp__sideboard__*"
|
|
1195
|
-
];
|
|
1196
|
-
var SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS = [
|
|
1197
|
-
"mcp__sideboard__present_artifact",
|
|
1198
|
-
"mcp__sideboard__present_schema",
|
|
1199
|
-
"mcp__sideboard__present_files",
|
|
1200
|
-
"mcp__sideboard__ask_user",
|
|
1201
|
-
"mcp__sideboard__present_plan",
|
|
1202
|
-
"mcp__sideboard__wait_for_job",
|
|
1203
|
-
"mcp__sideboard__stop_job",
|
|
1204
|
-
"mcp__sideboard__get_viewer_context",
|
|
1205
|
-
"mcp__sideboard__update_viewer_context"
|
|
1206
|
-
];
|
|
1207
|
-
var SIDEBOARD_GITHUB_MCP_ALLOWED_TOOLS = ["mcp__sideboard__github_*"];
|
|
1208
|
-
var SIDEBOARD_LINEAR_MCP_ALLOWED_TOOLS = ["mcp__sideboard__linear_*"];
|
|
1209
|
-
var SIDEBOARD_ABLETIME_MCP_ALLOWED_TOOLS = ["mcp__sideboard__abletime_*"];
|
|
1210
|
-
function sideboardWorktreeAllowedTools(opts) {
|
|
1211
|
-
const out = [...SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS];
|
|
1212
|
-
if (opts?.github !== false) out.push(...SIDEBOARD_GITHUB_MCP_ALLOWED_TOOLS);
|
|
1213
|
-
if (opts?.linear) out.push(...SIDEBOARD_LINEAR_MCP_ALLOWED_TOOLS);
|
|
1214
|
-
if (opts?.abletime) out.push(...SIDEBOARD_ABLETIME_MCP_ALLOWED_TOOLS);
|
|
1215
|
-
return out;
|
|
1258
|
+
function fileBasename(path) {
|
|
1259
|
+
const parts = path.replace(/\/$/, "").split(/[/\\]/);
|
|
1260
|
+
return parts[parts.length - 1] || path;
|
|
1216
1261
|
}
|
|
1217
|
-
|
|
1218
|
-
"
|
|
1219
|
-
"
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
if (brightsyMcpCommandCache && now - brightsyMcpCommandCache.at < 6e4 && brightsyMcpCommandCache.command) {
|
|
1225
|
-
return brightsyMcpCommandCache.command;
|
|
1262
|
+
function classifyTool(name) {
|
|
1263
|
+
const compact = name.replace(/^mcp__[^_]+__/, "").replace(/[_-]/g, "");
|
|
1264
|
+
if (/bash|shell|terminal|^zsh$|^sh$/i.test(compact)) return "shell";
|
|
1265
|
+
if (/grep|glob|search|ripgrep|findfiles|semsearch/i.test(compact)) return "search";
|
|
1266
|
+
if (/^(read|cat)$/i.test(compact) || /^read/i.test(compact)) return "read";
|
|
1267
|
+
if (/edit|write|apply|strreplace|multiedit|updatefile|createfile/i.test(compact)) {
|
|
1268
|
+
return "edit";
|
|
1226
1269
|
}
|
|
1227
|
-
|
|
1228
|
-
const command = which.exitCode === 0 && which.stdout.trim() ? "brightsy-mcp" : "npx";
|
|
1229
|
-
brightsyMcpCommandCache = { at: now, command };
|
|
1230
|
-
return command;
|
|
1270
|
+
return "other";
|
|
1231
1271
|
}
|
|
1232
|
-
function
|
|
1233
|
-
|
|
1234
|
-
|
|
1272
|
+
function isShellToolName(name) {
|
|
1273
|
+
return classifyTool(name ?? "") === "shell";
|
|
1274
|
+
}
|
|
1275
|
+
function toolActivityLine(parts) {
|
|
1276
|
+
const tools = parts.filter((p) => {
|
|
1277
|
+
if (p.type !== "tool" || p.parentId) return false;
|
|
1278
|
+
if (isPollWrapperToolName(p.name)) return false;
|
|
1279
|
+
if (/present_plan$/i.test(p.name ?? "")) return false;
|
|
1280
|
+
if (/ask_user|AskUserQuestion/i.test(p.name ?? "")) return false;
|
|
1235
1281
|
return true;
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1282
|
+
});
|
|
1283
|
+
if (tools.length === 0) return null;
|
|
1284
|
+
const edited = [];
|
|
1285
|
+
let reads = 0;
|
|
1286
|
+
let searches = 0;
|
|
1287
|
+
let shells = 0;
|
|
1288
|
+
let others = 0;
|
|
1289
|
+
let additions = 0;
|
|
1290
|
+
let deletions = 0;
|
|
1291
|
+
for (const tool of tools) {
|
|
1292
|
+
const kind = classifyTool(tool.name);
|
|
1293
|
+
if (kind === "edit") {
|
|
1294
|
+
const path = tool.filePath ?? toolFilePath(tool.input);
|
|
1295
|
+
edited.push({
|
|
1296
|
+
name: path ? fileBasename(path) : tool.name,
|
|
1297
|
+
running: tool.status === "running"
|
|
1298
|
+
});
|
|
1299
|
+
} else if (kind === "read") reads += 1;
|
|
1300
|
+
else if (kind === "search") searches += 1;
|
|
1301
|
+
else if (kind === "shell") shells += 1;
|
|
1302
|
+
else others += 1;
|
|
1303
|
+
if (typeof tool.additions === "number") additions += tool.additions;
|
|
1304
|
+
if (typeof tool.deletions === "number") deletions += tool.deletions;
|
|
1305
|
+
}
|
|
1306
|
+
const bits = [];
|
|
1307
|
+
const editing = edited.filter((e) => e.running);
|
|
1308
|
+
const editedDone = edited.filter((e) => !e.running);
|
|
1309
|
+
if (editing.length === 1) bits.push(`Editing ${editing[0].name}`);
|
|
1310
|
+
else if (editing.length > 1) bits.push(`Editing ${editing.length} files`);
|
|
1311
|
+
if (editedDone.length === 1) bits.push(`${editing.length ? "edited" : "Edited"} ${editedDone[0].name}`);
|
|
1312
|
+
else if (editedDone.length > 1) {
|
|
1313
|
+
bits.push(`${editing.length ? "edited" : "Edited"} ${editedDone.length} files`);
|
|
1238
1314
|
}
|
|
1315
|
+
if (reads === 1) bits.push("explored 1 file");
|
|
1316
|
+
else if (reads > 1) bits.push(`explored ${reads} files`);
|
|
1317
|
+
if (searches === 1) bits.push("1 search");
|
|
1318
|
+
else if (searches > 1) bits.push(`${searches} searches`);
|
|
1319
|
+
if (shells === 1) bits.push("ran 1 command");
|
|
1320
|
+
else if (shells > 1) bits.push(`ran ${shells} commands`);
|
|
1321
|
+
if (others === 1) bits.push("1 tool");
|
|
1322
|
+
else if (others > 1) bits.push(`${others} tools`);
|
|
1323
|
+
if (bits.length === 0) return null;
|
|
1324
|
+
return { text: bits.join(", "), additions, deletions };
|
|
1239
1325
|
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1326
|
+
function liveActivitySummary(parts, opts) {
|
|
1327
|
+
if (opts?.queued && parts.length === 0) {
|
|
1328
|
+
return "Queued \u2014 waiting for a slot";
|
|
1329
|
+
}
|
|
1330
|
+
const tools = parts.filter((p) => p.type === "tool");
|
|
1331
|
+
const runningSubs = tools.filter(
|
|
1332
|
+
(t) => t.status === "running" && !t.parentId && isSubagentToolName(t.name)
|
|
1333
|
+
);
|
|
1334
|
+
const runningNested = [...tools].reverse().find((t) => t.status === "running" && t.parentId && !isPollWrapperToolName(t.name));
|
|
1335
|
+
const runningTop = [...tools].reverse().find(
|
|
1336
|
+
(t) => t.status === "running" && !t.parentId && !isPollWrapperToolName(t.name) && !isSubagentToolName(t.name)
|
|
1337
|
+
);
|
|
1338
|
+
const runningPoll = [...tools].reverse().find((t) => t.status === "running" && isPollWrapperToolName(t.name));
|
|
1339
|
+
const thinking = lastTextPart(parts, "thinking");
|
|
1340
|
+
const text = lastTextPart(parts, "text");
|
|
1341
|
+
if (runningSubs.length > 0) {
|
|
1342
|
+
const heads = runningSubs.map(toolLabel);
|
|
1343
|
+
const head = runningSubs.length === 1 ? heads[0] : `${runningSubs.length} subagents \xB7 ${heads.slice(0, 2).join(" \xB7 ")}`;
|
|
1344
|
+
if (runningNested) return `${head} \xB7 ${toolLabel(runningNested)}`;
|
|
1345
|
+
return head;
|
|
1346
|
+
}
|
|
1347
|
+
if (runningTop) return toolLabel(runningTop);
|
|
1348
|
+
if (runningPoll) return toolLabel(runningPoll);
|
|
1349
|
+
if (thinking) return thinking.length > 96 ? `\u2026${thinking.slice(-96)}` : thinking;
|
|
1350
|
+
if (text) return "Writing reply\u2026";
|
|
1351
|
+
const last = [...tools].reverse().find((t) => !isPollWrapperToolName(t.name)) ?? tools.at(-1);
|
|
1352
|
+
if (last) {
|
|
1353
|
+
const label = toolLabel(last);
|
|
1354
|
+
return last.status === "running" ? label : `Finished ${label}`;
|
|
1355
|
+
}
|
|
1356
|
+
return "Working\u2026";
|
|
1243
1357
|
}
|
|
1244
|
-
function
|
|
1245
|
-
|
|
1246
|
-
|
|
1358
|
+
function messagePartParentId(part) {
|
|
1359
|
+
if ("parentId" in part && typeof part.parentId === "string" && part.parentId.trim()) {
|
|
1360
|
+
return part.parentId;
|
|
1361
|
+
}
|
|
1362
|
+
return void 0;
|
|
1247
1363
|
}
|
|
1248
|
-
function
|
|
1249
|
-
|
|
1250
|
-
return typeof ds === "string" && ds.toLowerCase() === "brightsy";
|
|
1364
|
+
function sameParentId(a, b) {
|
|
1365
|
+
return (a ?? "") === (b ?? "");
|
|
1251
1366
|
}
|
|
1252
|
-
function
|
|
1253
|
-
if (
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
if (isBrightsyMcpToolName(part.name)) return true;
|
|
1257
|
-
if (/present_(schema|files)$/i.test(part.name) && toolInputUsesBrightsyDatasource(part.input)) {
|
|
1258
|
-
return true;
|
|
1259
|
-
}
|
|
1367
|
+
function withEventParentId(event, parentId) {
|
|
1368
|
+
if (!parentId) return event;
|
|
1369
|
+
if (event.type === "stdout" || event.type === "thinking" || event.type === "tool_use" || event.type === "tool_result") {
|
|
1370
|
+
return { ...event, parentId: event.parentId ?? parentId };
|
|
1260
1371
|
}
|
|
1261
|
-
return
|
|
1262
|
-
}
|
|
1263
|
-
function threadRequestsBrightsyMcp(thread) {
|
|
1264
|
-
return (thread?.messages ?? []).some(messageUsedBrightsyMcp);
|
|
1372
|
+
return event;
|
|
1265
1373
|
}
|
|
1266
|
-
function
|
|
1267
|
-
if (!
|
|
1268
|
-
|
|
1269
|
-
const alwaysOn = opts?.alwaysOnWorktree ?? Boolean(loadAppSettings().brightsy?.injectWorktreeMcp);
|
|
1270
|
-
if (alwaysOn) return true;
|
|
1271
|
-
return threadRequestsBrightsyMcp(thread);
|
|
1374
|
+
function withEventsParentId(events, parentId) {
|
|
1375
|
+
if (!parentId) return events;
|
|
1376
|
+
return events.map((event) => withEventParentId(event, parentId));
|
|
1272
1377
|
}
|
|
1273
|
-
function
|
|
1274
|
-
if (
|
|
1275
|
-
|
|
1276
|
-
}
|
|
1277
|
-
return {
|
|
1278
|
-
name,
|
|
1279
|
-
command: "npx",
|
|
1280
|
-
args: ["-y", "@brightsy/mcp-server"],
|
|
1281
|
-
...env ? { env } : {}
|
|
1282
|
-
};
|
|
1378
|
+
function toolFilePath(input) {
|
|
1379
|
+
if (!input) return void 0;
|
|
1380
|
+
return str(input.file_path) ?? str(input.path) ?? str(input.filePath) ?? str(input.filename);
|
|
1283
1381
|
}
|
|
1284
|
-
function
|
|
1285
|
-
|
|
1286
|
-
return
|
|
1287
|
-
BRIGHTSY_API_TOKEN: team.access_token,
|
|
1288
|
-
...team.refresh_token ? { BRIGHTSY_REFRESH_TOKEN: team.refresh_token } : {},
|
|
1289
|
-
BRIGHTSY_ACCOUNT_ID: team.id,
|
|
1290
|
-
BRIGHTSY_API_URL: endpoint
|
|
1291
|
-
};
|
|
1382
|
+
function countLines(text) {
|
|
1383
|
+
if (!text) return 0;
|
|
1384
|
+
return text.split("\n").length;
|
|
1292
1385
|
}
|
|
1293
|
-
function
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1386
|
+
function diffFromInput(input) {
|
|
1387
|
+
if (!input) return {};
|
|
1388
|
+
const oldS = str(input.old_string) ?? str(input.oldString);
|
|
1389
|
+
const newS = str(input.new_string) ?? str(input.newString) ?? str(input.content);
|
|
1390
|
+
if (oldS != null || newS != null) {
|
|
1391
|
+
return {
|
|
1392
|
+
additions: countLines(newS),
|
|
1393
|
+
deletions: countLines(oldS)
|
|
1394
|
+
};
|
|
1297
1395
|
}
|
|
1298
|
-
return
|
|
1396
|
+
return {};
|
|
1299
1397
|
}
|
|
1300
|
-
function
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
}
|
|
1308
|
-
} catch {
|
|
1398
|
+
function parseDiffStat(result) {
|
|
1399
|
+
if (!result) return {};
|
|
1400
|
+
const paired = result.match(/\+(\d+)\s+-(\d+)/);
|
|
1401
|
+
if (paired) {
|
|
1402
|
+
return {
|
|
1403
|
+
additions: Number(paired[1]),
|
|
1404
|
+
deletions: Number(paired[2])
|
|
1405
|
+
};
|
|
1309
1406
|
}
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
return
|
|
1407
|
+
const verbose = result.match(
|
|
1408
|
+
/(\d+)\s+insertions?(?:,\s*(\d+)\s+deletions?)?/i
|
|
1409
|
+
);
|
|
1410
|
+
if (verbose) {
|
|
1411
|
+
return {
|
|
1412
|
+
additions: Number(verbose[1]),
|
|
1413
|
+
deletions: verbose[2] != null ? Number(verbose[2]) : void 0
|
|
1414
|
+
};
|
|
1315
1415
|
}
|
|
1416
|
+
return {};
|
|
1316
1417
|
}
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
if (
|
|
1320
|
-
const
|
|
1321
|
-
|
|
1322
|
-
let dir = corePackageDir();
|
|
1323
|
-
for (let i = 0; i < 10; i++) {
|
|
1324
|
-
const candidates = [
|
|
1325
|
-
join(dir, "mcp/run-stdio.js"),
|
|
1326
|
-
join(dir, "mcp/run-stdio.cjs"),
|
|
1327
|
-
join(dir, "dist/mcp/run-stdio.js"),
|
|
1328
|
-
join(dir, "dist/mcp/run-stdio.cjs"),
|
|
1329
|
-
join(dir, "packages/core/dist/mcp/run-stdio.js"),
|
|
1330
|
-
join(dir, "packages/cli/dist/index.js"),
|
|
1331
|
-
join(dir, "cli/dist/index.js")
|
|
1332
|
-
];
|
|
1333
|
-
for (const p of candidates) {
|
|
1334
|
-
if (existsSync2(p) && !isAsarPath(p)) return p;
|
|
1335
|
-
}
|
|
1336
|
-
const parent = dirname(dir);
|
|
1337
|
-
if (parent === dir) break;
|
|
1338
|
-
dir = parent;
|
|
1339
|
-
}
|
|
1340
|
-
return null;
|
|
1418
|
+
var THINKING_PART_MAX_CHARS = 64e3;
|
|
1419
|
+
function clipThinkingForStore(text, max = THINKING_PART_MAX_CHARS) {
|
|
1420
|
+
if (text.length <= max) return text;
|
|
1421
|
+
const marker = "\u2026(earlier thinking trimmed)\n";
|
|
1422
|
+
return marker + text.slice(text.length - Math.max(1, max - marker.length));
|
|
1341
1423
|
}
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1424
|
+
var STDOUT_EVENT_MAX_CHARS = 16e3;
|
|
1425
|
+
var STDERR_EVENT_MAX_CHARS = 4e3;
|
|
1426
|
+
function clipEventTail(text, max) {
|
|
1427
|
+
if (text.length <= max) return text;
|
|
1428
|
+
const marker = "\u2026(truncated)\n";
|
|
1429
|
+
return marker + text.slice(text.length - Math.max(1, max - marker.length));
|
|
1430
|
+
}
|
|
1431
|
+
function clipAgentEventForPaint(event) {
|
|
1432
|
+
if (event.type === "tool_result" && event.content) {
|
|
1433
|
+
const content = clipToolResultForStore(event.content);
|
|
1434
|
+
if (content === event.content) return event;
|
|
1435
|
+
return { ...event, content };
|
|
1436
|
+
}
|
|
1437
|
+
if (event.type === "thinking" && event.data.length > THINKING_PART_MAX_CHARS) {
|
|
1438
|
+
return { ...event, data: clipThinkingForStore(event.data) };
|
|
1356
1439
|
}
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
return { name: "sideboard", command: which.stdout.trim(), args: ["mcp"] };
|
|
1440
|
+
if (event.type === "stdout" && event.data.length > STDOUT_EVENT_MAX_CHARS) {
|
|
1441
|
+
return { ...event, data: clipEventTail(event.data, STDOUT_EVENT_MAX_CHARS) };
|
|
1360
1442
|
}
|
|
1361
|
-
|
|
1443
|
+
if (event.type === "stderr" && event.data.length > STDERR_EVENT_MAX_CHARS) {
|
|
1444
|
+
return { ...event, data: clipEventTail(event.data, STDERR_EVENT_MAX_CHARS) };
|
|
1445
|
+
}
|
|
1446
|
+
return event;
|
|
1362
1447
|
}
|
|
1363
|
-
|
|
1364
|
-
const
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
if (
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1448
|
+
function withPartTimes(next, prev) {
|
|
1449
|
+
const now = Date.now();
|
|
1450
|
+
const startedAt = prev && typeof prev.startedAt === "number" ? prev.startedAt : now;
|
|
1451
|
+
return { ...next, startedAt, updatedAt: now };
|
|
1452
|
+
}
|
|
1453
|
+
function applyAgentEvent(parts, event) {
|
|
1454
|
+
if (event.type === "stdout") {
|
|
1455
|
+
const data = event.data;
|
|
1456
|
+
if (!data) return parts;
|
|
1457
|
+
const next = [...parts];
|
|
1458
|
+
const last = next[next.length - 1];
|
|
1459
|
+
if (last?.type === "text" && sameParentId(last.parentId, event.parentId)) {
|
|
1460
|
+
next[next.length - 1] = withPartTimes(
|
|
1461
|
+
{
|
|
1462
|
+
type: "text",
|
|
1463
|
+
text: last.text + data,
|
|
1464
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
1465
|
+
},
|
|
1466
|
+
last
|
|
1467
|
+
);
|
|
1468
|
+
} else {
|
|
1469
|
+
next.push(
|
|
1470
|
+
withPartTimes({
|
|
1471
|
+
type: "text",
|
|
1472
|
+
text: data,
|
|
1473
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
1474
|
+
})
|
|
1381
1475
|
);
|
|
1382
|
-
} catch {
|
|
1383
1476
|
}
|
|
1384
|
-
|
|
1385
|
-
servers.push(sideboard);
|
|
1477
|
+
return next;
|
|
1386
1478
|
}
|
|
1387
|
-
if (
|
|
1388
|
-
const
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
for (
|
|
1393
|
-
|
|
1394
|
-
if (
|
|
1395
|
-
|
|
1396
|
-
|
|
1479
|
+
if (event.type === "thinking") {
|
|
1480
|
+
const data = event.data;
|
|
1481
|
+
if (!data) return parts;
|
|
1482
|
+
const next = [...parts];
|
|
1483
|
+
if (event.replace) {
|
|
1484
|
+
for (let i = next.length - 1; i >= 0; i--) {
|
|
1485
|
+
const prev = next[i];
|
|
1486
|
+
if (prev?.type === "thinking" && sameParentId(prev.parentId, event.parentId)) {
|
|
1487
|
+
next[i] = withPartTimes(
|
|
1488
|
+
{
|
|
1489
|
+
type: "thinking",
|
|
1490
|
+
text: clipThinkingForStore(data),
|
|
1491
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
1492
|
+
},
|
|
1493
|
+
prev
|
|
1494
|
+
);
|
|
1495
|
+
return next;
|
|
1496
|
+
}
|
|
1397
1497
|
}
|
|
1498
|
+
next.push(
|
|
1499
|
+
withPartTimes({
|
|
1500
|
+
type: "thinking",
|
|
1501
|
+
text: clipThinkingForStore(data),
|
|
1502
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
1503
|
+
})
|
|
1504
|
+
);
|
|
1505
|
+
return next;
|
|
1506
|
+
}
|
|
1507
|
+
const last = next[next.length - 1];
|
|
1508
|
+
if (last?.type === "thinking" && sameParentId(last.parentId, event.parentId)) {
|
|
1509
|
+
next[next.length - 1] = withPartTimes(
|
|
1510
|
+
{
|
|
1511
|
+
type: "thinking",
|
|
1512
|
+
text: clipThinkingForStore(last.text + data),
|
|
1513
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
1514
|
+
},
|
|
1515
|
+
last
|
|
1516
|
+
);
|
|
1398
1517
|
} else {
|
|
1399
|
-
|
|
1518
|
+
next.push(
|
|
1519
|
+
withPartTimes({
|
|
1520
|
+
type: "thinking",
|
|
1521
|
+
text: clipThinkingForStore(data),
|
|
1522
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
1523
|
+
})
|
|
1524
|
+
);
|
|
1400
1525
|
}
|
|
1526
|
+
return next;
|
|
1401
1527
|
}
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
}
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
...env ? { env } : {}
|
|
1430
|
-
};
|
|
1431
|
-
}
|
|
1432
|
-
return out;
|
|
1433
|
-
}
|
|
1434
|
-
function toCodexMcpConfigArgs(servers) {
|
|
1435
|
-
const args = [];
|
|
1436
|
-
for (const s of servers) {
|
|
1437
|
-
const prefix = `mcp_servers.${s.name}`;
|
|
1438
|
-
args.push("-c", `${prefix}.command=${JSON.stringify(s.command)}`);
|
|
1439
|
-
if (s.args?.length) {
|
|
1440
|
-
args.push("-c", `${prefix}.args=${JSON.stringify(s.args)}`);
|
|
1528
|
+
if (event.type === "tool_use") {
|
|
1529
|
+
const input = asRecord(event.input);
|
|
1530
|
+
const diff = diffFromInput(input);
|
|
1531
|
+
const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
|
|
1532
|
+
if (existing >= 0) {
|
|
1533
|
+
const prev = parts[existing];
|
|
1534
|
+
const next = [...parts];
|
|
1535
|
+
const mergedInput = {
|
|
1536
|
+
...prev.input ?? {},
|
|
1537
|
+
...input ?? {}
|
|
1538
|
+
};
|
|
1539
|
+
const mergedRecord = Object.keys(mergedInput).length > 0 ? mergedInput : void 0;
|
|
1540
|
+
next[existing] = withPartTimes(
|
|
1541
|
+
{
|
|
1542
|
+
...prev,
|
|
1543
|
+
name: event.name || prev.name,
|
|
1544
|
+
input: mergedRecord,
|
|
1545
|
+
description: toolDescription(event.name || prev.name, mergedRecord),
|
|
1546
|
+
detail: toolDetail(event.name || prev.name, mergedRecord) ?? prev.detail,
|
|
1547
|
+
filePath: toolFilePath(mergedRecord) ?? prev.filePath,
|
|
1548
|
+
additions: diff.additions ?? prev.additions,
|
|
1549
|
+
deletions: diff.deletions ?? prev.deletions,
|
|
1550
|
+
parentId: event.parentId ?? prev.parentId
|
|
1551
|
+
},
|
|
1552
|
+
prev
|
|
1553
|
+
);
|
|
1554
|
+
return next;
|
|
1441
1555
|
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1556
|
+
return [
|
|
1557
|
+
...parts,
|
|
1558
|
+
withPartTimes({
|
|
1559
|
+
type: "tool",
|
|
1560
|
+
id: event.id,
|
|
1561
|
+
name: event.name,
|
|
1562
|
+
input,
|
|
1563
|
+
description: toolDescription(event.name, input),
|
|
1564
|
+
detail: toolDetail(event.name, input),
|
|
1565
|
+
status: "running",
|
|
1566
|
+
filePath: toolFilePath(input),
|
|
1567
|
+
...event.parentId ? { parentId: event.parentId } : {},
|
|
1568
|
+
...diff
|
|
1569
|
+
})
|
|
1570
|
+
];
|
|
1571
|
+
}
|
|
1572
|
+
if (event.type === "tool_result") {
|
|
1573
|
+
const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
|
|
1574
|
+
const fromResult = parseDiffStat(event.content);
|
|
1575
|
+
if (event.partial) {
|
|
1576
|
+
if (existing < 0) {
|
|
1577
|
+
return [
|
|
1578
|
+
...parts,
|
|
1579
|
+
withPartTimes({
|
|
1580
|
+
type: "tool",
|
|
1581
|
+
id: event.id,
|
|
1582
|
+
name: "tool",
|
|
1583
|
+
description: "tool",
|
|
1584
|
+
status: event.isError ? "error" : "running",
|
|
1585
|
+
result: clipToolResultForStore(event.content),
|
|
1586
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
1587
|
+
})
|
|
1588
|
+
];
|
|
1446
1589
|
}
|
|
1590
|
+
return parts.map((p, i) => {
|
|
1591
|
+
if (i !== existing || p.type !== "tool") return p;
|
|
1592
|
+
if (p.status === "done" || p.status === "error") return p;
|
|
1593
|
+
return withPartTimes(
|
|
1594
|
+
{
|
|
1595
|
+
...p,
|
|
1596
|
+
result: clipToolResultForStore(event.content) ?? p.result
|
|
1597
|
+
},
|
|
1598
|
+
p
|
|
1599
|
+
);
|
|
1600
|
+
});
|
|
1447
1601
|
}
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1602
|
+
if (existing < 0) {
|
|
1603
|
+
return [
|
|
1604
|
+
...parts,
|
|
1605
|
+
withPartTimes({
|
|
1606
|
+
type: "tool",
|
|
1607
|
+
id: event.id,
|
|
1608
|
+
name: "tool",
|
|
1609
|
+
description: "tool",
|
|
1610
|
+
status: event.isError ? "error" : "done",
|
|
1611
|
+
result: clipToolResultForStore(event.content),
|
|
1612
|
+
...event.parentId ? { parentId: event.parentId } : {},
|
|
1613
|
+
...fromResult.additions != null ? { additions: fromResult.additions } : {},
|
|
1614
|
+
...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
|
|
1615
|
+
})
|
|
1616
|
+
];
|
|
1617
|
+
}
|
|
1618
|
+
return parts.map((p, i) => {
|
|
1619
|
+
if (i !== existing || p.type !== "tool") return p;
|
|
1620
|
+
return withPartTimes(
|
|
1621
|
+
{
|
|
1622
|
+
...p,
|
|
1623
|
+
status: event.isError ? "error" : "done",
|
|
1624
|
+
result: clipToolResultForStore(event.content),
|
|
1625
|
+
...fromResult.additions != null ? { additions: fromResult.additions } : {},
|
|
1626
|
+
...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
|
|
1627
|
+
},
|
|
1628
|
+
p
|
|
1629
|
+
);
|
|
1630
|
+
});
|
|
1451
1631
|
}
|
|
1452
|
-
return
|
|
1632
|
+
return parts;
|
|
1453
1633
|
}
|
|
1454
|
-
function
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
mcp[s.name] = {
|
|
1459
|
-
type: "local",
|
|
1460
|
-
command: [s.command, ...s.args ?? []],
|
|
1461
|
-
enabled: true,
|
|
1462
|
-
...env ? { environment: env } : {}
|
|
1463
|
-
};
|
|
1464
|
-
}
|
|
1465
|
-
for (const name of opts?.disableNames ?? []) {
|
|
1466
|
-
const key = name.trim();
|
|
1467
|
-
if (!key || mcp[key]?.enabled === true) continue;
|
|
1468
|
-
mcp[key] = { ...mcp[key], enabled: false };
|
|
1469
|
-
}
|
|
1470
|
-
return JSON.stringify({ mcp });
|
|
1634
|
+
function partsToAssistantText(parts) {
|
|
1635
|
+
return parts.filter(
|
|
1636
|
+
(p) => p.type === "text" && !messagePartParentId(p)
|
|
1637
|
+
).map((p) => p.text).join("").trim();
|
|
1471
1638
|
}
|
|
1472
|
-
function
|
|
1473
|
-
if (
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
...env ? { env } : {}
|
|
1481
|
-
};
|
|
1639
|
+
function lastAssistantMessageText(parts) {
|
|
1640
|
+
if (!parts?.length) return "";
|
|
1641
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
1642
|
+
const p = parts[i];
|
|
1643
|
+
if (!p || p.type !== "text" || messagePartParentId(p)) continue;
|
|
1644
|
+
const text = p.text.trim();
|
|
1645
|
+
if (!text || isInternalAgentStatusText(text)) continue;
|
|
1646
|
+
return text;
|
|
1482
1647
|
}
|
|
1483
|
-
|
|
1484
|
-
const cfgPath = join(dir, "mcp.json");
|
|
1485
|
-
writeFileSync(cfgPath, JSON.stringify({ mcpServers }, null, 2));
|
|
1486
|
-
return cfgPath;
|
|
1648
|
+
return "";
|
|
1487
1649
|
}
|
|
1488
|
-
|
|
1489
|
-
|
|
1650
|
+
function stripBrightsyNdjsonNoise(text) {
|
|
1651
|
+
if (!text || !text.includes('"type"')) return text;
|
|
1652
|
+
let out = text;
|
|
1653
|
+
out = out.replace(
|
|
1654
|
+
/\{"type":"(?:tool_use|tool_result|tool|thinking|usage|done|error)"[\s\S]*?\}\s*(?=\{"type":"|$|(?=[A-Za-z*#]))/g,
|
|
1655
|
+
""
|
|
1656
|
+
);
|
|
1657
|
+
out = out.replace(/\{"type":"(?:tool_use|tool_result|tool)"[\s\S]*$/g, "");
|
|
1658
|
+
return out.trim();
|
|
1659
|
+
}
|
|
1660
|
+
function isBrightsyNdjsonLine(line) {
|
|
1661
|
+
const t = line.trim();
|
|
1662
|
+
if (!t.startsWith("{")) return false;
|
|
1663
|
+
return /"type"\s*:\s*"(tool_use|tool_result|tool|thinking|usage|done|error|text)"/.test(t);
|
|
1664
|
+
}
|
|
1665
|
+
function finalizeParts(parts) {
|
|
1666
|
+
return parts.map(
|
|
1667
|
+
(p) => p.type === "tool" && p.status === "running" ? { ...p, status: "done" } : p
|
|
1668
|
+
);
|
|
1669
|
+
}
|
|
1670
|
+
function normalizeParseResult(parsed) {
|
|
1671
|
+
if (!parsed) return [];
|
|
1672
|
+
return Array.isArray(parsed) ? parsed : [parsed];
|
|
1490
1673
|
}
|
|
1491
1674
|
|
|
1492
1675
|
// src/agents/types.ts
|
|
@@ -1550,24 +1733,15 @@ var BASE_ALLOWED_TOOLS = [
|
|
|
1550
1733
|
"Skill"
|
|
1551
1734
|
];
|
|
1552
1735
|
var CLAUDE_PRINT_BG_WAIT_CEILING_MS = 72e5;
|
|
1736
|
+
function isLocalDevAppDataDir(dir = process.env.SIDEBOARD_APP_DATA) {
|
|
1737
|
+
return /[/\\]\.sideboard[/\\]dev-app-data(?:[/\\]|$)/.test(dir ?? "");
|
|
1738
|
+
}
|
|
1553
1739
|
var CLAUDE_CHROME_ALLOWED_TOOLS = [
|
|
1554
1740
|
"mcp__claude-in-chrome",
|
|
1555
1741
|
"mcp__claude-in-chrome__*",
|
|
1556
1742
|
"Skill(claude-in-chrome)"
|
|
1557
1743
|
];
|
|
1558
1744
|
var CLAUDE_PROMPT_ARG_MAX = 2e5;
|
|
1559
|
-
var mcpListCache = null;
|
|
1560
|
-
async function loadMcpServers() {
|
|
1561
|
-
if (mcpListCache && Date.now() - mcpListCache.at < 3e4) {
|
|
1562
|
-
return mcpListCache.servers;
|
|
1563
|
-
}
|
|
1564
|
-
const claude = resolveClaudeExecutable();
|
|
1565
|
-
const mcpText = await run(claude, ["mcp", "list"], { reject: false });
|
|
1566
|
-
const servers = parseMcpList(`${mcpText.stdout}
|
|
1567
|
-
${mcpText.stderr}`);
|
|
1568
|
-
mcpListCache = { at: Date.now(), servers };
|
|
1569
|
-
return servers;
|
|
1570
|
-
}
|
|
1571
1745
|
function usageFromClaude(usage) {
|
|
1572
1746
|
if (!usage) return null;
|
|
1573
1747
|
const inputTokens = Number(usage.input_tokens ?? 0);
|
|
@@ -1854,7 +2028,7 @@ var claudeAdapter = {
|
|
|
1854
2028
|
async detect() {
|
|
1855
2029
|
const claude = resolveClaudeExecutable();
|
|
1856
2030
|
if (claude !== "claude") {
|
|
1857
|
-
if (!
|
|
2031
|
+
if (!existsSync5(claude)) {
|
|
1858
2032
|
return {
|
|
1859
2033
|
agent: "claude",
|
|
1860
2034
|
installed: false,
|
|
@@ -1903,7 +2077,7 @@ var claudeAdapter = {
|
|
|
1903
2077
|
);
|
|
1904
2078
|
}
|
|
1905
2079
|
const mode = permissionMode(thread);
|
|
1906
|
-
const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-
|
|
2080
|
+
const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-4TEMLESD.js");
|
|
1907
2081
|
const isOrchestrator = isOrchestratorThread2(thread);
|
|
1908
2082
|
const injectedServers = await buildInjectedMcpServers({
|
|
1909
2083
|
includeSideboard: true,
|
|
@@ -1922,10 +2096,9 @@ var claudeAdapter = {
|
|
|
1922
2096
|
...brightsyMcpAllowedTools(injectedBrightsyNames)
|
|
1923
2097
|
];
|
|
1924
2098
|
} else {
|
|
1925
|
-
const servers = await loadMcpServers();
|
|
1926
2099
|
allowedTools = [
|
|
1927
2100
|
...BASE_ALLOWED_TOOLS,
|
|
1928
|
-
...
|
|
2101
|
+
...mcpAllowToolsFromNames(userMcpNamesToDisable({ names: listUserClaudeMcpNames() })),
|
|
1929
2102
|
...sideboardWorktreeAllowedTools({
|
|
1930
2103
|
github: true,
|
|
1931
2104
|
linear: isLinearConnected(),
|
|
@@ -1953,9 +2126,7 @@ var claudeAdapter = {
|
|
|
1953
2126
|
const mcpConfigPath = writeMcpServersConfig(injectedServers);
|
|
1954
2127
|
if (mcpConfigPath) {
|
|
1955
2128
|
args.push("--mcp-config", mcpConfigPath);
|
|
1956
|
-
|
|
1957
|
-
args.push("--strict-mcp-config");
|
|
1958
|
-
}
|
|
2129
|
+
args.push("--strict-mcp-config");
|
|
1959
2130
|
}
|
|
1960
2131
|
if (chromeOn) {
|
|
1961
2132
|
args.push("--chrome");
|
|
@@ -1987,8 +2158,10 @@ var claudeAdapter = {
|
|
|
1987
2158
|
CLAUDE_CODE_FORWARD_SUBAGENT_TEXT: "1",
|
|
1988
2159
|
CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS: process.env.CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS ?? String(CLAUDE_PRINT_BG_WAIT_CEILING_MS),
|
|
1989
2160
|
// `--strict-mcp-config` still fetches claude.ai connectors on some CLI
|
|
1990
|
-
// builds (anthropics/claude-code#60252). Coordinators
|
|
1991
|
-
|
|
2161
|
+
// builds (anthropics/claude-code#60252). Coordinators never need them.
|
|
2162
|
+
// Local `pnpm dev` also disables them: those HTTP connectors hang the
|
|
2163
|
+
// first worktree turn when Electron's cwd is this Sideboard repo.
|
|
2164
|
+
...isOrchestrator || isLocalDevAppDataDir() ? { ENABLE_CLAUDEAI_MCP_SERVERS: "false" } : {}
|
|
1992
2165
|
}
|
|
1993
2166
|
};
|
|
1994
2167
|
},
|
|
@@ -2139,179 +2312,6 @@ function parseIssuesJson(raw) {
|
|
|
2139
2312
|
import { existsSync as existsSync6, readFileSync as readFileSync3, statSync } from "fs";
|
|
2140
2313
|
import { homedir as homedir3 } from "os";
|
|
2141
2314
|
import { join as join4 } from "path";
|
|
2142
|
-
|
|
2143
|
-
// src/agents/orch-mcp-isolation.ts
|
|
2144
|
-
import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
|
|
2145
|
-
import { homedir as homedir2 } from "os";
|
|
2146
|
-
import { join as join3 } from "path";
|
|
2147
|
-
import { parse as parseToml } from "smol-toml";
|
|
2148
|
-
|
|
2149
|
-
// src/agents/user-mcp-config.ts
|
|
2150
|
-
import { existsSync as existsSync4, mkdirSync, readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
2151
|
-
import { homedir } from "os";
|
|
2152
|
-
import { dirname as dirname2, join as join2 } from "path";
|
|
2153
|
-
function userCursorMcpConfigPath() {
|
|
2154
|
-
return join2(homedir(), ".cursor", "mcp.json");
|
|
2155
|
-
}
|
|
2156
|
-
function userClaudeMcpConfigPath() {
|
|
2157
|
-
return join2(homedir(), ".claude.json");
|
|
2158
|
-
}
|
|
2159
|
-
function asObject(value) {
|
|
2160
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
2161
|
-
return { ...value };
|
|
2162
|
-
}
|
|
2163
|
-
function stripElectronSpawnEnv(env) {
|
|
2164
|
-
if (!env) return void 0;
|
|
2165
|
-
const next = { ...env };
|
|
2166
|
-
delete next.ELECTRON_RUN_AS_NODE;
|
|
2167
|
-
delete next.ELECTRON_RUN_AS_NODE;
|
|
2168
|
-
return Object.keys(next).length > 0 ? next : void 0;
|
|
2169
|
-
}
|
|
2170
|
-
function mergeSideboardIntoMcpServersJson(existing, sideboard) {
|
|
2171
|
-
const root = asObject(existing);
|
|
2172
|
-
const servers = asObject(root.mcpServers);
|
|
2173
|
-
const env = stripElectronSpawnEnv(sideboard.env);
|
|
2174
|
-
servers.sideboard = {
|
|
2175
|
-
type: "stdio",
|
|
2176
|
-
command: sideboard.command,
|
|
2177
|
-
...sideboard.args && sideboard.args.length > 0 ? { args: sideboard.args } : {},
|
|
2178
|
-
...env ? { env } : {}
|
|
2179
|
-
};
|
|
2180
|
-
return { ...root, mcpServers: servers };
|
|
2181
|
-
}
|
|
2182
|
-
function writeMergedMcpServersJson(configPath, sideboard) {
|
|
2183
|
-
let existing = {};
|
|
2184
|
-
if (existsSync4(configPath)) {
|
|
2185
|
-
try {
|
|
2186
|
-
existing = JSON.parse(readFileSync(configPath, "utf8"));
|
|
2187
|
-
} catch {
|
|
2188
|
-
existing = {};
|
|
2189
|
-
}
|
|
2190
|
-
}
|
|
2191
|
-
const next = mergeSideboardIntoMcpServersJson(existing, sideboard);
|
|
2192
|
-
mkdirSync(dirname2(configPath), { recursive: true });
|
|
2193
|
-
writeFileSync2(configPath, `${JSON.stringify(next, null, 2)}
|
|
2194
|
-
`);
|
|
2195
|
-
}
|
|
2196
|
-
function launchFromResolved(server) {
|
|
2197
|
-
return {
|
|
2198
|
-
command: server.command,
|
|
2199
|
-
args: server.args,
|
|
2200
|
-
env: {
|
|
2201
|
-
...server.env ?? {},
|
|
2202
|
-
SIDEBOARD_APP_DATA: appDataDir()
|
|
2203
|
-
}
|
|
2204
|
-
};
|
|
2205
|
-
}
|
|
2206
|
-
async function registerPackagedUserMcpClients() {
|
|
2207
|
-
const launch = launchFromResolved(await resolveSideboardMcpServer());
|
|
2208
|
-
const cursor = userCursorMcpConfigPath();
|
|
2209
|
-
writeMergedMcpServersJson(cursor, launch);
|
|
2210
|
-
const claude = userClaudeMcpConfigPath();
|
|
2211
|
-
if (existsSync4(claude)) {
|
|
2212
|
-
writeMergedMcpServersJson(claude, launch);
|
|
2213
|
-
return { cursor, claude };
|
|
2214
|
-
}
|
|
2215
|
-
return { cursor };
|
|
2216
|
-
}
|
|
2217
|
-
|
|
2218
|
-
// src/agents/orch-mcp-isolation.ts
|
|
2219
|
-
function isInjectedOrchMcpName(name) {
|
|
2220
|
-
const n = name.trim().toLowerCase();
|
|
2221
|
-
return n === "sideboard" || n === "brightsy" || n.startsWith("brightsy_");
|
|
2222
|
-
}
|
|
2223
|
-
function asObject2(value) {
|
|
2224
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
2225
|
-
return value;
|
|
2226
|
-
}
|
|
2227
|
-
function listMcpNamesFromJsonMap(raw, key) {
|
|
2228
|
-
return Object.keys(asObject2(asObject2(raw)[key])).filter((n) => n.trim());
|
|
2229
|
-
}
|
|
2230
|
-
function listMcpNamesFromCodexToml(text) {
|
|
2231
|
-
try {
|
|
2232
|
-
const parsed = parseToml(text);
|
|
2233
|
-
return Object.keys(asObject2(parsed.mcp_servers)).filter((n) => n.trim());
|
|
2234
|
-
} catch {
|
|
2235
|
-
return [];
|
|
2236
|
-
}
|
|
2237
|
-
}
|
|
2238
|
-
function readJsonObject(path) {
|
|
2239
|
-
try {
|
|
2240
|
-
return JSON.parse(readFileSync2(path, "utf8"));
|
|
2241
|
-
} catch {
|
|
2242
|
-
return {};
|
|
2243
|
-
}
|
|
2244
|
-
}
|
|
2245
|
-
function readText(path) {
|
|
2246
|
-
try {
|
|
2247
|
-
return readFileSync2(path, "utf8");
|
|
2248
|
-
} catch {
|
|
2249
|
-
return "";
|
|
2250
|
-
}
|
|
2251
|
-
}
|
|
2252
|
-
function userCodexConfigPaths(home = homedir2()) {
|
|
2253
|
-
return [join3(home, ".codex", "config.toml"), join3(home, ".config", "codex", "config.toml")];
|
|
2254
|
-
}
|
|
2255
|
-
function userOpencodeConfigPaths(home = homedir2()) {
|
|
2256
|
-
return [
|
|
2257
|
-
join3(home, ".config", "opencode", "opencode.json"),
|
|
2258
|
-
join3(home, ".config", "opencode", "opencode.jsonc")
|
|
2259
|
-
];
|
|
2260
|
-
}
|
|
2261
|
-
function userMcpNamesToDisable(opts) {
|
|
2262
|
-
const keep = new Set(
|
|
2263
|
-
[...opts.injectedNames ?? []].map((n) => n.trim().toLowerCase()).filter(Boolean)
|
|
2264
|
-
);
|
|
2265
|
-
const out = [];
|
|
2266
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2267
|
-
for (const raw of opts.names) {
|
|
2268
|
-
const name = raw.trim();
|
|
2269
|
-
if (!name) continue;
|
|
2270
|
-
const key = name.toLowerCase();
|
|
2271
|
-
if (seen.has(key) || keep.has(key) || isInjectedOrchMcpName(name)) continue;
|
|
2272
|
-
seen.add(key);
|
|
2273
|
-
out.push(name);
|
|
2274
|
-
}
|
|
2275
|
-
return out;
|
|
2276
|
-
}
|
|
2277
|
-
function listUserCodexMcpNames(home = homedir2()) {
|
|
2278
|
-
const names = [];
|
|
2279
|
-
for (const path of userCodexConfigPaths(home)) {
|
|
2280
|
-
if (!existsSync5(path)) continue;
|
|
2281
|
-
names.push(...listMcpNamesFromCodexToml(readText(path)));
|
|
2282
|
-
}
|
|
2283
|
-
return names;
|
|
2284
|
-
}
|
|
2285
|
-
function listUserOpencodeMcpNames(home = homedir2()) {
|
|
2286
|
-
const names = [];
|
|
2287
|
-
for (const path of userOpencodeConfigPaths(home)) {
|
|
2288
|
-
if (!existsSync5(path)) continue;
|
|
2289
|
-
names.push(...listMcpNamesFromJsonMap(readJsonObject(path), "mcp"));
|
|
2290
|
-
}
|
|
2291
|
-
return names;
|
|
2292
|
-
}
|
|
2293
|
-
function toCodexDisableUserMcpArgs(names) {
|
|
2294
|
-
const args = [];
|
|
2295
|
-
for (const name of names) {
|
|
2296
|
-
if (!/^[A-Za-z0-9_-]+$/.test(name)) continue;
|
|
2297
|
-
args.push("-c", `mcp_servers.${name}.enabled=false`);
|
|
2298
|
-
}
|
|
2299
|
-
return args;
|
|
2300
|
-
}
|
|
2301
|
-
function toCodexUnattendedAppsArgs() {
|
|
2302
|
-
return [
|
|
2303
|
-
"-c",
|
|
2304
|
-
"features.apps=false",
|
|
2305
|
-
"-c",
|
|
2306
|
-
"apps._default.enabled=false",
|
|
2307
|
-
"-c",
|
|
2308
|
-
"apps.posthog.enabled=false",
|
|
2309
|
-
"-c",
|
|
2310
|
-
"features.plugins=false"
|
|
2311
|
-
];
|
|
2312
|
-
}
|
|
2313
|
-
|
|
2314
|
-
// src/agents/codex.ts
|
|
2315
2315
|
var CODEX_PROMPT_ARG_MAX = 2e5;
|
|
2316
2316
|
var FALLBACK_CODEX_MODELS = [
|
|
2317
2317
|
{ id: "gpt-5.6-sol", displayName: "GPT-5.6 Sol" },
|
|
@@ -4056,6 +4056,7 @@ export {
|
|
|
4056
4056
|
parseMcpList,
|
|
4057
4057
|
sanitizeMcpServerName,
|
|
4058
4058
|
mcpAllowTools,
|
|
4059
|
+
mcpAllowToolsFromNames,
|
|
4059
4060
|
mcpAuthWarnings,
|
|
4060
4061
|
SIDEBOARD_MCP_PROFILE_ENV,
|
|
4061
4062
|
WORKTREE_MCP_TOOLS,
|
|
@@ -4071,16 +4072,16 @@ export {
|
|
|
4071
4072
|
shouldInjectBrightsyMcp,
|
|
4072
4073
|
brightsyMcpAllowedTools,
|
|
4073
4074
|
writeInjectedMcpConfig,
|
|
4074
|
-
PLAN_MODE_INSTRUCTION,
|
|
4075
|
-
permissionMode,
|
|
4076
|
-
mcpStatusThinkingFromClaudeInit,
|
|
4077
|
-
claudeAdapter,
|
|
4078
4075
|
userCursorMcpConfigPath,
|
|
4079
4076
|
userClaudeMcpConfigPath,
|
|
4080
4077
|
mergeSideboardIntoMcpServersJson,
|
|
4081
4078
|
registerPackagedUserMcpClients,
|
|
4082
4079
|
isInjectedOrchMcpName,
|
|
4083
4080
|
userMcpNamesToDisable,
|
|
4081
|
+
PLAN_MODE_INSTRUCTION,
|
|
4082
|
+
permissionMode,
|
|
4083
|
+
mcpStatusThinkingFromClaudeInit,
|
|
4084
|
+
claudeAdapter,
|
|
4084
4085
|
listCodexModels,
|
|
4085
4086
|
codexAdapter,
|
|
4086
4087
|
isCursorApiKeyConfigured,
|