@sideboard-ai/core 0.1.297 → 0.1.299

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 (37) hide show
  1. package/dist/{agents-ICHYDSCD.js → agents-IVKPCK56.js} +5 -5
  2. package/dist/{agents-DZTPUKVF.js → agents-JCCJJMA7.js} +5 -5
  3. package/dist/{chunk-VXNZG4J6.js → chunk-3IXGI2FP.js} +1 -1
  4. package/dist/{chunk-ED4ZXQVO.js → chunk-5XQ43YZU.js} +2 -1
  5. package/dist/{chunk-2O3ICR77.js → chunk-EB3F5AWV.js} +6 -4
  6. package/dist/{chunk-6GTABVNG.js → chunk-EB7B4D2Z.js} +6 -4
  7. package/dist/{chunk-Q3DPZ4DE.js → chunk-F36QRDM4.js} +2 -1
  8. package/dist/{chunk-ZXWRNA4G.js → chunk-F3JYJ4UB.js} +5 -5
  9. package/dist/{chunk-JWXJXEA3.js → chunk-G52HLBQR.js} +22 -2
  10. package/dist/{chunk-WZLV4WNS.js → chunk-K5EACGG2.js} +11 -0
  11. package/dist/{chunk-67BWWLEB.js → chunk-MMIDAD6J.js} +5 -5
  12. package/dist/{chunk-W2A63ZOK.js → chunk-NF3WDI7F.js} +501 -124
  13. package/dist/{chunk-BGEECLKH.js → chunk-QB4UQA5L.js} +10 -6
  14. package/dist/{chunk-WQREAX35.js → chunk-UMALFEDS.js} +10 -6
  15. package/dist/{chunk-QT36F4TD.js → chunk-YAILSKW3.js} +379 -64
  16. package/dist/{conductor-HJFDYIZD.js → conductor-JLHXMMKR.js} +5 -5
  17. package/dist/{conductor-ZXDIA6OF.js → conductor-UQDDDW35.js} +5 -5
  18. package/dist/{coordinator-prompt-ILHSVPOG.js → coordinator-prompt-HKLVS26O.js} +3 -3
  19. package/dist/{coordinator-prompt-L3V2CMWX.js → coordinator-prompt-SSUYZSJO.js} +3 -3
  20. package/dist/{global-workspace-47G453ZU.js → global-workspace-I32EWKX3.js} +3 -3
  21. package/dist/{global-workspace-NMRUGGFF.js → global-workspace-MHE6FCUZ.js} +3 -3
  22. package/dist/index.cjs +1125 -557
  23. package/dist/index.d.cts +60 -35
  24. package/dist/index.d.ts +60 -35
  25. package/dist/index.js +345 -230
  26. package/dist/mcp/run-stdio.cjs +816 -341
  27. package/dist/mcp/run-stdio.js +324 -218
  28. package/dist/{orchestrator-ZQU62CES.js → orchestrator-ESLNXVOX.js} +7 -7
  29. package/dist/{orchestrator-MR7TCIPP.js → orchestrator-SX4SGZHF.js} +7 -7
  30. package/dist/{thread-store-H6D6MDUM.js → thread-store-5XXHSWMN.js} +1 -1
  31. package/dist/{thread-store-SADLLKRE.js → thread-store-WXLXKDVN.js} +1 -1
  32. package/dist/{workspaces-WGZPRQJX.js → workspaces-ATMIKXLK.js} +3 -3
  33. package/dist/{workspaces-PGY4UWZ3.js → workspaces-O3L32CDO.js} +3 -3
  34. package/dist/{worktree-JZXDAKQF.js → worktree-6G5X4ABY.js} +2 -2
  35. package/dist/{worktree-3MF5J5XZ.js → worktree-RMYXBTJG.js} +2 -2
  36. package/package.json +1 -1
  37. /package/dist/{chunk-TVN4TDFB.js → chunk-HTL4FBTP.js} +0 -0
@@ -27,7 +27,7 @@ import {
27
27
  parseSessionQuotaResetAt,
28
28
  planModeTurnInstruction,
29
29
  resolveQuotaFallbackAgent
30
- } from "./chunk-2O3ICR77.js";
30
+ } from "./chunk-EB3F5AWV.js";
31
31
  import {
32
32
  REVIEW_TAB_TITLE,
33
33
  applyWorktreePkgCacheEnv,
@@ -66,7 +66,7 @@ import {
66
66
  worktreeDevProcessKey,
67
67
  worktreeRunProcessKey,
68
68
  worktreeSetupProcessKey
69
- } from "./chunk-ZXWRNA4G.js";
69
+ } from "./chunk-F3JYJ4UB.js";
70
70
  import {
71
71
  applyAgentEvent,
72
72
  clipAgentEventForPaint,
@@ -93,7 +93,7 @@ import {
93
93
  summarizeTurnStderr,
94
94
  toolDescription,
95
95
  turnFailChatText
96
- } from "./chunk-ED4ZXQVO.js";
96
+ } from "./chunk-5XQ43YZU.js";
97
97
  import {
98
98
  SLACK_REPLY_FORMATTING,
99
99
  addWorkspace,
@@ -118,7 +118,7 @@ import {
118
118
  stageAbsolutePathsAsAttachments,
119
119
  stageBuffersAsAttachments,
120
120
  syncWorkspacesFromThreads
121
- } from "./chunk-WQREAX35.js";
121
+ } from "./chunk-UMALFEDS.js";
122
122
  import {
123
123
  addPrStackLayer,
124
124
  allocateTeamSlug,
@@ -160,7 +160,7 @@ import {
160
160
  resolveRepoRoot,
161
161
  switchWorktreeBranch,
162
162
  withRepoGitLock
163
- } from "./chunk-VXNZG4J6.js";
163
+ } from "./chunk-3IXGI2FP.js";
164
164
  import {
165
165
  clearStaleIndexLocks,
166
166
  enrichPathWithNpmGlobalBin,
@@ -238,7 +238,7 @@ import {
238
238
  worktreeNameFromPath,
239
239
  worktreeSlugForTicket,
240
240
  writeThread
241
- } from "./chunk-TVN4TDFB.js";
241
+ } from "./chunk-HTL4FBTP.js";
242
242
  import {
243
243
  annotateDevAccessRuns,
244
244
  appDataDir,
@@ -503,9 +503,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
503
503
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
504
504
  );
505
505
  }
506
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-NMRUGGFF.js");
506
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-MHE6FCUZ.js");
507
507
  if (isGlobalThread2(thread)) {
508
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-L3V2CMWX.js");
508
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-SSUYZSJO.js");
509
509
  ensureGlobalCoordinatorCwd2(
510
510
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
511
511
  );
@@ -1522,6 +1522,23 @@ function denyPendingToolPermissions(threadId) {
1522
1522
  if (last) clearPrompt(threadId, last.id);
1523
1523
  }
1524
1524
 
1525
+ // src/threads/connected-orchestrator.ts
1526
+ function connectedOrchestrator(thread, find) {
1527
+ if (isOrchestratorThread(thread)) return null;
1528
+ const seen = /* @__PURE__ */ new Set();
1529
+ let current = thread;
1530
+ for (let hop = 0; hop < 6 && current; hop++) {
1531
+ const parentId = current.parentThreadId?.trim();
1532
+ if (!parentId || seen.has(parentId)) return null;
1533
+ seen.add(parentId);
1534
+ const parent = find(parentId);
1535
+ if (!parent) return null;
1536
+ if (isOrchestratorThread(parent)) return parent;
1537
+ current = parent;
1538
+ }
1539
+ return null;
1540
+ }
1541
+
1525
1542
  // src/orchestrator/notify-orchestrator.ts
1526
1543
  var MAX_NOTIFY_ORCHESTRATOR_MESSAGE_CHARS = 1500;
1527
1544
  var inputRequiredNotified = /* @__PURE__ */ new Set();
@@ -1574,22 +1591,25 @@ function resolveNotifyCallerThread(opts) {
1574
1591
  return running;
1575
1592
  }
1576
1593
  function skipReason(opts) {
1577
- if (!opts.child.parentThreadId) {
1594
+ const parentId = opts.child.parentThreadId?.trim();
1595
+ if (!parentId) {
1578
1596
  return "This chat has no orchestrator parent. notify_orchestrator is for worktree children of a Global chat.";
1579
1597
  }
1580
- if (!opts.parent) {
1581
- return "Parent orchestration chat was not found.";
1582
- }
1583
- if (opts.parent.status === "archived") {
1598
+ if (opts.parent?.status === "archived") {
1584
1599
  return "Parent orchestration chat is archived.";
1585
1600
  }
1601
+ if (!opts.parent) {
1602
+ const immediate = readThread(parentId);
1603
+ if (!immediate) return "Parent orchestration chat was not found.";
1604
+ return "Parent is not a Global orchestration chat.";
1605
+ }
1586
1606
  if (!isOrchestratorThread(opts.parent)) {
1587
1607
  return "Parent is not a Global orchestration chat.";
1588
1608
  }
1589
1609
  return "Cannot notify orchestrator.";
1590
1610
  }
1591
1611
  async function notifyOrchestrator(input) {
1592
- const parent = input.child.parentThreadId ? readThread(input.child.parentThreadId) : null;
1612
+ const parent = connectedOrchestrator(input.child, (id) => readThread(id));
1593
1613
  if (!shouldNotifyOrchestrator({ child: input.child, parent })) {
1594
1614
  return { ok: false, error: skipReason({ child: input.child, parent }) };
1595
1615
  }
@@ -1891,6 +1911,53 @@ function threadCardBlockFields(group) {
1891
1911
  return block ? { blockedReason: block.reason, blockedSource: block.source } : null;
1892
1912
  }
1893
1913
 
1914
+ // src/board/pr-state.ts
1915
+ function isOpenPrState(prUrl, prState) {
1916
+ if (!prUrl?.trim()) return false;
1917
+ const state = (prState ?? "OPEN").trim().toUpperCase();
1918
+ return state !== "MERGED" && state !== "CLOSED";
1919
+ }
1920
+ function isMergedPrState(prUrl, prState) {
1921
+ if (!prUrl?.trim()) return false;
1922
+ return (prState ?? "").trim().toUpperCase() === "MERGED";
1923
+ }
1924
+ function isApprovedOpenPr(thread) {
1925
+ if (!isOpenPrState(thread.prUrl, thread.prState) || thread.prIsDraft) return false;
1926
+ return (thread.prReviewDecision ?? "").trim().toUpperCase() === "APPROVED";
1927
+ }
1928
+
1929
+ // src/board/worktree-ownership.ts
1930
+ var DEFAULT_BOARD_OWNERSHIP = "all";
1931
+ function normalizeViewerLogin(login) {
1932
+ return (login ?? "").trim().toLowerCase();
1933
+ }
1934
+ function loginsInclude(logins, viewerLogin) {
1935
+ const me = normalizeViewerLogin(viewerLogin);
1936
+ if (!me) return false;
1937
+ return (logins ?? []).some((login) => normalizeViewerLogin(login) === me);
1938
+ }
1939
+ function classifyWorktreeOwnership(group, viewerLogin = "") {
1940
+ const me = normalizeViewerLogin(viewerLogin);
1941
+ if (group.some((t) => {
1942
+ const author = normalizeViewerLogin(t.prAuthorLogin);
1943
+ return Boolean(author && me && author === me);
1944
+ })) {
1945
+ return "mine";
1946
+ }
1947
+ const reviewing = group.some((t) => {
1948
+ const author = normalizeViewerLogin(t.prAuthorLogin);
1949
+ if (author && me && author !== me) return true;
1950
+ if (t.sourceType === "pr" && me && (!author || author !== me)) return true;
1951
+ if (me && loginsInclude(t.prReviewerLogins, me)) return true;
1952
+ return false;
1953
+ });
1954
+ return reviewing ? "reviewing" : "mine";
1955
+ }
1956
+ function worktreeMatchesOwnership(group, filter, viewerLogin = "") {
1957
+ if (filter === "all") return true;
1958
+ return classifyWorktreeOwnership(group, viewerLogin) === filter;
1959
+ }
1960
+
1894
1961
  // src/board/home-board.ts
1895
1962
  var GLOBAL_WORKSPACE_ID = "__global__";
1896
1963
  function isHomeBoardThread(thread) {
@@ -1902,18 +1969,10 @@ var BOARD_COLUMN_DEFS = [
1902
1969
  { id: "new", title: "New" },
1903
1970
  { id: "draft", title: "Draft" },
1904
1971
  { id: "review", title: "Review" },
1972
+ { id: "approved", title: "Approved" },
1905
1973
  { id: "done", title: "Merged" }
1906
1974
  ];
1907
1975
  var HOME_BOARD_CACHE_TTL_MS = 15 * 60 * 1e3;
1908
- function isOpenPrState(prUrl, prState) {
1909
- if (!prUrl?.trim()) return false;
1910
- const state = (prState ?? "OPEN").trim().toUpperCase();
1911
- return state !== "MERGED" && state !== "CLOSED";
1912
- }
1913
- function isMergedPrState(prUrl, prState) {
1914
- if (!prUrl?.trim()) return false;
1915
- return (prState ?? "").trim().toUpperCase() === "MERGED";
1916
- }
1917
1976
  var DEFAULT_WORKTREE_SORT = "created";
1918
1977
  function groupCreatedAt(group) {
1919
1978
  let min = group[0]?.createdAt ?? "";
@@ -1966,38 +2025,9 @@ function groupHomeBoardWorktrees(threads, sort = DEFAULT_WORKTREE_SORT, labelOf
1966
2025
  }
1967
2026
  return [...map.values()].map((list) => sortThreadsInWorktree(list)).sort((a, b) => compareWorktreeGroups(a, b, sort, labelOf));
1968
2027
  }
1969
- var DEFAULT_BOARD_OWNERSHIP = "all";
1970
- function normalizeViewerLogin(login) {
1971
- return (login ?? "").trim().toLowerCase();
1972
- }
1973
- function loginsInclude(logins, viewerLogin) {
1974
- const me = normalizeViewerLogin(viewerLogin);
1975
- if (!me) return false;
1976
- return (logins ?? []).some((login) => normalizeViewerLogin(login) === me);
1977
- }
1978
- function classifyWorktreeOwnership(group, viewerLogin = "") {
1979
- const me = normalizeViewerLogin(viewerLogin);
1980
- if (group.some((t) => {
1981
- const author = normalizeViewerLogin(t.prAuthorLogin);
1982
- return Boolean(author && me && author === me);
1983
- })) {
1984
- return "mine";
1985
- }
1986
- const reviewing = group.some((t) => {
1987
- const author = normalizeViewerLogin(t.prAuthorLogin);
1988
- if (author && me && author !== me) return true;
1989
- if (t.sourceType === "pr" && me && (!author || author !== me)) return true;
1990
- if (me && loginsInclude(t.prReviewerLogins, me)) return true;
1991
- return false;
1992
- });
1993
- return reviewing ? "reviewing" : "mine";
1994
- }
1995
- function worktreeMatchesOwnership(group, filter, viewerLogin = "") {
1996
- if (filter === "all") return true;
1997
- return classifyWorktreeOwnership(group, viewerLogin) === filter;
1998
- }
1999
2028
  function classifyWorktreeColumn(group) {
2000
2029
  if (group.some((t) => isMergedPrState(t.prUrl, t.prState))) return "done";
2030
+ if (group.some((t) => isApprovedOpenPr(t))) return "approved";
2001
2031
  if (group.some((t) => isOpenPrState(t.prUrl, t.prState) && !t.prIsDraft)) {
2002
2032
  return "review";
2003
2033
  }
@@ -2207,9 +2237,9 @@ var BOARD_PAGE_SIZE = 40;
2207
2237
  function tokenizeQuery(query) {
2208
2238
  return query.trim().toLowerCase().split(/\s+/).filter(Boolean);
2209
2239
  }
2210
- function haystackMatches(haystack, tokens) {
2240
+ function haystackMatches(haystack2, tokens) {
2211
2241
  if (tokens.length === 0) return true;
2212
- const h = haystack.toLowerCase();
2242
+ const h = haystack2.toLowerCase();
2213
2243
  return tokens.every((token) => h.includes(token));
2214
2244
  }
2215
2245
  function prAuthorLogin(pr) {
@@ -2248,6 +2278,7 @@ function emptyColumns() {
2248
2278
  new: [],
2249
2279
  draft: [],
2250
2280
  review: [],
2281
+ approved: [],
2251
2282
  done: []
2252
2283
  };
2253
2284
  }
@@ -2259,6 +2290,7 @@ function emptyHidden() {
2259
2290
  new: 0,
2260
2291
  draft: 0,
2261
2292
  review: 0,
2293
+ approved: 0,
2262
2294
  done: 0
2263
2295
  };
2264
2296
  }
@@ -2327,6 +2359,7 @@ function assembleHomeBoard(input) {
2327
2359
  new: 0,
2328
2360
  draft: 0,
2329
2361
  review: 0,
2362
+ approved: 0,
2330
2363
  done: 0,
2331
2364
  tickets: 0,
2332
2365
  prs: 0,
@@ -2339,6 +2372,7 @@ function assembleHomeBoard(input) {
2339
2372
  new: [],
2340
2373
  draft: [],
2341
2374
  review: [],
2375
+ approved: [],
2342
2376
  done: []
2343
2377
  };
2344
2378
  for (const group of groupHomeBoardWorktrees(live)) {
@@ -2373,7 +2407,7 @@ function assembleHomeBoard(input) {
2373
2407
  }
2374
2408
  return { columns, hidden, totals };
2375
2409
  }
2376
- var HOME_BOARD_AGENT_HINT = "Home is a Kanban of workspaces (one card per checkout; sibling chats on a card are separate agents). Do not create a second workspace for a ticket, PR, or named branch that already has a live checkout \u2014 create_workspace / start_board_card return that checkout (alreadyStarted) and its chats. Add another agent with fork_chat. Creating from the default branch still opens a new isolated workspace. Columns are the path to merge: New (no PR) \u2192 Draft (draft PR) \u2192 Review (open PR) \u2192 Merged. ownership=mine is PRs you authored (or WIP with no PR); ownership=reviewing is someone else's PR (or a review-requested checkout). Viewer login comes from gh, not a setting. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. blockedReason means an agent on that card is waiting on a person (the question, plan approval, or a reported block) \u2014 read it and do not send_to_chat a check-in. Orchestration chats stay in the sidebar. Do not invent status.";
2410
+ var HOME_BOARD_AGENT_HINT = "Home is a Kanban of workspaces (one card per checkout; sibling chats on a card are separate agents). Do not create a second workspace for a ticket, PR, or named branch that already has a live checkout \u2014 create_workspace / start_board_card return that checkout (alreadyStarted) and its chats. Add another agent with fork_chat. Creating from the default branch still opens a new isolated workspace. Columns are the path to merge: New (no PR) \u2192 Draft (draft PR) \u2192 Review (open PR) \u2192 Approved (approved, not merged) \u2192 Merged. ownership=mine is PRs you authored (or WIP with no PR); ownership=reviewing is someone else's PR (or a review-requested checkout). Viewer login comes from gh, not a setting. Archive removes the card to Settings \u2192 History (list_archive to browse, restore_workspace to bring it back). Queued/running are activity on the card, not columns. blockedReason means an agent on that card is waiting on a person (the question, plan approval, or a reported block) \u2014 read it and do not send_to_chat a check-in. Orchestration chats stay in the sidebar. Do not invent status.";
2377
2411
 
2378
2412
  // src/threads/workspace-tags.ts
2379
2413
  function workspaceTagTargetRef(opts) {
@@ -2672,6 +2706,271 @@ function shouldRemoveWorktreeOnTeardown(thread) {
2672
2706
  return !isCowboyThread(thread) && !isPrimaryCheckoutThread(thread);
2673
2707
  }
2674
2708
 
2709
+ // src/archive/fleet-archive.ts
2710
+ import { basename as basename2 } from "path";
2711
+ var DEFAULT_LIMIT = 40;
2712
+ var MAX_LIMIT = 250;
2713
+ function oneLine(value) {
2714
+ return value.replace(/\s+/g, " ").trim();
2715
+ }
2716
+ function stampOf(thread) {
2717
+ return thread.archivedAt?.trim() || thread.updatedAt || thread.createdAt || "";
2718
+ }
2719
+ function repoLabel(thread) {
2720
+ if (isOrchestratorThread(thread)) return "Orchestration";
2721
+ const path = thread.repoPath?.trim() ?? "";
2722
+ return path && basename2(path) || path || "project";
2723
+ }
2724
+ function toAgent(thread, flags) {
2725
+ const prUrl = thread.prUrl?.trim();
2726
+ return {
2727
+ id: thread.id,
2728
+ title: thread.title?.trim() || "Untitled",
2729
+ agent: thread.agent,
2730
+ repo: repoLabel(thread),
2731
+ branch: thread.branchName ?? "",
2732
+ sourceType: thread.sourceType,
2733
+ sourceRef: thread.sourceRef,
2734
+ archivedAt: stampOf(thread),
2735
+ ...prUrl ? { prUrl } : {},
2736
+ ...flags?.workspaceLive ? { workspaceLive: true } : {},
2737
+ ...flags?.workspaceArchived ? { workspaceArchived: true } : {}
2738
+ };
2739
+ }
2740
+ function checkoutKey(thread) {
2741
+ return thread.worktreePath?.trim() ? normalizeWorktreePath(thread.worktreePath) : "";
2742
+ }
2743
+ function liveCheckoutKeys(threads) {
2744
+ const keys = /* @__PURE__ */ new Set();
2745
+ for (const thread of threads) {
2746
+ if (thread.status === "archived" || isOrchestratorThread(thread)) continue;
2747
+ const key = checkoutKey(thread);
2748
+ if (key) keys.add(key);
2749
+ }
2750
+ return keys;
2751
+ }
2752
+ function workspaceFrom(agents) {
2753
+ const ordered = [...agents].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
2754
+ const primary = ordered[0];
2755
+ const rows = ordered.map((thread) => toAgent(thread));
2756
+ const archivedAt = rows.reduce(
2757
+ (latest, row) => row.archivedAt > latest ? row.archivedAt : latest,
2758
+ ""
2759
+ );
2760
+ const prUrl = ordered.map((thread) => thread.prUrl?.trim()).find(Boolean);
2761
+ return {
2762
+ id: primary.id,
2763
+ title: primary.title?.trim() || "Untitled",
2764
+ repo: repoLabel(primary),
2765
+ branch: primary.branchName ?? "",
2766
+ sourceType: primary.sourceType,
2767
+ sourceRef: primary.sourceRef,
2768
+ archivedAt,
2769
+ ...prUrl ? { prUrl } : {},
2770
+ ...ordered.some((thread) => thread.cowboy) ? { cowboy: true } : {},
2771
+ agents: rows
2772
+ };
2773
+ }
2774
+ function haystack(parts) {
2775
+ return parts.filter(Boolean).join(" ").toLowerCase();
2776
+ }
2777
+ function agentHay(agent) {
2778
+ return haystack([
2779
+ agent.id,
2780
+ agent.title,
2781
+ agent.agent,
2782
+ agent.repo,
2783
+ agent.branch,
2784
+ agent.sourceType,
2785
+ agent.sourceRef,
2786
+ agent.prUrl
2787
+ ]);
2788
+ }
2789
+ function workspaceMatches(workspace, query) {
2790
+ if (!query) return true;
2791
+ const own = haystack([
2792
+ workspace.id,
2793
+ workspace.title,
2794
+ workspace.repo,
2795
+ workspace.branch,
2796
+ workspace.sourceType,
2797
+ workspace.sourceRef,
2798
+ workspace.prUrl
2799
+ ]);
2800
+ if (own.includes(query)) return true;
2801
+ return workspace.agents.some((agent) => agentHay(agent).includes(query));
2802
+ }
2803
+ function clampLimit(limit) {
2804
+ if (limit == null || !Number.isFinite(limit)) return DEFAULT_LIMIT;
2805
+ return Math.min(MAX_LIMIT, Math.max(1, Math.floor(limit)));
2806
+ }
2807
+ function listFleetArchive(threads, opts) {
2808
+ const query = opts?.query?.trim().toLowerCase() ?? "";
2809
+ const kind = opts?.kind ?? "all";
2810
+ const limit = clampLimit(opts?.limit);
2811
+ const liveKeys = liveCheckoutKeys(threads);
2812
+ const archived = threads.filter((thread) => thread.status === "archived");
2813
+ const groups = /* @__PURE__ */ new Map();
2814
+ const loose = [];
2815
+ for (const thread of archived) {
2816
+ const key = checkoutKey(thread);
2817
+ if (!key || isOrchestratorThread(thread) || liveKeys.has(key)) {
2818
+ loose.push(thread);
2819
+ continue;
2820
+ }
2821
+ const group = groups.get(key) ?? [];
2822
+ group.push(thread);
2823
+ groups.set(key, group);
2824
+ }
2825
+ const rows = [];
2826
+ if (kind !== "agent") {
2827
+ for (const group of groups.values()) {
2828
+ const workspace = workspaceFrom(group);
2829
+ if (!workspaceMatches(workspace, query)) continue;
2830
+ rows.push({ kind: "workspace", stamp: workspace.archivedAt, workspace });
2831
+ }
2832
+ }
2833
+ if (kind !== "workspace") {
2834
+ const pool = kind === "agent" ? archived : loose;
2835
+ for (const thread of pool) {
2836
+ const key = checkoutKey(thread);
2837
+ const orchestration = isOrchestratorThread(thread);
2838
+ const agent = toAgent(thread, {
2839
+ workspaceLive: Boolean(key) && !orchestration && liveKeys.has(key),
2840
+ workspaceArchived: Boolean(key) && !orchestration && !liveKeys.has(key)
2841
+ });
2842
+ if (query && !agentHay(agent).includes(query)) continue;
2843
+ rows.push({ kind: "agent", stamp: agent.archivedAt, agent });
2844
+ }
2845
+ }
2846
+ rows.sort((a, b) => {
2847
+ if (a.stamp !== b.stamp) return a.stamp < b.stamp ? 1 : -1;
2848
+ const idA = a.kind === "workspace" ? a.workspace.id : a.agent.id;
2849
+ const idB = b.kind === "workspace" ? b.workspace.id : b.agent.id;
2850
+ return idA.localeCompare(idB);
2851
+ });
2852
+ const page = rows.slice(0, limit);
2853
+ return {
2854
+ rows: page,
2855
+ shown: page.length,
2856
+ hidden: Math.max(0, rows.length - page.length),
2857
+ total: rows.length,
2858
+ query
2859
+ };
2860
+ }
2861
+ function archivedAgentsOnCheckout(threads, worktreePath) {
2862
+ const key = worktreePath.trim() ? normalizeWorktreePath(worktreePath) : "";
2863
+ if (!key) return [];
2864
+ return threads.filter(
2865
+ (thread) => thread.status === "archived" && !isOrchestratorThread(thread) && Boolean(thread.worktreePath?.trim()) && normalizeWorktreePath(thread.worktreePath) === key
2866
+ ).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
2867
+ }
2868
+ function formatWorkspaceLine(workspace) {
2869
+ const bits = [
2870
+ workspace.id.slice(0, 8),
2871
+ "workspace",
2872
+ workspace.repo,
2873
+ workspace.branch ? `branch:${workspace.branch}` : "",
2874
+ workspace.sourceType && workspace.sourceRef ? `${workspace.sourceType}:${workspace.sourceRef}` : "",
2875
+ `agents:${workspace.agents.length}`,
2876
+ workspace.cowboy ? "cowboy" : "",
2877
+ workspace.prUrl ?? "",
2878
+ workspace.archivedAt ? `archived:${workspace.archivedAt}` : "",
2879
+ oneLine(workspace.title)
2880
+ ].filter(Boolean);
2881
+ return bits.join(" ");
2882
+ }
2883
+ function formatAgentLine(agent) {
2884
+ const bits = [
2885
+ agent.id.slice(0, 8),
2886
+ "agent",
2887
+ agent.repo,
2888
+ agent.agent,
2889
+ agent.branch ? `branch:${agent.branch}` : "",
2890
+ agent.sourceType && agent.sourceRef ? `${agent.sourceType}:${agent.sourceRef}` : "",
2891
+ agent.workspaceLive ? "workspace-live" : "",
2892
+ agent.workspaceArchived ? "workspace-archived" : "",
2893
+ agent.prUrl ?? "",
2894
+ agent.archivedAt ? `archived:${agent.archivedAt}` : "",
2895
+ oneLine(agent.title),
2896
+ `sideboard://chat/${agent.id}`
2897
+ ].filter(Boolean);
2898
+ return bits.join(" ");
2899
+ }
2900
+ function formatFleetArchive(view) {
2901
+ if (view.total === 0) {
2902
+ return view.query ? "(no archive matches)" : "(archive empty)";
2903
+ }
2904
+ const lines = [
2905
+ `${view.shown} shown, ${view.hidden} hidden. restore_workspace <id> brings that checkout and every archived agent back. restore_chat <id> brings one agent back.`
2906
+ ];
2907
+ for (const row of view.rows) {
2908
+ if (row.kind === "workspace") {
2909
+ lines.push(formatWorkspaceLine(row.workspace));
2910
+ for (const agent of row.workspace.agents) {
2911
+ lines.push(
2912
+ ` ${agent.id.slice(0, 8)} ${agent.agent} ${oneLine(agent.title)} sideboard://chat/${agent.id}`
2913
+ );
2914
+ }
2915
+ continue;
2916
+ }
2917
+ lines.push(formatAgentLine(row.agent));
2918
+ }
2919
+ return lines.join("\n");
2920
+ }
2921
+
2922
+ // src/orchestrator/workspace-archive.ts
2923
+ function requireHostThread(host, ref) {
2924
+ const thread = host.getThread(ref);
2925
+ if (!thread) throw new Error(`Thread not found: ${ref}`);
2926
+ return thread;
2927
+ }
2928
+ async function archiveWorkspaceAgents(host, threadRef) {
2929
+ const thread = requireHostThread(host, threadRef);
2930
+ if (isOrchestratorThread(thread)) {
2931
+ throw new Error("Orchestration chats are agents, not workspaces. Use archive_chat.");
2932
+ }
2933
+ if (!thread.worktreePath?.trim()) {
2934
+ throw new Error("Chat has no workspace checkout. Use archive_chat.");
2935
+ }
2936
+ const chats = threadsSharingWorktree(thread.worktreePath);
2937
+ if (chats.length === 0) {
2938
+ throw new Error(
2939
+ "That workspace is already archived. Use list_archive, then restore_workspace to bring it back."
2940
+ );
2941
+ }
2942
+ const archived = [];
2943
+ for (const chat of chats) {
2944
+ archived.push(await host.archive(chat.id));
2945
+ }
2946
+ return archived;
2947
+ }
2948
+ async function restoreWorkspaceAgents(host, threadRef) {
2949
+ const thread = requireHostThread(host, threadRef);
2950
+ if (isOrchestratorThread(thread)) {
2951
+ throw new Error("Orchestration chats are agents, not workspaces. Use restore_chat.");
2952
+ }
2953
+ if (!thread.worktreePath?.trim()) {
2954
+ throw new Error("Chat has no workspace checkout. Use restore_chat.");
2955
+ }
2956
+ const targets = archivedAgentsOnCheckout(
2957
+ listThreads({ includeArchived: true }),
2958
+ thread.worktreePath
2959
+ );
2960
+ const restored = [];
2961
+ for (const chat of targets) {
2962
+ const latest = readThread(chat.id);
2963
+ if (!latest || latest.status !== "archived") continue;
2964
+ restored.push(await host.restore(latest.id));
2965
+ }
2966
+ if (restored.length === 0) {
2967
+ throw new Error(
2968
+ "No archived agents on this workspace. list_archive shows what can be restored."
2969
+ );
2970
+ }
2971
+ return restored;
2972
+ }
2973
+
2675
2974
  // src/util/line-coalescer.ts
2676
2975
  function createLineCoalescer(onChunk, opts) {
2677
2976
  const delayMs = opts?.delayMs ?? 50;
@@ -3145,7 +3444,7 @@ async function adoptThread(input) {
3145
3444
  messages: input.messages ?? []
3146
3445
  });
3147
3446
  writeThread(thread);
3148
- const { addWorkspace: addWorkspace2 } = await import("./workspaces-PGY4UWZ3.js");
3447
+ const { addWorkspace: addWorkspace2 } = await import("./workspaces-O3L32CDO.js");
3149
3448
  await addWorkspace2(repoPath);
3150
3449
  const { ensureWorktreeSideboardIgnored } = await import("./worktree-exclude-5N2KTYIT.js");
3151
3450
  await ensureWorktreeSideboardIgnored(input.worktreePath);
@@ -5184,6 +5483,16 @@ function formatWorktreeDirective(thread, opts) {
5184
5483
  lines.push(formatProcessGuideDirective({ worktreePath: thread.worktreePath }));
5185
5484
  return lines.join("\n");
5186
5485
  }
5486
+ function formatConnectedOrchestratorReminder(parent) {
5487
+ if (!parent) return null;
5488
+ const title = parent.title?.trim() || "Orchestration";
5489
+ const archived = parent.status === "archived" ? " This orchestrator is archived." : "";
5490
+ return `Connected orchestrator: "${title}" (${parent.agent}, ${parent.status}). Open [${title}](sideboard://chat/${parent.id}). notify_orchestrator wakes that chat. You are not the orchestrator.${archived}`;
5491
+ }
5492
+ function connectedOrchestratorTurnReminder(thread, find) {
5493
+ if (thread.agent === "brightsy" || isOrchestratorThread(thread)) return null;
5494
+ return formatConnectedOrchestratorReminder(connectedOrchestrator(thread, find));
5495
+ }
5187
5496
  function formatWorktreeReminder() {
5188
5497
  return "Sideboard workspace: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push. Schedules: create_schedule (at / every / cron; chatId=self for this chat). set_workspace_tags updates labels on this workspace (omit ref).";
5189
5498
  }
@@ -6020,7 +6329,7 @@ function formatScheduledPrompt(name, prompt) {
6020
6329
  ${prompt}`;
6021
6330
  }
6022
6331
  async function defaultDeps() {
6023
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-MR7TCIPP.js");
6332
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-SX4SGZHF.js");
6024
6333
  const orch = getOrchestrator2();
6025
6334
  return {
6026
6335
  findThread: (id) => findThreadByRef(id),
@@ -7092,6 +7401,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
7092
7401
  );
7093
7402
  const turnReminders = [
7094
7403
  worktreeReminder,
7404
+ connectedOrchestratorTurnReminder(thread, (id) => readThread(id)),
7095
7405
  optionalServicesReminder,
7096
7406
  issueToolsReminder,
7097
7407
  reviewWriteGateReminder,
@@ -8689,14 +8999,16 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
8689
8999
  const thread = this.requireThread(threadRef);
8690
9000
  return threadsSharingWorktree(thread.worktreePath);
8691
9001
  }
8692
- /**
8693
- * Last-tab teardown must see siblings already archived. Serialize per
8694
- * worktree so parallel archive/purge of sibling tabs still removes once.
8695
- */
8696
9002
  enqueueWorktreeTeardown(thread, fn) {
8697
9003
  const key = isGlobalThread(thread) ? `archive:global:${thread.id}` : `archive:wt:${normalizeWorktreePath(thread.worktreePath)}`;
8698
9004
  return enqueueByKey(key, fn);
8699
9005
  }
9006
+ archiveWorkspace(threadRef) {
9007
+ return archiveWorkspaceAgents(this, threadRef);
9008
+ }
9009
+ restoreWorkspace(threadRef) {
9010
+ return restoreWorkspaceAgents(this, threadRef);
9011
+ }
8700
9012
  async archive(threadRef) {
8701
9013
  const thread = this.requireThread(threadRef);
8702
9014
  return this.enqueueWorktreeTeardown(thread, () => this.archiveUnlocked(threadRef));
@@ -8892,7 +9204,7 @@ async function startOrchestration(opts) {
8892
9204
  sourceRef: "default",
8893
9205
  ...createOpts
8894
9206
  }).catch(async () => {
8895
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-JZXDAKQF.js");
9207
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-6G5X4ABY.js");
8896
9208
  const repo = await resolveRepoRoot2(repoPath);
8897
9209
  const def = await resolveDefaultBranch2(repo);
8898
9210
  return orch.createThread({
@@ -8902,7 +9214,7 @@ async function startOrchestration(opts) {
8902
9214
  repoPath: repo
8903
9215
  });
8904
9216
  });
8905
- const { updateThread: upd } = await import("./thread-store-H6D6MDUM.js");
9217
+ const { updateThread: upd } = await import("./thread-store-5XXHSWMN.js");
8906
9218
  const updated = upd(thread.id, {
8907
9219
  sourceType: "orchestration",
8908
9220
  sourceRef: goal
@@ -8920,6 +9232,7 @@ export {
8920
9232
  DEFAULT_RUN_LOG_TAIL_CHARS,
8921
9233
  sliceRunLogOutput,
8922
9234
  setToolPermissionPublisher,
9235
+ connectedOrchestrator,
8923
9236
  resolveNotifyCallerThread,
8924
9237
  notifyOrchestrator,
8925
9238
  clampBlockReason,
@@ -8944,6 +9257,8 @@ export {
8944
9257
  HOME_BOARD_AGENT_HINT,
8945
9258
  workspaceTagTargetRef,
8946
9259
  applyWorkspaceTags,
9260
+ listFleetArchive,
9261
+ formatFleetArchive,
8947
9262
  readTurnLive,
8948
9263
  localhostPreviewUrl,
8949
9264
  parseIssueSince,
@@ -33,10 +33,10 @@ import {
33
33
  tryReservePort,
34
34
  tryReservePorts,
35
35
  worktreeRunPortsPath
36
- } from "./chunk-ZXWRNA4G.js";
37
- import "./chunk-ED4ZXQVO.js";
38
- import "./chunk-WQREAX35.js";
39
- import "./chunk-VXNZG4J6.js";
36
+ } from "./chunk-F3JYJ4UB.js";
37
+ import "./chunk-5XQ43YZU.js";
38
+ import "./chunk-UMALFEDS.js";
39
+ import "./chunk-3IXGI2FP.js";
40
40
  import "./chunk-N2WW62LF.js";
41
41
  import "./chunk-DD67JWTN.js";
42
42
  import "./chunk-2UG25RMK.js";
@@ -47,7 +47,7 @@ import {
47
47
  } from "./chunk-WWUABK6B.js";
48
48
  import "./chunk-FJZWM2ON.js";
49
49
  import "./chunk-UN5Q7POQ.js";
50
- import "./chunk-TVN4TDFB.js";
50
+ import "./chunk-HTL4FBTP.js";
51
51
  import "./chunk-4RZRHTSZ.js";
52
52
  import "./chunk-KGZBYWZ3.js";
53
53
  import {
@@ -31,11 +31,11 @@ import {
31
31
  tryReservePort,
32
32
  tryReservePorts,
33
33
  worktreeRunPortsPath
34
- } from "./chunk-67BWWLEB.js";
35
- import "./chunk-Q3DPZ4DE.js";
36
- import "./chunk-BGEECLKH.js";
37
- import "./chunk-JWXJXEA3.js";
38
- import "./chunk-WZLV4WNS.js";
34
+ } from "./chunk-MMIDAD6J.js";
35
+ import "./chunk-F36QRDM4.js";
36
+ import "./chunk-QB4UQA5L.js";
37
+ import "./chunk-G52HLBQR.js";
38
+ import "./chunk-K5EACGG2.js";
39
39
  import "./chunk-2P4XKWV2.js";
40
40
  import "./chunk-HO5R3ZRH.js";
41
41
  import "./chunk-6K64FJDD.js";