@sideboard-ai/core 0.1.109 → 0.1.110

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 (43) hide show
  1. package/dist/agents/cursor-runner.cjs +87 -12
  2. package/dist/agents/cursor-runner.js +37 -11
  3. package/dist/{agents-XB332VUJ.js → agents-MQDW3YXL.js} +6 -6
  4. package/dist/{agents-YIMHDI46.js → agents-WK54VP7J.js} +7 -7
  5. package/dist/{app-settings-EBCVMFF6.js → app-settings-5XAGNDX7.js} +3 -1
  6. package/dist/{app-settings-IYSSVZHH.js → app-settings-J4LUWIRN.js} +3 -1
  7. package/dist/{chunk-WD2TARQS.js → chunk-3UOKB6VQ.js} +40 -0
  8. package/dist/{chunk-756EO4GK.js → chunk-4EWPKYOU.js} +4 -4
  9. package/dist/{chunk-4LKD3PGO.js → chunk-4NR5FL46.js} +10 -0
  10. package/dist/{chunk-FBV6NK62.js → chunk-7KWYXGIU.js} +2 -2
  11. package/dist/{chunk-KGSVLZV6.js → chunk-A4VZXJEJ.js} +2 -0
  12. package/dist/{chunk-VRTKGKUW.js → chunk-AROMOP3C.js} +2 -2
  13. package/dist/{chunk-EW7COXYE.js → chunk-C4KHDW3U.js} +2 -2
  14. package/dist/{chunk-Q6B3NRCT.js → chunk-EAAB4EK5.js} +154 -287
  15. package/dist/{chunk-I2OJ2YSP.js → chunk-KQBI5HNT.js} +2 -2
  16. package/dist/{chunk-P3BQ5DOL.js → chunk-KYOTBZ6S.js} +2 -0
  17. package/dist/{chunk-KKHDPV45.js → chunk-RSIWPLRG.js} +473 -37
  18. package/dist/{chunk-NE4TOOKS.js → chunk-SGEVS5SY.js} +10 -0
  19. package/dist/{chunk-TROJ3PVH.js → chunk-VOD3HFLP.js} +4 -4
  20. package/dist/{chunk-AY2EQI2P.js → chunk-VOJSO3RM.js} +472 -46
  21. package/dist/{chunk-MMI4RJ5I.js → chunk-WIHKHR5R.js} +14 -3
  22. package/dist/{chunk-LTPX5N6K.js → chunk-XOZDAVOP.js} +162 -276
  23. package/dist/{chunk-OQDIYVDD.js → chunk-YFAXVUY2.js} +2 -2
  24. package/dist/{chunk-JVIW55KT.js → chunk-YXDR43ZC.js} +2 -2
  25. package/dist/{coordinator-prompt-JKOU6BWV.js → coordinator-prompt-AR66L3N4.js} +4 -4
  26. package/dist/{coordinator-prompt-V66BYTUV.js → coordinator-prompt-BHMLWL64.js} +4 -4
  27. package/dist/{global-workspace-WBQWQQQY.js → global-workspace-SKFODUQQ.js} +5 -5
  28. package/dist/{global-workspace-NCTBE7G7.js → global-workspace-XSUFEIAQ.js} +5 -5
  29. package/dist/index.cjs +835 -489
  30. package/dist/index.d.cts +51 -3
  31. package/dist/index.d.ts +51 -3
  32. package/dist/index.js +44 -23
  33. package/dist/mcp/run-stdio.cjs +812 -487
  34. package/dist/mcp/run-stdio.js +17 -12
  35. package/dist/{orchestrator-6W7EKSPO.js → orchestrator-FCZVCKBK.js} +10 -10
  36. package/dist/{orchestrator-JVGVPKLI.js → orchestrator-SCATSB3O.js} +8 -8
  37. package/dist/{thread-store-DQJGDKIJ.js → thread-store-LPTBVW5C.js} +1 -1
  38. package/dist/{thread-store-4OUEQ2DB.js → thread-store-OEALWU7Y.js} +1 -1
  39. package/dist/{workspaces-SUBEDSGB.js → workspaces-HV3J4TTW.js} +6 -6
  40. package/dist/{workspaces-I3554R3F.js → workspaces-XAQVKTLO.js} +6 -6
  41. package/dist/{worktree-7H6HB6GW.js → worktree-N2EV24EE.js} +3 -3
  42. package/dist/{worktree-2UWO7VEK.js → worktree-XFJED3VU.js} +3 -3
  43. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -820,6 +820,7 @@ __export(app_settings_exports, {
820
820
  childEnvWithAppSettings: () => childEnvWithAppSettings,
821
821
  claudeChromeEnabled: () => claudeChromeEnabled,
822
822
  claudeUserSettingsPath: () => claudeUserSettingsPath,
823
+ cowboyModeEnabled: () => cowboyModeEnabled,
823
824
  deleteBranchOnPurgeEnabled: () => deleteBranchOnPurgeEnabled,
824
825
  disconnectLinearConnection: () => disconnectLinearConnection,
825
826
  ensureSlackDeviceIdentity: () => ensureSlackDeviceIdentity,
@@ -1058,6 +1059,9 @@ function normalizeAdvanced(raw) {
1058
1059
  if (typeof source.deleteBranchOnPurge === "boolean") {
1059
1060
  out.deleteBranchOnPurge = source.deleteBranchOnPurge;
1060
1061
  }
1062
+ if (typeof source.cowboyMode === "boolean") {
1063
+ out.cowboyMode = source.cowboyMode;
1064
+ }
1061
1065
  if (typeof source.autoArchiveOnMerge === "boolean") {
1062
1066
  out.autoArchiveOnMerge = source.autoArchiveOnMerge;
1063
1067
  }
@@ -1582,6 +1586,9 @@ function updateAdvancedSettings(patch) {
1582
1586
  if (typeof patch.deleteBranchOnPurge === "boolean") {
1583
1587
  advanced.deleteBranchOnPurge = patch.deleteBranchOnPurge;
1584
1588
  }
1589
+ if (typeof patch.cowboyMode === "boolean") {
1590
+ advanced.cowboyMode = patch.cowboyMode;
1591
+ }
1585
1592
  if (typeof patch.autoArchiveOnMerge === "boolean") {
1586
1593
  advanced.autoArchiveOnMerge = patch.autoArchiveOnMerge;
1587
1594
  }
@@ -1632,6 +1639,9 @@ function caffeinateWhileCloudConnectEnabled(settings = loadAppSettings()) {
1632
1639
  function deleteBranchOnPurgeEnabled(settings = loadAppSettings()) {
1633
1640
  return Boolean(settings.advanced.deleteBranchOnPurge);
1634
1641
  }
1642
+ function cowboyModeEnabled(settings = loadAppSettings()) {
1643
+ return Boolean(settings.advanced.cowboyMode);
1644
+ }
1635
1645
  function autoArchiveOnMergeEnabled(settings = loadAppSettings()) {
1636
1646
  return Boolean(settings.advanced.autoArchiveOnMerge);
1637
1647
  }
@@ -1977,6 +1987,7 @@ function normalizeThread(raw) {
1977
1987
  prTitle: raw.prTitle ?? null,
1978
1988
  prState: raw.prState ?? null,
1979
1989
  skipAutoArchiveOnMerge: Boolean(raw.skipAutoArchiveOnMerge),
1990
+ cowboy: Boolean(raw.cowboy),
1980
1991
  stackId: raw.stackId ?? null,
1981
1992
  stackLayer: raw.stackLayer ?? null,
1982
1993
  userSetTitle: Boolean(raw.userSetTitle),
@@ -2005,6 +2016,7 @@ function createEmptyThread(partial) {
2005
2016
  prTitle: partial.prTitle ?? null,
2006
2017
  prState: partial.prState ?? null,
2007
2018
  skipAutoArchiveOnMerge: partial.skipAutoArchiveOnMerge ?? false,
2019
+ cowboy: Boolean(partial.cowboy),
2008
2020
  stackId: partial.stackId ?? null,
2009
2021
  stackLayer: partial.stackLayer ?? null,
2010
2022
  userSetTitle: partial.userSetTitle ?? false,
@@ -7531,6 +7543,300 @@ var init_claude_mcp = __esm({
7531
7543
  }
7532
7544
  });
7533
7545
 
7546
+ // src/agents/message-parts.ts
7547
+ function asRecord(input) {
7548
+ if (input && typeof input === "object" && !Array.isArray(input)) {
7549
+ return input;
7550
+ }
7551
+ return void 0;
7552
+ }
7553
+ function str2(v) {
7554
+ return typeof v === "string" && v.trim() ? v : void 0;
7555
+ }
7556
+ function toolDetail(name, input) {
7557
+ if (!input) return void 0;
7558
+ const command = str2(input.command) ?? str2(input.cmd);
7559
+ if (command) return command;
7560
+ const path2 = str2(input.file_path) ?? str2(input.path) ?? str2(input.filePath) ?? str2(input.filename);
7561
+ if (path2) return path2;
7562
+ const pattern = str2(input.pattern) ?? str2(input.glob) ?? str2(input.glob_pattern);
7563
+ if (pattern) return pattern;
7564
+ const query = str2(input.query) ?? str2(input.prompt);
7565
+ if (query) return query.length > 80 ? `${query.slice(0, 77)}\u2026` : query;
7566
+ try {
7567
+ const raw = JSON.stringify(input);
7568
+ return raw.length > 80 ? `${raw.slice(0, 77)}\u2026` : raw;
7569
+ } catch {
7570
+ return name;
7571
+ }
7572
+ }
7573
+ function toolDescription(name, input) {
7574
+ const n = name.replace(/^mcp__/, "").replace(/__/g, " \xB7 ");
7575
+ if (/^get_record_types$|recordTypes/i.test(name)) return "List record types";
7576
+ if (/connectedAgentRequest/i.test(name)) {
7577
+ return str2(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
7578
+ }
7579
+ if (/present_artifact$/i.test(name)) {
7580
+ return str2(input?.title) ? `Present ${str2(input?.title)}` : "Present artifact";
7581
+ }
7582
+ if (/present_plan$/i.test(name)) {
7583
+ return str2(input?.title) ? `Plan ${str2(input?.title)}` : "Present plan";
7584
+ }
7585
+ if (/present_schema$/i.test(name)) {
7586
+ return str2(input?.title) ? `Schema ${str2(input?.title)}` : "Present schema";
7587
+ }
7588
+ if (/present_files$/i.test(name)) {
7589
+ return str2(input?.title) ? `Files ${str2(input?.title)}` : "Present files";
7590
+ }
7591
+ if (isSubagentToolName(name)) {
7592
+ const desc = str2(input?.description);
7593
+ const sub = asRecord(input?.subagentType);
7594
+ const kind = str2(sub?.name) ?? str2(sub?.kind) ?? str2(input?.subagent_type) ?? str2(input?.subagentType);
7595
+ if (/^spawn_agent$/i.test(name)) {
7596
+ return str2(input?.prompt) ?? desc ?? "Subagent";
7597
+ }
7598
+ if (/connectedAgentRequest/i.test(name)) {
7599
+ return str2(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
7600
+ }
7601
+ if (desc && kind) return `${kind}: ${desc}`;
7602
+ if (desc) return desc;
7603
+ return "Subagent";
7604
+ }
7605
+ if (/^(create|update)_artifact$/i.test(name.replace(/^mcp__[^_]+__/, ""))) {
7606
+ return str2(input?.title) ? `Artifact ${str2(input?.title)}` : "Artifact";
7607
+ }
7608
+ if (!input) return n;
7609
+ if (/bash|shell|terminal/i.test(name) && str2(input.command)) {
7610
+ const cmd = str2(input.command);
7611
+ if (/git\s+fetch/i.test(cmd)) return "Fetch latest from origin and check status";
7612
+ if (/git\s+status/i.test(cmd)) return "Check git status";
7613
+ if (/git\s+log/i.test(cmd)) return "Inspect recent commits";
7614
+ if (/git\s+branch/i.test(cmd)) return "List branches";
7615
+ if (/grep|rg\b/i.test(cmd)) return "Search repository";
7616
+ return "Run shell command";
7617
+ }
7618
+ if (/edit|write|apply/i.test(name)) {
7619
+ const path2 = str2(input.file_path) ?? str2(input.path);
7620
+ return path2 ? `Edit ${path2.split("/").pop()}` : "Edit file";
7621
+ }
7622
+ if (/read/i.test(name)) {
7623
+ const path2 = str2(input.file_path) ?? str2(input.path);
7624
+ return path2 ? `Read ${path2.split("/").pop()}` : "Read file";
7625
+ }
7626
+ if (/grep/i.test(name)) return "Search files";
7627
+ if (/glob/i.test(name)) return "Find files";
7628
+ return n;
7629
+ }
7630
+ function isSubagentToolName(name) {
7631
+ const n = (name ?? "").trim();
7632
+ if (/^(task|agent|spawn_agent)$/i.test(n)) return true;
7633
+ return /connectedAgentRequest/i.test(n);
7634
+ }
7635
+ function messagePartParentId(part) {
7636
+ if ("parentId" in part && typeof part.parentId === "string" && part.parentId.trim()) {
7637
+ return part.parentId;
7638
+ }
7639
+ return void 0;
7640
+ }
7641
+ function sameParentId(a, b) {
7642
+ return (a ?? "") === (b ?? "");
7643
+ }
7644
+ function withEventParentId(event, parentId) {
7645
+ if (!parentId) return event;
7646
+ if (event.type === "stdout" || event.type === "thinking" || event.type === "tool_use" || event.type === "tool_result") {
7647
+ return { ...event, parentId: event.parentId ?? parentId };
7648
+ }
7649
+ return event;
7650
+ }
7651
+ function withEventsParentId(events, parentId) {
7652
+ if (!parentId) return events;
7653
+ return events.map((event) => withEventParentId(event, parentId));
7654
+ }
7655
+ function toolFilePath(input) {
7656
+ if (!input) return void 0;
7657
+ return str2(input.file_path) ?? str2(input.path) ?? str2(input.filePath) ?? str2(input.filename);
7658
+ }
7659
+ function countLines(text4) {
7660
+ if (!text4) return 0;
7661
+ return text4.split("\n").length;
7662
+ }
7663
+ function diffFromInput(input) {
7664
+ if (!input) return {};
7665
+ const oldS = str2(input.old_string) ?? str2(input.oldString);
7666
+ const newS = str2(input.new_string) ?? str2(input.newString) ?? str2(input.content);
7667
+ if (oldS != null || newS != null) {
7668
+ return {
7669
+ additions: countLines(newS),
7670
+ deletions: countLines(oldS)
7671
+ };
7672
+ }
7673
+ return {};
7674
+ }
7675
+ function parseDiffStat(result) {
7676
+ if (!result) return {};
7677
+ const paired = result.match(/\+(\d+)\s+-(\d+)/);
7678
+ if (paired) {
7679
+ return {
7680
+ additions: Number(paired[1]),
7681
+ deletions: Number(paired[2])
7682
+ };
7683
+ }
7684
+ const verbose = result.match(
7685
+ /(\d+)\s+insertions?(?:,\s*(\d+)\s+deletions?)?/i
7686
+ );
7687
+ if (verbose) {
7688
+ return {
7689
+ additions: Number(verbose[1]),
7690
+ deletions: verbose[2] != null ? Number(verbose[2]) : void 0
7691
+ };
7692
+ }
7693
+ return {};
7694
+ }
7695
+ function applyAgentEvent(parts, event) {
7696
+ if (event.type === "stdout") {
7697
+ const data = event.data;
7698
+ if (!data) return parts;
7699
+ const next = [...parts];
7700
+ const last = next[next.length - 1];
7701
+ if (last?.type === "text" && sameParentId(last.parentId, event.parentId)) {
7702
+ next[next.length - 1] = {
7703
+ type: "text",
7704
+ text: last.text + data,
7705
+ ...event.parentId ? { parentId: event.parentId } : {}
7706
+ };
7707
+ } else {
7708
+ next.push({
7709
+ type: "text",
7710
+ text: data,
7711
+ ...event.parentId ? { parentId: event.parentId } : {}
7712
+ });
7713
+ }
7714
+ return next;
7715
+ }
7716
+ if (event.type === "thinking") {
7717
+ const data = event.data;
7718
+ if (!data) return parts;
7719
+ const next = [...parts];
7720
+ const last = next[next.length - 1];
7721
+ if (last?.type === "thinking" && sameParentId(last.parentId, event.parentId)) {
7722
+ next[next.length - 1] = {
7723
+ type: "thinking",
7724
+ text: last.text + data,
7725
+ ...event.parentId ? { parentId: event.parentId } : {}
7726
+ };
7727
+ } else {
7728
+ next.push({
7729
+ type: "thinking",
7730
+ text: data,
7731
+ ...event.parentId ? { parentId: event.parentId } : {}
7732
+ });
7733
+ }
7734
+ return next;
7735
+ }
7736
+ if (event.type === "tool_use") {
7737
+ const input = asRecord(event.input);
7738
+ const diff = diffFromInput(input);
7739
+ const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
7740
+ if (existing >= 0) {
7741
+ const prev = parts[existing];
7742
+ const next = [...parts];
7743
+ const mergedInput = input && Object.keys(input).length > 0 ? input : prev.input ?? input;
7744
+ next[existing] = {
7745
+ ...prev,
7746
+ name: event.name || prev.name,
7747
+ input: mergedInput,
7748
+ description: toolDescription(event.name || prev.name, mergedInput),
7749
+ detail: toolDetail(event.name || prev.name, mergedInput) ?? prev.detail,
7750
+ filePath: toolFilePath(mergedInput) ?? prev.filePath,
7751
+ additions: diff.additions ?? prev.additions,
7752
+ deletions: diff.deletions ?? prev.deletions,
7753
+ parentId: event.parentId ?? prev.parentId
7754
+ };
7755
+ return next;
7756
+ }
7757
+ return [
7758
+ ...parts,
7759
+ {
7760
+ type: "tool",
7761
+ id: event.id,
7762
+ name: event.name,
7763
+ input,
7764
+ description: toolDescription(event.name, input),
7765
+ detail: toolDetail(event.name, input),
7766
+ status: "running",
7767
+ filePath: toolFilePath(input),
7768
+ ...event.parentId ? { parentId: event.parentId } : {},
7769
+ ...diff
7770
+ }
7771
+ ];
7772
+ }
7773
+ if (event.type === "tool_result") {
7774
+ const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
7775
+ const fromResult = parseDiffStat(event.content);
7776
+ if (existing < 0) {
7777
+ return [
7778
+ ...parts,
7779
+ {
7780
+ type: "tool",
7781
+ id: event.id,
7782
+ name: "tool",
7783
+ description: "tool",
7784
+ status: event.isError ? "error" : "done",
7785
+ result: event.content,
7786
+ ...event.parentId ? { parentId: event.parentId } : {},
7787
+ ...fromResult.additions != null ? { additions: fromResult.additions } : {},
7788
+ ...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
7789
+ }
7790
+ ];
7791
+ }
7792
+ return parts.map((p, i) => {
7793
+ if (i !== existing || p.type !== "tool") return p;
7794
+ return {
7795
+ ...p,
7796
+ status: event.isError ? "error" : "done",
7797
+ result: event.content,
7798
+ ...fromResult.additions != null ? { additions: fromResult.additions } : {},
7799
+ ...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
7800
+ };
7801
+ });
7802
+ }
7803
+ return parts;
7804
+ }
7805
+ function partsToAssistantText(parts) {
7806
+ return parts.filter(
7807
+ (p) => p.type === "text" && !messagePartParentId(p)
7808
+ ).map((p) => p.text).join("").trim();
7809
+ }
7810
+ function stripBrightsyNdjsonNoise(text4) {
7811
+ if (!text4 || !text4.includes('"type"')) return text4;
7812
+ let out = text4;
7813
+ out = out.replace(
7814
+ /\{"type":"(?:tool_use|tool_result|tool|thinking|usage|done|error)"[\s\S]*?\}\s*(?=\{"type":"|$|(?=[A-Za-z*#]))/g,
7815
+ ""
7816
+ );
7817
+ out = out.replace(/\{"type":"(?:tool_use|tool_result|tool)"[\s\S]*$/g, "");
7818
+ return out.trim();
7819
+ }
7820
+ function isBrightsyNdjsonLine(line) {
7821
+ const t = line.trim();
7822
+ if (!t.startsWith("{")) return false;
7823
+ return /"type"\s*:\s*"(tool_use|tool_result|tool|thinking|usage|done|error|text)"/.test(t);
7824
+ }
7825
+ function finalizeParts(parts) {
7826
+ return parts.map(
7827
+ (p) => p.type === "tool" && p.status === "running" ? { ...p, status: "done" } : p
7828
+ );
7829
+ }
7830
+ function normalizeParseResult(parsed) {
7831
+ if (!parsed) return [];
7832
+ return Array.isArray(parsed) ? parsed : [parsed];
7833
+ }
7834
+ var init_message_parts = __esm({
7835
+ "src/agents/message-parts.ts"() {
7836
+ "use strict";
7837
+ }
7838
+ });
7839
+
7534
7840
  // src/mcp/profile.ts
7535
7841
  function sideboardMcpProfile(env = process.env) {
7536
7842
  return env[SIDEBOARD_MCP_PROFILE_ENV]?.trim().toLowerCase() === "worktree" ? "worktree" : "orchestration";
@@ -8170,42 +8476,59 @@ function claudeResultErrorDetail(obj) {
8170
8476
  }
8171
8477
  return "Claude turn failed";
8172
8478
  }
8173
- function eventsFromContentBlocks(blocks) {
8479
+ function eventsFromContentBlocks(blocks, parentId) {
8174
8480
  if (!blocks?.length) return [];
8175
8481
  const out = [];
8176
8482
  for (const block of blocks) {
8177
8483
  if (!block?.type) continue;
8178
8484
  if (block.type === "text" && block.text) {
8179
- out.push({ type: "stdout", data: block.text });
8485
+ out.push(withEventParentId({ type: "stdout", data: block.text }, parentId));
8180
8486
  continue;
8181
8487
  }
8182
8488
  if ((block.type === "thinking" || block.type === "redacted_thinking") && block.thinking) {
8183
- out.push({ type: "thinking", data: block.thinking });
8489
+ out.push(withEventParentId({ type: "thinking", data: block.thinking }, parentId));
8184
8490
  continue;
8185
8491
  }
8186
8492
  if (block.type === "tool_use" && block.id && block.name) {
8187
- out.push({
8188
- type: "tool_use",
8189
- id: block.id,
8190
- name: block.name,
8191
- input: block.input
8192
- });
8493
+ out.push(
8494
+ withEventParentId(
8495
+ {
8496
+ type: "tool_use",
8497
+ id: block.id,
8498
+ name: block.name,
8499
+ input: block.input
8500
+ },
8501
+ parentId
8502
+ )
8503
+ );
8193
8504
  continue;
8194
8505
  }
8195
8506
  if (block.type === "tool_result" && block.tool_use_id) {
8196
8507
  const content = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map(
8197
8508
  (c) => typeof c === "string" ? c : c && typeof c === "object" && "text" in c ? String(c.text ?? "") : ""
8198
8509
  ).join("") : block.content != null ? JSON.stringify(block.content) : void 0;
8199
- out.push({
8200
- type: "tool_result",
8201
- id: block.tool_use_id,
8202
- content,
8203
- isError: Boolean(block.is_error)
8204
- });
8510
+ out.push(
8511
+ withEventParentId(
8512
+ {
8513
+ type: "tool_result",
8514
+ id: block.tool_use_id,
8515
+ content,
8516
+ isError: Boolean(block.is_error)
8517
+ },
8518
+ parentId
8519
+ )
8520
+ );
8205
8521
  }
8206
8522
  }
8207
8523
  return out;
8208
8524
  }
8525
+ function claudeParentToolUseId(obj) {
8526
+ const nested = obj.event && typeof obj.event === "object" && !Array.isArray(obj.event) ? obj.event : void 0;
8527
+ for (const id of [obj.parent_tool_use_id, nested?.parent_tool_use_id]) {
8528
+ if (typeof id === "string" && id.trim()) return id.trim();
8529
+ }
8530
+ return void 0;
8531
+ }
8209
8532
  function parseIssuesJson(raw) {
8210
8533
  const text4 = raw.trim();
8211
8534
  const candidates = [text4];
@@ -8240,6 +8563,7 @@ var init_claude = __esm({
8240
8563
  init_app_settings();
8241
8564
  init_claude_mcp();
8242
8565
  init_error_detail();
8566
+ init_message_parts();
8243
8567
  init_injected_mcp();
8244
8568
  init_turn_input();
8245
8569
  init_types();
@@ -8251,7 +8575,10 @@ var init_claude = __esm({
8251
8575
  "Glob",
8252
8576
  "Grep",
8253
8577
  "WebFetch",
8254
- "WebSearch"
8578
+ "WebSearch",
8579
+ // Subagents (Claude Code v2.1.63 renamed Task → Agent; allow both).
8580
+ "Task",
8581
+ "Agent"
8255
8582
  ];
8256
8583
  CLAUDE_CHROME_ALLOWED_TOOLS = [
8257
8584
  "mcp__claude-in-chrome",
@@ -8379,7 +8706,9 @@ var init_claude = __esm({
8379
8706
  args,
8380
8707
  cwd: thread.worktreePath,
8381
8708
  stdin: useStdin ? `${promptText}
8382
- ` : void 0
8709
+ ` : void 0,
8710
+ // Nested Task/Agent thinking+text in stream-json (Claude Code 2.1.211+).
8711
+ env: { CLAUDE_CODE_FORWARD_SUBAGENT_TEXT: "1" }
8383
8712
  };
8384
8713
  },
8385
8714
  parseEvent(line) {
@@ -8396,8 +8725,9 @@ var init_claude = __esm({
8396
8725
  return { type: "session_id", data: obj.session_id };
8397
8726
  }
8398
8727
  if (obj.type === "assistant" || obj.type === "user") {
8728
+ const parentId = claudeParentToolUseId(obj);
8399
8729
  const message = obj.message;
8400
- const events = eventsFromContentBlocks(message?.content);
8730
+ const events = eventsFromContentBlocks(message?.content, parentId);
8401
8731
  if (obj.type === "assistant") {
8402
8732
  const usage = usageFromClaude(message?.usage);
8403
8733
  if (usage) events.push({ type: "usage", data: usage, scope: "request" });
@@ -8406,34 +8736,45 @@ var init_claude = __esm({
8406
8736
  return events.length === 1 ? events[0] : events;
8407
8737
  }
8408
8738
  if (obj.type === "stream_event") {
8739
+ const parentId = claudeParentToolUseId(obj);
8409
8740
  const event = obj.event;
8410
8741
  if (!event) return null;
8411
8742
  if (event.type === "content_block_start" && event.content_block) {
8412
8743
  const block = event.content_block;
8413
8744
  if (block.type === "tool_use" && block.id && block.name) {
8414
- return {
8415
- type: "tool_use",
8416
- id: block.id,
8417
- name: block.name,
8418
- input: block.input
8419
- };
8745
+ return withEventParentId(
8746
+ {
8747
+ type: "tool_use",
8748
+ id: block.id,
8749
+ name: block.name,
8750
+ input: block.input
8751
+ },
8752
+ parentId
8753
+ );
8420
8754
  }
8421
8755
  if (block.type === "thinking" && block.thinking) {
8422
- return { type: "thinking", data: block.thinking };
8756
+ return withEventParentId({ type: "thinking", data: block.thinking }, parentId);
8423
8757
  }
8424
8758
  return null;
8425
8759
  }
8426
8760
  if (event.type === "content_block_delta" && event.delta) {
8427
- if (event.delta.text) return { type: "stdout", data: event.delta.text };
8428
- if (event.delta.thinking) return { type: "thinking", data: event.delta.thinking };
8761
+ if (event.delta.text) {
8762
+ return withEventParentId({ type: "stdout", data: event.delta.text }, parentId);
8763
+ }
8764
+ if (event.delta.thinking) {
8765
+ return withEventParentId({ type: "thinking", data: event.delta.thinking }, parentId);
8766
+ }
8429
8767
  return null;
8430
8768
  }
8431
8769
  return null;
8432
8770
  }
8433
8771
  if (obj.type === "content_block_delta") {
8772
+ const parentId = claudeParentToolUseId(obj);
8434
8773
  const delta = obj.delta;
8435
- if (delta?.text) return { type: "stdout", data: delta.text };
8436
- if (delta?.thinking) return { type: "thinking", data: delta.thinking };
8774
+ if (delta?.text) return withEventParentId({ type: "stdout", data: delta.text }, parentId);
8775
+ if (delta?.thinking) {
8776
+ return withEventParentId({ type: "thinking", data: delta.thinking }, parentId);
8777
+ }
8437
8778
  return null;
8438
8779
  }
8439
8780
  if (obj.type === "result") {
@@ -8566,7 +8907,7 @@ function unwrapCodexMcpResult(result) {
8566
8907
  return String(result);
8567
8908
  }
8568
8909
  }
8569
- function asRecord(value) {
8910
+ function asRecord2(value) {
8570
8911
  if (value && typeof value === "object" && !Array.isArray(value)) {
8571
8912
  return value;
8572
8913
  }
@@ -8731,7 +9072,7 @@ var init_codex = __esm({
8731
9072
  }
8732
9073
  if (itemType === "mcp_tool_call" && item.id && item.server && item.tool) {
8733
9074
  const name = `mcp__${item.server}__${item.tool}`;
8734
- const input = asRecord(item.arguments);
9075
+ const input = asRecord2(item.arguments);
8735
9076
  const events = [
8736
9077
  { type: "tool_use", id: item.id, name, input }
8737
9078
  ];
@@ -8762,6 +9103,36 @@ var init_codex = __esm({
8762
9103
  }
8763
9104
  return events.length === 1 ? events[0] : events;
8764
9105
  }
9106
+ if (itemType === "collab_tool_call" && item.id) {
9107
+ const rawTool = typeof item.tool === "string" ? item.tool : "spawn_agent";
9108
+ const name = /spawn/i.test(rawTool) ? "spawn_agent" : rawTool;
9109
+ const input = {};
9110
+ if (item.prompt) input.prompt = item.prompt;
9111
+ if (Array.isArray(item.receiver_thread_ids)) {
9112
+ input.receiver_thread_ids = item.receiver_thread_ids;
9113
+ }
9114
+ const events = [{ type: "tool_use", id: item.id, name, input }];
9115
+ const finished = type === "item.completed" || item.status === "completed" || item.status === "failed";
9116
+ if (finished) {
9117
+ const nested = [];
9118
+ if (item.agents_states && typeof item.agents_states === "object") {
9119
+ for (const value of Object.values(item.agents_states)) {
9120
+ const msg = value && typeof value === "object" && typeof value.message === "string" ? value.message.trim() : "";
9121
+ if (msg) nested.push(msg);
9122
+ }
9123
+ }
9124
+ for (const text4 of nested) {
9125
+ events.push({ type: "stdout", data: text4, parentId: item.id });
9126
+ }
9127
+ events.push({
9128
+ type: "tool_result",
9129
+ id: item.id,
9130
+ content: nested.join("\n") || void 0,
9131
+ isError: item.status === "failed"
9132
+ });
9133
+ }
9134
+ return events.length === 1 ? events[0] : events;
9135
+ }
8765
9136
  if (item.status === "failed") {
8766
9137
  const detail = item.message?.trim() || extractJsonErrorMessage(item) || `Codex ${itemType ?? "item"} failed`;
8767
9138
  return { type: "stderr", data: detail };
@@ -8850,20 +9221,20 @@ function usageFromCursor(usage) {
8850
9221
  cacheWriteTokens: usage.cacheWriteTokens ? Number(usage.cacheWriteTokens) : void 0
8851
9222
  };
8852
9223
  }
8853
- function asRecord2(value) {
9224
+ function asRecord3(value) {
8854
9225
  if (value && typeof value === "object" && !Array.isArray(value)) {
8855
9226
  return value;
8856
9227
  }
8857
9228
  return void 0;
8858
9229
  }
8859
9230
  function normalizeCursorToolCall(name, args) {
8860
- const raw = asRecord2(args);
9231
+ const raw = asRecord3(args);
8861
9232
  const toolName = typeof raw?.toolName === "string" && raw.toolName.trim() ? raw.toolName.trim() : null;
8862
- const looksLikeMcp = name === "mcp" || toolName != null && (typeof raw?.providerIdentifier === "string" || asRecord2(raw?.args) != null);
9233
+ const looksLikeMcp = name === "mcp" || toolName != null && (typeof raw?.providerIdentifier === "string" || asRecord3(raw?.args) != null);
8863
9234
  if (looksLikeMcp && toolName) {
8864
9235
  const providerRaw = typeof raw.providerIdentifier === "string" && raw.providerIdentifier.trim() ? raw.providerIdentifier.trim() : "sideboard";
8865
9236
  const provider = providerRaw.replace(/[^a-zA-Z0-9_-]/g, "_");
8866
- const nested = asRecord2(raw.args);
9237
+ const nested = asRecord3(raw.args);
8867
9238
  return {
8868
9239
  name: `mcp__${provider}__${toolName}`,
8869
9240
  input: nested ?? { toolName }
@@ -8874,7 +9245,7 @@ function normalizeCursorToolCall(name, args) {
8874
9245
  function unwrapCursorToolResult(result) {
8875
9246
  if (result == null) return void 0;
8876
9247
  if (typeof result === "string") return result;
8877
- const rec = asRecord2(result);
9248
+ const rec = asRecord3(result);
8878
9249
  if (!rec) {
8879
9250
  try {
8880
9251
  return JSON.stringify(result);
@@ -8885,18 +9256,18 @@ function unwrapCursorToolResult(result) {
8885
9256
  const collectTexts = (items) => {
8886
9257
  const texts = [];
8887
9258
  for (const item of items) {
8888
- const row = asRecord2(item);
9259
+ const row = asRecord3(item);
8889
9260
  if (!row) continue;
8890
9261
  if (typeof row.text === "string") {
8891
9262
  texts.push(row.text);
8892
9263
  continue;
8893
9264
  }
8894
- const nested = asRecord2(row.text);
9265
+ const nested = asRecord3(row.text);
8895
9266
  if (typeof nested?.text === "string") texts.push(nested.text);
8896
9267
  }
8897
9268
  return texts;
8898
9269
  };
8899
- const value = asRecord2(rec.value);
9270
+ const value = asRecord3(rec.value);
8900
9271
  if (value && Array.isArray(value.content)) {
8901
9272
  const texts = collectTexts(value.content);
8902
9273
  if (texts.length) return texts.join("\n");
@@ -8913,10 +9284,10 @@ function unwrapCursorToolResult(result) {
8913
9284
  }
8914
9285
  function cursorToolResultIsError(status, result) {
8915
9286
  if (status === "error") return true;
8916
- const rec = asRecord2(result);
9287
+ const rec = asRecord3(result);
8917
9288
  if (!rec) return false;
8918
9289
  if (rec.status === "error") return true;
8919
- const value = asRecord2(rec.value);
9290
+ const value = asRecord3(rec.value);
8920
9291
  return value?.isError === true;
8921
9292
  }
8922
9293
  function cursorSdkMessageToEvents(msg) {
@@ -9259,6 +9630,24 @@ var init_cursor = __esm({
9259
9630
  });
9260
9631
 
9261
9632
  // src/agents/opencode.ts
9633
+ function asRecord4(value) {
9634
+ if (value && typeof value === "object" && !Array.isArray(value)) {
9635
+ return value;
9636
+ }
9637
+ return void 0;
9638
+ }
9639
+ function str3(v) {
9640
+ return typeof v === "string" && v.trim() ? v.trim() : void 0;
9641
+ }
9642
+ function opencodeToolId(name, callId, metadata) {
9643
+ const sessionId = str3(metadata?.sessionId) ?? str3(metadata?.sessionID);
9644
+ if (name === "task" && sessionId) return sessionId;
9645
+ return callId || `tool-${Date.now()}`;
9646
+ }
9647
+ function withTaskParent(parentId, nested) {
9648
+ const events = Array.isArray(nested) ? nested : [nested];
9649
+ return [{ type: "tool_use", id: parentId, name: "task" }, ...events];
9650
+ }
9262
9651
  function displayNameFromOpencodeId(id) {
9263
9652
  const slash = id.indexOf("/");
9264
9653
  if (slash <= 0) return id;
@@ -9325,6 +9714,7 @@ var init_opencode = __esm({
9325
9714
  init_app_settings();
9326
9715
  init_global_workspace();
9327
9716
  init_error_detail();
9717
+ init_message_parts();
9328
9718
  init_injected_mcp();
9329
9719
  init_turn_input();
9330
9720
  init_types();
@@ -9441,10 +9831,15 @@ var init_opencode = __esm({
9441
9831
  }
9442
9832
  if (obj.type === "tool_use") {
9443
9833
  const part = obj.part;
9444
- const id = part?.callID ?? part?.id ?? obj.id ?? `tool-${Date.now()}`;
9445
9834
  const name = part?.tool ?? part?.name ?? obj.tool ?? obj.name ?? "tool";
9446
9835
  const state = part?.state;
9447
9836
  const input = (state?.input && typeof state.input === "object" ? state.input : void 0) ?? part?.input ?? obj.input;
9837
+ const metadata = asRecord4(state?.metadata);
9838
+ const id = opencodeToolId(
9839
+ name,
9840
+ part?.callID ?? part?.id ?? obj.id,
9841
+ metadata
9842
+ );
9448
9843
  const events = [{ type: "tool_use", id, name, input }];
9449
9844
  const output = state?.output;
9450
9845
  if (output != null || state?.status === "completed" || state?.status === "error") {
@@ -9458,6 +9853,53 @@ var init_opencode = __esm({
9458
9853
  }
9459
9854
  return events.length === 1 ? events[0] : events;
9460
9855
  }
9856
+ if (obj.type === "subtask_delta") {
9857
+ const parentId = str3(obj.childSessionID) ?? str3(obj.childSessionId);
9858
+ const delta = str3(obj.delta);
9859
+ if (parentId && delta) {
9860
+ return withTaskParent(
9861
+ parentId,
9862
+ withEventParentId({ type: "thinking", data: delta }, parentId)
9863
+ );
9864
+ }
9865
+ return null;
9866
+ }
9867
+ if (obj.type === "subtask_event") {
9868
+ const parentId = str3(obj.childSessionID) ?? str3(obj.childSessionId);
9869
+ const part = asRecord4(obj.part);
9870
+ if (!parentId || !part) return null;
9871
+ if (part.type === "text" && str3(part.text)) {
9872
+ return withTaskParent(
9873
+ parentId,
9874
+ withEventParentId({ type: "stdout", data: str3(part.text) }, parentId)
9875
+ );
9876
+ }
9877
+ if (part.type === "tool" || str3(part.tool)) {
9878
+ const name = str3(part.tool) ?? str3(part.name) ?? "tool";
9879
+ const id = str3(part.callID) ?? str3(part.id) ?? `${parentId}-${name}`;
9880
+ const state = asRecord4(part.state);
9881
+ const input = asRecord4(state?.input) ?? asRecord4(part.input);
9882
+ const nested = [
9883
+ withEventParentId({ type: "tool_use", id, name, input }, parentId)
9884
+ ];
9885
+ const output = state?.output ?? part.output;
9886
+ if (output != null || state?.status === "completed" || state?.status === "error") {
9887
+ nested.push(
9888
+ withEventParentId(
9889
+ {
9890
+ type: "tool_result",
9891
+ id,
9892
+ content: typeof output === "string" ? output : output != null ? JSON.stringify(output) : void 0,
9893
+ isError: state?.status === "error" || state?.status === "failed"
9894
+ },
9895
+ parentId
9896
+ )
9897
+ );
9898
+ }
9899
+ return withTaskParent(parentId, nested);
9900
+ }
9901
+ return null;
9902
+ }
9461
9903
  if (obj.type === "tool_result") {
9462
9904
  const part = obj.part;
9463
9905
  const id = part?.tool_use_id ?? part?.id ?? obj.tool_use_id ?? obj.id;
@@ -9887,422 +10329,189 @@ async function installAgent(agent) {
9887
10329
  };
9888
10330
  }
9889
10331
  }
9890
- enrichPathWithNpmGlobalBin();
9891
- let binPath = null;
9892
- if (cliBin) {
9893
- binPath = await whichCli(cliBin);
9894
- }
9895
- return {
9896
- ok: true,
9897
- command: info.installCommand,
9898
- exitCode: 0,
9899
- stdout: result.stdout,
9900
- stderr: result.stderr,
9901
- 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)`
9902
- };
9903
- }
9904
- async function loginAgent(agent) {
9905
- const info = getAgentSetupInfo(agent);
9906
- const loginCommand = resolveLoginCommand(agent);
9907
- if (!loginCommand) {
9908
- return {
9909
- ok: true,
9910
- message: info.kind === "bundled-sdk" || info.kind === "api-key" ? info.summary : `No login command for ${agent}`
9911
- };
9912
- }
9913
- const prepared = await prepareTerminalCommand(loginCommand);
9914
- await openInSystemTerminal(prepared);
9915
- return {
9916
- ok: true,
9917
- openedTerminal: true,
9918
- command: prepared,
9919
- message: `Opened Terminal to run: ${prepared}`
9920
- };
9921
- }
9922
- var SETUP, CLI_BIN, CLI_LOGIN_AGENTS;
9923
- var init_install = __esm({
9924
- "src/agents/install.ts"() {
9925
- "use strict";
9926
- init_run();
9927
- init_app_settings();
9928
- init_path();
9929
- SETUP = {
9930
- claude: {
9931
- agent: "claude",
9932
- kind: "cli",
9933
- summary: "Install the Claude Code CLI, then complete login in a terminal.",
9934
- docsUrl: "https://code.claude.com/docs/en/install",
9935
- installCommand: "npm install -g @anthropic-ai/claude-code",
9936
- loginCommand: "claude auth login",
9937
- npmPackage: "@anthropic-ai/claude-code"
9938
- },
9939
- codex: {
9940
- agent: "codex",
9941
- kind: "cli",
9942
- summary: "Install the Codex CLI, then run login in a terminal.",
9943
- docsUrl: "https://github.com/openai/codex",
9944
- installCommand: "npm install -g @openai/codex",
9945
- loginCommand: "codex login",
9946
- npmPackage: "@openai/codex"
9947
- },
9948
- opencode: {
9949
- agent: "opencode",
9950
- kind: "cli",
9951
- summary: "Install OpenCode (curl installer or npm), then authenticate providers.",
9952
- docsUrl: "https://opencode.ai/docs",
9953
- // Prefer the official installer; npm package name is opencode-ai.
9954
- installCommand: "curl -fsSL https://opencode.ai/install | bash",
9955
- loginCommand: "opencode auth login",
9956
- npmPackage: "opencode-ai@latest"
9957
- },
9958
- cursor: {
9959
- agent: "cursor",
9960
- kind: "bundled-sdk",
9961
- summary: "No CLI install \u2014 Sideboard ships the Cursor SDK. Add a CURSOR_API_KEY from the Cursor dashboard.",
9962
- docsUrl: "https://cursor.com/dashboard/integrations",
9963
- installCommand: null,
9964
- loginCommand: null
9965
- },
9966
- brightsy: {
9967
- agent: "brightsy",
9968
- kind: "cli",
9969
- summary: "Install the Brightsy CLI, then run `brightsy login`.",
9970
- docsUrl: "https://www.npmjs.com/package/@brightsy/cli",
9971
- installCommand: "npm install -g @brightsy/cli",
9972
- loginCommand: "brightsy login",
9973
- npmPackage: "@brightsy/cli"
9974
- }
9975
- };
9976
- CLI_BIN = {
9977
- claude: "claude",
9978
- codex: "codex",
9979
- opencode: "opencode",
9980
- brightsy: "brightsy"
9981
- };
9982
- CLI_LOGIN_AGENTS = /* @__PURE__ */ new Set([
9983
- "claude",
9984
- "codex",
9985
- "opencode",
9986
- "brightsy"
9987
- ]);
9988
- }
9989
- });
9990
-
9991
- // src/agents/index.ts
9992
- var agents_exports = {};
9993
- __export(agents_exports, {
9994
- CLAUDE_MODEL_CATALOG: () => CLAUDE_MODEL_CATALOG,
9995
- ORCHESTRATOR_AGENT_KINDS: () => ORCHESTRATOR_AGENT_KINDS,
9996
- PLAN_MODE_INSTRUCTION: () => PLAN_MODE_INSTRUCTION,
9997
- allAdapters: () => allAdapters,
9998
- assertOrchestratorCapableAgent: () => assertOrchestratorCapableAgent,
9999
- brightsyAdapter: () => brightsyAdapter,
10000
- claudeAdapter: () => claudeAdapter,
10001
- codexAdapter: () => codexAdapter,
10002
- coerceOrchestratorAgent: () => coerceOrchestratorAgent,
10003
- conductorBundledBinDir: () => conductorBundledBinDir,
10004
- cursorAdapter: () => cursorAdapter,
10005
- cursorSdkMessageToEvents: () => cursorSdkMessageToEvents,
10006
- decodeBrightsyTarget: () => decodeBrightsyTarget,
10007
- encodeBrightsyTarget: () => encodeBrightsyTarget,
10008
- enrichPathWithNpmGlobalBin: () => enrichPathWithNpmGlobalBin,
10009
- ensureAgentPath: () => ensureAgentPath,
10010
- getAdapter: () => getAdapter,
10011
- getAgentSetupInfo: () => getAgentSetupInfo,
10012
- installAgent: () => installAgent,
10013
- isConductorBundledCli: () => isConductorBundledCli,
10014
- isCursorAutoModel: () => isCursorAutoModel,
10015
- isOrchestratorCapableAgent: () => isOrchestratorCapableAgent,
10016
- isSessionQuotaLimit: () => isSessionQuotaLimit,
10017
- listAgentSetupInfo: () => listAgentSetupInfo,
10018
- listBrightsyChatTargets: () => listBrightsyChatTargets,
10019
- listCodexModels: () => listCodexModels,
10020
- listCursorModels: () => listCursorModels,
10021
- listModelsForAgent: () => listModelsForAgent,
10022
- listOpencodeModels: () => listOpencodeModels,
10023
- loginAgent: () => loginAgent,
10024
- openInSystemTerminal: () => openInSystemTerminal,
10025
- opencodeAdapter: () => opencodeAdapter,
10026
- parseCursorRunnerLine: () => parseCursorRunnerLine,
10027
- parseSessionQuotaResetAt: () => parseSessionQuotaResetAt,
10028
- permissionMode: () => permissionMode,
10029
- posixShellSingleQuote: () => posixShellSingleQuote,
10030
- prepareTerminalCommand: () => prepareTerminalCommand,
10031
- resolveCommandBinarySync: () => resolveCommandBinarySync,
10032
- resolveCursorModelId: () => resolveCursorModelId,
10033
- resolveLoginCommand: () => resolveLoginCommand,
10034
- resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent,
10035
- withExportedPath: () => withExportedPath
10036
- });
10037
- function getAdapter(kind) {
10038
- return adapters[kind];
10039
- }
10040
- function allAdapters() {
10041
- return Object.values(adapters);
10042
- }
10043
- var adapters;
10044
- var init_agents = __esm({
10045
- "src/agents/index.ts"() {
10046
- "use strict";
10047
- init_brightsy();
10048
- init_claude();
10049
- init_codex();
10050
- init_cursor();
10051
- init_opencode();
10052
- init_types();
10053
- init_brightsy();
10054
- init_brightsy_targets();
10055
- init_claude();
10056
- init_codex();
10057
- init_cursor();
10058
- init_cursor_events();
10059
- init_cursor();
10060
- init_opencode();
10061
- init_list_models();
10062
- init_session_quota();
10063
- init_orchestrator_capable();
10064
- init_path();
10065
- init_install();
10066
- adapters = {
10067
- claude: claudeAdapter,
10068
- codex: codexAdapter,
10069
- opencode: opencodeAdapter,
10070
- brightsy: brightsyAdapter,
10071
- cursor: cursorAdapter
10072
- };
10073
- }
10074
- });
10075
-
10076
- // src/agents/message-parts.ts
10077
- function asRecord3(input) {
10078
- if (input && typeof input === "object" && !Array.isArray(input)) {
10079
- return input;
10080
- }
10081
- return void 0;
10082
- }
10083
- function str2(v) {
10084
- return typeof v === "string" && v.trim() ? v : void 0;
10085
- }
10086
- function toolDetail(name, input) {
10087
- if (!input) return void 0;
10088
- const command = str2(input.command) ?? str2(input.cmd);
10089
- if (command) return command;
10090
- const path2 = str2(input.file_path) ?? str2(input.path) ?? str2(input.filePath) ?? str2(input.filename);
10091
- if (path2) return path2;
10092
- const pattern = str2(input.pattern) ?? str2(input.glob) ?? str2(input.glob_pattern);
10093
- if (pattern) return pattern;
10094
- const query = str2(input.query) ?? str2(input.prompt);
10095
- if (query) return query.length > 80 ? `${query.slice(0, 77)}\u2026` : query;
10096
- try {
10097
- const raw = JSON.stringify(input);
10098
- return raw.length > 80 ? `${raw.slice(0, 77)}\u2026` : raw;
10099
- } catch {
10100
- return name;
10101
- }
10102
- }
10103
- function toolDescription(name, input) {
10104
- const n = name.replace(/^mcp__/, "").replace(/__/g, " \xB7 ");
10105
- if (/^get_record_types$|recordTypes/i.test(name)) return "List record types";
10106
- if (/connectedAgentRequest/i.test(name)) {
10107
- return str2(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
10108
- }
10109
- if (/present_artifact$/i.test(name)) {
10110
- return str2(input?.title) ? `Present ${str2(input?.title)}` : "Present artifact";
10111
- }
10112
- if (/present_plan$/i.test(name)) {
10113
- return str2(input?.title) ? `Plan ${str2(input?.title)}` : "Present plan";
10114
- }
10115
- if (/present_schema$/i.test(name)) {
10116
- return str2(input?.title) ? `Schema ${str2(input?.title)}` : "Present schema";
10117
- }
10118
- if (/present_files$/i.test(name)) {
10119
- return str2(input?.title) ? `Files ${str2(input?.title)}` : "Present files";
10120
- }
10121
- if (/^(create|update)_artifact$/i.test(name.replace(/^mcp__[^_]+__/, ""))) {
10122
- return str2(input?.title) ? `Artifact ${str2(input?.title)}` : "Artifact";
10123
- }
10124
- if (!input) return n;
10125
- if (/bash|shell|terminal/i.test(name) && str2(input.command)) {
10126
- const cmd = str2(input.command);
10127
- if (/git\s+fetch/i.test(cmd)) return "Fetch latest from origin and check status";
10128
- if (/git\s+status/i.test(cmd)) return "Check git status";
10129
- if (/git\s+log/i.test(cmd)) return "Inspect recent commits";
10130
- if (/git\s+branch/i.test(cmd)) return "List branches";
10131
- if (/grep|rg\b/i.test(cmd)) return "Search repository";
10132
- return "Run shell command";
10133
- }
10134
- if (/edit|write|apply/i.test(name)) {
10135
- const path2 = str2(input.file_path) ?? str2(input.path);
10136
- return path2 ? `Edit ${path2.split("/").pop()}` : "Edit file";
10137
- }
10138
- if (/read/i.test(name)) {
10139
- const path2 = str2(input.file_path) ?? str2(input.path);
10140
- return path2 ? `Read ${path2.split("/").pop()}` : "Read file";
10141
- }
10142
- if (/grep/i.test(name)) return "Search files";
10143
- if (/glob/i.test(name)) return "Find files";
10144
- return n;
10145
- }
10146
- function toolFilePath(input) {
10147
- if (!input) return void 0;
10148
- return str2(input.file_path) ?? str2(input.path) ?? str2(input.filePath) ?? str2(input.filename);
10149
- }
10150
- function countLines(text4) {
10151
- if (!text4) return 0;
10152
- return text4.split("\n").length;
10153
- }
10154
- function diffFromInput(input) {
10155
- if (!input) return {};
10156
- const oldS = str2(input.old_string) ?? str2(input.oldString);
10157
- const newS = str2(input.new_string) ?? str2(input.newString) ?? str2(input.content);
10158
- if (oldS != null || newS != null) {
10159
- return {
10160
- additions: countLines(newS),
10161
- deletions: countLines(oldS)
10162
- };
10163
- }
10164
- return {};
10165
- }
10166
- function parseDiffStat(result) {
10167
- if (!result) return {};
10168
- const paired = result.match(/\+(\d+)\s+-(\d+)/);
10169
- if (paired) {
10170
- return {
10171
- additions: Number(paired[1]),
10172
- deletions: Number(paired[2])
10173
- };
10174
- }
10175
- const verbose = result.match(
10176
- /(\d+)\s+insertions?(?:,\s*(\d+)\s+deletions?)?/i
10177
- );
10178
- if (verbose) {
10179
- return {
10180
- additions: Number(verbose[1]),
10181
- deletions: verbose[2] != null ? Number(verbose[2]) : void 0
10182
- };
10183
- }
10184
- return {};
10185
- }
10186
- function applyAgentEvent(parts, event) {
10187
- if (event.type === "stdout") {
10188
- const data = event.data;
10189
- if (!data) return parts;
10190
- const next = [...parts];
10191
- const last = next[next.length - 1];
10192
- if (last?.type === "text") {
10193
- next[next.length - 1] = { type: "text", text: last.text + data };
10194
- } else {
10195
- next.push({ type: "text", text: data });
10196
- }
10197
- return next;
10198
- }
10199
- if (event.type === "thinking") {
10200
- const data = event.data;
10201
- if (!data) return parts;
10202
- const next = [...parts];
10203
- const last = next[next.length - 1];
10204
- if (last?.type === "thinking") {
10205
- next[next.length - 1] = { type: "thinking", text: last.text + data };
10206
- } else {
10207
- next.push({ type: "thinking", text: data });
10208
- }
10209
- return next;
10210
- }
10211
- if (event.type === "tool_use") {
10212
- const input = asRecord3(event.input);
10213
- const diff = diffFromInput(input);
10214
- const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
10215
- if (existing >= 0) {
10216
- const prev = parts[existing];
10217
- const next = [...parts];
10218
- next[existing] = {
10219
- ...prev,
10220
- name: event.name || prev.name,
10221
- input: input ?? prev.input,
10222
- description: toolDescription(event.name || prev.name, input ?? prev.input),
10223
- detail: toolDetail(event.name || prev.name, input ?? prev.input) ?? prev.detail,
10224
- filePath: toolFilePath(input) ?? prev.filePath,
10225
- additions: diff.additions ?? prev.additions,
10226
- deletions: diff.deletions ?? prev.deletions
10227
- };
10228
- return next;
10229
- }
10230
- return [
10231
- ...parts,
10232
- {
10233
- type: "tool",
10234
- id: event.id,
10235
- name: event.name,
10236
- input,
10237
- description: toolDescription(event.name, input),
10238
- detail: toolDetail(event.name, input),
10239
- status: "running",
10240
- filePath: toolFilePath(input),
10241
- ...diff
10242
- }
10243
- ];
10244
- }
10245
- if (event.type === "tool_result") {
10246
- const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
10247
- const fromResult = parseDiffStat(event.content);
10248
- if (existing < 0) {
10249
- return [
10250
- ...parts,
10251
- {
10252
- type: "tool",
10253
- id: event.id,
10254
- name: "tool",
10255
- description: "tool",
10256
- status: event.isError ? "error" : "done",
10257
- result: event.content,
10258
- ...fromResult.additions != null ? { additions: fromResult.additions } : {},
10259
- ...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
10260
- }
10261
- ];
10262
- }
10263
- return parts.map((p, i) => {
10264
- if (i !== existing || p.type !== "tool") return p;
10265
- return {
10266
- ...p,
10267
- status: event.isError ? "error" : "done",
10268
- result: event.content,
10269
- ...fromResult.additions != null ? { additions: fromResult.additions } : {},
10270
- ...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
10271
- };
10272
- });
10273
- }
10274
- return parts;
10275
- }
10276
- function partsToAssistantText(parts) {
10277
- return parts.filter((p) => p.type === "text").map((p) => p.text).join("").trim();
10278
- }
10279
- function stripBrightsyNdjsonNoise(text4) {
10280
- if (!text4 || !text4.includes('"type"')) return text4;
10281
- let out = text4;
10282
- out = out.replace(
10283
- /\{"type":"(?:tool_use|tool_result|tool|thinking|usage|done|error)"[\s\S]*?\}\s*(?=\{"type":"|$|(?=[A-Za-z*#]))/g,
10284
- ""
10285
- );
10286
- out = out.replace(/\{"type":"(?:tool_use|tool_result|tool)"[\s\S]*$/g, "");
10287
- return out.trim();
10332
+ enrichPathWithNpmGlobalBin();
10333
+ let binPath = null;
10334
+ if (cliBin) {
10335
+ binPath = await whichCli(cliBin);
10336
+ }
10337
+ return {
10338
+ ok: true,
10339
+ command: info.installCommand,
10340
+ exitCode: 0,
10341
+ stdout: result.stdout,
10342
+ stderr: result.stderr,
10343
+ 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)`
10344
+ };
10288
10345
  }
10289
- function isBrightsyNdjsonLine(line) {
10290
- const t = line.trim();
10291
- if (!t.startsWith("{")) return false;
10292
- return /"type"\s*:\s*"(tool_use|tool_result|tool|thinking|usage|done|error|text)"/.test(t);
10346
+ async function loginAgent(agent) {
10347
+ const info = getAgentSetupInfo(agent);
10348
+ const loginCommand = resolveLoginCommand(agent);
10349
+ if (!loginCommand) {
10350
+ return {
10351
+ ok: true,
10352
+ message: info.kind === "bundled-sdk" || info.kind === "api-key" ? info.summary : `No login command for ${agent}`
10353
+ };
10354
+ }
10355
+ const prepared = await prepareTerminalCommand(loginCommand);
10356
+ await openInSystemTerminal(prepared);
10357
+ return {
10358
+ ok: true,
10359
+ openedTerminal: true,
10360
+ command: prepared,
10361
+ message: `Opened Terminal to run: ${prepared}`
10362
+ };
10293
10363
  }
10294
- function finalizeParts(parts) {
10295
- return parts.map(
10296
- (p) => p.type === "tool" && p.status === "running" ? { ...p, status: "done" } : p
10297
- );
10364
+ var SETUP, CLI_BIN, CLI_LOGIN_AGENTS;
10365
+ var init_install = __esm({
10366
+ "src/agents/install.ts"() {
10367
+ "use strict";
10368
+ init_run();
10369
+ init_app_settings();
10370
+ init_path();
10371
+ SETUP = {
10372
+ claude: {
10373
+ agent: "claude",
10374
+ kind: "cli",
10375
+ summary: "Install the Claude Code CLI, then complete login in a terminal.",
10376
+ docsUrl: "https://code.claude.com/docs/en/install",
10377
+ installCommand: "npm install -g @anthropic-ai/claude-code",
10378
+ loginCommand: "claude auth login",
10379
+ npmPackage: "@anthropic-ai/claude-code"
10380
+ },
10381
+ codex: {
10382
+ agent: "codex",
10383
+ kind: "cli",
10384
+ summary: "Install the Codex CLI, then run login in a terminal.",
10385
+ docsUrl: "https://github.com/openai/codex",
10386
+ installCommand: "npm install -g @openai/codex",
10387
+ loginCommand: "codex login",
10388
+ npmPackage: "@openai/codex"
10389
+ },
10390
+ opencode: {
10391
+ agent: "opencode",
10392
+ kind: "cli",
10393
+ summary: "Install OpenCode (curl installer or npm), then authenticate providers.",
10394
+ docsUrl: "https://opencode.ai/docs",
10395
+ // Prefer the official installer; npm package name is opencode-ai.
10396
+ installCommand: "curl -fsSL https://opencode.ai/install | bash",
10397
+ loginCommand: "opencode auth login",
10398
+ npmPackage: "opencode-ai@latest"
10399
+ },
10400
+ cursor: {
10401
+ agent: "cursor",
10402
+ kind: "bundled-sdk",
10403
+ summary: "No CLI install \u2014 Sideboard ships the Cursor SDK. Add a CURSOR_API_KEY from the Cursor dashboard.",
10404
+ docsUrl: "https://cursor.com/dashboard/integrations",
10405
+ installCommand: null,
10406
+ loginCommand: null
10407
+ },
10408
+ brightsy: {
10409
+ agent: "brightsy",
10410
+ kind: "cli",
10411
+ summary: "Install the Brightsy CLI, then run `brightsy login`.",
10412
+ docsUrl: "https://www.npmjs.com/package/@brightsy/cli",
10413
+ installCommand: "npm install -g @brightsy/cli",
10414
+ loginCommand: "brightsy login",
10415
+ npmPackage: "@brightsy/cli"
10416
+ }
10417
+ };
10418
+ CLI_BIN = {
10419
+ claude: "claude",
10420
+ codex: "codex",
10421
+ opencode: "opencode",
10422
+ brightsy: "brightsy"
10423
+ };
10424
+ CLI_LOGIN_AGENTS = /* @__PURE__ */ new Set([
10425
+ "claude",
10426
+ "codex",
10427
+ "opencode",
10428
+ "brightsy"
10429
+ ]);
10430
+ }
10431
+ });
10432
+
10433
+ // src/agents/index.ts
10434
+ var agents_exports = {};
10435
+ __export(agents_exports, {
10436
+ CLAUDE_MODEL_CATALOG: () => CLAUDE_MODEL_CATALOG,
10437
+ ORCHESTRATOR_AGENT_KINDS: () => ORCHESTRATOR_AGENT_KINDS,
10438
+ PLAN_MODE_INSTRUCTION: () => PLAN_MODE_INSTRUCTION,
10439
+ allAdapters: () => allAdapters,
10440
+ assertOrchestratorCapableAgent: () => assertOrchestratorCapableAgent,
10441
+ brightsyAdapter: () => brightsyAdapter,
10442
+ claudeAdapter: () => claudeAdapter,
10443
+ codexAdapter: () => codexAdapter,
10444
+ coerceOrchestratorAgent: () => coerceOrchestratorAgent,
10445
+ conductorBundledBinDir: () => conductorBundledBinDir,
10446
+ cursorAdapter: () => cursorAdapter,
10447
+ cursorSdkMessageToEvents: () => cursorSdkMessageToEvents,
10448
+ decodeBrightsyTarget: () => decodeBrightsyTarget,
10449
+ encodeBrightsyTarget: () => encodeBrightsyTarget,
10450
+ enrichPathWithNpmGlobalBin: () => enrichPathWithNpmGlobalBin,
10451
+ ensureAgentPath: () => ensureAgentPath,
10452
+ getAdapter: () => getAdapter,
10453
+ getAgentSetupInfo: () => getAgentSetupInfo,
10454
+ installAgent: () => installAgent,
10455
+ isConductorBundledCli: () => isConductorBundledCli,
10456
+ isCursorAutoModel: () => isCursorAutoModel,
10457
+ isOrchestratorCapableAgent: () => isOrchestratorCapableAgent,
10458
+ isSessionQuotaLimit: () => isSessionQuotaLimit,
10459
+ listAgentSetupInfo: () => listAgentSetupInfo,
10460
+ listBrightsyChatTargets: () => listBrightsyChatTargets,
10461
+ listCodexModels: () => listCodexModels,
10462
+ listCursorModels: () => listCursorModels,
10463
+ listModelsForAgent: () => listModelsForAgent,
10464
+ listOpencodeModels: () => listOpencodeModels,
10465
+ loginAgent: () => loginAgent,
10466
+ openInSystemTerminal: () => openInSystemTerminal,
10467
+ opencodeAdapter: () => opencodeAdapter,
10468
+ parseCursorRunnerLine: () => parseCursorRunnerLine,
10469
+ parseSessionQuotaResetAt: () => parseSessionQuotaResetAt,
10470
+ permissionMode: () => permissionMode,
10471
+ posixShellSingleQuote: () => posixShellSingleQuote,
10472
+ prepareTerminalCommand: () => prepareTerminalCommand,
10473
+ resolveCommandBinarySync: () => resolveCommandBinarySync,
10474
+ resolveCursorModelId: () => resolveCursorModelId,
10475
+ resolveLoginCommand: () => resolveLoginCommand,
10476
+ resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent,
10477
+ withExportedPath: () => withExportedPath
10478
+ });
10479
+ function getAdapter(kind) {
10480
+ return adapters[kind];
10298
10481
  }
10299
- function normalizeParseResult(parsed) {
10300
- if (!parsed) return [];
10301
- return Array.isArray(parsed) ? parsed : [parsed];
10482
+ function allAdapters() {
10483
+ return Object.values(adapters);
10302
10484
  }
10303
- var init_message_parts = __esm({
10304
- "src/agents/message-parts.ts"() {
10485
+ var adapters;
10486
+ var init_agents = __esm({
10487
+ "src/agents/index.ts"() {
10305
10488
  "use strict";
10489
+ init_brightsy();
10490
+ init_claude();
10491
+ init_codex();
10492
+ init_cursor();
10493
+ init_opencode();
10494
+ init_types();
10495
+ init_brightsy();
10496
+ init_brightsy_targets();
10497
+ init_claude();
10498
+ init_codex();
10499
+ init_cursor();
10500
+ init_cursor_events();
10501
+ init_cursor();
10502
+ init_opencode();
10503
+ init_list_models();
10504
+ init_session_quota();
10505
+ init_orchestrator_capable();
10506
+ init_path();
10507
+ init_install();
10508
+ adapters = {
10509
+ claude: claudeAdapter,
10510
+ codex: codexAdapter,
10511
+ opencode: opencodeAdapter,
10512
+ brightsy: brightsyAdapter,
10513
+ cursor: cursorAdapter
10514
+ };
10306
10515
  }
10307
10516
  });
10308
10517
 
@@ -10310,6 +10519,9 @@ var init_message_parts = __esm({
10310
10519
  function isTextEvent(event) {
10311
10520
  return event.type === "stdout" || event.type === "thinking";
10312
10521
  }
10522
+ function parentKey(event) {
10523
+ return event.parentId ?? "";
10524
+ }
10313
10525
  function createAgentStreamCoalescer(emit, opts) {
10314
10526
  const intervalMs = opts?.intervalMs ?? 32;
10315
10527
  let pending = null;
@@ -10322,7 +10534,11 @@ function createAgentStreamCoalescer(emit, opts) {
10322
10534
  if (!pending) return;
10323
10535
  const event = pending;
10324
10536
  pending = null;
10325
- emit({ type: event.type, data: event.data });
10537
+ emit({
10538
+ type: event.type,
10539
+ data: event.data,
10540
+ ...event.parentId ? { parentId: event.parentId } : {}
10541
+ });
10326
10542
  };
10327
10543
  const schedule = () => {
10328
10544
  if (timer) return;
@@ -10337,11 +10553,15 @@ function createAgentStreamCoalescer(emit, opts) {
10337
10553
  return;
10338
10554
  }
10339
10555
  if (!event.data) return;
10340
- if (pending && pending.type === event.type) {
10556
+ if (pending && pending.type === event.type && parentKey(pending) === parentKey(event)) {
10341
10557
  pending.data += event.data;
10342
10558
  } else {
10343
10559
  flush2();
10344
- pending = { type: event.type, data: event.data };
10560
+ pending = {
10561
+ type: event.type,
10562
+ data: event.data,
10563
+ ...event.parentId ? { parentId: event.parentId } : {}
10564
+ };
10345
10565
  }
10346
10566
  schedule();
10347
10567
  },
@@ -10971,7 +11191,8 @@ function worktreeBindingFrom(from) {
10971
11191
  prState: from.prState,
10972
11192
  skipAutoArchiveOnMerge: from.skipAutoArchiveOnMerge,
10973
11193
  stackId: from.stackId,
10974
- stackLayer: from.stackLayer
11194
+ stackLayer: from.stackLayer,
11195
+ cowboy: from.cowboy
10975
11196
  };
10976
11197
  }
10977
11198
  function threadsSharingWorktree(worktreePath) {
@@ -12143,6 +12364,55 @@ async function createThread(input, _onSetupLine) {
12143
12364
  if (!(0, import_node_fs34.existsSync)(repoPath)) {
12144
12365
  throw new Error(`Repo not found: ${repoPath}`);
12145
12366
  }
12367
+ if (input.cowboy) {
12368
+ const { cowboyModeEnabled: cowboyModeEnabled2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
12369
+ if (!cowboyModeEnabled2()) {
12370
+ throw new Error(
12371
+ "Cowboy mode is off. Enable it in Settings \u2192 Advanced."
12372
+ );
12373
+ }
12374
+ if (input.sourceType === "pr" || input.sourceType === "ticket" || input.sourceType === "adopt") {
12375
+ throw new Error(
12376
+ "Cowboy mode works on the project folder (default branch), not a PR, ticket, or adopt."
12377
+ );
12378
+ }
12379
+ const defaultBranch = await resolveDefaultBranch(repoPath, { network: false });
12380
+ const head = await currentBranch(repoPath);
12381
+ if (head === "HEAD") {
12382
+ throw new Error(
12383
+ "Cowboy mode needs a named branch in the project folder (not a detached HEAD)."
12384
+ );
12385
+ }
12386
+ if (head !== defaultBranch) {
12387
+ throw new Error(
12388
+ `Cowboy mode uses ${defaultBranch} in the project folder. Switch that checkout to ${defaultBranch} first (currently ${head}).`
12389
+ );
12390
+ }
12391
+ const explicitTitle2 = input.title?.trim();
12392
+ const thread2 = createEmptyThread({
12393
+ title: explicitTitle2 || `Cowboy \xB7 ${head}`,
12394
+ userSetTitle: Boolean(explicitTitle2),
12395
+ sourceType: "branch",
12396
+ sourceRef: head,
12397
+ branchName: head,
12398
+ worktreePath: repoPath,
12399
+ repoPath,
12400
+ agent: resolved.agent,
12401
+ autonomy: input.autonomy ?? "default",
12402
+ model: resolved.model,
12403
+ effort: resolved.effort,
12404
+ fast: resolved.fast,
12405
+ planMode: Boolean(input.planMode),
12406
+ attachments: input.attachments ?? [],
12407
+ sourceIsFork: false,
12408
+ parentThreadId: input.parentThreadId ?? null,
12409
+ status: "idle",
12410
+ cowboy: true
12411
+ });
12412
+ writeThread(thread2);
12413
+ await ensureWorkspace(repoPath);
12414
+ return readThread(thread2.id) ?? thread2;
12415
+ }
12146
12416
  let sourceRef = input.sourceRef;
12147
12417
  let sourceIsFork = false;
12148
12418
  let prUrl = null;
@@ -12228,6 +12498,24 @@ var init_create = __esm({
12228
12498
  }
12229
12499
  });
12230
12500
 
12501
+ // src/threads/cowboy.ts
12502
+ function isCowboyThread(thread) {
12503
+ return Boolean(thread?.cowboy);
12504
+ }
12505
+ function isPrimaryCheckoutThread(thread) {
12506
+ if (!thread?.worktreePath || !thread.repoPath) return false;
12507
+ return normalizeWorktreePath(thread.worktreePath) === normalizeWorktreePath(thread.repoPath);
12508
+ }
12509
+ function shouldRemoveWorktreeOnTeardown(thread) {
12510
+ return !isCowboyThread(thread) && !isPrimaryCheckoutThread(thread);
12511
+ }
12512
+ var init_cowboy = __esm({
12513
+ "src/threads/cowboy.ts"() {
12514
+ "use strict";
12515
+ init_worktree_labels();
12516
+ }
12517
+ });
12518
+
12231
12519
  // src/store/turn-live.ts
12232
12520
  function trimParts(parts) {
12233
12521
  if (parts.length <= MAX_PARTS) return parts;
@@ -13883,13 +14171,22 @@ async function previewLand(thread) {
13883
14171
  isFork: true
13884
14172
  };
13885
14173
  }
13886
- const target = await resolveDefaultBranch(thread.repoPath);
13887
- if (thread.branchName === target || thread.branchName === "main" || thread.branchName === "master") {
14174
+ if (isCowboyThread(thread)) {
14175
+ const current2 = await currentBranch(thread.worktreePath);
14176
+ const dirty = await isDirty(thread.worktreePath);
14177
+ const diff2 = await getDiff(thread.worktreePath, thread.repoPath, { base: current2 });
14178
+ return {
14179
+ branch: current2,
14180
+ target: current2,
14181
+ diffStat: diff2.stat,
14182
+ dirty,
14183
+ blocked: false,
14184
+ isFork: false,
14185
+ cowboy: true
14186
+ };
13888
14187
  }
13889
- const { stdout: head } = await Promise.resolve().then(() => (init_run(), run_exports)).then(
13890
- ({ git: git2 }) => git2(["rev-parse", "--abbrev-ref", "HEAD"], thread.worktreePath)
13891
- );
13892
- const current = head.trim();
14188
+ const target = await resolveDefaultBranch(thread.repoPath);
14189
+ const current = await currentBranch(thread.worktreePath);
13893
14190
  if (current === target) {
13894
14191
  return {
13895
14192
  branch: current,
@@ -13916,6 +14213,19 @@ async function confirmLand(thread, opts) {
13916
14213
  if (preview.blocked) {
13917
14214
  throw new Error(preview.blockReason ?? "Land blocked");
13918
14215
  }
14216
+ if (preview.cowboy) {
14217
+ const meta2 = await suggestPrMetadata(thread.worktreePath, {
14218
+ base: preview.branch,
14219
+ fallbackTitle: thread.title,
14220
+ sourceLabel: `${thread.sourceType}:${thread.sourceRef}`
14221
+ });
14222
+ let committed2 = false;
14223
+ if (preview.dirty) {
14224
+ committed2 = await commitAll(thread.worktreePath, meta2.commitMessage);
14225
+ }
14226
+ await pushBranch(thread.worktreePath, preview.branch);
14227
+ return { prUrl: null, pushed: true, committed: committed2 };
14228
+ }
13919
14229
  const meta = await suggestPrMetadata(thread.worktreePath, {
13920
14230
  base: preview.target,
13921
14231
  fallbackTitle: thread.title,
@@ -13960,6 +14270,7 @@ var init_land = __esm({
13960
14270
  init_diff();
13961
14271
  init_pr_metadata();
13962
14272
  init_gh_errors();
14273
+ init_cowboy();
13963
14274
  }
13964
14275
  });
13965
14276
 
@@ -14649,7 +14960,7 @@ var init_instructions = __esm({
14649
14960
  });
14650
14961
 
14651
14962
  // src/plan/plan-present.ts
14652
- function asRecord4(v) {
14963
+ function asRecord5(v) {
14653
14964
  return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
14654
14965
  }
14655
14966
  function isPresentPlanToolName(name) {
@@ -14661,7 +14972,7 @@ function extractPresentedPlan(parts) {
14661
14972
  for (let i = parts.length - 1; i >= 0; i--) {
14662
14973
  const p = parts[i];
14663
14974
  if (p.type !== "tool" || !isPresentPlanToolName(p.name)) continue;
14664
- const input = asRecord4(p.input) ?? {};
14975
+ const input = asRecord5(p.input) ?? {};
14665
14976
  const content = typeof input.content === "string" ? input.content : typeof input.plan === "string" ? input.plan : typeof input.markdown === "string" ? input.markdown : "";
14666
14977
  if (!content.trim()) continue;
14667
14978
  const title = typeof input.title === "string" && input.title.trim() ? input.title.trim() : "Plan";
@@ -14992,6 +15303,7 @@ var init_orchestrator = __esm({
14992
15303
  init_thread_store();
14993
15304
  init_desktop_host();
14994
15305
  init_create();
15306
+ init_cowboy();
14995
15307
  init_orchestrator_capable();
14996
15308
  init_chat_tabs();
14997
15309
  init_enqueue_by_key();
@@ -15268,7 +15580,9 @@ var init_orchestrator = __esm({
15268
15580
  return thread;
15269
15581
  }
15270
15582
  async finishCreateThread(threadId, prompt) {
15271
- const setup = this.runSetupAfterCreate(threadId);
15583
+ const created = this.getThread(threadId);
15584
+ const skipSetup = isCowboyThread(created);
15585
+ const setup = skipSetup ? Promise.resolve() : this.runSetupAfterCreate(threadId);
15272
15586
  if (prompt) {
15273
15587
  try {
15274
15588
  await this.send(threadId, prompt);
@@ -15280,6 +15594,7 @@ var init_orchestrator = __esm({
15280
15594
  }
15281
15595
  }
15282
15596
  await setup;
15597
+ if (skipSetup) return;
15283
15598
  const { autoRunAfterSetupEnabled: autoRunAfterSetupEnabled2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
15284
15599
  if (autoRunAfterSetupEnabled2()) {
15285
15600
  try {
@@ -16611,17 +16926,19 @@ var init_orchestrator = __esm({
16611
16926
  const siblings = threadsSharingWorktree(thread.worktreePath).filter((t) => t.id !== thread.id);
16612
16927
  if (siblings.length === 0) {
16613
16928
  this.stopDev(thread.id);
16614
- try {
16615
- await runArchiveScript(thread.repoPath, thread.worktreePath, (line) => {
16616
- this.emit({
16617
- type: "turn_output",
16618
- threadId: thread.id,
16619
- event: { type: "stdout", data: `[archive] ${line}` }
16929
+ if (shouldRemoveWorktreeOnTeardown(thread)) {
16930
+ try {
16931
+ await runArchiveScript(thread.repoPath, thread.worktreePath, (line) => {
16932
+ this.emit({
16933
+ type: "turn_output",
16934
+ threadId: thread.id,
16935
+ event: { type: "stdout", data: `[archive] ${line}` }
16936
+ });
16620
16937
  });
16621
- });
16622
- } catch {
16938
+ } catch {
16939
+ }
16940
+ await removeWorktree(thread.repoPath, thread.worktreePath);
16623
16941
  }
16624
- await removeWorktree(thread.repoPath, thread.worktreePath);
16625
16942
  }
16626
16943
  const archived = setStatus(thread.id, "archived");
16627
16944
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
@@ -16649,15 +16966,17 @@ var init_orchestrator = __esm({
16649
16966
  const siblings = threadsSharingWorktree(thread.worktreePath).filter((t) => t.id !== thread.id);
16650
16967
  if (siblings.length === 0) {
16651
16968
  this.stopDev(thread.id);
16652
- try {
16653
- await runArchiveScript(thread.repoPath, thread.worktreePath);
16654
- } catch {
16969
+ if (shouldRemoveWorktreeOnTeardown(thread)) {
16970
+ try {
16971
+ await runArchiveScript(thread.repoPath, thread.worktreePath);
16972
+ } catch {
16973
+ }
16974
+ const { deleteBranchOnPurgeEnabled: deleteBranchOnPurgeEnabled2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
16975
+ const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled2();
16976
+ await removeWorktree(thread.repoPath, thread.worktreePath, {
16977
+ deleteBranch: deleteBranch ? thread.branchName : void 0
16978
+ });
16655
16979
  }
16656
- const { deleteBranchOnPurgeEnabled: deleteBranchOnPurgeEnabled2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
16657
- const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled2();
16658
- await removeWorktree(thread.repoPath, thread.worktreePath, {
16659
- deleteBranch: deleteBranch ? thread.branchName : void 0
16660
- });
16661
16980
  }
16662
16981
  deleteThreadRecord(thread.id);
16663
16982
  }
@@ -16674,6 +16993,11 @@ var init_orchestrator = __esm({
16674
16993
  return restored2;
16675
16994
  }
16676
16995
  if (!(0, import_node_fs44.existsSync)(thread.worktreePath)) {
16996
+ if (isCowboyThread(thread) || isPrimaryCheckoutThread(thread)) {
16997
+ throw new Error(
16998
+ `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
16999
+ );
17000
+ }
16677
17001
  const { createThreadWorktree: createThreadWorktree2 } = await Promise.resolve().then(() => (init_worktree(), worktree_exports));
16678
17002
  const slug = thread.worktreePath.split("/").pop();
16679
17003
  const dest = thread.worktreePath;
@@ -17010,6 +17334,7 @@ __export(index_exports, {
17010
17334
  coordinatorTurnReminder: () => coordinatorTurnReminder,
17011
17335
  copyConfiguredFiles: () => copyConfiguredFiles,
17012
17336
  countCacheControlBlocks: () => countCacheControlBlocks,
17337
+ cowboyModeEnabled: () => cowboyModeEnabled,
17013
17338
  createChatTab: () => createChatTab,
17014
17339
  createEmptyThread: () => createEmptyThread,
17015
17340
  createExistingBranchWorktree: () => createExistingBranchWorktree,
@@ -17160,6 +17485,7 @@ __export(index_exports, {
17160
17485
  isBrightsyNdjsonLine: () => isBrightsyNdjsonLine,
17161
17486
  isCloudCoordinatorThread: () => isCloudCoordinatorThread,
17162
17487
  isConductorBundledCli: () => isConductorBundledCli,
17488
+ isCowboyThread: () => isCowboyThread,
17163
17489
  isCursorAutoModel: () => isCursorAutoModel,
17164
17490
  isDefaultishSourceRef: () => isDefaultishSourceRef,
17165
17491
  isDesktopHostAlive: () => isDesktopHostAlive,
@@ -17178,11 +17504,13 @@ __export(index_exports, {
17178
17504
  isPlaceholderBranch: () => isPlaceholderBranch,
17179
17505
  isPrNotMergeableError: () => isPrNotMergeableError,
17180
17506
  isPresentPlanToolName: () => isPresentPlanToolName,
17507
+ isPrimaryCheckoutThread: () => isPrimaryCheckoutThread,
17181
17508
  isSessionQuotaLimit: () => isSessionQuotaLimit,
17182
17509
  isSideboardScratchPath: () => isSideboardScratchPath,
17183
17510
  isSlackCoordinatorThread: () => isSlackCoordinatorThread,
17184
17511
  isSlackExternalReplyPrompt: () => isSlackExternalReplyPrompt,
17185
17512
  isSlackOAuthCancelled: () => isSlackOAuthCancelled,
17513
+ isSubagentToolName: () => isSubagentToolName,
17186
17514
  isThinkingEffort: () => isThinkingEffort,
17187
17515
  isThisProcessDesktopHost: () => isThisProcessDesktopHost,
17188
17516
  isThreadCaffeinated: () => isThreadCaffeinated,
@@ -17237,6 +17565,7 @@ __export(index_exports, {
17237
17565
  mergePrStack: () => mergePrStack,
17238
17566
  mergeSideboardIntoMcpServersJson: () => mergeSideboardIntoMcpServersJson,
17239
17567
  mergeUsage: () => mergeUsage,
17568
+ messagePartParentId: () => messagePartParentId,
17240
17569
  nextPastedTextName: () => nextPastedTextName,
17241
17570
  nextThinkingEffort: () => nextThinkingEffort,
17242
17571
  nonInteractiveGitProcessEnv: () => nonInteractiveGitProcessEnv,
@@ -17355,6 +17684,7 @@ __export(index_exports, {
17355
17684
  shouldCompactContext: () => shouldCompactContext,
17356
17685
  shouldInjectBrightsyMcp: () => shouldInjectBrightsyMcp,
17357
17686
  shouldRefreshReviewRequestTemplate: () => shouldRefreshReviewRequestTemplate,
17687
+ shouldRemoveWorktreeOnTeardown: () => shouldRemoveWorktreeOnTeardown,
17358
17688
  shouldResetSessionForOccupancy: () => shouldResetSessionForOccupancy,
17359
17689
  shouldRunWorktreeCleanup: () => shouldRunWorktreeCleanup,
17360
17690
  sideboardHomeDir: () => sideboardHomeDir,
@@ -17425,6 +17755,8 @@ __export(index_exports, {
17425
17755
  waitForPidExit: () => waitForPidExit,
17426
17756
  warmGithubAgentAuth: () => warmGithubAgentAuth,
17427
17757
  withAgentInstructions: () => withAgentInstructions,
17758
+ withEventParentId: () => withEventParentId,
17759
+ withEventsParentId: () => withEventsParentId,
17428
17760
  withExportedPath: () => withExportedPath,
17429
17761
  withThreadLock: () => withThreadLock,
17430
17762
  workspaceSettingsSourceLabel: () => workspaceSettingsSourceLabel,
@@ -18278,6 +18610,7 @@ init_context_compact();
18278
18610
  init_summarize();
18279
18611
  init_land();
18280
18612
  init_create();
18613
+ init_cowboy();
18281
18614
  init_chat_tabs();
18282
18615
  init_fork_worktree();
18283
18616
  init_stack_layers();
@@ -18287,7 +18620,7 @@ init_request_review();
18287
18620
  init_workspace_scratch();
18288
18621
 
18289
18622
  // src/plan/ask-user.ts
18290
- function asRecord5(v) {
18623
+ function asRecord6(v) {
18291
18624
  return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
18292
18625
  }
18293
18626
  function parseOptions(raw) {
@@ -18298,7 +18631,7 @@ function parseOptions(raw) {
18298
18631
  out.push({ label: item.trim() });
18299
18632
  continue;
18300
18633
  }
18301
- const o = asRecord5(item);
18634
+ const o = asRecord6(item);
18302
18635
  if (!o) continue;
18303
18636
  const label = typeof o.label === "string" ? o.label.trim() : typeof o.text === "string" ? o.text.trim() : typeof o.title === "string" ? o.title.trim() : "";
18304
18637
  if (!label) continue;
@@ -18308,7 +18641,7 @@ function parseOptions(raw) {
18308
18641
  return out;
18309
18642
  }
18310
18643
  function parseOneQuestion(raw) {
18311
- const o = asRecord5(raw);
18644
+ const o = asRecord6(raw);
18312
18645
  if (!o) return null;
18313
18646
  const question = typeof o.question === "string" ? o.question.trim() : typeof o.text === "string" ? o.text.trim() : typeof o.prompt === "string" ? o.prompt.trim() : "";
18314
18647
  if (!question) return null;
@@ -18319,7 +18652,7 @@ function parseOneQuestion(raw) {
18319
18652
  return { question, header, multiSelect, options };
18320
18653
  }
18321
18654
  function parsePlanQuestionsInput(input) {
18322
- const root = asRecord5(input);
18655
+ const root = asRecord6(input);
18323
18656
  if (!root) return [];
18324
18657
  const list = Array.isArray(root.questions) ? root.questions : Array.isArray(root.question) ? root.question : root.question || root.prompt ? [root] : [];
18325
18658
  const out = [];
@@ -19488,7 +19821,7 @@ async function startMcpServer() {
19488
19821
  );
19489
19822
  server.tool(
19490
19823
  "create_thread",
19491
- `Create a new worktree thread (chat) from branch, pr, or ticket. Pass repoPath from list_workspaces. From an orchestration chat, omit parentThreadId (Sideboard binds the child to this chat) or pass the exact id from the turn reminder \u2014 never invent a uuid. Prefer omitting agent/model so Sideboard applies ${accountDefaultsHint}. Setup (settings.toml, .cursor/worktrees.json, or script/setup) runs in the background in parallel with the first turn. Then use send_to_thread to chat.`,
19824
+ `Create a new worktree thread (chat) from branch, pr, or ticket. Pass repoPath from list_workspaces. cowboy=true uses the project folder on the default branch (no isolated worktree; land is commit+push). From an orchestration chat, omit parentThreadId (Sideboard binds the child to this chat) or pass the exact id from the turn reminder \u2014 never invent a uuid. Prefer omitting agent/model so Sideboard applies ${accountDefaultsHint}. Setup (settings.toml, .cursor/worktrees.json, or script/setup) runs in the background in parallel with the first turn (skipped for cowboy). Then use send_to_thread to chat.`,
19492
19825
  {
19493
19826
  sourceType: import_zod4.z.enum(["branch", "pr", "ticket"]),
19494
19827
  sourceRef: import_zod4.z.string(),
@@ -19498,6 +19831,9 @@ async function startMcpServer() {
19498
19831
  ),
19499
19832
  repoPath: import_zod4.z.string(),
19500
19833
  title: import_zod4.z.string().optional(),
19834
+ cowboy: import_zod4.z.boolean().optional().describe(
19835
+ "If true, work in the project folder on the default branch (no thread/* worktree). Requires Settings \u2192 Advanced \u2192 Cowboy mode. Land is commit+push to that branch. Archive does not delete the folder."
19836
+ ),
19501
19837
  parentThreadId: import_zod4.z.string().optional().describe(
19502
19838
  "Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
19503
19839
  )
@@ -19558,7 +19894,8 @@ async function startMcpServer() {
19558
19894
  fast: opts.fast,
19559
19895
  repoPath: args.repoPath,
19560
19896
  title: args.title,
19561
- parentThreadId: parentId || null
19897
+ parentThreadId: parentId || null,
19898
+ cowboy: args.cowboy || void 0
19562
19899
  }),
19563
19900
  CREATE_THREAD_TIMEOUT_MS,
19564
19901
  "create_thread"
@@ -19576,6 +19913,7 @@ async function startMcpServer() {
19576
19913
  agent: thread.agent,
19577
19914
  model: thread.model,
19578
19915
  status: thread.status,
19916
+ cowboy: Boolean(thread.cowboy),
19579
19917
  link: `sideboard://thread/${thread.id}`,
19580
19918
  parentThreadId: thread.parentThreadId,
19581
19919
  ...agentCoercedFrom ? {
@@ -22741,6 +23079,7 @@ init_outbound_watch();
22741
23079
  coordinatorTurnReminder,
22742
23080
  copyConfiguredFiles,
22743
23081
  countCacheControlBlocks,
23082
+ cowboyModeEnabled,
22744
23083
  createChatTab,
22745
23084
  createEmptyThread,
22746
23085
  createExistingBranchWorktree,
@@ -22891,6 +23230,7 @@ init_outbound_watch();
22891
23230
  isBrightsyNdjsonLine,
22892
23231
  isCloudCoordinatorThread,
22893
23232
  isConductorBundledCli,
23233
+ isCowboyThread,
22894
23234
  isCursorAutoModel,
22895
23235
  isDefaultishSourceRef,
22896
23236
  isDesktopHostAlive,
@@ -22909,11 +23249,13 @@ init_outbound_watch();
22909
23249
  isPlaceholderBranch,
22910
23250
  isPrNotMergeableError,
22911
23251
  isPresentPlanToolName,
23252
+ isPrimaryCheckoutThread,
22912
23253
  isSessionQuotaLimit,
22913
23254
  isSideboardScratchPath,
22914
23255
  isSlackCoordinatorThread,
22915
23256
  isSlackExternalReplyPrompt,
22916
23257
  isSlackOAuthCancelled,
23258
+ isSubagentToolName,
22917
23259
  isThinkingEffort,
22918
23260
  isThisProcessDesktopHost,
22919
23261
  isThreadCaffeinated,
@@ -22968,6 +23310,7 @@ init_outbound_watch();
22968
23310
  mergePrStack,
22969
23311
  mergeSideboardIntoMcpServersJson,
22970
23312
  mergeUsage,
23313
+ messagePartParentId,
22971
23314
  nextPastedTextName,
22972
23315
  nextThinkingEffort,
22973
23316
  nonInteractiveGitProcessEnv,
@@ -23086,6 +23429,7 @@ init_outbound_watch();
23086
23429
  shouldCompactContext,
23087
23430
  shouldInjectBrightsyMcp,
23088
23431
  shouldRefreshReviewRequestTemplate,
23432
+ shouldRemoveWorktreeOnTeardown,
23089
23433
  shouldResetSessionForOccupancy,
23090
23434
  shouldRunWorktreeCleanup,
23091
23435
  sideboardHomeDir,
@@ -23156,6 +23500,8 @@ init_outbound_watch();
23156
23500
  waitForPidExit,
23157
23501
  warmGithubAgentAuth,
23158
23502
  withAgentInstructions,
23503
+ withEventParentId,
23504
+ withEventsParentId,
23159
23505
  withExportedPath,
23160
23506
  withThreadLock,
23161
23507
  workspaceSettingsSourceLabel,