@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
@@ -42,7 +42,7 @@ import {
42
42
  summarizeTurnStderr,
43
43
  toolDescription,
44
44
  turnFailChatText
45
- } from "./chunk-IA5IPGZI.js";
45
+ } from "./chunk-7MTVRNP4.js";
46
46
  import {
47
47
  SLACK_REPLY_FORMATTING,
48
48
  addWorkspace,
@@ -65,7 +65,7 @@ import {
65
65
  stageAbsolutePathsAsAttachments,
66
66
  stageBuffersAsAttachments,
67
67
  syncWorkspacesFromThreads
68
- } from "./chunk-WNVQJZ7P.js";
68
+ } from "./chunk-KFD2OXCE.js";
69
69
  import {
70
70
  addPrStackLayer,
71
71
  allocateTeamName,
@@ -115,7 +115,7 @@ import {
115
115
  worktreeDisplayLabelForGroup,
116
116
  worktreeNameFromPath,
117
117
  worktreeSlugForTicket
118
- } from "./chunk-VPQXJYIW.js";
118
+ } from "./chunk-XYKVRRC2.js";
119
119
  import {
120
120
  ATTACHMENTS_DIR,
121
121
  DETACHED_JOBS_DIR,
@@ -133,7 +133,7 @@ import {
133
133
  updateThread,
134
134
  withThreadLock,
135
135
  writeThread
136
- } from "./chunk-GHKFSBES.js";
136
+ } from "./chunk-HY2GFZ5S.js";
137
137
  import {
138
138
  ensureWorktreeSideboardIgnored
139
139
  } from "./chunk-SYFVUF2C.js";
@@ -417,7 +417,7 @@ async function continueSourceThread(threadId, prompt) {
417
417
  await continueOnReply(threadId, prompt);
418
418
  return;
419
419
  }
420
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-NZO4PVFH.js");
420
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-MI7A5DQZ.js");
421
421
  await getOrchestrator2().send(threadId, prompt);
422
422
  } catch {
423
423
  }
@@ -810,9 +810,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
810
810
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
811
811
  );
812
812
  }
813
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-RHABXQWB.js");
813
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-KT3FRHZL.js");
814
814
  if (isGlobalThread2(thread)) {
815
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-EHRDZ2ZU.js");
815
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-LE2GQZ2X.js");
816
816
  ensureGlobalCoordinatorCwd2(
817
817
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
818
818
  );
@@ -2317,6 +2317,29 @@ ${line}` : line;
2317
2317
  }
2318
2318
  var memory = /* @__PURE__ */ new Map();
2319
2319
  var persistTimers = /* @__PURE__ */ new Map();
2320
+ function fromSnapshot(snap) {
2321
+ return {
2322
+ chunks: snap.output ? [snap.output] : [],
2323
+ length: snap.output.length,
2324
+ running: snap.running,
2325
+ exitCode: snap.exitCode,
2326
+ source: snap.source
2327
+ };
2328
+ }
2329
+ function materialize(mem) {
2330
+ let output = mem.chunks.join("\n");
2331
+ if (output.length > MAX_SETUP_LOG_CHARS) {
2332
+ output = appendSetupOutput("", output);
2333
+ mem.chunks = [output];
2334
+ mem.length = output.length;
2335
+ }
2336
+ return {
2337
+ output,
2338
+ running: mem.running,
2339
+ exitCode: mem.exitCode,
2340
+ source: mem.source
2341
+ };
2342
+ }
2320
2343
  function setupLogPath(threadId) {
2321
2344
  return join7(threadsDir(), `${threadId}.setup.log.json`);
2322
2345
  }
@@ -2327,20 +2350,22 @@ function setupLogKeyForWorktree(worktreePath) {
2327
2350
  function emptySetupLog() {
2328
2351
  return { ...EMPTY };
2329
2352
  }
2330
- function clone(snap) {
2331
- return { ...snap };
2332
- }
2353
+ var PERSIST_DEBOUNCE_MS = 500;
2333
2354
  function persistNow(threadId) {
2334
2355
  const timer = persistTimers.get(threadId);
2335
2356
  if (timer) {
2336
2357
  clearTimeout(timer);
2337
2358
  persistTimers.delete(threadId);
2338
2359
  }
2339
- const snap = memory.get(threadId);
2340
- if (!snap) return;
2360
+ const mem = memory.get(threadId);
2361
+ if (!mem) return;
2341
2362
  try {
2342
- writeFileSync2(setupLogPath(threadId), `${JSON.stringify(snap)}
2343
- `, "utf8");
2363
+ writeFileSync2(
2364
+ setupLogPath(threadId),
2365
+ `${JSON.stringify(materialize(mem))}
2366
+ `,
2367
+ "utf8"
2368
+ );
2344
2369
  } catch {
2345
2370
  }
2346
2371
  }
@@ -2351,7 +2376,7 @@ function schedulePersist(threadId) {
2351
2376
  setTimeout(() => {
2352
2377
  persistTimers.delete(threadId);
2353
2378
  persistNow(threadId);
2354
- }, 100)
2379
+ }, PERSIST_DEBOUNCE_MS)
2355
2380
  );
2356
2381
  }
2357
2382
  function loadFromDisk(threadId) {
@@ -2369,49 +2394,50 @@ function loadFromDisk(threadId) {
2369
2394
  return null;
2370
2395
  }
2371
2396
  }
2397
+ function memFor(threadId) {
2398
+ const cached = memory.get(threadId);
2399
+ if (cached) return cached;
2400
+ const mem = fromSnapshot(loadFromDisk(threadId) ?? emptySetupLog());
2401
+ memory.set(threadId, mem);
2402
+ return mem;
2403
+ }
2372
2404
  function readSetupLog(threadId) {
2373
2405
  const cached = memory.get(threadId);
2374
- if (cached) return clone(cached);
2406
+ if (cached) return materialize(cached);
2375
2407
  const disk = loadFromDisk(threadId);
2376
2408
  if (disk) {
2377
- memory.set(threadId, disk);
2378
- return clone(disk);
2409
+ memory.set(threadId, fromSnapshot(disk));
2410
+ return disk;
2379
2411
  }
2380
2412
  return emptySetupLog();
2381
2413
  }
2382
2414
  function beginSetupLog(threadId) {
2383
- const snap = {
2384
- output: "",
2415
+ const mem = {
2416
+ chunks: [],
2417
+ length: 0,
2385
2418
  running: true,
2386
2419
  exitCode: null,
2387
2420
  source: null
2388
2421
  };
2389
- memory.set(threadId, snap);
2422
+ memory.set(threadId, mem);
2390
2423
  persistNow(threadId);
2391
- return clone(snap);
2424
+ return materialize(mem);
2392
2425
  }
2393
2426
  function appendSetupLog(threadId, line) {
2394
- const current = memory.get(threadId) ?? readSetupLog(threadId);
2395
- const snap = {
2396
- ...current,
2397
- output: appendSetupOutput(current.output, line),
2398
- running: true
2399
- };
2400
- memory.set(threadId, snap);
2427
+ const mem = memFor(threadId);
2428
+ mem.chunks.push(line);
2429
+ mem.length += line.length + 1;
2430
+ mem.running = true;
2431
+ if (mem.length > MAX_SETUP_LOG_CHARS * 2) materialize(mem);
2401
2432
  schedulePersist(threadId);
2402
- return clone(snap);
2403
2433
  }
2404
2434
  function finishSetupLog(threadId, exitCode, source) {
2405
- const current = memory.get(threadId) ?? readSetupLog(threadId);
2406
- const snap = {
2407
- ...current,
2408
- running: false,
2409
- exitCode,
2410
- source: source ?? current.source
2411
- };
2412
- memory.set(threadId, snap);
2435
+ const mem = memFor(threadId);
2436
+ mem.running = false;
2437
+ mem.exitCode = exitCode;
2438
+ mem.source = source ?? mem.source;
2413
2439
  persistNow(threadId);
2414
- return clone(snap);
2440
+ return materialize(mem);
2415
2441
  }
2416
2442
 
2417
2443
  // src/git/orphan-cleanup.ts
@@ -3903,6 +3929,33 @@ function shouldRemoveWorktreeOnTeardown(thread) {
3903
3929
  return !isCowboyThread(thread) && !isPrimaryCheckoutThread(thread);
3904
3930
  }
3905
3931
 
3932
+ // src/util/line-coalescer.ts
3933
+ function createLineCoalescer(onChunk, opts) {
3934
+ const delayMs = opts?.delayMs ?? 50;
3935
+ const maxLines = opts?.maxLines ?? 200;
3936
+ let buffer = [];
3937
+ let timer = null;
3938
+ const flush2 = () => {
3939
+ if (timer) {
3940
+ clearTimeout(timer);
3941
+ timer = null;
3942
+ }
3943
+ if (buffer.length === 0) return;
3944
+ const chunk = buffer.join("\n");
3945
+ buffer = [];
3946
+ onChunk(chunk);
3947
+ };
3948
+ const push = (line) => {
3949
+ buffer.push(line);
3950
+ if (buffer.length >= maxLines) {
3951
+ flush2();
3952
+ return;
3953
+ }
3954
+ if (!timer) timer = setTimeout(flush2, delayMs);
3955
+ };
3956
+ return { push, flush: flush2 };
3957
+ }
3958
+
3906
3959
  // src/store/turn-live.ts
3907
3960
  import {
3908
3961
  existsSync as existsSync11,
@@ -4314,7 +4367,7 @@ async function adoptThread(input) {
4314
4367
  messages: input.messages ?? []
4315
4368
  });
4316
4369
  writeThread(thread);
4317
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-VIUUE4RT.js");
4370
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-KM7KTLVO.js");
4318
4371
  await ensureWorkspace2(repoPath);
4319
4372
  const { ensureWorktreeSideboardIgnored: ensureWorktreeSideboardIgnored2 } = await import("./worktree-exclude-N5X5I422.js");
4320
4373
  await ensureWorktreeSideboardIgnored2(input.worktreePath);
@@ -5521,6 +5574,29 @@ function writeWorktreeFile(worktreePath, relativePath, content) {
5521
5574
  writeFileSync6(abs, content, "utf8");
5522
5575
  return { path: relativePath };
5523
5576
  }
5577
+ async function getWorktreeDirtyStat(worktreePath) {
5578
+ const status = await inspectGitWorktree(worktreePath);
5579
+ if (status !== "ok") {
5580
+ return { additions: 0, deletions: 0, dirty: false };
5581
+ }
5582
+ const head = await git(["rev-parse", "--verify", "HEAD"], worktreePath, {
5583
+ reject: false,
5584
+ timeoutMs: 4e3
5585
+ });
5586
+ if (head.exitCode !== 0) {
5587
+ return { additions: 0, deletions: 0, dirty: await isDirty(worktreePath) };
5588
+ }
5589
+ const [num, dirty] = await Promise.all([
5590
+ gitDiff(["--numstat", "HEAD"], worktreePath, { timeoutMs: 4e3 }),
5591
+ isDirty(worktreePath)
5592
+ ]);
5593
+ const counts = statFromNumstat(num.stdout);
5594
+ return {
5595
+ additions: counts.additions,
5596
+ deletions: counts.deletions,
5597
+ dirty: dirty || counts.additions > 0 || counts.deletions > 0
5598
+ };
5599
+ }
5524
5600
  async function getDiffSummary(worktreePath, repoPath, opts) {
5525
5601
  const full = await getDiff(worktreePath, repoPath, {
5526
5602
  maxHunkChars: opts?.maxHunkChars ?? 2e3,
@@ -6067,9 +6143,7 @@ function consumeComposerAttachments(thread) {
6067
6143
  };
6068
6144
  }
6069
6145
  function takePendingTurnAttachments(thread) {
6070
- const pending = thread.pendingTurnAttachments ?? [];
6071
- if (pending.length > 0) return [...pending];
6072
- return thread.attachments.length > 0 ? [...thread.attachments] : [];
6146
+ return [...thread.pendingTurnAttachments ?? []];
6073
6147
  }
6074
6148
  function alignQueueAttachments(queueLength, existing) {
6075
6149
  const next = (existing ?? []).slice(0, queueLength).map((slot) => [...slot ?? []]);
@@ -6746,7 +6820,7 @@ function formatScheduledPrompt(name, prompt) {
6746
6820
  ${prompt}`;
6747
6821
  }
6748
6822
  async function defaultDeps() {
6749
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-NZO4PVFH.js");
6823
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-MI7A5DQZ.js");
6750
6824
  const orch = getOrchestrator2();
6751
6825
  return {
6752
6826
  findThread: (id) => findThreadByRef(id),
@@ -6877,6 +6951,7 @@ function resolveOrchChildFollowUp(requested) {
6877
6951
  return followUpBehavior();
6878
6952
  }
6879
6953
  var STALE_AGENT_PID_WAIT_MS = 2500;
6954
+ var LIGHT_RECONCILE_THROTTLE_MS = 1e4;
6880
6955
  function writeLiveStatus(threadId, status, lastError) {
6881
6956
  const latest = readThread(threadId);
6882
6957
  if (!latest || latest.status === "archived") return latest;
@@ -6891,6 +6966,8 @@ var Orchestrator = class {
6891
6966
  startingTurns = /* @__PURE__ */ new Set();
6892
6967
  /** Last sync thread-file read for false-stop heal (throttled per thread). */
6893
6968
  lastReconcileHealAt = /* @__PURE__ */ new Map();
6969
+ /** Per-repo timestamp of the last `reconcile(repo, { light: true })`. */
6970
+ lightReconcileAt = /* @__PURE__ */ new Map();
6894
6971
  /**
6895
6972
  * Threads intentionally force-stopped. Prevents runTurn from re-asserting
6896
6973
  * `running` after spawn, and from overwriting `stopped` with idle/error when
@@ -6976,6 +7053,20 @@ var Orchestrator = class {
6976
7053
  async reconcile(repoPath, opts) {
6977
7054
  const reclaimStaleTurns = opts?.reclaimStaleTurns === true;
6978
7055
  const drainQueues = opts?.drainQueues !== false;
7056
+ if (opts?.light && repoPath) {
7057
+ const last = this.lightReconcileAt.get(repoPath) ?? 0;
7058
+ if (Date.now() - last < LIGHT_RECONCILE_THROTTLE_MS) return;
7059
+ this.lightReconcileAt.set(repoPath, Date.now());
7060
+ for (const thread of listThreads()) {
7061
+ if (isGlobalThread(thread) || thread.repoPath !== repoPath) continue;
7062
+ if (!existsSync19(thread.worktreePath)) {
7063
+ setStatus(thread.id, "broken", "Worktree missing on disk");
7064
+ this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
7065
+ }
7066
+ }
7067
+ await this.reconcileOrphans([repoPath]);
7068
+ return;
7069
+ }
6979
7070
  healOrchestrationSoccerTitles();
6980
7071
  for (const thread of listThreads({ includeArchived: true })) {
6981
7072
  if (thread.status === "archived") continue;
@@ -7008,6 +7099,21 @@ var Orchestrator = class {
7008
7099
  }
7009
7100
  }
7010
7101
  const repoPaths = (repoPath ? [repoPath] : [...new Set(listThreads({ includeArchived: true }).map((t) => t.repoPath))]).filter((p) => !isGlobalRepoPath(p));
7102
+ await this.reconcileOrphans(repoPaths);
7103
+ try {
7104
+ if (shouldRunHistoryCleanup()) {
7105
+ cleanupArchivedHistory();
7106
+ }
7107
+ } catch {
7108
+ }
7109
+ if (drainQueues) {
7110
+ this.adoptPersistedQueues();
7111
+ }
7112
+ this.schedulePendingQuotaResumes();
7113
+ armSchedules();
7114
+ }
7115
+ /** Surface (and, when enabled, clean up) worktrees git knows about but no thread owns. */
7116
+ async reconcileOrphans(repoPaths) {
7011
7117
  try {
7012
7118
  const orphans = await findOrphanWorktrees(repoPaths);
7013
7119
  if (orphans.length) {
@@ -7022,17 +7128,6 @@ var Orchestrator = class {
7022
7128
  }
7023
7129
  } catch {
7024
7130
  }
7025
- try {
7026
- if (shouldRunHistoryCleanup()) {
7027
- cleanupArchivedHistory();
7028
- }
7029
- } catch {
7030
- }
7031
- if (drainQueues) {
7032
- this.adoptPersistedQueues();
7033
- }
7034
- this.schedulePendingQuotaResumes();
7035
- armSchedules();
7036
7131
  }
7037
7132
  /**
7038
7133
  * Adopt queues persisted by another process (MCP stdio / CLI) into this
@@ -7266,7 +7361,7 @@ var Orchestrator = class {
7266
7361
  return listThreads({ includeArchived });
7267
7362
  }
7268
7363
  getThread(idOrRef) {
7269
- return findThreadByRef(idOrRef) ?? readThread(idOrRef);
7364
+ return readThread(idOrRef) ?? findThreadByRef(idOrRef);
7270
7365
  }
7271
7366
  async createThread(input) {
7272
7367
  const prior = new Set(
@@ -7437,9 +7532,11 @@ var Orchestrator = class {
7437
7532
  const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id);
7438
7533
  updateThread(thread.id, {
7439
7534
  ...removed,
7440
- // Old sends parked on pendingTurnAttachments; drop that bag when the
7441
- // follow-up that owned it is gone so a later turn cannot inherit it.
7442
- ...stillQueued ? {} : { pendingTurnAttachments: [] },
7535
+ // Follow-up files live on queueAttachments. pendingTurnAttachments is
7536
+ // the in-flight / just-dequeued snapshot — do not wipe it when a
7537
+ // later follow-up is removed. Only drop a leftover bag when nothing
7538
+ // is running and the queue is empty (legacy sends that never drained).
7539
+ ...stillQueued || inFlight ? {} : { pendingTurnAttachments: [] },
7443
7540
  status: !stillQueued && !inFlight && current.status === "queued" ? "idle" : current.status
7444
7541
  });
7445
7542
  this.emit({ type: "queue_changed", threadId: thread.id, queue: removed.queue });
@@ -7521,7 +7618,9 @@ var Orchestrator = class {
7521
7618
  updateThread(threadId, {
7522
7619
  queue: shifted.queue,
7523
7620
  queueAttachments: shifted.queueAttachments,
7524
- ...shifted.attachments.length > 0 ? { pendingTurnAttachments: shifted.attachments } : {}
7621
+ // Always write the snapshot, including [] for text-only, so
7622
+ // takePending cannot fall back to leftover pending or composer files.
7623
+ pendingTurnAttachments: shifted.attachments
7525
7624
  });
7526
7625
  this.emit({ type: "queue_changed", threadId, queue: shifted.queue });
7527
7626
  await this.runTurn(threadId, shifted.prompt);
@@ -7569,7 +7668,6 @@ var Orchestrator = class {
7569
7668
  thread = this.requireThread(threadId);
7570
7669
  }
7571
7670
  const autoContinue = isJobContinuePrompt(prompt) || prompt.trim().startsWith("The previous agent process ended before it finished.");
7572
- const parkedSnapshot = thread.pendingTurnAttachments ?? [];
7573
7671
  const parked = takePendingTurnAttachments(thread);
7574
7672
  const sentAttachments = !autoContinue && parked.length > 0 ? parked : void 0;
7575
7673
  appendMessage(threadId, {
@@ -7585,7 +7683,7 @@ var Orchestrator = class {
7585
7683
  thread.worktreePath,
7586
7684
  gitPrompt,
7587
7685
  {
7588
- attachments: autoContinue ? [] : sentAttachments ?? thread.attachments
7686
+ attachments: autoContinue ? [] : sentAttachments ?? []
7589
7687
  }
7590
7688
  );
7591
7689
  const slackInbound = /^(Slack DM|Slack @mention)(?:\n|$)/.test(
@@ -7639,10 +7737,7 @@ var Orchestrator = class {
7639
7737
  expandedPrompt
7640
7738
  ].filter(Boolean).join("\n\n");
7641
7739
  if (!autoContinue && sentAttachments) {
7642
- updateThread(
7643
- threadId,
7644
- parkedSnapshot.length > 0 ? { pendingTurnAttachments: [] } : { attachments: [], pendingTurnAttachments: [] }
7645
- );
7740
+ updateThread(threadId, { pendingTurnAttachments: [] });
7646
7741
  }
7647
7742
  const adapter = getAdapter(thread.agent);
7648
7743
  const resolved = await adapter.resolveSessionId(thread.worktreePath, thread.sessionId);
@@ -8122,17 +8217,18 @@ var Orchestrator = class {
8122
8217
  }
8123
8218
  }
8124
8219
  }
8220
+ const runOutput = createLineCoalescer((chunk) => {
8221
+ this.emit({
8222
+ type: "run_output",
8223
+ threadId: thread.id,
8224
+ scriptName: resolvedName,
8225
+ line: chunk
8226
+ });
8227
+ });
8125
8228
  const handle = await startDevServer(
8126
8229
  thread.repoPath,
8127
8230
  thread.worktreePath,
8128
- (line) => {
8129
- this.emit({
8130
- type: "run_output",
8131
- threadId: thread.id,
8132
- scriptName: resolvedName,
8133
- line
8134
- });
8135
- },
8231
+ runOutput.push,
8136
8232
  { scriptName: resolvedName }
8137
8233
  );
8138
8234
  if (!handle) {
@@ -8178,6 +8274,7 @@ var Orchestrator = class {
8178
8274
  scriptName: resolvedName
8179
8275
  });
8180
8276
  void handle.done.then(() => {
8277
+ runOutput.flush();
8181
8278
  this.processes.delete(runKey);
8182
8279
  if (isDefault) this.processes.delete(worktreeDevProcessKey(thread.worktreePath));
8183
8280
  const latest = mergeWorktreeActiveRuns(threadsSharingWorktree(thread.worktreePath));
@@ -8263,9 +8360,14 @@ var Orchestrator = class {
8263
8360
  (t) => this.processes.has(`${t.id}:setup`)
8264
8361
  );
8265
8362
  }
8266
- writeSetupLog(threadId, worktreePath, fn) {
8363
+ /**
8364
+ * The worktree-keyed log is canonical (`resolveWorktreeSetupLog` prefers it
8365
+ * whenever it has output/running/exitCode); per-chat keys only exist for
8366
+ * records that predate it. Writing one key halves the sync persists during
8367
+ * `pnpm install`.
8368
+ */
8369
+ writeSetupLog(worktreePath, fn) {
8267
8370
  fn(setupLogKeyForWorktree(worktreePath));
8268
- fn(threadId);
8269
8371
  }
8270
8372
  async runSetup(threadRef, opts) {
8271
8373
  const thread = this.requireThread(threadRef);
@@ -8280,14 +8382,18 @@ var Orchestrator = class {
8280
8382
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
8281
8383
  kill: () => abort.abort()
8282
8384
  });
8283
- this.writeSetupLog(thread.id, thread.worktreePath, beginSetupLog);
8385
+ this.writeSetupLog(thread.worktreePath, beginSetupLog);
8284
8386
  this.emit({ type: "setup_started", threadId: thread.id });
8387
+ const output = createLineCoalescer((chunk) => {
8388
+ this.writeSetupLog(thread.worktreePath, (logKey) => appendSetupLog(logKey, chunk));
8389
+ this.emit({ type: "setup_output", threadId: thread.id, line: chunk });
8390
+ });
8285
8391
  let finished = false;
8286
8392
  const finish = (exitCode, source) => {
8287
8393
  if (finished) return;
8288
8394
  finished = true;
8395
+ output.flush();
8289
8396
  this.writeSetupLog(
8290
- thread.id,
8291
8397
  thread.worktreePath,
8292
8398
  (logKey) => finishSetupLog(logKey, exitCode, source)
8293
8399
  );
@@ -8297,24 +8403,12 @@ var Orchestrator = class {
8297
8403
  const setup = await runWorkspaceSetup(
8298
8404
  thread.repoPath,
8299
8405
  thread.worktreePath,
8300
- (line) => {
8301
- this.writeSetupLog(
8302
- thread.id,
8303
- thread.worktreePath,
8304
- (logKey) => appendSetupLog(logKey, line)
8305
- );
8306
- this.emit({ type: "setup_output", threadId: thread.id, line });
8307
- },
8406
+ output.push,
8308
8407
  { signal: abort.signal }
8309
8408
  );
8310
8409
  if (!setup.ran) {
8311
8410
  const line = "No setup script in .sideboard/settings.toml, .conductor/settings.toml, .cursor/worktrees.json, or script/setup (bin/setup, scripts/setup)";
8312
- this.writeSetupLog(
8313
- thread.id,
8314
- thread.worktreePath,
8315
- (logKey) => appendSetupLog(logKey, line)
8316
- );
8317
- this.emit({ type: "setup_output", threadId: thread.id, line });
8411
+ output.push(line);
8318
8412
  finish(null, null);
8319
8413
  throw new Error(line);
8320
8414
  }
@@ -8520,6 +8614,11 @@ var Orchestrator = class {
8520
8614
  lastTurnBase: this.turnBaselines.get(thread.id) ?? null
8521
8615
  });
8522
8616
  }
8617
+ async worktreeDirtyStat(threadRef) {
8618
+ const thread = this.requireThread(threadRef);
8619
+ this.assertNotGlobal(thread, "Diff");
8620
+ return getWorktreeDirtyStat(thread.worktreePath);
8621
+ }
8523
8622
  async diffSummary(threadRef) {
8524
8623
  const thread = this.requireThread(threadRef);
8525
8624
  this.assertNotGlobal(thread, "Diff");
@@ -8968,7 +9067,7 @@ var Orchestrator = class {
8968
9067
  this.enforceHistoryRetention();
8969
9068
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
8970
9069
  try {
8971
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-VIUUE4RT.js");
9070
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-KM7KTLVO.js");
8972
9071
  await ensureWorkspace2(thread.repoPath);
8973
9072
  } catch {
8974
9073
  }
@@ -9022,7 +9121,7 @@ var Orchestrator = class {
9022
9121
  `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
9023
9122
  );
9024
9123
  }
9025
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-3MEG62KB.js");
9124
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-VXRI7LYM.js");
9026
9125
  const slug = thread.worktreePath.split("/").pop();
9027
9126
  const dest = thread.worktreePath;
9028
9127
  await withRepoGitLock(thread.repoPath, async () => {
@@ -9132,7 +9231,7 @@ async function startOrchestration(opts) {
9132
9231
  sourceRef: "default",
9133
9232
  ...createOpts
9134
9233
  }).catch(async () => {
9135
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-3MEG62KB.js");
9234
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-VXRI7LYM.js");
9136
9235
  const repo = await resolveRepoRoot2(repoPath);
9137
9236
  const def = await resolveDefaultBranch2(repo);
9138
9237
  return orch.createThread({
@@ -9142,7 +9241,7 @@ async function startOrchestration(opts) {
9142
9241
  repoPath: repo
9143
9242
  });
9144
9243
  });
9145
- const { updateThread: upd } = await import("./thread-store-MSMBQ3V4.js");
9244
+ const { updateThread: upd } = await import("./thread-store-KDPPXJJ3.js");
9146
9245
  const updated = upd(thread.id, {
9147
9246
  sourceType: "orchestration",
9148
9247
  sourceRef: goal
@@ -9194,6 +9293,7 @@ export {
9194
9293
  waitForPidExit,
9195
9294
  resolveSendFollowUp,
9196
9295
  resolveOrchChildFollowUp,
9296
+ LIGHT_RECONCILE_THROTTLE_MS,
9197
9297
  Orchestrator,
9198
9298
  getOrchestrator,
9199
9299
  startOrchestration
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  isOrchestratorThread
3
- } from "./chunk-2PE6SZCM.js";
3
+ } from "./chunk-FNGXKY5W.js";
4
4
  import {
5
5
  codexUnattendedGitConfigArgs,
6
6
  mergeAgentGitAuthEnv,
7
7
  resolveAgentGitAuthEnv,
8
8
  resolveCodexGitWritableRoots
9
- } from "./chunk-NRKDRQM3.js";
9
+ } from "./chunk-NI243N27.js";
10
10
  import {
11
11
  applyConnectedTeamToCli,
12
12
  brightsyMcpServerName,
@@ -1903,7 +1903,7 @@ var claudeAdapter = {
1903
1903
  );
1904
1904
  }
1905
1905
  const mode = permissionMode(thread);
1906
- const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-NG2TBUI5.js");
1906
+ const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-6P236KKG.js");
1907
1907
  const isOrchestrator = isOrchestratorThread2(thread);
1908
1908
  const injectedServers = await buildInjectedMcpServers({
1909
1909
  includeSideboard: true,
@@ -5,7 +5,7 @@ import {
5
5
  resolveRepoRoot,
6
6
  takenSlugsFromThread,
7
7
  teamSlugFromName
8
- } from "./chunk-NRKDRQM3.js";
8
+ } from "./chunk-NI243N27.js";
9
9
  import {
10
10
  ATTACHMENTS_DIR,
11
11
  attachmentsGitignoreBody
@@ -28,7 +28,7 @@ import {
28
28
  listThreads,
29
29
  updateThread,
30
30
  writeThread
31
- } from "./chunk-JJ3ONQ4Q.js";
31
+ } from "./chunk-3IMSQNTS.js";
32
32
  import {
33
33
  appDataDir,
34
34
  globalAgentCwd,
@@ -146,6 +146,7 @@ function forgetThread(id) {
146
146
  const cache = cacheForDir();
147
147
  cache.byId.delete(id);
148
148
  cache.mtimeMs.delete(id);
149
+ selfWriteMtimes.delete(id);
149
150
  }
150
151
  function invalidateThreadListCache() {
151
152
  listCache = null;
@@ -169,20 +170,28 @@ function readThread(id) {
169
170
  rememberThread(thread, mtimeMs);
170
171
  return thread;
171
172
  }
173
+ var selfWriteMtimes = /* @__PURE__ */ new Map();
172
174
  function writeThread(thread, opts) {
173
175
  const path = threadFilePath(idPath(thread.id));
174
176
  const tmp = `${path}.${process.pid}.tmp`;
175
177
  const next = opts?.touch === false ? { ...thread } : { ...thread, updatedAt: nowIso() };
176
178
  writeFileSync(tmp, JSON.stringify(next, null, 2), "utf8");
177
179
  renameSync(tmp, path);
178
- rememberThread(next, fileMtimeMs(path) ?? Date.now());
180
+ const mtimeMs = fileMtimeMs(path) ?? Date.now();
181
+ rememberThread(next, mtimeMs);
182
+ selfWriteMtimes.set(thread.id, mtimeMs);
183
+ }
184
+ function isSelfWrittenRecord(id) {
185
+ const ours = selfWriteMtimes.get(id);
186
+ if (ours == null) return false;
187
+ return fileMtimeMs(threadFilePath(id)) === ours;
179
188
  }
180
189
  function idPath(id) {
181
190
  return id;
182
191
  }
183
192
  function isThreadRecordFile(nameOrPath) {
184
193
  const name = basename(nameOrPath);
185
- return name.endsWith(".json") && !name.endsWith(".live.json");
194
+ return name.endsWith(".json") && !name.endsWith(".live.json") && !name.endsWith(".setup.log.json");
186
195
  }
187
196
  function listThreads(opts) {
188
197
  const cache = cacheForDir();
@@ -258,6 +267,7 @@ export {
258
267
  invalidateThreadRecord,
259
268
  readThread,
260
269
  writeThread,
270
+ isSelfWrittenRecord,
261
271
  isThreadRecordFile,
262
272
  listThreads,
263
273
  deleteThreadRecord,
@@ -7,7 +7,7 @@ import {
7
7
  resolveRepoRoot,
8
8
  takenSlugsFromThread,
9
9
  teamSlugFromName
10
- } from "./chunk-VPQXJYIW.js";
10
+ } from "./chunk-XYKVRRC2.js";
11
11
  import {
12
12
  ATTACHMENTS_DIR,
13
13
  attachmentsGitignoreBody
@@ -17,7 +17,7 @@ import {
17
17
  listThreads,
18
18
  updateThread,
19
19
  writeThread
20
- } from "./chunk-GHKFSBES.js";
20
+ } from "./chunk-HY2GFZ5S.js";
21
21
  import {
22
22
  assertOrchestratorCapableAgent,
23
23
  formatAccountProfilePlaybookLine,