@sideboard-ai/core 0.1.216 → 0.1.218

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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-WBDDLRBY.js → agents-NWJ7DFJD.js} +8 -7
  9. package/dist/{agents-HETHRRZJ.js → agents-YABJUV3V.js} +9 -9
  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-7MTVRNP4.js → chunk-FSIJGLSV.js} +1173 -1186
  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-SALM35KD.js → chunk-IILG7DPX.js} +5 -5
  20. package/dist/{chunk-D2IKXMF3.js → chunk-JWBPSD7Q.js} +453 -289
  21. package/dist/{chunk-NI243N27.js → chunk-KTZESHYH.js} +15 -7
  22. package/dist/{chunk-KFD2OXCE.js → chunk-MW57SKKR.js} +19 -6
  23. package/dist/{chunk-XYKVRRC2.js → chunk-NFWPDEN4.js} +15 -7
  24. package/dist/{chunk-FNGXKY5W.js → chunk-OPYBLX4V.js} +19 -6
  25. package/dist/{chunk-QQKDGPUI.js → chunk-PO4M4JAJ.js} +1 -1
  26. package/dist/{chunk-2M5B7YNY.js → chunk-RRW2XWAY.js} +2 -2
  27. package/dist/{chunk-REZRGCKV.js → chunk-SOGGKYFV.js} +3 -3
  28. package/dist/{chunk-C32SBNA3.js → chunk-SRFFIFW6.js} +457 -294
  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-DFTVZEZY.js → chunk-XRHTRXZN.js} +1051 -1050
  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-5V7GEPBG.js} +6 -6
  39. package/dist/{coordinator-prompt-LE2GQZ2X.js → coordinator-prompt-J4QSNTKE.js} +6 -5
  40. package/dist/{global-workspace-6P236KKG.js → global-workspace-4TEMLESD.js} +6 -6
  41. package/dist/{global-workspace-KT3FRHZL.js → global-workspace-XPTFHMKL.js} +6 -5
  42. package/dist/index.cjs +1027 -821
  43. package/dist/index.d.cts +10 -1
  44. package/dist/index.d.ts +10 -1
  45. package/dist/index.js +22 -18
  46. package/dist/mcp/run-stdio.cjs +1838 -1651
  47. package/dist/mcp/run-stdio.js +16 -14
  48. package/dist/{orchestrator-WVZPTHBV.js → orchestrator-HKW3VLGX.js} +10 -10
  49. package/dist/{orchestrator-MI7A5DQZ.js → orchestrator-SWRTYFHX.js} +9 -8
  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-KM7KTLVO.js → workspaces-2HQP654Q.js} +6 -5
  53. package/dist/{workspaces-R5LJCJRB.js → workspaces-BCBBMMXB.js} +6 -6
  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-MW57SKKR.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,1291 @@ 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;
1362
1247
  }
1363
- function readExistingFile(path) {
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() {
1364
1260
  try {
1365
- return existsSync2(path) ? readFileSync(path, "utf8") : "";
1261
+ loadBrightsyConfig();
1262
+ return true;
1366
1263
  } catch {
1367
- return "";
1264
+ return false;
1368
1265
  }
1369
1266
  }
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 : "";
1267
+ var BRIGHTSY_WORD = /(?<![a-z0-9_-])brightsy(?![a-z0-9_-])/i;
1268
+ function promptMentionsBrightsy(text) {
1269
+ return BRIGHTSY_WORD.test(text ?? "");
1378
1270
  }
1379
- function linuxSystemCaPem() {
1380
- for (const path of LINUX_CA_FILES) {
1381
- const pem = readExistingFile(path);
1382
- if (splitPemCertificates(pem).length > 0) return pem;
1383
- }
1384
- return "";
1271
+ function isBrightsyMcpToolName(name) {
1272
+ const n = name.toLowerCase();
1273
+ return n === "brightsy" || n.startsWith("mcp__brightsy") || n.startsWith("brightsy_");
1385
1274
  }
1386
- function collectSystemCaPem(io = {}) {
1387
- const platform = io.platform ?? process.platform;
1388
- if (platform === "darwin") {
1389
- return darwinKeychainPem(io.spawn ?? spawnSync);
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
+ }
1390
1287
  }
1391
- if (platform === "linux") {
1392
- return linuxSystemCaPem();
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 } : {} };
1393
1303
  }
1394
- return "";
1304
+ return {
1305
+ name,
1306
+ command: "npx",
1307
+ args: ["-y", "@brightsy/mcp-server"],
1308
+ ...env ? { env } : {}
1309
+ };
1395
1310
  }
1396
- function systemCaBundlePath(io = {}) {
1397
- return join((io.appData ?? appDataDir)(), BUNDLE_NAME);
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
+ };
1398
1319
  }
1399
- function bundleIsFresh(path, now) {
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;
1400
1330
  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;
1331
+ const url = import.meta.url;
1332
+ if (typeof url === "string" && url.length > 0) {
1333
+ return dirname2(fileURLToPath(url));
1334
+ }
1404
1335
  } catch {
1405
- return false;
1406
1336
  }
1407
- }
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;
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"] };
1412
1387
  }
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;
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;
1417
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);
1418
1413
  }
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
- }
1426
- try {
1427
- mkdirSync(dirname(dest), { recursive: true });
1428
- writeFileSync(dest, pem.endsWith("\n") ? pem : `${pem}
1429
- `, { mode: 384 });
1430
- return dest;
1431
- } catch {
1432
- return void 0;
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
+ }
1433
1428
  }
1429
+ return servers;
1434
1430
  }
1435
- function applySystemCaEnv(env) {
1436
- if (!String(env[NODE_USE_SYSTEM_CA] ?? "").trim()) {
1437
- env[NODE_USE_SYSTEM_CA] = "1";
1431
+ function cursorSafeMcpLaunch(command, args) {
1432
+ if (process.platform === "win32") {
1433
+ return args && args.length > 0 ? { command, args } : { command };
1438
1434
  }
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;
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 };
1442
1439
  }
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));
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;
1448
1446
  }
1449
- function trustSystemCertificates(io = {}) {
1450
- try {
1451
- const api = tls;
1452
- if (typeof api.getCACertificates !== "function" || typeof api.setDefaultCACertificates !== "function") {
1453
- return false;
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
+ };
1458
+ }
1459
+ return out;
1460
+ }
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)}`);
1454
1468
  }
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));
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
+ }
1460
1474
  }
1461
- if (system.length === 0 && extra.length === 0) return false;
1462
- api.setDefaultCACertificates([...current, ...system, ...extra]);
1463
- return true;
1464
- } catch {
1465
- return false;
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`);
1466
1478
  }
1479
+ return args;
1467
1480
  }
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;
1494
- }
1495
- function packagedMcpStdioPath() {
1496
- const dir = packagedMcpDir();
1497
- return dir ? join2(dir, "core-dist", "mcp", "run-stdio.js") : null;
1498
- }
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;
1505
- }
1506
- function packagedCursorRipgrepCandidate(platformPkg, binName) {
1507
- const dir = packagedCursorRuntimeDir();
1508
- if (!dir) return null;
1509
- return join2(dir, "node_modules", platformPkg, "bin", binName);
1510
- }
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();
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
+ };
1522
1491
  }
1523
- return existing ? `${existing} --max-old-space-size=${heapMb}` : `--max-old-space-size=${heapMb}`;
1524
- }
1525
- function applyAgentRunnerHeapEnv(env) {
1526
- env.NODE_OPTIONS = withMaxOldSpaceSize(
1527
- env.NODE_OPTIONS,
1528
- AGENT_RUNNER_MAX_OLD_SPACE_MB
1529
- );
1530
- applySystemCaEnv(env);
1531
- }
1532
- function envWithAgentHeap(env) {
1533
- const next = { ...env };
1534
- applyAgentRunnerHeapEnv(next);
1535
- return next;
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 });
1536
1498
  }
1537
- function isAsarPath(filePath) {
1538
- if (/\.asar\.unpacked([/\\]|$)/.test(filePath)) return false;
1539
- return /\.asar([/\\]|$)/.test(filePath);
1499
+ function writeMcpServersConfig(servers) {
1500
+ if (servers.length === 0) return null;
1501
+ const mcpServers = {};
1502
+ for (const s of servers) {
1503
+ const env = mcpSpawnEnv(s.env);
1504
+ mcpServers[s.name] = {
1505
+ command: s.command,
1506
+ ...s.args ? { args: s.args } : {},
1507
+ ...env ? { env } : {}
1508
+ };
1509
+ }
1510
+ const dir = mkdtempSync(join4(tmpdir(), "sideboard-mcp-"));
1511
+ const cfgPath = join4(dir, "mcp.json");
1512
+ writeFileSync2(cfgPath, JSON.stringify({ mcpServers }, null, 2));
1513
+ return cfgPath;
1540
1514
  }
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;
1515
+
1516
+ // src/agents/user-mcp-config.ts
1517
+ function userClaudeMcpConfigPath() {
1518
+ return join5(homedir2(), ".claude.json");
1546
1519
  }
1547
- function nodeReadableScriptPath(scriptPath) {
1548
- return unpackedAsarPath(scriptPath) ?? scriptPath;
1520
+
1521
+ // src/agents/orch-mcp-isolation.ts
1522
+ function isInjectedOrchMcpName(name) {
1523
+ const n = name.trim().toLowerCase();
1524
+ return n === "sideboard" || n === "brightsy" || n.startsWith("brightsy_");
1549
1525
  }
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;
1526
+ function asObject(value) {
1527
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
1528
+ return value;
1556
1529
  }
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;
1530
+ function listMcpNamesFromJsonMap(raw, key) {
1531
+ return Object.keys(asObject(asObject(raw)[key])).filter((n) => n.trim());
1532
+ }
1533
+ function listMcpNamesFromCodexToml(text) {
1534
+ try {
1535
+ const parsed = parseToml(text);
1536
+ return Object.keys(asObject(parsed.mcp_servers)).filter((n) => n.trim());
1537
+ } catch {
1538
+ return [];
1570
1539
  }
1571
- return score;
1572
1540
  }
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
- }
1541
+ function readJsonObject(path) {
1542
+ try {
1543
+ return JSON.parse(readFileSync3(path, "utf8"));
1544
+ } catch {
1545
+ return {};
1582
1546
  }
1583
- return best;
1584
1547
  }
1585
- function versionDirNodeBins(root, toBin) {
1586
- if (!existsSync4(root)) return [];
1548
+ function readText(path) {
1587
1549
  try {
1588
- return readdirSync(root).map(toBin);
1550
+ return readFileSync3(path, "utf8");
1589
1551
  } catch {
1590
- return [];
1552
+ return "";
1591
1553
  }
1592
1554
  }
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
- );
1555
+ function userCodexConfigPaths(home = homedir3()) {
1556
+ return [join6(home, ".codex", "config.toml"), join6(home, ".config", "codex", "config.toml")];
1557
+ }
1558
+ function userOpencodeConfigPaths(home = homedir3()) {
1597
1559
  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
- )
1560
+ join6(home, ".config", "opencode", "opencode.json"),
1561
+ join6(home, ".config", "opencode", "opencode.jsonc")
1618
1562
  ];
1619
1563
  }
1620
- function uniqueExistingNodeBins(paths) {
1621
- const seen = /* @__PURE__ */ new Set();
1564
+ function userMcpNamesToDisable(opts) {
1565
+ const keep = new Set(
1566
+ [...opts.injectedNames ?? []].map((n) => n.trim().toLowerCase()).filter(Boolean)
1567
+ );
1622
1568
  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;
1631
- }
1632
- if (seen.has(key)) continue;
1569
+ const seen = /* @__PURE__ */ new Set();
1570
+ for (const raw of opts.names) {
1571
+ const name = raw.trim();
1572
+ if (!name) continue;
1573
+ const key = name.toLowerCase();
1574
+ if (seen.has(key) || keep.has(key) || isInjectedOrchMcpName(name)) continue;
1633
1575
  seen.add(key);
1634
- out.push(p);
1576
+ out.push(name);
1635
1577
  }
1636
1578
  return out;
1637
1579
  }
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);
1580
+ function listUserCodexMcpNames(home = homedir3()) {
1581
+ const names = [];
1582
+ for (const path of userCodexConfigPaths(home)) {
1583
+ if (!existsSync7(path)) continue;
1584
+ names.push(...listMcpNamesFromCodexToml(readText(path)));
1643
1585
  }
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);
1586
+ return names;
1587
+ }
1588
+ function listUserOpencodeMcpNames(home = homedir3()) {
1589
+ const names = [];
1590
+ for (const path of userOpencodeConfigPaths(home)) {
1591
+ if (!existsSync7(path)) continue;
1592
+ names.push(...listMcpNamesFromJsonMap(readJsonObject(path), "mcp"));
1593
+ }
1594
+ return names;
1595
+ }
1596
+ function listUserClaudeMcpNames(home = homedir3()) {
1597
+ const claudePath = home === homedir3() ? userClaudeMcpConfigPath() : join6(home, ".claude.json");
1598
+ if (!existsSync7(claudePath)) return [];
1599
+ return listMcpNamesFromJsonMap(readJsonObject(claudePath), "mcpServers");
1600
+ }
1601
+ function toCodexDisableUserMcpArgs(names) {
1602
+ const args = [];
1603
+ for (const name of names) {
1604
+ if (!/^[A-Za-z0-9_-]+$/.test(name)) continue;
1605
+ args.push("-c", `mcp_servers.${name}.enabled=false`);
1606
+ }
1607
+ return args;
1608
+ }
1609
+ function toCodexUnattendedAppsArgs() {
1610
+ return [
1611
+ "-c",
1612
+ "features.apps=false",
1613
+ "-c",
1614
+ "apps._default.enabled=false",
1615
+ "-c",
1616
+ "apps.posthog.enabled=false",
1617
+ "-c",
1618
+ "features.plugins=false"
1619
+ ];
1620
+ }
1621
+
1622
+ // src/agents/message-parts.ts
1623
+ function asRecord(input) {
1624
+ if (input && typeof input === "object" && !Array.isArray(input)) {
1625
+ return input;
1626
+ }
1627
+ return void 0;
1628
+ }
1629
+ function str(v) {
1630
+ return typeof v === "string" && v.trim() ? v : void 0;
1631
+ }
1632
+ var CHAT_DETAIL_MAX = 72;
1633
+ function clipChatLine(value, max = CHAT_DETAIL_MAX) {
1634
+ const line = value.trim().split(/\r?\n/, 1)[0] ?? "";
1635
+ if (line.length <= max) return line;
1636
+ return `${line.slice(0, Math.max(1, max - 1))}\u2026`;
1637
+ }
1638
+ function toolDetail(name, input) {
1639
+ if (!input) return void 0;
1640
+ const command = str(input.command) ?? str(input.cmd);
1641
+ if (command) return clipChatLine(command);
1642
+ const pattern = str(input.pattern) ?? str(input.glob) ?? str(input.glob_pattern);
1643
+ const isSearch = /grep|glob|search|ripgrep|findfiles|semsearch/i.test(name);
1644
+ if (isSearch && pattern) {
1645
+ return pattern.length > 80 ? `${pattern.slice(0, 77)}\u2026` : pattern;
1646
+ }
1647
+ const path = str(input.file_path) ?? str(input.path) ?? str(input.filePath) ?? str(input.filename);
1648
+ if (path) return path;
1649
+ if (pattern) return pattern.length > 80 ? `${pattern.slice(0, 77)}\u2026` : pattern;
1650
+ const query = str(input.query) ?? str(input.prompt);
1651
+ if (query) return query.length > 80 ? `${query.slice(0, 77)}\u2026` : query;
1652
+ try {
1653
+ const raw = JSON.stringify(input);
1654
+ return raw.length > 80 ? `${raw.slice(0, 77)}\u2026` : raw;
1655
+ } catch {
1656
+ return name;
1657
+ }
1658
+ }
1659
+ function toolDescription(name, input) {
1660
+ const n = name.replace(/^mcp__/, "").replace(/__/g, " \xB7 ");
1661
+ if (/^get_record_types$|recordTypes/i.test(name)) return "List record types";
1662
+ if (/connectedAgentRequest/i.test(name)) {
1663
+ return str(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
1664
+ }
1665
+ if (/present_artifact$/i.test(name)) {
1666
+ return str(input?.title) ? `Present ${str(input?.title)}` : "Present artifact";
1667
+ }
1668
+ if (/present_plan$/i.test(name)) {
1669
+ return str(input?.title) ? `Plan ${str(input?.title)}` : "Present plan";
1670
+ }
1671
+ if (/present_schema$/i.test(name)) {
1672
+ return str(input?.title) ? `Schema ${str(input?.title)}` : "Present schema";
1673
+ }
1674
+ if (/present_files$/i.test(name)) {
1675
+ return str(input?.title) ? `Files ${str(input?.title)}` : "Present files";
1676
+ }
1677
+ if (/wait_for_job$/i.test(name)) {
1678
+ return str(input?.id) ? `Wait for ${str(input?.id)}` : "Wait for job";
1679
+ }
1680
+ if (/stop_job$/i.test(name)) {
1681
+ return str(input?.id) ? `Stop ${str(input?.id)}` : "Stop job";
1682
+ }
1683
+ if (isSubagentToolName(name)) {
1684
+ const desc = str(input?.description);
1685
+ const sub = asRecord(input?.subagentType);
1686
+ const kind = str(sub?.name) ?? str(sub?.kind) ?? str(input?.subagent_type) ?? str(input?.subagentType);
1687
+ if (/^spawn_agent$/i.test(name)) {
1688
+ return str(input?.prompt) ?? desc ?? "Subagent";
1689
+ }
1690
+ if (/connectedAgentRequest/i.test(name)) {
1691
+ return str(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
1692
+ }
1693
+ if (desc && kind) return `${kind}: ${desc}${subagentLiveSuffix(input)}`;
1694
+ if (desc) return `${desc}${subagentLiveSuffix(input)}`;
1695
+ return `Subagent${subagentLiveSuffix(input)}`;
1696
+ }
1697
+ if (/^(create|update)_artifact$/i.test(name.replace(/^mcp__[^_]+__/, ""))) {
1698
+ return str(input?.title) ? `Artifact ${str(input?.title)}` : "Artifact";
1699
+ }
1700
+ if (!input) return n;
1701
+ if (/bash|shell|terminal/i.test(name) && str(input.command)) {
1702
+ const cmd = str(input.command);
1703
+ if (/git\s+fetch/i.test(cmd)) return "Fetch latest from origin and check status";
1704
+ if (/git\s+status/i.test(cmd)) return "Check git status";
1705
+ if (/git\s+log/i.test(cmd)) return "Inspect recent commits";
1706
+ if (/git\s+branch/i.test(cmd)) return "List branches";
1707
+ if (/grep|rg\b/i.test(cmd)) return "Search repository";
1708
+ return "Run shell command";
1709
+ }
1710
+ if (/edit|write|apply/i.test(name)) {
1711
+ const path = str(input.file_path) ?? str(input.path);
1712
+ return path ? `Edit ${path.split("/").pop()}` : "Edit file";
1713
+ }
1714
+ if (/read/i.test(name)) {
1715
+ const path = str(input.file_path) ?? str(input.path);
1716
+ return path ? `Read ${path.split("/").pop()}` : "Read file";
1717
+ }
1718
+ if (/grep/i.test(name)) return "Search files";
1719
+ if (/glob/i.test(name)) return "Find files";
1720
+ const compact = n.replace(/[_-]/g, "");
1721
+ if (/^taskoutput$/i.test(compact)) {
1722
+ return str(input.task_id) ? `Wait for ${str(input.task_id)}` : "Wait for background task";
1723
+ }
1724
+ if (/^taskstop$/i.test(compact)) {
1725
+ return str(input.task_id) ? `Stop ${str(input.task_id)}` : "Stop background task";
1726
+ }
1727
+ if (/^monitor$/i.test(compact)) {
1728
+ const command = str(input.command);
1729
+ return command ? `Watch ${command.length > 48 ? `${command.slice(0, 45)}\u2026` : command}` : "Watch command";
1730
+ }
1731
+ return n;
1732
+ }
1733
+ function subagentLiveSuffix(input) {
1734
+ if (!input) return "";
1735
+ const bits = [];
1736
+ const status = str(input.live_status);
1737
+ if (status && !/^runn(ing)?$|^working$/i.test(status)) bits.push(status);
1738
+ if (typeof input.live_tool_uses === "number") bits.push(`${input.live_tool_uses} tools`);
1739
+ if (typeof input.live_duration_ms === "number") {
1740
+ bits.push(`${Math.max(0, Math.round(Number(input.live_duration_ms) / 1e3))}s`);
1647
1741
  }
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()];
1742
+ const last = str(input.live_last_tool);
1743
+ if (last) bits.push(last);
1744
+ return bits.length ? ` \xB7 ${bits.join(" \xB7 ")}` : "";
1651
1745
  }
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;
1746
+ function isSubagentToolName(name) {
1747
+ const n = (name ?? "").trim();
1748
+ if (/^(task|agent|spawn_agent)$/i.test(n)) return true;
1749
+ return /connectedAgentRequest/i.test(n);
1657
1750
  }
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));
1751
+ function isInternalAgentStatusText(text) {
1752
+ const t = text.trim();
1753
+ if (!t) return false;
1754
+ if (/waiting for gate to pass/i.test(t)) return true;
1755
+ if (/^agent is running\b/i.test(t) && t.length < 160) return true;
1756
+ return false;
1669
1757
  }
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 };
1758
+ function isPollWrapperToolName(name) {
1759
+ const n = (name ?? "").replace(/[_-]/g, "");
1760
+ return /^(taskoutput|taskstop|sleep)$/i.test(n);
1690
1761
  }
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
- }
1762
+ function lastTextPart(parts, type) {
1763
+ for (let i = parts.length - 1; i >= 0; i--) {
1764
+ const p = parts[i];
1765
+ if (p.type !== type) continue;
1766
+ const text = p.text.trim();
1767
+ if (!text || isInternalAgentStatusText(text)) continue;
1768
+ return text;
1702
1769
  }
1703
- return {
1704
- file: process.execPath,
1705
- env: { ELECTRON_RUN_AS_NODE: "1" }
1706
- };
1770
+ return "";
1707
1771
  }
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;
1772
+ function toolLabel(tool) {
1773
+ return tool.description || toolDescription(tool.name, tool.input) || tool.name;
1734
1774
  }
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;
1775
+ function liveActivitySummary(parts, opts) {
1776
+ if (opts?.queued && parts.length === 0) {
1777
+ return "Queued \u2014 waiting for a slot";
1740
1778
  }
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;
1779
+ const tools = parts.filter((p) => p.type === "tool");
1780
+ const runningSubs = tools.filter(
1781
+ (t) => t.status === "running" && !t.parentId && isSubagentToolName(t.name)
1782
+ );
1783
+ const runningNested = [...tools].reverse().find((t) => t.status === "running" && t.parentId && !isPollWrapperToolName(t.name));
1784
+ const runningTop = [...tools].reverse().find(
1785
+ (t) => t.status === "running" && !t.parentId && !isPollWrapperToolName(t.name) && !isSubagentToolName(t.name)
1786
+ );
1787
+ const runningPoll = [...tools].reverse().find((t) => t.status === "running" && isPollWrapperToolName(t.name));
1788
+ const thinking = lastTextPart(parts, "thinking");
1789
+ const text = lastTextPart(parts, "text");
1790
+ if (runningSubs.length > 0) {
1791
+ const heads = runningSubs.map(toolLabel);
1792
+ const head = runningSubs.length === 1 ? heads[0] : `${runningSubs.length} subagents \xB7 ${heads.slice(0, 2).join(" \xB7 ")}`;
1793
+ if (runningNested) return `${head} \xB7 ${toolLabel(runningNested)}`;
1794
+ return head;
1752
1795
  }
1796
+ if (runningTop) return toolLabel(runningTop);
1797
+ if (runningPoll) return toolLabel(runningPoll);
1798
+ if (thinking) return thinking.length > 96 ? `\u2026${thinking.slice(-96)}` : thinking;
1799
+ if (text) return "Writing reply\u2026";
1800
+ const last = [...tools].reverse().find((t) => !isPollWrapperToolName(t.name)) ?? tools.at(-1);
1801
+ if (last) {
1802
+ const label = toolLabel(last);
1803
+ return last.status === "running" ? label : `Finished ${label}`;
1804
+ }
1805
+ return "Working\u2026";
1753
1806
  }
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_");
1807
+ function messagePartParentId(part) {
1808
+ if ("parentId" in part && typeof part.parentId === "string" && part.parentId.trim()) {
1809
+ return part.parentId;
1810
+ }
1811
+ return void 0;
1761
1812
  }
1762
- function toolInputUsesBrightsyDatasource(input) {
1763
- const ds = input?.datasource;
1764
- return typeof ds === "string" && ds.toLowerCase() === "brightsy";
1813
+ function sameParentId(a, b) {
1814
+ return (a ?? "") === (b ?? "");
1765
1815
  }
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
- }
1816
+ function withEventParentId(event, parentId) {
1817
+ if (!parentId) return event;
1818
+ if (event.type === "stdout" || event.type === "thinking" || event.type === "tool_use" || event.type === "tool_result") {
1819
+ return { ...event, parentId: event.parentId ?? parentId };
1774
1820
  }
1775
- return false;
1821
+ return event;
1776
1822
  }
1777
- function threadRequestsBrightsyMcp(thread) {
1778
- return (thread?.messages ?? []).some(messageUsedBrightsyMcp);
1823
+ function toolFilePath(input) {
1824
+ if (!input) return void 0;
1825
+ return str(input.file_path) ?? str(input.path) ?? str(input.filePath) ?? str(input.filename);
1779
1826
  }
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);
1827
+ function countLines(text) {
1828
+ if (!text) return 0;
1829
+ return text.split("\n").length;
1786
1830
  }
1787
- function mcpLaunch(cmd, name, env) {
1788
- if (cmd === "brightsy-mcp") {
1789
- return { name, command: "brightsy-mcp", ...env ? { env } : {} };
1831
+ function diffFromInput(input) {
1832
+ if (!input) return {};
1833
+ const oldS = str(input.old_string) ?? str(input.oldString);
1834
+ const newS = str(input.new_string) ?? str(input.newString) ?? str(input.content);
1835
+ if (oldS != null || newS != null) {
1836
+ return {
1837
+ additions: countLines(newS),
1838
+ deletions: countLines(oldS)
1839
+ };
1790
1840
  }
1791
- return {
1792
- name,
1793
- command: "npx",
1794
- args: ["-y", "@brightsy/mcp-server"],
1795
- ...env ? { env } : {}
1796
- };
1841
+ return {};
1797
1842
  }
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
- };
1843
+ function parseDiffStat(result) {
1844
+ if (!result) return {};
1845
+ const paired = result.match(/\+(\d+)\s+-(\d+)/);
1846
+ if (paired) {
1847
+ return {
1848
+ additions: Number(paired[1]),
1849
+ deletions: Number(paired[2])
1850
+ };
1851
+ }
1852
+ const verbose = result.match(
1853
+ /(\d+)\s+insertions?(?:,\s*(\d+)\s+deletions?)?/i
1854
+ );
1855
+ if (verbose) {
1856
+ return {
1857
+ additions: Number(verbose[1]),
1858
+ deletions: verbose[2] != null ? Number(verbose[2]) : void 0
1859
+ };
1860
+ }
1861
+ return {};
1862
+ }
1863
+ var THINKING_PART_MAX_CHARS = 64e3;
1864
+ function clipThinkingForStore(text, max = THINKING_PART_MAX_CHARS) {
1865
+ if (text.length <= max) return text;
1866
+ const marker = "\u2026(earlier thinking trimmed)\n";
1867
+ return marker + text.slice(text.length - Math.max(1, max - marker.length));
1868
+ }
1869
+ var STDOUT_EVENT_MAX_CHARS = 16e3;
1870
+ var STDERR_EVENT_MAX_CHARS = 4e3;
1871
+ function clipEventTail(text, max) {
1872
+ if (text.length <= max) return text;
1873
+ const marker = "\u2026(truncated)\n";
1874
+ return marker + text.slice(text.length - Math.max(1, max - marker.length));
1806
1875
  }
1807
- function brightsyMcpAllowedTools(serverNames) {
1808
- const out = [];
1809
- for (const name of serverNames) {
1810
- out.push(`mcp__${name}`, `mcp__${name}__*`);
1876
+ function clipAgentEventForPaint(event) {
1877
+ if (event.type === "tool_result" && event.content) {
1878
+ const content = clipToolResultForStore(event.content);
1879
+ if (content === event.content) return event;
1880
+ return { ...event, content };
1811
1881
  }
1812
- return out;
1813
- }
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 {
1882
+ if (event.type === "thinking" && event.data.length > THINKING_PART_MAX_CHARS) {
1883
+ return { ...event, data: clipThinkingForStore(event.data) };
1823
1884
  }
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();
1885
+ if (event.type === "stdout" && event.data.length > STDOUT_EVENT_MAX_CHARS) {
1886
+ return { ...event, data: clipEventTail(event.data, STDOUT_EVENT_MAX_CHARS) };
1887
+ }
1888
+ if (event.type === "stderr" && event.data.length > STDERR_EVENT_MAX_CHARS) {
1889
+ return { ...event, data: clipEventTail(event.data, STDERR_EVENT_MAX_CHARS) };
1829
1890
  }
1891
+ return event;
1830
1892
  }
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;
1893
+ function withPartTimes(next, prev) {
1894
+ const now = Date.now();
1895
+ const startedAt = prev && typeof prev.startedAt === "number" ? prev.startedAt : now;
1896
+ return { ...next, startedAt, updatedAt: now };
1897
+ }
1898
+ function applyAgentEvent(parts, event) {
1899
+ if (event.type === "stdout") {
1900
+ const data = event.data;
1901
+ if (!data) return parts;
1902
+ const next = [...parts];
1903
+ const last = next[next.length - 1];
1904
+ if (last?.type === "text" && sameParentId(last.parentId, event.parentId)) {
1905
+ next[next.length - 1] = withPartTimes(
1906
+ {
1907
+ type: "text",
1908
+ text: last.text + data,
1909
+ ...event.parentId ? { parentId: event.parentId } : {}
1910
+ },
1911
+ last
1912
+ );
1913
+ } else {
1914
+ next.push(
1915
+ withPartTimes({
1916
+ type: "text",
1917
+ text: data,
1918
+ ...event.parentId ? { parentId: event.parentId } : {}
1919
+ })
1920
+ );
1849
1921
  }
1850
- const parent = dirname2(dir);
1851
- if (parent === dir) break;
1852
- dir = parent;
1922
+ return next;
1853
1923
  }
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
- };
1924
+ if (event.type === "thinking") {
1925
+ const data = event.data;
1926
+ if (!data) return parts;
1927
+ const next = [...parts];
1928
+ if (event.replace) {
1929
+ for (let i = next.length - 1; i >= 0; i--) {
1930
+ const prev = next[i];
1931
+ if (prev?.type === "thinking" && sameParentId(prev.parentId, event.parentId)) {
1932
+ next[i] = withPartTimes(
1933
+ {
1934
+ type: "thinking",
1935
+ text: clipThinkingForStore(data),
1936
+ ...event.parentId ? { parentId: event.parentId } : {}
1937
+ },
1938
+ prev
1939
+ );
1940
+ return next;
1941
+ }
1942
+ }
1943
+ next.push(
1944
+ withPartTimes({
1945
+ type: "thinking",
1946
+ text: clipThinkingForStore(data),
1947
+ ...event.parentId ? { parentId: event.parentId } : {}
1948
+ })
1949
+ );
1950
+ return next;
1951
+ }
1952
+ const last = next[next.length - 1];
1953
+ if (last?.type === "thinking" && sameParentId(last.parentId, event.parentId)) {
1954
+ next[next.length - 1] = withPartTimes(
1955
+ {
1956
+ type: "thinking",
1957
+ text: clipThinkingForStore(last.text + data),
1958
+ ...event.parentId ? { parentId: event.parentId } : {}
1959
+ },
1960
+ last
1961
+ );
1962
+ } else {
1963
+ next.push(
1964
+ withPartTimes({
1965
+ type: "thinking",
1966
+ text: clipThinkingForStore(data),
1967
+ ...event.parentId ? { parentId: event.parentId } : {}
1968
+ })
1969
+ );
1869
1970
  }
1971
+ return next;
1870
1972
  }
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"] };
1973
+ if (event.type === "tool_use") {
1974
+ const input = asRecord(event.input);
1975
+ const diff = diffFromInput(input);
1976
+ const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
1977
+ if (existing >= 0) {
1978
+ const prev = parts[existing];
1979
+ const next = [...parts];
1980
+ const mergedInput = {
1981
+ ...prev.input ?? {},
1982
+ ...input ?? {}
1983
+ };
1984
+ const mergedRecord = Object.keys(mergedInput).length > 0 ? mergedInput : void 0;
1985
+ next[existing] = withPartTimes(
1986
+ {
1987
+ ...prev,
1988
+ name: event.name || prev.name,
1989
+ input: mergedRecord,
1990
+ description: toolDescription(event.name || prev.name, mergedRecord),
1991
+ detail: toolDetail(event.name || prev.name, mergedRecord) ?? prev.detail,
1992
+ filePath: toolFilePath(mergedRecord) ?? prev.filePath,
1993
+ additions: diff.additions ?? prev.additions,
1994
+ deletions: diff.deletions ?? prev.deletions,
1995
+ parentId: event.parentId ?? prev.parentId
1996
+ },
1997
+ prev
1998
+ );
1999
+ return next;
2000
+ }
2001
+ return [
2002
+ ...parts,
2003
+ withPartTimes({
2004
+ type: "tool",
2005
+ id: event.id,
2006
+ name: event.name,
2007
+ input,
2008
+ description: toolDescription(event.name, input),
2009
+ detail: toolDetail(event.name, input),
2010
+ status: "running",
2011
+ filePath: toolFilePath(input),
2012
+ ...event.parentId ? { parentId: event.parentId } : {},
2013
+ ...diff
2014
+ })
2015
+ ];
1874
2016
  }
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;
2017
+ if (event.type === "tool_result") {
2018
+ const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
2019
+ const fromResult = parseDiffStat(event.content);
2020
+ if (event.partial) {
2021
+ if (existing < 0) {
2022
+ return [
2023
+ ...parts,
2024
+ withPartTimes({
2025
+ type: "tool",
2026
+ id: event.id,
2027
+ name: "tool",
2028
+ description: "tool",
2029
+ status: event.isError ? "error" : "running",
2030
+ result: clipToolResultForStore(event.content),
2031
+ ...event.parentId ? { parentId: event.parentId } : {}
2032
+ })
2033
+ ];
2034
+ }
2035
+ return parts.map((p, i) => {
2036
+ if (i !== existing || p.type !== "tool") return p;
2037
+ if (p.status === "done" || p.status === "error") return p;
2038
+ return withPartTimes(
2039
+ {
2040
+ ...p,
2041
+ result: clipToolResultForStore(event.content) ?? p.result
2042
+ },
2043
+ p
2044
+ );
2045
+ });
2046
+ }
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" : "done",
2056
+ result: clipToolResultForStore(event.content),
2057
+ ...event.parentId ? { parentId: event.parentId } : {},
2058
+ ...fromResult.additions != null ? { additions: fromResult.additions } : {},
2059
+ ...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
2060
+ })
2061
+ ];
1890
2062
  }
1891
- try {
1892
- mergeAgentGitAuthEnv(
1893
- sideboard.env,
1894
- await resolveAgentGitAuthEnv(sideboard.env)
2063
+ return parts.map((p, i) => {
2064
+ if (i !== existing || p.type !== "tool") return p;
2065
+ return withPartTimes(
2066
+ {
2067
+ ...p,
2068
+ status: event.isError ? "error" : "done",
2069
+ result: clipToolResultForStore(event.content),
2070
+ ...fromResult.additions != null ? { additions: fromResult.additions } : {},
2071
+ ...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
2072
+ },
2073
+ p
1895
2074
  );
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 };
2075
+ });
1921
2076
  }
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 };
2077
+ return parts;
1926
2078
  }
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;
2079
+ function partsToAssistantText(parts) {
2080
+ return parts.filter(
2081
+ (p) => p.type === "text" && !messagePartParentId(p)
2082
+ ).map((p) => p.text).join("").trim();
1933
2083
  }
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;
2084
+ function stripBrightsyNdjsonNoise(text) {
2085
+ if (!text || !text.includes('"type"')) return text;
2086
+ let out = text;
2087
+ out = out.replace(
2088
+ /\{"type":"(?:tool_use|tool_result|tool|thinking|usage|done|error)"[\s\S]*?\}\s*(?=\{"type":"|$|(?=[A-Za-z*#]))/g,
2089
+ ""
2090
+ );
2091
+ out = out.replace(/\{"type":"(?:tool_use|tool_result|tool)"[\s\S]*$/g, "");
2092
+ return out.trim();
1947
2093
  }
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;
2094
+ function isBrightsyNdjsonLine(line) {
2095
+ const t = line.trim();
2096
+ if (!t.startsWith("{")) return false;
2097
+ return /"type"\s*:\s*"(tool_use|tool_result|tool|thinking|usage|done|error|text)"/.test(t);
1967
2098
  }
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 });
2099
+ function finalizeParts(parts) {
2100
+ return parts.map(
2101
+ (p) => p.type === "tool" && p.status === "running" ? { ...p, status: "done" } : p
2102
+ );
1985
2103
  }
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;
2104
+ function normalizeParseResult(parsed) {
2105
+ if (!parsed) return [];
2106
+ return Array.isArray(parsed) ? parsed : [parsed];
2001
2107
  }
2002
2108
 
2003
2109
  // src/agents/types.ts
@@ -2061,24 +2167,15 @@ var BASE_ALLOWED_TOOLS = [
2061
2167
  "Skill"
2062
2168
  ];
2063
2169
  var CLAUDE_PRINT_BG_WAIT_CEILING_MS = 72e5;
2170
+ function isLocalDevAppDataDir(dir = process.env.SIDEBOARD_APP_DATA) {
2171
+ return /[/\\]\.sideboard[/\\]dev-app-data(?:[/\\]|$)/.test(dir ?? "");
2172
+ }
2064
2173
  var CLAUDE_CHROME_ALLOWED_TOOLS = [
2065
2174
  "mcp__claude-in-chrome",
2066
2175
  "mcp__claude-in-chrome__*",
2067
2176
  "Skill(claude-in-chrome)"
2068
2177
  ];
2069
2178
  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
2179
  function usageFromClaude(usage) {
2083
2180
  if (!usage) return null;
2084
2181
  const inputTokens = Number(usage.input_tokens ?? 0);
@@ -2365,7 +2462,7 @@ var claudeAdapter = {
2365
2462
  async detect() {
2366
2463
  const claude = resolveClaudeExecutable();
2367
2464
  if (claude !== "claude") {
2368
- if (!existsSync6(claude)) {
2465
+ if (!existsSync8(claude)) {
2369
2466
  return {
2370
2467
  agent: "claude",
2371
2468
  installed: false,
@@ -2414,7 +2511,7 @@ var claudeAdapter = {
2414
2511
  );
2415
2512
  }
2416
2513
  const mode = permissionMode(thread);
2417
- const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-KT3FRHZL.js");
2514
+ const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-XPTFHMKL.js");
2418
2515
  const isOrchestrator = isOrchestratorThread2(thread);
2419
2516
  const injectedServers = await buildInjectedMcpServers({
2420
2517
  includeSideboard: true,
@@ -2433,10 +2530,9 @@ var claudeAdapter = {
2433
2530
  ...brightsyMcpAllowedTools(injectedBrightsyNames)
2434
2531
  ];
2435
2532
  } else {
2436
- const servers = await loadMcpServers();
2437
2533
  allowedTools = [
2438
2534
  ...BASE_ALLOWED_TOOLS,
2439
- ...mcpAllowTools(servers),
2535
+ ...mcpAllowToolsFromNames(userMcpNamesToDisable({ names: listUserClaudeMcpNames() })),
2440
2536
  ...sideboardWorktreeAllowedTools({
2441
2537
  github: true,
2442
2538
  linear: isLinearConnected(),
@@ -2464,9 +2560,7 @@ var claudeAdapter = {
2464
2560
  const mcpConfigPath = writeMcpServersConfig(injectedServers);
2465
2561
  if (mcpConfigPath) {
2466
2562
  args.push("--mcp-config", mcpConfigPath);
2467
- if (isOrchestrator) {
2468
- args.push("--strict-mcp-config");
2469
- }
2563
+ args.push("--strict-mcp-config");
2470
2564
  }
2471
2565
  if (chromeOn) {
2472
2566
  args.push("--chrome");
@@ -2498,8 +2592,10 @@ var claudeAdapter = {
2498
2592
  CLAUDE_CODE_FORWARD_SUBAGENT_TEXT: "1",
2499
2593
  CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS: process.env.CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS ?? String(CLAUDE_PRINT_BG_WAIT_CEILING_MS),
2500
2594
  // `--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" } : {}
2595
+ // builds (anthropics/claude-code#60252). Coordinators never need them.
2596
+ // Local `pnpm dev` also disables them: those HTTP connectors hang the
2597
+ // first worktree turn when Electron's cwd is this Sideboard repo.
2598
+ ...isOrchestrator || isLocalDevAppDataDir() ? { ENABLE_CLAUDEAI_MCP_SERVERS: "false" } : {}
2503
2599
  }
2504
2600
  };
2505
2601
  },
@@ -2650,115 +2746,6 @@ function parseIssuesJson(raw) {
2650
2746
  import { existsSync as existsSync9, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
2651
2747
  import { homedir as homedir4 } from "os";
2652
2748
  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
2749
  var CODEX_PROMPT_ARG_MAX = 2e5;
2763
2750
  var FALLBACK_CODEX_MODELS = [
2764
2751
  { id: "gpt-5.6-sol", displayName: "GPT-5.6 Sol" },
@@ -4755,6 +4742,9 @@ export {
4755
4742
  adjustBrightsyTurnExit,
4756
4743
  listBrightsyChatTargets,
4757
4744
  brightsyAdapter,
4745
+ sideboardMcpProfile,
4746
+ packagedMcpDir,
4747
+ applyAgentRunnerHeapEnv,
4758
4748
  toolDescription,
4759
4749
  isInternalAgentStatusText,
4760
4750
  isPollWrapperToolName,
@@ -4766,9 +4756,6 @@ export {
4766
4756
  isBrightsyNdjsonLine,
4767
4757
  finalizeParts,
4768
4758
  normalizeParseResult,
4769
- sideboardMcpProfile,
4770
- packagedMcpDir,
4771
- applyAgentRunnerHeapEnv,
4772
4759
  PLAN_MODE_INSTRUCTION,
4773
4760
  permissionMode,
4774
4761
  mcpStatusThinkingFromClaudeInit,