@sideboard-ai/core 0.1.61 → 0.1.66

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 (45) hide show
  1. package/dist/{agents-B2723EOF.js → agents-EWFD765A.js} +23 -7
  2. package/dist/{agents-EADRAAPG.js → agents-MD7SCFYX.js} +23 -7
  3. package/dist/{app-settings-YBNZ4XSR.js → app-settings-GJVZGU3M.js} +15 -3
  4. package/dist/{app-settings-TBHTJWLQ.js → app-settings-ONKMFSEJ.js} +15 -3
  5. package/dist/{chunk-RIWUU7FO.js → chunk-5AG5BUQN.js} +61 -3
  6. package/dist/{chunk-HLEX5AQ6.js → chunk-7FD7COKE.js} +4 -0
  7. package/dist/{chunk-XWPKLRB3.js → chunk-AKPEXIKK.js} +2 -2
  8. package/dist/{chunk-MX7UH5AT.js → chunk-BBEH2CJE.js} +264 -93
  9. package/dist/{chunk-FGNBTFRD.js → chunk-BPOTVI5J.js} +125 -3
  10. package/dist/{chunk-CHGOMFTA.js → chunk-DNPP7QMK.js} +32 -7
  11. package/dist/{chunk-MBAL5FE6.js → chunk-DZYMNW2A.js} +4 -4
  12. package/dist/{chunk-JB2H7E7V.js → chunk-EVD4KNKR.js} +1 -1
  13. package/dist/{chunk-TSRXOSVD.js → chunk-FWJYLBO6.js} +4 -0
  14. package/dist/{chunk-QJBC4BRK.js → chunk-HTOTTJJX.js} +2 -2
  15. package/dist/{chunk-MFGGIRUA.js → chunk-I2KGRAXC.js} +2 -2
  16. package/dist/{chunk-ZXDOI6IH.js → chunk-IFAU2DEY.js} +269 -93
  17. package/dist/{chunk-NTYRBRE7.js → chunk-LRBRQFBU.js} +2 -2
  18. package/dist/{chunk-75QR74KM.js → chunk-MHR4OGZ4.js} +125 -3
  19. package/dist/{chunk-2P2UK7E7.js → chunk-PESJDZTB.js} +2 -2
  20. package/dist/{chunk-P7EQUFB6.js → chunk-R7GUDEPY.js} +2 -2
  21. package/dist/{chunk-73AT5K4A.js → chunk-TXQ6K2GR.js} +61 -3
  22. package/dist/{chunk-MLAST5BB.js → chunk-YTNLOJPK.js} +32 -7
  23. package/dist/{connected-teams-RSL4VB3X.js → connected-teams-CHXO5FFA.js} +2 -2
  24. package/dist/{connected-teams-MKIYKOYA.js → connected-teams-GJTESSTX.js} +2 -2
  25. package/dist/{coordinator-prompt-4P23VVFC.js → coordinator-prompt-E5UGSJES.js} +5 -5
  26. package/dist/{coordinator-prompt-LNWHZ64D.js → coordinator-prompt-ILSULI7C.js} +5 -5
  27. package/dist/{global-workspace-TMMAVRVW.js → global-workspace-44UG64NA.js} +6 -7
  28. package/dist/{global-workspace-BHT4XUAA.js → global-workspace-5PM5LA6E.js} +6 -7
  29. package/dist/index.cjs +1433 -944
  30. package/dist/index.d.cts +117 -2
  31. package/dist/index.d.ts +117 -2
  32. package/dist/index.js +859 -736
  33. package/dist/mcp/run-stdio.cjs +1388 -993
  34. package/dist/mcp/run-stdio.js +798 -765
  35. package/dist/{run-HZTBWGQR.js → run-74S2NZWB.js} +1 -1
  36. package/dist/{run-GPSVV5OH.js → run-JLN5JK7Q.js} +1 -1
  37. package/dist/{thread-store-GHOADGL2.js → thread-store-BUBR24YA.js} +1 -1
  38. package/dist/{thread-store-UJIGMI5J.js → thread-store-XTPFCJAI.js} +1 -1
  39. package/dist/{workspaces-3GO3EDQG.js → workspaces-OVBCSS5Y.js} +7 -8
  40. package/dist/{workspaces-GFPGDGCS.js → workspaces-RXFXQRRF.js} +7 -8
  41. package/dist/{worktree-GBLHEFHR.js → worktree-K2JB53R5.js} +3 -3
  42. package/dist/{worktree-3L2G6PCM.js → worktree-S2N63FLV.js} +3 -3
  43. package/package.json +1 -1
  44. package/dist/chunk-GI7E5733.js +0 -30
  45. package/dist/chunk-S2LL5HA4.js +0 -33
@@ -6,20 +6,29 @@ import {
6
6
  ensureCliTeamTracked,
7
7
  ensureConnectedBrightsyTeamTokens,
8
8
  loadBrightsyConfig
9
- } from "./chunk-JB2H7E7V.js";
9
+ } from "./chunk-EVD4KNKR.js";
10
10
  import {
11
- ensureAgentPath,
12
- run
13
- } from "./chunk-RIWUU7FO.js";
11
+ isOrchestratorThread
12
+ } from "./chunk-DNPP7QMK.js";
13
+ import {
14
+ enrichPathWithNpmGlobalBin,
15
+ resolveCommandBinarySync,
16
+ run,
17
+ withExportedPath
18
+ } from "./chunk-5AG5BUQN.js";
14
19
  import {
15
20
  claudeChromeEnabled,
16
21
  loadAppSettings,
22
+ resolveAgentExecutable,
17
23
  resolveClaudeExecutable
18
- } from "./chunk-FGNBTFRD.js";
24
+ } from "./chunk-BPOTVI5J.js";
19
25
  import {
20
26
  appDataDir
21
27
  } from "./chunk-7MV3RXSC.js";
22
28
 
29
+ // src/agents/brightsy.ts
30
+ import { existsSync } from "fs";
31
+
23
32
  // src/agents/brightsy-targets.ts
24
33
  function encodeBrightsyTarget(type, id, accountId) {
25
34
  if (accountId) return `team:${accountId}:${type}:${id}`;
@@ -176,6 +185,11 @@ function normalizeTurnInput(input) {
176
185
  if (typeof input === "string") return { prompt: input };
177
186
  return { prompt: input.prompt, cachedPrefix: input.cachedPrefix?.trim() || void 0 };
178
187
  }
188
+ function dropCachedPrefixOnResume(input, sessionId) {
189
+ const turn = normalizeTurnInput(input);
190
+ if (sessionId) return { prompt: turn.prompt };
191
+ return turn;
192
+ }
179
193
  function flattenTurnInput(input) {
180
194
  const { cachedPrefix, prompt } = normalizeTurnInput(input);
181
195
  if (!cachedPrefix) return prompt;
@@ -315,9 +329,13 @@ async function fetchTeamChatTargets(team) {
315
329
  };
316
330
  }
317
331
  async function listBrightsyChatTargetsViaCli() {
318
- const listed = await run("brightsy", ["chat", "--list-targets", "--json"], {
319
- reject: false
320
- });
332
+ const listed = await run(
333
+ resolveAgentExecutable("brightsy"),
334
+ ["chat", "--list-targets", "--json"],
335
+ {
336
+ reject: false
337
+ }
338
+ );
321
339
  if (listed.exitCode !== 0 || !listed.stdout.trim()) {
322
340
  throw new Error(
323
341
  listed.stderr.trim() || "Failed to list Brightsy chat targets \u2014 is `brightsy` installed and logged in?"
@@ -404,18 +422,32 @@ async function syncCliForTarget(accountId) {
404
422
  var brightsyAdapter = {
405
423
  kind: "brightsy",
406
424
  async detect() {
407
- const which = await run("which", ["brightsy"], { reject: false });
408
- if (which.exitCode !== 0) {
409
- return {
410
- agent: "brightsy",
411
- installed: false,
412
- authenticated: false,
413
- linearMcp: false,
414
- warnings: [],
415
- reason: "brightsy CLI not found on PATH \u2014 npm i -g @brightsy/cli"
416
- };
425
+ const brightsy = resolveAgentExecutable("brightsy");
426
+ if (brightsy !== "brightsy") {
427
+ if (!existsSync(brightsy)) {
428
+ return {
429
+ agent: "brightsy",
430
+ installed: false,
431
+ authenticated: false,
432
+ linearMcp: false,
433
+ warnings: [],
434
+ reason: `Brightsy executable not found: ${brightsy}`
435
+ };
436
+ }
437
+ } else {
438
+ const which = await run("which", ["brightsy"], { reject: false });
439
+ if (which.exitCode !== 0) {
440
+ return {
441
+ agent: "brightsy",
442
+ installed: false,
443
+ authenticated: false,
444
+ linearMcp: false,
445
+ warnings: [],
446
+ reason: "brightsy CLI not found on PATH \u2014 npm i -g @brightsy/cli"
447
+ };
448
+ }
417
449
  }
418
- const who = await run("brightsy", ["whoami"], { reject: false });
450
+ const who = await run(brightsy, ["whoami"], { reject: false });
419
451
  const authenticated = who.exitCode === 0 && /logged in as/i.test(who.stdout);
420
452
  return {
421
453
  agent: "brightsy",
@@ -440,7 +472,7 @@ var brightsyAdapter = {
440
472
  target.id
441
473
  ];
442
474
  return {
443
- file: "brightsy",
475
+ file: resolveAgentExecutable("brightsy"),
444
476
  args,
445
477
  cwd: thread.worktreePath,
446
478
  // Piped stdin becomes the message body (avoids ARG_MAX for long seeds).
@@ -457,12 +489,16 @@ var brightsyAdapter = {
457
489
  const target = decodeBrightsyTarget(thread.model);
458
490
  await syncCliForTarget(target.accountId);
459
491
  const args = target.type === "model" ? ["chat", "--model", target.id] : ["chat", "--agent", target.id];
460
- return { file: "brightsy", args, cwd: thread.worktreePath };
492
+ return {
493
+ file: resolveAgentExecutable("brightsy"),
494
+ args,
495
+ cwd: thread.worktreePath
496
+ };
461
497
  }
462
498
  };
463
499
 
464
500
  // src/agents/claude.ts
465
- import { existsSync as existsSync2 } from "fs";
501
+ import { existsSync as existsSync3 } from "fs";
466
502
 
467
503
  // src/agents/claude-mcp.ts
468
504
  function parseMcpList(text) {
@@ -499,12 +535,18 @@ function mcpAllowTools(servers) {
499
535
  }
500
536
 
501
537
  // src/agents/injected-mcp.ts
502
- import { existsSync, mkdtempSync, writeFileSync } from "fs";
538
+ import { existsSync as existsSync2, mkdtempSync, writeFileSync } from "fs";
503
539
  import { createRequire } from "module";
504
540
  import { tmpdir } from "os";
505
541
  import { dirname, join } from "path";
506
542
  import { fileURLToPath } from "url";
507
543
 
544
+ // src/mcp/profile.ts
545
+ var SIDEBOARD_MCP_PROFILE_ENV = "SIDEBOARD_MCP_PROFILE";
546
+ function sideboardMcpProfile(env = process.env) {
547
+ return env[SIDEBOARD_MCP_PROFILE_ENV]?.trim().toLowerCase() === "worktree" ? "worktree" : "orchestration";
548
+ }
549
+
508
550
  // src/agents/node-launch.ts
509
551
  function isAsarPath(filePath) {
510
552
  return /\.asar([/\\]|$)/.test(filePath);
@@ -558,6 +600,39 @@ function isBrightsyConnected() {
558
600
  return false;
559
601
  }
560
602
  }
603
+ var BRIGHTSY_WORD = /(?<![a-z0-9_-])brightsy(?![a-z0-9_-])/i;
604
+ function promptMentionsBrightsy(text) {
605
+ return BRIGHTSY_WORD.test(text ?? "");
606
+ }
607
+ function isBrightsyMcpToolName(name) {
608
+ const n = name.toLowerCase();
609
+ return n === "brightsy" || n.startsWith("mcp__brightsy") || n.startsWith("brightsy_");
610
+ }
611
+ function toolInputUsesBrightsyDatasource(input) {
612
+ const ds = input?.datasource;
613
+ return typeof ds === "string" && ds.toLowerCase() === "brightsy";
614
+ }
615
+ function messageUsedBrightsyMcp(msg) {
616
+ if (msg.role === "user" && promptMentionsBrightsy(msg.text)) return true;
617
+ for (const part of msg.parts ?? []) {
618
+ if (part.type !== "tool") continue;
619
+ if (isBrightsyMcpToolName(part.name)) return true;
620
+ if (/present_(schema|files)$/i.test(part.name) && toolInputUsesBrightsyDatasource(part.input)) {
621
+ return true;
622
+ }
623
+ }
624
+ return false;
625
+ }
626
+ function threadRequestsBrightsyMcp(thread) {
627
+ return (thread?.messages ?? []).some(messageUsedBrightsyMcp);
628
+ }
629
+ function shouldInjectBrightsyMcp(thread, opts) {
630
+ if (!(opts?.connected ?? isBrightsyConnected())) return false;
631
+ if (opts?.orchestrator) return true;
632
+ const alwaysOn = opts?.alwaysOnWorktree ?? Boolean(loadAppSettings().brightsy?.injectWorktreeMcp);
633
+ if (alwaysOn) return true;
634
+ return threadRequestsBrightsyMcp(thread);
635
+ }
561
636
  function mcpLaunch(cmd, name, env) {
562
637
  if (cmd === "brightsy-mcp") {
563
638
  return { name, command: "brightsy-mcp", ...env ? { env } : {} };
@@ -603,7 +678,7 @@ function corePackageDir() {
603
678
  }
604
679
  function findSideboardMcpJsEntry() {
605
680
  const override = process.env.SIDEBOARD_MCP_ENTRY?.trim() || process.env.SIDEBOARD_CLI?.trim();
606
- if (override && existsSync(override)) return override;
681
+ if (override && existsSync2(override)) return override;
607
682
  let dir = corePackageDir();
608
683
  for (let i = 0; i < 10; i++) {
609
684
  const candidates = [
@@ -616,7 +691,7 @@ function findSideboardMcpJsEntry() {
616
691
  join(dir, "cli/dist/index.js")
617
692
  ];
618
693
  for (const p of candidates) {
619
- if (existsSync(p)) return p;
694
+ if (existsSync2(p)) return p;
620
695
  }
621
696
  const parent = dirname(dir);
622
697
  if (parent === dir) break;
@@ -647,11 +722,13 @@ async function buildInjectedMcpServers(opts) {
647
722
  const servers = [];
648
723
  if (opts.includeSideboard) {
649
724
  const sideboard = await resolveSideboardMcpServer();
725
+ const orchId = opts.orchestratorThreadId?.trim();
726
+ const profile = orchId ? "orchestration" : "worktree";
650
727
  sideboard.env = {
651
728
  ...sideboard.env ?? {},
652
- SIDEBOARD_APP_DATA: appDataDir()
729
+ SIDEBOARD_APP_DATA: appDataDir(),
730
+ [SIDEBOARD_MCP_PROFILE_ENV]: profile
653
731
  };
654
- const orchId = opts.orchestratorThreadId?.trim();
655
732
  if (orchId) {
656
733
  sideboard.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID = orchId;
657
734
  }
@@ -873,7 +950,7 @@ var claudeAdapter = {
873
950
  async detect() {
874
951
  const claude = resolveClaudeExecutable();
875
952
  if (claude !== "claude") {
876
- if (!existsSync2(claude)) {
953
+ if (!existsSync3(claude)) {
877
954
  return {
878
955
  agent: "claude",
879
956
  installed: false,
@@ -912,9 +989,8 @@ var claudeAdapter = {
912
989
  },
913
990
  async buildTurn(thread, input) {
914
991
  const claude = resolveClaudeExecutable();
915
- const turn = normalizeTurnInput(input);
916
992
  const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
917
- const effective = sessionId ? { prompt: turn.prompt } : turn;
993
+ const effective = dropCachedPrefixOnResume(input, sessionId);
918
994
  const promptText = flattenTurnInput(effective);
919
995
  const useStdin = promptText.length > CLAUDE_PROMPT_ARG_MAX;
920
996
  if (process.env.SIDEBOARD_DEBUG_CLAUDE_TURN === "1") {
@@ -923,11 +999,13 @@ var claudeAdapter = {
923
999
  );
924
1000
  }
925
1001
  const mode = permissionMode(thread);
926
- const { isOrchestratorThread } = await import("./global-workspace-BHT4XUAA.js");
927
- const isOrchestrator = isOrchestratorThread(thread);
1002
+ const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-5PM5LA6E.js");
1003
+ const isOrchestrator = isOrchestratorThread2(thread);
928
1004
  const injectedServers = await buildInjectedMcpServers({
929
1005
  includeSideboard: true,
930
- includeBrightsy: isBrightsyConnected(),
1006
+ includeBrightsy: shouldInjectBrightsyMcp(thread, {
1007
+ orchestrator: isOrchestrator
1008
+ }),
931
1009
  orchestratorThreadId: isOrchestrator ? thread.id : null
932
1010
  });
933
1011
  const injectedBrightsyNames = injectedServers.filter((s) => s.name === "brightsy" || s.name.startsWith("brightsy_")).map((s) => s.name);
@@ -1119,7 +1197,7 @@ function parseIssuesJson(raw) {
1119
1197
  }
1120
1198
 
1121
1199
  // src/agents/codex.ts
1122
- import { existsSync as existsSync3, readFileSync, statSync } from "fs";
1200
+ import { existsSync as existsSync4, readFileSync, statSync } from "fs";
1123
1201
  import { homedir } from "os";
1124
1202
  import { join as join2 } from "path";
1125
1203
  var CODEX_PROMPT_ARG_MAX = 2e5;
@@ -1137,9 +1215,14 @@ async function listCodexModels() {
1137
1215
  if (cachedCodexModels && now - cachedCodexModels.at < CODEX_MODEL_CACHE_MS) {
1138
1216
  return cachedCodexModels.models;
1139
1217
  }
1140
- const which = await run("which", ["codex"], { reject: false });
1141
- if (which.exitCode !== 0) return FALLBACK_CODEX_MODELS;
1142
- const listed = await run("codex", ["debug", "models"], { reject: false });
1218
+ const codex = resolveAgentExecutable("codex");
1219
+ if (codex === "codex") {
1220
+ const which = await run("which", ["codex"], { reject: false });
1221
+ if (which.exitCode !== 0) return FALLBACK_CODEX_MODELS;
1222
+ } else if (!existsSync4(codex)) {
1223
+ return FALLBACK_CODEX_MODELS;
1224
+ }
1225
+ const listed = await run(codex, ["debug", "models"], { reject: false });
1143
1226
  if (listed.exitCode !== 0 || !listed.stdout.trim()) {
1144
1227
  return cachedCodexModels?.models ?? FALLBACK_CODEX_MODELS;
1145
1228
  }
@@ -1178,7 +1261,7 @@ function codexConfigHasNetworkAccess() {
1178
1261
  join2(homedir(), ".config", "codex", "config.toml")
1179
1262
  ];
1180
1263
  for (const path of candidates) {
1181
- if (!existsSync3(path)) continue;
1264
+ if (!existsSync4(path)) continue;
1182
1265
  const text = readFileSync(path, "utf8");
1183
1266
  if (/network_access\s*=\s*true/.test(text)) return true;
1184
1267
  }
@@ -1212,7 +1295,7 @@ function asRecord(value) {
1212
1295
  }
1213
1296
  function codexLooksAuthenticated() {
1214
1297
  const authPath = join2(homedir(), ".codex", "auth.json");
1215
- if (!existsSync3(authPath)) return false;
1298
+ if (!existsSync4(authPath)) return false;
1216
1299
  try {
1217
1300
  return statSync(authPath).size > 2;
1218
1301
  } catch {
@@ -1222,16 +1305,30 @@ function codexLooksAuthenticated() {
1222
1305
  var codexAdapter = {
1223
1306
  kind: "codex",
1224
1307
  async detect() {
1225
- const which = await run("which", ["codex"], { reject: false, timeoutMs: 3e3 });
1226
- if (which.exitCode !== 0) {
1227
- return {
1228
- agent: "codex",
1229
- installed: false,
1230
- authenticated: false,
1231
- linearMcp: false,
1232
- warnings: [],
1233
- reason: "codex CLI not found on PATH"
1234
- };
1308
+ const codex = resolveAgentExecutable("codex");
1309
+ if (codex !== "codex") {
1310
+ if (!existsSync4(codex)) {
1311
+ return {
1312
+ agent: "codex",
1313
+ installed: false,
1314
+ authenticated: false,
1315
+ linearMcp: false,
1316
+ warnings: [],
1317
+ reason: `Codex executable not found: ${codex}`
1318
+ };
1319
+ }
1320
+ } else {
1321
+ const which = await run("which", ["codex"], { reject: false, timeoutMs: 3e3 });
1322
+ if (which.exitCode !== 0) {
1323
+ return {
1324
+ agent: "codex",
1325
+ installed: false,
1326
+ authenticated: false,
1327
+ linearMcp: false,
1328
+ warnings: [],
1329
+ reason: "codex CLI not found on PATH"
1330
+ };
1331
+ }
1235
1332
  }
1236
1333
  const authenticated = codexLooksAuthenticated();
1237
1334
  const linearMcp = false;
@@ -1251,22 +1348,24 @@ var codexAdapter = {
1251
1348
  };
1252
1349
  },
1253
1350
  async buildTurn(thread, input) {
1254
- const prompt = flattenTurnInput(input);
1255
1351
  const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
1352
+ const prompt = flattenTurnInput(dropCachedPrefixOnResume(input, sessionId));
1256
1353
  const useStdin = prompt.length > CODEX_PROMPT_ARG_MAX;
1257
1354
  const promptArg = useStdin ? "-" : prompt;
1258
1355
  if (process.env.SIDEBOARD_DEBUG_CODEX_TURN === "1") {
1259
- const { cachedPrefix } = normalizeTurnInput(input);
1260
1356
  console.error(
1261
- `[sideboard/codex] promptChars=${prompt.length} resumed=${Boolean(sessionId)} stdin=${useStdin} hasPrefix=${Boolean(cachedPrefix)}`
1357
+ `[sideboard/codex] promptChars=${prompt.length} resumed=${Boolean(sessionId)} stdin=${useStdin} hasPrefix=${prompt.includes("Current request:")}`
1262
1358
  );
1263
1359
  }
1264
1360
  const mode = permissionMode(thread);
1265
1361
  const model = thread.model?.trim();
1362
+ const isOrchestrator = isOrchestratorThread(thread);
1266
1363
  const injected = await buildInjectedMcpServers({
1267
1364
  includeSideboard: true,
1268
- includeBrightsy: isBrightsyConnected(),
1269
- orchestratorThreadId: thread.sourceType === "orchestration" ? thread.id : null
1365
+ includeBrightsy: shouldInjectBrightsyMcp(thread, {
1366
+ orchestrator: isOrchestrator
1367
+ }),
1368
+ orchestratorThreadId: isOrchestrator ? thread.id : null
1270
1369
  });
1271
1370
  const mcpOverrides = toCodexMcpConfigArgs(injected);
1272
1371
  const execOpts = [
@@ -1286,7 +1385,7 @@ var codexAdapter = {
1286
1385
  ];
1287
1386
  const args = sessionId ? ["exec", ...execOpts, "resume", sessionId, promptArg] : ["exec", ...execOpts, promptArg];
1288
1387
  return {
1289
- file: "codex",
1388
+ file: resolveAgentExecutable("codex"),
1290
1389
  args,
1291
1390
  cwd: thread.worktreePath,
1292
1391
  stdin: useStdin ? `${prompt}
@@ -1382,16 +1481,17 @@ var codexAdapter = {
1382
1481
  return cached;
1383
1482
  },
1384
1483
  async buildAttach(thread) {
1484
+ const codex = resolveAgentExecutable("codex");
1385
1485
  const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
1386
1486
  if (sessionId) {
1387
1487
  return {
1388
- file: "codex",
1488
+ file: codex,
1389
1489
  args: ["exec", "--cd", thread.worktreePath, "resume", sessionId],
1390
1490
  cwd: thread.worktreePath
1391
1491
  };
1392
1492
  }
1393
1493
  return {
1394
- file: "codex",
1494
+ file: codex,
1395
1495
  args: ["--cd", thread.worktreePath],
1396
1496
  cwd: thread.worktreePath
1397
1497
  };
@@ -1399,7 +1499,7 @@ var codexAdapter = {
1399
1499
  async listLinearIssues(_repoPath) {
1400
1500
  const prompt = "List my assigned Linear issues as JSON array only: id, identifier, title, url, labels.";
1401
1501
  const { stdout, exitCode } = await run(
1402
- "codex",
1502
+ resolveAgentExecutable("codex"),
1403
1503
  [
1404
1504
  "exec",
1405
1505
  "--json",
@@ -1424,7 +1524,7 @@ var codexAdapter = {
1424
1524
  };
1425
1525
 
1426
1526
  // src/agents/cursor.ts
1427
- import { existsSync as existsSync4 } from "fs";
1527
+ import { existsSync as existsSync5 } from "fs";
1428
1528
  import { createRequire as createRequire2 } from "module";
1429
1529
  import { dirname as dirname2, join as join3 } from "path";
1430
1530
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -1668,7 +1768,7 @@ function cursorRunnerPath() {
1668
1768
  join3(root, "src", "agents", "cursor-runner.ts")
1669
1769
  ];
1670
1770
  for (const candidate of candidates) {
1671
- if (existsSync4(candidate)) return candidate;
1771
+ if (existsSync5(candidate)) return candidate;
1672
1772
  }
1673
1773
  return candidates[0];
1674
1774
  }
@@ -1695,13 +1795,16 @@ var cursorAdapter = {
1695
1795
  };
1696
1796
  },
1697
1797
  async buildTurn(thread, input) {
1698
- const prompt = flattenTurnInput(input);
1699
1798
  const agentId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
1799
+ const prompt = flattenTurnInput(dropCachedPrefixOnResume(input, agentId));
1700
1800
  const apiKey = resolveCursorApiKey() || void 0;
1801
+ const isOrchestrator = isOrchestratorThread(thread);
1701
1802
  const injected = await buildInjectedMcpServers({
1702
1803
  includeSideboard: true,
1703
- includeBrightsy: isBrightsyConnected(),
1704
- orchestratorThreadId: thread.sourceType === "orchestration" ? thread.id : null
1804
+ includeBrightsy: shouldInjectBrightsyMcp(thread, {
1805
+ orchestrator: isOrchestrator
1806
+ }),
1807
+ orchestratorThreadId: isOrchestrator ? thread.id : null
1705
1808
  });
1706
1809
  const mcpServers = toCursorMcpServers(injected);
1707
1810
  const req = {
@@ -1747,6 +1850,7 @@ var cursorAdapter = {
1747
1850
  };
1748
1851
 
1749
1852
  // src/agents/opencode.ts
1853
+ import { existsSync as existsSync6 } from "fs";
1750
1854
  var FALLBACK_OPENCODE_MODELS = [
1751
1855
  { id: "opencode/big-pickle", displayName: "opencode \xB7 big-pickle" },
1752
1856
  {
@@ -1783,9 +1887,14 @@ async function listOpencodeModels() {
1783
1887
  if (cachedOpencodeModels && now - cachedOpencodeModels.at < OPENCODE_MODEL_CACHE_MS) {
1784
1888
  return cachedOpencodeModels.models;
1785
1889
  }
1786
- const which = await run("which", ["opencode"], { reject: false });
1787
- if (which.exitCode !== 0) return FALLBACK_OPENCODE_MODELS;
1788
- const listed = await run("opencode", ["models"], { reject: false });
1890
+ const opencode = resolveAgentExecutable("opencode");
1891
+ if (opencode === "opencode") {
1892
+ const which = await run("which", ["opencode"], { reject: false });
1893
+ if (which.exitCode !== 0) return FALLBACK_OPENCODE_MODELS;
1894
+ } else if (!existsSync6(opencode)) {
1895
+ return FALLBACK_OPENCODE_MODELS;
1896
+ }
1897
+ const listed = await run(opencode, ["models"], { reject: false });
1789
1898
  if (listed.exitCode !== 0 || !listed.stdout.trim()) {
1790
1899
  return cachedOpencodeModels?.models ?? FALLBACK_OPENCODE_MODELS;
1791
1900
  }
@@ -1815,20 +1924,34 @@ function usageFromOpencode(tokens) {
1815
1924
  var opencodeAdapter = {
1816
1925
  kind: "opencode",
1817
1926
  async detect() {
1818
- const which = await run("which", ["opencode"], { reject: false });
1819
- if (which.exitCode !== 0) {
1820
- return {
1821
- agent: "opencode",
1822
- installed: false,
1823
- authenticated: false,
1824
- linearMcp: false,
1825
- warnings: [],
1826
- reason: "opencode CLI not found on PATH"
1827
- };
1927
+ const opencode = resolveAgentExecutable("opencode");
1928
+ if (opencode !== "opencode") {
1929
+ if (!existsSync6(opencode)) {
1930
+ return {
1931
+ agent: "opencode",
1932
+ installed: false,
1933
+ authenticated: false,
1934
+ linearMcp: false,
1935
+ warnings: [],
1936
+ reason: `OpenCode executable not found: ${opencode}`
1937
+ };
1938
+ }
1939
+ } else {
1940
+ const which = await run("which", ["opencode"], { reject: false });
1941
+ if (which.exitCode !== 0) {
1942
+ return {
1943
+ agent: "opencode",
1944
+ installed: false,
1945
+ authenticated: false,
1946
+ linearMcp: false,
1947
+ warnings: [],
1948
+ reason: "opencode CLI not found on PATH"
1949
+ };
1950
+ }
1828
1951
  }
1829
- const auth = await run("opencode", ["auth", "list"], { reject: false });
1952
+ const auth = await run(opencode, ["auth", "list"], { reject: false });
1830
1953
  const authenticated = auth.exitCode === 0 && auth.stdout.trim().length > 0;
1831
- const mcp = await run("opencode", ["mcp", "list"], { reject: false });
1954
+ const mcp = await run(opencode, ["mcp", "list"], { reject: false });
1832
1955
  const linearMcp = /linear/i.test(mcp.stdout + mcp.stderr);
1833
1956
  return {
1834
1957
  agent: "opencode",
@@ -1840,8 +1963,8 @@ var opencodeAdapter = {
1840
1963
  };
1841
1964
  },
1842
1965
  async buildTurn(thread, input) {
1843
- const prompt = flattenTurnInput(input);
1844
1966
  const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
1967
+ const prompt = flattenTurnInput(dropCachedPrefixOnResume(input, sessionId));
1845
1968
  const mode = permissionMode(thread);
1846
1969
  const model = thread.model?.trim();
1847
1970
  const args = [
@@ -1857,14 +1980,17 @@ var opencodeAdapter = {
1857
1980
  if (model) {
1858
1981
  args.push("--model", model);
1859
1982
  }
1983
+ const isOrchestrator = isOrchestratorThread(thread);
1860
1984
  const injected = await buildInjectedMcpServers({
1861
1985
  includeSideboard: true,
1862
- includeBrightsy: isBrightsyConnected(),
1863
- orchestratorThreadId: thread.sourceType === "orchestration" ? thread.id : null
1986
+ includeBrightsy: shouldInjectBrightsyMcp(thread, {
1987
+ orchestrator: isOrchestrator
1988
+ }),
1989
+ orchestratorThreadId: isOrchestrator ? thread.id : null
1864
1990
  });
1865
1991
  const mcpContent = injected.length > 0 ? toOpencodeMcpConfigContent(injected) : null;
1866
1992
  return {
1867
- file: "opencode",
1993
+ file: resolveAgentExecutable("opencode"),
1868
1994
  args,
1869
1995
  cwd: thread.worktreePath,
1870
1996
  // `opencode run` treats non-TTY stdin as the message body when no positional
@@ -1940,7 +2066,7 @@ var opencodeAdapter = {
1940
2066
  async resolveSessionId(worktreePath, cached) {
1941
2067
  const cachedId = cached?.trim() || null;
1942
2068
  const listed = await run(
1943
- "opencode",
2069
+ resolveAgentExecutable("opencode"),
1944
2070
  ["session", "list", "--format", "json"],
1945
2071
  { cwd: worktreePath, reject: false }
1946
2072
  );
@@ -1971,7 +2097,7 @@ var opencodeAdapter = {
1971
2097
  const args = ["--dir", thread.worktreePath];
1972
2098
  if (sessionId) args.push("--session", sessionId);
1973
2099
  return {
1974
- file: "opencode",
2100
+ file: resolveAgentExecutable("opencode"),
1975
2101
  args,
1976
2102
  cwd: thread.worktreePath,
1977
2103
  env: {
@@ -1982,7 +2108,7 @@ var opencodeAdapter = {
1982
2108
  async listLinearIssues(_repoPath) {
1983
2109
  const prompt = "List my assigned Linear issues as JSON array only: id, identifier, title, url, labels.";
1984
2110
  const { stdout, exitCode } = await run(
1985
- "opencode",
2111
+ resolveAgentExecutable("opencode"),
1986
2112
  ["run", prompt, "--format", "json"],
1987
2113
  {
1988
2114
  reject: false,
@@ -2237,15 +2363,48 @@ var SETUP = {
2237
2363
  npmPackage: "@brightsy/cli"
2238
2364
  }
2239
2365
  };
2366
+ var CLI_BIN = {
2367
+ claude: "claude",
2368
+ codex: "codex",
2369
+ opencode: "opencode",
2370
+ brightsy: "brightsy"
2371
+ };
2240
2372
  function getAgentSetupInfo(agent) {
2241
2373
  return SETUP[agent];
2242
2374
  }
2243
2375
  function listAgentSetupInfo() {
2244
2376
  return Object.values(SETUP);
2245
2377
  }
2246
- async function openInSystemTerminal(command) {
2247
- ensureAgentPath();
2378
+ var CLI_LOGIN_AGENTS = /* @__PURE__ */ new Set([
2379
+ "claude",
2380
+ "codex",
2381
+ "opencode",
2382
+ "brightsy"
2383
+ ]);
2384
+ function resolveLoginCommand(agent) {
2385
+ const info = getAgentSetupInfo(agent);
2386
+ if (!info.loginCommand) return null;
2387
+ if (!CLI_LOGIN_AGENTS.has(agent)) return info.loginCommand;
2388
+ const exe = resolveAgentExecutable(agent);
2389
+ return info.loginCommand.replace(/^\S+/, exe);
2390
+ }
2391
+ async function prepareTerminalCommand(command) {
2392
+ enrichPathWithNpmGlobalBin();
2248
2393
  const trimmed = command.trim();
2394
+ if (!trimmed) return trimmed;
2395
+ if (/^(export\s+PATH=|PATH=)/.test(trimmed)) return trimmed;
2396
+ const match = /^(\S+)(\s[\s\S]*)?$/.exec(trimmed);
2397
+ const bin = match?.[1];
2398
+ let whichPath = null;
2399
+ if (bin && !bin.includes("/") && !bin.includes("\\")) {
2400
+ const which = await run("which", [bin], { reject: false, timeoutMs: 3e3 });
2401
+ whichPath = which.exitCode === 0 ? which.stdout.trim() : null;
2402
+ }
2403
+ const resolved = resolveCommandBinarySync(trimmed, whichPath);
2404
+ return withExportedPath(resolved, process.env.PATH ?? "");
2405
+ }
2406
+ async function openInSystemTerminal(command) {
2407
+ const trimmed = (await prepareTerminalCommand(command)).trim();
2249
2408
  if (!trimmed) throw new Error("Command is empty");
2250
2409
  if (process.platform === "darwin") {
2251
2410
  const escaped = trimmed.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
@@ -2306,7 +2465,7 @@ async function installAgent(agent) {
2306
2465
  message: `Opened Terminal to run: ${info.installCommand}`
2307
2466
  };
2308
2467
  }
2309
- ensureAgentPath();
2468
+ enrichPathWithNpmGlobalBin();
2310
2469
  const result = await run("npm", ["install", "-g", info.npmPackage], { reject: false });
2311
2470
  const ok = result.exitCode === 0;
2312
2471
  if (!ok) {
@@ -2332,29 +2491,38 @@ async function installAgent(agent) {
2332
2491
  };
2333
2492
  }
2334
2493
  }
2494
+ enrichPathWithNpmGlobalBin();
2495
+ const cliBin = agent === "cursor" ? null : CLI_BIN[agent];
2496
+ let binPath = null;
2497
+ if (cliBin) {
2498
+ const which = await run("which", [cliBin], { reject: false, timeoutMs: 3e3 });
2499
+ binPath = which.exitCode === 0 ? which.stdout.trim().split(/\r?\n/).find(Boolean) ?? null : null;
2500
+ }
2335
2501
  return {
2336
2502
  ok: true,
2337
2503
  command: info.installCommand,
2338
2504
  exitCode: 0,
2339
2505
  stdout: result.stdout,
2340
2506
  stderr: result.stderr,
2341
- message: `Installed ${info.npmPackage}`
2507
+ message: binPath ? `Installed ${info.npmPackage} \u2192 ${binPath}` : `Installed ${info.npmPackage} (CLI not yet on PATH \u2014 Log in still exports Electron\u2019s PATH into Terminal)`
2342
2508
  };
2343
2509
  }
2344
2510
  async function loginAgent(agent) {
2345
2511
  const info = getAgentSetupInfo(agent);
2346
- if (!info.loginCommand) {
2512
+ const loginCommand = resolveLoginCommand(agent);
2513
+ if (!loginCommand) {
2347
2514
  return {
2348
2515
  ok: true,
2349
2516
  message: info.kind === "bundled-sdk" || info.kind === "api-key" ? info.summary : `No login command for ${agent}`
2350
2517
  };
2351
2518
  }
2352
- await openInSystemTerminal(info.loginCommand);
2519
+ const prepared = await prepareTerminalCommand(loginCommand);
2520
+ await openInSystemTerminal(prepared);
2353
2521
  return {
2354
2522
  ok: true,
2355
2523
  openedTerminal: true,
2356
- command: info.loginCommand,
2357
- message: `Opened Terminal to run: ${info.loginCommand}`
2524
+ command: prepared,
2525
+ message: `Opened Terminal to run: ${prepared}`
2358
2526
  };
2359
2527
  }
2360
2528
 
@@ -2386,6 +2554,7 @@ export {
2386
2554
  parseBrightsyCliLine,
2387
2555
  listBrightsyChatTargets,
2388
2556
  brightsyAdapter,
2557
+ sideboardMcpProfile,
2389
2558
  PLAN_MODE_INSTRUCTION,
2390
2559
  permissionMode,
2391
2560
  claudeAdapter,
@@ -2406,6 +2575,8 @@ export {
2406
2575
  resolveQuotaFallbackAgent,
2407
2576
  getAgentSetupInfo,
2408
2577
  listAgentSetupInfo,
2578
+ resolveLoginCommand,
2579
+ prepareTerminalCommand,
2409
2580
  openInSystemTerminal,
2410
2581
  installAgent,
2411
2582
  loginAgent,