@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.
Files changed (59) hide show
  1. package/dist/{abletime-QGGYL2G4.js → abletime-BF253OZT.js} +5 -4
  2. package/dist/{abletime-GDZLZRMY.js → abletime-D6773P33.js} +5 -5
  3. package/dist/{abletime-oauth-F335TWKY.js → abletime-oauth-H4672PRH.js} +4 -3
  4. package/dist/{abletime-oauth-3X3BAPTF.js → abletime-oauth-KQPVVBKA.js} +4 -4
  5. package/dist/{abletime-rest-PLJVY3H3.js → abletime-rest-DAFPXZ7H.js} +4 -4
  6. package/dist/{abletime-rest-LAGTPAJL.js → abletime-rest-OPEVV3XC.js} +4 -3
  7. package/dist/agents/cursor-runner.js +5 -5
  8. package/dist/{agents-HETHRRZJ.js → agents-IFVGY77W.js} +9 -9
  9. package/dist/{agents-WBDDLRBY.js → agents-IK3U4Z2O.js} +8 -7
  10. package/dist/{app-settings-XCRNCEEE.js → app-settings-5RQMQKSB.js} +2 -1
  11. package/dist/{app-settings-2TN3WLZV.js → app-settings-V2BBCX2H.js} +2 -2
  12. package/dist/{chunk-MN6YUD4O.js → chunk-3NWUHXU3.js} +3 -3
  13. package/dist/{chunk-G4TANQC3.js → chunk-646I442S.js} +5 -5
  14. package/dist/{chunk-FNQNJOHY.js → chunk-ABOBTSO6.js} +4 -1
  15. package/dist/{chunk-YVE4PXCJ.js → chunk-CLI5DVU4.js} +2 -2
  16. package/dist/{chunk-C32SBNA3.js → chunk-EIICOB7P.js} +457 -294
  17. package/dist/{chunk-LS6TSOCE.js → chunk-HSYE5ID6.js} +5 -5
  18. package/dist/{chunk-UFHNSFAS.js → chunk-HVUVQXZ2.js} +5 -1
  19. package/dist/{chunk-7MTVRNP4.js → chunk-IAP5NXQ4.js} +1206 -1192
  20. package/dist/{chunk-SALM35KD.js → chunk-IILG7DPX.js} +5 -5
  21. package/dist/{chunk-DFTVZEZY.js → chunk-K642ORWF.js} +1085 -1057
  22. package/dist/{chunk-NI243N27.js → chunk-KTZESHYH.js} +15 -7
  23. package/dist/{chunk-FNGXKY5W.js → chunk-N2VST3NL.js} +68 -8
  24. package/dist/{chunk-XYKVRRC2.js → chunk-NFWPDEN4.js} +15 -7
  25. package/dist/{chunk-KFD2OXCE.js → chunk-P2EAAM3H.js} +68 -8
  26. package/dist/{chunk-QQKDGPUI.js → chunk-PO4M4JAJ.js} +1 -1
  27. package/dist/{chunk-2M5B7YNY.js → chunk-RRW2XWAY.js} +2 -2
  28. package/dist/{chunk-REZRGCKV.js → chunk-SOGGKYFV.js} +3 -3
  29. package/dist/{chunk-3OHR73VK.js → chunk-SVXIJ2IC.js} +1 -1
  30. package/dist/{chunk-SYFVUF2C.js → chunk-SZJZEFTY.js} +1 -1
  31. package/dist/{chunk-42IULNIY.js → chunk-THLVCXLF.js} +3 -3
  32. package/dist/{chunk-JZHRZ3DR.js → chunk-TQBUXKIB.js} +5 -5
  33. package/dist/chunk-WWUABK6B.js +56 -0
  34. package/dist/{chunk-D2IKXMF3.js → chunk-XGE6YUO6.js} +453 -289
  35. package/dist/{chunk-BTC2D6LS.js → chunk-Y5JES2VV.js} +3 -51
  36. package/dist/{connected-teams-WK47ATT7.js → connected-teams-LX5DLL2I.js} +3 -2
  37. package/dist/{connected-teams-PU4AMFNA.js → connected-teams-PQETXUWV.js} +3 -2
  38. package/dist/{coordinator-prompt-FQX6ALAI.js → coordinator-prompt-JLMQVORO.js} +6 -6
  39. package/dist/{coordinator-prompt-LE2GQZ2X.js → coordinator-prompt-VZ7OSRLD.js} +6 -5
  40. package/dist/{global-workspace-6P236KKG.js → global-workspace-3FDAUNN2.js} +6 -6
  41. package/dist/{global-workspace-KT3FRHZL.js → global-workspace-QPSJEXGR.js} +6 -5
  42. package/dist/index.cjs +1107 -826
  43. package/dist/index.d.cts +15 -1
  44. package/dist/index.d.ts +15 -1
  45. package/dist/index.js +24 -18
  46. package/dist/mcp/run-stdio.cjs +1912 -1652
  47. package/dist/mcp/run-stdio.js +16 -14
  48. package/dist/{orchestrator-MI7A5DQZ.js → orchestrator-5VVR7LOU.js} +9 -8
  49. package/dist/{orchestrator-WVZPTHBV.js → orchestrator-LFC3V3AX.js} +10 -10
  50. package/dist/{run-EBWL2Q4N.js → run-QDZCTP4Y.js} +2 -1
  51. package/dist/{run-CAGKZLHA.js → run-UEQM4JMZ.js} +2 -1
  52. package/dist/{workspaces-R5LJCJRB.js → workspaces-76W7IEKR.js} +8 -6
  53. package/dist/{workspaces-KM7KTLVO.js → workspaces-TDBA64II.js} +8 -5
  54. package/dist/{worktree-MDMTCAHX.js → worktree-7V67XTEO.js} +5 -5
  55. package/dist/{worktree-VXRI7LYM.js → worktree-I573UOPD.js} +5 -4
  56. package/dist/{worktree-exclude-N5X5I422.js → worktree-exclude-K7QK4RI5.js} +3 -2
  57. package/dist/{worktree-exclude-X54WSOXM.js → worktree-exclude-L32WEBX6.js} +3 -2
  58. package/package.json +1 -1
  59. package/dist/{chunk-L5CIEBLR.js → chunk-EWHJNJHV.js} +3 -3
@@ -2,13 +2,13 @@
2
2
 
3
3
  import {
4
4
  isOrchestratorThread
5
- } from "./chunk-KFD2OXCE.js";
5
+ } from "./chunk-P2EAAM3H.js";
6
6
  import {
7
7
  applyConnectedTeamToCli,
8
8
  brightsyMcpServerName,
9
9
  ensureCliTeamTracked,
10
10
  ensureConnectedBrightsyTeamTokens
11
- } from "./chunk-3OHR73VK.js";
11
+ } from "./chunk-SVXIJ2IC.js";
12
12
  import {
13
13
  loadBrightsyConfig
14
14
  } from "./chunk-Y4MIXGPA.js";
@@ -20,29 +20,31 @@ import {
20
20
  mergeAgentGitAuthEnv,
21
21
  resolveAgentGitAuthEnv,
22
22
  resolveCodexGitWritableRoots
23
- } from "./chunk-XYKVRRC2.js";
23
+ } from "./chunk-NFWPDEN4.js";
24
24
  import {
25
25
  enrichPathWithNpmGlobalBin,
26
26
  isConductorBundledCli,
27
27
  resolveCommandBinarySync,
28
28
  run,
29
29
  withExportedPath
30
- } from "./chunk-FNQNJOHY.js";
30
+ } from "./chunk-ABOBTSO6.js";
31
31
  import {
32
32
  claudeChromeEnabled,
33
33
  fromInclusiveInputUsage,
34
34
  isAbleTimeConnected,
35
- isElectronLikeCommand,
36
35
  isLinearConnected,
37
36
  loadAppSettings,
38
37
  resolveAgentExecutable,
39
- resolveClaudeExecutable,
40
- unwrapStrippedElectronLaunch,
41
- wrapElectronAsNodeLaunch
42
- } from "./chunk-BTC2D6LS.js";
38
+ resolveClaudeExecutable
39
+ } from "./chunk-Y5JES2VV.js";
43
40
  import {
44
41
  appDataDir
45
42
  } from "./chunk-EQNRWDLN.js";
43
+ import {
44
+ isElectronLikeCommand,
45
+ unwrapStrippedElectronLaunch,
46
+ wrapElectronAsNodeLaunch
47
+ } from "./chunk-WWUABK6B.js";
46
48
 
47
49
  // src/agents/brightsy.ts
48
50
  import { existsSync } from "fs";
@@ -800,27 +802,9 @@ var brightsyAdapter = {
800
802
  };
801
803
 
802
804
  // src/agents/claude.ts
803
- import { existsSync as existsSync6 } from "fs";
805
+ import { existsSync as existsSync8 } from "fs";
804
806
 
805
807
  // src/agents/claude-mcp.ts
806
- function parseMcpList(text) {
807
- const servers = [];
808
- const seen = /* @__PURE__ */ new Set();
809
- for (const raw of text.split("\n")) {
810
- const line = raw.trim();
811
- if (!line || /^Checking MCP/i.test(line)) continue;
812
- const m = line.match(/^(.+?):\s+\S+/);
813
- if (!m) continue;
814
- const name = m[1].trim();
815
- if (!name || seen.has(name)) continue;
816
- const needsAuth = /Needs authentication/i.test(line);
817
- const connected = !needsAuth && /Connected/i.test(line);
818
- if (!needsAuth && !connected) continue;
819
- seen.add(name);
820
- servers.push({ name, connected, needsAuth });
821
- }
822
- return servers;
823
- }
824
808
  function sanitizeMcpServerName(name) {
825
809
  return name.replace(/[^A-Za-z0-9_-]/g, "_");
826
810
  }
@@ -835,1169 +819,1317 @@ function mcpAllowTools(servers) {
835
819
  }
836
820
  return out;
837
821
  }
838
-
839
- // src/agents/message-parts.ts
840
- function asRecord(input) {
841
- if (input && typeof input === "object" && !Array.isArray(input)) {
842
- return input;
843
- }
844
- return void 0;
822
+ function mcpAllowToolsFromNames(names) {
823
+ return mcpAllowTools(
824
+ [...names].map((name) => name.trim()).filter(Boolean).map((name) => ({ name, connected: true, needsAuth: false }))
825
+ );
845
826
  }
846
- function str(v) {
847
- return typeof v === "string" && v.trim() ? v : void 0;
827
+
828
+ // src/agents/orch-mcp-isolation.ts
829
+ import { existsSync as existsSync7, readFileSync as readFileSync3 } from "fs";
830
+ import { homedir as homedir3 } from "os";
831
+ import { join as join6 } from "path";
832
+ import { parse as parseToml } from "smol-toml";
833
+
834
+ // src/agents/user-mcp-config.ts
835
+ import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
836
+ import { homedir as homedir2 } from "os";
837
+ import { dirname as dirname3, join as join5 } from "path";
838
+
839
+ // src/agents/injected-mcp.ts
840
+ import { existsSync as existsSync5, mkdtempSync, writeFileSync as writeFileSync2 } from "fs";
841
+ import { createRequire } from "module";
842
+ import { tmpdir } from "os";
843
+ import { dirname as dirname2, join as join4 } from "path";
844
+ import { fileURLToPath } from "url";
845
+
846
+ // src/mcp/profile.ts
847
+ var SIDEBOARD_MCP_PROFILE_ENV = "SIDEBOARD_MCP_PROFILE";
848
+ function sideboardMcpProfile(env = process.env) {
849
+ return env[SIDEBOARD_MCP_PROFILE_ENV]?.trim().toLowerCase() === "worktree" ? "worktree" : "orchestration";
848
850
  }
849
- var CHAT_DETAIL_MAX = 72;
850
- function clipChatLine(value, max = CHAT_DETAIL_MAX) {
851
- const line = value.trim().split(/\r?\n/, 1)[0] ?? "";
852
- if (line.length <= max) return line;
853
- return `${line.slice(0, Math.max(1, max - 1))}\u2026`;
851
+
852
+ // src/agents/node-launch.ts
853
+ import { existsSync as existsSync4, readdirSync, realpathSync } from "fs";
854
+ import { homedir } from "os";
855
+ import { join as join3 } from "path";
856
+
857
+ // src/http/system-ca.ts
858
+ import { spawnSync } from "child_process";
859
+ import { existsSync as existsSync2, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
860
+ import { dirname, join } from "path";
861
+ import tls from "tls";
862
+ var NODE_USE_SYSTEM_CA = "NODE_USE_SYSTEM_CA";
863
+ var NODE_EXTRA_CA_CERTS = "NODE_EXTRA_CA_CERTS";
864
+ var BUNDLE_NAME = "system-ca.pem";
865
+ var BUNDLE_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
866
+ var LINUX_CA_FILES = [
867
+ "/etc/ssl/certs/ca-certificates.crt",
868
+ "/etc/pki/tls/certs/ca-bundle.crt",
869
+ "/etc/ssl/ca-bundle.pem",
870
+ "/etc/ssl/cert.pem"
871
+ ];
872
+ var PEM_CERT_RE = /-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g;
873
+ function splitPemCertificates(pem) {
874
+ return pem.match(PEM_CERT_RE) ?? [];
854
875
  }
855
- function toolDetail(name, input) {
856
- if (!input) return void 0;
857
- const command = str(input.command) ?? str(input.cmd);
858
- if (command) return clipChatLine(command);
859
- const pattern = str(input.pattern) ?? str(input.glob) ?? str(input.glob_pattern);
860
- const isSearch = /grep|glob|search|ripgrep|findfiles|semsearch/i.test(name);
861
- if (isSearch && pattern) {
862
- return pattern.length > 80 ? `${pattern.slice(0, 77)}\u2026` : pattern;
863
- }
864
- const path = str(input.file_path) ?? str(input.path) ?? str(input.filePath) ?? str(input.filename);
865
- if (path) return path;
866
- if (pattern) return pattern.length > 80 ? `${pattern.slice(0, 77)}\u2026` : pattern;
867
- const query = str(input.query) ?? str(input.prompt);
868
- if (query) return query.length > 80 ? `${query.slice(0, 77)}\u2026` : query;
876
+ function readExistingFile(path) {
869
877
  try {
870
- const raw = JSON.stringify(input);
871
- return raw.length > 80 ? `${raw.slice(0, 77)}\u2026` : raw;
878
+ return existsSync2(path) ? readFileSync(path, "utf8") : "";
872
879
  } catch {
873
- return name;
880
+ return "";
874
881
  }
875
882
  }
876
- function toolDescription(name, input) {
877
- const n = name.replace(/^mcp__/, "").replace(/__/g, " \xB7 ");
878
- if (/^get_record_types$|recordTypes/i.test(name)) return "List record types";
879
- if (/connectedAgentRequest/i.test(name)) {
880
- return str(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
881
- }
882
- if (/present_artifact$/i.test(name)) {
883
- return str(input?.title) ? `Present ${str(input?.title)}` : "Present artifact";
884
- }
885
- if (/present_plan$/i.test(name)) {
886
- return str(input?.title) ? `Plan ${str(input?.title)}` : "Present plan";
883
+ function darwinKeychainPem(spawn) {
884
+ const result = spawn("/usr/bin/security", ["find-certificate", "-a", "-p"], {
885
+ encoding: "utf8",
886
+ maxBuffer: 32 * 1024 * 1024,
887
+ timeout: 15e3
888
+ });
889
+ if (result.status !== 0) return "";
890
+ return typeof result.stdout === "string" ? result.stdout : "";
891
+ }
892
+ function linuxSystemCaPem() {
893
+ for (const path of LINUX_CA_FILES) {
894
+ const pem = readExistingFile(path);
895
+ if (splitPemCertificates(pem).length > 0) return pem;
887
896
  }
888
- if (/present_schema$/i.test(name)) {
889
- return str(input?.title) ? `Schema ${str(input?.title)}` : "Present schema";
897
+ return "";
898
+ }
899
+ function collectSystemCaPem(io = {}) {
900
+ const platform = io.platform ?? process.platform;
901
+ if (platform === "darwin") {
902
+ return darwinKeychainPem(io.spawn ?? spawnSync);
890
903
  }
891
- if (/present_files$/i.test(name)) {
892
- return str(input?.title) ? `Files ${str(input?.title)}` : "Present files";
904
+ if (platform === "linux") {
905
+ return linuxSystemCaPem();
893
906
  }
894
- if (/wait_for_job$/i.test(name)) {
895
- return str(input?.id) ? `Wait for ${str(input?.id)}` : "Wait for job";
907
+ return "";
908
+ }
909
+ function systemCaBundlePath(io = {}) {
910
+ return join((io.appData ?? appDataDir)(), BUNDLE_NAME);
911
+ }
912
+ function bundleIsFresh(path, now) {
913
+ try {
914
+ if (!existsSync2(path)) return false;
915
+ if (splitPemCertificates(readFileSync(path, "utf8")).length === 0) return false;
916
+ return now - statSync(path).mtimeMs < BUNDLE_MAX_AGE_MS;
917
+ } catch {
918
+ return false;
896
919
  }
897
- if (/stop_job$/i.test(name)) {
898
- return str(input?.id) ? `Stop ${str(input?.id)}` : "Stop job";
920
+ }
921
+ function materializeSystemCaBundle(io = {}) {
922
+ const inherited = process.env.NODE_EXTRA_CA_CERTS?.trim();
923
+ if (inherited && splitPemCertificates(readExistingFile(inherited)).length > 0) {
924
+ return inherited;
899
925
  }
900
- if (isSubagentToolName(name)) {
901
- const desc = str(input?.description);
902
- const sub = asRecord(input?.subagentType);
903
- const kind = str(sub?.name) ?? str(sub?.kind) ?? str(input?.subagent_type) ?? str(input?.subagentType);
904
- if (/^spawn_agent$/i.test(name)) {
905
- return str(input?.prompt) ?? desc ?? "Subagent";
906
- }
907
- if (/connectedAgentRequest/i.test(name)) {
908
- return str(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
926
+ const platform = io.platform ?? process.platform;
927
+ if (platform === "linux") {
928
+ for (const path of LINUX_CA_FILES) {
929
+ if (splitPemCertificates(readExistingFile(path)).length > 0) return path;
909
930
  }
910
- if (desc && kind) return `${kind}: ${desc}${subagentLiveSuffix(input)}`;
911
- if (desc) return `${desc}${subagentLiveSuffix(input)}`;
912
- return `Subagent${subagentLiveSuffix(input)}`;
913
- }
914
- if (/^(create|update)_artifact$/i.test(name.replace(/^mcp__[^_]+__/, ""))) {
915
- return str(input?.title) ? `Artifact ${str(input?.title)}` : "Artifact";
916
- }
917
- if (!input) return n;
918
- if (/bash|shell|terminal/i.test(name) && str(input.command)) {
919
- const cmd = str(input.command);
920
- if (/git\s+fetch/i.test(cmd)) return "Fetch latest from origin and check status";
921
- if (/git\s+status/i.test(cmd)) return "Check git status";
922
- if (/git\s+log/i.test(cmd)) return "Inspect recent commits";
923
- if (/git\s+branch/i.test(cmd)) return "List branches";
924
- if (/grep|rg\b/i.test(cmd)) return "Search repository";
925
- return "Run shell command";
926
- }
927
- if (/edit|write|apply/i.test(name)) {
928
- const path = str(input.file_path) ?? str(input.path);
929
- return path ? `Edit ${path.split("/").pop()}` : "Edit file";
930
- }
931
- if (/read/i.test(name)) {
932
- const path = str(input.file_path) ?? str(input.path);
933
- return path ? `Read ${path.split("/").pop()}` : "Read file";
934
- }
935
- if (/grep/i.test(name)) return "Search files";
936
- if (/glob/i.test(name)) return "Find files";
937
- const compact = n.replace(/[_-]/g, "");
938
- if (/^taskoutput$/i.test(compact)) {
939
- return str(input.task_id) ? `Wait for ${str(input.task_id)}` : "Wait for background task";
940
931
  }
941
- if (/^taskstop$/i.test(compact)) {
942
- return str(input.task_id) ? `Stop ${str(input.task_id)}` : "Stop background task";
932
+ const dest = systemCaBundlePath(io);
933
+ const now = (io.now ?? Date.now)();
934
+ if (bundleIsFresh(dest, now)) return dest;
935
+ const pem = collectSystemCaPem(io);
936
+ if (splitPemCertificates(pem).length === 0) {
937
+ return existsSync2(dest) && splitPemCertificates(readExistingFile(dest)).length > 0 ? dest : void 0;
943
938
  }
944
- if (/^monitor$/i.test(compact)) {
945
- const command = str(input.command);
946
- return command ? `Watch ${command.length > 48 ? `${command.slice(0, 45)}\u2026` : command}` : "Watch command";
939
+ try {
940
+ mkdirSync(dirname(dest), { recursive: true });
941
+ writeFileSync(dest, pem.endsWith("\n") ? pem : `${pem}
942
+ `, { mode: 384 });
943
+ return dest;
944
+ } catch {
945
+ return void 0;
947
946
  }
948
- return n;
949
947
  }
950
- function subagentLiveSuffix(input) {
951
- if (!input) return "";
952
- const bits = [];
953
- const status = str(input.live_status);
954
- if (status && !/^runn(ing)?$|^working$/i.test(status)) bits.push(status);
955
- if (typeof input.live_tool_uses === "number") bits.push(`${input.live_tool_uses} tools`);
956
- if (typeof input.live_duration_ms === "number") {
957
- bits.push(`${Math.max(0, Math.round(Number(input.live_duration_ms) / 1e3))}s`);
948
+ function applySystemCaEnv(env) {
949
+ if (!String(env[NODE_USE_SYSTEM_CA] ?? "").trim()) {
950
+ env[NODE_USE_SYSTEM_CA] = "1";
958
951
  }
959
- const last = str(input.live_last_tool);
960
- if (last) bits.push(last);
961
- return bits.length ? ` \xB7 ${bits.join(" \xB7 ")}` : "";
962
- }
963
- function isSubagentToolName(name) {
964
- const n = (name ?? "").trim();
965
- if (/^(task|agent|spawn_agent)$/i.test(n)) return true;
966
- return /connectedAgentRequest/i.test(n);
967
- }
968
- function isInternalAgentStatusText(text) {
969
- const t = text.trim();
970
- if (!t) return false;
971
- if (/waiting for gate to pass/i.test(t)) return true;
972
- if (/^agent is running\b/i.test(t) && t.length < 160) return true;
973
- return false;
952
+ if (String(env[NODE_EXTRA_CA_CERTS] ?? "").trim()) return;
953
+ const inherited = process.env.NODE_EXTRA_CA_CERTS?.trim();
954
+ if (inherited) env[NODE_EXTRA_CA_CERTS] = inherited;
974
955
  }
975
- function isPollWrapperToolName(name) {
976
- const n = (name ?? "").replace(/[_-]/g, "");
977
- return /^(taskoutput|taskstop|sleep)$/i.test(n);
956
+ function extraCertsFromBundle() {
957
+ const path = process.env.NODE_EXTRA_CA_CERTS?.trim();
958
+ if (path) return splitPemCertificates(readExistingFile(path));
959
+ const dest = systemCaBundlePath();
960
+ return splitPemCertificates(readExistingFile(dest));
978
961
  }
979
- function lastTextPart(parts, type) {
980
- for (let i = parts.length - 1; i >= 0; i--) {
981
- const p = parts[i];
982
- if (p.type !== type) continue;
983
- const text = p.text.trim();
984
- if (!text || isInternalAgentStatusText(text)) continue;
985
- return text;
986
- }
987
- return "";
962
+ function trustSystemCertificates(io = {}) {
963
+ try {
964
+ const api = tls;
965
+ if (typeof api.getCACertificates !== "function" || typeof api.setDefaultCACertificates !== "function") {
966
+ return false;
967
+ }
968
+ const current = api.getCACertificates("default");
969
+ const system = api.getCACertificates("system");
970
+ let extra = extraCertsFromBundle();
971
+ if (system.length === 0 && extra.length === 0) {
972
+ extra = splitPemCertificates(collectSystemCaPem(io));
973
+ }
974
+ if (system.length === 0 && extra.length === 0) return false;
975
+ api.setDefaultCACertificates([...current, ...system, ...extra]);
976
+ return true;
977
+ } catch {
978
+ return false;
979
+ }
988
980
  }
989
- function toolLabel(tool) {
990
- return tool.description || toolDescription(tool.name, tool.input) || tool.name;
981
+
982
+ // src/agents/packaged-runtime.ts
983
+ import { existsSync as existsSync3 } from "fs";
984
+ import { join as join2 } from "path";
985
+ function electronResourcesPath() {
986
+ const resources = process.resourcesPath;
987
+ if (typeof resources !== "string" || !resources) return null;
988
+ return resources;
991
989
  }
992
- function liveActivitySummary(parts, opts) {
993
- if (opts?.queued && parts.length === 0) {
994
- return "Queued \u2014 waiting for a slot";
995
- }
996
- const tools = parts.filter((p) => p.type === "tool");
997
- const runningSubs = tools.filter(
998
- (t) => t.status === "running" && !t.parentId && isSubagentToolName(t.name)
999
- );
1000
- const runningNested = [...tools].reverse().find((t) => t.status === "running" && t.parentId && !isPollWrapperToolName(t.name));
1001
- const runningTop = [...tools].reverse().find(
1002
- (t) => t.status === "running" && !t.parentId && !isPollWrapperToolName(t.name) && !isSubagentToolName(t.name)
1003
- );
1004
- const runningPoll = [...tools].reverse().find((t) => t.status === "running" && isPollWrapperToolName(t.name));
1005
- const thinking = lastTextPart(parts, "thinking");
1006
- const text = lastTextPart(parts, "text");
1007
- if (runningSubs.length > 0) {
1008
- const heads = runningSubs.map(toolLabel);
1009
- const head = runningSubs.length === 1 ? heads[0] : `${runningSubs.length} subagents \xB7 ${heads.slice(0, 2).join(" \xB7 ")}`;
1010
- if (runningNested) return `${head} \xB7 ${toolLabel(runningNested)}`;
1011
- return head;
1012
- }
1013
- if (runningTop) return toolLabel(runningTop);
1014
- if (runningPoll) return toolLabel(runningPoll);
1015
- if (thinking) return thinking.length > 96 ? `\u2026${thinking.slice(-96)}` : thinking;
1016
- if (text) return "Writing reply\u2026";
1017
- const last = [...tools].reverse().find((t) => !isPollWrapperToolName(t.name)) ?? tools.at(-1);
1018
- if (last) {
1019
- const label = toolLabel(last);
1020
- return last.status === "running" ? label : `Finished ${label}`;
1021
- }
1022
- return "Working\u2026";
990
+ function packagedCursorRuntimeDir() {
991
+ const resources = electronResourcesPath();
992
+ if (!resources) return null;
993
+ const dir = join2(resources, "cursor-runtime");
994
+ if (!existsSync3(join2(dir, "core-dist", "agents", "cursor-runner.js"))) return null;
995
+ return dir;
1023
996
  }
1024
- function messagePartParentId(part) {
1025
- if ("parentId" in part && typeof part.parentId === "string" && part.parentId.trim()) {
1026
- return part.parentId;
1027
- }
1028
- return void 0;
997
+ function packagedCursorRunnerPath() {
998
+ const dir = packagedCursorRuntimeDir();
999
+ return dir ? join2(dir, "core-dist", "agents", "cursor-runner.js") : null;
1029
1000
  }
1030
- function sameParentId(a, b) {
1031
- return (a ?? "") === (b ?? "");
1001
+ function packagedMcpDir() {
1002
+ const resources = electronResourcesPath();
1003
+ if (!resources) return null;
1004
+ const dir = join2(resources, "sideboard-mcp");
1005
+ if (!existsSync3(join2(dir, "core-dist", "mcp", "run-stdio.js"))) return null;
1006
+ return dir;
1032
1007
  }
1033
- function withEventParentId(event, parentId) {
1034
- if (!parentId) return event;
1035
- if (event.type === "stdout" || event.type === "thinking" || event.type === "tool_use" || event.type === "tool_result") {
1036
- return { ...event, parentId: event.parentId ?? parentId };
1037
- }
1038
- return event;
1008
+ function packagedMcpStdioPath() {
1009
+ const dir = packagedMcpDir();
1010
+ return dir ? join2(dir, "core-dist", "mcp", "run-stdio.js") : null;
1039
1011
  }
1040
- function toolFilePath(input) {
1041
- if (!input) return void 0;
1042
- return str(input.file_path) ?? str(input.path) ?? str(input.filePath) ?? str(input.filename);
1012
+ function packagedBundledNodePath() {
1013
+ const resources = electronResourcesPath();
1014
+ if (!resources) return null;
1015
+ const bin = join2(resources, "node", "bin", "node");
1016
+ if (!existsSync3(bin)) return null;
1017
+ return bin;
1043
1018
  }
1044
- function countLines(text) {
1045
- if (!text) return 0;
1046
- return text.split("\n").length;
1019
+ function packagedCursorRipgrepCandidate(platformPkg, binName) {
1020
+ const dir = packagedCursorRuntimeDir();
1021
+ if (!dir) return null;
1022
+ return join2(dir, "node_modules", platformPkg, "bin", binName);
1047
1023
  }
1048
- function diffFromInput(input) {
1049
- if (!input) return {};
1050
- const oldS = str(input.old_string) ?? str(input.oldString);
1051
- const newS = str(input.new_string) ?? str(input.newString) ?? str(input.content);
1052
- if (oldS != null || newS != null) {
1053
- return {
1054
- additions: countLines(newS),
1055
- deletions: countLines(oldS)
1056
- };
1024
+
1025
+ // src/agents/node-launch.ts
1026
+ var AGENT_RUNNER_MAX_OLD_SPACE_MB = 8192;
1027
+ var MAX_OLD_SPACE_FLAG = /(?:^|\s)(--max[-_]old[-_]space[-_]size)(?:[= ](\d+))?(?=\s|$)/;
1028
+ function withMaxOldSpaceSize(nodeOptions, heapMb) {
1029
+ const existing = (nodeOptions ?? "").trim();
1030
+ const match = MAX_OLD_SPACE_FLAG.exec(existing);
1031
+ if (match) {
1032
+ const current = match[2] ? Number(match[2]) : 0;
1033
+ if (Number.isFinite(current) && current >= heapMb) return existing;
1034
+ return existing.replace(MAX_OLD_SPACE_FLAG, ` --max-old-space-size=${heapMb}`).trim();
1057
1035
  }
1058
- return {};
1036
+ return existing ? `${existing} --max-old-space-size=${heapMb}` : `--max-old-space-size=${heapMb}`;
1059
1037
  }
1060
- function parseDiffStat(result) {
1061
- if (!result) return {};
1062
- const paired = result.match(/\+(\d+)\s+-(\d+)/);
1063
- if (paired) {
1064
- return {
1065
- additions: Number(paired[1]),
1066
- deletions: Number(paired[2])
1067
- };
1068
- }
1069
- const verbose = result.match(
1070
- /(\d+)\s+insertions?(?:,\s*(\d+)\s+deletions?)?/i
1038
+ function applyAgentRunnerHeapEnv(env) {
1039
+ env.NODE_OPTIONS = withMaxOldSpaceSize(
1040
+ env.NODE_OPTIONS,
1041
+ AGENT_RUNNER_MAX_OLD_SPACE_MB
1071
1042
  );
1072
- if (verbose) {
1073
- return {
1074
- additions: Number(verbose[1]),
1075
- deletions: verbose[2] != null ? Number(verbose[2]) : void 0
1076
- };
1077
- }
1078
- return {};
1043
+ applySystemCaEnv(env);
1079
1044
  }
1080
- var THINKING_PART_MAX_CHARS = 64e3;
1081
- function clipThinkingForStore(text, max = THINKING_PART_MAX_CHARS) {
1082
- if (text.length <= max) return text;
1083
- const marker = "\u2026(earlier thinking trimmed)\n";
1084
- return marker + text.slice(text.length - Math.max(1, max - marker.length));
1045
+ function envWithAgentHeap(env) {
1046
+ const next = { ...env };
1047
+ applyAgentRunnerHeapEnv(next);
1048
+ return next;
1085
1049
  }
1086
- var STDOUT_EVENT_MAX_CHARS = 16e3;
1087
- var STDERR_EVENT_MAX_CHARS = 4e3;
1088
- function clipEventTail(text, max) {
1089
- if (text.length <= max) return text;
1090
- const marker = "\u2026(truncated)\n";
1091
- return marker + text.slice(text.length - Math.max(1, max - marker.length));
1050
+ function isAsarPath(filePath) {
1051
+ if (/\.asar\.unpacked([/\\]|$)/.test(filePath)) return false;
1052
+ return /\.asar([/\\]|$)/.test(filePath);
1092
1053
  }
1093
- function clipAgentEventForPaint(event) {
1094
- if (event.type === "tool_result" && event.content) {
1095
- const content = clipToolResultForStore(event.content);
1096
- if (content === event.content) return event;
1097
- return { ...event, content };
1098
- }
1099
- if (event.type === "thinking" && event.data.length > THINKING_PART_MAX_CHARS) {
1100
- return { ...event, data: clipThinkingForStore(event.data) };
1101
- }
1102
- if (event.type === "stdout" && event.data.length > STDOUT_EVENT_MAX_CHARS) {
1103
- return { ...event, data: clipEventTail(event.data, STDOUT_EVENT_MAX_CHARS) };
1054
+ function unpackedAsarPath(filePath) {
1055
+ if (!isAsarPath(filePath)) return null;
1056
+ const unpacked = filePath.replace(/\.asar(?=[/\\])/, ".asar.unpacked");
1057
+ if (unpacked === filePath) return null;
1058
+ return existsSync4(unpacked) ? unpacked : null;
1059
+ }
1060
+ function nodeReadableScriptPath(scriptPath) {
1061
+ return unpackedAsarPath(scriptPath) ?? scriptPath;
1062
+ }
1063
+ var PREFERRED_LTS_MAJORS = [24, 22];
1064
+ function parseNodeMajor(version) {
1065
+ const match = /^v?(\d+)/.exec(version.trim());
1066
+ if (!match) return null;
1067
+ const major = Number(match[1]);
1068
+ return Number.isInteger(major) ? major : null;
1069
+ }
1070
+ function scoreNodeForAgentRuntime(candidate) {
1071
+ const major = parseNodeMajor(candidate.version);
1072
+ if (major == null) return Number.NEGATIVE_INFINITY;
1073
+ if (major < 22) return major - 100;
1074
+ const posix = candidate.path.replace(/\\/g, "/");
1075
+ let score = 0;
1076
+ if (major % 2 === 0) {
1077
+ score += 1e3 + major * 10;
1078
+ } else {
1079
+ score += major;
1104
1080
  }
1105
- if (event.type === "stderr" && event.data.length > STDERR_EVENT_MAX_CHARS) {
1106
- return { ...event, data: clipEventTail(event.data, STDERR_EVENT_MAX_CHARS) };
1081
+ if (/\/Cellar\/node\/\d/.test(posix) && !/\/Cellar\/node@\d+/.test(posix)) {
1082
+ score -= 50;
1107
1083
  }
1108
- return event;
1109
- }
1110
- function withPartTimes(next, prev) {
1111
- const now = Date.now();
1112
- const startedAt = prev && typeof prev.startedAt === "number" ? prev.startedAt : now;
1113
- return { ...next, startedAt, updatedAt: now };
1084
+ return score;
1114
1085
  }
1115
- function applyAgentEvent(parts, event) {
1116
- if (event.type === "stdout") {
1117
- const data = event.data;
1118
- if (!data) return parts;
1119
- const next = [...parts];
1120
- const last = next[next.length - 1];
1121
- if (last?.type === "text" && sameParentId(last.parentId, event.parentId)) {
1122
- next[next.length - 1] = withPartTimes(
1123
- {
1124
- type: "text",
1125
- text: last.text + data,
1126
- ...event.parentId ? { parentId: event.parentId } : {}
1127
- },
1128
- last
1129
- );
1130
- } else {
1131
- next.push(
1132
- withPartTimes({
1133
- type: "text",
1134
- text: data,
1135
- ...event.parentId ? { parentId: event.parentId } : {}
1136
- })
1137
- );
1086
+ function pickPreferredNode(candidates) {
1087
+ let best = null;
1088
+ let bestScore = Number.NEGATIVE_INFINITY;
1089
+ for (const candidate of candidates) {
1090
+ const score = scoreNodeForAgentRuntime(candidate);
1091
+ if (score > bestScore) {
1092
+ bestScore = score;
1093
+ best = candidate;
1138
1094
  }
1139
- return next;
1140
1095
  }
1141
- if (event.type === "thinking") {
1142
- const data = event.data;
1143
- if (!data) return parts;
1144
- const next = [...parts];
1145
- if (event.replace) {
1146
- for (let i = next.length - 1; i >= 0; i--) {
1147
- const prev = next[i];
1148
- if (prev?.type === "thinking" && sameParentId(prev.parentId, event.parentId)) {
1149
- next[i] = withPartTimes(
1150
- {
1151
- type: "thinking",
1152
- text: clipThinkingForStore(data),
1153
- ...event.parentId ? { parentId: event.parentId } : {}
1154
- },
1155
- prev
1156
- );
1157
- return next;
1158
- }
1159
- }
1160
- next.push(
1161
- withPartTimes({
1162
- type: "thinking",
1163
- text: clipThinkingForStore(data),
1164
- ...event.parentId ? { parentId: event.parentId } : {}
1165
- })
1166
- );
1167
- return next;
1168
- }
1169
- const last = next[next.length - 1];
1170
- if (last?.type === "thinking" && sameParentId(last.parentId, event.parentId)) {
1171
- next[next.length - 1] = withPartTimes(
1172
- {
1173
- type: "thinking",
1174
- text: clipThinkingForStore(last.text + data),
1175
- ...event.parentId ? { parentId: event.parentId } : {}
1176
- },
1177
- last
1178
- );
1179
- } else {
1180
- next.push(
1181
- withPartTimes({
1182
- type: "thinking",
1183
- text: clipThinkingForStore(data),
1184
- ...event.parentId ? { parentId: event.parentId } : {}
1185
- })
1186
- );
1187
- }
1188
- return next;
1096
+ return best;
1097
+ }
1098
+ function versionDirNodeBins(root, toBin) {
1099
+ if (!existsSync4(root)) return [];
1100
+ try {
1101
+ return readdirSync(root).map(toBin);
1102
+ } catch {
1103
+ return [];
1189
1104
  }
1190
- if (event.type === "tool_use") {
1191
- const input = asRecord(event.input);
1192
- const diff = diffFromInput(input);
1193
- const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
1194
- if (existing >= 0) {
1195
- const prev = parts[existing];
1196
- const next = [...parts];
1197
- const mergedInput = {
1198
- ...prev.input ?? {},
1199
- ...input ?? {}
1200
- };
1201
- const mergedRecord = Object.keys(mergedInput).length > 0 ? mergedInput : void 0;
1202
- next[existing] = withPartTimes(
1203
- {
1204
- ...prev,
1205
- name: event.name || prev.name,
1206
- input: mergedRecord,
1207
- description: toolDescription(event.name || prev.name, mergedRecord),
1208
- detail: toolDetail(event.name || prev.name, mergedRecord) ?? prev.detail,
1209
- filePath: toolFilePath(mergedRecord) ?? prev.filePath,
1210
- additions: diff.additions ?? prev.additions,
1211
- deletions: diff.deletions ?? prev.deletions,
1212
- parentId: event.parentId ?? prev.parentId
1213
- },
1214
- prev
1215
- );
1216
- return next;
1105
+ }
1106
+ function defaultNodeBinCandidates(home = homedir()) {
1107
+ const kegs = ["/opt/homebrew", "/usr/local"].flatMap(
1108
+ (prefix) => PREFERRED_LTS_MAJORS.map((major) => join3(prefix, "opt", `node@${major}`, "bin", "node"))
1109
+ );
1110
+ return [
1111
+ ...kegs,
1112
+ "/opt/homebrew/bin/node",
1113
+ "/usr/local/bin/node",
1114
+ join3(home, ".local/share/fnm/aliases/default/bin/node"),
1115
+ join3(home, ".nvm/current/bin/node"),
1116
+ join3(home, ".volta/bin/node"),
1117
+ join3(home, ".asdf/shims/node"),
1118
+ join3(home, ".local/share/mise/shims/node"),
1119
+ ...versionDirNodeBins(
1120
+ join3(home, ".nvm", "versions", "node"),
1121
+ (name) => join3(home, ".nvm", "versions", "node", name, "bin", "node")
1122
+ ),
1123
+ ...versionDirNodeBins(
1124
+ join3(home, ".local/share/fnm", "node-versions"),
1125
+ (name) => join3(home, ".local/share/fnm", "node-versions", name, "installation", "bin", "node")
1126
+ ),
1127
+ ...versionDirNodeBins(
1128
+ join3(home, ".volta", "tools", "image", "node"),
1129
+ (name) => join3(home, ".volta", "tools", "image", "node", name, "bin", "node")
1130
+ )
1131
+ ];
1132
+ }
1133
+ function uniqueExistingNodeBins(paths) {
1134
+ const seen = /* @__PURE__ */ new Set();
1135
+ const out = [];
1136
+ for (const raw of paths) {
1137
+ const p = raw.trim();
1138
+ if (!p || !existsSync4(p) || isElectronLikeCommand(p)) continue;
1139
+ let key = p;
1140
+ try {
1141
+ key = realpathSync(p);
1142
+ } catch {
1143
+ continue;
1217
1144
  }
1218
- return [
1219
- ...parts,
1220
- withPartTimes({
1221
- type: "tool",
1222
- id: event.id,
1223
- name: event.name,
1224
- input,
1225
- description: toolDescription(event.name, input),
1226
- detail: toolDetail(event.name, input),
1227
- status: "running",
1228
- filePath: toolFilePath(input),
1229
- ...event.parentId ? { parentId: event.parentId } : {},
1230
- ...diff
1231
- })
1232
- ];
1145
+ if (seen.has(key)) continue;
1146
+ seen.add(key);
1147
+ out.push(p);
1233
1148
  }
1234
- if (event.type === "tool_result") {
1235
- const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
1236
- const fromResult = parseDiffStat(event.content);
1237
- if (event.partial) {
1238
- if (existing < 0) {
1239
- return [
1240
- ...parts,
1241
- withPartTimes({
1242
- type: "tool",
1243
- id: event.id,
1244
- name: "tool",
1245
- description: "tool",
1246
- status: event.isError ? "error" : "running",
1247
- result: clipToolResultForStore(event.content),
1248
- ...event.parentId ? { parentId: event.parentId } : {}
1249
- })
1250
- ];
1251
- }
1252
- return parts.map((p, i) => {
1253
- if (i !== existing || p.type !== "tool") return p;
1254
- if (p.status === "done" || p.status === "error") return p;
1255
- return withPartTimes(
1256
- {
1257
- ...p,
1258
- result: clipToolResultForStore(event.content) ?? p.result
1259
- },
1260
- p
1261
- );
1262
- });
1263
- }
1264
- if (existing < 0) {
1265
- return [
1266
- ...parts,
1267
- withPartTimes({
1268
- type: "tool",
1269
- id: event.id,
1270
- name: "tool",
1271
- description: "tool",
1272
- status: event.isError ? "error" : "done",
1273
- result: clipToolResultForStore(event.content),
1274
- ...event.parentId ? { parentId: event.parentId } : {},
1275
- ...fromResult.additions != null ? { additions: fromResult.additions } : {},
1276
- ...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
1277
- })
1278
- ];
1279
- }
1280
- return parts.map((p, i) => {
1281
- if (i !== existing || p.type !== "tool") return p;
1282
- return withPartTimes(
1283
- {
1284
- ...p,
1285
- status: event.isError ? "error" : "done",
1286
- result: clipToolResultForStore(event.content),
1287
- ...fromResult.additions != null ? { additions: fromResult.additions } : {},
1288
- ...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
1289
- },
1290
- p
1291
- );
1292
- });
1149
+ return out;
1150
+ }
1151
+ async function whichAllNode() {
1152
+ if (process.platform === "win32") {
1153
+ const located = await run("where", ["node"], { reject: false, timeoutMs: 5e3 });
1154
+ if (located.exitCode !== 0) return [];
1155
+ return located.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
1293
1156
  }
1294
- return parts;
1157
+ const all = await run("which", ["-a", "node"], { reject: false, timeoutMs: 5e3 });
1158
+ if (all.exitCode === 0 && all.stdout.trim()) {
1159
+ return all.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
1160
+ }
1161
+ const single = await run("which", ["node"], { reject: false, timeoutMs: 5e3 });
1162
+ if (single.exitCode !== 0 || !single.stdout.trim()) return [];
1163
+ return [single.stdout.trim()];
1295
1164
  }
1296
- function partsToAssistantText(parts) {
1297
- return parts.filter(
1298
- (p) => p.type === "text" && !messagePartParentId(p)
1299
- ).map((p) => p.text).join("").trim();
1165
+ async function probeNodeVersion(bin) {
1166
+ const probed = await run(bin, ["-v"], { reject: false, timeoutMs: 4e3 });
1167
+ if (probed.exitCode !== 0) return null;
1168
+ const version = probed.stdout.trim().split(/\r?\n/).find(Boolean);
1169
+ return version || null;
1300
1170
  }
1301
- function stripBrightsyNdjsonNoise(text) {
1302
- if (!text || !text.includes('"type"')) return text;
1303
- let out = text;
1304
- out = out.replace(
1305
- /\{"type":"(?:tool_use|tool_result|tool|thinking|usage|done|error)"[\s\S]*?\}\s*(?=\{"type":"|$|(?=[A-Za-z*#]))/g,
1306
- ""
1171
+ async function findSystemNode() {
1172
+ const fromPath = await whichAllNode();
1173
+ const bins = uniqueExistingNodeBins([...fromPath, ...defaultNodeBinCandidates()]);
1174
+ if (bins.length === 0) return null;
1175
+ const probed = await Promise.all(
1176
+ bins.map(async (path) => {
1177
+ const version = await probeNodeVersion(path);
1178
+ return version ? { path, version } : null;
1179
+ })
1307
1180
  );
1308
- out = out.replace(/\{"type":"(?:tool_use|tool_result|tool)"[\s\S]*$/g, "");
1309
- return out.trim();
1310
- }
1311
- function isBrightsyNdjsonLine(line) {
1312
- const t = line.trim();
1313
- if (!t.startsWith("{")) return false;
1314
- return /"type"\s*:\s*"(tool_use|tool_result|tool|thinking|usage|done|error|text)"/.test(t);
1181
+ return pickPreferredNode(probed.filter((c) => c != null));
1315
1182
  }
1316
- function finalizeParts(parts) {
1317
- return parts.map(
1318
- (p) => p.type === "tool" && p.status === "running" ? { ...p, status: "done" } : p
1319
- );
1183
+ function applyNodeLaunch(launch, args) {
1184
+ const readableArgs = args.map(nodeReadableScriptPath);
1185
+ if (!launch.env.ELECTRON_RUN_AS_NODE) {
1186
+ return {
1187
+ file: launch.file,
1188
+ args: readableArgs,
1189
+ env: envWithAgentHeap(launch.env)
1190
+ };
1191
+ }
1192
+ const wrapped = wrapElectronAsNodeLaunch(launch.file, readableArgs);
1193
+ if (process.platform === "win32") {
1194
+ return {
1195
+ file: wrapped.file,
1196
+ args: wrapped.args,
1197
+ env: envWithAgentHeap(launch.env)
1198
+ };
1199
+ }
1200
+ const env = envWithAgentHeap(launch.env);
1201
+ delete env.ELECTRON_RUN_AS_NODE;
1202
+ return { file: wrapped.file, args: wrapped.args, env };
1320
1203
  }
1321
- function normalizeParseResult(parsed) {
1322
- if (!parsed) return [];
1323
- return Array.isArray(parsed) ? parsed : [parsed];
1204
+ async function resolveNodeLaunch(scriptPath) {
1205
+ const script = nodeReadableScriptPath(scriptPath);
1206
+ if (!isAsarPath(script)) {
1207
+ const bundled = packagedBundledNodePath();
1208
+ if (bundled && !isElectronLikeCommand(bundled)) {
1209
+ return { file: bundled, env: {} };
1210
+ }
1211
+ const node = await findSystemNode();
1212
+ if (node) {
1213
+ return { file: node.path, env: {}, nodeVersion: node.version };
1214
+ }
1215
+ }
1216
+ return {
1217
+ file: process.execPath,
1218
+ env: { ELECTRON_RUN_AS_NODE: "1" }
1219
+ };
1324
1220
  }
1325
1221
 
1326
1222
  // src/agents/injected-mcp.ts
1327
- import { existsSync as existsSync5, mkdtempSync, writeFileSync as writeFileSync2 } from "fs";
1328
- import { createRequire } from "module";
1329
- import { tmpdir } from "os";
1330
- import { dirname as dirname2, join as join4 } from "path";
1331
- import { fileURLToPath } from "url";
1332
-
1333
- // src/mcp/profile.ts
1334
- var SIDEBOARD_MCP_PROFILE_ENV = "SIDEBOARD_MCP_PROFILE";
1335
- function sideboardMcpProfile(env = process.env) {
1336
- return env[SIDEBOARD_MCP_PROFILE_ENV]?.trim().toLowerCase() === "worktree" ? "worktree" : "orchestration";
1337
- }
1338
-
1339
- // src/agents/node-launch.ts
1340
- import { existsSync as existsSync4, readdirSync, realpathSync } from "fs";
1341
- import { homedir } from "os";
1342
- import { join as join3 } from "path";
1343
-
1344
- // src/http/system-ca.ts
1345
- import { spawnSync } from "child_process";
1346
- import { existsSync as existsSync2, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
1347
- import { dirname, join } from "path";
1348
- import tls from "tls";
1349
- var NODE_USE_SYSTEM_CA = "NODE_USE_SYSTEM_CA";
1350
- var NODE_EXTRA_CA_CERTS = "NODE_EXTRA_CA_CERTS";
1351
- var BUNDLE_NAME = "system-ca.pem";
1352
- var BUNDLE_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
1353
- var LINUX_CA_FILES = [
1354
- "/etc/ssl/certs/ca-certificates.crt",
1355
- "/etc/pki/tls/certs/ca-bundle.crt",
1356
- "/etc/ssl/ca-bundle.pem",
1357
- "/etc/ssl/cert.pem"
1223
+ var SIDEBOARD_MCP_ALLOWED_TOOLS = [
1224
+ "mcp__sideboard",
1225
+ "mcp__sideboard__*"
1358
1226
  ];
1359
- var PEM_CERT_RE = /-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g;
1360
- function splitPemCertificates(pem) {
1361
- return pem.match(PEM_CERT_RE) ?? [];
1227
+ var SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS = [
1228
+ "mcp__sideboard__present_artifact",
1229
+ "mcp__sideboard__present_schema",
1230
+ "mcp__sideboard__present_files",
1231
+ "mcp__sideboard__ask_user",
1232
+ "mcp__sideboard__present_plan",
1233
+ "mcp__sideboard__wait_for_job",
1234
+ "mcp__sideboard__stop_job",
1235
+ "mcp__sideboard__get_viewer_context",
1236
+ "mcp__sideboard__update_viewer_context"
1237
+ ];
1238
+ var SIDEBOARD_GITHUB_MCP_ALLOWED_TOOLS = ["mcp__sideboard__github_*"];
1239
+ var SIDEBOARD_LINEAR_MCP_ALLOWED_TOOLS = ["mcp__sideboard__linear_*"];
1240
+ var SIDEBOARD_ABLETIME_MCP_ALLOWED_TOOLS = ["mcp__sideboard__abletime_*"];
1241
+ function sideboardWorktreeAllowedTools(opts) {
1242
+ const out = [...SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS];
1243
+ if (opts?.github !== false) out.push(...SIDEBOARD_GITHUB_MCP_ALLOWED_TOOLS);
1244
+ if (opts?.linear) out.push(...SIDEBOARD_LINEAR_MCP_ALLOWED_TOOLS);
1245
+ if (opts?.abletime) out.push(...SIDEBOARD_ABLETIME_MCP_ALLOWED_TOOLS);
1246
+ return out;
1247
+ }
1248
+ var brightsyMcpCommandCache = null;
1249
+ async function resolveBrightsyMcpCommand() {
1250
+ const now = Date.now();
1251
+ if (brightsyMcpCommandCache && now - brightsyMcpCommandCache.at < 6e4 && brightsyMcpCommandCache.command) {
1252
+ return brightsyMcpCommandCache.command;
1253
+ }
1254
+ const which = await run("which", ["brightsy-mcp"], { reject: false });
1255
+ const command = which.exitCode === 0 && which.stdout.trim() ? "brightsy-mcp" : "npx";
1256
+ brightsyMcpCommandCache = { at: now, command };
1257
+ return command;
1258
+ }
1259
+ function isBrightsyConnected() {
1260
+ try {
1261
+ loadBrightsyConfig();
1262
+ return true;
1263
+ } catch {
1264
+ return false;
1265
+ }
1266
+ }
1267
+ var BRIGHTSY_WORD = /(?<![a-z0-9_-])brightsy(?![a-z0-9_-])/i;
1268
+ function promptMentionsBrightsy(text) {
1269
+ return BRIGHTSY_WORD.test(text ?? "");
1270
+ }
1271
+ function isBrightsyMcpToolName(name) {
1272
+ const n = name.toLowerCase();
1273
+ return n === "brightsy" || n.startsWith("mcp__brightsy") || n.startsWith("brightsy_");
1274
+ }
1275
+ function toolInputUsesBrightsyDatasource(input) {
1276
+ const ds = input?.datasource;
1277
+ return typeof ds === "string" && ds.toLowerCase() === "brightsy";
1278
+ }
1279
+ function messageUsedBrightsyMcp(msg) {
1280
+ if (msg.role === "user" && promptMentionsBrightsy(msg.text)) return true;
1281
+ for (const part of msg.parts ?? []) {
1282
+ if (part.type !== "tool") continue;
1283
+ if (isBrightsyMcpToolName(part.name)) return true;
1284
+ if (/present_(schema|files)$/i.test(part.name) && toolInputUsesBrightsyDatasource(part.input)) {
1285
+ return true;
1286
+ }
1287
+ }
1288
+ return false;
1289
+ }
1290
+ function threadRequestsBrightsyMcp(thread) {
1291
+ return (thread?.messages ?? []).some(messageUsedBrightsyMcp);
1292
+ }
1293
+ function shouldInjectBrightsyMcp(thread, opts) {
1294
+ if (!(opts?.connected ?? isBrightsyConnected())) return false;
1295
+ if (opts?.orchestrator) return true;
1296
+ const alwaysOn = opts?.alwaysOnWorktree ?? Boolean(loadAppSettings().brightsy?.injectWorktreeMcp);
1297
+ if (alwaysOn) return true;
1298
+ return threadRequestsBrightsyMcp(thread);
1299
+ }
1300
+ function mcpLaunch(cmd, name, env) {
1301
+ if (cmd === "brightsy-mcp") {
1302
+ return { name, command: "brightsy-mcp", ...env ? { env } : {} };
1303
+ }
1304
+ return {
1305
+ name,
1306
+ command: "npx",
1307
+ args: ["-y", "@brightsy/mcp-server"],
1308
+ ...env ? { env } : {}
1309
+ };
1310
+ }
1311
+ function teamEnv(team) {
1312
+ const endpoint = (team.endpoint || "https://brightsy.ai").replace(/\/$/, "");
1313
+ return {
1314
+ BRIGHTSY_API_TOKEN: team.access_token,
1315
+ ...team.refresh_token ? { BRIGHTSY_REFRESH_TOKEN: team.refresh_token } : {},
1316
+ BRIGHTSY_ACCOUNT_ID: team.id,
1317
+ BRIGHTSY_API_URL: endpoint
1318
+ };
1319
+ }
1320
+ function brightsyMcpAllowedTools(serverNames) {
1321
+ const out = [];
1322
+ for (const name of serverNames) {
1323
+ out.push(`mcp__${name}`, `mcp__${name}__*`);
1324
+ }
1325
+ return out;
1326
+ }
1327
+ function corePackageDir() {
1328
+ const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
1329
+ if (cjsDir) return cjsDir;
1330
+ try {
1331
+ const url = import.meta.url;
1332
+ if (typeof url === "string" && url.length > 0) {
1333
+ return dirname2(fileURLToPath(url));
1334
+ }
1335
+ } catch {
1336
+ }
1337
+ try {
1338
+ const req = createRequire(join4(process.cwd(), "package.json"));
1339
+ return dirname2(req.resolve("@sideboard-ai/core"));
1340
+ } catch {
1341
+ return process.cwd();
1342
+ }
1343
+ }
1344
+ function findSideboardMcpJsEntry() {
1345
+ const override = process.env.SIDEBOARD_MCP_ENTRY?.trim() || process.env.SIDEBOARD_CLI?.trim();
1346
+ if (override && existsSync5(override)) return override;
1347
+ const packaged = packagedMcpStdioPath();
1348
+ if (packaged) return packaged;
1349
+ let dir = corePackageDir();
1350
+ for (let i = 0; i < 10; i++) {
1351
+ const candidates = [
1352
+ join4(dir, "mcp/run-stdio.js"),
1353
+ join4(dir, "mcp/run-stdio.cjs"),
1354
+ join4(dir, "dist/mcp/run-stdio.js"),
1355
+ join4(dir, "dist/mcp/run-stdio.cjs"),
1356
+ join4(dir, "packages/core/dist/mcp/run-stdio.js"),
1357
+ join4(dir, "packages/cli/dist/index.js"),
1358
+ join4(dir, "cli/dist/index.js")
1359
+ ];
1360
+ for (const p of candidates) {
1361
+ if (existsSync5(p) && !isAsarPath(p)) return p;
1362
+ }
1363
+ const parent = dirname2(dir);
1364
+ if (parent === dir) break;
1365
+ dir = parent;
1366
+ }
1367
+ return null;
1368
+ }
1369
+ async function resolveSideboardMcpServer() {
1370
+ const entry = findSideboardMcpJsEntry();
1371
+ if (entry) {
1372
+ const isCli = /[/\\]cli[/\\]dist[/\\]index\.js$/.test(entry);
1373
+ const scriptArgs = isCli ? [entry, "mcp"] : [entry];
1374
+ const launch = applyNodeLaunch(await resolveNodeLaunch(entry), scriptArgs);
1375
+ if (launch.file !== "/bin/sh" && !isElectronLikeCommand(launch.file)) {
1376
+ return {
1377
+ name: "sideboard",
1378
+ command: launch.file,
1379
+ args: launch.args,
1380
+ ...Object.keys(launch.env).length > 0 ? { env: launch.env } : {}
1381
+ };
1382
+ }
1383
+ }
1384
+ const which = await run("which", ["sideboard"], { reject: false });
1385
+ if (which.exitCode === 0 && which.stdout.trim()) {
1386
+ return { name: "sideboard", command: which.stdout.trim(), args: ["mcp"] };
1387
+ }
1388
+ return { name: "sideboard", command: "sideboard", args: ["mcp"] };
1389
+ }
1390
+ async function buildInjectedMcpServers(opts) {
1391
+ const servers = [];
1392
+ if (opts.includeSideboard) {
1393
+ const sideboard = await resolveSideboardMcpServer();
1394
+ const orchId = opts.orchestratorThreadId?.trim();
1395
+ const profile = orchId ? "orchestration" : "worktree";
1396
+ sideboard.env = {
1397
+ ...sideboard.env ?? {},
1398
+ SIDEBOARD_APP_DATA: appDataDir(),
1399
+ [SIDEBOARD_MCP_PROFILE_ENV]: profile
1400
+ };
1401
+ if (orchId) {
1402
+ sideboard.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID = orchId;
1403
+ }
1404
+ try {
1405
+ mergeAgentGitAuthEnv(
1406
+ sideboard.env,
1407
+ await resolveAgentGitAuthEnv(sideboard.env)
1408
+ );
1409
+ } catch {
1410
+ }
1411
+ applyAgentRunnerHeapEnv(sideboard.env);
1412
+ servers.push(sideboard);
1413
+ }
1414
+ if (opts.includeBrightsy && isBrightsyConnected()) {
1415
+ const cmd = await resolveBrightsyMcpCommand();
1416
+ const teams = await ensureConnectedBrightsyTeamTokens();
1417
+ if (teams.length > 0) {
1418
+ const used = /* @__PURE__ */ new Set();
1419
+ for (const team of teams) {
1420
+ let name = brightsyMcpServerName(team.slug);
1421
+ if (used.has(name)) name = `${name}_${team.id.slice(0, 8)}`;
1422
+ used.add(name);
1423
+ servers.push(mcpLaunch(cmd, name, teamEnv(team)));
1424
+ }
1425
+ } else {
1426
+ servers.push(mcpLaunch(cmd, "brightsy"));
1427
+ }
1428
+ }
1429
+ return servers;
1362
1430
  }
1363
- function readExistingFile(path) {
1364
- try {
1365
- return existsSync2(path) ? readFileSync(path, "utf8") : "";
1366
- } catch {
1367
- return "";
1431
+ function cursorSafeMcpLaunch(command, args) {
1432
+ if (process.platform === "win32") {
1433
+ return args && args.length > 0 ? { command, args } : { command };
1368
1434
  }
1435
+ const unwrapped = unwrapStrippedElectronLaunch(command, args);
1436
+ const file = unwrapped?.file ?? command;
1437
+ const fileArgs = unwrapped?.args ?? args ?? [];
1438
+ return fileArgs.length > 0 ? { command: file, args: fileArgs } : { command: file };
1369
1439
  }
1370
- function darwinKeychainPem(spawn) {
1371
- const result = spawn("/usr/bin/security", ["find-certificate", "-a", "-p"], {
1372
- encoding: "utf8",
1373
- maxBuffer: 32 * 1024 * 1024,
1374
- timeout: 15e3
1375
- });
1376
- if (result.status !== 0) return "";
1377
- return typeof result.stdout === "string" ? result.stdout : "";
1440
+ function mcpSpawnEnv(env) {
1441
+ if (!env) return void 0;
1442
+ const out = { ...env };
1443
+ delete out.ELECTRON_RUN_AS_NODE;
1444
+ delete out.ELECTRON_RUN_AS_NODE;
1445
+ return Object.keys(out).length > 0 ? out : void 0;
1378
1446
  }
1379
- function linuxSystemCaPem() {
1380
- for (const path of LINUX_CA_FILES) {
1381
- const pem = readExistingFile(path);
1382
- if (splitPemCertificates(pem).length > 0) return pem;
1447
+ function toCursorMcpServers(servers) {
1448
+ const out = {};
1449
+ for (const s of servers) {
1450
+ const env = mcpSpawnEnv(s.env);
1451
+ const launch = cursorSafeMcpLaunch(s.command, s.args);
1452
+ out[s.name] = {
1453
+ type: "stdio",
1454
+ command: launch.command,
1455
+ ...launch.args && launch.args.length > 0 ? { args: launch.args } : {},
1456
+ ...env ? { env } : {}
1457
+ };
1383
1458
  }
1384
- return "";
1459
+ return out;
1385
1460
  }
1386
- function collectSystemCaPem(io = {}) {
1387
- const platform = io.platform ?? process.platform;
1388
- if (platform === "darwin") {
1389
- return darwinKeychainPem(io.spawn ?? spawnSync);
1390
- }
1391
- if (platform === "linux") {
1392
- return linuxSystemCaPem();
1461
+ function toCodexMcpConfigArgs(servers) {
1462
+ const args = [];
1463
+ for (const s of servers) {
1464
+ const prefix = `mcp_servers.${s.name}`;
1465
+ args.push("-c", `${prefix}.command=${JSON.stringify(s.command)}`);
1466
+ if (s.args?.length) {
1467
+ args.push("-c", `${prefix}.args=${JSON.stringify(s.args)}`);
1468
+ }
1469
+ const env = mcpSpawnEnv(s.env);
1470
+ if (env) {
1471
+ for (const [key, value] of Object.entries(env)) {
1472
+ args.push("-c", `${prefix}.env.${key}=${JSON.stringify(value)}`);
1473
+ }
1474
+ }
1475
+ args.push("-c", `${prefix}.default_tools_approval_mode=${JSON.stringify("approve")}`);
1476
+ args.push("-c", `${prefix}.tool_timeout_sec=300`);
1477
+ args.push("-c", `${prefix}.startup_timeout_sec=30`);
1393
1478
  }
1394
- return "";
1395
- }
1396
- function systemCaBundlePath(io = {}) {
1397
- return join((io.appData ?? appDataDir)(), BUNDLE_NAME);
1479
+ return args;
1398
1480
  }
1399
- function bundleIsFresh(path, now) {
1400
- try {
1401
- if (!existsSync2(path)) return false;
1402
- if (splitPemCertificates(readFileSync(path, "utf8")).length === 0) return false;
1403
- return now - statSync(path).mtimeMs < BUNDLE_MAX_AGE_MS;
1404
- } catch {
1405
- return false;
1481
+ function toOpencodeMcpConfigContent(servers, opts) {
1482
+ const mcp = {};
1483
+ for (const s of servers) {
1484
+ const env = mcpSpawnEnv(s.env);
1485
+ mcp[s.name] = {
1486
+ type: "local",
1487
+ command: [s.command, ...s.args ?? []],
1488
+ enabled: true,
1489
+ ...env ? { environment: env } : {}
1490
+ };
1406
1491
  }
1492
+ for (const name of opts?.disableNames ?? []) {
1493
+ const key = name.trim();
1494
+ if (!key || mcp[key]?.enabled === true) continue;
1495
+ mcp[key] = { ...mcp[key], enabled: false };
1496
+ }
1497
+ return JSON.stringify({ mcp });
1407
1498
  }
1408
- function materializeSystemCaBundle(io = {}) {
1409
- const inherited = process.env.NODE_EXTRA_CA_CERTS?.trim();
1410
- if (inherited && splitPemCertificates(readExistingFile(inherited)).length > 0) {
1411
- return inherited;
1499
+ function stripElectronFromUserMcpEntry(entry) {
1500
+ const next = { ...entry };
1501
+ const env = next.env;
1502
+ if (!env || typeof env !== "object" || Array.isArray(env)) return next;
1503
+ const cleaned = { ...env };
1504
+ delete cleaned.ELECTRON_RUN_AS_NODE;
1505
+ next.env = cleaned;
1506
+ return next;
1507
+ }
1508
+ function writeMcpServersConfig(servers, extraServers) {
1509
+ const mcpServers = {};
1510
+ for (const s of servers) {
1511
+ const env = mcpSpawnEnv(s.env);
1512
+ mcpServers[s.name] = {
1513
+ command: s.command,
1514
+ ...s.args ? { args: s.args } : {},
1515
+ ...env ? { env } : {}
1516
+ };
1412
1517
  }
1413
- const platform = io.platform ?? process.platform;
1414
- if (platform === "linux") {
1415
- for (const path of LINUX_CA_FILES) {
1416
- if (splitPemCertificates(readExistingFile(path)).length > 0) return path;
1518
+ if (extraServers) {
1519
+ for (const [rawName, entry] of Object.entries(extraServers)) {
1520
+ const name = rawName.trim();
1521
+ if (!name || mcpServers[name] || !entry) continue;
1522
+ mcpServers[name] = stripElectronFromUserMcpEntry(entry);
1417
1523
  }
1418
1524
  }
1419
- const dest = systemCaBundlePath(io);
1420
- const now = (io.now ?? Date.now)();
1421
- if (bundleIsFresh(dest, now)) return dest;
1422
- const pem = collectSystemCaPem(io);
1423
- if (splitPemCertificates(pem).length === 0) {
1424
- return existsSync2(dest) && splitPemCertificates(readExistingFile(dest)).length > 0 ? dest : void 0;
1425
- }
1525
+ if (Object.keys(mcpServers).length === 0) return null;
1526
+ const dir = mkdtempSync(join4(tmpdir(), "sideboard-mcp-"));
1527
+ const cfgPath = join4(dir, "mcp.json");
1528
+ writeFileSync2(cfgPath, JSON.stringify({ mcpServers }, null, 2));
1529
+ return cfgPath;
1530
+ }
1531
+
1532
+ // src/agents/user-mcp-config.ts
1533
+ function userClaudeMcpConfigPath() {
1534
+ return join5(homedir2(), ".claude.json");
1535
+ }
1536
+
1537
+ // src/agents/orch-mcp-isolation.ts
1538
+ function isInjectedOrchMcpName(name) {
1539
+ const n = name.trim().toLowerCase();
1540
+ return n === "sideboard" || n === "brightsy" || n.startsWith("brightsy_");
1541
+ }
1542
+ function asObject(value) {
1543
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
1544
+ return value;
1545
+ }
1546
+ function listMcpNamesFromJsonMap(raw, key) {
1547
+ return Object.keys(asObject(asObject(raw)[key])).filter((n) => n.trim());
1548
+ }
1549
+ function listMcpNamesFromCodexToml(text) {
1426
1550
  try {
1427
- mkdirSync(dirname(dest), { recursive: true });
1428
- writeFileSync(dest, pem.endsWith("\n") ? pem : `${pem}
1429
- `, { mode: 384 });
1430
- return dest;
1551
+ const parsed = parseToml(text);
1552
+ return Object.keys(asObject(parsed.mcp_servers)).filter((n) => n.trim());
1431
1553
  } catch {
1432
- return void 0;
1554
+ return [];
1433
1555
  }
1434
1556
  }
1435
- function applySystemCaEnv(env) {
1436
- if (!String(env[NODE_USE_SYSTEM_CA] ?? "").trim()) {
1437
- env[NODE_USE_SYSTEM_CA] = "1";
1557
+ function readJsonObject(path) {
1558
+ try {
1559
+ return JSON.parse(readFileSync3(path, "utf8"));
1560
+ } catch {
1561
+ return {};
1438
1562
  }
1439
- if (String(env[NODE_EXTRA_CA_CERTS] ?? "").trim()) return;
1440
- const inherited = process.env.NODE_EXTRA_CA_CERTS?.trim();
1441
- if (inherited) env[NODE_EXTRA_CA_CERTS] = inherited;
1442
1563
  }
1443
- function extraCertsFromBundle() {
1444
- const path = process.env.NODE_EXTRA_CA_CERTS?.trim();
1445
- if (path) return splitPemCertificates(readExistingFile(path));
1446
- const dest = systemCaBundlePath();
1447
- return splitPemCertificates(readExistingFile(dest));
1448
- }
1449
- function trustSystemCertificates(io = {}) {
1564
+ function readText(path) {
1450
1565
  try {
1451
- const api = tls;
1452
- if (typeof api.getCACertificates !== "function" || typeof api.setDefaultCACertificates !== "function") {
1453
- return false;
1454
- }
1455
- const current = api.getCACertificates("default");
1456
- const system = api.getCACertificates("system");
1457
- let extra = extraCertsFromBundle();
1458
- if (system.length === 0 && extra.length === 0) {
1459
- extra = splitPemCertificates(collectSystemCaPem(io));
1460
- }
1461
- if (system.length === 0 && extra.length === 0) return false;
1462
- api.setDefaultCACertificates([...current, ...system, ...extra]);
1463
- return true;
1566
+ return readFileSync3(path, "utf8");
1464
1567
  } catch {
1465
- return false;
1568
+ return "";
1466
1569
  }
1467
1570
  }
1468
-
1469
- // src/agents/packaged-runtime.ts
1470
- import { existsSync as existsSync3 } from "fs";
1471
- import { join as join2 } from "path";
1472
- function electronResourcesPath() {
1473
- const resources = process.resourcesPath;
1474
- if (typeof resources !== "string" || !resources) return null;
1475
- return resources;
1476
- }
1477
- function packagedCursorRuntimeDir() {
1478
- const resources = electronResourcesPath();
1479
- if (!resources) return null;
1480
- const dir = join2(resources, "cursor-runtime");
1481
- if (!existsSync3(join2(dir, "core-dist", "agents", "cursor-runner.js"))) return null;
1482
- return dir;
1483
- }
1484
- function packagedCursorRunnerPath() {
1485
- const dir = packagedCursorRuntimeDir();
1486
- return dir ? join2(dir, "core-dist", "agents", "cursor-runner.js") : null;
1487
- }
1488
- function packagedMcpDir() {
1489
- const resources = electronResourcesPath();
1490
- if (!resources) return null;
1491
- const dir = join2(resources, "sideboard-mcp");
1492
- if (!existsSync3(join2(dir, "core-dist", "mcp", "run-stdio.js"))) return null;
1493
- return dir;
1571
+ function userCodexConfigPaths(home = homedir3()) {
1572
+ return [join6(home, ".codex", "config.toml"), join6(home, ".config", "codex", "config.toml")];
1494
1573
  }
1495
- function packagedMcpStdioPath() {
1496
- const dir = packagedMcpDir();
1497
- return dir ? join2(dir, "core-dist", "mcp", "run-stdio.js") : null;
1574
+ function userOpencodeConfigPaths(home = homedir3()) {
1575
+ return [
1576
+ join6(home, ".config", "opencode", "opencode.json"),
1577
+ join6(home, ".config", "opencode", "opencode.jsonc")
1578
+ ];
1498
1579
  }
1499
- function packagedBundledNodePath() {
1500
- const resources = electronResourcesPath();
1501
- if (!resources) return null;
1502
- const bin = join2(resources, "node", "bin", "node");
1503
- if (!existsSync3(bin)) return null;
1504
- return bin;
1580
+ function userMcpNamesToDisable(opts) {
1581
+ const keep = new Set(
1582
+ [...opts.injectedNames ?? []].map((n) => n.trim().toLowerCase()).filter(Boolean)
1583
+ );
1584
+ const out = [];
1585
+ const seen = /* @__PURE__ */ new Set();
1586
+ for (const raw of opts.names) {
1587
+ const name = raw.trim();
1588
+ if (!name) continue;
1589
+ const key = name.toLowerCase();
1590
+ if (seen.has(key) || keep.has(key) || isInjectedOrchMcpName(name)) continue;
1591
+ seen.add(key);
1592
+ out.push(name);
1593
+ }
1594
+ return out;
1505
1595
  }
1506
- function packagedCursorRipgrepCandidate(platformPkg, binName) {
1507
- const dir = packagedCursorRuntimeDir();
1508
- if (!dir) return null;
1509
- return join2(dir, "node_modules", platformPkg, "bin", binName);
1596
+ function listUserCodexMcpNames(home = homedir3()) {
1597
+ const names = [];
1598
+ for (const path of userCodexConfigPaths(home)) {
1599
+ if (!existsSync7(path)) continue;
1600
+ names.push(...listMcpNamesFromCodexToml(readText(path)));
1601
+ }
1602
+ return names;
1510
1603
  }
1511
-
1512
- // src/agents/node-launch.ts
1513
- var AGENT_RUNNER_MAX_OLD_SPACE_MB = 8192;
1514
- var MAX_OLD_SPACE_FLAG = /(?:^|\s)(--max[-_]old[-_]space[-_]size)(?:[= ](\d+))?(?=\s|$)/;
1515
- function withMaxOldSpaceSize(nodeOptions, heapMb) {
1516
- const existing = (nodeOptions ?? "").trim();
1517
- const match = MAX_OLD_SPACE_FLAG.exec(existing);
1518
- if (match) {
1519
- const current = match[2] ? Number(match[2]) : 0;
1520
- if (Number.isFinite(current) && current >= heapMb) return existing;
1521
- return existing.replace(MAX_OLD_SPACE_FLAG, ` --max-old-space-size=${heapMb}`).trim();
1604
+ function listUserOpencodeMcpNames(home = homedir3()) {
1605
+ const names = [];
1606
+ for (const path of userOpencodeConfigPaths(home)) {
1607
+ if (!existsSync7(path)) continue;
1608
+ names.push(...listMcpNamesFromJsonMap(readJsonObject(path), "mcp"));
1522
1609
  }
1523
- return existing ? `${existing} --max-old-space-size=${heapMb}` : `--max-old-space-size=${heapMb}`;
1610
+ return names;
1524
1611
  }
1525
- function applyAgentRunnerHeapEnv(env) {
1526
- env.NODE_OPTIONS = withMaxOldSpaceSize(
1527
- env.NODE_OPTIONS,
1528
- AGENT_RUNNER_MAX_OLD_SPACE_MB
1529
- );
1530
- applySystemCaEnv(env);
1612
+ function userClaudeMcpConfigObject(home = homedir3()) {
1613
+ const claudePath = home === homedir3() ? userClaudeMcpConfigPath() : join6(home, ".claude.json");
1614
+ if (!existsSync7(claudePath)) return {};
1615
+ return asObject(asObject(readJsonObject(claudePath)).mcpServers);
1531
1616
  }
1532
- function envWithAgentHeap(env) {
1533
- const next = { ...env };
1534
- applyAgentRunnerHeapEnv(next);
1535
- return next;
1617
+ function listUserClaudeMcpServerEntries(home = homedir3()) {
1618
+ const out = {};
1619
+ for (const [rawName, value] of Object.entries(userClaudeMcpConfigObject(home))) {
1620
+ const name = rawName.trim();
1621
+ if (!name || isInjectedOrchMcpName(name)) continue;
1622
+ if (!value || typeof value !== "object" || Array.isArray(value)) continue;
1623
+ out[name] = value;
1624
+ }
1625
+ return out;
1536
1626
  }
1537
- function isAsarPath(filePath) {
1538
- if (/\.asar\.unpacked([/\\]|$)/.test(filePath)) return false;
1539
- return /\.asar([/\\]|$)/.test(filePath);
1627
+ function toCodexDisableUserMcpArgs(names) {
1628
+ const args = [];
1629
+ for (const name of names) {
1630
+ if (!/^[A-Za-z0-9_-]+$/.test(name)) continue;
1631
+ args.push("-c", `mcp_servers.${name}.enabled=false`);
1632
+ }
1633
+ return args;
1540
1634
  }
1541
- function unpackedAsarPath(filePath) {
1542
- if (!isAsarPath(filePath)) return null;
1543
- const unpacked = filePath.replace(/\.asar(?=[/\\])/, ".asar.unpacked");
1544
- if (unpacked === filePath) return null;
1545
- return existsSync4(unpacked) ? unpacked : null;
1635
+ function toCodexUnattendedAppsArgs() {
1636
+ return [
1637
+ "-c",
1638
+ "features.apps=false",
1639
+ "-c",
1640
+ "apps._default.enabled=false",
1641
+ "-c",
1642
+ "apps.posthog.enabled=false",
1643
+ "-c",
1644
+ "features.plugins=false"
1645
+ ];
1546
1646
  }
1547
- function nodeReadableScriptPath(scriptPath) {
1548
- return unpackedAsarPath(scriptPath) ?? scriptPath;
1647
+
1648
+ // src/agents/message-parts.ts
1649
+ function asRecord(input) {
1650
+ if (input && typeof input === "object" && !Array.isArray(input)) {
1651
+ return input;
1652
+ }
1653
+ return void 0;
1549
1654
  }
1550
- var PREFERRED_LTS_MAJORS = [24, 22];
1551
- function parseNodeMajor(version) {
1552
- const match = /^v?(\d+)/.exec(version.trim());
1553
- if (!match) return null;
1554
- const major = Number(match[1]);
1555
- return Number.isInteger(major) ? major : null;
1655
+ function str(v) {
1656
+ return typeof v === "string" && v.trim() ? v : void 0;
1556
1657
  }
1557
- function scoreNodeForAgentRuntime(candidate) {
1558
- const major = parseNodeMajor(candidate.version);
1559
- if (major == null) return Number.NEGATIVE_INFINITY;
1560
- if (major < 22) return major - 100;
1561
- const posix = candidate.path.replace(/\\/g, "/");
1562
- let score = 0;
1563
- if (major % 2 === 0) {
1564
- score += 1e3 + major * 10;
1565
- } else {
1566
- score += major;
1567
- }
1568
- if (/\/Cellar\/node\/\d/.test(posix) && !/\/Cellar\/node@\d+/.test(posix)) {
1569
- score -= 50;
1570
- }
1571
- return score;
1658
+ var CHAT_DETAIL_MAX = 72;
1659
+ function clipChatLine(value, max = CHAT_DETAIL_MAX) {
1660
+ const line = value.trim().split(/\r?\n/, 1)[0] ?? "";
1661
+ if (line.length <= max) return line;
1662
+ return `${line.slice(0, Math.max(1, max - 1))}\u2026`;
1572
1663
  }
1573
- function pickPreferredNode(candidates) {
1574
- let best = null;
1575
- let bestScore = Number.NEGATIVE_INFINITY;
1576
- for (const candidate of candidates) {
1577
- const score = scoreNodeForAgentRuntime(candidate);
1578
- if (score > bestScore) {
1579
- bestScore = score;
1580
- best = candidate;
1581
- }
1664
+ function toolDetail(name, input) {
1665
+ if (!input) return void 0;
1666
+ const command = str(input.command) ?? str(input.cmd);
1667
+ if (command) return clipChatLine(command);
1668
+ const pattern = str(input.pattern) ?? str(input.glob) ?? str(input.glob_pattern);
1669
+ const isSearch = /grep|glob|search|ripgrep|findfiles|semsearch/i.test(name);
1670
+ if (isSearch && pattern) {
1671
+ return pattern.length > 80 ? `${pattern.slice(0, 77)}\u2026` : pattern;
1582
1672
  }
1583
- return best;
1584
- }
1585
- function versionDirNodeBins(root, toBin) {
1586
- if (!existsSync4(root)) return [];
1673
+ const path = str(input.file_path) ?? str(input.path) ?? str(input.filePath) ?? str(input.filename);
1674
+ if (path) return path;
1675
+ if (pattern) return pattern.length > 80 ? `${pattern.slice(0, 77)}\u2026` : pattern;
1676
+ const query = str(input.query) ?? str(input.prompt);
1677
+ if (query) return query.length > 80 ? `${query.slice(0, 77)}\u2026` : query;
1587
1678
  try {
1588
- return readdirSync(root).map(toBin);
1679
+ const raw = JSON.stringify(input);
1680
+ return raw.length > 80 ? `${raw.slice(0, 77)}\u2026` : raw;
1589
1681
  } catch {
1590
- return [];
1682
+ return name;
1591
1683
  }
1592
1684
  }
1593
- function defaultNodeBinCandidates(home = homedir()) {
1594
- const kegs = ["/opt/homebrew", "/usr/local"].flatMap(
1595
- (prefix) => PREFERRED_LTS_MAJORS.map((major) => join3(prefix, "opt", `node@${major}`, "bin", "node"))
1596
- );
1597
- return [
1598
- ...kegs,
1599
- "/opt/homebrew/bin/node",
1600
- "/usr/local/bin/node",
1601
- join3(home, ".local/share/fnm/aliases/default/bin/node"),
1602
- join3(home, ".nvm/current/bin/node"),
1603
- join3(home, ".volta/bin/node"),
1604
- join3(home, ".asdf/shims/node"),
1605
- join3(home, ".local/share/mise/shims/node"),
1606
- ...versionDirNodeBins(
1607
- join3(home, ".nvm", "versions", "node"),
1608
- (name) => join3(home, ".nvm", "versions", "node", name, "bin", "node")
1609
- ),
1610
- ...versionDirNodeBins(
1611
- join3(home, ".local/share/fnm", "node-versions"),
1612
- (name) => join3(home, ".local/share/fnm", "node-versions", name, "installation", "bin", "node")
1613
- ),
1614
- ...versionDirNodeBins(
1615
- join3(home, ".volta", "tools", "image", "node"),
1616
- (name) => join3(home, ".volta", "tools", "image", "node", name, "bin", "node")
1617
- )
1618
- ];
1619
- }
1620
- function uniqueExistingNodeBins(paths) {
1621
- const seen = /* @__PURE__ */ new Set();
1622
- const out = [];
1623
- for (const raw of paths) {
1624
- const p = raw.trim();
1625
- if (!p || !existsSync4(p) || isElectronLikeCommand(p)) continue;
1626
- let key = p;
1627
- try {
1628
- key = realpathSync(p);
1629
- } catch {
1630
- continue;
1685
+ function toolDescription(name, input) {
1686
+ const n = name.replace(/^mcp__/, "").replace(/__/g, " \xB7 ");
1687
+ if (/^get_record_types$|recordTypes/i.test(name)) return "List record types";
1688
+ if (/connectedAgentRequest/i.test(name)) {
1689
+ return str(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
1690
+ }
1691
+ if (/present_artifact$/i.test(name)) {
1692
+ return str(input?.title) ? `Present ${str(input?.title)}` : "Present artifact";
1693
+ }
1694
+ if (/present_plan$/i.test(name)) {
1695
+ return str(input?.title) ? `Plan ${str(input?.title)}` : "Present plan";
1696
+ }
1697
+ if (/present_schema$/i.test(name)) {
1698
+ return str(input?.title) ? `Schema ${str(input?.title)}` : "Present schema";
1699
+ }
1700
+ if (/present_files$/i.test(name)) {
1701
+ return str(input?.title) ? `Files ${str(input?.title)}` : "Present files";
1702
+ }
1703
+ if (/wait_for_job$/i.test(name)) {
1704
+ return str(input?.id) ? `Wait for ${str(input?.id)}` : "Wait for job";
1705
+ }
1706
+ if (/stop_job$/i.test(name)) {
1707
+ return str(input?.id) ? `Stop ${str(input?.id)}` : "Stop job";
1708
+ }
1709
+ if (isSubagentToolName(name)) {
1710
+ const desc = str(input?.description);
1711
+ const sub = asRecord(input?.subagentType);
1712
+ const kind = str(sub?.name) ?? str(sub?.kind) ?? str(input?.subagent_type) ?? str(input?.subagentType);
1713
+ if (/^spawn_agent$/i.test(name)) {
1714
+ return str(input?.prompt) ?? desc ?? "Subagent";
1631
1715
  }
1632
- if (seen.has(key)) continue;
1633
- seen.add(key);
1634
- out.push(p);
1716
+ if (/connectedAgentRequest/i.test(name)) {
1717
+ return str(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
1718
+ }
1719
+ if (desc && kind) return `${kind}: ${desc}${subagentLiveSuffix(input)}`;
1720
+ if (desc) return `${desc}${subagentLiveSuffix(input)}`;
1721
+ return `Subagent${subagentLiveSuffix(input)}`;
1635
1722
  }
1636
- return out;
1637
- }
1638
- async function whichAllNode() {
1639
- if (process.platform === "win32") {
1640
- const located = await run("where", ["node"], { reject: false, timeoutMs: 5e3 });
1641
- if (located.exitCode !== 0) return [];
1642
- return located.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
1723
+ if (/^(create|update)_artifact$/i.test(name.replace(/^mcp__[^_]+__/, ""))) {
1724
+ return str(input?.title) ? `Artifact ${str(input?.title)}` : "Artifact";
1643
1725
  }
1644
- const all = await run("which", ["-a", "node"], { reject: false, timeoutMs: 5e3 });
1645
- if (all.exitCode === 0 && all.stdout.trim()) {
1646
- return all.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
1726
+ if (!input) return n;
1727
+ if (/bash|shell|terminal/i.test(name) && str(input.command)) {
1728
+ const cmd = str(input.command);
1729
+ if (/git\s+fetch/i.test(cmd)) return "Fetch latest from origin and check status";
1730
+ if (/git\s+status/i.test(cmd)) return "Check git status";
1731
+ if (/git\s+log/i.test(cmd)) return "Inspect recent commits";
1732
+ if (/git\s+branch/i.test(cmd)) return "List branches";
1733
+ if (/grep|rg\b/i.test(cmd)) return "Search repository";
1734
+ return "Run shell command";
1735
+ }
1736
+ if (/edit|write|apply/i.test(name)) {
1737
+ const path = str(input.file_path) ?? str(input.path);
1738
+ return path ? `Edit ${path.split("/").pop()}` : "Edit file";
1739
+ }
1740
+ if (/read/i.test(name)) {
1741
+ const path = str(input.file_path) ?? str(input.path);
1742
+ return path ? `Read ${path.split("/").pop()}` : "Read file";
1743
+ }
1744
+ if (/grep/i.test(name)) return "Search files";
1745
+ if (/glob/i.test(name)) return "Find files";
1746
+ const compact = n.replace(/[_-]/g, "");
1747
+ if (/^taskoutput$/i.test(compact)) {
1748
+ return str(input.task_id) ? `Wait for ${str(input.task_id)}` : "Wait for background task";
1749
+ }
1750
+ if (/^taskstop$/i.test(compact)) {
1751
+ return str(input.task_id) ? `Stop ${str(input.task_id)}` : "Stop background task";
1752
+ }
1753
+ if (/^monitor$/i.test(compact)) {
1754
+ const command = str(input.command);
1755
+ return command ? `Watch ${command.length > 48 ? `${command.slice(0, 45)}\u2026` : command}` : "Watch command";
1756
+ }
1757
+ return n;
1758
+ }
1759
+ function subagentLiveSuffix(input) {
1760
+ if (!input) return "";
1761
+ const bits = [];
1762
+ const status = str(input.live_status);
1763
+ if (status && !/^runn(ing)?$|^working$/i.test(status)) bits.push(status);
1764
+ if (typeof input.live_tool_uses === "number") bits.push(`${input.live_tool_uses} tools`);
1765
+ if (typeof input.live_duration_ms === "number") {
1766
+ bits.push(`${Math.max(0, Math.round(Number(input.live_duration_ms) / 1e3))}s`);
1647
1767
  }
1648
- const single = await run("which", ["node"], { reject: false, timeoutMs: 5e3 });
1649
- if (single.exitCode !== 0 || !single.stdout.trim()) return [];
1650
- return [single.stdout.trim()];
1768
+ const last = str(input.live_last_tool);
1769
+ if (last) bits.push(last);
1770
+ return bits.length ? ` \xB7 ${bits.join(" \xB7 ")}` : "";
1651
1771
  }
1652
- async function probeNodeVersion(bin) {
1653
- const probed = await run(bin, ["-v"], { reject: false, timeoutMs: 4e3 });
1654
- if (probed.exitCode !== 0) return null;
1655
- const version = probed.stdout.trim().split(/\r?\n/).find(Boolean);
1656
- return version || null;
1772
+ function isSubagentToolName(name) {
1773
+ const n = (name ?? "").trim();
1774
+ if (/^(task|agent|spawn_agent)$/i.test(n)) return true;
1775
+ return /connectedAgentRequest/i.test(n);
1657
1776
  }
1658
- async function findSystemNode() {
1659
- const fromPath = await whichAllNode();
1660
- const bins = uniqueExistingNodeBins([...fromPath, ...defaultNodeBinCandidates()]);
1661
- if (bins.length === 0) return null;
1662
- const probed = await Promise.all(
1663
- bins.map(async (path) => {
1664
- const version = await probeNodeVersion(path);
1665
- return version ? { path, version } : null;
1666
- })
1667
- );
1668
- return pickPreferredNode(probed.filter((c) => c != null));
1777
+ function isInternalAgentStatusText(text) {
1778
+ const t = text.trim();
1779
+ if (!t) return false;
1780
+ if (/waiting for gate to pass/i.test(t)) return true;
1781
+ if (/^agent is running\b/i.test(t) && t.length < 160) return true;
1782
+ return false;
1669
1783
  }
1670
- function applyNodeLaunch(launch, args) {
1671
- const readableArgs = args.map(nodeReadableScriptPath);
1672
- if (!launch.env.ELECTRON_RUN_AS_NODE) {
1673
- return {
1674
- file: launch.file,
1675
- args: readableArgs,
1676
- env: envWithAgentHeap(launch.env)
1677
- };
1678
- }
1679
- const wrapped = wrapElectronAsNodeLaunch(launch.file, readableArgs);
1680
- if (process.platform === "win32") {
1681
- return {
1682
- file: wrapped.file,
1683
- args: wrapped.args,
1684
- env: envWithAgentHeap(launch.env)
1685
- };
1686
- }
1687
- const env = envWithAgentHeap(launch.env);
1688
- delete env.ELECTRON_RUN_AS_NODE;
1689
- return { file: wrapped.file, args: wrapped.args, env };
1784
+ function isPollWrapperToolName(name) {
1785
+ const n = (name ?? "").replace(/[_-]/g, "");
1786
+ return /^(taskoutput|taskstop|sleep)$/i.test(n);
1690
1787
  }
1691
- async function resolveNodeLaunch(scriptPath) {
1692
- const script = nodeReadableScriptPath(scriptPath);
1693
- if (!isAsarPath(script)) {
1694
- const bundled = packagedBundledNodePath();
1695
- if (bundled && !isElectronLikeCommand(bundled)) {
1696
- return { file: bundled, env: {} };
1697
- }
1698
- const node = await findSystemNode();
1699
- if (node) {
1700
- return { file: node.path, env: {}, nodeVersion: node.version };
1701
- }
1788
+ function lastTextPart(parts, type) {
1789
+ for (let i = parts.length - 1; i >= 0; i--) {
1790
+ const p = parts[i];
1791
+ if (p.type !== type) continue;
1792
+ const text = p.text.trim();
1793
+ if (!text || isInternalAgentStatusText(text)) continue;
1794
+ return text;
1702
1795
  }
1703
- return {
1704
- file: process.execPath,
1705
- env: { ELECTRON_RUN_AS_NODE: "1" }
1706
- };
1796
+ return "";
1707
1797
  }
1708
-
1709
- // src/agents/injected-mcp.ts
1710
- var SIDEBOARD_MCP_ALLOWED_TOOLS = [
1711
- "mcp__sideboard",
1712
- "mcp__sideboard__*"
1713
- ];
1714
- var SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS = [
1715
- "mcp__sideboard__present_artifact",
1716
- "mcp__sideboard__present_schema",
1717
- "mcp__sideboard__present_files",
1718
- "mcp__sideboard__ask_user",
1719
- "mcp__sideboard__present_plan",
1720
- "mcp__sideboard__wait_for_job",
1721
- "mcp__sideboard__stop_job",
1722
- "mcp__sideboard__get_viewer_context",
1723
- "mcp__sideboard__update_viewer_context"
1724
- ];
1725
- var SIDEBOARD_GITHUB_MCP_ALLOWED_TOOLS = ["mcp__sideboard__github_*"];
1726
- var SIDEBOARD_LINEAR_MCP_ALLOWED_TOOLS = ["mcp__sideboard__linear_*"];
1727
- var SIDEBOARD_ABLETIME_MCP_ALLOWED_TOOLS = ["mcp__sideboard__abletime_*"];
1728
- function sideboardWorktreeAllowedTools(opts) {
1729
- const out = [...SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS];
1730
- if (opts?.github !== false) out.push(...SIDEBOARD_GITHUB_MCP_ALLOWED_TOOLS);
1731
- if (opts?.linear) out.push(...SIDEBOARD_LINEAR_MCP_ALLOWED_TOOLS);
1732
- if (opts?.abletime) out.push(...SIDEBOARD_ABLETIME_MCP_ALLOWED_TOOLS);
1733
- return out;
1798
+ function toolLabel(tool) {
1799
+ return tool.description || toolDescription(tool.name, tool.input) || tool.name;
1734
1800
  }
1735
- var brightsyMcpCommandCache = null;
1736
- async function resolveBrightsyMcpCommand() {
1737
- const now = Date.now();
1738
- if (brightsyMcpCommandCache && now - brightsyMcpCommandCache.at < 6e4 && brightsyMcpCommandCache.command) {
1739
- return brightsyMcpCommandCache.command;
1801
+ function liveActivitySummary(parts, opts) {
1802
+ if (opts?.queued && parts.length === 0) {
1803
+ return "Queued \u2014 waiting for a slot";
1740
1804
  }
1741
- const which = await run("which", ["brightsy-mcp"], { reject: false });
1742
- const command = which.exitCode === 0 && which.stdout.trim() ? "brightsy-mcp" : "npx";
1743
- brightsyMcpCommandCache = { at: now, command };
1744
- return command;
1745
- }
1746
- function isBrightsyConnected() {
1747
- try {
1748
- loadBrightsyConfig();
1749
- return true;
1750
- } catch {
1751
- return false;
1805
+ const tools = parts.filter((p) => p.type === "tool");
1806
+ const runningSubs = tools.filter(
1807
+ (t) => t.status === "running" && !t.parentId && isSubagentToolName(t.name)
1808
+ );
1809
+ const runningNested = [...tools].reverse().find((t) => t.status === "running" && t.parentId && !isPollWrapperToolName(t.name));
1810
+ const runningTop = [...tools].reverse().find(
1811
+ (t) => t.status === "running" && !t.parentId && !isPollWrapperToolName(t.name) && !isSubagentToolName(t.name)
1812
+ );
1813
+ const runningPoll = [...tools].reverse().find((t) => t.status === "running" && isPollWrapperToolName(t.name));
1814
+ const thinking = lastTextPart(parts, "thinking");
1815
+ const text = lastTextPart(parts, "text");
1816
+ if (runningSubs.length > 0) {
1817
+ const heads = runningSubs.map(toolLabel);
1818
+ const head = runningSubs.length === 1 ? heads[0] : `${runningSubs.length} subagents \xB7 ${heads.slice(0, 2).join(" \xB7 ")}`;
1819
+ if (runningNested) return `${head} \xB7 ${toolLabel(runningNested)}`;
1820
+ return head;
1752
1821
  }
1822
+ if (runningTop) return toolLabel(runningTop);
1823
+ if (runningPoll) return toolLabel(runningPoll);
1824
+ if (thinking) return thinking.length > 96 ? `\u2026${thinking.slice(-96)}` : thinking;
1825
+ if (text) return "Writing reply\u2026";
1826
+ const last = [...tools].reverse().find((t) => !isPollWrapperToolName(t.name)) ?? tools.at(-1);
1827
+ if (last) {
1828
+ const label = toolLabel(last);
1829
+ return last.status === "running" ? label : `Finished ${label}`;
1830
+ }
1831
+ return "Working\u2026";
1753
1832
  }
1754
- var BRIGHTSY_WORD = /(?<![a-z0-9_-])brightsy(?![a-z0-9_-])/i;
1755
- function promptMentionsBrightsy(text) {
1756
- return BRIGHTSY_WORD.test(text ?? "");
1757
- }
1758
- function isBrightsyMcpToolName(name) {
1759
- const n = name.toLowerCase();
1760
- return n === "brightsy" || n.startsWith("mcp__brightsy") || n.startsWith("brightsy_");
1833
+ function messagePartParentId(part) {
1834
+ if ("parentId" in part && typeof part.parentId === "string" && part.parentId.trim()) {
1835
+ return part.parentId;
1836
+ }
1837
+ return void 0;
1761
1838
  }
1762
- function toolInputUsesBrightsyDatasource(input) {
1763
- const ds = input?.datasource;
1764
- return typeof ds === "string" && ds.toLowerCase() === "brightsy";
1839
+ function sameParentId(a, b) {
1840
+ return (a ?? "") === (b ?? "");
1765
1841
  }
1766
- function messageUsedBrightsyMcp(msg) {
1767
- if (msg.role === "user" && promptMentionsBrightsy(msg.text)) return true;
1768
- for (const part of msg.parts ?? []) {
1769
- if (part.type !== "tool") continue;
1770
- if (isBrightsyMcpToolName(part.name)) return true;
1771
- if (/present_(schema|files)$/i.test(part.name) && toolInputUsesBrightsyDatasource(part.input)) {
1772
- return true;
1773
- }
1842
+ function withEventParentId(event, parentId) {
1843
+ if (!parentId) return event;
1844
+ if (event.type === "stdout" || event.type === "thinking" || event.type === "tool_use" || event.type === "tool_result") {
1845
+ return { ...event, parentId: event.parentId ?? parentId };
1774
1846
  }
1775
- return false;
1847
+ return event;
1776
1848
  }
1777
- function threadRequestsBrightsyMcp(thread) {
1778
- return (thread?.messages ?? []).some(messageUsedBrightsyMcp);
1849
+ function toolFilePath(input) {
1850
+ if (!input) return void 0;
1851
+ return str(input.file_path) ?? str(input.path) ?? str(input.filePath) ?? str(input.filename);
1779
1852
  }
1780
- function shouldInjectBrightsyMcp(thread, opts) {
1781
- if (!(opts?.connected ?? isBrightsyConnected())) return false;
1782
- if (opts?.orchestrator) return true;
1783
- const alwaysOn = opts?.alwaysOnWorktree ?? Boolean(loadAppSettings().brightsy?.injectWorktreeMcp);
1784
- if (alwaysOn) return true;
1785
- return threadRequestsBrightsyMcp(thread);
1853
+ function countLines(text) {
1854
+ if (!text) return 0;
1855
+ return text.split("\n").length;
1786
1856
  }
1787
- function mcpLaunch(cmd, name, env) {
1788
- if (cmd === "brightsy-mcp") {
1789
- return { name, command: "brightsy-mcp", ...env ? { env } : {} };
1857
+ function diffFromInput(input) {
1858
+ if (!input) return {};
1859
+ const oldS = str(input.old_string) ?? str(input.oldString);
1860
+ const newS = str(input.new_string) ?? str(input.newString) ?? str(input.content);
1861
+ if (oldS != null || newS != null) {
1862
+ return {
1863
+ additions: countLines(newS),
1864
+ deletions: countLines(oldS)
1865
+ };
1790
1866
  }
1791
- return {
1792
- name,
1793
- command: "npx",
1794
- args: ["-y", "@brightsy/mcp-server"],
1795
- ...env ? { env } : {}
1796
- };
1867
+ return {};
1797
1868
  }
1798
- function teamEnv(team) {
1799
- const endpoint = (team.endpoint || "https://brightsy.ai").replace(/\/$/, "");
1800
- return {
1801
- BRIGHTSY_API_TOKEN: team.access_token,
1802
- ...team.refresh_token ? { BRIGHTSY_REFRESH_TOKEN: team.refresh_token } : {},
1803
- BRIGHTSY_ACCOUNT_ID: team.id,
1804
- BRIGHTSY_API_URL: endpoint
1805
- };
1869
+ function parseDiffStat(result) {
1870
+ if (!result) return {};
1871
+ const paired = result.match(/\+(\d+)\s+-(\d+)/);
1872
+ if (paired) {
1873
+ return {
1874
+ additions: Number(paired[1]),
1875
+ deletions: Number(paired[2])
1876
+ };
1877
+ }
1878
+ const verbose = result.match(
1879
+ /(\d+)\s+insertions?(?:,\s*(\d+)\s+deletions?)?/i
1880
+ );
1881
+ if (verbose) {
1882
+ return {
1883
+ additions: Number(verbose[1]),
1884
+ deletions: verbose[2] != null ? Number(verbose[2]) : void 0
1885
+ };
1886
+ }
1887
+ return {};
1888
+ }
1889
+ var THINKING_PART_MAX_CHARS = 64e3;
1890
+ function clipThinkingForStore(text, max = THINKING_PART_MAX_CHARS) {
1891
+ if (text.length <= max) return text;
1892
+ const marker = "\u2026(earlier thinking trimmed)\n";
1893
+ return marker + text.slice(text.length - Math.max(1, max - marker.length));
1806
1894
  }
1807
- function brightsyMcpAllowedTools(serverNames) {
1808
- const out = [];
1809
- for (const name of serverNames) {
1810
- out.push(`mcp__${name}`, `mcp__${name}__*`);
1811
- }
1812
- return out;
1895
+ var STDOUT_EVENT_MAX_CHARS = 16e3;
1896
+ var STDERR_EVENT_MAX_CHARS = 4e3;
1897
+ function clipEventTail(text, max) {
1898
+ if (text.length <= max) return text;
1899
+ const marker = "\u2026(truncated)\n";
1900
+ return marker + text.slice(text.length - Math.max(1, max - marker.length));
1813
1901
  }
1814
- function corePackageDir() {
1815
- const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
1816
- if (cjsDir) return cjsDir;
1817
- try {
1818
- const url = import.meta.url;
1819
- if (typeof url === "string" && url.length > 0) {
1820
- return dirname2(fileURLToPath(url));
1821
- }
1822
- } catch {
1902
+ function clipAgentEventForPaint(event) {
1903
+ if (event.type === "tool_result" && event.content) {
1904
+ const content = clipToolResultForStore(event.content);
1905
+ if (content === event.content) return event;
1906
+ return { ...event, content };
1823
1907
  }
1824
- try {
1825
- const req = createRequire(join4(process.cwd(), "package.json"));
1826
- return dirname2(req.resolve("@sideboard-ai/core"));
1827
- } catch {
1828
- return process.cwd();
1908
+ if (event.type === "thinking" && event.data.length > THINKING_PART_MAX_CHARS) {
1909
+ return { ...event, data: clipThinkingForStore(event.data) };
1910
+ }
1911
+ if (event.type === "stdout" && event.data.length > STDOUT_EVENT_MAX_CHARS) {
1912
+ return { ...event, data: clipEventTail(event.data, STDOUT_EVENT_MAX_CHARS) };
1913
+ }
1914
+ if (event.type === "stderr" && event.data.length > STDERR_EVENT_MAX_CHARS) {
1915
+ return { ...event, data: clipEventTail(event.data, STDERR_EVENT_MAX_CHARS) };
1829
1916
  }
1917
+ return event;
1830
1918
  }
1831
- function findSideboardMcpJsEntry() {
1832
- const override = process.env.SIDEBOARD_MCP_ENTRY?.trim() || process.env.SIDEBOARD_CLI?.trim();
1833
- if (override && existsSync5(override)) return override;
1834
- const packaged = packagedMcpStdioPath();
1835
- if (packaged) return packaged;
1836
- let dir = corePackageDir();
1837
- for (let i = 0; i < 10; i++) {
1838
- const candidates = [
1839
- join4(dir, "mcp/run-stdio.js"),
1840
- join4(dir, "mcp/run-stdio.cjs"),
1841
- join4(dir, "dist/mcp/run-stdio.js"),
1842
- join4(dir, "dist/mcp/run-stdio.cjs"),
1843
- join4(dir, "packages/core/dist/mcp/run-stdio.js"),
1844
- join4(dir, "packages/cli/dist/index.js"),
1845
- join4(dir, "cli/dist/index.js")
1846
- ];
1847
- for (const p of candidates) {
1848
- if (existsSync5(p) && !isAsarPath(p)) return p;
1919
+ function withPartTimes(next, prev) {
1920
+ const now = Date.now();
1921
+ const startedAt = prev && typeof prev.startedAt === "number" ? prev.startedAt : now;
1922
+ return { ...next, startedAt, updatedAt: now };
1923
+ }
1924
+ function applyAgentEvent(parts, event) {
1925
+ if (event.type === "stdout") {
1926
+ const data = event.data;
1927
+ if (!data) return parts;
1928
+ const next = [...parts];
1929
+ const last = next[next.length - 1];
1930
+ if (last?.type === "text" && sameParentId(last.parentId, event.parentId)) {
1931
+ next[next.length - 1] = withPartTimes(
1932
+ {
1933
+ type: "text",
1934
+ text: last.text + data,
1935
+ ...event.parentId ? { parentId: event.parentId } : {}
1936
+ },
1937
+ last
1938
+ );
1939
+ } else {
1940
+ next.push(
1941
+ withPartTimes({
1942
+ type: "text",
1943
+ text: data,
1944
+ ...event.parentId ? { parentId: event.parentId } : {}
1945
+ })
1946
+ );
1849
1947
  }
1850
- const parent = dirname2(dir);
1851
- if (parent === dir) break;
1852
- dir = parent;
1948
+ return next;
1853
1949
  }
1854
- return null;
1855
- }
1856
- async function resolveSideboardMcpServer() {
1857
- const entry = findSideboardMcpJsEntry();
1858
- if (entry) {
1859
- const isCli = /[/\\]cli[/\\]dist[/\\]index\.js$/.test(entry);
1860
- const scriptArgs = isCli ? [entry, "mcp"] : [entry];
1861
- const launch = applyNodeLaunch(await resolveNodeLaunch(entry), scriptArgs);
1862
- if (launch.file !== "/bin/sh" && !isElectronLikeCommand(launch.file)) {
1863
- return {
1864
- name: "sideboard",
1865
- command: launch.file,
1866
- args: launch.args,
1867
- ...Object.keys(launch.env).length > 0 ? { env: launch.env } : {}
1868
- };
1950
+ if (event.type === "thinking") {
1951
+ const data = event.data;
1952
+ if (!data) return parts;
1953
+ const next = [...parts];
1954
+ if (event.replace) {
1955
+ for (let i = next.length - 1; i >= 0; i--) {
1956
+ const prev = next[i];
1957
+ if (prev?.type === "thinking" && sameParentId(prev.parentId, event.parentId)) {
1958
+ next[i] = withPartTimes(
1959
+ {
1960
+ type: "thinking",
1961
+ text: clipThinkingForStore(data),
1962
+ ...event.parentId ? { parentId: event.parentId } : {}
1963
+ },
1964
+ prev
1965
+ );
1966
+ return next;
1967
+ }
1968
+ }
1969
+ next.push(
1970
+ withPartTimes({
1971
+ type: "thinking",
1972
+ text: clipThinkingForStore(data),
1973
+ ...event.parentId ? { parentId: event.parentId } : {}
1974
+ })
1975
+ );
1976
+ return next;
1977
+ }
1978
+ const last = next[next.length - 1];
1979
+ if (last?.type === "thinking" && sameParentId(last.parentId, event.parentId)) {
1980
+ next[next.length - 1] = withPartTimes(
1981
+ {
1982
+ type: "thinking",
1983
+ text: clipThinkingForStore(last.text + data),
1984
+ ...event.parentId ? { parentId: event.parentId } : {}
1985
+ },
1986
+ last
1987
+ );
1988
+ } else {
1989
+ next.push(
1990
+ withPartTimes({
1991
+ type: "thinking",
1992
+ text: clipThinkingForStore(data),
1993
+ ...event.parentId ? { parentId: event.parentId } : {}
1994
+ })
1995
+ );
1869
1996
  }
1997
+ return next;
1870
1998
  }
1871
- const which = await run("which", ["sideboard"], { reject: false });
1872
- if (which.exitCode === 0 && which.stdout.trim()) {
1873
- return { name: "sideboard", command: which.stdout.trim(), args: ["mcp"] };
1999
+ if (event.type === "tool_use") {
2000
+ const input = asRecord(event.input);
2001
+ const diff = diffFromInput(input);
2002
+ const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
2003
+ if (existing >= 0) {
2004
+ const prev = parts[existing];
2005
+ const next = [...parts];
2006
+ const mergedInput = {
2007
+ ...prev.input ?? {},
2008
+ ...input ?? {}
2009
+ };
2010
+ const mergedRecord = Object.keys(mergedInput).length > 0 ? mergedInput : void 0;
2011
+ next[existing] = withPartTimes(
2012
+ {
2013
+ ...prev,
2014
+ name: event.name || prev.name,
2015
+ input: mergedRecord,
2016
+ description: toolDescription(event.name || prev.name, mergedRecord),
2017
+ detail: toolDetail(event.name || prev.name, mergedRecord) ?? prev.detail,
2018
+ filePath: toolFilePath(mergedRecord) ?? prev.filePath,
2019
+ additions: diff.additions ?? prev.additions,
2020
+ deletions: diff.deletions ?? prev.deletions,
2021
+ parentId: event.parentId ?? prev.parentId
2022
+ },
2023
+ prev
2024
+ );
2025
+ return next;
2026
+ }
2027
+ return [
2028
+ ...parts,
2029
+ withPartTimes({
2030
+ type: "tool",
2031
+ id: event.id,
2032
+ name: event.name,
2033
+ input,
2034
+ description: toolDescription(event.name, input),
2035
+ detail: toolDetail(event.name, input),
2036
+ status: "running",
2037
+ filePath: toolFilePath(input),
2038
+ ...event.parentId ? { parentId: event.parentId } : {},
2039
+ ...diff
2040
+ })
2041
+ ];
1874
2042
  }
1875
- return { name: "sideboard", command: "sideboard", args: ["mcp"] };
1876
- }
1877
- async function buildInjectedMcpServers(opts) {
1878
- const servers = [];
1879
- if (opts.includeSideboard) {
1880
- const sideboard = await resolveSideboardMcpServer();
1881
- const orchId = opts.orchestratorThreadId?.trim();
1882
- const profile = orchId ? "orchestration" : "worktree";
1883
- sideboard.env = {
1884
- ...sideboard.env ?? {},
1885
- SIDEBOARD_APP_DATA: appDataDir(),
1886
- [SIDEBOARD_MCP_PROFILE_ENV]: profile
1887
- };
1888
- if (orchId) {
1889
- sideboard.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID = orchId;
2043
+ if (event.type === "tool_result") {
2044
+ const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
2045
+ const fromResult = parseDiffStat(event.content);
2046
+ if (event.partial) {
2047
+ if (existing < 0) {
2048
+ return [
2049
+ ...parts,
2050
+ withPartTimes({
2051
+ type: "tool",
2052
+ id: event.id,
2053
+ name: "tool",
2054
+ description: "tool",
2055
+ status: event.isError ? "error" : "running",
2056
+ result: clipToolResultForStore(event.content),
2057
+ ...event.parentId ? { parentId: event.parentId } : {}
2058
+ })
2059
+ ];
2060
+ }
2061
+ return parts.map((p, i) => {
2062
+ if (i !== existing || p.type !== "tool") return p;
2063
+ if (p.status === "done" || p.status === "error") return p;
2064
+ return withPartTimes(
2065
+ {
2066
+ ...p,
2067
+ result: clipToolResultForStore(event.content) ?? p.result
2068
+ },
2069
+ p
2070
+ );
2071
+ });
2072
+ }
2073
+ if (existing < 0) {
2074
+ return [
2075
+ ...parts,
2076
+ withPartTimes({
2077
+ type: "tool",
2078
+ id: event.id,
2079
+ name: "tool",
2080
+ description: "tool",
2081
+ status: event.isError ? "error" : "done",
2082
+ result: clipToolResultForStore(event.content),
2083
+ ...event.parentId ? { parentId: event.parentId } : {},
2084
+ ...fromResult.additions != null ? { additions: fromResult.additions } : {},
2085
+ ...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
2086
+ })
2087
+ ];
1890
2088
  }
1891
- try {
1892
- mergeAgentGitAuthEnv(
1893
- sideboard.env,
1894
- await resolveAgentGitAuthEnv(sideboard.env)
2089
+ return parts.map((p, i) => {
2090
+ if (i !== existing || p.type !== "tool") return p;
2091
+ return withPartTimes(
2092
+ {
2093
+ ...p,
2094
+ status: event.isError ? "error" : "done",
2095
+ result: clipToolResultForStore(event.content),
2096
+ ...fromResult.additions != null ? { additions: fromResult.additions } : {},
2097
+ ...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
2098
+ },
2099
+ p
1895
2100
  );
1896
- } catch {
1897
- }
1898
- applyAgentRunnerHeapEnv(sideboard.env);
1899
- servers.push(sideboard);
1900
- }
1901
- if (opts.includeBrightsy && isBrightsyConnected()) {
1902
- const cmd = await resolveBrightsyMcpCommand();
1903
- const teams = await ensureConnectedBrightsyTeamTokens();
1904
- if (teams.length > 0) {
1905
- const used = /* @__PURE__ */ new Set();
1906
- for (const team of teams) {
1907
- let name = brightsyMcpServerName(team.slug);
1908
- if (used.has(name)) name = `${name}_${team.id.slice(0, 8)}`;
1909
- used.add(name);
1910
- servers.push(mcpLaunch(cmd, name, teamEnv(team)));
1911
- }
1912
- } else {
1913
- servers.push(mcpLaunch(cmd, "brightsy"));
1914
- }
1915
- }
1916
- return servers;
1917
- }
1918
- function cursorSafeMcpLaunch(command, args) {
1919
- if (process.platform === "win32") {
1920
- return args && args.length > 0 ? { command, args } : { command };
2101
+ });
1921
2102
  }
1922
- const unwrapped = unwrapStrippedElectronLaunch(command, args);
1923
- const file = unwrapped?.file ?? command;
1924
- const fileArgs = unwrapped?.args ?? args ?? [];
1925
- return fileArgs.length > 0 ? { command: file, args: fileArgs } : { command: file };
2103
+ return parts;
1926
2104
  }
1927
- function mcpSpawnEnv(env) {
1928
- if (!env) return void 0;
1929
- const out = { ...env };
1930
- delete out.ELECTRON_RUN_AS_NODE;
1931
- delete out.ELECTRON_RUN_AS_NODE;
1932
- return Object.keys(out).length > 0 ? out : void 0;
2105
+ function partsToAssistantText(parts) {
2106
+ return parts.filter(
2107
+ (p) => p.type === "text" && !messagePartParentId(p)
2108
+ ).map((p) => p.text).join("").trim();
1933
2109
  }
1934
- function toCursorMcpServers(servers) {
1935
- const out = {};
1936
- for (const s of servers) {
1937
- const env = mcpSpawnEnv(s.env);
1938
- const launch = cursorSafeMcpLaunch(s.command, s.args);
1939
- out[s.name] = {
1940
- type: "stdio",
1941
- command: launch.command,
1942
- ...launch.args && launch.args.length > 0 ? { args: launch.args } : {},
1943
- ...env ? { env } : {}
1944
- };
1945
- }
1946
- return out;
2110
+ function stripBrightsyNdjsonNoise(text) {
2111
+ if (!text || !text.includes('"type"')) return text;
2112
+ let out = text;
2113
+ out = out.replace(
2114
+ /\{"type":"(?:tool_use|tool_result|tool|thinking|usage|done|error)"[\s\S]*?\}\s*(?=\{"type":"|$|(?=[A-Za-z*#]))/g,
2115
+ ""
2116
+ );
2117
+ out = out.replace(/\{"type":"(?:tool_use|tool_result|tool)"[\s\S]*$/g, "");
2118
+ return out.trim();
1947
2119
  }
1948
- function toCodexMcpConfigArgs(servers) {
1949
- const args = [];
1950
- for (const s of servers) {
1951
- const prefix = `mcp_servers.${s.name}`;
1952
- args.push("-c", `${prefix}.command=${JSON.stringify(s.command)}`);
1953
- if (s.args?.length) {
1954
- args.push("-c", `${prefix}.args=${JSON.stringify(s.args)}`);
1955
- }
1956
- const env = mcpSpawnEnv(s.env);
1957
- if (env) {
1958
- for (const [key, value] of Object.entries(env)) {
1959
- args.push("-c", `${prefix}.env.${key}=${JSON.stringify(value)}`);
1960
- }
1961
- }
1962
- args.push("-c", `${prefix}.default_tools_approval_mode=${JSON.stringify("approve")}`);
1963
- args.push("-c", `${prefix}.tool_timeout_sec=300`);
1964
- args.push("-c", `${prefix}.startup_timeout_sec=30`);
1965
- }
1966
- return args;
2120
+ function isBrightsyNdjsonLine(line) {
2121
+ const t = line.trim();
2122
+ if (!t.startsWith("{")) return false;
2123
+ return /"type"\s*:\s*"(tool_use|tool_result|tool|thinking|usage|done|error|text)"/.test(t);
1967
2124
  }
1968
- function toOpencodeMcpConfigContent(servers, opts) {
1969
- const mcp = {};
1970
- for (const s of servers) {
1971
- const env = mcpSpawnEnv(s.env);
1972
- mcp[s.name] = {
1973
- type: "local",
1974
- command: [s.command, ...s.args ?? []],
1975
- enabled: true,
1976
- ...env ? { environment: env } : {}
1977
- };
1978
- }
1979
- for (const name of opts?.disableNames ?? []) {
1980
- const key = name.trim();
1981
- if (!key || mcp[key]?.enabled === true) continue;
1982
- mcp[key] = { ...mcp[key], enabled: false };
1983
- }
1984
- return JSON.stringify({ mcp });
2125
+ function finalizeParts(parts) {
2126
+ return parts.map(
2127
+ (p) => p.type === "tool" && p.status === "running" ? { ...p, status: "done" } : p
2128
+ );
1985
2129
  }
1986
- function writeMcpServersConfig(servers) {
1987
- if (servers.length === 0) return null;
1988
- const mcpServers = {};
1989
- for (const s of servers) {
1990
- const env = mcpSpawnEnv(s.env);
1991
- mcpServers[s.name] = {
1992
- command: s.command,
1993
- ...s.args ? { args: s.args } : {},
1994
- ...env ? { env } : {}
1995
- };
1996
- }
1997
- const dir = mkdtempSync(join4(tmpdir(), "sideboard-mcp-"));
1998
- const cfgPath = join4(dir, "mcp.json");
1999
- writeFileSync2(cfgPath, JSON.stringify({ mcpServers }, null, 2));
2000
- return cfgPath;
2130
+ function normalizeParseResult(parsed) {
2131
+ if (!parsed) return [];
2132
+ return Array.isArray(parsed) ? parsed : [parsed];
2001
2133
  }
2002
2134
 
2003
2135
  // src/agents/types.ts
@@ -2061,24 +2193,15 @@ var BASE_ALLOWED_TOOLS = [
2061
2193
  "Skill"
2062
2194
  ];
2063
2195
  var CLAUDE_PRINT_BG_WAIT_CEILING_MS = 72e5;
2196
+ function isLocalDevAppDataDir(dir = process.env.SIDEBOARD_APP_DATA) {
2197
+ return /[/\\]\.sideboard[/\\]dev-app-data(?:[/\\]|$)/.test(dir ?? "");
2198
+ }
2064
2199
  var CLAUDE_CHROME_ALLOWED_TOOLS = [
2065
2200
  "mcp__claude-in-chrome",
2066
2201
  "mcp__claude-in-chrome__*",
2067
2202
  "Skill(claude-in-chrome)"
2068
2203
  ];
2069
2204
  var CLAUDE_PROMPT_ARG_MAX = 2e5;
2070
- var mcpListCache = null;
2071
- async function loadMcpServers() {
2072
- if (mcpListCache && Date.now() - mcpListCache.at < 3e4) {
2073
- return mcpListCache.servers;
2074
- }
2075
- const claude = resolveClaudeExecutable();
2076
- const mcpText = await run(claude, ["mcp", "list"], { reject: false });
2077
- const servers = parseMcpList(`${mcpText.stdout}
2078
- ${mcpText.stderr}`);
2079
- mcpListCache = { at: Date.now(), servers };
2080
- return servers;
2081
- }
2082
2205
  function usageFromClaude(usage) {
2083
2206
  if (!usage) return null;
2084
2207
  const inputTokens = Number(usage.input_tokens ?? 0);
@@ -2365,7 +2488,7 @@ var claudeAdapter = {
2365
2488
  async detect() {
2366
2489
  const claude = resolveClaudeExecutable();
2367
2490
  if (claude !== "claude") {
2368
- if (!existsSync6(claude)) {
2491
+ if (!existsSync8(claude)) {
2369
2492
  return {
2370
2493
  agent: "claude",
2371
2494
  installed: false,
@@ -2414,7 +2537,7 @@ var claudeAdapter = {
2414
2537
  );
2415
2538
  }
2416
2539
  const mode = permissionMode(thread);
2417
- const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-KT3FRHZL.js");
2540
+ const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-QPSJEXGR.js");
2418
2541
  const isOrchestrator = isOrchestratorThread2(thread);
2419
2542
  const injectedServers = await buildInjectedMcpServers({
2420
2543
  includeSideboard: true,
@@ -2425,6 +2548,7 @@ var claudeAdapter = {
2425
2548
  });
2426
2549
  const injectedBrightsyNames = injectedServers.filter((s) => s.name === "brightsy" || s.name.startsWith("brightsy_")).map((s) => s.name);
2427
2550
  const chromeOn = claudeChromeEnabled();
2551
+ const userMcpEntries = isOrchestrator ? {} : listUserClaudeMcpServerEntries();
2428
2552
  let allowedTools;
2429
2553
  if (isOrchestrator) {
2430
2554
  allowedTools = [
@@ -2433,10 +2557,9 @@ var claudeAdapter = {
2433
2557
  ...brightsyMcpAllowedTools(injectedBrightsyNames)
2434
2558
  ];
2435
2559
  } else {
2436
- const servers = await loadMcpServers();
2437
2560
  allowedTools = [
2438
2561
  ...BASE_ALLOWED_TOOLS,
2439
- ...mcpAllowTools(servers),
2562
+ ...mcpAllowToolsFromNames(Object.keys(userMcpEntries)),
2440
2563
  ...sideboardWorktreeAllowedTools({
2441
2564
  github: true,
2442
2565
  linear: isLinearConnected(),
@@ -2461,12 +2584,10 @@ var claudeAdapter = {
2461
2584
  if (systemPrompt) {
2462
2585
  args.push("--append-system-prompt", systemPrompt);
2463
2586
  }
2464
- const mcpConfigPath = writeMcpServersConfig(injectedServers);
2587
+ const mcpConfigPath = writeMcpServersConfig(injectedServers, userMcpEntries);
2465
2588
  if (mcpConfigPath) {
2466
2589
  args.push("--mcp-config", mcpConfigPath);
2467
- if (isOrchestrator) {
2468
- args.push("--strict-mcp-config");
2469
- }
2590
+ args.push("--strict-mcp-config");
2470
2591
  }
2471
2592
  if (chromeOn) {
2472
2593
  args.push("--chrome");
@@ -2498,8 +2619,10 @@ var claudeAdapter = {
2498
2619
  CLAUDE_CODE_FORWARD_SUBAGENT_TEXT: "1",
2499
2620
  CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS: process.env.CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS ?? String(CLAUDE_PRINT_BG_WAIT_CEILING_MS),
2500
2621
  // `--strict-mcp-config` still fetches claude.ai connectors on some CLI
2501
- // builds (anthropics/claude-code#60252). Coordinators do not need them.
2502
- ...isOrchestrator ? { ENABLE_CLAUDEAI_MCP_SERVERS: "false" } : {}
2622
+ // builds (anthropics/claude-code#60252). Coordinators never need them.
2623
+ // Local `pnpm dev` also disables them: those HTTP connectors hang the
2624
+ // first worktree turn when Electron's cwd is this Sideboard repo.
2625
+ ...isOrchestrator || isLocalDevAppDataDir() ? { ENABLE_CLAUDEAI_MCP_SERVERS: "false" } : {}
2503
2626
  }
2504
2627
  };
2505
2628
  },
@@ -2650,115 +2773,6 @@ function parseIssuesJson(raw) {
2650
2773
  import { existsSync as existsSync9, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
2651
2774
  import { homedir as homedir4 } from "os";
2652
2775
  import { join as join7 } from "path";
2653
-
2654
- // src/agents/orch-mcp-isolation.ts
2655
- import { existsSync as existsSync8, readFileSync as readFileSync3 } from "fs";
2656
- import { homedir as homedir3 } from "os";
2657
- import { join as join6 } from "path";
2658
- import { parse as parseToml } from "smol-toml";
2659
-
2660
- // src/agents/user-mcp-config.ts
2661
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
2662
- import { homedir as homedir2 } from "os";
2663
- import { dirname as dirname3, join as join5 } from "path";
2664
-
2665
- // src/agents/orch-mcp-isolation.ts
2666
- function isInjectedOrchMcpName(name) {
2667
- const n = name.trim().toLowerCase();
2668
- return n === "sideboard" || n === "brightsy" || n.startsWith("brightsy_");
2669
- }
2670
- function asObject(value) {
2671
- if (!value || typeof value !== "object" || Array.isArray(value)) return {};
2672
- return value;
2673
- }
2674
- function listMcpNamesFromJsonMap(raw, key) {
2675
- return Object.keys(asObject(asObject(raw)[key])).filter((n) => n.trim());
2676
- }
2677
- function listMcpNamesFromCodexToml(text) {
2678
- try {
2679
- const parsed = parseToml(text);
2680
- return Object.keys(asObject(parsed.mcp_servers)).filter((n) => n.trim());
2681
- } catch {
2682
- return [];
2683
- }
2684
- }
2685
- function readJsonObject(path) {
2686
- try {
2687
- return JSON.parse(readFileSync3(path, "utf8"));
2688
- } catch {
2689
- return {};
2690
- }
2691
- }
2692
- function readText(path) {
2693
- try {
2694
- return readFileSync3(path, "utf8");
2695
- } catch {
2696
- return "";
2697
- }
2698
- }
2699
- function userCodexConfigPaths(home = homedir3()) {
2700
- return [join6(home, ".codex", "config.toml"), join6(home, ".config", "codex", "config.toml")];
2701
- }
2702
- function userOpencodeConfigPaths(home = homedir3()) {
2703
- return [
2704
- join6(home, ".config", "opencode", "opencode.json"),
2705
- join6(home, ".config", "opencode", "opencode.jsonc")
2706
- ];
2707
- }
2708
- function userMcpNamesToDisable(opts) {
2709
- const keep = new Set(
2710
- [...opts.injectedNames ?? []].map((n) => n.trim().toLowerCase()).filter(Boolean)
2711
- );
2712
- const out = [];
2713
- const seen = /* @__PURE__ */ new Set();
2714
- for (const raw of opts.names) {
2715
- const name = raw.trim();
2716
- if (!name) continue;
2717
- const key = name.toLowerCase();
2718
- if (seen.has(key) || keep.has(key) || isInjectedOrchMcpName(name)) continue;
2719
- seen.add(key);
2720
- out.push(name);
2721
- }
2722
- return out;
2723
- }
2724
- function listUserCodexMcpNames(home = homedir3()) {
2725
- const names = [];
2726
- for (const path of userCodexConfigPaths(home)) {
2727
- if (!existsSync8(path)) continue;
2728
- names.push(...listMcpNamesFromCodexToml(readText(path)));
2729
- }
2730
- return names;
2731
- }
2732
- function listUserOpencodeMcpNames(home = homedir3()) {
2733
- const names = [];
2734
- for (const path of userOpencodeConfigPaths(home)) {
2735
- if (!existsSync8(path)) continue;
2736
- names.push(...listMcpNamesFromJsonMap(readJsonObject(path), "mcp"));
2737
- }
2738
- return names;
2739
- }
2740
- function toCodexDisableUserMcpArgs(names) {
2741
- const args = [];
2742
- for (const name of names) {
2743
- if (!/^[A-Za-z0-9_-]+$/.test(name)) continue;
2744
- args.push("-c", `mcp_servers.${name}.enabled=false`);
2745
- }
2746
- return args;
2747
- }
2748
- function toCodexUnattendedAppsArgs() {
2749
- return [
2750
- "-c",
2751
- "features.apps=false",
2752
- "-c",
2753
- "apps._default.enabled=false",
2754
- "-c",
2755
- "apps.posthog.enabled=false",
2756
- "-c",
2757
- "features.plugins=false"
2758
- ];
2759
- }
2760
-
2761
- // src/agents/codex.ts
2762
2776
  var CODEX_PROMPT_ARG_MAX = 2e5;
2763
2777
  var FALLBACK_CODEX_MODELS = [
2764
2778
  { id: "gpt-5.6-sol", displayName: "GPT-5.6 Sol" },
@@ -4755,6 +4769,9 @@ export {
4755
4769
  adjustBrightsyTurnExit,
4756
4770
  listBrightsyChatTargets,
4757
4771
  brightsyAdapter,
4772
+ sideboardMcpProfile,
4773
+ packagedMcpDir,
4774
+ applyAgentRunnerHeapEnv,
4758
4775
  toolDescription,
4759
4776
  isInternalAgentStatusText,
4760
4777
  isPollWrapperToolName,
@@ -4766,9 +4783,6 @@ export {
4766
4783
  isBrightsyNdjsonLine,
4767
4784
  finalizeParts,
4768
4785
  normalizeParseResult,
4769
- sideboardMcpProfile,
4770
- packagedMcpDir,
4771
- applyAgentRunnerHeapEnv,
4772
4786
  PLAN_MODE_INSTRUCTION,
4773
4787
  permissionMode,
4774
4788
  mcpStatusThinkingFromClaudeInit,