@sideboard-ai/core 0.1.133 → 0.1.136

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 (35) hide show
  1. package/dist/{agents-NHS6P25H.js → agents-ELWR7A2T.js} +5 -5
  2. package/dist/{agents-SOU5GPWI.js → agents-QNTTLMG2.js} +5 -5
  3. package/dist/{chunk-Y645NFYN.js → chunk-4XKUHP6G.js} +2 -2
  4. package/dist/{chunk-4YAVXWFR.js → chunk-CYM5DCHI.js} +39 -5
  5. package/dist/{chunk-LTP7GHIF.js → chunk-GSKRGF7B.js} +286 -5
  6. package/dist/{chunk-WEU5M7BW.js → chunk-HQQNLDVC.js} +1 -1
  7. package/dist/{chunk-IM36S4HZ.js → chunk-IFZ4MOTN.js} +3 -3
  8. package/dist/{chunk-G6X6UFJO.js → chunk-J5IBSVB3.js} +42 -12
  9. package/dist/{chunk-AK7SWE2U.js → chunk-JPBRMUM6.js} +9 -5
  10. package/dist/{chunk-YEIH7P7D.js → chunk-K5YT5GX2.js} +244 -5
  11. package/dist/{chunk-3UVVWNHF.js → chunk-MDCKV2NF.js} +2 -2
  12. package/dist/{chunk-HLJUNJBF.js → chunk-PM3C2J6K.js} +42 -12
  13. package/dist/{chunk-Q6XQXCOM.js → chunk-QAV3HGVS.js} +1 -1
  14. package/dist/{chunk-TG7YU2KG.js → chunk-TIGKDMIA.js} +144 -271
  15. package/dist/{chunk-6CRKPD4C.js → chunk-TQ4S5AGJ.js} +3 -3
  16. package/dist/{chunk-VSON7EJ6.js → chunk-WS5LFFU3.js} +140 -222
  17. package/dist/{coordinator-prompt-DXHEDRRN.js → coordinator-prompt-2OWSUAUR.js} +3 -3
  18. package/dist/{coordinator-prompt-XF7LVOUN.js → coordinator-prompt-IPL4Z6SL.js} +3 -3
  19. package/dist/{global-workspace-2LO5ATOH.js → global-workspace-JDUCUL7S.js} +4 -4
  20. package/dist/{global-workspace-MLQNULOU.js → global-workspace-NIKZAKOO.js} +4 -4
  21. package/dist/index.cjs +873 -628
  22. package/dist/index.d.cts +26 -1
  23. package/dist/index.d.ts +26 -1
  24. package/dist/index.js +42 -19
  25. package/dist/mcp/run-stdio.cjs +700 -498
  26. package/dist/mcp/run-stdio.js +22 -11
  27. package/dist/{orchestrator-P2SYHVLS.js → orchestrator-6I47JMU2.js} +7 -7
  28. package/dist/{orchestrator-KR5RA5B7.js → orchestrator-KK3CUW37.js} +7 -7
  29. package/dist/{thread-store-5MLYY35S.js → thread-store-CRLQJ2HM.js} +3 -1
  30. package/dist/{thread-store-F6BCARQG.js → thread-store-FADXSMEJ.js} +3 -1
  31. package/dist/{workspaces-MINB764G.js → workspaces-5EWNNALF.js} +5 -5
  32. package/dist/{workspaces-ARWPZG6F.js → workspaces-KZA3TCEE.js} +5 -5
  33. package/dist/{worktree-EBZMUUAL.js → worktree-BC6XDMQK.js} +2 -2
  34. package/dist/{worktree-S7MKMYAT.js → worktree-MX7XBX6Z.js} +2 -2
  35. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -2075,6 +2075,7 @@ __export(thread_store_exports, {
2075
2075
  createEmptyThread: () => createEmptyThread,
2076
2076
  deleteThreadRecord: () => deleteThreadRecord,
2077
2077
  findThreadByRef: () => findThreadByRef,
2078
+ invalidateThreadListCache: () => invalidateThreadListCache,
2078
2079
  isThreadRecordFile: () => isThreadRecordFile,
2079
2080
  listThreads: () => listThreads,
2080
2081
  normalizeThread: () => normalizeThread,
@@ -2172,11 +2173,28 @@ async function withThreadLock(id, fn) {
2172
2173
  if (release) await release();
2173
2174
  }
2174
2175
  }
2176
+ function cacheForDir() {
2177
+ const dir = threadsDir();
2178
+ if (!listCache || listCache.dir !== dir) {
2179
+ listCache = { dir, byId: /* @__PURE__ */ new Map(), listed: false };
2180
+ }
2181
+ return listCache;
2182
+ }
2183
+ function invalidateThreadListCache() {
2184
+ listCache = null;
2185
+ }
2186
+ function rememberThread(thread) {
2187
+ cacheForDir().byId.set(thread.id, thread);
2188
+ }
2175
2189
  function readThread(id) {
2190
+ const cached = cacheForDir().byId.get(id);
2191
+ if (cached) return cached;
2176
2192
  const path2 = threadFilePath(id);
2177
2193
  if (!(0, import_node_fs9.existsSync)(path2)) return null;
2178
2194
  const raw = (0, import_node_fs9.readFileSync)(path2, "utf8");
2179
- return normalizeThread(JSON.parse(raw));
2195
+ const thread = normalizeThread(JSON.parse(raw));
2196
+ rememberThread(thread);
2197
+ return thread;
2180
2198
  }
2181
2199
  function writeThread(thread) {
2182
2200
  const path2 = threadFilePath(idPath(thread.id));
@@ -2184,6 +2202,7 @@ function writeThread(thread) {
2184
2202
  const next = { ...thread, updatedAt: nowIso() };
2185
2203
  (0, import_node_fs9.writeFileSync)(tmp, JSON.stringify(next, null, 2), "utf8");
2186
2204
  (0, import_node_fs9.renameSync)(tmp, path2);
2205
+ rememberThread(next);
2187
2206
  }
2188
2207
  function idPath(id) {
2189
2208
  return id;
@@ -2193,22 +2212,32 @@ function isThreadRecordFile(nameOrPath) {
2193
2212
  return name.endsWith(".json") && !name.endsWith(".live.json");
2194
2213
  }
2195
2214
  function listThreads(opts) {
2196
- const files = (0, import_node_fs9.readdirSync)(threadsDir()).filter(isThreadRecordFile);
2197
- const threads = files.map((f) => {
2198
- try {
2199
- return normalizeThread(
2200
- JSON.parse((0, import_node_fs9.readFileSync)(threadFilePath(f.replace(/\.json$/, "")), "utf8"))
2201
- );
2202
- } catch {
2203
- return null;
2215
+ const cache = cacheForDir();
2216
+ if (!cache.listed) {
2217
+ const files = (0, import_node_fs9.readdirSync)(threadsDir()).filter(isThreadRecordFile);
2218
+ const byId = /* @__PURE__ */ new Map();
2219
+ for (const f of files) {
2220
+ try {
2221
+ const thread = normalizeThread(
2222
+ JSON.parse((0, import_node_fs9.readFileSync)(threadFilePath(f.replace(/\.json$/, "")), "utf8"))
2223
+ );
2224
+ if (typeof thread.id === "string" && thread.id.length > 0) {
2225
+ byId.set(thread.id, thread);
2226
+ }
2227
+ } catch {
2228
+ }
2204
2229
  }
2205
- }).filter(
2206
- (t) => t !== null && typeof t.id === "string" && t.id.length > 0
2207
- ).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
2230
+ cache.byId = byId;
2231
+ cache.listed = true;
2232
+ }
2233
+ const threads = [...cache.byId.values()].sort(
2234
+ (a, b) => b.updatedAt.localeCompare(a.updatedAt)
2235
+ );
2208
2236
  if (opts?.includeArchived) return threads;
2209
2237
  return threads.filter((t) => t.status !== "archived");
2210
2238
  }
2211
2239
  function deleteThreadRecord(id) {
2240
+ cacheForDir().byId.delete(id);
2212
2241
  const path2 = threadFilePath(id);
2213
2242
  if ((0, import_node_fs9.existsSync)(path2)) (0, import_node_fs9.unlinkSync)(path2);
2214
2243
  const lock = threadLockPath(id);
@@ -2240,7 +2269,7 @@ function findThreadByRef(ref) {
2240
2269
  (t) => t.id === ref || t.id.startsWith(ref) || t.branchName === ref || t.title === ref
2241
2270
  ) ?? null;
2242
2271
  }
2243
- var import_node_crypto3, import_node_fs9, import_node_path10, import_proper_lockfile;
2272
+ var import_node_crypto3, import_node_fs9, import_node_path10, import_proper_lockfile, listCache;
2244
2273
  var init_thread_store = __esm({
2245
2274
  "src/store/thread-store.ts"() {
2246
2275
  "use strict";
@@ -2250,6 +2279,7 @@ var init_thread_store = __esm({
2250
2279
  import_proper_lockfile = __toESM(require("proper-lockfile"), 1);
2251
2280
  init_thinking_effort();
2252
2281
  init_paths();
2282
+ listCache = null;
2253
2283
  }
2254
2284
  });
2255
2285
 
@@ -2618,6 +2648,300 @@ var init_cloud_connect_constants = __esm({
2618
2648
  }
2619
2649
  });
2620
2650
 
2651
+ // src/paths/workspace-scratch.ts
2652
+ function attachmentsGitignoreBody() {
2653
+ return ATTACHMENTS_GITIGNORE;
2654
+ }
2655
+ function isWorkspaceScratchPath(relativePath) {
2656
+ const p = relativePath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/$/, "");
2657
+ return p === ATTACHMENTS_DIR || p.startsWith(`${ATTACHMENTS_DIR}/`) || p === LEGACY_ATTACHMENTS_DIR || p.startsWith(`${LEGACY_ATTACHMENTS_DIR}/`) || p === ".context" || p.startsWith(".context/");
2658
+ }
2659
+ var ATTACHMENTS_DIR, LEGACY_ATTACHMENTS_DIR, ATTACHMENTS_GITIGNORE;
2660
+ var init_workspace_scratch = __esm({
2661
+ "src/paths/workspace-scratch.ts"() {
2662
+ "use strict";
2663
+ ATTACHMENTS_DIR = ".context/attachments";
2664
+ LEGACY_ATTACHMENTS_DIR = ".sideboard/attachments";
2665
+ ATTACHMENTS_GITIGNORE = `# Sideboard / workspace attachments (local only)
2666
+ *
2667
+ !.gitignore
2668
+ `;
2669
+ }
2670
+ });
2671
+
2672
+ // src/composer/stage-files.ts
2673
+ function fileExtension(filePath) {
2674
+ const base = (0, import_node_path13.basename)(filePath).toLowerCase();
2675
+ return base.includes(".") ? base.split(".").pop() || "" : "";
2676
+ }
2677
+ function isImageFilePath(filePath) {
2678
+ return IMAGE_EXTENSIONS.has(fileExtension(filePath));
2679
+ }
2680
+ function imageMimeType(filePath) {
2681
+ return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
2682
+ }
2683
+ function ensureAttachmentsDir(worktreePath) {
2684
+ const dir = (0, import_node_path13.join)(worktreePath, ATTACHMENTS_DIR);
2685
+ (0, import_node_fs12.mkdirSync)(dir, { recursive: true });
2686
+ const gi = (0, import_node_path13.join)(dir, ".gitignore");
2687
+ if (!(0, import_node_fs12.existsSync)(gi)) {
2688
+ (0, import_node_fs12.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
2689
+ }
2690
+ return dir;
2691
+ }
2692
+ function uniqueAttachmentName(dir, originalName) {
2693
+ const safe = originalName.replace(/[/\\]/g, "_") || "file";
2694
+ if (!(0, import_node_fs12.existsSync)((0, import_node_path13.join)(dir, safe))) return safe;
2695
+ const ext = (0, import_node_path13.extname)(safe);
2696
+ const stem = ext ? safe.slice(0, -ext.length) : safe;
2697
+ for (let i = 1; i < 1e4; i++) {
2698
+ const candidate = `${stem}-${i}${ext}`;
2699
+ if (!(0, import_node_fs12.existsSync)((0, import_node_path13.join)(dir, candidate))) return candidate;
2700
+ }
2701
+ return `${stem}-${(0, import_node_crypto5.randomUUID)()}${ext}`;
2702
+ }
2703
+ function previewDataUrlFromBuf(filePath, buf) {
2704
+ if (!isImageFilePath(filePath)) return void 0;
2705
+ if (buf.length > MAX_PREVIEW_BYTES) return void 0;
2706
+ return `data:${imageMimeType(filePath)};base64,${buf.toString("base64")}`;
2707
+ }
2708
+ function attachmentFromBuffer(name, buf, opts) {
2709
+ const previewDataUrl = previewDataUrlFromBuf(name, buf);
2710
+ if (isImageFilePath(name)) {
2711
+ const pathHint = opts.path ? `\`${opts.path}\`` : opts.sourceLabel || name;
2712
+ return {
2713
+ id: (0, import_node_crypto5.randomUUID)(),
2714
+ name,
2715
+ kind: "file",
2716
+ path: opts.path,
2717
+ previewDataUrl,
2718
+ content: [
2719
+ `Image attached: ${pathHint}`,
2720
+ opts.path ? `Use the Read tool on \`${opts.path}\` to view this image.` : "The image is shown in the composer; copy it into the worktree if you need to inspect pixels."
2721
+ ].join("\n")
2722
+ };
2723
+ }
2724
+ if (buf.length > MAX_INLINE_BYTES) {
2725
+ return {
2726
+ id: (0, import_node_crypto5.randomUUID)(),
2727
+ name,
2728
+ kind: "file",
2729
+ path: opts.path,
2730
+ content: opts.path ? `(file too large to attach inline: \`${opts.path}\`, ${buf.length} bytes \u2014 use the Read tool)` : `(file too large to attach inline: ${opts.sourceLabel || name}, ${buf.length} bytes)`
2731
+ };
2732
+ }
2733
+ if (buf.includes(0)) {
2734
+ return {
2735
+ id: (0, import_node_crypto5.randomUUID)(),
2736
+ name,
2737
+ kind: "file",
2738
+ path: opts.path,
2739
+ content: opts.path ? `(binary file at \`${opts.path}\` \u2014 use tools to inspect)` : `(binary file attached by path only: ${opts.sourceLabel || name})`
2740
+ };
2741
+ }
2742
+ return {
2743
+ id: (0, import_node_crypto5.randomUUID)(),
2744
+ name,
2745
+ kind: "file",
2746
+ path: opts.path,
2747
+ content: buf.toString("utf8")
2748
+ };
2749
+ }
2750
+ function attachmentFromAbsolutePath(absolutePath) {
2751
+ const name = (0, import_node_path13.basename)(absolutePath);
2752
+ try {
2753
+ const st = (0, import_node_fs12.statSync)(absolutePath);
2754
+ if (!st.isFile()) {
2755
+ return {
2756
+ id: (0, import_node_crypto5.randomUUID)(),
2757
+ name,
2758
+ kind: "file",
2759
+ content: `(not a file: ${absolutePath})`
2760
+ };
2761
+ }
2762
+ const buf = (0, import_node_fs12.readFileSync)(absolutePath);
2763
+ return attachmentFromBuffer(name, buf, { sourceLabel: absolutePath });
2764
+ } catch (err) {
2765
+ return {
2766
+ id: (0, import_node_crypto5.randomUUID)(),
2767
+ name,
2768
+ kind: "file",
2769
+ content: `(could not read ${absolutePath}: ${err instanceof Error ? err.message : String(err)})`
2770
+ };
2771
+ }
2772
+ }
2773
+ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
2774
+ if (absolutePaths.length === 0) return [];
2775
+ const dir = ensureAttachmentsDir(worktreePath);
2776
+ const out = [];
2777
+ for (const abs of absolutePaths) {
2778
+ const originalName = (0, import_node_path13.basename)(abs);
2779
+ try {
2780
+ const st = (0, import_node_fs12.statSync)(abs);
2781
+ if (!st.isFile()) continue;
2782
+ const name = uniqueAttachmentName(dir, originalName);
2783
+ const destAbs = (0, import_node_path13.join)(dir, name);
2784
+ (0, import_node_fs12.copyFileSync)(abs, destAbs);
2785
+ const rel = `${ATTACHMENTS_DIR}/${name}`;
2786
+ const buf = (0, import_node_fs12.readFileSync)(destAbs);
2787
+ out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
2788
+ } catch (err) {
2789
+ out.push({
2790
+ id: (0, import_node_crypto5.randomUUID)(),
2791
+ name: originalName,
2792
+ kind: "file",
2793
+ content: `(could not attach ${abs}: ${err instanceof Error ? err.message : String(err)})`
2794
+ });
2795
+ }
2796
+ }
2797
+ return out;
2798
+ }
2799
+ function stageBuffersAsAttachments(worktreePath, buffers2) {
2800
+ if (buffers2.length === 0) return [];
2801
+ const dir = ensureAttachmentsDir(worktreePath);
2802
+ const out = [];
2803
+ for (const item of buffers2) {
2804
+ const originalName = (item.name || "file").replace(/[/\\]/g, "_") || "file";
2805
+ try {
2806
+ const buf = Buffer.from(item.dataBase64, "base64");
2807
+ const name = uniqueAttachmentName(dir, originalName);
2808
+ const destAbs = (0, import_node_path13.join)(dir, name);
2809
+ (0, import_node_fs12.writeFileSync)(destAbs, buf);
2810
+ const rel = `${ATTACHMENTS_DIR}/${name}`;
2811
+ out.push(attachmentFromBuffer(name, buf, { path: rel }));
2812
+ } catch (err) {
2813
+ out.push({
2814
+ id: (0, import_node_crypto5.randomUUID)(),
2815
+ name: originalName,
2816
+ kind: "file",
2817
+ content: `(could not attach ${originalName}: ${err instanceof Error ? err.message : String(err)})`
2818
+ });
2819
+ }
2820
+ }
2821
+ return out;
2822
+ }
2823
+ function attachmentsFromBuffers(buffers2) {
2824
+ return buffers2.map((item) => {
2825
+ const name = (item.name || "file").replace(/[/\\]/g, "_") || "file";
2826
+ try {
2827
+ const buf = Buffer.from(item.dataBase64, "base64");
2828
+ return attachmentFromBuffer(name, buf, { sourceLabel: name });
2829
+ } catch (err) {
2830
+ return {
2831
+ id: (0, import_node_crypto5.randomUUID)(),
2832
+ name,
2833
+ kind: "file",
2834
+ content: `(could not attach ${name}: ${err instanceof Error ? err.message : String(err)})`
2835
+ };
2836
+ }
2837
+ });
2838
+ }
2839
+ function isWorktreeRelativePath(p) {
2840
+ if (!p || p.includes("..")) return false;
2841
+ if (p.startsWith("/")) return false;
2842
+ if (/^[A-Za-z]:[\\/]/.test(p)) return false;
2843
+ return true;
2844
+ }
2845
+ function dataUrlToBase64(url) {
2846
+ if (!url) return null;
2847
+ const m = /^data:[^;]+;base64,(.+)$/s.exec(url);
2848
+ return m?.[1] ?? null;
2849
+ }
2850
+ function persistPendingFileAttachments(worktreePath, attachments) {
2851
+ if (attachments.length === 0) return attachments;
2852
+ const keep = [];
2853
+ const buffers2 = [];
2854
+ for (const att of attachments) {
2855
+ if (att.kind !== "file") {
2856
+ keep.push(att);
2857
+ continue;
2858
+ }
2859
+ if (att.path && isWorktreeRelativePath(att.path)) {
2860
+ keep.push(att);
2861
+ continue;
2862
+ }
2863
+ const fromPreview = dataUrlToBase64(att.previewDataUrl);
2864
+ if (fromPreview) {
2865
+ buffers2.push({ name: att.name, dataBase64: fromPreview });
2866
+ continue;
2867
+ }
2868
+ if (att.content && !IMAGE_HINT_RE.test(att.content) && !PLACEHOLDER_CONTENT_RE.test(att.content)) {
2869
+ buffers2.push({
2870
+ name: att.name,
2871
+ dataBase64: Buffer.from(att.content, "utf8").toString("base64")
2872
+ });
2873
+ continue;
2874
+ }
2875
+ keep.push(att);
2876
+ }
2877
+ if (buffers2.length === 0) return attachments;
2878
+ return [...keep, ...stageBuffersAsAttachments(worktreePath, buffers2)];
2879
+ }
2880
+ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
2881
+ const out = [];
2882
+ for (const rel of relativePaths) {
2883
+ if (!rel || rel.includes("..") || rel.startsWith("/")) {
2884
+ out.push({
2885
+ id: (0, import_node_crypto5.randomUUID)(),
2886
+ name: (0, import_node_path13.basename)(rel) || "file",
2887
+ kind: "file",
2888
+ content: `(invalid path: ${rel})`
2889
+ });
2890
+ continue;
2891
+ }
2892
+ const name = (0, import_node_path13.basename)(rel);
2893
+ try {
2894
+ const abs = (0, import_node_path13.join)(worktreePath, rel);
2895
+ const st = (0, import_node_fs12.statSync)(abs);
2896
+ if (!st.isFile()) continue;
2897
+ const buf = (0, import_node_fs12.readFileSync)(abs);
2898
+ out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
2899
+ } catch (err) {
2900
+ out.push({
2901
+ id: (0, import_node_crypto5.randomUUID)(),
2902
+ name,
2903
+ kind: "file",
2904
+ content: `(could not read ${rel}: ${err instanceof Error ? err.message : String(err)})`
2905
+ });
2906
+ }
2907
+ }
2908
+ return out;
2909
+ }
2910
+ var import_node_fs12, import_node_path13, import_node_crypto5, IMAGE_EXTENSIONS, IMAGE_MIME_BY_EXT, MAX_INLINE_BYTES, MAX_PREVIEW_BYTES, IMAGE_HINT_RE, PLACEHOLDER_CONTENT_RE;
2911
+ var init_stage_files = __esm({
2912
+ "src/composer/stage-files.ts"() {
2913
+ "use strict";
2914
+ import_node_fs12 = require("fs");
2915
+ import_node_path13 = require("path");
2916
+ import_node_crypto5 = require("crypto");
2917
+ init_workspace_scratch();
2918
+ IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
2919
+ "png",
2920
+ "jpg",
2921
+ "jpeg",
2922
+ "gif",
2923
+ "webp",
2924
+ "svg",
2925
+ "bmp",
2926
+ "ico"
2927
+ ]);
2928
+ IMAGE_MIME_BY_EXT = {
2929
+ png: "image/png",
2930
+ jpg: "image/jpeg",
2931
+ jpeg: "image/jpeg",
2932
+ gif: "image/gif",
2933
+ webp: "image/webp",
2934
+ svg: "image/svg+xml",
2935
+ bmp: "image/bmp",
2936
+ ico: "image/x-icon"
2937
+ };
2938
+ MAX_INLINE_BYTES = 4e5;
2939
+ MAX_PREVIEW_BYTES = 5e6;
2940
+ IMAGE_HINT_RE = /^Image attached:/;
2941
+ PLACEHOLDER_CONTENT_RE = /^\((could not |file too large|binary file|not a file|invalid path)/;
2942
+ }
2943
+ });
2944
+
2621
2945
  // src/git/team-meta.ts
2622
2946
  var SOCCER_TEAM_META;
2623
2947
  var init_team_meta = __esm({
@@ -3529,23 +3853,23 @@ var init_gh_errors = __esm({
3529
3853
  function githubAgentAuthDir() {
3530
3854
  const override = process.env.SIDEBOARD_GIT_AUTH_DIR?.trim();
3531
3855
  if (override) return override;
3532
- return (0, import_node_path13.join)((0, import_node_os4.homedir)(), ".sideboard-git-auth");
3856
+ return (0, import_node_path14.join)((0, import_node_os4.homedir)(), ".sideboard-git-auth");
3533
3857
  }
3534
3858
  function githubCredentialStorePath() {
3535
- return (0, import_node_path13.join)(githubAgentAuthDir(), "git-credentials");
3859
+ return (0, import_node_path14.join)(githubAgentAuthDir(), "git-credentials");
3536
3860
  }
3537
3861
  function githubGhConfigDir() {
3538
- return (0, import_node_path13.join)(githubAgentAuthDir(), "gh");
3862
+ return (0, import_node_path14.join)(githubAgentAuthDir(), "gh");
3539
3863
  }
3540
3864
  function writePrivateFile2(file, body) {
3541
- (0, import_node_fs12.mkdirSync)((0, import_node_path13.dirname)(file), { recursive: true, mode: 448 });
3865
+ (0, import_node_fs13.mkdirSync)((0, import_node_path14.dirname)(file), { recursive: true, mode: 448 });
3542
3866
  try {
3543
- (0, import_node_fs12.chmodSync)((0, import_node_path13.dirname)(file), 448);
3867
+ (0, import_node_fs13.chmodSync)((0, import_node_path14.dirname)(file), 448);
3544
3868
  } catch {
3545
3869
  }
3546
- (0, import_node_fs12.writeFileSync)(file, body, { encoding: "utf8", mode: 384 });
3870
+ (0, import_node_fs13.writeFileSync)(file, body, { encoding: "utf8", mode: 384 });
3547
3871
  try {
3548
- (0, import_node_fs12.chmodSync)(file, 384);
3872
+ (0, import_node_fs13.chmodSync)(file, 384);
3549
3873
  } catch {
3550
3874
  }
3551
3875
  }
@@ -3570,19 +3894,19 @@ function materializeGithubAgentAuth(token, user) {
3570
3894
  const trimmed = token.trim();
3571
3895
  if (!trimmed) return;
3572
3896
  const root = githubAgentAuthDir();
3573
- (0, import_node_fs12.mkdirSync)(root, { recursive: true, mode: 448 });
3897
+ (0, import_node_fs13.mkdirSync)(root, { recursive: true, mode: 448 });
3574
3898
  try {
3575
- (0, import_node_fs12.chmodSync)(root, 448);
3899
+ (0, import_node_fs13.chmodSync)(root, 448);
3576
3900
  } catch {
3577
3901
  }
3578
3902
  writePrivateFile2(githubCredentialStorePath(), gitCredentialStoreContents(trimmed));
3579
3903
  const ghDir = githubGhConfigDir();
3580
- (0, import_node_fs12.mkdirSync)(ghDir, { recursive: true, mode: 448 });
3581
- writePrivateFile2((0, import_node_path13.join)(ghDir, "hosts.yml"), ghHostsYml(trimmed, user));
3582
- writePrivateFile2((0, import_node_path13.join)(ghDir, "config.yml"), "git_protocol: https\nprompt: disabled\n");
3904
+ (0, import_node_fs13.mkdirSync)(ghDir, { recursive: true, mode: 448 });
3905
+ writePrivateFile2((0, import_node_path14.join)(ghDir, "hosts.yml"), ghHostsYml(trimmed, user));
3906
+ writePrivateFile2((0, import_node_path14.join)(ghDir, "config.yml"), "git_protocol: https\nprompt: disabled\n");
3583
3907
  }
3584
3908
  function githubAgentAuthReady() {
3585
- return (0, import_node_fs12.existsSync)(githubCredentialStorePath()) && (0, import_node_fs12.existsSync)((0, import_node_path13.join)(githubGhConfigDir(), "hosts.yml"));
3909
+ return (0, import_node_fs13.existsSync)(githubCredentialStorePath()) && (0, import_node_fs13.existsSync)((0, import_node_path14.join)(githubGhConfigDir(), "hosts.yml"));
3586
3910
  }
3587
3911
  function githubCredentialHelperGitConfig() {
3588
3912
  const file = githubCredentialStorePath();
@@ -3597,29 +3921,29 @@ function githubGhConfigEnv() {
3597
3921
  GH_PROMPT_DISABLED: "1"
3598
3922
  };
3599
3923
  }
3600
- var import_node_fs12, import_node_os4, import_node_path13;
3924
+ var import_node_fs13, import_node_os4, import_node_path14;
3601
3925
  var init_github_agent_auth = __esm({
3602
3926
  "src/git/github-agent-auth.ts"() {
3603
3927
  "use strict";
3604
- import_node_fs12 = require("fs");
3928
+ import_node_fs13 = require("fs");
3605
3929
  import_node_os4 = require("os");
3606
- import_node_path13 = require("path");
3930
+ import_node_path14 = require("path");
3607
3931
  }
3608
3932
  });
3609
3933
 
3610
3934
  // src/agents/path.ts
3611
3935
  function prependPathDir(env, dir) {
3612
- if (!dir || !(0, import_node_fs13.existsSync)(dir)) return;
3936
+ if (!dir || !(0, import_node_fs14.existsSync)(dir)) return;
3613
3937
  const current = env.PATH ?? "";
3614
- const parts = current.split(import_node_path14.delimiter).filter(Boolean);
3938
+ const parts = current.split(import_node_path15.delimiter).filter(Boolean);
3615
3939
  if (parts.includes(dir)) {
3616
3940
  env.PATH = current;
3617
3941
  return;
3618
3942
  }
3619
- env.PATH = [dir, ...parts].join(import_node_path14.delimiter);
3943
+ env.PATH = [dir, ...parts].join(import_node_path15.delimiter);
3620
3944
  }
3621
3945
  function conductorBundledBinDir(home = process.env.HOME || process.env.USERPROFILE || (0, import_node_os5.homedir)()) {
3622
- return (0, import_node_path14.join)(home, "Library", "Application Support", "com.conductor.app", "bin");
3946
+ return (0, import_node_path15.join)(home, "Library", "Application Support", "com.conductor.app", "bin");
3623
3947
  }
3624
3948
  function isConductorBundledCli(filePath) {
3625
3949
  const p = (filePath ?? "").replace(/\\/g, "/");
@@ -3628,21 +3952,21 @@ function isConductorBundledCli(filePath) {
3628
3952
  function ensureAgentPath(env = process.env) {
3629
3953
  const home = env.HOME || env.USERPROFILE || (0, import_node_os5.homedir)();
3630
3954
  const current = env.PATH ?? "";
3631
- const parts = current.split(import_node_path14.delimiter).filter(Boolean);
3955
+ const parts = current.split(import_node_path15.delimiter).filter(Boolean);
3632
3956
  const seen = new Set(parts);
3633
3957
  const extras = [
3634
- ...EXTRA_BIN_DIRS.map((rel) => (0, import_node_path14.join)(home, rel)),
3958
+ ...EXTRA_BIN_DIRS.map((rel) => (0, import_node_path15.join)(home, rel)),
3635
3959
  "/opt/homebrew/bin",
3636
3960
  "/usr/local/bin",
3637
3961
  // Keep after Homebrew/npm so a user-installed CLI still wins.
3638
3962
  conductorBundledBinDir(home)
3639
3963
  ];
3640
3964
  for (const dir of extras.reverse()) {
3641
- if (!dir || seen.has(dir) || !(0, import_node_fs13.existsSync)(dir)) continue;
3965
+ if (!dir || seen.has(dir) || !(0, import_node_fs14.existsSync)(dir)) continue;
3642
3966
  parts.unshift(dir);
3643
3967
  seen.add(dir);
3644
3968
  }
3645
- const next = parts.join(import_node_path14.delimiter);
3969
+ const next = parts.join(import_node_path15.delimiter);
3646
3970
  env.PATH = next;
3647
3971
  return next;
3648
3972
  }
@@ -3656,7 +3980,7 @@ function enrichPathWithNpmGlobalBin(env = process.env) {
3656
3980
  stdio: ["ignore", "pipe", "ignore"]
3657
3981
  }).trim().split(/\r?\n/).find(Boolean);
3658
3982
  if (prefix) {
3659
- const binDir = process.platform === "win32" ? prefix : (0, import_node_path14.join)(prefix, "bin");
3983
+ const binDir = process.platform === "win32" ? prefix : (0, import_node_path15.join)(prefix, "bin");
3660
3984
  prependPathDir(env, binDir);
3661
3985
  }
3662
3986
  } catch {
@@ -3684,14 +4008,14 @@ function withExportedPath(command, pathValue) {
3684
4008
  if (/^(export\s+PATH=|PATH=)/.test(trimmed)) return trimmed;
3685
4009
  return `export PATH=${posixShellSingleQuote(pathValue)} && ${trimmed}`;
3686
4010
  }
3687
- var import_node_fs13, import_node_child_process3, import_node_os5, import_node_path14, EXTRA_BIN_DIRS;
4011
+ var import_node_fs14, import_node_child_process3, import_node_os5, import_node_path15, EXTRA_BIN_DIRS;
3688
4012
  var init_path = __esm({
3689
4013
  "src/agents/path.ts"() {
3690
4014
  "use strict";
3691
- import_node_fs13 = require("fs");
4015
+ import_node_fs14 = require("fs");
3692
4016
  import_node_child_process3 = require("child_process");
3693
4017
  import_node_os5 = require("os");
3694
- import_node_path14 = require("path");
4018
+ import_node_path15 = require("path");
3695
4019
  EXTRA_BIN_DIRS = [
3696
4020
  ".local/bin",
3697
4021
  ".cargo/bin",
@@ -3713,11 +4037,11 @@ function isIndexLockError(text5) {
3713
4037
  return /Unable to create ['"][^'"]*index\.lock['"]: File exists/i.test(text5);
3714
4038
  }
3715
4039
  function clearStaleIndexLock(gitDir, maxAgeMs = STALE_INDEX_LOCK_MS, now = Date.now()) {
3716
- const lockPath = (0, import_node_path15.join)(gitDir, "index.lock");
4040
+ const lockPath = (0, import_node_path16.join)(gitDir, "index.lock");
3717
4041
  try {
3718
- if (!(0, import_node_fs14.existsSync)(lockPath)) return null;
3719
- if (now - (0, import_node_fs14.statSync)(lockPath).mtimeMs < maxAgeMs) return null;
3720
- (0, import_node_fs14.unlinkSync)(lockPath);
4042
+ if (!(0, import_node_fs15.existsSync)(lockPath)) return null;
4043
+ if (now - (0, import_node_fs15.statSync)(lockPath).mtimeMs < maxAgeMs) return null;
4044
+ (0, import_node_fs15.unlinkSync)(lockPath);
3721
4045
  return lockPath;
3722
4046
  } catch {
3723
4047
  return null;
@@ -3732,12 +4056,12 @@ function clearStaleIndexLocks(gitDirs, maxAgeMs = STALE_INDEX_LOCK_MS) {
3732
4056
  }
3733
4057
  return removed;
3734
4058
  }
3735
- var import_node_fs14, import_node_path15, STALE_INDEX_LOCK_MS;
4059
+ var import_node_fs15, import_node_path16, STALE_INDEX_LOCK_MS;
3736
4060
  var init_stale_lock = __esm({
3737
4061
  "src/git/stale-lock.ts"() {
3738
4062
  "use strict";
3739
- import_node_fs14 = require("fs");
3740
- import_node_path15 = require("path");
4063
+ import_node_fs15 = require("fs");
4064
+ import_node_path16 = require("path");
3741
4065
  STALE_INDEX_LOCK_MS = 2e4;
3742
4066
  }
3743
4067
  });
@@ -3945,7 +4269,7 @@ async function warmGithubAgentAuth(opts) {
3945
4269
  }
3946
4270
  function normalizeWritableRoot(raw) {
3947
4271
  const trimmed = raw.trim().replace(/\/+$/, "");
3948
- return trimmed && (0, import_node_path16.isAbsolute)(trimmed) ? trimmed : null;
4272
+ return trimmed && (0, import_node_path17.isAbsolute)(trimmed) ? trimmed : null;
3949
4273
  }
3950
4274
  async function resolveCodexGitWritableRoots(cwd) {
3951
4275
  const roots = /* @__PURE__ */ new Set();
@@ -4022,11 +4346,11 @@ function formatGitAuthModeDirective(mode) {
4022
4346
  ].join("\n");
4023
4347
  }
4024
4348
  }
4025
- var import_node_path16, HTTPS_REWRITE, TOKEN_TTL_MS, tokenMemo, GITHUB_CHILD_TOKEN_KEYS;
4349
+ var import_node_path17, HTTPS_REWRITE, TOKEN_TTL_MS, tokenMemo, GITHUB_CHILD_TOKEN_KEYS;
4026
4350
  var init_git_auth_mode = __esm({
4027
4351
  "src/git/git-auth-mode.ts"() {
4028
4352
  "use strict";
4029
- import_node_path16 = require("path");
4353
+ import_node_path17 = require("path");
4030
4354
  init_app_settings();
4031
4355
  init_github_agent_auth();
4032
4356
  init_run();
@@ -4396,27 +4720,6 @@ var init_stack = __esm({
4396
4720
  }
4397
4721
  });
4398
4722
 
4399
- // src/paths/workspace-scratch.ts
4400
- function attachmentsGitignoreBody() {
4401
- return ATTACHMENTS_GITIGNORE;
4402
- }
4403
- function isWorkspaceScratchPath(relativePath) {
4404
- const p = relativePath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/$/, "");
4405
- return p === ATTACHMENTS_DIR || p.startsWith(`${ATTACHMENTS_DIR}/`) || p === LEGACY_ATTACHMENTS_DIR || p.startsWith(`${LEGACY_ATTACHMENTS_DIR}/`) || p === ".context" || p.startsWith(".context/");
4406
- }
4407
- var ATTACHMENTS_DIR, LEGACY_ATTACHMENTS_DIR, ATTACHMENTS_GITIGNORE;
4408
- var init_workspace_scratch = __esm({
4409
- "src/paths/workspace-scratch.ts"() {
4410
- "use strict";
4411
- ATTACHMENTS_DIR = ".context/attachments";
4412
- LEGACY_ATTACHMENTS_DIR = ".sideboard/attachments";
4413
- ATTACHMENTS_GITIGNORE = `# Sideboard / workspace attachments (local only)
4414
- *
4415
- !.gitignore
4416
- `;
4417
- }
4418
- });
4419
-
4420
4723
  // src/git/worktree.ts
4421
4724
  var worktree_exports = {};
4422
4725
  __export(worktree_exports, {
@@ -4504,7 +4807,7 @@ async function resolveRepoRoot(cwd) {
4504
4807
  }
4505
4808
  function canonicalizeRepoPath(path2) {
4506
4809
  try {
4507
- return (0, import_node_fs15.realpathSync)(path2);
4810
+ return (0, import_node_fs16.realpathSync)(path2);
4508
4811
  } catch {
4509
4812
  return path2.replace(/\/+$/, "");
4510
4813
  }
@@ -5232,8 +5535,8 @@ function isLocalPrFetchBranch(ref) {
5232
5535
  }
5233
5536
  async function createThreadWorktree(opts) {
5234
5537
  let branchName = `thread/${opts.slug}`;
5235
- const worktreePath = (0, import_node_path17.join)(worktreesRoot(opts.repoPath), opts.slug);
5236
- if ((0, import_node_fs15.existsSync)(worktreePath)) {
5538
+ const worktreePath = (0, import_node_path18.join)(worktreesRoot(opts.repoPath), opts.slug);
5539
+ if ((0, import_node_fs16.existsSync)(worktreePath)) {
5237
5540
  throw new Error(`Worktree already exists at ${worktreePath}`);
5238
5541
  }
5239
5542
  await ensureGhPreferOrigin(opts.repoPath);
@@ -5300,8 +5603,8 @@ ${add.stdout}`;
5300
5603
  async function createExistingBranchWorktree(opts) {
5301
5604
  const branchName = opts.branchName.trim();
5302
5605
  if (!branchName) throw new Error("branch name required");
5303
- const worktreePath = (0, import_node_path17.join)(worktreesRoot(opts.repoPath), opts.slug);
5304
- if ((0, import_node_fs15.existsSync)(worktreePath)) {
5606
+ const worktreePath = (0, import_node_path18.join)(worktreesRoot(opts.repoPath), opts.slug);
5607
+ if ((0, import_node_fs16.existsSync)(worktreePath)) {
5305
5608
  throw new Error(`Worktree already exists at ${worktreePath}`);
5306
5609
  }
5307
5610
  await ensureGhPreferOrigin(opts.repoPath);
@@ -5592,10 +5895,10 @@ function sameRepoPath(a, b) {
5592
5895
  return normalizeWorktreePath(a) === normalizeWorktreePath(b);
5593
5896
  }
5594
5897
  function listLocalThreadBranchSlugs(repoPath) {
5595
- const refsDir = (0, import_node_path17.join)(repoPath, ".git", "refs", "heads", "thread");
5596
- if (!(0, import_node_fs15.existsSync)(refsDir)) return [];
5898
+ const refsDir = (0, import_node_path18.join)(repoPath, ".git", "refs", "heads", "thread");
5899
+ if (!(0, import_node_fs16.existsSync)(refsDir)) return [];
5597
5900
  try {
5598
- return (0, import_node_fs15.readdirSync)(refsDir).filter((name) => !name.startsWith(".")).map((name) => normalizeTakenSlug(name));
5901
+ return (0, import_node_fs16.readdirSync)(refsDir).filter((name) => !name.startsWith(".")).map((name) => normalizeTakenSlug(name));
5599
5902
  } catch {
5600
5903
  return [];
5601
5904
  }
@@ -5603,8 +5906,8 @@ function listLocalThreadBranchSlugs(repoPath) {
5603
5906
  function collectTakenTeamSlugs(repoPath) {
5604
5907
  const taken = /* @__PURE__ */ new Set();
5605
5908
  const root = worktreesRoot(repoPath);
5606
- if ((0, import_node_fs15.existsSync)(root)) {
5607
- for (const entry of (0, import_node_fs15.readdirSync)(root, { withFileTypes: true })) {
5909
+ if ((0, import_node_fs16.existsSync)(root)) {
5910
+ for (const entry of (0, import_node_fs16.readdirSync)(root, { withFileTypes: true })) {
5608
5911
  if (entry.isDirectory() && entry.name !== ".DS_Store") {
5609
5912
  taken.add(normalizeTakenSlug(entry.name));
5610
5913
  }
@@ -5625,18 +5928,18 @@ function allocateTeamSlug(repoPath) {
5625
5928
  const taken = collectTakenTeamSlugs(repoPath);
5626
5929
  for (let attempt = 0; attempt < 32; attempt++) {
5627
5930
  const team = allocateTeamName(taken);
5628
- const path2 = (0, import_node_path17.join)(worktreesRoot(repoPath), team.slug);
5629
- if (!(0, import_node_fs15.existsSync)(path2)) return team;
5931
+ const path2 = (0, import_node_path18.join)(worktreesRoot(repoPath), team.slug);
5932
+ if (!(0, import_node_fs16.existsSync)(path2)) return team;
5630
5933
  taken.add(team.slug);
5631
5934
  }
5632
5935
  throw new Error("No available soccer team worktree directories left");
5633
5936
  }
5634
- var import_node_fs15, import_node_path17;
5937
+ var import_node_fs16, import_node_path18;
5635
5938
  var init_worktree = __esm({
5636
5939
  "src/git/worktree.ts"() {
5637
5940
  "use strict";
5638
- import_node_fs15 = require("fs");
5639
- import_node_path17 = require("path");
5941
+ import_node_fs16 = require("fs");
5942
+ import_node_path18 = require("path");
5640
5943
  init_paths();
5641
5944
  init_thread_store();
5642
5945
  init_teams();
@@ -5707,13 +6010,13 @@ function coordinatorTurnReminder(opts) {
5707
6010
  `- YOUR orchestration thread id is ${opts.parentId} \u2014 pass parentThreadId="${opts.parentId}" on create_thread, or omit it.`,
5708
6011
  goal ? `- Goal / title: ${goal}` : null,
5709
6012
  accountDefaultsPlaybookLine(),
5710
- "- Status: list_board (worktree Kanban: New \u2192 Draft \u2192 Review \u2192 Merged) or list_threads. Link chats as `[Title](sideboard://thread/<id>)`. Merge only if the user asked."
6013
+ "- Status: list_board (worktree Kanban: New \u2192 Draft \u2192 Review \u2192 Merged) or list_threads. Link chats as `[Title](sideboard://thread/<id>)`. Merge only if the user asked. If a child is stopped/error/broken, it did not finish \u2014 resume or tell the user."
5711
6014
  ].filter(Boolean).join("\n");
5712
6015
  }
5713
6016
  function ensureGlobalCoordinatorCwd(opts) {
5714
6017
  const dir = globalAgentCwd();
5715
6018
  try {
5716
- (0, import_node_fs16.mkdirSync)(dir, { recursive: true });
6019
+ (0, import_node_fs17.mkdirSync)(dir, { recursive: true });
5717
6020
  } catch {
5718
6021
  return dir;
5719
6022
  }
@@ -5721,7 +6024,7 @@ function ensureGlobalCoordinatorCwd(opts) {
5721
6024
  let orchId = opts?.orchestratorThreadId?.trim() || "";
5722
6025
  if (!orchId) {
5723
6026
  try {
5724
- const existing = (0, import_node_fs16.readFileSync)((0, import_node_path18.join)(dir, "AGENTS.md"), "utf8");
6027
+ const existing = (0, import_node_fs17.readFileSync)((0, import_node_path19.join)(dir, "AGENTS.md"), "utf8");
5725
6028
  const m = existing.match(
5726
6029
  /YOUR orchestration thread id is `([0-9a-f-]{36})`/i
5727
6030
  );
@@ -5764,9 +6067,9 @@ function ensureGlobalCoordinatorCwd(opts) {
5764
6067
  "Always ask worktree agents to commit, push, and open draft PRs (`ask_git` / `send_to_thread`). Tell them to merge only when the user explicitly asked. The worktree agent runs git/gh; never merge from this orchestration cwd."
5765
6068
  ].join("\n");
5766
6069
  try {
5767
- (0, import_node_fs16.writeFileSync)((0, import_node_path18.join)(dir, "CLAUDE.md"), `${body}
6070
+ (0, import_node_fs17.writeFileSync)((0, import_node_path19.join)(dir, "CLAUDE.md"), `${body}
5768
6071
  `, "utf8");
5769
- (0, import_node_fs16.writeFileSync)((0, import_node_path18.join)(dir, "AGENTS.md"), `${body}
6072
+ (0, import_node_fs17.writeFileSync)((0, import_node_path19.join)(dir, "AGENTS.md"), `${body}
5770
6073
  `, "utf8");
5771
6074
  } catch {
5772
6075
  }
@@ -5798,12 +6101,12 @@ function coordinatorSystemPrompt(opts) {
5798
6101
  formatWorkspaceInventory(opts.workspaces)
5799
6102
  ].join("\n");
5800
6103
  }
5801
- var import_node_fs16, import_node_path18, COORDINATOR_TOOL_PLAYBOOK, SLACK_REPLY_FORMATTING;
6104
+ var import_node_fs17, import_node_path19, COORDINATOR_TOOL_PLAYBOOK, SLACK_REPLY_FORMATTING;
5802
6105
  var init_coordinator_prompt = __esm({
5803
6106
  "src/orchestrator/coordinator-prompt.ts"() {
5804
6107
  "use strict";
5805
- import_node_fs16 = require("fs");
5806
- import_node_path18 = require("path");
6108
+ import_node_fs17 = require("fs");
6109
+ import_node_path19 = require("path");
5807
6110
  init_worktree();
5808
6111
  init_app_settings();
5809
6112
  init_paths();
@@ -5832,7 +6135,7 @@ var init_coordinator_prompt = __esm({
5832
6135
  "- fork_worktree \u2014 fork a worktree chat into a NEW git worktree + chat (transcript attached); optional agent; leave model unset (Auto) unless you have a reason. Not for orchestration chats.",
5833
6136
  "- fork_chat \u2014 fork a worktree chat (same worktree tab) OR a Global orchestration chat (new orchestration tab); optional agent; leave model unset (Auto) unless you have a reason. Remote coordinators: use this to continue another orchestration chat on a different agent after session limits.",
5834
6137
  "- send_to_thread \u2014 queue a prompt (start/continue a chat turn); pass force_stop: true to interrupt mid-turn / clear stale queued prompts before replacing with a new request",
5835
- "- wait_for_turn / get_turn_result \u2014 wait for and read the agent reply (includes last-turn usage / costUsd when the child agent reported it). wait_for_turn returns within ~45s even if the child is still working (MCP clients kill longer tool calls). If stillRunning is true, progress is a live snapshot of tools/thinking \u2014 call wait_for_turn again. status=queued with no lastActivityAt means the child has not started yet (concurrency cap) \u2014 keep waiting; do not force_stop or send a check-in. Do not send \u201Care you stuck?\u201D or assume a hang while lastActivityAt is recent. On status error, lastError (and text) is the failure \u2014 switch agent, tell the user, or retry; do not treat empty text as success.",
6138
+ "- wait_for_turn / get_turn_result \u2014 wait for and read the agent reply (includes last-turn usage / costUsd when the child agent reported it). wait_for_turn returns within ~45s even if the child is still working (MCP clients kill longer tool calls). If stillRunning is true, progress is a live snapshot of tools/thinking \u2014 call wait_for_turn again. status=queued with no lastActivityAt means the child has not started yet (concurrency cap) \u2014 keep waiting; do not force_stop or send a check-in. Do not send \u201Care you stuck?\u201D or assume a hang while lastActivityAt is recent. On status error, lastError (and text) is the failure \u2014 switch agent, tell the user, or retry; do not treat empty text as success. On status stopped or broken (or incomplete=true), the child was interrupted or died \u2014 resume with send_to_thread or tell the user; never treat stopped as a finished turn. Sideboard also injects a notice into this chat when a child stops unexpectedly.",
5836
6139
  "- stop_thread \u2014 force-stop: kill in-flight turn AND clear queued prompts (do not leave stale queue after an interrupt)",
5837
6140
  "- archive_thread / restore_thread \u2014 archive (tears down worktree when last tab) or restore",
5838
6141
  "Setup / run:",
@@ -5952,6 +6255,7 @@ function createGlobalChat(opts) {
5952
6255
  fast: opts.fast
5953
6256
  });
5954
6257
  const agent = assertOrchestratorCapableAgent(resolved.agent);
6258
+ const worktreePath = globalAgentCwd();
5955
6259
  const thread = createEmptyThread({
5956
6260
  title,
5957
6261
  // Stick nicknames the same way chat tabs do (avoid later sync overwrites).
@@ -5959,7 +6263,7 @@ function createGlobalChat(opts) {
5959
6263
  sourceType: "orchestration",
5960
6264
  sourceRef,
5961
6265
  branchName: "global",
5962
- worktreePath: globalAgentCwd(),
6266
+ worktreePath,
5963
6267
  repoPath: GLOBAL_WORKSPACE_ID,
5964
6268
  agent,
5965
6269
  autonomy: opts.autonomy ?? "default",
@@ -5967,7 +6271,10 @@ function createGlobalChat(opts) {
5967
6271
  effort: resolved.effort,
5968
6272
  fast: resolved.fast,
5969
6273
  planMode: Boolean(opts.planMode),
5970
- attachments: opts.attachments ?? [],
6274
+ attachments: persistPendingFileAttachments(
6275
+ worktreePath,
6276
+ opts.attachments ?? []
6277
+ ),
5971
6278
  parentThreadId: opts.parentThreadId ?? null,
5972
6279
  status: "idle"
5973
6280
  });
@@ -6092,6 +6399,7 @@ var init_global_workspace = __esm({
6092
6399
  "src/store/global-workspace.ts"() {
6093
6400
  "use strict";
6094
6401
  init_cloud_connect_constants();
6402
+ init_stage_files();
6095
6403
  init_orchestrator_capable();
6096
6404
  init_teams();
6097
6405
  init_coordinator_prompt();
@@ -6149,13 +6457,13 @@ var init_api = __esm({
6149
6457
 
6150
6458
  // src/slack/reply-target.ts
6151
6459
  function storePath() {
6152
- return (0, import_node_path19.join)(appDataDir(), "slack-reply-to.json");
6460
+ return (0, import_node_path20.join)(appDataDir(), "slack-reply-to.json");
6153
6461
  }
6154
6462
  function readStore() {
6155
6463
  const path2 = storePath();
6156
- if (!(0, import_node_fs17.existsSync)(path2)) return {};
6464
+ if (!(0, import_node_fs18.existsSync)(path2)) return {};
6157
6465
  try {
6158
- const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs17.readFileSync)(path2, "utf8"));
6466
+ const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs18.readFileSync)(path2, "utf8"));
6159
6467
  return parsed?.targets && typeof parsed.targets === "object" ? parsed.targets : {};
6160
6468
  } catch {
6161
6469
  return {};
@@ -6170,12 +6478,12 @@ function setSlackReplyTarget(target) {
6170
6478
  function getSlackReplyTarget(threadId) {
6171
6479
  return readStore()[threadId] ?? null;
6172
6480
  }
6173
- var import_node_fs17, import_node_path19;
6481
+ var import_node_fs18, import_node_path20;
6174
6482
  var init_reply_target = __esm({
6175
6483
  "src/slack/reply-target.ts"() {
6176
6484
  "use strict";
6177
- import_node_fs17 = require("fs");
6178
- import_node_path19 = require("path");
6485
+ import_node_fs18 = require("fs");
6486
+ import_node_path20 = require("path");
6179
6487
  init_paths();
6180
6488
  init_private_file();
6181
6489
  init_secure_file();
@@ -6184,7 +6492,7 @@ var init_reply_target = __esm({
6184
6492
 
6185
6493
  // src/slack/workspaces.ts
6186
6494
  function storePath2() {
6187
- return (0, import_node_path20.join)(appDataDir(), "slack-workspaces.json");
6495
+ return (0, import_node_path21.join)(appDataDir(), "slack-workspaces.json");
6188
6496
  }
6189
6497
  function readStore2() {
6190
6498
  try {
@@ -6291,11 +6599,11 @@ function requireSlackWorkspace(teamId) {
6291
6599
  }
6292
6600
  return ws;
6293
6601
  }
6294
- var import_node_path20;
6602
+ var import_node_path21;
6295
6603
  var init_workspaces = __esm({
6296
6604
  "src/slack/workspaces.ts"() {
6297
6605
  "use strict";
6298
- import_node_path20 = require("path");
6606
+ import_node_path21 = require("path");
6299
6607
  init_paths();
6300
6608
  init_secure_file();
6301
6609
  init_api();
@@ -6304,7 +6612,7 @@ var init_workspaces = __esm({
6304
6612
 
6305
6613
  // src/slack/outbound-watch.ts
6306
6614
  function storePath3() {
6307
- return (0, import_node_path21.join)(appDataDir(), "slack-outbound-watch.json");
6615
+ return (0, import_node_path22.join)(appDataDir(), "slack-outbound-watch.json");
6308
6616
  }
6309
6617
  function watchId(teamId, channelId, ts) {
6310
6618
  return `${teamId}:${channelId}:${ts}`;
@@ -6317,9 +6625,9 @@ function tsNewer(a, b) {
6317
6625
  }
6318
6626
  function readStore3() {
6319
6627
  const path2 = storePath3();
6320
- if (!(0, import_node_fs18.existsSync)(path2)) return [];
6628
+ if (!(0, import_node_fs19.existsSync)(path2)) return [];
6321
6629
  try {
6322
- const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs18.readFileSync)(path2, "utf8"));
6630
+ const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs19.readFileSync)(path2, "utf8"));
6323
6631
  return Array.isArray(parsed?.watches) ? parsed.watches : [];
6324
6632
  } catch {
6325
6633
  return [];
@@ -6641,12 +6949,12 @@ async function pollSlackOutboundWatches(opts) {
6641
6949
  }
6642
6950
  if (changed) writeStore2(watches);
6643
6951
  }
6644
- var import_node_fs18, import_node_path21, MAX_WATCHES, MAX_REPLIES_PER_WATCH, WATCH_TTL_MS, POLL_INTERVAL_MS, lastPollMs, nameCache, continueOnReply;
6952
+ var import_node_fs19, import_node_path22, MAX_WATCHES, MAX_REPLIES_PER_WATCH, WATCH_TTL_MS, POLL_INTERVAL_MS, lastPollMs, nameCache, continueOnReply;
6645
6953
  var init_outbound_watch = __esm({
6646
6954
  "src/slack/outbound-watch.ts"() {
6647
6955
  "use strict";
6648
- import_node_fs18 = require("fs");
6649
- import_node_path21 = require("path");
6956
+ import_node_fs19 = require("fs");
6957
+ import_node_path22 = require("path");
6650
6958
  init_paths();
6651
6959
  init_private_file();
6652
6960
  init_secure_file();
@@ -6927,32 +7235,32 @@ var init_error_detail = __esm({
6927
7235
  function brightsyConfigPath() {
6928
7236
  const override = process.env.BRIGHTSY_CONFIG?.trim();
6929
7237
  if (override) return override;
6930
- return (0, import_node_path22.join)((0, import_node_os6.homedir)(), ".brightsy", "config.json");
7238
+ return (0, import_node_path23.join)((0, import_node_os6.homedir)(), ".brightsy", "config.json");
6931
7239
  }
6932
7240
  function loadBrightsyConfig() {
6933
7241
  const path2 = brightsyConfigPath();
6934
- if (!(0, import_node_fs19.existsSync)(path2)) {
7242
+ if (!(0, import_node_fs20.existsSync)(path2)) {
6935
7243
  throw new Error("Brightsy not logged in \u2014 run `brightsy login` first");
6936
7244
  }
6937
- const raw = JSON.parse((0, import_node_fs19.readFileSync)(path2, "utf8"));
7245
+ const raw = JSON.parse((0, import_node_fs20.readFileSync)(path2, "utf8"));
6938
7246
  if (!raw.access_token || !raw.account_id) {
6939
7247
  throw new Error("Brightsy config incomplete \u2014 run `brightsy login`");
6940
7248
  }
6941
7249
  return raw;
6942
7250
  }
6943
7251
  function saveBrightsyConfig(cfg) {
6944
- (0, import_node_fs19.writeFileSync)(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
7252
+ (0, import_node_fs20.writeFileSync)(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
6945
7253
  `, {
6946
7254
  mode: 384
6947
7255
  });
6948
7256
  }
6949
- var import_node_fs19, import_node_os6, import_node_path22;
7257
+ var import_node_fs20, import_node_os6, import_node_path23;
6950
7258
  var init_config = __esm({
6951
7259
  "src/brightsy/config.ts"() {
6952
7260
  "use strict";
6953
- import_node_fs19 = require("fs");
7261
+ import_node_fs20 = require("fs");
6954
7262
  import_node_os6 = require("os");
6955
- import_node_path22 = require("path");
7263
+ import_node_path23 = require("path");
6956
7264
  }
6957
7265
  });
6958
7266
 
@@ -7170,22 +7478,22 @@ __export(connected_teams_exports, {
7170
7478
  listConnectedBrightsyTeams: () => listConnectedBrightsyTeams
7171
7479
  });
7172
7480
  function storePath4() {
7173
- return (0, import_node_path23.join)(appDataDir(), "brightsy-teams.json");
7481
+ return (0, import_node_path24.join)(appDataDir(), "brightsy-teams.json");
7174
7482
  }
7175
7483
  function readStore4() {
7176
7484
  const path2 = storePath4();
7177
- if (!(0, import_node_fs20.existsSync)(path2)) return [];
7485
+ if (!(0, import_node_fs21.existsSync)(path2)) return [];
7178
7486
  try {
7179
- const parsed = JSON.parse((0, import_node_fs20.readFileSync)(path2, "utf8"));
7487
+ const parsed = JSON.parse((0, import_node_fs21.readFileSync)(path2, "utf8"));
7180
7488
  return Array.isArray(parsed.teams) ? parsed.teams : [];
7181
7489
  } catch {
7182
7490
  return [];
7183
7491
  }
7184
7492
  }
7185
7493
  function writeStore3(teams) {
7186
- (0, import_node_fs20.mkdirSync)(appDataDir(), { recursive: true });
7494
+ (0, import_node_fs21.mkdirSync)(appDataDir(), { recursive: true });
7187
7495
  const path2 = storePath4();
7188
- (0, import_node_fs20.writeFileSync)(path2, `${JSON.stringify({ teams }, null, 2)}
7496
+ (0, import_node_fs21.writeFileSync)(path2, `${JSON.stringify({ teams }, null, 2)}
7189
7497
  `, {
7190
7498
  mode: 384
7191
7499
  });
@@ -7341,12 +7649,12 @@ function brightsyMcpServerName(slug) {
7341
7649
  const cleaned = slug.replace(/[^A-Za-z0-9_-]/g, "_").replace(/^_+|_+$/g, "");
7342
7650
  return `brightsy_${cleaned || "team"}`;
7343
7651
  }
7344
- var import_node_fs20, import_node_path23;
7652
+ var import_node_fs21, import_node_path24;
7345
7653
  var init_connected_teams = __esm({
7346
7654
  "src/brightsy/connected-teams.ts"() {
7347
7655
  "use strict";
7348
- import_node_fs20 = require("fs");
7349
- import_node_path23 = require("path");
7656
+ import_node_fs21 = require("fs");
7657
+ import_node_path24 = require("path");
7350
7658
  init_paths();
7351
7659
  init_accounts();
7352
7660
  init_config();
@@ -7765,11 +8073,11 @@ async function syncCliForTarget(accountId) {
7765
8073
  }
7766
8074
  applyConnectedTeamToCli(team);
7767
8075
  }
7768
- var import_node_fs21, brightsyAdapter;
8076
+ var import_node_fs22, brightsyAdapter;
7769
8077
  var init_brightsy = __esm({
7770
8078
  "src/agents/brightsy.ts"() {
7771
8079
  "use strict";
7772
- import_node_fs21 = require("fs");
8080
+ import_node_fs22 = require("fs");
7773
8081
  init_run();
7774
8082
  init_connected_teams();
7775
8083
  init_config();
@@ -7784,7 +8092,7 @@ var init_brightsy = __esm({
7784
8092
  async detect() {
7785
8093
  const brightsy = resolveAgentExecutable("brightsy");
7786
8094
  if (brightsy !== "brightsy") {
7787
- if (!(0, import_node_fs21.existsSync)(brightsy)) {
8095
+ if (!(0, import_node_fs22.existsSync)(brightsy)) {
7788
8096
  return {
7789
8097
  agent: "brightsy",
7790
8098
  installed: false,
@@ -7915,14 +8223,47 @@ function asRecord(input) {
7915
8223
  function str2(v) {
7916
8224
  return typeof v === "string" && v.trim() ? v : void 0;
7917
8225
  }
8226
+ function looksLikeFilePath(value) {
8227
+ if (value.startsWith("/") || value.startsWith("~/")) return true;
8228
+ if (/^[A-Za-z]:[\\/]/.test(value)) return true;
8229
+ return value.includes("/") || value.includes("\\");
8230
+ }
8231
+ function stripWorktreePrefix(path2, worktreePath) {
8232
+ if (!worktreePath) return path2;
8233
+ const prefix = worktreePath.replace(/[/\\]+$/, "");
8234
+ if (path2 === prefix || path2 === `${prefix}/`) return "";
8235
+ if (path2.startsWith(`${prefix}/`)) return path2.slice(prefix.length + 1);
8236
+ return path2;
8237
+ }
8238
+ function visibleToolRowDetail(detail, description, worktreePath) {
8239
+ if (!detail?.trim()) return void 0;
8240
+ const raw = detail.trim();
8241
+ const desc = (description ?? "").trim();
8242
+ if (!looksLikeFilePath(raw)) {
8243
+ if (desc === raw) return void 0;
8244
+ return raw;
8245
+ }
8246
+ const rel = stripWorktreePrefix(raw, worktreePath);
8247
+ if (!rel) return void 0;
8248
+ const base = fileBasename(rel);
8249
+ if (desc && (desc === base || desc.endsWith(` ${base}`))) return void 0;
8250
+ if (rel.length <= 42) return rel;
8251
+ const parts = rel.split(/[/\\]/).filter(Boolean);
8252
+ if (parts.length >= 2) return `\u2026/${parts.slice(-2).join("/")}`;
8253
+ return base;
8254
+ }
7918
8255
  function toolDetail(name, input) {
7919
8256
  if (!input) return void 0;
7920
8257
  const command = str2(input.command) ?? str2(input.cmd);
7921
8258
  if (command) return command;
8259
+ const pattern = str2(input.pattern) ?? str2(input.glob) ?? str2(input.glob_pattern);
8260
+ const isSearch = /grep|glob|search|ripgrep|findfiles|semsearch/i.test(name);
8261
+ if (isSearch && pattern) {
8262
+ return pattern.length > 80 ? `${pattern.slice(0, 77)}\u2026` : pattern;
8263
+ }
7922
8264
  const path2 = str2(input.file_path) ?? str2(input.path) ?? str2(input.filePath) ?? str2(input.filename);
7923
8265
  if (path2) return path2;
7924
- const pattern = str2(input.pattern) ?? str2(input.glob) ?? str2(input.glob_pattern);
7925
- if (pattern) return pattern;
8266
+ if (pattern) return pattern.length > 80 ? `${pattern.slice(0, 77)}\u2026` : pattern;
7926
8267
  const query = str2(input.query) ?? str2(input.prompt);
7927
8268
  if (query) return query.length > 80 ? `${query.slice(0, 77)}\u2026` : query;
7928
8269
  try {
@@ -8441,43 +8782,43 @@ function electronResourcesPath() {
8441
8782
  function packagedCursorRuntimeDir() {
8442
8783
  const resources = electronResourcesPath();
8443
8784
  if (!resources) return null;
8444
- const dir = (0, import_node_path24.join)(resources, "cursor-runtime");
8445
- if (!(0, import_node_fs22.existsSync)((0, import_node_path24.join)(dir, "core-dist", "agents", "cursor-runner.js"))) return null;
8785
+ const dir = (0, import_node_path25.join)(resources, "cursor-runtime");
8786
+ if (!(0, import_node_fs23.existsSync)((0, import_node_path25.join)(dir, "core-dist", "agents", "cursor-runner.js"))) return null;
8446
8787
  return dir;
8447
8788
  }
8448
8789
  function packagedCursorRunnerPath() {
8449
8790
  const dir = packagedCursorRuntimeDir();
8450
- return dir ? (0, import_node_path24.join)(dir, "core-dist", "agents", "cursor-runner.js") : null;
8791
+ return dir ? (0, import_node_path25.join)(dir, "core-dist", "agents", "cursor-runner.js") : null;
8451
8792
  }
8452
8793
  function packagedMcpDir() {
8453
8794
  const resources = electronResourcesPath();
8454
8795
  if (!resources) return null;
8455
- const dir = (0, import_node_path24.join)(resources, "sideboard-mcp");
8456
- if (!(0, import_node_fs22.existsSync)((0, import_node_path24.join)(dir, "core-dist", "mcp", "run-stdio.js"))) return null;
8796
+ const dir = (0, import_node_path25.join)(resources, "sideboard-mcp");
8797
+ if (!(0, import_node_fs23.existsSync)((0, import_node_path25.join)(dir, "core-dist", "mcp", "run-stdio.js"))) return null;
8457
8798
  return dir;
8458
8799
  }
8459
8800
  function packagedMcpStdioPath() {
8460
8801
  const dir = packagedMcpDir();
8461
- return dir ? (0, import_node_path24.join)(dir, "core-dist", "mcp", "run-stdio.js") : null;
8802
+ return dir ? (0, import_node_path25.join)(dir, "core-dist", "mcp", "run-stdio.js") : null;
8462
8803
  }
8463
8804
  function packagedBundledNodePath() {
8464
8805
  const resources = electronResourcesPath();
8465
8806
  if (!resources) return null;
8466
- const bin = (0, import_node_path24.join)(resources, "node", "bin", "node");
8467
- if (!(0, import_node_fs22.existsSync)(bin)) return null;
8807
+ const bin = (0, import_node_path25.join)(resources, "node", "bin", "node");
8808
+ if (!(0, import_node_fs23.existsSync)(bin)) return null;
8468
8809
  return bin;
8469
8810
  }
8470
8811
  function packagedCursorRipgrepCandidate(platformPkg, binName) {
8471
8812
  const dir = packagedCursorRuntimeDir();
8472
8813
  if (!dir) return null;
8473
- return (0, import_node_path24.join)(dir, "node_modules", platformPkg, "bin", binName);
8814
+ return (0, import_node_path25.join)(dir, "node_modules", platformPkg, "bin", binName);
8474
8815
  }
8475
- var import_node_fs22, import_node_path24;
8816
+ var import_node_fs23, import_node_path25;
8476
8817
  var init_packaged_runtime = __esm({
8477
8818
  "src/agents/packaged-runtime.ts"() {
8478
8819
  "use strict";
8479
- import_node_fs22 = require("fs");
8480
- import_node_path24 = require("path");
8820
+ import_node_fs23 = require("fs");
8821
+ import_node_path25 = require("path");
8481
8822
  }
8482
8823
  });
8483
8824
 
@@ -8511,7 +8852,7 @@ function unpackedAsarPath(filePath) {
8511
8852
  if (!isAsarPath(filePath)) return null;
8512
8853
  const unpacked = filePath.replace(/\.asar(?=[/\\])/, ".asar.unpacked");
8513
8854
  if (unpacked === filePath) return null;
8514
- return (0, import_node_fs23.existsSync)(unpacked) ? unpacked : null;
8855
+ return (0, import_node_fs24.existsSync)(unpacked) ? unpacked : null;
8515
8856
  }
8516
8857
  function nodeReadableScriptPath(scriptPath) {
8517
8858
  return unpackedAsarPath(scriptPath) ?? scriptPath;
@@ -8551,37 +8892,37 @@ function pickPreferredNode(candidates) {
8551
8892
  return best;
8552
8893
  }
8553
8894
  function versionDirNodeBins(root, toBin) {
8554
- if (!(0, import_node_fs23.existsSync)(root)) return [];
8895
+ if (!(0, import_node_fs24.existsSync)(root)) return [];
8555
8896
  try {
8556
- return (0, import_node_fs23.readdirSync)(root).map(toBin);
8897
+ return (0, import_node_fs24.readdirSync)(root).map(toBin);
8557
8898
  } catch {
8558
8899
  return [];
8559
8900
  }
8560
8901
  }
8561
8902
  function defaultNodeBinCandidates(home = (0, import_node_os7.homedir)()) {
8562
8903
  const kegs = ["/opt/homebrew", "/usr/local"].flatMap(
8563
- (prefix) => PREFERRED_LTS_MAJORS.map((major) => (0, import_node_path25.join)(prefix, "opt", `node@${major}`, "bin", "node"))
8904
+ (prefix) => PREFERRED_LTS_MAJORS.map((major) => (0, import_node_path26.join)(prefix, "opt", `node@${major}`, "bin", "node"))
8564
8905
  );
8565
8906
  return [
8566
8907
  ...kegs,
8567
8908
  "/opt/homebrew/bin/node",
8568
8909
  "/usr/local/bin/node",
8569
- (0, import_node_path25.join)(home, ".local/share/fnm/aliases/default/bin/node"),
8570
- (0, import_node_path25.join)(home, ".nvm/current/bin/node"),
8571
- (0, import_node_path25.join)(home, ".volta/bin/node"),
8572
- (0, import_node_path25.join)(home, ".asdf/shims/node"),
8573
- (0, import_node_path25.join)(home, ".local/share/mise/shims/node"),
8910
+ (0, import_node_path26.join)(home, ".local/share/fnm/aliases/default/bin/node"),
8911
+ (0, import_node_path26.join)(home, ".nvm/current/bin/node"),
8912
+ (0, import_node_path26.join)(home, ".volta/bin/node"),
8913
+ (0, import_node_path26.join)(home, ".asdf/shims/node"),
8914
+ (0, import_node_path26.join)(home, ".local/share/mise/shims/node"),
8574
8915
  ...versionDirNodeBins(
8575
- (0, import_node_path25.join)(home, ".nvm", "versions", "node"),
8576
- (name) => (0, import_node_path25.join)(home, ".nvm", "versions", "node", name, "bin", "node")
8916
+ (0, import_node_path26.join)(home, ".nvm", "versions", "node"),
8917
+ (name) => (0, import_node_path26.join)(home, ".nvm", "versions", "node", name, "bin", "node")
8577
8918
  ),
8578
8919
  ...versionDirNodeBins(
8579
- (0, import_node_path25.join)(home, ".local/share/fnm", "node-versions"),
8580
- (name) => (0, import_node_path25.join)(home, ".local/share/fnm", "node-versions", name, "installation", "bin", "node")
8920
+ (0, import_node_path26.join)(home, ".local/share/fnm", "node-versions"),
8921
+ (name) => (0, import_node_path26.join)(home, ".local/share/fnm", "node-versions", name, "installation", "bin", "node")
8581
8922
  ),
8582
8923
  ...versionDirNodeBins(
8583
- (0, import_node_path25.join)(home, ".volta", "tools", "image", "node"),
8584
- (name) => (0, import_node_path25.join)(home, ".volta", "tools", "image", "node", name, "bin", "node")
8924
+ (0, import_node_path26.join)(home, ".volta", "tools", "image", "node"),
8925
+ (name) => (0, import_node_path26.join)(home, ".volta", "tools", "image", "node", name, "bin", "node")
8585
8926
  )
8586
8927
  ];
8587
8928
  }
@@ -8590,10 +8931,10 @@ function uniqueExistingNodeBins(paths) {
8590
8931
  const out = [];
8591
8932
  for (const raw of paths) {
8592
8933
  const p = raw.trim();
8593
- if (!p || !(0, import_node_fs23.existsSync)(p) || isElectronLikeCommand(p)) continue;
8934
+ if (!p || !(0, import_node_fs24.existsSync)(p) || isElectronLikeCommand(p)) continue;
8594
8935
  let key = p;
8595
8936
  try {
8596
- key = (0, import_node_fs23.realpathSync)(p);
8937
+ key = (0, import_node_fs24.realpathSync)(p);
8597
8938
  } catch {
8598
8939
  continue;
8599
8940
  }
@@ -8673,13 +9014,13 @@ async function resolveNodeLaunch(scriptPath) {
8673
9014
  env: { ELECTRON_RUN_AS_NODE: "1" }
8674
9015
  };
8675
9016
  }
8676
- var import_node_fs23, import_node_os7, import_node_path25, AGENT_RUNNER_MAX_OLD_SPACE_MB, MAX_OLD_SPACE_FLAG, PREFERRED_LTS_MAJORS;
9017
+ var import_node_fs24, import_node_os7, import_node_path26, AGENT_RUNNER_MAX_OLD_SPACE_MB, MAX_OLD_SPACE_FLAG, PREFERRED_LTS_MAJORS;
8677
9018
  var init_node_launch = __esm({
8678
9019
  "src/agents/node-launch.ts"() {
8679
9020
  "use strict";
8680
- import_node_fs23 = require("fs");
9021
+ import_node_fs24 = require("fs");
8681
9022
  import_node_os7 = require("os");
8682
- import_node_path25 = require("path");
9023
+ import_node_path26 = require("path");
8683
9024
  init_nested_electron_env();
8684
9025
  init_run();
8685
9026
  init_packaged_runtime();
@@ -8773,37 +9114,37 @@ function corePackageDir() {
8773
9114
  try {
8774
9115
  const url = import_meta.url;
8775
9116
  if (typeof url === "string" && url.length > 0) {
8776
- return (0, import_node_path26.dirname)((0, import_node_url.fileURLToPath)(url));
9117
+ return (0, import_node_path27.dirname)((0, import_node_url.fileURLToPath)(url));
8777
9118
  }
8778
9119
  } catch {
8779
9120
  }
8780
9121
  try {
8781
- const req = (0, import_node_module.createRequire)((0, import_node_path26.join)(process.cwd(), "package.json"));
8782
- return (0, import_node_path26.dirname)(req.resolve("@sideboard-ai/core"));
9122
+ const req = (0, import_node_module.createRequire)((0, import_node_path27.join)(process.cwd(), "package.json"));
9123
+ return (0, import_node_path27.dirname)(req.resolve("@sideboard-ai/core"));
8783
9124
  } catch {
8784
9125
  return process.cwd();
8785
9126
  }
8786
9127
  }
8787
9128
  function findSideboardMcpJsEntry() {
8788
9129
  const override = process.env.SIDEBOARD_MCP_ENTRY?.trim() || process.env.SIDEBOARD_CLI?.trim();
8789
- if (override && (0, import_node_fs24.existsSync)(override)) return override;
9130
+ if (override && (0, import_node_fs25.existsSync)(override)) return override;
8790
9131
  const packaged = packagedMcpStdioPath();
8791
9132
  if (packaged) return packaged;
8792
9133
  let dir = corePackageDir();
8793
9134
  for (let i = 0; i < 10; i++) {
8794
9135
  const candidates = [
8795
- (0, import_node_path26.join)(dir, "mcp/run-stdio.js"),
8796
- (0, import_node_path26.join)(dir, "mcp/run-stdio.cjs"),
8797
- (0, import_node_path26.join)(dir, "dist/mcp/run-stdio.js"),
8798
- (0, import_node_path26.join)(dir, "dist/mcp/run-stdio.cjs"),
8799
- (0, import_node_path26.join)(dir, "packages/core/dist/mcp/run-stdio.js"),
8800
- (0, import_node_path26.join)(dir, "packages/cli/dist/index.js"),
8801
- (0, import_node_path26.join)(dir, "cli/dist/index.js")
9136
+ (0, import_node_path27.join)(dir, "mcp/run-stdio.js"),
9137
+ (0, import_node_path27.join)(dir, "mcp/run-stdio.cjs"),
9138
+ (0, import_node_path27.join)(dir, "dist/mcp/run-stdio.js"),
9139
+ (0, import_node_path27.join)(dir, "dist/mcp/run-stdio.cjs"),
9140
+ (0, import_node_path27.join)(dir, "packages/core/dist/mcp/run-stdio.js"),
9141
+ (0, import_node_path27.join)(dir, "packages/cli/dist/index.js"),
9142
+ (0, import_node_path27.join)(dir, "cli/dist/index.js")
8802
9143
  ];
8803
9144
  for (const p of candidates) {
8804
- if ((0, import_node_fs24.existsSync)(p) && !isAsarPath(p)) return p;
9145
+ if ((0, import_node_fs25.existsSync)(p) && !isAsarPath(p)) return p;
8805
9146
  }
8806
- const parent = (0, import_node_path26.dirname)(dir);
9147
+ const parent = (0, import_node_path27.dirname)(dir);
8807
9148
  if (parent === dir) break;
8808
9149
  dir = parent;
8809
9150
  }
@@ -8945,22 +9286,22 @@ function writeMcpServersConfig(servers) {
8945
9286
  ...env ? { env } : {}
8946
9287
  };
8947
9288
  }
8948
- const dir = (0, import_node_fs24.mkdtempSync)((0, import_node_path26.join)((0, import_node_os8.tmpdir)(), "sideboard-mcp-"));
8949
- const cfgPath = (0, import_node_path26.join)(dir, "mcp.json");
8950
- (0, import_node_fs24.writeFileSync)(cfgPath, JSON.stringify({ mcpServers }, null, 2));
9289
+ const dir = (0, import_node_fs25.mkdtempSync)((0, import_node_path27.join)((0, import_node_os8.tmpdir)(), "sideboard-mcp-"));
9290
+ const cfgPath = (0, import_node_path27.join)(dir, "mcp.json");
9291
+ (0, import_node_fs25.writeFileSync)(cfgPath, JSON.stringify({ mcpServers }, null, 2));
8951
9292
  return cfgPath;
8952
9293
  }
8953
9294
  async function writeInjectedMcpConfig(opts) {
8954
9295
  return writeMcpServersConfig(await buildInjectedMcpServers(opts));
8955
9296
  }
8956
- var import_node_fs24, import_node_module, import_node_os8, import_node_path26, import_node_url, import_meta, SIDEBOARD_MCP_ALLOWED_TOOLS, SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS, BRIGHTSY_MCP_ALLOWED_TOOLS, brightsyMcpCommandCache, BRIGHTSY_WORD;
9297
+ var import_node_fs25, import_node_module, import_node_os8, import_node_path27, import_node_url, import_meta, SIDEBOARD_MCP_ALLOWED_TOOLS, SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS, BRIGHTSY_MCP_ALLOWED_TOOLS, brightsyMcpCommandCache, BRIGHTSY_WORD;
8957
9298
  var init_injected_mcp = __esm({
8958
9299
  "src/agents/injected-mcp.ts"() {
8959
9300
  "use strict";
8960
- import_node_fs24 = require("fs");
9301
+ import_node_fs25 = require("fs");
8961
9302
  import_node_module = require("module");
8962
9303
  import_node_os8 = require("os");
8963
- import_node_path26 = require("path");
9304
+ import_node_path27 = require("path");
8964
9305
  import_node_url = require("url");
8965
9306
  init_run();
8966
9307
  init_config();
@@ -9264,11 +9605,11 @@ function parseIssuesJson(raw) {
9264
9605
  }
9265
9606
  return [];
9266
9607
  }
9267
- var import_node_fs25, BASE_ALLOWED_TOOLS, CLAUDE_PRINT_BG_WAIT_CEILING_MS, CLAUDE_CHROME_ALLOWED_TOOLS, CLAUDE_PROMPT_ARG_MAX, mcpListCache, claudeAdapter;
9608
+ var import_node_fs26, BASE_ALLOWED_TOOLS, CLAUDE_PRINT_BG_WAIT_CEILING_MS, CLAUDE_CHROME_ALLOWED_TOOLS, CLAUDE_PROMPT_ARG_MAX, mcpListCache, claudeAdapter;
9268
9609
  var init_claude = __esm({
9269
9610
  "src/agents/claude.ts"() {
9270
9611
  "use strict";
9271
- import_node_fs25 = require("fs");
9612
+ import_node_fs26 = require("fs");
9272
9613
  init_run();
9273
9614
  init_app_settings();
9274
9615
  init_claude_mcp();
@@ -9308,7 +9649,7 @@ var init_claude = __esm({
9308
9649
  async detect() {
9309
9650
  const claude = resolveClaudeExecutable();
9310
9651
  if (claude !== "claude") {
9311
- if (!(0, import_node_fs25.existsSync)(claude)) {
9652
+ if (!(0, import_node_fs26.existsSync)(claude)) {
9312
9653
  return {
9313
9654
  agent: "claude",
9314
9655
  installed: false,
@@ -9563,7 +9904,7 @@ async function listCodexModels() {
9563
9904
  if (codex === "codex") {
9564
9905
  const which = await run("which", ["codex"], { reject: false });
9565
9906
  if (which.exitCode !== 0) return FALLBACK_CODEX_MODELS;
9566
- } else if (!(0, import_node_fs26.existsSync)(codex)) {
9907
+ } else if (!(0, import_node_fs27.existsSync)(codex)) {
9567
9908
  return FALLBACK_CODEX_MODELS;
9568
9909
  }
9569
9910
  const listed = await run(codex, ["debug", "models"], { reject: false });
@@ -9598,12 +9939,12 @@ function usageFromCodex(usage) {
9598
9939
  }
9599
9940
  function codexConfigHasNetworkAccess() {
9600
9941
  const candidates = [
9601
- (0, import_node_path27.join)((0, import_node_os9.homedir)(), ".codex", "config.toml"),
9602
- (0, import_node_path27.join)((0, import_node_os9.homedir)(), ".config", "codex", "config.toml")
9942
+ (0, import_node_path28.join)((0, import_node_os9.homedir)(), ".codex", "config.toml"),
9943
+ (0, import_node_path28.join)((0, import_node_os9.homedir)(), ".config", "codex", "config.toml")
9603
9944
  ];
9604
9945
  for (const path2 of candidates) {
9605
- if (!(0, import_node_fs26.existsSync)(path2)) continue;
9606
- const text5 = (0, import_node_fs26.readFileSync)(path2, "utf8");
9946
+ if (!(0, import_node_fs27.existsSync)(path2)) continue;
9947
+ const text5 = (0, import_node_fs27.readFileSync)(path2, "utf8");
9607
9948
  if (/network_access\s*=\s*true/.test(text5)) return true;
9608
9949
  }
9609
9950
  return false;
@@ -9635,21 +9976,21 @@ function asRecord2(value) {
9635
9976
  return void 0;
9636
9977
  }
9637
9978
  function codexLooksAuthenticated() {
9638
- const authPath = (0, import_node_path27.join)((0, import_node_os9.homedir)(), ".codex", "auth.json");
9639
- if (!(0, import_node_fs26.existsSync)(authPath)) return false;
9979
+ const authPath = (0, import_node_path28.join)((0, import_node_os9.homedir)(), ".codex", "auth.json");
9980
+ if (!(0, import_node_fs27.existsSync)(authPath)) return false;
9640
9981
  try {
9641
- return (0, import_node_fs26.statSync)(authPath).size > 2;
9982
+ return (0, import_node_fs27.statSync)(authPath).size > 2;
9642
9983
  } catch {
9643
9984
  return false;
9644
9985
  }
9645
9986
  }
9646
- var import_node_fs26, import_node_os9, import_node_path27, CODEX_PROMPT_ARG_MAX, FALLBACK_CODEX_MODELS, cachedCodexModels, CODEX_MODEL_CACHE_MS, codexAdapter;
9987
+ var import_node_fs27, import_node_os9, import_node_path28, CODEX_PROMPT_ARG_MAX, FALLBACK_CODEX_MODELS, cachedCodexModels, CODEX_MODEL_CACHE_MS, codexAdapter;
9647
9988
  var init_codex = __esm({
9648
9989
  "src/agents/codex.ts"() {
9649
9990
  "use strict";
9650
- import_node_fs26 = require("fs");
9991
+ import_node_fs27 = require("fs");
9651
9992
  import_node_os9 = require("os");
9652
- import_node_path27 = require("path");
9993
+ import_node_path28 = require("path");
9653
9994
  init_run();
9654
9995
  init_app_settings();
9655
9996
  init_global_workspace();
@@ -9674,7 +10015,7 @@ var init_codex = __esm({
9674
10015
  async detect() {
9675
10016
  const codex = resolveAgentExecutable("codex");
9676
10017
  if (codex !== "codex") {
9677
- if (!(0, import_node_fs26.existsSync)(codex)) {
10018
+ if (!(0, import_node_fs27.existsSync)(codex)) {
9678
10019
  return {
9679
10020
  agent: "codex",
9680
10021
  installed: false,
@@ -10161,21 +10502,21 @@ function platformRipgrepPackage() {
10161
10502
  }
10162
10503
  function usableRipgrepPath(candidate) {
10163
10504
  const raw = candidate?.trim();
10164
- if (!raw || !(0, import_node_path28.isAbsolute)(raw)) return null;
10505
+ if (!raw || !(0, import_node_path29.isAbsolute)(raw)) return null;
10165
10506
  const readable = nodeReadableScriptPath(raw);
10166
- if (!(0, import_node_fs27.existsSync)(readable) || isAsarPath(readable)) return null;
10507
+ if (!(0, import_node_fs28.existsSync)(readable) || isAsarPath(readable)) return null;
10167
10508
  return readable;
10168
10509
  }
10169
10510
  function walkForBundledRipgrep(startFile) {
10170
10511
  if (!startFile) return null;
10171
10512
  const pkg = platformRipgrepPackage();
10172
10513
  const name = rgBinaryName();
10173
- let dir = (0, import_node_path28.dirname)((0, import_node_path28.resolve)(startFile));
10174
- const root = (0, import_node_path28.parse)(dir).root;
10514
+ let dir = (0, import_node_path29.dirname)((0, import_node_path29.resolve)(startFile));
10515
+ const root = (0, import_node_path29.parse)(dir).root;
10175
10516
  while (dir !== root) {
10176
- const hit = usableRipgrepPath((0, import_node_path28.join)(dir, "node_modules", pkg, "bin", name));
10517
+ const hit = usableRipgrepPath((0, import_node_path29.join)(dir, "node_modules", pkg, "bin", name));
10177
10518
  if (hit) return hit;
10178
- const next = (0, import_node_path28.dirname)(dir);
10519
+ const next = (0, import_node_path29.dirname)(dir);
10179
10520
  if (next === dir) break;
10180
10521
  dir = next;
10181
10522
  }
@@ -10185,7 +10526,7 @@ function requireResolveBundledRipgrep(fromFile) {
10185
10526
  try {
10186
10527
  const req = (0, import_node_module2.createRequire)(fromFile);
10187
10528
  const pkgJson = req.resolve(`${platformRipgrepPackage()}/package.json`);
10188
- return usableRipgrepPath((0, import_node_path28.join)((0, import_node_path28.dirname)(pkgJson), "bin", rgBinaryName()));
10529
+ return usableRipgrepPath((0, import_node_path29.join)((0, import_node_path29.dirname)(pkgJson), "bin", rgBinaryName()));
10189
10530
  } catch {
10190
10531
  return null;
10191
10532
  }
@@ -10207,13 +10548,13 @@ function cursorRipgrepEnv(opts) {
10207
10548
  const path2 = resolveCursorRipgrepPath(opts);
10208
10549
  return path2 ? { [RIPGREP_ENV]: path2 } : {};
10209
10550
  }
10210
- var import_node_fs27, import_node_module2, import_node_path28, RIPGREP_ENV;
10551
+ var import_node_fs28, import_node_module2, import_node_path29, RIPGREP_ENV;
10211
10552
  var init_cursor_ripgrep = __esm({
10212
10553
  "src/agents/cursor-ripgrep.ts"() {
10213
10554
  "use strict";
10214
- import_node_fs27 = require("fs");
10555
+ import_node_fs28 = require("fs");
10215
10556
  import_node_module2 = require("module");
10216
- import_node_path28 = require("path");
10557
+ import_node_path29 = require("path");
10217
10558
  init_node_launch();
10218
10559
  init_packaged_runtime();
10219
10560
  RIPGREP_ENV = "CURSOR_RIPGREP_PATH";
@@ -10259,11 +10600,11 @@ function entryDir() {
10259
10600
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
10260
10601
  if (cjsDir) return cjsDir;
10261
10602
  try {
10262
- return (0, import_node_path29.dirname)((0, import_node_url2.fileURLToPath)(import_meta2.url));
10603
+ return (0, import_node_path30.dirname)((0, import_node_url2.fileURLToPath)(import_meta2.url));
10263
10604
  } catch {
10264
10605
  try {
10265
10606
  const req = (0, import_node_module3.createRequire)(process.cwd() + "/");
10266
- return (0, import_node_path29.dirname)(req.resolve("@sideboard-ai/core"));
10607
+ return (0, import_node_path30.dirname)(req.resolve("@sideboard-ai/core"));
10267
10608
  } catch {
10268
10609
  return process.cwd();
10269
10610
  }
@@ -10274,27 +10615,27 @@ function cursorRunnerPath() {
10274
10615
  if (packaged) return packaged;
10275
10616
  const root = entryDir();
10276
10617
  const candidates = [
10277
- (0, import_node_path29.join)(root, "agents", "cursor-runner.js"),
10278
- (0, import_node_path29.join)(root, "agents", "cursor-runner.cjs"),
10618
+ (0, import_node_path30.join)(root, "agents", "cursor-runner.js"),
10619
+ (0, import_node_path30.join)(root, "agents", "cursor-runner.cjs"),
10279
10620
  // If somehow resolved from package root instead of dist/
10280
- (0, import_node_path29.join)(root, "dist", "agents", "cursor-runner.js"),
10281
- (0, import_node_path29.join)(root, "dist", "agents", "cursor-runner.cjs"),
10621
+ (0, import_node_path30.join)(root, "dist", "agents", "cursor-runner.js"),
10622
+ (0, import_node_path30.join)(root, "dist", "agents", "cursor-runner.cjs"),
10282
10623
  // Source tree (dev): packages/core/src/agents/cursor-runner.ts
10283
- (0, import_node_path29.join)(root, "cursor-runner.ts"),
10284
- (0, import_node_path29.join)(root, "src", "agents", "cursor-runner.ts")
10624
+ (0, import_node_path30.join)(root, "cursor-runner.ts"),
10625
+ (0, import_node_path30.join)(root, "src", "agents", "cursor-runner.ts")
10285
10626
  ];
10286
10627
  for (const candidate of candidates) {
10287
- if ((0, import_node_fs28.existsSync)(candidate)) return candidate;
10628
+ if ((0, import_node_fs29.existsSync)(candidate)) return candidate;
10288
10629
  }
10289
10630
  return candidates[0];
10290
10631
  }
10291
- var import_node_fs28, import_node_module3, import_node_path29, import_node_url2, import_sdk, import_meta2, FALLBACK_CURSOR_MODELS, cachedModels, MODEL_CACHE_MS, cursorAdapter;
10632
+ var import_node_fs29, import_node_module3, import_node_path30, import_node_url2, import_sdk, import_meta2, FALLBACK_CURSOR_MODELS, cachedModels, MODEL_CACHE_MS, cursorAdapter;
10292
10633
  var init_cursor = __esm({
10293
10634
  "src/agents/cursor.ts"() {
10294
10635
  "use strict";
10295
- import_node_fs28 = require("fs");
10636
+ import_node_fs29 = require("fs");
10296
10637
  import_node_module3 = require("module");
10297
- import_node_path29 = require("path");
10638
+ import_node_path30 = require("path");
10298
10639
  import_node_url2 = require("url");
10299
10640
  import_sdk = require("@cursor/sdk");
10300
10641
  init_run();
@@ -10445,7 +10786,7 @@ async function listOpencodeModels() {
10445
10786
  if (opencode === "opencode") {
10446
10787
  const which = await run("which", ["opencode"], { reject: false });
10447
10788
  if (which.exitCode !== 0) return FALLBACK_OPENCODE_MODELS;
10448
- } else if (!(0, import_node_fs29.existsSync)(opencode)) {
10789
+ } else if (!(0, import_node_fs30.existsSync)(opencode)) {
10449
10790
  return FALLBACK_OPENCODE_MODELS;
10450
10791
  }
10451
10792
  const listed = await run(opencode, ["models"], { reject: false });
@@ -10475,11 +10816,11 @@ function usageFromOpencode(tokens) {
10475
10816
  cacheWriteTokens: tokens.cache?.write ? Number(tokens.cache.write) : void 0
10476
10817
  };
10477
10818
  }
10478
- var import_node_fs29, FALLBACK_OPENCODE_MODELS, cachedOpencodeModels, OPENCODE_MODEL_CACHE_MS, opencodeAdapter;
10819
+ var import_node_fs30, FALLBACK_OPENCODE_MODELS, cachedOpencodeModels, OPENCODE_MODEL_CACHE_MS, opencodeAdapter;
10479
10820
  var init_opencode = __esm({
10480
10821
  "src/agents/opencode.ts"() {
10481
10822
  "use strict";
10482
- import_node_fs29 = require("fs");
10823
+ import_node_fs30 = require("fs");
10483
10824
  init_run();
10484
10825
  init_app_settings();
10485
10826
  init_global_workspace();
@@ -10506,7 +10847,7 @@ var init_opencode = __esm({
10506
10847
  async detect() {
10507
10848
  const opencode = resolveAgentExecutable("opencode");
10508
10849
  if (opencode !== "opencode") {
10509
- if (!(0, import_node_fs29.existsSync)(opencode)) {
10850
+ if (!(0, import_node_fs30.existsSync)(opencode)) {
10510
10851
  return {
10511
10852
  agent: "opencode",
10512
10853
  installed: false,
@@ -12009,7 +12350,7 @@ function forkMessageSlice(from, throughIndex) {
12009
12350
  function buildForkTranscriptAttachment(baseTitle, messages) {
12010
12351
  const title = baseTitle || "Chat";
12011
12352
  return {
12012
- id: (0, import_node_crypto5.randomUUID)(),
12353
+ id: (0, import_node_crypto6.randomUUID)(),
12013
12354
  name: `Transcript of ${title}.md`,
12014
12355
  kind: "transcript",
12015
12356
  content: formatTranscriptMarkdown(title, messages)
@@ -12066,11 +12407,11 @@ function forkChatTab(input) {
12066
12407
  }
12067
12408
  return tab;
12068
12409
  }
12069
- var import_node_crypto5;
12410
+ var import_node_crypto6;
12070
12411
  var init_chat_tabs = __esm({
12071
12412
  "src/threads/chat-tabs.ts"() {
12072
12413
  "use strict";
12073
- import_node_crypto5 = require("crypto");
12414
+ import_node_crypto6 = require("crypto");
12074
12415
  init_context_compact();
12075
12416
  init_teams();
12076
12417
  init_worktree_labels();
@@ -12240,21 +12581,21 @@ function shouldRefreshReviewRequestTemplate(content) {
12240
12581
  return LEGACY_REVIEW_TEMPLATE_MARKERS.every((m) => trimmed.includes(m));
12241
12582
  }
12242
12583
  function readTextIfPresent(abs) {
12243
- if (!(0, import_node_fs30.existsSync)(abs)) return null;
12584
+ if (!(0, import_node_fs31.existsSync)(abs)) return null;
12244
12585
  try {
12245
- const content = (0, import_node_fs30.readFileSync)(abs, "utf8");
12586
+ const content = (0, import_node_fs31.readFileSync)(abs, "utf8");
12246
12587
  return content.trim() ? content : null;
12247
12588
  } catch {
12248
12589
  return null;
12249
12590
  }
12250
12591
  }
12251
12592
  function readLocalGuidelines(worktreePath) {
12252
- const localAbs = (0, import_node_path30.join)(worktreePath, REVIEW_REQUEST_PATH);
12593
+ const localAbs = (0, import_node_path31.join)(worktreePath, REVIEW_REQUEST_PATH);
12253
12594
  const localContent = readTextIfPresent(localAbs);
12254
12595
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
12255
12596
  return { path: REVIEW_REQUEST_PATH, content: localContent };
12256
12597
  }
12257
- const legacyAbs = (0, import_node_path30.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
12598
+ const legacyAbs = (0, import_node_path31.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
12258
12599
  const legacyContent = readTextIfPresent(legacyAbs);
12259
12600
  if (legacyContent && !shouldRefreshReviewRequestTemplate(legacyContent)) {
12260
12601
  return { path: LEGACY_REVIEW_REQUEST_PATH, content: legacyContent };
@@ -12270,20 +12611,20 @@ function skillGuidelines(content, source) {
12270
12611
  };
12271
12612
  }
12272
12613
  function ensureReviewSkillFile(worktreePath) {
12273
- const abs = (0, import_node_path30.join)(worktreePath, REVIEW_SKILL_PATH);
12614
+ const abs = (0, import_node_path31.join)(worktreePath, REVIEW_SKILL_PATH);
12274
12615
  const existing = readTextIfPresent(abs);
12275
12616
  if (existing) {
12276
12617
  return { path: REVIEW_SKILL_PATH, content: existing, wrote: false };
12277
12618
  }
12278
- const fromRepo = readTextIfPresent((0, import_node_path30.join)(worktreePath, REPO_REVIEW_PATH));
12619
+ const fromRepo = readTextIfPresent((0, import_node_path31.join)(worktreePath, REPO_REVIEW_PATH));
12279
12620
  const fromLocal = readLocalGuidelines(worktreePath)?.content ?? null;
12280
12621
  const content = wrapReviewSkillMarkdown(fromRepo ?? fromLocal ?? REVIEW_REQUEST_TEMPLATE);
12281
- (0, import_node_fs30.mkdirSync)((0, import_node_path30.dirname)(abs), { recursive: true });
12282
- (0, import_node_fs30.writeFileSync)(abs, content, "utf8");
12622
+ (0, import_node_fs31.mkdirSync)((0, import_node_path31.dirname)(abs), { recursive: true });
12623
+ (0, import_node_fs31.writeFileSync)(abs, content, "utf8");
12283
12624
  return { path: REVIEW_SKILL_PATH, content, wrote: true };
12284
12625
  }
12285
12626
  function resolveReviewGuidelines(worktreePath) {
12286
- const skillContent = readTextIfPresent((0, import_node_path30.join)(worktreePath, REVIEW_SKILL_PATH));
12627
+ const skillContent = readTextIfPresent((0, import_node_path31.join)(worktreePath, REVIEW_SKILL_PATH));
12287
12628
  if (skillContent) return skillGuidelines(skillContent, "skill");
12288
12629
  const local = readLocalGuidelines(worktreePath);
12289
12630
  if (local) {
@@ -12305,7 +12646,7 @@ function buildReviewRequestAttachment(content, opts) {
12305
12646
  const path2 = opts?.path ?? REVIEW_SKILL_PATH;
12306
12647
  const name = opts?.name ?? (path2 === REVIEW_SKILL_PATH ? REVIEW_SKILL_NAME : path2 === REPO_REVIEW_PATH ? REPO_REVIEW_NAME : REVIEW_REQUEST_NAME);
12307
12648
  return {
12308
- id: (0, import_node_crypto6.randomUUID)(),
12649
+ id: (0, import_node_crypto7.randomUUID)(),
12309
12650
  name,
12310
12651
  kind: "file",
12311
12652
  path: path2,
@@ -12313,7 +12654,7 @@ function buildReviewRequestAttachment(content, opts) {
12313
12654
  };
12314
12655
  }
12315
12656
  function readExistingReviewRequestFile(worktreePath) {
12316
- return readTextIfPresent((0, import_node_path30.join)(worktreePath, REVIEW_SKILL_PATH)) ?? readTextIfPresent((0, import_node_path30.join)(worktreePath, REPO_REVIEW_PATH)) ?? readTextIfPresent((0, import_node_path30.join)(worktreePath, REVIEW_REQUEST_PATH)) ?? readTextIfPresent((0, import_node_path30.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
12657
+ return readTextIfPresent((0, import_node_path31.join)(worktreePath, REVIEW_SKILL_PATH)) ?? readTextIfPresent((0, import_node_path31.join)(worktreePath, REPO_REVIEW_PATH)) ?? readTextIfPresent((0, import_node_path31.join)(worktreePath, REVIEW_REQUEST_PATH)) ?? readTextIfPresent((0, import_node_path31.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
12317
12658
  }
12318
12659
  async function requestReview(threadRef, send2) {
12319
12660
  const from = findThreadByRef(threadRef);
@@ -12340,13 +12681,13 @@ async function requestReview(threadRef, send2) {
12340
12681
  const started = await send2(tab.id, REVIEW_REQUEST_PREFILL);
12341
12682
  return { tab: started, from };
12342
12683
  }
12343
- var import_node_crypto6, import_node_fs30, import_node_path30, REPO_REVIEW_PATH, REPO_REVIEW_NAME, REVIEW_REQUEST_PATH, LEGACY_REVIEW_REQUEST_PATH, REVIEW_REQUEST_NAME, REVIEW_REQUEST_PREFILL, LEGACY_REVIEW_TEMPLATE_MARKERS;
12684
+ var import_node_crypto7, import_node_fs31, import_node_path31, REPO_REVIEW_PATH, REPO_REVIEW_NAME, REVIEW_REQUEST_PATH, LEGACY_REVIEW_REQUEST_PATH, REVIEW_REQUEST_NAME, REVIEW_REQUEST_PREFILL, LEGACY_REVIEW_TEMPLATE_MARKERS;
12344
12685
  var init_request_review = __esm({
12345
12686
  "src/review/request-review.ts"() {
12346
12687
  "use strict";
12347
- import_node_crypto6 = require("crypto");
12348
- import_node_fs30 = require("fs");
12349
- import_node_path30 = require("path");
12688
+ import_node_crypto7 = require("crypto");
12689
+ import_node_fs31 = require("fs");
12690
+ import_node_path31 = require("path");
12350
12691
  init_global_workspace();
12351
12692
  init_chat_tabs();
12352
12693
  init_thread_store();
@@ -12371,9 +12712,9 @@ function matchSimpleGlob(pattern, name) {
12371
12712
  return new RegExp(`^${escaped}$`).test(name);
12372
12713
  }
12373
12714
  function readWorktreeInclude(repoPath) {
12374
- const path2 = (0, import_node_path31.join)(repoPath, ".worktreeinclude");
12375
- if (!(0, import_node_fs31.existsSync)(path2)) return [];
12376
- return (0, import_node_fs31.readFileSync)(path2, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
12715
+ const path2 = (0, import_node_path32.join)(repoPath, ".worktreeinclude");
12716
+ if (!(0, import_node_fs32.existsSync)(path2)) return [];
12717
+ return (0, import_node_fs32.readFileSync)(path2, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
12377
12718
  }
12378
12719
  function resolveFilesToCopy(repoPath) {
12379
12720
  const fromInclude = readWorktreeInclude(repoPath);
@@ -12383,10 +12724,10 @@ function resolveFilesToCopy(repoPath) {
12383
12724
  if (settings?.fileIncludeGlobs?.length) {
12384
12725
  const matched = [];
12385
12726
  try {
12386
- for (const entry of (0, import_node_fs31.readdirSync)(repoPath, { withFileTypes: true })) {
12727
+ for (const entry of (0, import_node_fs32.readdirSync)(repoPath, { withFileTypes: true })) {
12387
12728
  if (!entry.isFile()) continue;
12388
12729
  for (const glob of settings.fileIncludeGlobs) {
12389
- if (matchSimpleGlob(glob, entry.name) || matchSimpleGlob((0, import_node_path31.basename)(glob), entry.name)) {
12730
+ if (matchSimpleGlob(glob, entry.name) || matchSimpleGlob((0, import_node_path32.basename)(glob), entry.name)) {
12390
12731
  matched.push(entry.name);
12391
12732
  break;
12392
12733
  }
@@ -12398,7 +12739,7 @@ function resolveFilesToCopy(repoPath) {
12398
12739
  }
12399
12740
  const defaults = [];
12400
12741
  try {
12401
- for (const entry of (0, import_node_fs31.readdirSync)(repoPath, { withFileTypes: true })) {
12742
+ for (const entry of (0, import_node_fs32.readdirSync)(repoPath, { withFileTypes: true })) {
12402
12743
  if (entry.isFile() && entry.name.startsWith(".env")) {
12403
12744
  defaults.push(entry.name);
12404
12745
  }
@@ -12412,11 +12753,11 @@ function copyConfiguredFiles(repoPath, worktreePath) {
12412
12753
  const patterns = resolveFilesToCopy(repoPath);
12413
12754
  const copied = [];
12414
12755
  for (const rel of patterns) {
12415
- const src = (0, import_node_path31.join)(repoPath, rel);
12416
- if (!(0, import_node_fs31.existsSync)(src)) continue;
12417
- const dest = (0, import_node_path31.join)(worktreePath, rel);
12418
- (0, import_node_fs31.mkdirSync)((0, import_node_path31.dirname)(dest), { recursive: true });
12419
- (0, import_node_fs31.copyFileSync)(src, dest);
12756
+ const src = (0, import_node_path32.join)(repoPath, rel);
12757
+ if (!(0, import_node_fs32.existsSync)(src)) continue;
12758
+ const dest = (0, import_node_path32.join)(worktreePath, rel);
12759
+ (0, import_node_fs32.mkdirSync)((0, import_node_path32.dirname)(dest), { recursive: true });
12760
+ (0, import_node_fs32.copyFileSync)(src, dest);
12420
12761
  copied.push(rel);
12421
12762
  }
12422
12763
  return copied;
@@ -12451,7 +12792,7 @@ function buildWorkspaceScriptEnv(opts, baseEnv) {
12451
12792
  const env = stripNestedElectronEnv({
12452
12793
  ...baseEnv ?? process.env
12453
12794
  });
12454
- const name = opts.workspaceName ?? (0, import_node_path31.basename)(opts.worktreePath);
12795
+ const name = opts.workspaceName ?? (0, import_node_path32.basename)(opts.worktreePath);
12455
12796
  const ports = opts.ports ?? [];
12456
12797
  const primary = ports[0];
12457
12798
  env.SIDEBOARD_WORKSPACE_NAME = name;
@@ -12712,13 +13053,13 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
12712
13053
  done: handle.done
12713
13054
  };
12714
13055
  }
12715
- var import_node_fs31, import_node_net, import_node_path31, import_execa4, import_node_readline3, PORT_RANGE_SIZE, cachedLoginEnv;
13056
+ var import_node_fs32, import_node_net, import_node_path32, import_execa4, import_node_readline3, PORT_RANGE_SIZE, cachedLoginEnv;
12716
13057
  var init_conductor = __esm({
12717
13058
  "src/hook/conductor.ts"() {
12718
13059
  "use strict";
12719
- import_node_fs31 = require("fs");
13060
+ import_node_fs32 = require("fs");
12720
13061
  import_node_net = require("net");
12721
- import_node_path31 = require("path");
13062
+ import_node_path32 = require("path");
12722
13063
  import_execa4 = require("execa");
12723
13064
  import_node_readline3 = require("readline");
12724
13065
  init_settings();
@@ -12744,9 +13085,9 @@ async function findOrphanWorktrees(repoPaths) {
12744
13085
  repoPaths?.length ? repoPaths : threads.map((t) => t.repoPath).filter(Boolean)
12745
13086
  );
12746
13087
  const homeRoot = sideboardWorkspacesDir();
12747
- if ((0, import_node_fs32.existsSync)(homeRoot)) {
13088
+ if ((0, import_node_fs33.existsSync)(homeRoot)) {
12748
13089
  try {
12749
- for (const entry of (0, import_node_fs32.readdirSync)(homeRoot, { withFileTypes: true })) {
13090
+ for (const entry of (0, import_node_fs33.readdirSync)(homeRoot, { withFileTypes: true })) {
12750
13091
  if (!entry.isDirectory()) continue;
12751
13092
  void entry;
12752
13093
  }
@@ -12756,7 +13097,7 @@ async function findOrphanWorktrees(repoPaths) {
12756
13097
  const orphans = [];
12757
13098
  const seen = /* @__PURE__ */ new Set();
12758
13099
  for (const repoPath of repos) {
12759
- if (!repoPath || !(0, import_node_fs32.existsSync)(repoPath)) continue;
13100
+ if (!repoPath || !(0, import_node_fs33.existsSync)(repoPath)) continue;
12760
13101
  try {
12761
13102
  const wts = await listWorktrees(repoPath);
12762
13103
  for (const wt of wts) {
@@ -12767,7 +13108,7 @@ async function findOrphanWorktrees(repoPaths) {
12767
13108
  seen.add(path2);
12768
13109
  let mtimeMs = 0;
12769
13110
  try {
12770
- mtimeMs = (0, import_node_fs32.statSync)(path2).mtimeMs;
13111
+ mtimeMs = (0, import_node_fs33.statSync)(path2).mtimeMs;
12771
13112
  } catch {
12772
13113
  mtimeMs = 0;
12773
13114
  }
@@ -12777,16 +13118,16 @@ async function findOrphanWorktrees(repoPaths) {
12777
13118
  }
12778
13119
  try {
12779
13120
  const root = worktreesRoot(repoPath);
12780
- if ((0, import_node_fs32.existsSync)(root)) {
12781
- for (const entry of (0, import_node_fs32.readdirSync)(root, { withFileTypes: true })) {
13121
+ if ((0, import_node_fs33.existsSync)(root)) {
13122
+ for (const entry of (0, import_node_fs33.readdirSync)(root, { withFileTypes: true })) {
12782
13123
  if (!entry.isDirectory()) continue;
12783
- const path2 = (0, import_node_path32.join)(root, entry.name).replace(/\/$/, "");
13124
+ const path2 = (0, import_node_path33.join)(root, entry.name).replace(/\/$/, "");
12784
13125
  if (known.has(path2) || seen.has(path2)) continue;
12785
- if (!(0, import_node_fs32.existsSync)((0, import_node_path32.join)(path2, ".git"))) continue;
13126
+ if (!(0, import_node_fs33.existsSync)((0, import_node_path33.join)(path2, ".git"))) continue;
12786
13127
  seen.add(path2);
12787
13128
  let mtimeMs = 0;
12788
13129
  try {
12789
- mtimeMs = (0, import_node_fs32.statSync)(path2).mtimeMs;
13130
+ mtimeMs = (0, import_node_fs33.statSync)(path2).mtimeMs;
12790
13131
  } catch {
12791
13132
  mtimeMs = Date.now();
12792
13133
  }
@@ -12847,12 +13188,12 @@ function worktreeCleanupSettings() {
12847
13188
  autoCleanupOrphans: a.autoCleanupOrphans
12848
13189
  };
12849
13190
  }
12850
- var import_node_fs32, import_node_path32;
13191
+ var import_node_fs33, import_node_path33;
12851
13192
  var init_orphan_cleanup = __esm({
12852
13193
  "src/git/orphan-cleanup.ts"() {
12853
13194
  "use strict";
12854
- import_node_fs32 = require("fs");
12855
- import_node_path32 = require("path");
13195
+ import_node_fs33 = require("fs");
13196
+ import_node_path33 = require("path");
12856
13197
  init_worktree();
12857
13198
  init_thread_store();
12858
13199
  init_paths();
@@ -12959,38 +13300,38 @@ __export(workspaces_exports, {
12959
13300
  syncWorkspacesFromThreads: () => syncWorkspacesFromThreads
12960
13301
  });
12961
13302
  function workspacesFile() {
12962
- return (0, import_node_path33.join)(appDataDir(), "workspaces.json");
13303
+ return (0, import_node_path34.join)(appDataDir(), "workspaces.json");
12963
13304
  }
12964
13305
  function removedWorkspacesFile() {
12965
- return (0, import_node_path33.join)(appDataDir(), "removed-workspaces.json");
13306
+ return (0, import_node_path34.join)(appDataDir(), "removed-workspaces.json");
12966
13307
  }
12967
13308
  function readAll2() {
12968
13309
  const path2 = workspacesFile();
12969
- if (!(0, import_node_fs33.existsSync)(path2)) return [];
13310
+ if (!(0, import_node_fs34.existsSync)(path2)) return [];
12970
13311
  try {
12971
- const raw = JSON.parse((0, import_node_fs33.readFileSync)(path2, "utf8"));
13312
+ const raw = JSON.parse((0, import_node_fs34.readFileSync)(path2, "utf8"));
12972
13313
  return Array.isArray(raw) ? raw : [];
12973
13314
  } catch {
12974
13315
  return [];
12975
13316
  }
12976
13317
  }
12977
13318
  function writeAll2(list) {
12978
- (0, import_node_fs33.mkdirSync)(appDataDir(), { recursive: true });
12979
- (0, import_node_fs33.writeFileSync)(workspacesFile(), JSON.stringify(list, null, 2), "utf8");
13319
+ (0, import_node_fs34.mkdirSync)(appDataDir(), { recursive: true });
13320
+ (0, import_node_fs34.writeFileSync)(workspacesFile(), JSON.stringify(list, null, 2), "utf8");
12980
13321
  }
12981
13322
  function readRemoved() {
12982
13323
  const path2 = removedWorkspacesFile();
12983
- if (!(0, import_node_fs33.existsSync)(path2)) return /* @__PURE__ */ new Set();
13324
+ if (!(0, import_node_fs34.existsSync)(path2)) return /* @__PURE__ */ new Set();
12984
13325
  try {
12985
- const raw = JSON.parse((0, import_node_fs33.readFileSync)(path2, "utf8"));
13326
+ const raw = JSON.parse((0, import_node_fs34.readFileSync)(path2, "utf8"));
12986
13327
  return new Set(Array.isArray(raw) ? raw.filter((p) => typeof p === "string") : []);
12987
13328
  } catch {
12988
13329
  return /* @__PURE__ */ new Set();
12989
13330
  }
12990
13331
  }
12991
13332
  function writeRemoved(paths) {
12992
- (0, import_node_fs33.mkdirSync)(appDataDir(), { recursive: true });
12993
- (0, import_node_fs33.writeFileSync)(removedWorkspacesFile(), JSON.stringify([...paths].sort(), null, 2), "utf8");
13333
+ (0, import_node_fs34.mkdirSync)(appDataDir(), { recursive: true });
13334
+ (0, import_node_fs34.writeFileSync)(removedWorkspacesFile(), JSON.stringify([...paths].sort(), null, 2), "utf8");
12994
13335
  }
12995
13336
  function rememberRemoved(repoPath) {
12996
13337
  const next = readRemoved();
@@ -13013,7 +13354,7 @@ function listWorkspaces() {
13013
13354
  async function addWorkspace(repoPath) {
13014
13355
  const root = await resolveRepoRoot(repoPath);
13015
13356
  if (!root || root === "/") throw new Error(`Invalid repo path: ${repoPath}`);
13016
- if (!(0, import_node_fs33.existsSync)(root)) throw new Error(`Repo not found: ${root}`);
13357
+ if (!(0, import_node_fs34.existsSync)(root)) throw new Error(`Repo not found: ${root}`);
13017
13358
  forgetRemoved(root);
13018
13359
  await ensureGhPreferOrigin(root);
13019
13360
  const current = readAll2();
@@ -13021,7 +13362,7 @@ async function addWorkspace(repoPath) {
13021
13362
  if (existing) return existing;
13022
13363
  const next = {
13023
13364
  path: root,
13024
- name: (0, import_node_path33.basename)(root),
13365
+ name: (0, import_node_path34.basename)(root),
13025
13366
  addedAt: (/* @__PURE__ */ new Date()).toISOString()
13026
13367
  };
13027
13368
  writeAll2([...current, next]);
@@ -13043,10 +13384,10 @@ function syncWorkspacesFromThreads(repoPaths) {
13043
13384
  if (!path2 || path2 === "/" || isGlobalRepoPath(path2) || byPath.has(path2) || removed.has(path2)) {
13044
13385
  continue;
13045
13386
  }
13046
- if (!(0, import_node_fs33.existsSync)(path2)) continue;
13387
+ if (!(0, import_node_fs34.existsSync)(path2)) continue;
13047
13388
  const ws = {
13048
13389
  path: path2,
13049
- name: (0, import_node_path33.basename)(path2),
13390
+ name: (0, import_node_path34.basename)(path2),
13050
13391
  addedAt: (/* @__PURE__ */ new Date()).toISOString()
13051
13392
  };
13052
13393
  byPath.set(path2, ws);
@@ -13056,12 +13397,12 @@ function syncWorkspacesFromThreads(repoPaths) {
13056
13397
  if (dirty) writeAll2(next);
13057
13398
  return next.sort((a, b) => a.name.localeCompare(b.name));
13058
13399
  }
13059
- var import_node_fs33, import_node_path33;
13400
+ var import_node_fs34, import_node_path34;
13060
13401
  var init_workspaces2 = __esm({
13061
13402
  "src/store/workspaces.ts"() {
13062
13403
  "use strict";
13063
- import_node_fs33 = require("fs");
13064
- import_node_path33 = require("path");
13404
+ import_node_fs34 = require("fs");
13405
+ import_node_path34 = require("path");
13065
13406
  init_paths();
13066
13407
  init_global_workspace();
13067
13408
  init_worktree();
@@ -13074,12 +13415,12 @@ async function cloneRepoIntoSideboard(opts) {
13074
13415
  if (!url) throw new Error("Clone URL is required");
13075
13416
  let name = opts.name?.trim();
13076
13417
  if (!name) {
13077
- const leaf = (0, import_node_path34.basename)(url.replace(/\/$/, "").replace(/\.git$/, ""));
13418
+ const leaf = (0, import_node_path35.basename)(url.replace(/\/$/, "").replace(/\.git$/, ""));
13078
13419
  name = leaf || "repo";
13079
13420
  }
13080
13421
  name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
13081
- const dest = (0, import_node_path34.join)(sideboardReposDir(), name);
13082
- if ((0, import_node_fs34.existsSync)(dest)) {
13422
+ const dest = (0, import_node_path35.join)(sideboardReposDir(), name);
13423
+ if ((0, import_node_fs35.existsSync)(dest)) {
13083
13424
  const repoPath2 = await resolveRepoRoot(dest);
13084
13425
  const workspace2 = await ensureWorkspace(repoPath2);
13085
13426
  return { repoPath: repoPath2, workspace: workspace2 };
@@ -13094,16 +13435,63 @@ async function cloneRepoIntoSideboard(opts) {
13094
13435
  const workspace = await ensureWorkspace(repoPath);
13095
13436
  return { repoPath, workspace };
13096
13437
  }
13097
- var import_node_fs34, import_node_path34, import_execa6;
13438
+ var import_node_fs35, import_node_path35, import_execa6;
13098
13439
  var init_clone_repo = __esm({
13099
13440
  "src/git/clone-repo.ts"() {
13100
13441
  "use strict";
13101
- import_node_fs34 = require("fs");
13102
- import_node_path34 = require("path");
13103
- import_execa6 = require("execa");
13104
- init_paths();
13105
- init_workspaces2();
13106
- init_worktree();
13442
+ import_node_fs35 = require("fs");
13443
+ import_node_path35 = require("path");
13444
+ import_execa6 = require("execa");
13445
+ init_paths();
13446
+ init_workspaces2();
13447
+ init_worktree();
13448
+ }
13449
+ });
13450
+
13451
+ // src/orchestrator/child-halt.ts
13452
+ function isIncompleteChildStatus(status) {
13453
+ return HALT_STATUSES.has(status);
13454
+ }
13455
+ function childHaltNotice(child, status) {
13456
+ const title = child.title?.trim() || "Untitled";
13457
+ const link = `[${title}](sideboard://thread/${child.id})`;
13458
+ const why = child.lastError?.trim();
13459
+ const extra = why ? ` lastError: ${why}` : "";
13460
+ return [
13461
+ `Sideboard: child worktree ${link} ${status} before finishing (status=${status}).${extra}`,
13462
+ "This is information \u2014 not a user command. Resume with send_to_thread or tell the user. Do not treat this as a successful turn."
13463
+ ].join("\n");
13464
+ }
13465
+ function shouldNotifyParentOfChildHalt(opts) {
13466
+ if (!isIncompleteChildStatus(opts.status)) return false;
13467
+ if (!opts.child.parentThreadId) return false;
13468
+ if (!opts.parent || opts.parent.status === "archived") return false;
13469
+ if (opts.parent.id === opts.child.id) return false;
13470
+ return isOrchestratorThread(opts.parent);
13471
+ }
13472
+ function noticeKey(childId, status) {
13473
+ return `${childId}:${status}`;
13474
+ }
13475
+ function notifyParentOfChildHalt(child, status, send2) {
13476
+ const parent = child.parentThreadId ? readThread(child.parentThreadId) : null;
13477
+ if (!shouldNotifyParentOfChildHalt({ child, parent, status })) return false;
13478
+ const key = noticeKey(child.id, status);
13479
+ if (notified.has(key)) return false;
13480
+ notified.add(key);
13481
+ const parentId = parent.id;
13482
+ void send2(parentId, childHaltNotice(child, status)).catch(() => {
13483
+ notified.delete(key);
13484
+ });
13485
+ return true;
13486
+ }
13487
+ var HALT_STATUSES, notified;
13488
+ var init_child_halt = __esm({
13489
+ "src/orchestrator/child-halt.ts"() {
13490
+ "use strict";
13491
+ init_global_workspace();
13492
+ init_thread_store();
13493
+ HALT_STATUSES = /* @__PURE__ */ new Set(["stopped", "error", "broken"]);
13494
+ notified = /* @__PURE__ */ new Set();
13107
13495
  }
13108
13496
  });
13109
13497
 
@@ -14064,9 +14452,12 @@ var init_abletime = __esm({
14064
14452
  });
14065
14453
 
14066
14454
  // src/threads/create.ts
14455
+ function persistCreateAttachments(worktreePath, attachments) {
14456
+ return persistPendingFileAttachments(worktreePath, attachments ?? []);
14457
+ }
14067
14458
  async function createThread(input, _onSetupLine) {
14068
14459
  const repoPath = await resolveRepoRoot(input.repoPath);
14069
- if (!(0, import_node_fs35.existsSync)(repoPath)) {
14460
+ if (!(0, import_node_fs36.existsSync)(repoPath)) {
14070
14461
  throw new Error(`Repo not found: ${repoPath}`);
14071
14462
  }
14072
14463
  if (input.reuseExisting !== false) {
@@ -14083,7 +14474,16 @@ async function createThread(input, _onSetupLine) {
14083
14474
  repoPath: canonicalizeRepoPath(t.repoPath)
14084
14475
  }))
14085
14476
  );
14086
- if (existing) return readThread(existing.id) ?? existing;
14477
+ if (existing) {
14478
+ const thread2 = readThread(existing.id) ?? existing;
14479
+ if (!input.attachments?.length) return thread2;
14480
+ return updateThread(thread2.id, {
14481
+ attachments: persistCreateAttachments(thread2.worktreePath, [
14482
+ ...thread2.attachments,
14483
+ ...input.attachments
14484
+ ])
14485
+ });
14486
+ }
14087
14487
  }
14088
14488
  const resolved = resolveNewThreadOptions({
14089
14489
  agent: input.agent,
@@ -14131,7 +14531,7 @@ async function createThread(input, _onSetupLine) {
14131
14531
  effort: resolved.effort,
14132
14532
  fast: resolved.fast,
14133
14533
  planMode: Boolean(input.planMode),
14134
- attachments: input.attachments ?? [],
14534
+ attachments: persistCreateAttachments(repoPath, input.attachments),
14135
14535
  sourceIsFork: false,
14136
14536
  parentThreadId: input.parentThreadId ?? null,
14137
14537
  status: "idle",
@@ -14210,7 +14610,7 @@ async function createThread(input, _onSetupLine) {
14210
14610
  effort: resolved.effort,
14211
14611
  fast: resolved.fast,
14212
14612
  planMode: Boolean(input.planMode),
14213
- attachments,
14613
+ attachments: persistCreateAttachments(worktreePath, attachments),
14214
14614
  sourceIsFork,
14215
14615
  parentThreadId: input.parentThreadId ?? null,
14216
14616
  status: "idle",
@@ -14230,16 +14630,17 @@ async function listLinearIssues(agent, repoPath) {
14230
14630
  }
14231
14631
  return adapter.listLinearIssues(repoPath);
14232
14632
  }
14233
- var import_node_fs35;
14633
+ var import_node_fs36;
14234
14634
  var init_create = __esm({
14235
14635
  "src/threads/create.ts"() {
14236
14636
  "use strict";
14237
- import_node_fs35 = require("fs");
14637
+ import_node_fs36 = require("fs");
14238
14638
  init_detect();
14239
14639
  init_worktree();
14240
14640
  init_home_board();
14241
14641
  init_conductor();
14242
14642
  init_app_settings();
14643
+ init_stage_files();
14243
14644
  init_thread_store();
14244
14645
  init_workspaces2();
14245
14646
  }
@@ -14336,20 +14737,20 @@ function writeTurnLive(threadId, progress) {
14336
14737
  const path2 = threadLivePath(threadId);
14337
14738
  const tmp = `${path2}.${process.pid}.tmp`;
14338
14739
  try {
14339
- (0, import_node_fs36.writeFileSync)(tmp, JSON.stringify(progress), "utf8");
14340
- (0, import_node_fs36.renameSync)(tmp, path2);
14740
+ (0, import_node_fs37.writeFileSync)(tmp, JSON.stringify(progress), "utf8");
14741
+ (0, import_node_fs37.renameSync)(tmp, path2);
14341
14742
  } catch {
14342
14743
  try {
14343
- (0, import_node_fs36.unlinkSync)(tmp);
14744
+ (0, import_node_fs37.unlinkSync)(tmp);
14344
14745
  } catch {
14345
14746
  }
14346
14747
  }
14347
14748
  }
14348
14749
  function readTurnLive(threadId) {
14349
14750
  const path2 = threadLivePath(threadId);
14350
- if (!(0, import_node_fs36.existsSync)(path2)) return null;
14751
+ if (!(0, import_node_fs37.existsSync)(path2)) return null;
14351
14752
  try {
14352
- const raw = JSON.parse((0, import_node_fs36.readFileSync)(path2, "utf8"));
14753
+ const raw = JSON.parse((0, import_node_fs37.readFileSync)(path2, "utf8"));
14353
14754
  if (!raw || typeof raw.summary !== "string") return null;
14354
14755
  return raw;
14355
14756
  } catch {
@@ -14361,17 +14762,17 @@ function clearTurnLive(threadId) {
14361
14762
  if (buf?.timer) clearTimeout(buf.timer);
14362
14763
  buffers.delete(threadId);
14363
14764
  const path2 = threadLivePath(threadId);
14364
- if (!(0, import_node_fs36.existsSync)(path2)) return;
14765
+ if (!(0, import_node_fs37.existsSync)(path2)) return;
14365
14766
  try {
14366
- (0, import_node_fs36.unlinkSync)(path2);
14767
+ (0, import_node_fs37.unlinkSync)(path2);
14367
14768
  } catch {
14368
14769
  }
14369
14770
  }
14370
- var import_node_fs36, buffers, FLUSH_MS, MAX_PARTS;
14771
+ var import_node_fs37, buffers, FLUSH_MS, MAX_PARTS;
14371
14772
  var init_turn_live = __esm({
14372
14773
  "src/store/turn-live.ts"() {
14373
14774
  "use strict";
14374
- import_node_fs36 = require("fs");
14775
+ import_node_fs37 = require("fs");
14375
14776
  init_message_parts();
14376
14777
  init_paths();
14377
14778
  buffers = /* @__PURE__ */ new Map();
@@ -14530,7 +14931,7 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
14530
14931
  `- Do not wait on the limited ${from.agent} account; keep going on ${fallbackAgent}.`
14531
14932
  ].join("\n");
14532
14933
  return {
14533
- id: (0, import_node_crypto7.randomUUID)(),
14934
+ id: (0, import_node_crypto8.randomUUID)(),
14534
14935
  name: "Orchestration quota handoff.md",
14535
14936
  kind: "transcript",
14536
14937
  content: body
@@ -14551,11 +14952,11 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
14551
14952
  sourceType: "orchestration"
14552
14953
  });
14553
14954
  }
14554
- var import_node_crypto7, QUOTA_CONTINUE_PROMPT, QUOTA_RESUME_PROMPT;
14955
+ var import_node_crypto8, QUOTA_CONTINUE_PROMPT, QUOTA_RESUME_PROMPT;
14555
14956
  var init_quota_failover = __esm({
14556
14957
  "src/orchestrator/quota-failover.ts"() {
14557
14958
  "use strict";
14558
- import_node_crypto7 = require("crypto");
14959
+ import_node_crypto8 = require("crypto");
14559
14960
  init_session_quota();
14560
14961
  init_app_settings();
14561
14962
  init_global_workspace();
@@ -14572,7 +14973,7 @@ var init_quota_failover = __esm({
14572
14973
  // src/threads/adopt.ts
14573
14974
  function thisModuleFile() {
14574
14975
  const cjsFile = typeof __filename !== "undefined" ? __filename : "";
14575
- return cjsFile || process.argv[1] || (0, import_node_path35.join)(process.cwd(), "package.json");
14976
+ return cjsFile || process.argv[1] || (0, import_node_path36.join)(process.cwd(), "package.json");
14576
14977
  }
14577
14978
  function openReadonlySqlite(file) {
14578
14979
  const req = (0, import_node_module4.createRequire)(thisModuleFile());
@@ -14590,21 +14991,21 @@ function mapAgentType(raw) {
14590
14991
  return null;
14591
14992
  }
14592
14993
  function resolveConductorCursorAgentId(workspacePath) {
14593
- if (!workspacePath || !(0, import_node_fs37.existsSync)(CURSOR_SDK_STORE)) return null;
14994
+ if (!workspacePath || !(0, import_node_fs38.existsSync)(CURSOR_SDK_STORE)) return null;
14594
14995
  const normalized = workspacePath.replace(/\/$/, "");
14595
14996
  let best = null;
14596
14997
  let hashes;
14597
14998
  try {
14598
- hashes = (0, import_node_fs37.readdirSync)(CURSOR_SDK_STORE);
14999
+ hashes = (0, import_node_fs38.readdirSync)(CURSOR_SDK_STORE);
14599
15000
  } catch {
14600
15001
  return null;
14601
15002
  }
14602
15003
  for (const hash of hashes) {
14603
- const agentsFile = (0, import_node_path35.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
14604
- if (!(0, import_node_fs37.existsSync)(agentsFile)) continue;
15004
+ const agentsFile = (0, import_node_path36.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
15005
+ if (!(0, import_node_fs38.existsSync)(agentsFile)) continue;
14605
15006
  let text5;
14606
15007
  try {
14607
- text5 = (0, import_node_fs37.readFileSync)(agentsFile, "utf8");
15008
+ text5 = (0, import_node_fs38.readFileSync)(agentsFile, "utf8");
14608
15009
  } catch {
14609
15010
  continue;
14610
15011
  }
@@ -14628,7 +15029,7 @@ function resolveConductorCursorAgentId(workspacePath) {
14628
15029
  return best?.agentId ?? null;
14629
15030
  }
14630
15031
  async function adoptThread(input) {
14631
- if (!(0, import_node_fs37.existsSync)(input.worktreePath)) {
15032
+ if (!(0, import_node_fs38.existsSync)(input.worktreePath)) {
14632
15033
  throw new Error(`Worktree not found: ${input.worktreePath}`);
14633
15034
  }
14634
15035
  const repoPath = await resolveRepoRoot(input.worktreePath);
@@ -14655,18 +15056,18 @@ function conductorDbPath() {
14655
15056
  return CONDUCTOR_DB;
14656
15057
  }
14657
15058
  function listConductorWorkspaces() {
14658
- if (!(0, import_node_fs37.existsSync)(CONDUCTOR_DB)) {
15059
+ if (!(0, import_node_fs38.existsSync)(CONDUCTOR_DB)) {
14659
15060
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
14660
15061
  }
14661
- const tmp = (0, import_node_fs37.mkdtempSync)((0, import_node_path35.join)((0, import_node_os10.tmpdir)(), "sideboard-conductor-"));
14662
- const snapshot = (0, import_node_path35.join)(tmp, "conductor.db");
15062
+ const tmp = (0, import_node_fs38.mkdtempSync)((0, import_node_path36.join)((0, import_node_os10.tmpdir)(), "sideboard-conductor-"));
15063
+ const snapshot = (0, import_node_path36.join)(tmp, "conductor.db");
14663
15064
  try {
14664
- (0, import_node_fs37.copyFileSync)(CONDUCTOR_DB, snapshot);
15065
+ (0, import_node_fs38.copyFileSync)(CONDUCTOR_DB, snapshot);
14665
15066
  for (const suffix of ["-wal", "-shm"]) {
14666
15067
  const src = `${CONDUCTOR_DB}${suffix}`;
14667
- if ((0, import_node_fs37.existsSync)(src)) {
15068
+ if ((0, import_node_fs38.existsSync)(src)) {
14668
15069
  try {
14669
- (0, import_node_fs37.copyFileSync)(src, `${snapshot}${suffix}`);
15070
+ (0, import_node_fs38.copyFileSync)(src, `${snapshot}${suffix}`);
14670
15071
  } catch {
14671
15072
  }
14672
15073
  }
@@ -14742,22 +15143,22 @@ function listConductorWorkspaces() {
14742
15143
  db.close();
14743
15144
  }
14744
15145
  } finally {
14745
- (0, import_node_fs37.rmSync)(tmp, { recursive: true, force: true });
15146
+ (0, import_node_fs38.rmSync)(tmp, { recursive: true, force: true });
14746
15147
  }
14747
15148
  }
14748
15149
  function importConductorWorkspace(workspaceId) {
14749
- if (!(0, import_node_fs37.existsSync)(CONDUCTOR_DB)) {
15150
+ if (!(0, import_node_fs38.existsSync)(CONDUCTOR_DB)) {
14750
15151
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
14751
15152
  }
14752
- const tmp = (0, import_node_fs37.mkdtempSync)((0, import_node_path35.join)((0, import_node_os10.tmpdir)(), "sideboard-conductor-"));
14753
- const snapshot = (0, import_node_path35.join)(tmp, "conductor.db");
15153
+ const tmp = (0, import_node_fs38.mkdtempSync)((0, import_node_path36.join)((0, import_node_os10.tmpdir)(), "sideboard-conductor-"));
15154
+ const snapshot = (0, import_node_path36.join)(tmp, "conductor.db");
14754
15155
  try {
14755
- (0, import_node_fs37.copyFileSync)(CONDUCTOR_DB, snapshot);
15156
+ (0, import_node_fs38.copyFileSync)(CONDUCTOR_DB, snapshot);
14756
15157
  for (const suffix of ["-wal", "-shm"]) {
14757
15158
  const src = `${CONDUCTOR_DB}${suffix}`;
14758
- if ((0, import_node_fs37.existsSync)(src)) {
15159
+ if ((0, import_node_fs38.existsSync)(src)) {
14759
15160
  try {
14760
- (0, import_node_fs37.copyFileSync)(src, `${snapshot}${suffix}`);
15161
+ (0, import_node_fs38.copyFileSync)(src, `${snapshot}${suffix}`);
14761
15162
  } catch {
14762
15163
  }
14763
15164
  }
@@ -14775,7 +15176,7 @@ function importConductorWorkspace(workspaceId) {
14775
15176
  ).get(workspaceId);
14776
15177
  if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
14777
15178
  const worktreePath = String(row.workspacePath);
14778
- if (!(0, import_node_fs37.existsSync)(worktreePath)) {
15179
+ if (!(0, import_node_fs38.existsSync)(worktreePath)) {
14779
15180
  throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
14780
15181
  }
14781
15182
  let sessionId = null;
@@ -14838,31 +15239,31 @@ function importConductorWorkspace(workspaceId) {
14838
15239
  db.close();
14839
15240
  }
14840
15241
  } finally {
14841
- (0, import_node_fs37.rmSync)(tmp, { recursive: true, force: true });
15242
+ (0, import_node_fs38.rmSync)(tmp, { recursive: true, force: true });
14842
15243
  }
14843
15244
  }
14844
15245
  async function importConductorWorkspaceAsync(workspaceId) {
14845
15246
  return importConductorWorkspace(workspaceId);
14846
15247
  }
14847
- var import_node_child_process4, import_node_fs37, import_node_os10, import_node_path35, import_node_module4, CONDUCTOR_APP_SUPPORT, CONDUCTOR_DB, CURSOR_SDK_STORE;
15248
+ var import_node_child_process4, import_node_fs38, import_node_os10, import_node_path36, import_node_module4, CONDUCTOR_APP_SUPPORT, CONDUCTOR_DB, CURSOR_SDK_STORE;
14848
15249
  var init_adopt = __esm({
14849
15250
  "src/threads/adopt.ts"() {
14850
15251
  "use strict";
14851
15252
  import_node_child_process4 = require("child_process");
14852
- import_node_fs37 = require("fs");
15253
+ import_node_fs38 = require("fs");
14853
15254
  import_node_os10 = require("os");
14854
- import_node_path35 = require("path");
15255
+ import_node_path36 = require("path");
14855
15256
  import_node_module4 = require("module");
14856
15257
  init_worktree();
14857
15258
  init_thread_store();
14858
- CONDUCTOR_APP_SUPPORT = (0, import_node_path35.join)(
15259
+ CONDUCTOR_APP_SUPPORT = (0, import_node_path36.join)(
14859
15260
  process.env.HOME ?? "",
14860
15261
  "Library",
14861
15262
  "Application Support",
14862
15263
  "com.conductor.app"
14863
15264
  );
14864
- CONDUCTOR_DB = (0, import_node_path35.join)(CONDUCTOR_APP_SUPPORT, "conductor.db");
14865
- CURSOR_SDK_STORE = (0, import_node_path35.join)(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
15265
+ CONDUCTOR_DB = (0, import_node_path36.join)(CONDUCTOR_APP_SUPPORT, "conductor.db");
15266
+ CURSOR_SDK_STORE = (0, import_node_path36.join)(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
14866
15267
  }
14867
15268
  });
14868
15269
 
@@ -14929,7 +15330,7 @@ async function openStackLayer(input, _onSetupLine) {
14929
15330
  let createdWorktree = false;
14930
15331
  const trees = await listWorktrees(repoPath);
14931
15332
  const checkedOut = trees.find((w) => w.branch === branchName);
14932
- if (checkedOut?.path && (0, import_node_fs38.existsSync)(checkedOut.path)) {
15333
+ if (checkedOut?.path && (0, import_node_fs39.existsSync)(checkedOut.path)) {
14933
15334
  if (input.reuseExistingWorktree !== false) {
14934
15335
  worktreePath = checkedOut.path;
14935
15336
  } else {
@@ -15071,7 +15472,7 @@ async function initStackFromThread(input, onSetupLine) {
15071
15472
  async function createPrStack(input, onSetupLine) {
15072
15473
  await requireAgent(input.agent);
15073
15474
  const repoPath = await resolveRepoRoot(input.repoPath);
15074
- if (!(0, import_node_fs38.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
15475
+ if (!(0, import_node_fs39.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
15075
15476
  if (!input.branches.length) throw new Error("At least one branch name required");
15076
15477
  const status = await detectGhStack(repoPath);
15077
15478
  if (!status.available) throw new Error(status.reason);
@@ -15138,7 +15539,7 @@ async function createPrStack(input, onSetupLine) {
15138
15539
  }
15139
15540
  }
15140
15541
  const claimed = new Set(threads.map((t) => t.worktreePath));
15141
- if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs38.existsSync)(bootstrap.worktreePath)) {
15542
+ if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs39.existsSync)(bootstrap.worktreePath)) {
15142
15543
  try {
15143
15544
  await removeWorktree(repoPath, bootstrap.worktreePath, {
15144
15545
  deleteBranch: bootstrap.branchName
@@ -15158,11 +15559,11 @@ function stackAgentDefaultsFrom(input) {
15158
15559
  planMode: input.planMode
15159
15560
  };
15160
15561
  }
15161
- var import_node_fs38;
15562
+ var import_node_fs39;
15162
15563
  var init_stack_layers = __esm({
15163
15564
  "src/threads/stack-layers.ts"() {
15164
15565
  "use strict";
15165
- import_node_fs38 = require("fs");
15566
+ import_node_fs39 = require("fs");
15166
15567
  init_detect();
15167
15568
  init_run();
15168
15569
  init_stack();
@@ -15175,7 +15576,7 @@ var init_stack_layers = __esm({
15175
15576
 
15176
15577
  // src/diff/diff.ts
15177
15578
  async function inspectGitWorktree(worktreePath) {
15178
- if (!worktreePath || !(0, import_node_fs39.existsSync)(worktreePath)) return "missing_worktree";
15579
+ if (!worktreePath || !(0, import_node_fs40.existsSync)(worktreePath)) return "missing_worktree";
15179
15580
  const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
15180
15581
  reject: false
15181
15582
  });
@@ -15183,7 +15584,7 @@ async function inspectGitWorktree(worktreePath) {
15183
15584
  return "ok";
15184
15585
  }
15185
15586
  async function initializeGitRepository(worktreePath) {
15186
- if (!worktreePath || !(0, import_node_fs39.existsSync)(worktreePath)) {
15587
+ if (!worktreePath || !(0, import_node_fs40.existsSync)(worktreePath)) {
15187
15588
  throw new Error("Worktree not found");
15188
15589
  }
15189
15590
  const status = await inspectGitWorktree(worktreePath);
@@ -15317,11 +15718,11 @@ new file mode 100644
15317
15718
  };
15318
15719
  }
15319
15720
  async function untrackedPatch(worktreePath, path2, maxHunk) {
15320
- const abs = (0, import_node_path36.join)(worktreePath, path2);
15721
+ const abs = (0, import_node_path37.join)(worktreePath, path2);
15321
15722
  try {
15322
- const st = (0, import_node_fs39.statSync)(abs);
15723
+ const st = (0, import_node_fs40.statSync)(abs);
15323
15724
  if (st.isFile() && st.size > maxHunk) {
15324
- const buf = (0, import_node_fs39.readFileSync)(abs).subarray(0, maxHunk);
15725
+ const buf = (0, import_node_fs40.readFileSync)(abs).subarray(0, maxHunk);
15325
15726
  return syntheticAddPatch(path2, buf.toString("utf8"), maxHunk);
15326
15727
  }
15327
15728
  } catch {
@@ -15805,13 +16206,13 @@ async function listWorktreeFiles(worktreePath, opts) {
15805
16206
  function isImageRelativePath(relativePath) {
15806
16207
  const base = relativePath.split("/").pop()?.toLowerCase() || "";
15807
16208
  const ext = base.includes(".") ? base.split(".").pop() || "" : "";
15808
- return IMAGE_EXTENSIONS.has(ext);
16209
+ return IMAGE_EXTENSIONS2.has(ext);
15809
16210
  }
15810
16211
  function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
15811
16212
  assertSafeRelativePath(relativePath);
15812
16213
  const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
15813
- const abs = (0, import_node_path36.join)(worktreePath, relativePath);
15814
- const st = (0, import_node_fs39.statSync)(abs);
16214
+ const abs = (0, import_node_path37.join)(worktreePath, relativePath);
16215
+ const st = (0, import_node_fs40.statSync)(abs);
15815
16216
  if (!st.isFile()) {
15816
16217
  throw new Error(`Not a file: ${relativePath}`);
15817
16218
  }
@@ -15820,7 +16221,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
15820
16221
  `File too large to upload (${st.size} bytes; max ${maxBytes})`
15821
16222
  );
15822
16223
  }
15823
- const buf = (0, import_node_fs39.readFileSync)(abs);
16224
+ const buf = (0, import_node_fs40.readFileSync)(abs);
15824
16225
  return {
15825
16226
  path: relativePath,
15826
16227
  contentBase64: buf.toString("base64"),
@@ -15830,12 +16231,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
15830
16231
  function readWorktreeFile(worktreePath, relativePath, opts) {
15831
16232
  assertSafeRelativePath(relativePath);
15832
16233
  const maxBytes = opts?.maxBytes ?? 2e5;
15833
- const abs = (0, import_node_path36.join)(worktreePath, relativePath);
15834
- const st = (0, import_node_fs39.statSync)(abs);
16234
+ const abs = (0, import_node_path37.join)(worktreePath, relativePath);
16235
+ const st = (0, import_node_fs40.statSync)(abs);
15835
16236
  if (!st.isFile()) {
15836
16237
  throw new Error(`Not a file: ${relativePath}`);
15837
16238
  }
15838
- const buf = (0, import_node_fs39.readFileSync)(abs);
16239
+ const buf = (0, import_node_fs40.readFileSync)(abs);
15839
16240
  if (isImageRelativePath(relativePath)) {
15840
16241
  const maxImageBytes = Math.max(maxBytes, 15e6);
15841
16242
  const truncated2 = buf.length > maxImageBytes;
@@ -15878,9 +16279,9 @@ function assertSafeRelativePath(relativePath) {
15878
16279
  }
15879
16280
  function writeWorktreeFile(worktreePath, relativePath, content) {
15880
16281
  assertSafeRelativePath(relativePath);
15881
- const abs = (0, import_node_path36.join)(worktreePath, relativePath);
15882
- (0, import_node_fs39.mkdirSync)((0, import_node_path36.dirname)(abs), { recursive: true });
15883
- (0, import_node_fs39.writeFileSync)(abs, content, "utf8");
16282
+ const abs = (0, import_node_path37.join)(worktreePath, relativePath);
16283
+ (0, import_node_fs40.mkdirSync)((0, import_node_path37.dirname)(abs), { recursive: true });
16284
+ (0, import_node_fs40.writeFileSync)(abs, content, "utf8");
15884
16285
  return { path: relativePath };
15885
16286
  }
15886
16287
  async function getDiffSummary(worktreePath, repoPath, opts) {
@@ -15897,18 +16298,18 @@ async function getDiffSummary(worktreePath, repoPath, opts) {
15897
16298
  truncated: full.files.length > maxFiles
15898
16299
  };
15899
16300
  }
15900
- var import_node_fs39, import_node_path36, mergeBaseCache, MERGE_BASE_TTL_MS, SHA_RE, IMAGE_EXTENSIONS, DEFAULT_UPLOAD_MAX_BYTES;
16301
+ var import_node_fs40, import_node_path37, mergeBaseCache, MERGE_BASE_TTL_MS, SHA_RE, IMAGE_EXTENSIONS2, DEFAULT_UPLOAD_MAX_BYTES;
15901
16302
  var init_diff = __esm({
15902
16303
  "src/diff/diff.ts"() {
15903
16304
  "use strict";
15904
- import_node_fs39 = require("fs");
15905
- import_node_path36 = require("path");
16305
+ import_node_fs40 = require("fs");
16306
+ import_node_path37 = require("path");
15906
16307
  init_run();
15907
16308
  init_worktree();
15908
16309
  mergeBaseCache = /* @__PURE__ */ new Map();
15909
16310
  MERGE_BASE_TTL_MS = 45e3;
15910
16311
  SHA_RE = /^[0-9a-f]{7,40}$/i;
15911
- IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
16312
+ IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
15912
16313
  "png",
15913
16314
  "jpg",
15914
16315
  "jpeg",
@@ -16070,7 +16471,7 @@ function parseFrontmatter(content) {
16070
16471
  }
16071
16472
  function readSkill(skillMd, source) {
16072
16473
  try {
16073
- const content = (0, import_node_fs40.readFileSync)(skillMd, "utf8");
16474
+ const content = (0, import_node_fs41.readFileSync)(skillMd, "utf8");
16074
16475
  const { name: fmName, description } = parseFrontmatter(content);
16075
16476
  const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
16076
16477
  const name = fmName || dirName;
@@ -16089,19 +16490,19 @@ function readSkill(skillMd, source) {
16089
16490
  }
16090
16491
  }
16091
16492
  function scanSkillsDir(dir, source, out) {
16092
- if (!(0, import_node_fs40.existsSync)(dir)) return;
16493
+ if (!(0, import_node_fs41.existsSync)(dir)) return;
16093
16494
  let entries;
16094
16495
  try {
16095
- entries = (0, import_node_fs40.readdirSync)(dir);
16496
+ entries = (0, import_node_fs41.readdirSync)(dir);
16096
16497
  } catch {
16097
16498
  return;
16098
16499
  }
16099
16500
  for (const entry of entries) {
16100
16501
  if (entry.startsWith(".")) continue;
16101
- const skillMd = (0, import_node_path37.join)(dir, entry, "SKILL.md");
16102
- if (!(0, import_node_fs40.existsSync)(skillMd)) continue;
16502
+ const skillMd = (0, import_node_path38.join)(dir, entry, "SKILL.md");
16503
+ if (!(0, import_node_fs41.existsSync)(skillMd)) continue;
16103
16504
  try {
16104
- if (!(0, import_node_fs40.statSync)(skillMd).isFile()) continue;
16505
+ if (!(0, import_node_fs41.statSync)(skillMd).isFile()) continue;
16105
16506
  } catch {
16106
16507
  continue;
16107
16508
  }
@@ -16110,24 +16511,24 @@ function scanSkillsDir(dir, source, out) {
16110
16511
  }
16111
16512
  }
16112
16513
  function scanClaudePluginSkills(pluginsRoot, out) {
16113
- if (!(0, import_node_fs40.existsSync)(pluginsRoot)) return;
16514
+ if (!(0, import_node_fs41.existsSync)(pluginsRoot)) return;
16114
16515
  const walk = (dir, depth, lookingForSkillsDir) => {
16115
16516
  if (depth > 7) return;
16116
16517
  let entries;
16117
16518
  try {
16118
- entries = (0, import_node_fs40.readdirSync)(dir);
16519
+ entries = (0, import_node_fs41.readdirSync)(dir);
16119
16520
  } catch {
16120
16521
  return;
16121
16522
  }
16122
16523
  if (lookingForSkillsDir && entries.includes("SKILL.md")) {
16123
- const skill = readSkill((0, import_node_path37.join)(dir, "SKILL.md"), "cli");
16524
+ const skill = readSkill((0, import_node_path38.join)(dir, "SKILL.md"), "cli");
16124
16525
  if (skill) out.push(skill);
16125
16526
  }
16126
16527
  for (const entry of entries) {
16127
16528
  if (entry === "node_modules" || entry === ".git") continue;
16128
- const full = (0, import_node_path37.join)(dir, entry);
16529
+ const full = (0, import_node_path38.join)(dir, entry);
16129
16530
  try {
16130
- if (!(0, import_node_fs40.statSync)(full).isDirectory()) continue;
16531
+ if (!(0, import_node_fs41.statSync)(full).isDirectory()) continue;
16131
16532
  } catch {
16132
16533
  continue;
16133
16534
  }
@@ -16145,17 +16546,17 @@ function discoverSkills(worktreePath) {
16145
16546
  const home = (0, import_node_os11.homedir)();
16146
16547
  const collected = [];
16147
16548
  for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
16148
- scanSkillsDir((0, import_node_path37.join)(worktreePath, rel), "workspace", collected);
16549
+ scanSkillsDir((0, import_node_path38.join)(worktreePath, rel), "workspace", collected);
16149
16550
  }
16150
16551
  for (const abs of [
16151
- (0, import_node_path37.join)(home, ".claude/skills"),
16152
- (0, import_node_path37.join)(home, ".cursor/skills"),
16153
- (0, import_node_path37.join)(home, ".sideboard/skills"),
16154
- (0, import_node_path37.join)(home, ".brightsy/skills")
16552
+ (0, import_node_path38.join)(home, ".claude/skills"),
16553
+ (0, import_node_path38.join)(home, ".cursor/skills"),
16554
+ (0, import_node_path38.join)(home, ".sideboard/skills"),
16555
+ (0, import_node_path38.join)(home, ".brightsy/skills")
16155
16556
  ]) {
16156
16557
  scanSkillsDir(abs, "user", collected);
16157
16558
  }
16158
- scanClaudePluginSkills((0, import_node_path37.join)(home, ".claude/plugins"), collected);
16559
+ scanClaudePluginSkills((0, import_node_path38.join)(home, ".claude/plugins"), collected);
16159
16560
  const rank = { workspace: 0, user: 1, cli: 2 };
16160
16561
  const byCommand = /* @__PURE__ */ new Map();
16161
16562
  for (const skill of collected) {
@@ -16167,7 +16568,7 @@ function discoverSkills(worktreePath) {
16167
16568
  return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
16168
16569
  }
16169
16570
  function readSkillBody(skillPath, maxChars = 12e3) {
16170
- const raw = (0, import_node_fs40.readFileSync)(skillPath, "utf8");
16571
+ const raw = (0, import_node_fs41.readFileSync)(skillPath, "utf8");
16171
16572
  if (raw.startsWith("---")) {
16172
16573
  const end = raw.indexOf("\n---", 3);
16173
16574
  if (end >= 0) {
@@ -16181,13 +16582,13 @@ function readSkillBody(skillPath, maxChars = 12e3) {
16181
16582
 
16182
16583
  \u2026(truncated)` : raw;
16183
16584
  }
16184
- var import_node_fs40, import_node_os11, import_node_path37;
16585
+ var import_node_fs41, import_node_os11, import_node_path38;
16185
16586
  var init_discover = __esm({
16186
16587
  "src/skills/discover.ts"() {
16187
16588
  "use strict";
16188
- import_node_fs40 = require("fs");
16589
+ import_node_fs41 = require("fs");
16189
16590
  import_node_os11 = require("os");
16190
- import_node_path37 = require("path");
16591
+ import_node_path38 = require("path");
16191
16592
  }
16192
16593
  });
16193
16594
 
@@ -16276,236 +16677,6 @@ var init_expand = __esm({
16276
16677
  }
16277
16678
  });
16278
16679
 
16279
- // src/composer/stage-files.ts
16280
- function fileExtension(filePath) {
16281
- const base = (0, import_node_path38.basename)(filePath).toLowerCase();
16282
- return base.includes(".") ? base.split(".").pop() || "" : "";
16283
- }
16284
- function isImageFilePath(filePath) {
16285
- return IMAGE_EXTENSIONS2.has(fileExtension(filePath));
16286
- }
16287
- function imageMimeType(filePath) {
16288
- return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
16289
- }
16290
- function ensureAttachmentsDir(worktreePath) {
16291
- const dir = (0, import_node_path38.join)(worktreePath, ATTACHMENTS_DIR);
16292
- (0, import_node_fs41.mkdirSync)(dir, { recursive: true });
16293
- const gi = (0, import_node_path38.join)(dir, ".gitignore");
16294
- if (!(0, import_node_fs41.existsSync)(gi)) {
16295
- (0, import_node_fs41.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
16296
- }
16297
- return dir;
16298
- }
16299
- function uniqueAttachmentName(dir, originalName) {
16300
- const safe = originalName.replace(/[/\\]/g, "_") || "file";
16301
- if (!(0, import_node_fs41.existsSync)((0, import_node_path38.join)(dir, safe))) return safe;
16302
- const ext = (0, import_node_path38.extname)(safe);
16303
- const stem = ext ? safe.slice(0, -ext.length) : safe;
16304
- for (let i = 1; i < 1e4; i++) {
16305
- const candidate = `${stem}-${i}${ext}`;
16306
- if (!(0, import_node_fs41.existsSync)((0, import_node_path38.join)(dir, candidate))) return candidate;
16307
- }
16308
- return `${stem}-${(0, import_node_crypto8.randomUUID)()}${ext}`;
16309
- }
16310
- function previewDataUrlFromBuf(filePath, buf) {
16311
- if (!isImageFilePath(filePath)) return void 0;
16312
- if (buf.length > MAX_PREVIEW_BYTES) return void 0;
16313
- return `data:${imageMimeType(filePath)};base64,${buf.toString("base64")}`;
16314
- }
16315
- function attachmentFromBuffer(name, buf, opts) {
16316
- const previewDataUrl = previewDataUrlFromBuf(name, buf);
16317
- if (isImageFilePath(name)) {
16318
- const pathHint = opts.path ? `\`${opts.path}\`` : opts.sourceLabel || name;
16319
- return {
16320
- id: (0, import_node_crypto8.randomUUID)(),
16321
- name,
16322
- kind: "file",
16323
- path: opts.path,
16324
- previewDataUrl,
16325
- content: [
16326
- `Image attached: ${pathHint}`,
16327
- opts.path ? `Use the Read tool on \`${opts.path}\` to view this image.` : "The image is shown in the composer; copy it into the worktree if you need to inspect pixels."
16328
- ].join("\n")
16329
- };
16330
- }
16331
- if (buf.length > MAX_INLINE_BYTES) {
16332
- return {
16333
- id: (0, import_node_crypto8.randomUUID)(),
16334
- name,
16335
- kind: "file",
16336
- path: opts.path,
16337
- content: opts.path ? `(file too large to attach inline: \`${opts.path}\`, ${buf.length} bytes \u2014 use the Read tool)` : `(file too large to attach inline: ${opts.sourceLabel || name}, ${buf.length} bytes)`
16338
- };
16339
- }
16340
- if (buf.includes(0)) {
16341
- return {
16342
- id: (0, import_node_crypto8.randomUUID)(),
16343
- name,
16344
- kind: "file",
16345
- path: opts.path,
16346
- content: opts.path ? `(binary file at \`${opts.path}\` \u2014 use tools to inspect)` : `(binary file attached by path only: ${opts.sourceLabel || name})`
16347
- };
16348
- }
16349
- return {
16350
- id: (0, import_node_crypto8.randomUUID)(),
16351
- name,
16352
- kind: "file",
16353
- path: opts.path,
16354
- content: buf.toString("utf8")
16355
- };
16356
- }
16357
- function attachmentFromAbsolutePath(absolutePath) {
16358
- const name = (0, import_node_path38.basename)(absolutePath);
16359
- try {
16360
- const st = (0, import_node_fs41.statSync)(absolutePath);
16361
- if (!st.isFile()) {
16362
- return {
16363
- id: (0, import_node_crypto8.randomUUID)(),
16364
- name,
16365
- kind: "file",
16366
- content: `(not a file: ${absolutePath})`
16367
- };
16368
- }
16369
- const buf = (0, import_node_fs41.readFileSync)(absolutePath);
16370
- return attachmentFromBuffer(name, buf, { sourceLabel: absolutePath });
16371
- } catch (err) {
16372
- return {
16373
- id: (0, import_node_crypto8.randomUUID)(),
16374
- name,
16375
- kind: "file",
16376
- content: `(could not read ${absolutePath}: ${err instanceof Error ? err.message : String(err)})`
16377
- };
16378
- }
16379
- }
16380
- function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
16381
- if (absolutePaths.length === 0) return [];
16382
- const dir = ensureAttachmentsDir(worktreePath);
16383
- const out = [];
16384
- for (const abs of absolutePaths) {
16385
- const originalName = (0, import_node_path38.basename)(abs);
16386
- try {
16387
- const st = (0, import_node_fs41.statSync)(abs);
16388
- if (!st.isFile()) continue;
16389
- const name = uniqueAttachmentName(dir, originalName);
16390
- const destAbs = (0, import_node_path38.join)(dir, name);
16391
- (0, import_node_fs41.copyFileSync)(abs, destAbs);
16392
- const rel = `${ATTACHMENTS_DIR}/${name}`;
16393
- const buf = (0, import_node_fs41.readFileSync)(destAbs);
16394
- out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
16395
- } catch (err) {
16396
- out.push({
16397
- id: (0, import_node_crypto8.randomUUID)(),
16398
- name: originalName,
16399
- kind: "file",
16400
- content: `(could not attach ${abs}: ${err instanceof Error ? err.message : String(err)})`
16401
- });
16402
- }
16403
- }
16404
- return out;
16405
- }
16406
- function stageBuffersAsAttachments(worktreePath, buffers2) {
16407
- if (buffers2.length === 0) return [];
16408
- const dir = ensureAttachmentsDir(worktreePath);
16409
- const out = [];
16410
- for (const item of buffers2) {
16411
- const originalName = (item.name || "file").replace(/[/\\]/g, "_") || "file";
16412
- try {
16413
- const buf = Buffer.from(item.dataBase64, "base64");
16414
- const name = uniqueAttachmentName(dir, originalName);
16415
- const destAbs = (0, import_node_path38.join)(dir, name);
16416
- (0, import_node_fs41.writeFileSync)(destAbs, buf);
16417
- const rel = `${ATTACHMENTS_DIR}/${name}`;
16418
- out.push(attachmentFromBuffer(name, buf, { path: rel }));
16419
- } catch (err) {
16420
- out.push({
16421
- id: (0, import_node_crypto8.randomUUID)(),
16422
- name: originalName,
16423
- kind: "file",
16424
- content: `(could not attach ${originalName}: ${err instanceof Error ? err.message : String(err)})`
16425
- });
16426
- }
16427
- }
16428
- return out;
16429
- }
16430
- function attachmentsFromBuffers(buffers2) {
16431
- return buffers2.map((item) => {
16432
- const name = (item.name || "file").replace(/[/\\]/g, "_") || "file";
16433
- try {
16434
- const buf = Buffer.from(item.dataBase64, "base64");
16435
- return attachmentFromBuffer(name, buf, { sourceLabel: name });
16436
- } catch (err) {
16437
- return {
16438
- id: (0, import_node_crypto8.randomUUID)(),
16439
- name,
16440
- kind: "file",
16441
- content: `(could not attach ${name}: ${err instanceof Error ? err.message : String(err)})`
16442
- };
16443
- }
16444
- });
16445
- }
16446
- function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
16447
- const out = [];
16448
- for (const rel of relativePaths) {
16449
- if (!rel || rel.includes("..") || rel.startsWith("/")) {
16450
- out.push({
16451
- id: (0, import_node_crypto8.randomUUID)(),
16452
- name: (0, import_node_path38.basename)(rel) || "file",
16453
- kind: "file",
16454
- content: `(invalid path: ${rel})`
16455
- });
16456
- continue;
16457
- }
16458
- const name = (0, import_node_path38.basename)(rel);
16459
- try {
16460
- const abs = (0, import_node_path38.join)(worktreePath, rel);
16461
- const st = (0, import_node_fs41.statSync)(abs);
16462
- if (!st.isFile()) continue;
16463
- const buf = (0, import_node_fs41.readFileSync)(abs);
16464
- out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
16465
- } catch (err) {
16466
- out.push({
16467
- id: (0, import_node_crypto8.randomUUID)(),
16468
- name,
16469
- kind: "file",
16470
- content: `(could not read ${rel}: ${err instanceof Error ? err.message : String(err)})`
16471
- });
16472
- }
16473
- }
16474
- return out;
16475
- }
16476
- var import_node_fs41, import_node_path38, import_node_crypto8, IMAGE_EXTENSIONS2, IMAGE_MIME_BY_EXT, MAX_INLINE_BYTES, MAX_PREVIEW_BYTES;
16477
- var init_stage_files = __esm({
16478
- "src/composer/stage-files.ts"() {
16479
- "use strict";
16480
- import_node_fs41 = require("fs");
16481
- import_node_path38 = require("path");
16482
- import_node_crypto8 = require("crypto");
16483
- init_workspace_scratch();
16484
- IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
16485
- "png",
16486
- "jpg",
16487
- "jpeg",
16488
- "gif",
16489
- "webp",
16490
- "svg",
16491
- "bmp",
16492
- "ico"
16493
- ]);
16494
- IMAGE_MIME_BY_EXT = {
16495
- png: "image/png",
16496
- jpg: "image/jpeg",
16497
- jpeg: "image/jpeg",
16498
- gif: "image/gif",
16499
- webp: "image/webp",
16500
- svg: "image/svg+xml",
16501
- bmp: "image/bmp",
16502
- ico: "image/x-icon"
16503
- };
16504
- MAX_INLINE_BYTES = 4e5;
16505
- MAX_PREVIEW_BYTES = 5e6;
16506
- }
16507
- });
16508
-
16509
16680
  // src/agents/instructions.ts
16510
16681
  function normPath3(p) {
16511
16682
  return p.replace(/\/+$/, "");
@@ -17108,6 +17279,7 @@ var init_orchestrator = __esm({
17108
17279
  init_usage();
17109
17280
  init_thread_store();
17110
17281
  init_desktop_host();
17282
+ init_child_halt();
17111
17283
  init_create();
17112
17284
  init_cowboy();
17113
17285
  init_orchestrator_capable();
@@ -17255,6 +17427,9 @@ var init_orchestrator = __esm({
17255
17427
  * MCP-created review threads don't stay `queued` after the MCP child exits.
17256
17428
  */
17257
17429
  adoptPersistedQueues() {
17430
+ if (thisProcessShouldDrainAgentQueues()) {
17431
+ this.healStaleRunningTurns();
17432
+ }
17258
17433
  for (const thread of listThreads()) {
17259
17434
  if (thread.status === "stopped" || thread.status === "archived") continue;
17260
17435
  const pid = thread.agentPid;
@@ -17275,6 +17450,32 @@ var init_orchestrator = __esm({
17275
17450
  }
17276
17451
  }
17277
17452
  }
17453
+ /**
17454
+ * Mid-session: a worktree can sit at `running` after the agent process dies
17455
+ * (Cursor/CLI crash, OOM) while wait_for_turn still reports stillRunning.
17456
+ * Reclaim those and wake the parent orchestration chat.
17457
+ */
17458
+ healStaleRunningTurns() {
17459
+ for (const thread of listThreads()) {
17460
+ if (thread.status === "archived") continue;
17461
+ const handle = this.activeTurns.get(thread.id);
17462
+ if (handle) {
17463
+ const pid = thread.agentPid;
17464
+ if (typeof pid === "number" && pid > 0 && !isPidAlive(pid)) {
17465
+ handle.kill();
17466
+ }
17467
+ continue;
17468
+ }
17469
+ if (!this.shouldReclaimRunningThread(thread)) continue;
17470
+ setStatus(thread.id, "stopped", "Process died (agent exited)");
17471
+ this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
17472
+ this.emit({ type: "turn_finished", threadId: thread.id, exitCode: 1 });
17473
+ const latest = readThread(thread.id);
17474
+ if (latest) {
17475
+ notifyParentOfChildHalt(latest, "stopped", (id, prompt) => this.send(id, prompt));
17476
+ }
17477
+ }
17478
+ }
17278
17479
  clearQuotaResumeTimer(threadId) {
17279
17480
  const timer = this.quotaResumeTimers.get(threadId);
17280
17481
  if (timer) clearTimeout(timer);
@@ -17992,6 +18193,12 @@ var init_orchestrator = __esm({
17992
18193
  assistantText: chatText,
17993
18194
  partsCount: parts.length
17994
18195
  });
18196
+ if (!this.crashContinued.has(threadId)) {
18197
+ const failed = readThread(threadId);
18198
+ if (failed) {
18199
+ notifyParentOfChildHalt(failed, "error", (id, prompt2) => this.send(id, prompt2));
18200
+ }
18201
+ }
17995
18202
  }
17996
18203
  }
17997
18204
  } catch (err) {
@@ -18016,6 +18223,12 @@ var init_orchestrator = __esm({
18016
18223
  assistantText: "",
18017
18224
  partsCount: 0
18018
18225
  });
18226
+ if (!this.crashContinued.has(threadId)) {
18227
+ const failed = readThread(threadId);
18228
+ if (failed) {
18229
+ notifyParentOfChildHalt(failed, "error", (id, prompt2) => this.send(id, prompt2));
18230
+ }
18231
+ }
18019
18232
  }
18020
18233
  } finally {
18021
18234
  this.startingTurns.delete(threadId);
@@ -18071,6 +18284,9 @@ var init_orchestrator = __esm({
18071
18284
  const stopped = writeLiveStatus(thread.id, "stopped") ?? readThread(thread.id) ?? thread;
18072
18285
  if (stopped.status === "stopped") {
18073
18286
  this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
18287
+ if (opts?.notifyParent !== false) {
18288
+ notifyParentOfChildHalt(stopped, "stopped", (id, prompt) => this.send(id, prompt));
18289
+ }
18074
18290
  }
18075
18291
  return stopped;
18076
18292
  }
@@ -18315,21 +18531,25 @@ var init_orchestrator = __esm({
18315
18531
  fn();
18316
18532
  };
18317
18533
  const off = this.on((event) => {
18318
- if (event.type === "turn_finished" && event.threadId === thread.id) {
18534
+ if (!("threadId" in event) || event.threadId !== thread.id) return;
18535
+ if (event.type === "turn_finished" || event.type === "error") {
18319
18536
  const latest = readThread(thread.id);
18320
18537
  if (!latest) {
18321
18538
  finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
18322
18539
  return;
18323
18540
  }
18324
18541
  finish(() => resolve(latest));
18542
+ return;
18325
18543
  }
18326
- if (event.type === "error" && event.threadId === thread.id) {
18544
+ if (event.type === "status_changed") {
18327
18545
  const latest = readThread(thread.id);
18328
18546
  if (!latest) {
18329
18547
  finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
18330
18548
  return;
18331
18549
  }
18332
- finish(() => resolve(latest));
18550
+ if (!["running", "queued"].includes(latest.status)) {
18551
+ finish(() => resolve(latest));
18552
+ }
18333
18553
  }
18334
18554
  });
18335
18555
  timer = setInterval(() => {
@@ -18364,7 +18584,7 @@ var init_orchestrator = __esm({
18364
18584
  const thread = this.requireThread(threadRef);
18365
18585
  const lastAgent = [...thread.messages].reverse().find((m) => m.role === "agent");
18366
18586
  const lastError = thread.lastError ?? null;
18367
- const text5 = (lastAgent?.text ?? "").trim() || (thread.status === "error" ? lastError ?? "" : "");
18587
+ const text5 = (lastAgent?.text ?? "").trim() || (thread.status === "error" || thread.status === "stopped" || thread.status === "broken" ? lastError ?? "" : "");
18368
18588
  const stillRunning = thread.status === "running" || thread.status === "queued";
18369
18589
  const live = stillRunning ? readTurnLive(thread.id) : null;
18370
18590
  const queuedHint = thread.status === "queued" && !live?.summary ? "Queued \u2014 waiting for a concurrency slot" : null;
@@ -19142,6 +19362,7 @@ __export(index_exports, {
19142
19362
  PLAN_FILE_NAME: () => PLAN_FILE_NAME,
19143
19363
  PLAN_FILE_REL: () => PLAN_FILE_REL,
19144
19364
  PLAN_MODE_INSTRUCTION: () => PLAN_MODE_INSTRUCTION,
19365
+ PLAN_QUESTION_ANSWERS_PREFIX: () => PLAN_QUESTION_ANSWERS_PREFIX,
19145
19366
  REPO_REVIEW_NAME: () => REPO_REVIEW_NAME,
19146
19367
  REPO_REVIEW_PATH: () => REPO_REVIEW_PATH,
19147
19368
  REVIEW_REQUEST_NAME: () => REVIEW_REQUEST_NAME,
@@ -19415,6 +19636,7 @@ __export(index_exports, {
19415
19636
  inspectGitWorktree: () => inspectGitWorktree,
19416
19637
  installAgent: () => installAgent,
19417
19638
  interruptSlackCoordinatorForInbound: () => interruptSlackCoordinatorForInbound,
19639
+ invalidateThreadListCache: () => invalidateThreadListCache,
19418
19640
  isAbleTimeConnected: () => isAbleTimeConnected,
19419
19641
  isAskUserToolName: () => isAskUserToolName,
19420
19642
  isBrightsyConnected: () => isBrightsyConnected,
@@ -19440,6 +19662,7 @@ __export(index_exports, {
19440
19662
  isOrchestratorThread: () => isOrchestratorThread,
19441
19663
  isPidAlive: () => isPidAlive,
19442
19664
  isPlaceholderBranch: () => isPlaceholderBranch,
19665
+ isPlanQuestionAnswersMessage: () => isPlanQuestionAnswersMessage,
19443
19666
  isPollWrapperToolName: () => isPollWrapperToolName,
19444
19667
  isPrNotMergeableError: () => isPrNotMergeableError,
19445
19668
  isPresentPlanToolName: () => isPresentPlanToolName,
@@ -19550,6 +19773,7 @@ __export(index_exports, {
19550
19773
  pastedTextStats: () => pastedTextStats,
19551
19774
  pendingSlackExternalReplies: () => pendingSlackExternalReplies,
19552
19775
  permissionMode: () => permissionMode,
19776
+ persistPendingFileAttachments: () => persistPendingFileAttachments,
19553
19777
  persistVaultKeyInKeychain: () => persistVaultKeyInKeychain,
19554
19778
  planFileAbs: () => planFileAbs,
19555
19779
  planQuestionsSignature: () => planQuestionsSignature,
@@ -19719,6 +19943,7 @@ __export(index_exports, {
19719
19943
  userCursorMcpConfigPath: () => userCursorMcpConfigPath,
19720
19944
  validateLinearApiKey: () => validateLinearApiKey,
19721
19945
  verifyAbleTimeConnection: () => verifyAbleTimeConnection,
19946
+ visibleToolRowDetail: () => visibleToolRowDetail,
19722
19947
  waitForPidExit: () => waitForPidExit,
19723
19948
  warmGithubAgentAuth: () => warmGithubAgentAuth,
19724
19949
  withAgentInstructions: () => withAgentInstructions,
@@ -20744,8 +20969,12 @@ function latestPendingPlanQuestions(input) {
20744
20969
  if (last?.role === "agent") return extractPendingPlanQuestions(last.parts);
20745
20970
  return null;
20746
20971
  }
20972
+ var PLAN_QUESTION_ANSWERS_PREFIX = "Answers to your questions:";
20973
+ function isPlanQuestionAnswersMessage(text5) {
20974
+ return text5.startsWith(PLAN_QUESTION_ANSWERS_PREFIX);
20975
+ }
20747
20976
  function formatPlanQuestionAnswers(questions, answers) {
20748
- const lines = ["Answers to your questions:"];
20977
+ const lines = [PLAN_QUESTION_ANSWERS_PREFIX, ""];
20749
20978
  for (let i = 0; i < questions.length; i++) {
20750
20979
  const q = questions[i];
20751
20980
  const a = answers.find((x) => x.questionIndex === i);
@@ -20754,7 +20983,7 @@ function formatPlanQuestionAnswers(questions, answers) {
20754
20983
  if (a?.selected.length) parts.push(a.selected.join(", "));
20755
20984
  if (a?.other?.trim()) parts.push(a.other.trim());
20756
20985
  const body = parts.length ? parts.join(" \xB7 ") : "(no answer)";
20757
- lines.push(`${i + 1}. ${header}${q.question}`);
20986
+ lines.push(`${i + 1}. ${header}${q.question} `);
20758
20987
  lines.push(` \u2192 ${body}`);
20759
20988
  }
20760
20989
  return lines.join("\n");
@@ -20822,6 +21051,15 @@ var MCP_WAIT_QUEUED_HINT = "Child is queued waiting for a concurrency slot \u201
20822
21051
  function mcpWaitStillRunningHint(status) {
20823
21052
  return status === "queued" ? MCP_WAIT_QUEUED_HINT : MCP_WAIT_STILL_RUNNING_HINT;
20824
21053
  }
21054
+ var MCP_WAIT_STOPPED_HINT = "Child was stopped before the turn finished. Do not treat this as success. send_to_thread to resume, or tell the user.";
21055
+ var MCP_WAIT_BROKEN_HINT = "Child worktree is broken (missing on disk). Tell the user \u2014 do not treat this as success.";
21056
+ var MCP_WAIT_ERROR_HINT = "Child turn failed. lastError/text is the failure \u2014 switch agent, tell the user, or retry. Do not treat empty text as success.";
21057
+ function mcpWaitFinishedHint(status) {
21058
+ if (status === "stopped") return MCP_WAIT_STOPPED_HINT;
21059
+ if (status === "broken") return MCP_WAIT_BROKEN_HINT;
21060
+ if (status === "error") return MCP_WAIT_ERROR_HINT;
21061
+ return void 0;
21062
+ }
20825
21063
 
20826
21064
  // src/mcp/server.ts
20827
21065
  init_turn_live();
@@ -22600,7 +22838,7 @@ async function startMcpServer() {
22600
22838
  );
22601
22839
  server.tool(
22602
22840
  "wait_for_turn",
22603
- "Wait until the thread finishes its current/queued turn, or return early with a live progress snapshot. MCP clients often kill tools around 60s, so this returns within 45s even while the child is still working. If stillRunning is true, progress is tools/thinking (or \u201Cqueued, waiting for a concurrency slot\u201D if it has not started). Call wait_for_turn again. Do not send a check-in prompt, force_stop, or assume a hang. On status error, lastError/text is the failure. When finished, usage is the last agent turn\u2019s tokens + costUsd (when the provider reported cost).",
22841
+ "Wait until the thread finishes its current/queued turn, or return early with a live progress snapshot. MCP clients often kill tools around 60s, so this returns within 45s even while the child is still working. If stillRunning is true, progress is tools/thinking (or \u201Cqueued, waiting for a concurrency slot\u201D if it has not started). Call wait_for_turn again. Do not send a check-in prompt, force_stop, or assume a hang. On status error, lastError/text is the failure. On status stopped or broken, the child did not finish \u2014 resume with send_to_thread or tell the user; do not treat that as success. When finished, usage is the last agent turn\u2019s tokens + costUsd (when the provider reported cost).",
22604
22842
  {
22605
22843
  ref: import_zod5.z.string(),
22606
22844
  timeoutMs: import_zod5.z.number().optional()
@@ -22622,7 +22860,8 @@ async function startMcpServer() {
22622
22860
  stillRunning: result.stillRunning,
22623
22861
  progress: result.progress,
22624
22862
  lastActivityAt: result.lastActivityAt,
22625
- hint: result.stillRunning ? mcpWaitStillRunningHint(result.status) : void 0
22863
+ hint: result.stillRunning ? mcpWaitStillRunningHint(result.status) : mcpWaitFinishedHint(result.status),
22864
+ incomplete: !result.stillRunning && Boolean(mcpWaitFinishedHint(result.status))
22626
22865
  })
22627
22866
  }
22628
22867
  ]
@@ -22641,7 +22880,8 @@ async function startMcpServer() {
22641
22880
  type: "text",
22642
22881
  text: JSON.stringify({
22643
22882
  ...result,
22644
- hint: result.stillRunning ? mcpWaitStillRunningHint(result.status) : void 0
22883
+ hint: result.stillRunning ? mcpWaitStillRunningHint(result.status) : mcpWaitFinishedHint(result.status),
22884
+ incomplete: !result.stillRunning && Boolean(mcpWaitFinishedHint(result.status))
22645
22885
  })
22646
22886
  }
22647
22887
  ]
@@ -22665,7 +22905,7 @@ async function startMcpServer() {
22665
22905
  }
22666
22906
  const clearQueue = force !== false;
22667
22907
  const hadQueued = t.queue.length > 0;
22668
- const stopped = orch.stop(ref, { clearQueue });
22908
+ const stopped = orch.stop(ref, { clearQueue, notifyParent: false });
22669
22909
  return {
22670
22910
  content: [
22671
22911
  {
@@ -25600,6 +25840,7 @@ init_outbound_watch();
25600
25840
  PLAN_FILE_NAME,
25601
25841
  PLAN_FILE_REL,
25602
25842
  PLAN_MODE_INSTRUCTION,
25843
+ PLAN_QUESTION_ANSWERS_PREFIX,
25603
25844
  REPO_REVIEW_NAME,
25604
25845
  REPO_REVIEW_PATH,
25605
25846
  REVIEW_REQUEST_NAME,
@@ -25873,6 +26114,7 @@ init_outbound_watch();
25873
26114
  inspectGitWorktree,
25874
26115
  installAgent,
25875
26116
  interruptSlackCoordinatorForInbound,
26117
+ invalidateThreadListCache,
25876
26118
  isAbleTimeConnected,
25877
26119
  isAskUserToolName,
25878
26120
  isBrightsyConnected,
@@ -25898,6 +26140,7 @@ init_outbound_watch();
25898
26140
  isOrchestratorThread,
25899
26141
  isPidAlive,
25900
26142
  isPlaceholderBranch,
26143
+ isPlanQuestionAnswersMessage,
25901
26144
  isPollWrapperToolName,
25902
26145
  isPrNotMergeableError,
25903
26146
  isPresentPlanToolName,
@@ -26008,6 +26251,7 @@ init_outbound_watch();
26008
26251
  pastedTextStats,
26009
26252
  pendingSlackExternalReplies,
26010
26253
  permissionMode,
26254
+ persistPendingFileAttachments,
26011
26255
  persistVaultKeyInKeychain,
26012
26256
  planFileAbs,
26013
26257
  planQuestionsSignature,
@@ -26177,6 +26421,7 @@ init_outbound_watch();
26177
26421
  userCursorMcpConfigPath,
26178
26422
  validateLinearApiKey,
26179
26423
  verifyAbleTimeConnection,
26424
+ visibleToolRowDetail,
26180
26425
  waitForPidExit,
26181
26426
  warmGithubAgentAuth,
26182
26427
  withAgentInstructions,