@sideboard-ai/core 0.1.211 → 0.1.213

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 (31) hide show
  1. package/dist/{agents-GQANI7M5.js → agents-HETHRRZJ.js} +4 -4
  2. package/dist/{agents-A2U3K7VK.js → agents-WBDDLRBY.js} +4 -4
  3. package/dist/{chunk-JJ3ONQ4Q.js → chunk-3IMSQNTS.js} +12 -2
  4. package/dist/{chunk-IA5IPGZI.js → chunk-7MTVRNP4.js} +3 -3
  5. package/dist/{chunk-KZUA4QG2.js → chunk-C32SBNA3.js} +198 -97
  6. package/dist/{chunk-ERNK7AWM.js → chunk-D2IKXMF3.js} +196 -96
  7. package/dist/{chunk-IRCAWYTN.js → chunk-DFTVZEZY.js} +3 -3
  8. package/dist/{chunk-2PE6SZCM.js → chunk-FNGXKY5W.js} +2 -2
  9. package/dist/{chunk-GHKFSBES.js → chunk-HY2GFZ5S.js} +12 -2
  10. package/dist/{chunk-WNVQJZ7P.js → chunk-KFD2OXCE.js} +2 -2
  11. package/dist/{chunk-NRKDRQM3.js → chunk-NI243N27.js} +1 -1
  12. package/dist/{chunk-VPQXJYIW.js → chunk-XYKVRRC2.js} +1 -1
  13. package/dist/{coordinator-prompt-LMTMWCPN.js → coordinator-prompt-FQX6ALAI.js} +3 -3
  14. package/dist/{coordinator-prompt-EHRDZ2ZU.js → coordinator-prompt-LE2GQZ2X.js} +3 -3
  15. package/dist/{global-workspace-NG2TBUI5.js → global-workspace-6P236KKG.js} +3 -3
  16. package/dist/{global-workspace-RHABXQWB.js → global-workspace-KT3FRHZL.js} +3 -3
  17. package/dist/index.cjs +210 -88
  18. package/dist/index.d.cts +61 -6
  19. package/dist/index.d.ts +61 -6
  20. package/dist/index.js +11 -5
  21. package/dist/mcp/run-stdio.cjs +204 -88
  22. package/dist/mcp/run-stdio.js +5 -5
  23. package/dist/{orchestrator-NZO4PVFH.js → orchestrator-MI7A5DQZ.js} +7 -5
  24. package/dist/{orchestrator-QMGH4V4K.js → orchestrator-WVZPTHBV.js} +7 -5
  25. package/dist/{thread-store-MSMBQ3V4.js → thread-store-KDPPXJJ3.js} +3 -1
  26. package/dist/{thread-store-4M6WETL6.js → thread-store-N6WQIWZ4.js} +3 -1
  27. package/dist/{workspaces-VIUUE4RT.js → workspaces-KM7KTLVO.js} +3 -3
  28. package/dist/{workspaces-FRR6Y4BY.js → workspaces-R5LJCJRB.js} +3 -3
  29. package/dist/{worktree-GKUOHXXB.js → worktree-MDMTCAHX.js} +2 -2
  30. package/dist/{worktree-3MEG62KB.js → worktree-VXRI7LYM.js} +2 -2
  31. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -3079,6 +3079,7 @@ __export(thread_store_exports, {
3079
3079
  findThreadByRef: () => findThreadByRef,
3080
3080
  invalidateThreadListCache: () => invalidateThreadListCache,
3081
3081
  invalidateThreadRecord: () => invalidateThreadRecord,
3082
+ isSelfWrittenRecord: () => isSelfWrittenRecord,
3082
3083
  isThreadRecordFile: () => isThreadRecordFile,
3083
3084
  listThreads: () => listThreads,
3084
3085
  normalizeThread: () => normalizeThread,
@@ -3208,6 +3209,7 @@ function forgetThread(id) {
3208
3209
  const cache2 = cacheForDir();
3209
3210
  cache2.byId.delete(id);
3210
3211
  cache2.mtimeMs.delete(id);
3212
+ selfWriteMtimes.delete(id);
3211
3213
  }
3212
3214
  function invalidateThreadListCache() {
3213
3215
  listCache = null;
@@ -3237,14 +3239,21 @@ function writeThread(thread, opts) {
3237
3239
  const next = opts?.touch === false ? { ...thread } : { ...thread, updatedAt: nowIso() };
3238
3240
  (0, import_node_fs9.writeFileSync)(tmp, JSON.stringify(next, null, 2), "utf8");
3239
3241
  (0, import_node_fs9.renameSync)(tmp, path2);
3240
- rememberThread(next, fileMtimeMs(path2) ?? Date.now());
3242
+ const mtimeMs = fileMtimeMs(path2) ?? Date.now();
3243
+ rememberThread(next, mtimeMs);
3244
+ selfWriteMtimes.set(thread.id, mtimeMs);
3245
+ }
3246
+ function isSelfWrittenRecord(id) {
3247
+ const ours = selfWriteMtimes.get(id);
3248
+ if (ours == null) return false;
3249
+ return fileMtimeMs(threadFilePath(id)) === ours;
3241
3250
  }
3242
3251
  function idPath(id) {
3243
3252
  return id;
3244
3253
  }
3245
3254
  function isThreadRecordFile(nameOrPath) {
3246
3255
  const name = (0, import_node_path11.basename)(nameOrPath);
3247
- return name.endsWith(".json") && !name.endsWith(".live.json");
3256
+ return name.endsWith(".json") && !name.endsWith(".live.json") && !name.endsWith(".setup.log.json");
3248
3257
  }
3249
3258
  function listThreads(opts) {
3250
3259
  const cache2 = cacheForDir();
@@ -3310,7 +3319,7 @@ function findThreadByRef(ref) {
3310
3319
  (t) => t.id === ref || t.id.startsWith(ref) || t.branchName === ref || t.title === ref
3311
3320
  ) ?? null;
3312
3321
  }
3313
- var import_node_crypto3, import_node_fs9, import_node_path11, import_proper_lockfile, listCache;
3322
+ var import_node_crypto3, import_node_fs9, import_node_path11, import_proper_lockfile, listCache, selfWriteMtimes;
3314
3323
  var init_thread_store = __esm({
3315
3324
  "src/store/thread-store.ts"() {
3316
3325
  "use strict";
@@ -3322,6 +3331,7 @@ var init_thread_store = __esm({
3322
3331
  init_cursor_store();
3323
3332
  init_paths();
3324
3333
  listCache = null;
3334
+ selfWriteMtimes = /* @__PURE__ */ new Map();
3325
3335
  }
3326
3336
  });
3327
3337
 
@@ -5756,6 +5766,29 @@ ${line}` : line;
5756
5766
  const nl = cut.indexOf("\n");
5757
5767
  return nl >= 0 && nl < cut.length - 1 ? cut.slice(nl + 1) : cut;
5758
5768
  }
5769
+ function fromSnapshot(snap) {
5770
+ return {
5771
+ chunks: snap.output ? [snap.output] : [],
5772
+ length: snap.output.length,
5773
+ running: snap.running,
5774
+ exitCode: snap.exitCode,
5775
+ source: snap.source
5776
+ };
5777
+ }
5778
+ function materialize(mem) {
5779
+ let output = mem.chunks.join("\n");
5780
+ if (output.length > MAX_SETUP_LOG_CHARS) {
5781
+ output = appendSetupOutput("", output);
5782
+ mem.chunks = [output];
5783
+ mem.length = output.length;
5784
+ }
5785
+ return {
5786
+ output,
5787
+ running: mem.running,
5788
+ exitCode: mem.exitCode,
5789
+ source: mem.source
5790
+ };
5791
+ }
5759
5792
  function setupLogPath(threadId) {
5760
5793
  return (0, import_node_path12.join)(threadsDir(), `${threadId}.setup.log.json`);
5761
5794
  }
@@ -5774,20 +5807,21 @@ function mergeSetupOutput(prev, incoming) {
5774
5807
  if (incoming.startsWith(prev) || incoming.endsWith(prev)) return incoming;
5775
5808
  return incoming.length >= prev.length ? incoming : prev;
5776
5809
  }
5777
- function clone(snap) {
5778
- return { ...snap };
5779
- }
5780
5810
  function persistNow(threadId) {
5781
5811
  const timer = persistTimers.get(threadId);
5782
5812
  if (timer) {
5783
5813
  clearTimeout(timer);
5784
5814
  persistTimers.delete(threadId);
5785
5815
  }
5786
- const snap = memory.get(threadId);
5787
- if (!snap) return;
5816
+ const mem = memory.get(threadId);
5817
+ if (!mem) return;
5788
5818
  try {
5789
- (0, import_node_fs10.writeFileSync)(setupLogPath(threadId), `${JSON.stringify(snap)}
5790
- `, "utf8");
5819
+ (0, import_node_fs10.writeFileSync)(
5820
+ setupLogPath(threadId),
5821
+ `${JSON.stringify(materialize(mem))}
5822
+ `,
5823
+ "utf8"
5824
+ );
5791
5825
  } catch {
5792
5826
  }
5793
5827
  }
@@ -5798,7 +5832,7 @@ function schedulePersist(threadId) {
5798
5832
  setTimeout(() => {
5799
5833
  persistTimers.delete(threadId);
5800
5834
  persistNow(threadId);
5801
- }, 100)
5835
+ }, PERSIST_DEBOUNCE_MS)
5802
5836
  );
5803
5837
  }
5804
5838
  function loadFromDisk(threadId) {
@@ -5816,51 +5850,52 @@ function loadFromDisk(threadId) {
5816
5850
  return null;
5817
5851
  }
5818
5852
  }
5853
+ function memFor(threadId) {
5854
+ const cached = memory.get(threadId);
5855
+ if (cached) return cached;
5856
+ const mem = fromSnapshot(loadFromDisk(threadId) ?? emptySetupLog());
5857
+ memory.set(threadId, mem);
5858
+ return mem;
5859
+ }
5819
5860
  function readSetupLog(threadId) {
5820
5861
  const cached = memory.get(threadId);
5821
- if (cached) return clone(cached);
5862
+ if (cached) return materialize(cached);
5822
5863
  const disk = loadFromDisk(threadId);
5823
5864
  if (disk) {
5824
- memory.set(threadId, disk);
5825
- return clone(disk);
5865
+ memory.set(threadId, fromSnapshot(disk));
5866
+ return disk;
5826
5867
  }
5827
5868
  return emptySetupLog();
5828
5869
  }
5829
5870
  function beginSetupLog(threadId) {
5830
- const snap = {
5831
- output: "",
5871
+ const mem = {
5872
+ chunks: [],
5873
+ length: 0,
5832
5874
  running: true,
5833
5875
  exitCode: null,
5834
5876
  source: null
5835
5877
  };
5836
- memory.set(threadId, snap);
5878
+ memory.set(threadId, mem);
5837
5879
  persistNow(threadId);
5838
- return clone(snap);
5880
+ return materialize(mem);
5839
5881
  }
5840
5882
  function appendSetupLog(threadId, line) {
5841
- const current = memory.get(threadId) ?? readSetupLog(threadId);
5842
- const snap = {
5843
- ...current,
5844
- output: appendSetupOutput(current.output, line),
5845
- running: true
5846
- };
5847
- memory.set(threadId, snap);
5883
+ const mem = memFor(threadId);
5884
+ mem.chunks.push(line);
5885
+ mem.length += line.length + 1;
5886
+ mem.running = true;
5887
+ if (mem.length > MAX_SETUP_LOG_CHARS * 2) materialize(mem);
5848
5888
  schedulePersist(threadId);
5849
- return clone(snap);
5850
5889
  }
5851
5890
  function finishSetupLog(threadId, exitCode, source) {
5852
- const current = memory.get(threadId) ?? readSetupLog(threadId);
5853
- const snap = {
5854
- ...current,
5855
- running: false,
5856
- exitCode,
5857
- source: source ?? current.source
5858
- };
5859
- memory.set(threadId, snap);
5891
+ const mem = memFor(threadId);
5892
+ mem.running = false;
5893
+ mem.exitCode = exitCode;
5894
+ mem.source = source ?? mem.source;
5860
5895
  persistNow(threadId);
5861
- return clone(snap);
5896
+ return materialize(mem);
5862
5897
  }
5863
- var import_node_crypto4, import_node_fs10, import_node_path12, EMPTY, MAX_SETUP_LOG_CHARS, memory, persistTimers;
5898
+ var import_node_crypto4, import_node_fs10, import_node_path12, EMPTY, MAX_SETUP_LOG_CHARS, memory, persistTimers, PERSIST_DEBOUNCE_MS;
5864
5899
  var init_setup_log = __esm({
5865
5900
  "src/store/setup-log.ts"() {
5866
5901
  "use strict";
@@ -5878,6 +5913,7 @@ var init_setup_log = __esm({
5878
5913
  MAX_SETUP_LOG_CHARS = 256e3;
5879
5914
  memory = /* @__PURE__ */ new Map();
5880
5915
  persistTimers = /* @__PURE__ */ new Map();
5916
+ PERSIST_DEBOUNCE_MS = 500;
5881
5917
  }
5882
5918
  });
5883
5919
 
@@ -19630,6 +19666,38 @@ var init_cowboy = __esm({
19630
19666
  }
19631
19667
  });
19632
19668
 
19669
+ // src/util/line-coalescer.ts
19670
+ function createLineCoalescer(onChunk, opts) {
19671
+ const delayMs = opts?.delayMs ?? 50;
19672
+ const maxLines = opts?.maxLines ?? 200;
19673
+ let buffer = [];
19674
+ let timer = null;
19675
+ const flush2 = () => {
19676
+ if (timer) {
19677
+ clearTimeout(timer);
19678
+ timer = null;
19679
+ }
19680
+ if (buffer.length === 0) return;
19681
+ const chunk = buffer.join("\n");
19682
+ buffer = [];
19683
+ onChunk(chunk);
19684
+ };
19685
+ const push = (line) => {
19686
+ buffer.push(line);
19687
+ if (buffer.length >= maxLines) {
19688
+ flush2();
19689
+ return;
19690
+ }
19691
+ if (!timer) timer = setTimeout(flush2, delayMs);
19692
+ };
19693
+ return { push, flush: flush2 };
19694
+ }
19695
+ var init_line_coalescer = __esm({
19696
+ "src/util/line-coalescer.ts"() {
19697
+ "use strict";
19698
+ }
19699
+ });
19700
+
19633
19701
  // src/store/turn-live.ts
19634
19702
  function trimParts(parts) {
19635
19703
  if (parts.length <= MAX_PARTS) return parts;
@@ -21295,6 +21363,29 @@ function writeWorktreeFile(worktreePath, relativePath, content) {
21295
21363
  (0, import_node_fs48.writeFileSync)(abs, content, "utf8");
21296
21364
  return { path: relativePath };
21297
21365
  }
21366
+ async function getWorktreeDirtyStat(worktreePath) {
21367
+ const status = await inspectGitWorktree(worktreePath);
21368
+ if (status !== "ok") {
21369
+ return { additions: 0, deletions: 0, dirty: false };
21370
+ }
21371
+ const head = await git(["rev-parse", "--verify", "HEAD"], worktreePath, {
21372
+ reject: false,
21373
+ timeoutMs: 4e3
21374
+ });
21375
+ if (head.exitCode !== 0) {
21376
+ return { additions: 0, deletions: 0, dirty: await isDirty(worktreePath) };
21377
+ }
21378
+ const [num2, dirty] = await Promise.all([
21379
+ gitDiff(["--numstat", "HEAD"], worktreePath, { timeoutMs: 4e3 }),
21380
+ isDirty(worktreePath)
21381
+ ]);
21382
+ const counts = statFromNumstat(num2.stdout);
21383
+ return {
21384
+ additions: counts.additions,
21385
+ deletions: counts.deletions,
21386
+ dirty: dirty || counts.additions > 0 || counts.deletions > 0
21387
+ };
21388
+ }
21298
21389
  async function getDiffSummary(worktreePath, repoPath, opts) {
21299
21390
  const full = await getDiff(worktreePath, repoPath, {
21300
21391
  maxHunkChars: opts?.maxHunkChars ?? 2e3,
@@ -21901,9 +21992,7 @@ function consumeComposerAttachments(thread) {
21901
21992
  };
21902
21993
  }
21903
21994
  function takePendingTurnAttachments(thread) {
21904
- const pending = thread.pendingTurnAttachments ?? [];
21905
- if (pending.length > 0) return [...pending];
21906
- return thread.attachments.length > 0 ? [...thread.attachments] : [];
21995
+ return [...thread.pendingTurnAttachments ?? []];
21907
21996
  }
21908
21997
  function alignQueueAttachments(queueLength, existing) {
21909
21998
  const next = (existing ?? []).slice(0, queueLength).map((slot) => [...slot ?? []]);
@@ -22767,6 +22856,7 @@ var init_title = __esm({
22767
22856
  // src/orchestrator/orchestrator.ts
22768
22857
  var orchestrator_exports = {};
22769
22858
  __export(orchestrator_exports, {
22859
+ LIGHT_RECONCILE_THROTTLE_MS: () => LIGHT_RECONCILE_THROTTLE_MS,
22770
22860
  LIVE_TURN_SPAWN_GRACE_MS: () => LIVE_TURN_SPAWN_GRACE_MS,
22771
22861
  Orchestrator: () => Orchestrator,
22772
22862
  getOrchestrator: () => getOrchestrator,
@@ -22870,7 +22960,7 @@ async function startOrchestration(opts) {
22870
22960
  }
22871
22961
  return updated;
22872
22962
  }
22873
- var import_node_events, import_node_fs54, LIVE_TURN_SPAWN_GRACE_MS, STALE_AGENT_PID_WAIT_MS, Orchestrator, singleton;
22963
+ var import_node_events, import_node_fs54, LIVE_TURN_SPAWN_GRACE_MS, STALE_AGENT_PID_WAIT_MS, LIGHT_RECONCILE_THROTTLE_MS, Orchestrator, singleton;
22874
22964
  var init_orchestrator = __esm({
22875
22965
  "src/orchestrator/orchestrator.ts"() {
22876
22966
  "use strict";
@@ -22904,6 +22994,7 @@ var init_orchestrator = __esm({
22904
22994
  init_orchestrator_capable();
22905
22995
  init_chat_tabs();
22906
22996
  init_enqueue_by_key();
22997
+ init_line_coalescer();
22907
22998
  init_run();
22908
22999
  init_repo_git_lock();
22909
23000
  init_turn_live();
@@ -22937,6 +23028,7 @@ var init_orchestrator = __esm({
22937
23028
  init_coordinator_prompt();
22938
23029
  LIVE_TURN_SPAWN_GRACE_MS = 15e3;
22939
23030
  STALE_AGENT_PID_WAIT_MS = 2500;
23031
+ LIGHT_RECONCILE_THROTTLE_MS = 1e4;
22940
23032
  Orchestrator = class {
22941
23033
  events = new import_node_events.EventEmitter();
22942
23034
  processes = /* @__PURE__ */ new Map();
@@ -22946,6 +23038,8 @@ var init_orchestrator = __esm({
22946
23038
  startingTurns = /* @__PURE__ */ new Set();
22947
23039
  /** Last sync thread-file read for false-stop heal (throttled per thread). */
22948
23040
  lastReconcileHealAt = /* @__PURE__ */ new Map();
23041
+ /** Per-repo timestamp of the last `reconcile(repo, { light: true })`. */
23042
+ lightReconcileAt = /* @__PURE__ */ new Map();
22949
23043
  /**
22950
23044
  * Threads intentionally force-stopped. Prevents runTurn from re-asserting
22951
23045
  * `running` after spawn, and from overwriting `stopped` with idle/error when
@@ -23031,6 +23125,20 @@ var init_orchestrator = __esm({
23031
23125
  async reconcile(repoPath, opts) {
23032
23126
  const reclaimStaleTurns = opts?.reclaimStaleTurns === true;
23033
23127
  const drainQueues = opts?.drainQueues !== false;
23128
+ if (opts?.light && repoPath) {
23129
+ const last = this.lightReconcileAt.get(repoPath) ?? 0;
23130
+ if (Date.now() - last < LIGHT_RECONCILE_THROTTLE_MS) return;
23131
+ this.lightReconcileAt.set(repoPath, Date.now());
23132
+ for (const thread of listThreads()) {
23133
+ if (isGlobalThread(thread) || thread.repoPath !== repoPath) continue;
23134
+ if (!(0, import_node_fs54.existsSync)(thread.worktreePath)) {
23135
+ setStatus(thread.id, "broken", "Worktree missing on disk");
23136
+ this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
23137
+ }
23138
+ }
23139
+ await this.reconcileOrphans([repoPath]);
23140
+ return;
23141
+ }
23034
23142
  healOrchestrationSoccerTitles();
23035
23143
  for (const thread of listThreads({ includeArchived: true })) {
23036
23144
  if (thread.status === "archived") continue;
@@ -23063,6 +23171,21 @@ var init_orchestrator = __esm({
23063
23171
  }
23064
23172
  }
23065
23173
  const repoPaths = (repoPath ? [repoPath] : [...new Set(listThreads({ includeArchived: true }).map((t) => t.repoPath))]).filter((p) => !isGlobalRepoPath(p));
23174
+ await this.reconcileOrphans(repoPaths);
23175
+ try {
23176
+ if (shouldRunHistoryCleanup()) {
23177
+ cleanupArchivedHistory();
23178
+ }
23179
+ } catch {
23180
+ }
23181
+ if (drainQueues) {
23182
+ this.adoptPersistedQueues();
23183
+ }
23184
+ this.schedulePendingQuotaResumes();
23185
+ armSchedules();
23186
+ }
23187
+ /** Surface (and, when enabled, clean up) worktrees git knows about but no thread owns. */
23188
+ async reconcileOrphans(repoPaths) {
23066
23189
  try {
23067
23190
  const orphans = await findOrphanWorktrees(repoPaths);
23068
23191
  if (orphans.length) {
@@ -23077,17 +23200,6 @@ var init_orchestrator = __esm({
23077
23200
  }
23078
23201
  } catch {
23079
23202
  }
23080
- try {
23081
- if (shouldRunHistoryCleanup()) {
23082
- cleanupArchivedHistory();
23083
- }
23084
- } catch {
23085
- }
23086
- if (drainQueues) {
23087
- this.adoptPersistedQueues();
23088
- }
23089
- this.schedulePendingQuotaResumes();
23090
- armSchedules();
23091
23203
  }
23092
23204
  /**
23093
23205
  * Adopt queues persisted by another process (MCP stdio / CLI) into this
@@ -23321,7 +23433,7 @@ var init_orchestrator = __esm({
23321
23433
  return listThreads({ includeArchived });
23322
23434
  }
23323
23435
  getThread(idOrRef) {
23324
- return findThreadByRef(idOrRef) ?? readThread(idOrRef);
23436
+ return readThread(idOrRef) ?? findThreadByRef(idOrRef);
23325
23437
  }
23326
23438
  async createThread(input) {
23327
23439
  const prior = new Set(
@@ -23492,9 +23604,11 @@ var init_orchestrator = __esm({
23492
23604
  const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id);
23493
23605
  updateThread(thread.id, {
23494
23606
  ...removed,
23495
- // Old sends parked on pendingTurnAttachments; drop that bag when the
23496
- // follow-up that owned it is gone so a later turn cannot inherit it.
23497
- ...stillQueued ? {} : { pendingTurnAttachments: [] },
23607
+ // Follow-up files live on queueAttachments. pendingTurnAttachments is
23608
+ // the in-flight / just-dequeued snapshot — do not wipe it when a
23609
+ // later follow-up is removed. Only drop a leftover bag when nothing
23610
+ // is running and the queue is empty (legacy sends that never drained).
23611
+ ...stillQueued || inFlight ? {} : { pendingTurnAttachments: [] },
23498
23612
  status: !stillQueued && !inFlight && current.status === "queued" ? "idle" : current.status
23499
23613
  });
23500
23614
  this.emit({ type: "queue_changed", threadId: thread.id, queue: removed.queue });
@@ -23576,7 +23690,9 @@ var init_orchestrator = __esm({
23576
23690
  updateThread(threadId, {
23577
23691
  queue: shifted.queue,
23578
23692
  queueAttachments: shifted.queueAttachments,
23579
- ...shifted.attachments.length > 0 ? { pendingTurnAttachments: shifted.attachments } : {}
23693
+ // Always write the snapshot, including [] for text-only, so
23694
+ // takePending cannot fall back to leftover pending or composer files.
23695
+ pendingTurnAttachments: shifted.attachments
23580
23696
  });
23581
23697
  this.emit({ type: "queue_changed", threadId, queue: shifted.queue });
23582
23698
  await this.runTurn(threadId, shifted.prompt);
@@ -23624,7 +23740,6 @@ var init_orchestrator = __esm({
23624
23740
  thread = this.requireThread(threadId);
23625
23741
  }
23626
23742
  const autoContinue = isJobContinuePrompt(prompt) || prompt.trim().startsWith("The previous agent process ended before it finished.");
23627
- const parkedSnapshot = thread.pendingTurnAttachments ?? [];
23628
23743
  const parked = takePendingTurnAttachments(thread);
23629
23744
  const sentAttachments = !autoContinue && parked.length > 0 ? parked : void 0;
23630
23745
  appendMessage(threadId, {
@@ -23640,7 +23755,7 @@ var init_orchestrator = __esm({
23640
23755
  thread.worktreePath,
23641
23756
  gitPrompt,
23642
23757
  {
23643
- attachments: autoContinue ? [] : sentAttachments ?? thread.attachments
23758
+ attachments: autoContinue ? [] : sentAttachments ?? []
23644
23759
  }
23645
23760
  );
23646
23761
  const slackInbound = /^(Slack DM|Slack @mention)(?:\n|$)/.test(
@@ -23694,10 +23809,7 @@ var init_orchestrator = __esm({
23694
23809
  expandedPrompt
23695
23810
  ].filter(Boolean).join("\n\n");
23696
23811
  if (!autoContinue && sentAttachments) {
23697
- updateThread(
23698
- threadId,
23699
- parkedSnapshot.length > 0 ? { pendingTurnAttachments: [] } : { attachments: [], pendingTurnAttachments: [] }
23700
- );
23812
+ updateThread(threadId, { pendingTurnAttachments: [] });
23701
23813
  }
23702
23814
  const adapter = getAdapter(thread.agent);
23703
23815
  const resolved = await adapter.resolveSessionId(thread.worktreePath, thread.sessionId);
@@ -24177,17 +24289,18 @@ var init_orchestrator = __esm({
24177
24289
  }
24178
24290
  }
24179
24291
  }
24292
+ const runOutput = createLineCoalescer((chunk) => {
24293
+ this.emit({
24294
+ type: "run_output",
24295
+ threadId: thread.id,
24296
+ scriptName: resolvedName,
24297
+ line: chunk
24298
+ });
24299
+ });
24180
24300
  const handle = await startDevServer(
24181
24301
  thread.repoPath,
24182
24302
  thread.worktreePath,
24183
- (line) => {
24184
- this.emit({
24185
- type: "run_output",
24186
- threadId: thread.id,
24187
- scriptName: resolvedName,
24188
- line
24189
- });
24190
- },
24303
+ runOutput.push,
24191
24304
  { scriptName: resolvedName }
24192
24305
  );
24193
24306
  if (!handle) {
@@ -24233,6 +24346,7 @@ var init_orchestrator = __esm({
24233
24346
  scriptName: resolvedName
24234
24347
  });
24235
24348
  void handle.done.then(() => {
24349
+ runOutput.flush();
24236
24350
  this.processes.delete(runKey);
24237
24351
  if (isDefault) this.processes.delete(worktreeDevProcessKey(thread.worktreePath));
24238
24352
  const latest = mergeWorktreeActiveRuns(threadsSharingWorktree(thread.worktreePath));
@@ -24318,9 +24432,14 @@ var init_orchestrator = __esm({
24318
24432
  (t) => this.processes.has(`${t.id}:setup`)
24319
24433
  );
24320
24434
  }
24321
- writeSetupLog(threadId, worktreePath, fn) {
24435
+ /**
24436
+ * The worktree-keyed log is canonical (`resolveWorktreeSetupLog` prefers it
24437
+ * whenever it has output/running/exitCode); per-chat keys only exist for
24438
+ * records that predate it. Writing one key halves the sync persists during
24439
+ * `pnpm install`.
24440
+ */
24441
+ writeSetupLog(worktreePath, fn) {
24322
24442
  fn(setupLogKeyForWorktree(worktreePath));
24323
- fn(threadId);
24324
24443
  }
24325
24444
  async runSetup(threadRef, opts) {
24326
24445
  const thread = this.requireThread(threadRef);
@@ -24335,14 +24454,18 @@ var init_orchestrator = __esm({
24335
24454
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
24336
24455
  kill: () => abort.abort()
24337
24456
  });
24338
- this.writeSetupLog(thread.id, thread.worktreePath, beginSetupLog);
24457
+ this.writeSetupLog(thread.worktreePath, beginSetupLog);
24339
24458
  this.emit({ type: "setup_started", threadId: thread.id });
24459
+ const output = createLineCoalescer((chunk) => {
24460
+ this.writeSetupLog(thread.worktreePath, (logKey) => appendSetupLog(logKey, chunk));
24461
+ this.emit({ type: "setup_output", threadId: thread.id, line: chunk });
24462
+ });
24340
24463
  let finished = false;
24341
24464
  const finish = (exitCode, source) => {
24342
24465
  if (finished) return;
24343
24466
  finished = true;
24467
+ output.flush();
24344
24468
  this.writeSetupLog(
24345
- thread.id,
24346
24469
  thread.worktreePath,
24347
24470
  (logKey) => finishSetupLog(logKey, exitCode, source)
24348
24471
  );
@@ -24352,24 +24475,12 @@ var init_orchestrator = __esm({
24352
24475
  const setup = await runWorkspaceSetup(
24353
24476
  thread.repoPath,
24354
24477
  thread.worktreePath,
24355
- (line) => {
24356
- this.writeSetupLog(
24357
- thread.id,
24358
- thread.worktreePath,
24359
- (logKey) => appendSetupLog(logKey, line)
24360
- );
24361
- this.emit({ type: "setup_output", threadId: thread.id, line });
24362
- },
24478
+ output.push,
24363
24479
  { signal: abort.signal }
24364
24480
  );
24365
24481
  if (!setup.ran) {
24366
24482
  const line = "No setup script in .sideboard/settings.toml, .conductor/settings.toml, .cursor/worktrees.json, or script/setup (bin/setup, scripts/setup)";
24367
- this.writeSetupLog(
24368
- thread.id,
24369
- thread.worktreePath,
24370
- (logKey) => appendSetupLog(logKey, line)
24371
- );
24372
- this.emit({ type: "setup_output", threadId: thread.id, line });
24483
+ output.push(line);
24373
24484
  finish(null, null);
24374
24485
  throw new Error(line);
24375
24486
  }
@@ -24575,6 +24686,11 @@ var init_orchestrator = __esm({
24575
24686
  lastTurnBase: this.turnBaselines.get(thread.id) ?? null
24576
24687
  });
24577
24688
  }
24689
+ async worktreeDirtyStat(threadRef) {
24690
+ const thread = this.requireThread(threadRef);
24691
+ this.assertNotGlobal(thread, "Diff");
24692
+ return getWorktreeDirtyStat(thread.worktreePath);
24693
+ }
24578
24694
  async diffSummary(threadRef) {
24579
24695
  const thread = this.requireThread(threadRef);
24580
24696
  this.assertNotGlobal(thread, "Diff");
@@ -25334,6 +25450,7 @@ __export(index_exports, {
25334
25450
  LEGACY_DETACHED_JOBS_DIR: () => LEGACY_DETACHED_JOBS_DIR,
25335
25451
  LEGACY_PLAN_FILE_REL: () => LEGACY_PLAN_FILE_REL,
25336
25452
  LEGACY_REVIEW_REQUEST_PATH: () => LEGACY_REVIEW_REQUEST_PATH,
25453
+ LIGHT_RECONCILE_THROTTLE_MS: () => LIGHT_RECONCILE_THROTTLE_MS,
25337
25454
  LINEAR_OAUTH_CANCELLED: () => LINEAR_OAUTH_CANCELLED,
25338
25455
  LINEAR_OAUTH_PORT: () => LINEAR_OAUTH_PORT,
25339
25456
  LINEAR_OAUTH_REDIRECT: () => LINEAR_OAUTH_REDIRECT,
@@ -25691,6 +25808,7 @@ __export(index_exports, {
25691
25808
  getRunScript: () => getRunScript,
25692
25809
  getSchedule: () => getSchedule,
25693
25810
  getSlackWorkspace: () => getSlackWorkspace,
25811
+ getWorktreeDirtyStat: () => getWorktreeDirtyStat,
25694
25812
  gh: () => gh,
25695
25813
  ghHeadRef: () => ghHeadRef,
25696
25814
  ghRepoSelectArgs: () => ghRepoSelectArgs,
@@ -25768,6 +25886,7 @@ __export(index_exports, {
25768
25886
  isPrNotMergeableError: () => isPrNotMergeableError,
25769
25887
  isPresentPlanToolName: () => isPresentPlanToolName,
25770
25888
  isPrimaryCheckoutThread: () => isPrimaryCheckoutThread,
25889
+ isSelfWrittenRecord: () => isSelfWrittenRecord,
25771
25890
  isSessionQuotaLimit: () => isSessionQuotaLimit,
25772
25891
  isSetupLastError: () => isSetupLastError,
25773
25892
  isShellToolName: () => isShellToolName,
@@ -34120,6 +34239,7 @@ init_outbound_watch();
34120
34239
  LEGACY_DETACHED_JOBS_DIR,
34121
34240
  LEGACY_PLAN_FILE_REL,
34122
34241
  LEGACY_REVIEW_REQUEST_PATH,
34242
+ LIGHT_RECONCILE_THROTTLE_MS,
34123
34243
  LINEAR_OAUTH_CANCELLED,
34124
34244
  LINEAR_OAUTH_PORT,
34125
34245
  LINEAR_OAUTH_REDIRECT,
@@ -34477,6 +34597,7 @@ init_outbound_watch();
34477
34597
  getRunScript,
34478
34598
  getSchedule,
34479
34599
  getSlackWorkspace,
34600
+ getWorktreeDirtyStat,
34480
34601
  gh,
34481
34602
  ghHeadRef,
34482
34603
  ghRepoSelectArgs,
@@ -34554,6 +34675,7 @@ init_outbound_watch();
34554
34675
  isPrNotMergeableError,
34555
34676
  isPresentPlanToolName,
34556
34677
  isPrimaryCheckoutThread,
34678
+ isSelfWrittenRecord,
34557
34679
  isSessionQuotaLimit,
34558
34680
  isSetupLastError,
34559
34681
  isShellToolName,