@sideboard-ai/core 0.1.217 → 0.1.219
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-HETHRRZJ.js → agents-IFVGY77W.js} +9 -9
- package/dist/{agents-WBDDLRBY.js → agents-IK3U4Z2O.js} +8 -7
- 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-C32SBNA3.js → chunk-EIICOB7P.js} +457 -294
- package/dist/{chunk-LS6TSOCE.js → chunk-HSYE5ID6.js} +5 -5
- package/dist/{chunk-UFHNSFAS.js → chunk-HVUVQXZ2.js} +5 -1
- package/dist/{chunk-7MTVRNP4.js → chunk-IAP5NXQ4.js} +1206 -1192
- package/dist/{chunk-SALM35KD.js → chunk-IILG7DPX.js} +5 -5
- package/dist/{chunk-DFTVZEZY.js → chunk-K642ORWF.js} +1085 -1057
- package/dist/{chunk-NI243N27.js → chunk-KTZESHYH.js} +15 -7
- package/dist/{chunk-FNGXKY5W.js → chunk-N2VST3NL.js} +68 -8
- package/dist/{chunk-XYKVRRC2.js → chunk-NFWPDEN4.js} +15 -7
- package/dist/{chunk-KFD2OXCE.js → chunk-P2EAAM3H.js} +68 -8
- 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-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-D2IKXMF3.js → chunk-XGE6YUO6.js} +453 -289
- 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-JLMQVORO.js} +6 -6
- package/dist/{coordinator-prompt-LE2GQZ2X.js → coordinator-prompt-VZ7OSRLD.js} +6 -5
- package/dist/{global-workspace-6P236KKG.js → global-workspace-3FDAUNN2.js} +6 -6
- package/dist/{global-workspace-KT3FRHZL.js → global-workspace-QPSJEXGR.js} +6 -5
- package/dist/index.cjs +1107 -826
- package/dist/index.d.cts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +24 -18
- package/dist/mcp/run-stdio.cjs +1912 -1652
- package/dist/mcp/run-stdio.js +16 -14
- package/dist/{orchestrator-MI7A5DQZ.js → orchestrator-5VVR7LOU.js} +9 -8
- package/dist/{orchestrator-WVZPTHBV.js → orchestrator-LFC3V3AX.js} +10 -10
- package/dist/{run-EBWL2Q4N.js → run-QDZCTP4Y.js} +2 -1
- package/dist/{run-CAGKZLHA.js → run-UEQM4JMZ.js} +2 -1
- package/dist/{workspaces-R5LJCJRB.js → workspaces-76W7IEKR.js} +8 -6
- package/dist/{workspaces-KM7KTLVO.js → workspaces-TDBA64II.js} +8 -5
- 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-N2VST3NL.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,1168 @@ 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";
|
|
648
|
-
}
|
|
649
|
-
if (!input) return n;
|
|
650
|
-
if (/bash|shell|terminal/i.test(name) && str(input.command)) {
|
|
651
|
-
const cmd = str(input.command);
|
|
652
|
-
if (/git\s+fetch/i.test(cmd)) return "Fetch latest from origin and check status";
|
|
653
|
-
if (/git\s+status/i.test(cmd)) return "Check git status";
|
|
654
|
-
if (/git\s+log/i.test(cmd)) return "Inspect recent commits";
|
|
655
|
-
if (/git\s+branch/i.test(cmd)) return "List branches";
|
|
656
|
-
if (/grep|rg\b/i.test(cmd)) return "Search repository";
|
|
657
|
-
return "Run shell command";
|
|
658
|
-
}
|
|
659
|
-
if (/edit|write|apply/i.test(name)) {
|
|
660
|
-
const path = str(input.file_path) ?? str(input.path);
|
|
661
|
-
return path ? `Edit ${path.split("/").pop()}` : "Edit file";
|
|
791
|
+
applyAgentRunnerHeapEnv(sideboard.env);
|
|
792
|
+
servers.push(sideboard);
|
|
662
793
|
}
|
|
663
|
-
if (
|
|
664
|
-
const
|
|
665
|
-
|
|
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
|
+
}
|
|
666
808
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
if (
|
|
671
|
-
return
|
|
809
|
+
return servers;
|
|
810
|
+
}
|
|
811
|
+
function cursorSafeMcpLaunch(command, args) {
|
|
812
|
+
if (process.platform === "win32") {
|
|
813
|
+
return args && args.length > 0 ? { command, args } : { command };
|
|
672
814
|
}
|
|
673
|
-
|
|
674
|
-
|
|
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
|
+
};
|
|
675
838
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
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`);
|
|
679
858
|
}
|
|
680
|
-
return
|
|
859
|
+
return args;
|
|
681
860
|
}
|
|
682
|
-
function
|
|
683
|
-
|
|
684
|
-
const
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
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
|
+
};
|
|
690
871
|
}
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
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 };
|
|
876
|
+
}
|
|
877
|
+
return JSON.stringify({ mcp });
|
|
694
878
|
}
|
|
695
|
-
function
|
|
696
|
-
const
|
|
697
|
-
|
|
698
|
-
|
|
879
|
+
function stripElectronFromUserMcpEntry(entry) {
|
|
880
|
+
const next = { ...entry };
|
|
881
|
+
const env = next.env;
|
|
882
|
+
if (!env || typeof env !== "object" || Array.isArray(env)) return next;
|
|
883
|
+
const cleaned = { ...env };
|
|
884
|
+
delete cleaned.ELECTRON_RUN_AS_NODE;
|
|
885
|
+
next.env = cleaned;
|
|
886
|
+
return next;
|
|
699
887
|
}
|
|
700
|
-
function
|
|
701
|
-
const
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
888
|
+
function writeMcpServersConfig(servers, extraServers) {
|
|
889
|
+
const mcpServers = {};
|
|
890
|
+
for (const s of servers) {
|
|
891
|
+
const env = mcpSpawnEnv(s.env);
|
|
892
|
+
mcpServers[s.name] = {
|
|
893
|
+
command: s.command,
|
|
894
|
+
...s.args ? { args: s.args } : {},
|
|
895
|
+
...env ? { env } : {}
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
if (extraServers) {
|
|
899
|
+
for (const [rawName, entry] of Object.entries(extraServers)) {
|
|
900
|
+
const name = rawName.trim();
|
|
901
|
+
if (!name || mcpServers[name] || !entry) continue;
|
|
902
|
+
mcpServers[name] = stripElectronFromUserMcpEntry(entry);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
if (Object.keys(mcpServers).length === 0) return null;
|
|
906
|
+
const dir = mkdtempSync(join(tmpdir(), "sideboard-mcp-"));
|
|
907
|
+
const cfgPath = join(dir, "mcp.json");
|
|
908
|
+
writeFileSync(cfgPath, JSON.stringify({ mcpServers }, null, 2));
|
|
909
|
+
return cfgPath;
|
|
706
910
|
}
|
|
707
|
-
function
|
|
708
|
-
|
|
709
|
-
return /^(taskoutput|taskstop|sleep)$/i.test(n);
|
|
911
|
+
async function writeInjectedMcpConfig(opts) {
|
|
912
|
+
return writeMcpServersConfig(await buildInjectedMcpServers(opts));
|
|
710
913
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
const text = p.text.trim();
|
|
716
|
-
if (!text || isInternalAgentStatusText(text)) continue;
|
|
717
|
-
return text;
|
|
718
|
-
}
|
|
719
|
-
return "";
|
|
914
|
+
|
|
915
|
+
// src/agents/user-mcp-config.ts
|
|
916
|
+
function userCursorMcpConfigPath() {
|
|
917
|
+
return join2(homedir(), ".cursor", "mcp.json");
|
|
720
918
|
}
|
|
721
|
-
function
|
|
722
|
-
return
|
|
919
|
+
function userClaudeMcpConfigPath() {
|
|
920
|
+
return join2(homedir(), ".claude.json");
|
|
723
921
|
}
|
|
724
|
-
function
|
|
725
|
-
|
|
726
|
-
return
|
|
922
|
+
function asObject(value) {
|
|
923
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
924
|
+
return { ...value };
|
|
727
925
|
}
|
|
728
|
-
function
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
return "edit";
|
|
735
|
-
}
|
|
736
|
-
return "other";
|
|
926
|
+
function stripElectronSpawnEnv(env) {
|
|
927
|
+
if (!env) return void 0;
|
|
928
|
+
const next = { ...env };
|
|
929
|
+
delete next.ELECTRON_RUN_AS_NODE;
|
|
930
|
+
delete next.ELECTRON_RUN_AS_NODE;
|
|
931
|
+
return Object.keys(next).length > 0 ? next : void 0;
|
|
737
932
|
}
|
|
738
|
-
function
|
|
739
|
-
|
|
933
|
+
function mergeSideboardIntoMcpServersJson(existing, sideboard) {
|
|
934
|
+
const root = asObject(existing);
|
|
935
|
+
const servers = asObject(root.mcpServers);
|
|
936
|
+
const env = stripElectronSpawnEnv(sideboard.env);
|
|
937
|
+
servers.sideboard = {
|
|
938
|
+
type: "stdio",
|
|
939
|
+
command: sideboard.command,
|
|
940
|
+
...sideboard.args && sideboard.args.length > 0 ? { args: sideboard.args } : {},
|
|
941
|
+
...env ? { env } : {}
|
|
942
|
+
};
|
|
943
|
+
return { ...root, mcpServers: servers };
|
|
740
944
|
}
|
|
741
|
-
function
|
|
742
|
-
|
|
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;
|
|
771
|
-
}
|
|
772
|
-
const bits = [];
|
|
773
|
-
const editing = edited.filter((e) => e.running);
|
|
774
|
-
const editedDone = edited.filter((e) => !e.running);
|
|
775
|
-
if (editing.length === 1) bits.push(`Editing ${editing[0].name}`);
|
|
776
|
-
else if (editing.length > 1) bits.push(`Editing ${editing.length} files`);
|
|
777
|
-
if (editedDone.length === 1) bits.push(`${editing.length ? "edited" : "Edited"} ${editedDone[0].name}`);
|
|
778
|
-
else if (editedDone.length > 1) {
|
|
779
|
-
bits.push(`${editing.length ? "edited" : "Edited"} ${editedDone.length} files`);
|
|
945
|
+
function writeMergedMcpServersJson(configPath, sideboard) {
|
|
946
|
+
let existing = {};
|
|
947
|
+
if (existsSync3(configPath)) {
|
|
948
|
+
try {
|
|
949
|
+
existing = JSON.parse(readFileSync(configPath, "utf8"));
|
|
950
|
+
} catch {
|
|
951
|
+
existing = {};
|
|
952
|
+
}
|
|
780
953
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
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 };
|
|
954
|
+
const next = mergeSideboardIntoMcpServersJson(existing, sideboard);
|
|
955
|
+
mkdirSync(dirname2(configPath), { recursive: true });
|
|
956
|
+
writeFileSync2(configPath, `${JSON.stringify(next, null, 2)}
|
|
957
|
+
`);
|
|
791
958
|
}
|
|
792
|
-
function
|
|
793
|
-
|
|
794
|
-
|
|
959
|
+
function launchFromResolved(server) {
|
|
960
|
+
return {
|
|
961
|
+
command: server.command,
|
|
962
|
+
args: server.args,
|
|
963
|
+
env: {
|
|
964
|
+
...server.env ?? {},
|
|
965
|
+
SIDEBOARD_APP_DATA: appDataDir()
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
async function registerPackagedUserMcpClients() {
|
|
970
|
+
const launch = launchFromResolved(await resolveSideboardMcpServer());
|
|
971
|
+
const cursor = userCursorMcpConfigPath();
|
|
972
|
+
writeMergedMcpServersJson(cursor, launch);
|
|
973
|
+
const claude = userClaudeMcpConfigPath();
|
|
974
|
+
if (existsSync3(claude)) {
|
|
975
|
+
writeMergedMcpServersJson(claude, launch);
|
|
976
|
+
return { cursor, claude };
|
|
795
977
|
}
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
const
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
978
|
+
return { cursor };
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// src/agents/orch-mcp-isolation.ts
|
|
982
|
+
function isInjectedOrchMcpName(name) {
|
|
983
|
+
const n = name.trim().toLowerCase();
|
|
984
|
+
return n === "sideboard" || n === "brightsy" || n.startsWith("brightsy_");
|
|
985
|
+
}
|
|
986
|
+
function asObject2(value) {
|
|
987
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
988
|
+
return value;
|
|
989
|
+
}
|
|
990
|
+
function listMcpNamesFromJsonMap(raw, key) {
|
|
991
|
+
return Object.keys(asObject2(asObject2(raw)[key])).filter((n) => n.trim());
|
|
992
|
+
}
|
|
993
|
+
function listMcpNamesFromCodexToml(text) {
|
|
994
|
+
try {
|
|
995
|
+
const parsed = parseToml(text);
|
|
996
|
+
return Object.keys(asObject2(parsed.mcp_servers)).filter((n) => n.trim());
|
|
997
|
+
} catch {
|
|
998
|
+
return [];
|
|
812
999
|
}
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
const label = toolLabel(last);
|
|
820
|
-
return last.status === "running" ? label : `Finished ${label}`;
|
|
1000
|
+
}
|
|
1001
|
+
function readJsonObject(path) {
|
|
1002
|
+
try {
|
|
1003
|
+
return JSON.parse(readFileSync2(path, "utf8"));
|
|
1004
|
+
} catch {
|
|
1005
|
+
return {};
|
|
821
1006
|
}
|
|
822
|
-
return "Working\u2026";
|
|
823
1007
|
}
|
|
824
|
-
function
|
|
825
|
-
|
|
826
|
-
return
|
|
1008
|
+
function readText(path) {
|
|
1009
|
+
try {
|
|
1010
|
+
return readFileSync2(path, "utf8");
|
|
1011
|
+
} catch {
|
|
1012
|
+
return "";
|
|
827
1013
|
}
|
|
828
|
-
return void 0;
|
|
829
1014
|
}
|
|
830
|
-
function
|
|
831
|
-
return (
|
|
1015
|
+
function userCodexConfigPaths(home = homedir2()) {
|
|
1016
|
+
return [join3(home, ".codex", "config.toml"), join3(home, ".config", "codex", "config.toml")];
|
|
832
1017
|
}
|
|
833
|
-
function
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
1018
|
+
function userOpencodeConfigPaths(home = homedir2()) {
|
|
1019
|
+
return [
|
|
1020
|
+
join3(home, ".config", "opencode", "opencode.json"),
|
|
1021
|
+
join3(home, ".config", "opencode", "opencode.jsonc")
|
|
1022
|
+
];
|
|
1023
|
+
}
|
|
1024
|
+
function userMcpNamesToDisable(opts) {
|
|
1025
|
+
const keep = new Set(
|
|
1026
|
+
[...opts.injectedNames ?? []].map((n) => n.trim().toLowerCase()).filter(Boolean)
|
|
1027
|
+
);
|
|
1028
|
+
const out = [];
|
|
1029
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1030
|
+
for (const raw of opts.names) {
|
|
1031
|
+
const name = raw.trim();
|
|
1032
|
+
if (!name) continue;
|
|
1033
|
+
const key = name.toLowerCase();
|
|
1034
|
+
if (seen.has(key) || keep.has(key) || isInjectedOrchMcpName(name)) continue;
|
|
1035
|
+
seen.add(key);
|
|
1036
|
+
out.push(name);
|
|
837
1037
|
}
|
|
838
|
-
return
|
|
1038
|
+
return out;
|
|
839
1039
|
}
|
|
840
|
-
function
|
|
841
|
-
|
|
842
|
-
|
|
1040
|
+
function listUserCodexMcpNames(home = homedir2()) {
|
|
1041
|
+
const names = [];
|
|
1042
|
+
for (const path of userCodexConfigPaths(home)) {
|
|
1043
|
+
if (!existsSync4(path)) continue;
|
|
1044
|
+
names.push(...listMcpNamesFromCodexToml(readText(path)));
|
|
1045
|
+
}
|
|
1046
|
+
return names;
|
|
843
1047
|
}
|
|
844
|
-
function
|
|
845
|
-
|
|
846
|
-
|
|
1048
|
+
function listUserOpencodeMcpNames(home = homedir2()) {
|
|
1049
|
+
const names = [];
|
|
1050
|
+
for (const path of userOpencodeConfigPaths(home)) {
|
|
1051
|
+
if (!existsSync4(path)) continue;
|
|
1052
|
+
names.push(...listMcpNamesFromJsonMap(readJsonObject(path), "mcp"));
|
|
1053
|
+
}
|
|
1054
|
+
return names;
|
|
847
1055
|
}
|
|
848
|
-
function
|
|
849
|
-
|
|
850
|
-
|
|
1056
|
+
function userClaudeMcpConfigObject(home = homedir2()) {
|
|
1057
|
+
const claudePath = home === homedir2() ? userClaudeMcpConfigPath() : join3(home, ".claude.json");
|
|
1058
|
+
if (!existsSync4(claudePath)) return {};
|
|
1059
|
+
return asObject2(asObject2(readJsonObject(claudePath)).mcpServers);
|
|
851
1060
|
}
|
|
852
|
-
function
|
|
853
|
-
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
deletions: countLines(oldS)
|
|
860
|
-
};
|
|
1061
|
+
function listUserClaudeMcpServerEntries(home = homedir2()) {
|
|
1062
|
+
const out = {};
|
|
1063
|
+
for (const [rawName, value] of Object.entries(userClaudeMcpConfigObject(home))) {
|
|
1064
|
+
const name = rawName.trim();
|
|
1065
|
+
if (!name || isInjectedOrchMcpName(name)) continue;
|
|
1066
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
1067
|
+
out[name] = value;
|
|
861
1068
|
}
|
|
862
|
-
return
|
|
1069
|
+
return out;
|
|
863
1070
|
}
|
|
864
|
-
function
|
|
865
|
-
|
|
866
|
-
const
|
|
867
|
-
|
|
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
|
-
};
|
|
1071
|
+
function toCodexDisableUserMcpArgs(names) {
|
|
1072
|
+
const args = [];
|
|
1073
|
+
for (const name of names) {
|
|
1074
|
+
if (!/^[A-Za-z0-9_-]+$/.test(name)) continue;
|
|
1075
|
+
args.push("-c", `mcp_servers.${name}.enabled=false`);
|
|
881
1076
|
}
|
|
882
|
-
return
|
|
1077
|
+
return args;
|
|
883
1078
|
}
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
return marker + text.slice(text.length - Math.max(1, max - marker.length));
|
|
1079
|
+
function toCodexUnattendedAppsArgs() {
|
|
1080
|
+
return [
|
|
1081
|
+
"-c",
|
|
1082
|
+
"features.apps=false",
|
|
1083
|
+
"-c",
|
|
1084
|
+
"apps._default.enabled=false",
|
|
1085
|
+
"-c",
|
|
1086
|
+
"apps.posthog.enabled=false",
|
|
1087
|
+
"-c",
|
|
1088
|
+
"features.plugins=false"
|
|
1089
|
+
];
|
|
896
1090
|
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
return
|
|
902
|
-
}
|
|
903
|
-
if (event.type === "thinking" && event.data.length > THINKING_PART_MAX_CHARS) {
|
|
904
|
-
return { ...event, data: clipThinkingForStore(event.data) };
|
|
905
|
-
}
|
|
906
|
-
if (event.type === "stdout" && event.data.length > STDOUT_EVENT_MAX_CHARS) {
|
|
907
|
-
return { ...event, data: clipEventTail(event.data, STDOUT_EVENT_MAX_CHARS) };
|
|
908
|
-
}
|
|
909
|
-
if (event.type === "stderr" && event.data.length > STDERR_EVENT_MAX_CHARS) {
|
|
910
|
-
return { ...event, data: clipEventTail(event.data, STDERR_EVENT_MAX_CHARS) };
|
|
1091
|
+
|
|
1092
|
+
// src/agents/message-parts.ts
|
|
1093
|
+
function asRecord(input) {
|
|
1094
|
+
if (input && typeof input === "object" && !Array.isArray(input)) {
|
|
1095
|
+
return input;
|
|
911
1096
|
}
|
|
912
|
-
return
|
|
1097
|
+
return void 0;
|
|
913
1098
|
}
|
|
914
|
-
function
|
|
915
|
-
|
|
916
|
-
const startedAt = prev && typeof prev.startedAt === "number" ? prev.startedAt : now;
|
|
917
|
-
return { ...next, startedAt, updatedAt: now };
|
|
1099
|
+
function str(v) {
|
|
1100
|
+
return typeof v === "string" && v.trim() ? v : void 0;
|
|
918
1101
|
}
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1102
|
+
var CHAT_DETAIL_MAX = 72;
|
|
1103
|
+
function clipChatLine(value, max = CHAT_DETAIL_MAX) {
|
|
1104
|
+
const line = value.trim().split(/\r?\n/, 1)[0] ?? "";
|
|
1105
|
+
if (line.length <= max) return line;
|
|
1106
|
+
return `${line.slice(0, Math.max(1, max - 1))}\u2026`;
|
|
1107
|
+
}
|
|
1108
|
+
var SHELL_BIN_DIR = /^(?:\/usr\/local\/bin|\/usr\/bin|\/bin|\/opt\/homebrew\/bin|\/sbin|\/usr\/sbin)\//;
|
|
1109
|
+
function rootedPathWithSpaces(trimmed) {
|
|
1110
|
+
const [head = "", ...rest] = trimmed.split(/\s+/);
|
|
1111
|
+
if (SHELL_BIN_DIR.test(head)) return false;
|
|
1112
|
+
return rest.every((tok) => !/^[-/~$]/.test(tok) && !tok.includes("="));
|
|
1113
|
+
}
|
|
1114
|
+
function looksLikeFilePath(value) {
|
|
1115
|
+
const trimmed = value.trim();
|
|
1116
|
+
if (!trimmed || /[\r\n;|&<>]/.test(trimmed)) return false;
|
|
1117
|
+
const rooted = trimmed.startsWith("/") || trimmed.startsWith("~/") || /^[A-Za-z]:[\\/]/.test(trimmed);
|
|
1118
|
+
if (/\s/.test(trimmed)) return rooted && rootedPathWithSpaces(trimmed);
|
|
1119
|
+
if (rooted) return true;
|
|
1120
|
+
return trimmed.includes("/") || trimmed.includes("\\");
|
|
1121
|
+
}
|
|
1122
|
+
function stripWorktreePrefix(path, worktreePath) {
|
|
1123
|
+
if (!worktreePath) return path;
|
|
1124
|
+
const prefix = worktreePath.replace(/[/\\]+$/, "");
|
|
1125
|
+
if (path === prefix || path === `${prefix}/`) return "";
|
|
1126
|
+
if (path.startsWith(`${prefix}/`)) return path.slice(prefix.length + 1);
|
|
1127
|
+
return path;
|
|
1128
|
+
}
|
|
1129
|
+
function visibleToolRowDetail(detail, description, worktreePath) {
|
|
1130
|
+
if (!detail?.trim()) return void 0;
|
|
1131
|
+
const raw = detail.trim();
|
|
1132
|
+
const desc = (description ?? "").trim();
|
|
1133
|
+
if (!looksLikeFilePath(raw)) {
|
|
1134
|
+
const clipped = clipChatLine(raw);
|
|
1135
|
+
if (!clipped || desc === clipped || desc === raw) return void 0;
|
|
1136
|
+
return clipped;
|
|
944
1137
|
}
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
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;
|
|
1138
|
+
const rel = stripWorktreePrefix(raw, worktreePath);
|
|
1139
|
+
if (!rel) return void 0;
|
|
1140
|
+
const base = fileBasename(rel);
|
|
1141
|
+
if (desc && (desc === base || desc.endsWith(` ${base}`))) return void 0;
|
|
1142
|
+
if (rel.length <= 42) return rel;
|
|
1143
|
+
const parts = rel.split(/[/\\]/).filter(Boolean);
|
|
1144
|
+
if (parts.length >= 2) return `\u2026/${parts.slice(-2).join("/")}`;
|
|
1145
|
+
return base;
|
|
1146
|
+
}
|
|
1147
|
+
function toolDetail(name, input) {
|
|
1148
|
+
if (!input) return void 0;
|
|
1149
|
+
const command = str(input.command) ?? str(input.cmd);
|
|
1150
|
+
if (command) return clipChatLine(command);
|
|
1151
|
+
const pattern = str(input.pattern) ?? str(input.glob) ?? str(input.glob_pattern);
|
|
1152
|
+
const isSearch = /grep|glob|search|ripgrep|findfiles|semsearch/i.test(name);
|
|
1153
|
+
if (isSearch && pattern) {
|
|
1154
|
+
return pattern.length > 80 ? `${pattern.slice(0, 77)}\u2026` : pattern;
|
|
993
1155
|
}
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
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
|
-
];
|
|
1156
|
+
const path = str(input.file_path) ?? str(input.path) ?? str(input.filePath) ?? str(input.filename);
|
|
1157
|
+
if (path) return path;
|
|
1158
|
+
if (pattern) return pattern.length > 80 ? `${pattern.slice(0, 77)}\u2026` : pattern;
|
|
1159
|
+
const query = str(input.query) ?? str(input.prompt);
|
|
1160
|
+
if (query) return query.length > 80 ? `${query.slice(0, 77)}\u2026` : query;
|
|
1161
|
+
try {
|
|
1162
|
+
const raw = JSON.stringify(input);
|
|
1163
|
+
return raw.length > 80 ? `${raw.slice(0, 77)}\u2026` : raw;
|
|
1164
|
+
} catch {
|
|
1165
|
+
return name;
|
|
1037
1166
|
}
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1167
|
+
}
|
|
1168
|
+
function toolDescription(name, input) {
|
|
1169
|
+
const n = name.replace(/^mcp__/, "").replace(/__/g, " \xB7 ");
|
|
1170
|
+
if (/^get_record_types$|recordTypes/i.test(name)) return "List record types";
|
|
1171
|
+
if (/connectedAgentRequest/i.test(name)) {
|
|
1172
|
+
return str(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
|
|
1173
|
+
}
|
|
1174
|
+
if (/present_artifact$/i.test(name)) {
|
|
1175
|
+
return str(input?.title) ? `Present ${str(input?.title)}` : "Present artifact";
|
|
1176
|
+
}
|
|
1177
|
+
if (/present_plan$/i.test(name)) {
|
|
1178
|
+
return str(input?.title) ? `Plan ${str(input?.title)}` : "Present plan";
|
|
1179
|
+
}
|
|
1180
|
+
if (/present_schema$/i.test(name)) {
|
|
1181
|
+
return str(input?.title) ? `Schema ${str(input?.title)}` : "Present schema";
|
|
1182
|
+
}
|
|
1183
|
+
if (/present_files$/i.test(name)) {
|
|
1184
|
+
return str(input?.title) ? `Files ${str(input?.title)}` : "Present files";
|
|
1185
|
+
}
|
|
1186
|
+
if (/wait_for_job$/i.test(name)) {
|
|
1187
|
+
return str(input?.id) ? `Wait for ${str(input?.id)}` : "Wait for job";
|
|
1188
|
+
}
|
|
1189
|
+
if (/stop_job$/i.test(name)) {
|
|
1190
|
+
return str(input?.id) ? `Stop ${str(input?.id)}` : "Stop job";
|
|
1191
|
+
}
|
|
1192
|
+
if (isSubagentToolName(name)) {
|
|
1193
|
+
const desc = str(input?.description);
|
|
1194
|
+
const sub = asRecord(input?.subagentType);
|
|
1195
|
+
const kind = str(sub?.name) ?? str(sub?.kind) ?? str(input?.subagent_type) ?? str(input?.subagentType);
|
|
1196
|
+
if (/^spawn_agent$/i.test(name)) {
|
|
1197
|
+
return str(input?.prompt) ?? desc ?? "Subagent";
|
|
1067
1198
|
}
|
|
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
|
-
];
|
|
1199
|
+
if (/connectedAgentRequest/i.test(name)) {
|
|
1200
|
+
return str(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
|
|
1083
1201
|
}
|
|
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
|
-
});
|
|
1202
|
+
if (desc && kind) return `${kind}: ${desc}${subagentLiveSuffix(input)}`;
|
|
1203
|
+
if (desc) return `${desc}${subagentLiveSuffix(input)}`;
|
|
1204
|
+
return `Subagent${subagentLiveSuffix(input)}`;
|
|
1097
1205
|
}
|
|
1098
|
-
|
|
1206
|
+
if (/^(create|update)_artifact$/i.test(name.replace(/^mcp__[^_]+__/, ""))) {
|
|
1207
|
+
return str(input?.title) ? `Artifact ${str(input?.title)}` : "Artifact";
|
|
1208
|
+
}
|
|
1209
|
+
if (!input) return n;
|
|
1210
|
+
if (/bash|shell|terminal/i.test(name) && str(input.command)) {
|
|
1211
|
+
const cmd = str(input.command);
|
|
1212
|
+
if (/git\s+fetch/i.test(cmd)) return "Fetch latest from origin and check status";
|
|
1213
|
+
if (/git\s+status/i.test(cmd)) return "Check git status";
|
|
1214
|
+
if (/git\s+log/i.test(cmd)) return "Inspect recent commits";
|
|
1215
|
+
if (/git\s+branch/i.test(cmd)) return "List branches";
|
|
1216
|
+
if (/grep|rg\b/i.test(cmd)) return "Search repository";
|
|
1217
|
+
return "Run shell command";
|
|
1218
|
+
}
|
|
1219
|
+
if (/edit|write|apply/i.test(name)) {
|
|
1220
|
+
const path = str(input.file_path) ?? str(input.path);
|
|
1221
|
+
return path ? `Edit ${path.split("/").pop()}` : "Edit file";
|
|
1222
|
+
}
|
|
1223
|
+
if (/read/i.test(name)) {
|
|
1224
|
+
const path = str(input.file_path) ?? str(input.path);
|
|
1225
|
+
return path ? `Read ${path.split("/").pop()}` : "Read file";
|
|
1226
|
+
}
|
|
1227
|
+
if (/grep/i.test(name)) return "Search files";
|
|
1228
|
+
if (/glob/i.test(name)) return "Find files";
|
|
1229
|
+
const compact = n.replace(/[_-]/g, "");
|
|
1230
|
+
if (/^taskoutput$/i.test(compact)) {
|
|
1231
|
+
return str(input.task_id) ? `Wait for ${str(input.task_id)}` : "Wait for background task";
|
|
1232
|
+
}
|
|
1233
|
+
if (/^taskstop$/i.test(compact)) {
|
|
1234
|
+
return str(input.task_id) ? `Stop ${str(input.task_id)}` : "Stop background task";
|
|
1235
|
+
}
|
|
1236
|
+
if (/^monitor$/i.test(compact)) {
|
|
1237
|
+
const command = str(input.command);
|
|
1238
|
+
return command ? `Watch ${command.length > 48 ? `${command.slice(0, 45)}\u2026` : command}` : "Watch command";
|
|
1239
|
+
}
|
|
1240
|
+
return n;
|
|
1099
1241
|
}
|
|
1100
|
-
function
|
|
1101
|
-
return
|
|
1102
|
-
|
|
1103
|
-
|
|
1242
|
+
function subagentLiveSuffix(input) {
|
|
1243
|
+
if (!input) return "";
|
|
1244
|
+
const bits = [];
|
|
1245
|
+
const status = str(input.live_status);
|
|
1246
|
+
if (status && !/^runn(ing)?$|^working$/i.test(status)) bits.push(status);
|
|
1247
|
+
if (typeof input.live_tool_uses === "number") bits.push(`${input.live_tool_uses} tools`);
|
|
1248
|
+
if (typeof input.live_duration_ms === "number") {
|
|
1249
|
+
bits.push(`${Math.max(0, Math.round(Number(input.live_duration_ms) / 1e3))}s`);
|
|
1250
|
+
}
|
|
1251
|
+
const last = str(input.live_last_tool);
|
|
1252
|
+
if (last) bits.push(last);
|
|
1253
|
+
return bits.length ? ` \xB7 ${bits.join(" \xB7 ")}` : "";
|
|
1104
1254
|
}
|
|
1105
|
-
function
|
|
1106
|
-
|
|
1255
|
+
function isSubagentToolName(name) {
|
|
1256
|
+
const n = (name ?? "").trim();
|
|
1257
|
+
if (/^(task|agent|spawn_agent)$/i.test(n)) return true;
|
|
1258
|
+
return /connectedAgentRequest/i.test(n);
|
|
1259
|
+
}
|
|
1260
|
+
function isInternalAgentStatusText(text) {
|
|
1261
|
+
const t = text.trim();
|
|
1262
|
+
if (!t) return false;
|
|
1263
|
+
if (/waiting for gate to pass/i.test(t)) return true;
|
|
1264
|
+
if (/^agent is running\b/i.test(t) && t.length < 160) return true;
|
|
1265
|
+
return false;
|
|
1266
|
+
}
|
|
1267
|
+
function isPollWrapperToolName(name) {
|
|
1268
|
+
const n = (name ?? "").replace(/[_-]/g, "");
|
|
1269
|
+
return /^(taskoutput|taskstop|sleep)$/i.test(n);
|
|
1270
|
+
}
|
|
1271
|
+
function lastTextPart(parts, type) {
|
|
1107
1272
|
for (let i = parts.length - 1; i >= 0; i--) {
|
|
1108
1273
|
const p = parts[i];
|
|
1109
|
-
if (
|
|
1274
|
+
if (p.type !== type) continue;
|
|
1110
1275
|
const text = p.text.trim();
|
|
1111
1276
|
if (!text || isInternalAgentStatusText(text)) continue;
|
|
1112
1277
|
return text;
|
|
1113
1278
|
}
|
|
1114
1279
|
return "";
|
|
1115
1280
|
}
|
|
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";
|
|
1281
|
+
function toolLabel(tool) {
|
|
1282
|
+
return tool.description || toolDescription(tool.name, tool.input) || tool.name;
|
|
1189
1283
|
}
|
|
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;
|
|
1284
|
+
function fileBasename(path) {
|
|
1285
|
+
const parts = path.replace(/\/$/, "").split(/[/\\]/);
|
|
1286
|
+
return parts[parts.length - 1] || path;
|
|
1216
1287
|
}
|
|
1217
|
-
|
|
1218
|
-
"
|
|
1219
|
-
"
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
}
|
|
1227
|
-
const which = await run("which", ["brightsy-mcp"], { reject: false });
|
|
1228
|
-
const command = which.exitCode === 0 && which.stdout.trim() ? "brightsy-mcp" : "npx";
|
|
1229
|
-
brightsyMcpCommandCache = { at: now, command };
|
|
1230
|
-
return command;
|
|
1288
|
+
function classifyTool(name) {
|
|
1289
|
+
const compact = name.replace(/^mcp__[^_]+__/, "").replace(/[_-]/g, "");
|
|
1290
|
+
if (/bash|shell|terminal|^zsh$|^sh$/i.test(compact)) return "shell";
|
|
1291
|
+
if (/grep|glob|search|ripgrep|findfiles|semsearch/i.test(compact)) return "search";
|
|
1292
|
+
if (/^(read|cat)$/i.test(compact) || /^read/i.test(compact)) return "read";
|
|
1293
|
+
if (/edit|write|apply|strreplace|multiedit|updatefile|createfile/i.test(compact)) {
|
|
1294
|
+
return "edit";
|
|
1295
|
+
}
|
|
1296
|
+
return "other";
|
|
1231
1297
|
}
|
|
1232
|
-
function
|
|
1233
|
-
|
|
1234
|
-
|
|
1298
|
+
function isShellToolName(name) {
|
|
1299
|
+
return classifyTool(name ?? "") === "shell";
|
|
1300
|
+
}
|
|
1301
|
+
function toolActivityLine(parts) {
|
|
1302
|
+
const tools = parts.filter((p) => {
|
|
1303
|
+
if (p.type !== "tool" || p.parentId) return false;
|
|
1304
|
+
if (isPollWrapperToolName(p.name)) return false;
|
|
1305
|
+
if (/present_plan$/i.test(p.name ?? "")) return false;
|
|
1306
|
+
if (/ask_user|AskUserQuestion/i.test(p.name ?? "")) return false;
|
|
1235
1307
|
return true;
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1308
|
+
});
|
|
1309
|
+
if (tools.length === 0) return null;
|
|
1310
|
+
const edited = [];
|
|
1311
|
+
let reads = 0;
|
|
1312
|
+
let searches = 0;
|
|
1313
|
+
let shells = 0;
|
|
1314
|
+
let others = 0;
|
|
1315
|
+
let additions = 0;
|
|
1316
|
+
let deletions = 0;
|
|
1317
|
+
for (const tool of tools) {
|
|
1318
|
+
const kind = classifyTool(tool.name);
|
|
1319
|
+
if (kind === "edit") {
|
|
1320
|
+
const path = tool.filePath ?? toolFilePath(tool.input);
|
|
1321
|
+
edited.push({
|
|
1322
|
+
name: path ? fileBasename(path) : tool.name,
|
|
1323
|
+
running: tool.status === "running"
|
|
1324
|
+
});
|
|
1325
|
+
} else if (kind === "read") reads += 1;
|
|
1326
|
+
else if (kind === "search") searches += 1;
|
|
1327
|
+
else if (kind === "shell") shells += 1;
|
|
1328
|
+
else others += 1;
|
|
1329
|
+
if (typeof tool.additions === "number") additions += tool.additions;
|
|
1330
|
+
if (typeof tool.deletions === "number") deletions += tool.deletions;
|
|
1331
|
+
}
|
|
1332
|
+
const bits = [];
|
|
1333
|
+
const editing = edited.filter((e) => e.running);
|
|
1334
|
+
const editedDone = edited.filter((e) => !e.running);
|
|
1335
|
+
if (editing.length === 1) bits.push(`Editing ${editing[0].name}`);
|
|
1336
|
+
else if (editing.length > 1) bits.push(`Editing ${editing.length} files`);
|
|
1337
|
+
if (editedDone.length === 1) bits.push(`${editing.length ? "edited" : "Edited"} ${editedDone[0].name}`);
|
|
1338
|
+
else if (editedDone.length > 1) {
|
|
1339
|
+
bits.push(`${editing.length ? "edited" : "Edited"} ${editedDone.length} files`);
|
|
1238
1340
|
}
|
|
1341
|
+
if (reads === 1) bits.push("explored 1 file");
|
|
1342
|
+
else if (reads > 1) bits.push(`explored ${reads} files`);
|
|
1343
|
+
if (searches === 1) bits.push("1 search");
|
|
1344
|
+
else if (searches > 1) bits.push(`${searches} searches`);
|
|
1345
|
+
if (shells === 1) bits.push("ran 1 command");
|
|
1346
|
+
else if (shells > 1) bits.push(`ran ${shells} commands`);
|
|
1347
|
+
if (others === 1) bits.push("1 tool");
|
|
1348
|
+
else if (others > 1) bits.push(`${others} tools`);
|
|
1349
|
+
if (bits.length === 0) return null;
|
|
1350
|
+
return { text: bits.join(", "), additions, deletions };
|
|
1239
1351
|
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1352
|
+
function liveActivitySummary(parts, opts) {
|
|
1353
|
+
if (opts?.queued && parts.length === 0) {
|
|
1354
|
+
return "Queued \u2014 waiting for a slot";
|
|
1355
|
+
}
|
|
1356
|
+
const tools = parts.filter((p) => p.type === "tool");
|
|
1357
|
+
const runningSubs = tools.filter(
|
|
1358
|
+
(t) => t.status === "running" && !t.parentId && isSubagentToolName(t.name)
|
|
1359
|
+
);
|
|
1360
|
+
const runningNested = [...tools].reverse().find((t) => t.status === "running" && t.parentId && !isPollWrapperToolName(t.name));
|
|
1361
|
+
const runningTop = [...tools].reverse().find(
|
|
1362
|
+
(t) => t.status === "running" && !t.parentId && !isPollWrapperToolName(t.name) && !isSubagentToolName(t.name)
|
|
1363
|
+
);
|
|
1364
|
+
const runningPoll = [...tools].reverse().find((t) => t.status === "running" && isPollWrapperToolName(t.name));
|
|
1365
|
+
const thinking = lastTextPart(parts, "thinking");
|
|
1366
|
+
const text = lastTextPart(parts, "text");
|
|
1367
|
+
if (runningSubs.length > 0) {
|
|
1368
|
+
const heads = runningSubs.map(toolLabel);
|
|
1369
|
+
const head = runningSubs.length === 1 ? heads[0] : `${runningSubs.length} subagents \xB7 ${heads.slice(0, 2).join(" \xB7 ")}`;
|
|
1370
|
+
if (runningNested) return `${head} \xB7 ${toolLabel(runningNested)}`;
|
|
1371
|
+
return head;
|
|
1372
|
+
}
|
|
1373
|
+
if (runningTop) return toolLabel(runningTop);
|
|
1374
|
+
if (runningPoll) return toolLabel(runningPoll);
|
|
1375
|
+
if (thinking) return thinking.length > 96 ? `\u2026${thinking.slice(-96)}` : thinking;
|
|
1376
|
+
if (text) return "Writing reply\u2026";
|
|
1377
|
+
const last = [...tools].reverse().find((t) => !isPollWrapperToolName(t.name)) ?? tools.at(-1);
|
|
1378
|
+
if (last) {
|
|
1379
|
+
const label = toolLabel(last);
|
|
1380
|
+
return last.status === "running" ? label : `Finished ${label}`;
|
|
1381
|
+
}
|
|
1382
|
+
return "Working\u2026";
|
|
1243
1383
|
}
|
|
1244
|
-
function
|
|
1245
|
-
|
|
1246
|
-
|
|
1384
|
+
function messagePartParentId(part) {
|
|
1385
|
+
if ("parentId" in part && typeof part.parentId === "string" && part.parentId.trim()) {
|
|
1386
|
+
return part.parentId;
|
|
1387
|
+
}
|
|
1388
|
+
return void 0;
|
|
1247
1389
|
}
|
|
1248
|
-
function
|
|
1249
|
-
|
|
1250
|
-
return typeof ds === "string" && ds.toLowerCase() === "brightsy";
|
|
1390
|
+
function sameParentId(a, b) {
|
|
1391
|
+
return (a ?? "") === (b ?? "");
|
|
1251
1392
|
}
|
|
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
|
-
}
|
|
1393
|
+
function withEventParentId(event, parentId) {
|
|
1394
|
+
if (!parentId) return event;
|
|
1395
|
+
if (event.type === "stdout" || event.type === "thinking" || event.type === "tool_use" || event.type === "tool_result") {
|
|
1396
|
+
return { ...event, parentId: event.parentId ?? parentId };
|
|
1260
1397
|
}
|
|
1261
|
-
return
|
|
1262
|
-
}
|
|
1263
|
-
function threadRequestsBrightsyMcp(thread) {
|
|
1264
|
-
return (thread?.messages ?? []).some(messageUsedBrightsyMcp);
|
|
1398
|
+
return event;
|
|
1265
1399
|
}
|
|
1266
|
-
function
|
|
1267
|
-
if (!
|
|
1268
|
-
|
|
1269
|
-
const alwaysOn = opts?.alwaysOnWorktree ?? Boolean(loadAppSettings().brightsy?.injectWorktreeMcp);
|
|
1270
|
-
if (alwaysOn) return true;
|
|
1271
|
-
return threadRequestsBrightsyMcp(thread);
|
|
1400
|
+
function withEventsParentId(events, parentId) {
|
|
1401
|
+
if (!parentId) return events;
|
|
1402
|
+
return events.map((event) => withEventParentId(event, parentId));
|
|
1272
1403
|
}
|
|
1273
|
-
function
|
|
1274
|
-
if (
|
|
1275
|
-
|
|
1276
|
-
}
|
|
1277
|
-
return {
|
|
1278
|
-
name,
|
|
1279
|
-
command: "npx",
|
|
1280
|
-
args: ["-y", "@brightsy/mcp-server"],
|
|
1281
|
-
...env ? { env } : {}
|
|
1282
|
-
};
|
|
1404
|
+
function toolFilePath(input) {
|
|
1405
|
+
if (!input) return void 0;
|
|
1406
|
+
return str(input.file_path) ?? str(input.path) ?? str(input.filePath) ?? str(input.filename);
|
|
1283
1407
|
}
|
|
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
|
-
};
|
|
1408
|
+
function countLines(text) {
|
|
1409
|
+
if (!text) return 0;
|
|
1410
|
+
return text.split("\n").length;
|
|
1292
1411
|
}
|
|
1293
|
-
function
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1412
|
+
function diffFromInput(input) {
|
|
1413
|
+
if (!input) return {};
|
|
1414
|
+
const oldS = str(input.old_string) ?? str(input.oldString);
|
|
1415
|
+
const newS = str(input.new_string) ?? str(input.newString) ?? str(input.content);
|
|
1416
|
+
if (oldS != null || newS != null) {
|
|
1417
|
+
return {
|
|
1418
|
+
additions: countLines(newS),
|
|
1419
|
+
deletions: countLines(oldS)
|
|
1420
|
+
};
|
|
1297
1421
|
}
|
|
1298
|
-
return
|
|
1422
|
+
return {};
|
|
1299
1423
|
}
|
|
1300
|
-
function
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
}
|
|
1308
|
-
} catch {
|
|
1424
|
+
function parseDiffStat(result) {
|
|
1425
|
+
if (!result) return {};
|
|
1426
|
+
const paired = result.match(/\+(\d+)\s+-(\d+)/);
|
|
1427
|
+
if (paired) {
|
|
1428
|
+
return {
|
|
1429
|
+
additions: Number(paired[1]),
|
|
1430
|
+
deletions: Number(paired[2])
|
|
1431
|
+
};
|
|
1309
1432
|
}
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
return
|
|
1433
|
+
const verbose = result.match(
|
|
1434
|
+
/(\d+)\s+insertions?(?:,\s*(\d+)\s+deletions?)?/i
|
|
1435
|
+
);
|
|
1436
|
+
if (verbose) {
|
|
1437
|
+
return {
|
|
1438
|
+
additions: Number(verbose[1]),
|
|
1439
|
+
deletions: verbose[2] != null ? Number(verbose[2]) : void 0
|
|
1440
|
+
};
|
|
1315
1441
|
}
|
|
1442
|
+
return {};
|
|
1316
1443
|
}
|
|
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;
|
|
1444
|
+
var THINKING_PART_MAX_CHARS = 64e3;
|
|
1445
|
+
function clipThinkingForStore(text, max = THINKING_PART_MAX_CHARS) {
|
|
1446
|
+
if (text.length <= max) return text;
|
|
1447
|
+
const marker = "\u2026(earlier thinking trimmed)\n";
|
|
1448
|
+
return marker + text.slice(text.length - Math.max(1, max - marker.length));
|
|
1341
1449
|
}
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
};
|
|
1355
|
-
}
|
|
1450
|
+
var STDOUT_EVENT_MAX_CHARS = 16e3;
|
|
1451
|
+
var STDERR_EVENT_MAX_CHARS = 4e3;
|
|
1452
|
+
function clipEventTail(text, max) {
|
|
1453
|
+
if (text.length <= max) return text;
|
|
1454
|
+
const marker = "\u2026(truncated)\n";
|
|
1455
|
+
return marker + text.slice(text.length - Math.max(1, max - marker.length));
|
|
1456
|
+
}
|
|
1457
|
+
function clipAgentEventForPaint(event) {
|
|
1458
|
+
if (event.type === "tool_result" && event.content) {
|
|
1459
|
+
const content = clipToolResultForStore(event.content);
|
|
1460
|
+
if (content === event.content) return event;
|
|
1461
|
+
return { ...event, content };
|
|
1356
1462
|
}
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
return { name: "sideboard", command: which.stdout.trim(), args: ["mcp"] };
|
|
1463
|
+
if (event.type === "thinking" && event.data.length > THINKING_PART_MAX_CHARS) {
|
|
1464
|
+
return { ...event, data: clipThinkingForStore(event.data) };
|
|
1360
1465
|
}
|
|
1361
|
-
|
|
1466
|
+
if (event.type === "stdout" && event.data.length > STDOUT_EVENT_MAX_CHARS) {
|
|
1467
|
+
return { ...event, data: clipEventTail(event.data, STDOUT_EVENT_MAX_CHARS) };
|
|
1468
|
+
}
|
|
1469
|
+
if (event.type === "stderr" && event.data.length > STDERR_EVENT_MAX_CHARS) {
|
|
1470
|
+
return { ...event, data: clipEventTail(event.data, STDERR_EVENT_MAX_CHARS) };
|
|
1471
|
+
}
|
|
1472
|
+
return event;
|
|
1362
1473
|
}
|
|
1363
|
-
|
|
1364
|
-
const
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
if (
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1474
|
+
function withPartTimes(next, prev) {
|
|
1475
|
+
const now = Date.now();
|
|
1476
|
+
const startedAt = prev && typeof prev.startedAt === "number" ? prev.startedAt : now;
|
|
1477
|
+
return { ...next, startedAt, updatedAt: now };
|
|
1478
|
+
}
|
|
1479
|
+
function applyAgentEvent(parts, event) {
|
|
1480
|
+
if (event.type === "stdout") {
|
|
1481
|
+
const data = event.data;
|
|
1482
|
+
if (!data) return parts;
|
|
1483
|
+
const next = [...parts];
|
|
1484
|
+
const last = next[next.length - 1];
|
|
1485
|
+
if (last?.type === "text" && sameParentId(last.parentId, event.parentId)) {
|
|
1486
|
+
next[next.length - 1] = withPartTimes(
|
|
1487
|
+
{
|
|
1488
|
+
type: "text",
|
|
1489
|
+
text: last.text + data,
|
|
1490
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
1491
|
+
},
|
|
1492
|
+
last
|
|
1493
|
+
);
|
|
1494
|
+
} else {
|
|
1495
|
+
next.push(
|
|
1496
|
+
withPartTimes({
|
|
1497
|
+
type: "text",
|
|
1498
|
+
text: data,
|
|
1499
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
1500
|
+
})
|
|
1381
1501
|
);
|
|
1382
|
-
} catch {
|
|
1383
1502
|
}
|
|
1384
|
-
|
|
1385
|
-
servers.push(sideboard);
|
|
1503
|
+
return next;
|
|
1386
1504
|
}
|
|
1387
|
-
if (
|
|
1388
|
-
const
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
for (
|
|
1393
|
-
|
|
1394
|
-
if (
|
|
1395
|
-
|
|
1396
|
-
|
|
1505
|
+
if (event.type === "thinking") {
|
|
1506
|
+
const data = event.data;
|
|
1507
|
+
if (!data) return parts;
|
|
1508
|
+
const next = [...parts];
|
|
1509
|
+
if (event.replace) {
|
|
1510
|
+
for (let i = next.length - 1; i >= 0; i--) {
|
|
1511
|
+
const prev = next[i];
|
|
1512
|
+
if (prev?.type === "thinking" && sameParentId(prev.parentId, event.parentId)) {
|
|
1513
|
+
next[i] = withPartTimes(
|
|
1514
|
+
{
|
|
1515
|
+
type: "thinking",
|
|
1516
|
+
text: clipThinkingForStore(data),
|
|
1517
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
1518
|
+
},
|
|
1519
|
+
prev
|
|
1520
|
+
);
|
|
1521
|
+
return next;
|
|
1522
|
+
}
|
|
1397
1523
|
}
|
|
1524
|
+
next.push(
|
|
1525
|
+
withPartTimes({
|
|
1526
|
+
type: "thinking",
|
|
1527
|
+
text: clipThinkingForStore(data),
|
|
1528
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
1529
|
+
})
|
|
1530
|
+
);
|
|
1531
|
+
return next;
|
|
1532
|
+
}
|
|
1533
|
+
const last = next[next.length - 1];
|
|
1534
|
+
if (last?.type === "thinking" && sameParentId(last.parentId, event.parentId)) {
|
|
1535
|
+
next[next.length - 1] = withPartTimes(
|
|
1536
|
+
{
|
|
1537
|
+
type: "thinking",
|
|
1538
|
+
text: clipThinkingForStore(last.text + data),
|
|
1539
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
1540
|
+
},
|
|
1541
|
+
last
|
|
1542
|
+
);
|
|
1398
1543
|
} else {
|
|
1399
|
-
|
|
1544
|
+
next.push(
|
|
1545
|
+
withPartTimes({
|
|
1546
|
+
type: "thinking",
|
|
1547
|
+
text: clipThinkingForStore(data),
|
|
1548
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
1549
|
+
})
|
|
1550
|
+
);
|
|
1400
1551
|
}
|
|
1552
|
+
return next;
|
|
1401
1553
|
}
|
|
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)}`);
|
|
1554
|
+
if (event.type === "tool_use") {
|
|
1555
|
+
const input = asRecord(event.input);
|
|
1556
|
+
const diff = diffFromInput(input);
|
|
1557
|
+
const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
|
|
1558
|
+
if (existing >= 0) {
|
|
1559
|
+
const prev = parts[existing];
|
|
1560
|
+
const next = [...parts];
|
|
1561
|
+
const mergedInput = {
|
|
1562
|
+
...prev.input ?? {},
|
|
1563
|
+
...input ?? {}
|
|
1564
|
+
};
|
|
1565
|
+
const mergedRecord = Object.keys(mergedInput).length > 0 ? mergedInput : void 0;
|
|
1566
|
+
next[existing] = withPartTimes(
|
|
1567
|
+
{
|
|
1568
|
+
...prev,
|
|
1569
|
+
name: event.name || prev.name,
|
|
1570
|
+
input: mergedRecord,
|
|
1571
|
+
description: toolDescription(event.name || prev.name, mergedRecord),
|
|
1572
|
+
detail: toolDetail(event.name || prev.name, mergedRecord) ?? prev.detail,
|
|
1573
|
+
filePath: toolFilePath(mergedRecord) ?? prev.filePath,
|
|
1574
|
+
additions: diff.additions ?? prev.additions,
|
|
1575
|
+
deletions: diff.deletions ?? prev.deletions,
|
|
1576
|
+
parentId: event.parentId ?? prev.parentId
|
|
1577
|
+
},
|
|
1578
|
+
prev
|
|
1579
|
+
);
|
|
1580
|
+
return next;
|
|
1441
1581
|
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1582
|
+
return [
|
|
1583
|
+
...parts,
|
|
1584
|
+
withPartTimes({
|
|
1585
|
+
type: "tool",
|
|
1586
|
+
id: event.id,
|
|
1587
|
+
name: event.name,
|
|
1588
|
+
input,
|
|
1589
|
+
description: toolDescription(event.name, input),
|
|
1590
|
+
detail: toolDetail(event.name, input),
|
|
1591
|
+
status: "running",
|
|
1592
|
+
filePath: toolFilePath(input),
|
|
1593
|
+
...event.parentId ? { parentId: event.parentId } : {},
|
|
1594
|
+
...diff
|
|
1595
|
+
})
|
|
1596
|
+
];
|
|
1597
|
+
}
|
|
1598
|
+
if (event.type === "tool_result") {
|
|
1599
|
+
const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
|
|
1600
|
+
const fromResult = parseDiffStat(event.content);
|
|
1601
|
+
if (event.partial) {
|
|
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" : "running",
|
|
1611
|
+
result: clipToolResultForStore(event.content),
|
|
1612
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
1613
|
+
})
|
|
1614
|
+
];
|
|
1446
1615
|
}
|
|
1616
|
+
return parts.map((p, i) => {
|
|
1617
|
+
if (i !== existing || p.type !== "tool") return p;
|
|
1618
|
+
if (p.status === "done" || p.status === "error") return p;
|
|
1619
|
+
return withPartTimes(
|
|
1620
|
+
{
|
|
1621
|
+
...p,
|
|
1622
|
+
result: clipToolResultForStore(event.content) ?? p.result
|
|
1623
|
+
},
|
|
1624
|
+
p
|
|
1625
|
+
);
|
|
1626
|
+
});
|
|
1447
1627
|
}
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1628
|
+
if (existing < 0) {
|
|
1629
|
+
return [
|
|
1630
|
+
...parts,
|
|
1631
|
+
withPartTimes({
|
|
1632
|
+
type: "tool",
|
|
1633
|
+
id: event.id,
|
|
1634
|
+
name: "tool",
|
|
1635
|
+
description: "tool",
|
|
1636
|
+
status: event.isError ? "error" : "done",
|
|
1637
|
+
result: clipToolResultForStore(event.content),
|
|
1638
|
+
...event.parentId ? { parentId: event.parentId } : {},
|
|
1639
|
+
...fromResult.additions != null ? { additions: fromResult.additions } : {},
|
|
1640
|
+
...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
|
|
1641
|
+
})
|
|
1642
|
+
];
|
|
1643
|
+
}
|
|
1644
|
+
return parts.map((p, i) => {
|
|
1645
|
+
if (i !== existing || p.type !== "tool") return p;
|
|
1646
|
+
return withPartTimes(
|
|
1647
|
+
{
|
|
1648
|
+
...p,
|
|
1649
|
+
status: event.isError ? "error" : "done",
|
|
1650
|
+
result: clipToolResultForStore(event.content),
|
|
1651
|
+
...fromResult.additions != null ? { additions: fromResult.additions } : {},
|
|
1652
|
+
...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
|
|
1653
|
+
},
|
|
1654
|
+
p
|
|
1655
|
+
);
|
|
1656
|
+
});
|
|
1451
1657
|
}
|
|
1452
|
-
return
|
|
1658
|
+
return parts;
|
|
1453
1659
|
}
|
|
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 });
|
|
1660
|
+
function partsToAssistantText(parts) {
|
|
1661
|
+
return parts.filter(
|
|
1662
|
+
(p) => p.type === "text" && !messagePartParentId(p)
|
|
1663
|
+
).map((p) => p.text).join("").trim();
|
|
1471
1664
|
}
|
|
1472
|
-
function
|
|
1473
|
-
if (
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
...env ? { env } : {}
|
|
1481
|
-
};
|
|
1665
|
+
function lastAssistantMessageText(parts) {
|
|
1666
|
+
if (!parts?.length) return "";
|
|
1667
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
1668
|
+
const p = parts[i];
|
|
1669
|
+
if (!p || p.type !== "text" || messagePartParentId(p)) continue;
|
|
1670
|
+
const text = p.text.trim();
|
|
1671
|
+
if (!text || isInternalAgentStatusText(text)) continue;
|
|
1672
|
+
return text;
|
|
1482
1673
|
}
|
|
1483
|
-
|
|
1484
|
-
const cfgPath = join(dir, "mcp.json");
|
|
1485
|
-
writeFileSync(cfgPath, JSON.stringify({ mcpServers }, null, 2));
|
|
1486
|
-
return cfgPath;
|
|
1674
|
+
return "";
|
|
1487
1675
|
}
|
|
1488
|
-
|
|
1489
|
-
|
|
1676
|
+
function stripBrightsyNdjsonNoise(text) {
|
|
1677
|
+
if (!text || !text.includes('"type"')) return text;
|
|
1678
|
+
let out = text;
|
|
1679
|
+
out = out.replace(
|
|
1680
|
+
/\{"type":"(?:tool_use|tool_result|tool|thinking|usage|done|error)"[\s\S]*?\}\s*(?=\{"type":"|$|(?=[A-Za-z*#]))/g,
|
|
1681
|
+
""
|
|
1682
|
+
);
|
|
1683
|
+
out = out.replace(/\{"type":"(?:tool_use|tool_result|tool)"[\s\S]*$/g, "");
|
|
1684
|
+
return out.trim();
|
|
1685
|
+
}
|
|
1686
|
+
function isBrightsyNdjsonLine(line) {
|
|
1687
|
+
const t = line.trim();
|
|
1688
|
+
if (!t.startsWith("{")) return false;
|
|
1689
|
+
return /"type"\s*:\s*"(tool_use|tool_result|tool|thinking|usage|done|error|text)"/.test(t);
|
|
1690
|
+
}
|
|
1691
|
+
function finalizeParts(parts) {
|
|
1692
|
+
return parts.map(
|
|
1693
|
+
(p) => p.type === "tool" && p.status === "running" ? { ...p, status: "done" } : p
|
|
1694
|
+
);
|
|
1695
|
+
}
|
|
1696
|
+
function normalizeParseResult(parsed) {
|
|
1697
|
+
if (!parsed) return [];
|
|
1698
|
+
return Array.isArray(parsed) ? parsed : [parsed];
|
|
1490
1699
|
}
|
|
1491
1700
|
|
|
1492
1701
|
// src/agents/types.ts
|
|
@@ -1550,24 +1759,15 @@ var BASE_ALLOWED_TOOLS = [
|
|
|
1550
1759
|
"Skill"
|
|
1551
1760
|
];
|
|
1552
1761
|
var CLAUDE_PRINT_BG_WAIT_CEILING_MS = 72e5;
|
|
1762
|
+
function isLocalDevAppDataDir(dir = process.env.SIDEBOARD_APP_DATA) {
|
|
1763
|
+
return /[/\\]\.sideboard[/\\]dev-app-data(?:[/\\]|$)/.test(dir ?? "");
|
|
1764
|
+
}
|
|
1553
1765
|
var CLAUDE_CHROME_ALLOWED_TOOLS = [
|
|
1554
1766
|
"mcp__claude-in-chrome",
|
|
1555
1767
|
"mcp__claude-in-chrome__*",
|
|
1556
1768
|
"Skill(claude-in-chrome)"
|
|
1557
1769
|
];
|
|
1558
1770
|
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
1771
|
function usageFromClaude(usage) {
|
|
1572
1772
|
if (!usage) return null;
|
|
1573
1773
|
const inputTokens = Number(usage.input_tokens ?? 0);
|
|
@@ -1854,7 +2054,7 @@ var claudeAdapter = {
|
|
|
1854
2054
|
async detect() {
|
|
1855
2055
|
const claude = resolveClaudeExecutable();
|
|
1856
2056
|
if (claude !== "claude") {
|
|
1857
|
-
if (!
|
|
2057
|
+
if (!existsSync5(claude)) {
|
|
1858
2058
|
return {
|
|
1859
2059
|
agent: "claude",
|
|
1860
2060
|
installed: false,
|
|
@@ -1903,7 +2103,7 @@ var claudeAdapter = {
|
|
|
1903
2103
|
);
|
|
1904
2104
|
}
|
|
1905
2105
|
const mode = permissionMode(thread);
|
|
1906
|
-
const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-
|
|
2106
|
+
const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-3FDAUNN2.js");
|
|
1907
2107
|
const isOrchestrator = isOrchestratorThread2(thread);
|
|
1908
2108
|
const injectedServers = await buildInjectedMcpServers({
|
|
1909
2109
|
includeSideboard: true,
|
|
@@ -1914,6 +2114,7 @@ var claudeAdapter = {
|
|
|
1914
2114
|
});
|
|
1915
2115
|
const injectedBrightsyNames = injectedServers.filter((s) => s.name === "brightsy" || s.name.startsWith("brightsy_")).map((s) => s.name);
|
|
1916
2116
|
const chromeOn = claudeChromeEnabled();
|
|
2117
|
+
const userMcpEntries = isOrchestrator ? {} : listUserClaudeMcpServerEntries();
|
|
1917
2118
|
let allowedTools;
|
|
1918
2119
|
if (isOrchestrator) {
|
|
1919
2120
|
allowedTools = [
|
|
@@ -1922,10 +2123,9 @@ var claudeAdapter = {
|
|
|
1922
2123
|
...brightsyMcpAllowedTools(injectedBrightsyNames)
|
|
1923
2124
|
];
|
|
1924
2125
|
} else {
|
|
1925
|
-
const servers = await loadMcpServers();
|
|
1926
2126
|
allowedTools = [
|
|
1927
2127
|
...BASE_ALLOWED_TOOLS,
|
|
1928
|
-
...
|
|
2128
|
+
...mcpAllowToolsFromNames(Object.keys(userMcpEntries)),
|
|
1929
2129
|
...sideboardWorktreeAllowedTools({
|
|
1930
2130
|
github: true,
|
|
1931
2131
|
linear: isLinearConnected(),
|
|
@@ -1950,12 +2150,10 @@ var claudeAdapter = {
|
|
|
1950
2150
|
if (systemPrompt) {
|
|
1951
2151
|
args.push("--append-system-prompt", systemPrompt);
|
|
1952
2152
|
}
|
|
1953
|
-
const mcpConfigPath = writeMcpServersConfig(injectedServers);
|
|
2153
|
+
const mcpConfigPath = writeMcpServersConfig(injectedServers, userMcpEntries);
|
|
1954
2154
|
if (mcpConfigPath) {
|
|
1955
2155
|
args.push("--mcp-config", mcpConfigPath);
|
|
1956
|
-
|
|
1957
|
-
args.push("--strict-mcp-config");
|
|
1958
|
-
}
|
|
2156
|
+
args.push("--strict-mcp-config");
|
|
1959
2157
|
}
|
|
1960
2158
|
if (chromeOn) {
|
|
1961
2159
|
args.push("--chrome");
|
|
@@ -1987,8 +2185,10 @@ var claudeAdapter = {
|
|
|
1987
2185
|
CLAUDE_CODE_FORWARD_SUBAGENT_TEXT: "1",
|
|
1988
2186
|
CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS: process.env.CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS ?? String(CLAUDE_PRINT_BG_WAIT_CEILING_MS),
|
|
1989
2187
|
// `--strict-mcp-config` still fetches claude.ai connectors on some CLI
|
|
1990
|
-
// builds (anthropics/claude-code#60252). Coordinators
|
|
1991
|
-
|
|
2188
|
+
// builds (anthropics/claude-code#60252). Coordinators never need them.
|
|
2189
|
+
// Local `pnpm dev` also disables them: those HTTP connectors hang the
|
|
2190
|
+
// first worktree turn when Electron's cwd is this Sideboard repo.
|
|
2191
|
+
...isOrchestrator || isLocalDevAppDataDir() ? { ENABLE_CLAUDEAI_MCP_SERVERS: "false" } : {}
|
|
1992
2192
|
}
|
|
1993
2193
|
};
|
|
1994
2194
|
},
|
|
@@ -2139,179 +2339,6 @@ function parseIssuesJson(raw) {
|
|
|
2139
2339
|
import { existsSync as existsSync6, readFileSync as readFileSync3, statSync } from "fs";
|
|
2140
2340
|
import { homedir as homedir3 } from "os";
|
|
2141
2341
|
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
2342
|
var CODEX_PROMPT_ARG_MAX = 2e5;
|
|
2316
2343
|
var FALLBACK_CODEX_MODELS = [
|
|
2317
2344
|
{ id: "gpt-5.6-sol", displayName: "GPT-5.6 Sol" },
|
|
@@ -4056,6 +4083,7 @@ export {
|
|
|
4056
4083
|
parseMcpList,
|
|
4057
4084
|
sanitizeMcpServerName,
|
|
4058
4085
|
mcpAllowTools,
|
|
4086
|
+
mcpAllowToolsFromNames,
|
|
4059
4087
|
mcpAuthWarnings,
|
|
4060
4088
|
SIDEBOARD_MCP_PROFILE_ENV,
|
|
4061
4089
|
WORKTREE_MCP_TOOLS,
|
|
@@ -4071,16 +4099,16 @@ export {
|
|
|
4071
4099
|
shouldInjectBrightsyMcp,
|
|
4072
4100
|
brightsyMcpAllowedTools,
|
|
4073
4101
|
writeInjectedMcpConfig,
|
|
4074
|
-
PLAN_MODE_INSTRUCTION,
|
|
4075
|
-
permissionMode,
|
|
4076
|
-
mcpStatusThinkingFromClaudeInit,
|
|
4077
|
-
claudeAdapter,
|
|
4078
4102
|
userCursorMcpConfigPath,
|
|
4079
4103
|
userClaudeMcpConfigPath,
|
|
4080
4104
|
mergeSideboardIntoMcpServersJson,
|
|
4081
4105
|
registerPackagedUserMcpClients,
|
|
4082
4106
|
isInjectedOrchMcpName,
|
|
4083
4107
|
userMcpNamesToDisable,
|
|
4108
|
+
PLAN_MODE_INSTRUCTION,
|
|
4109
|
+
permissionMode,
|
|
4110
|
+
mcpStatusThinkingFromClaudeInit,
|
|
4111
|
+
claudeAdapter,
|
|
4084
4112
|
listCodexModels,
|
|
4085
4113
|
codexAdapter,
|
|
4086
4114
|
isCursorApiKeyConfigured,
|