@sideboard-ai/core 0.1.194 → 0.1.195

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.
@@ -406,7 +406,7 @@ async function continueSourceThread(threadId, prompt) {
406
406
  await continueOnReply(threadId, prompt);
407
407
  return;
408
408
  }
409
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-A5KF2335.js");
409
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-EHRFN3Q6.js");
410
410
  await getOrchestrator2().send(threadId, prompt);
411
411
  } catch {
412
412
  }
@@ -1444,6 +1444,53 @@ async function maybeCompactContext(thread, thresholds = {}, summarize = summariz
1444
1444
  };
1445
1445
  }
1446
1446
 
1447
+ // src/threads/worktree-runtime.ts
1448
+ function worktreeProcessPrefix(worktreePath) {
1449
+ return `wt:${normalizeWorktreePath(worktreePath)}`;
1450
+ }
1451
+ function worktreeSetupProcessKey(worktreePath) {
1452
+ return `${worktreeProcessPrefix(worktreePath)}:setup`;
1453
+ }
1454
+ function worktreeRunProcessKey(worktreePath, scriptName) {
1455
+ return `${worktreeProcessPrefix(worktreePath)}:run:${scriptName}`;
1456
+ }
1457
+ function worktreeDevProcessKey(worktreePath) {
1458
+ return `${worktreeProcessPrefix(worktreePath)}:dev`;
1459
+ }
1460
+ function isWorktreeRunProcessKey(key, worktreePath) {
1461
+ const prefix = worktreeProcessPrefix(worktreePath);
1462
+ return key.startsWith(`${prefix}:run:`) || key === `${prefix}:dev`;
1463
+ }
1464
+ function pickRichestSetupLog(snaps) {
1465
+ let best;
1466
+ for (const snap of snaps) {
1467
+ if (!snap.output && !snap.running && snap.exitCode == null) continue;
1468
+ if (!best) {
1469
+ best = snap;
1470
+ continue;
1471
+ }
1472
+ if (snap.running && !best.running) {
1473
+ best = snap;
1474
+ continue;
1475
+ }
1476
+ if (snap.running === best.running && snap.output.length > best.output.length) {
1477
+ best = snap;
1478
+ }
1479
+ }
1480
+ return best;
1481
+ }
1482
+ function mergeWorktreeActiveRuns(threads) {
1483
+ const byName = /* @__PURE__ */ new Map();
1484
+ let devPort = null;
1485
+ for (const thread of threads) {
1486
+ if (thread.devPort != null && devPort == null) devPort = thread.devPort;
1487
+ for (const run2 of thread.activeRuns ?? []) {
1488
+ if (!byName.has(run2.scriptName)) byName.set(run2.scriptName, run2);
1489
+ }
1490
+ }
1491
+ return { activeRuns: [...byName.values()], devPort };
1492
+ }
1493
+
1447
1494
  // src/threads/chat-tabs.ts
1448
1495
  function sameWorktreePath(a, b) {
1449
1496
  return normalizeWorktreePath(a) === normalizeWorktreePath(b);
@@ -1540,7 +1587,8 @@ function createChatTab(input) {
1540
1587
  planMode: from.planMode,
1541
1588
  autonomy: input.autonomy ?? from.autonomy,
1542
1589
  attachments: input.attachments ?? [],
1543
- status: "idle"
1590
+ status: "idle",
1591
+ ...mergeWorktreeActiveRuns(threadsSharingWorktree(binding.worktreePath))
1544
1592
  });
1545
1593
  writeThread(thread);
1546
1594
  return thread;
@@ -2185,6 +2233,7 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
2185
2233
  }
2186
2234
 
2187
2235
  // src/store/setup-log.ts
2236
+ import { createHash } from "crypto";
2188
2237
  import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
2189
2238
  import { join as join7 } from "path";
2190
2239
  var EMPTY = {
@@ -2198,6 +2247,10 @@ var persistTimers = /* @__PURE__ */ new Map();
2198
2247
  function setupLogPath(threadId) {
2199
2248
  return join7(threadsDir(), `${threadId}.setup.log.json`);
2200
2249
  }
2250
+ function setupLogKeyForWorktree(worktreePath) {
2251
+ const norm = normalizeWorktreePath(worktreePath);
2252
+ return `wt-${createHash("sha1").update(norm).digest("hex").slice(0, 16)}`;
2253
+ }
2201
2254
  function emptySetupLog() {
2202
2255
  return { ...EMPTY };
2203
2256
  }
@@ -6476,7 +6529,7 @@ function formatScheduledPrompt(name, prompt) {
6476
6529
  ${prompt}`;
6477
6530
  }
6478
6531
  async function defaultDeps() {
6479
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-A5KF2335.js");
6532
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-EHRFN3Q6.js");
6480
6533
  const orch = getOrchestrator2();
6481
6534
  return {
6482
6535
  findThread: (id) => findThreadByRef(id),
@@ -7758,22 +7811,25 @@ var Orchestrator = class {
7758
7811
  "No run script found for this worktree. Add [scripts.run.*] in the worktree\u2019s .sideboard/settings.toml or .conductor/settings.toml (or settings.local.toml on the main repo)."
7759
7812
  );
7760
7813
  }
7761
- const runKey = `${thread.id}:run:${resolvedName}`;
7762
- const existing = this.processes.get(runKey);
7763
- const active = (thread.activeRuns ?? []).find((r) => r.scriptName === resolvedName);
7814
+ const siblings = threadsSharingWorktree(thread.worktreePath);
7815
+ const shared = mergeWorktreeActiveRuns(siblings);
7816
+ const runKey = worktreeRunProcessKey(thread.worktreePath, resolvedName);
7817
+ const existing = this.processes.get(runKey) ?? siblings.map((t) => this.processes.get(`${t.id}:run:${resolvedName}`)).find(Boolean);
7818
+ const active = shared.activeRuns.find((r) => r.scriptName === resolvedName);
7764
7819
  if (existing && active) {
7765
7820
  return { port: active.port, scriptName: resolvedName, ports: active.ports };
7766
7821
  }
7767
7822
  if (!scriptName) {
7768
- const legacy = this.processes.get(`${thread.id}:dev`);
7769
- if (legacy && thread.devPort) {
7770
- return { port: thread.devPort, scriptName: resolvedName, ports: [thread.devPort] };
7823
+ const legacy = this.processes.get(worktreeDevProcessKey(thread.worktreePath)) ?? siblings.map((t) => this.processes.get(`${t.id}:dev`)).find(Boolean);
7824
+ if (legacy && shared.devPort) {
7825
+ return { port: shared.devPort, scriptName: resolvedName, ports: [shared.devPort] };
7771
7826
  }
7772
7827
  }
7773
7828
  const mode = getRunMode(thread.worktreePath, thread.repoPath);
7774
7829
  if (mode === "nonconcurrent") {
7830
+ const here = normalizeWorktreePath(thread.worktreePath);
7775
7831
  for (const t of listThreads()) {
7776
- if (t.id === thread.id) continue;
7832
+ if (normalizeWorktreePath(t.worktreePath) === here) continue;
7777
7833
  const runs = t.activeRuns ?? [];
7778
7834
  if (runs.length > 0 || t.devPort) {
7779
7835
  throw new Error(
@@ -7808,7 +7864,7 @@ var Orchestrator = class {
7808
7864
  });
7809
7865
  const isDefault = scripts.find((s) => s.default)?.name === resolvedName || !scripts.some((s) => s.default) && (resolvedName === "dev" || resolvedName === scripts[0]?.name);
7810
7866
  if (isDefault) {
7811
- this.processes.set(`${thread.id}:dev`, {
7867
+ this.processes.set(worktreeDevProcessKey(thread.worktreePath), {
7812
7868
  kind: "dev",
7813
7869
  pid: handle.pid,
7814
7870
  startedAt,
@@ -7823,13 +7879,14 @@ var Orchestrator = class {
7823
7879
  startedAt
7824
7880
  };
7825
7881
  const nextRuns = [
7826
- ...(thread.activeRuns ?? []).filter((r) => r.scriptName !== resolvedName),
7882
+ ...shared.activeRuns.filter((r) => r.scriptName !== resolvedName),
7827
7883
  run2
7828
7884
  ];
7829
- updateThread(thread.id, {
7830
- activeRuns: nextRuns,
7831
- devPort: isDefault ? handle.port : thread.devPort
7832
- });
7885
+ this.syncWorktreeRuns(
7886
+ thread.worktreePath,
7887
+ nextRuns,
7888
+ isDefault ? handle.port : shared.devPort
7889
+ );
7833
7890
  this.emit({
7834
7891
  type: "dev_server_started",
7835
7892
  threadId: thread.id,
@@ -7838,15 +7895,14 @@ var Orchestrator = class {
7838
7895
  });
7839
7896
  void handle.done.then(() => {
7840
7897
  this.processes.delete(runKey);
7841
- if (isDefault) this.processes.delete(`${thread.id}:dev`);
7842
- const latest = readThread(thread.id);
7843
- const remaining = (latest?.activeRuns ?? []).filter(
7844
- (r) => r.scriptName !== resolvedName
7898
+ if (isDefault) this.processes.delete(worktreeDevProcessKey(thread.worktreePath));
7899
+ const latest = mergeWorktreeActiveRuns(threadsSharingWorktree(thread.worktreePath));
7900
+ const remaining = latest.activeRuns.filter((r) => r.scriptName !== resolvedName);
7901
+ this.syncWorktreeRuns(
7902
+ thread.worktreePath,
7903
+ remaining,
7904
+ isDefault ? null : latest.devPort
7845
7905
  );
7846
- updateThread(thread.id, {
7847
- activeRuns: remaining,
7848
- devPort: isDefault ? null : latest?.devPort ?? null
7849
- });
7850
7906
  this.emit({
7851
7907
  type: "dev_server_stopped",
7852
7908
  threadId: thread.id,
@@ -7857,51 +7913,82 @@ var Orchestrator = class {
7857
7913
  }
7858
7914
  stopDev(threadRef, scriptName) {
7859
7915
  const thread = this.requireThread(threadRef);
7916
+ const siblings = threadsSharingWorktree(thread.worktreePath);
7917
+ const shared = mergeWorktreeActiveRuns(siblings);
7860
7918
  if (scriptName) {
7861
- const runKey = `${thread.id}:run:${scriptName}`;
7862
- const proc = this.processes.get(runKey);
7863
- if (proc) proc.kill();
7864
- this.processes.delete(runKey);
7865
- const remaining = (thread.activeRuns ?? []).filter((r) => r.scriptName !== scriptName);
7866
- const isPrimary = thread.devPort != null && thread.activeRuns?.find((r) => r.scriptName === scriptName)?.port === thread.devPort;
7867
- updateThread(thread.id, {
7868
- activeRuns: remaining,
7869
- devPort: isPrimary ? null : thread.devPort
7870
- });
7919
+ const keys = [
7920
+ worktreeRunProcessKey(thread.worktreePath, scriptName),
7921
+ ...siblings.map((t) => `${t.id}:run:${scriptName}`)
7922
+ ];
7923
+ for (const key of keys) {
7924
+ const proc = this.processes.get(key);
7925
+ if (proc) proc.kill();
7926
+ this.processes.delete(key);
7927
+ }
7928
+ const remaining = shared.activeRuns.filter((r) => r.scriptName !== scriptName);
7929
+ const isPrimary = shared.devPort != null && shared.activeRuns.find((r) => r.scriptName === scriptName)?.port === shared.devPort;
7930
+ this.syncWorktreeRuns(
7931
+ thread.worktreePath,
7932
+ remaining,
7933
+ isPrimary ? null : shared.devPort
7934
+ );
7871
7935
  this.emit({ type: "dev_server_stopped", threadId: thread.id, scriptName });
7872
7936
  return;
7873
7937
  }
7874
7938
  for (const [key, proc] of [...this.processes.entries()]) {
7875
- if (key.startsWith(`${thread.id}:run:`) || key === `${thread.id}:dev`) {
7939
+ if (isWorktreeRunProcessKey(key, thread.worktreePath) || siblings.some((t) => key.startsWith(`${t.id}:run:`) || key === `${t.id}:dev`)) {
7876
7940
  proc.kill();
7877
7941
  this.processes.delete(key);
7878
7942
  }
7879
7943
  }
7880
- updateThread(thread.id, { activeRuns: [], devPort: null });
7944
+ this.syncWorktreeRuns(thread.worktreePath, [], null);
7881
7945
  this.emit({ type: "dev_server_stopped", threadId: thread.id });
7882
7946
  }
7947
+ syncWorktreeRuns(worktreePath, activeRuns, devPort) {
7948
+ for (const sibling of threadsSharingWorktree(worktreePath)) {
7949
+ updateThread(sibling.id, { activeRuns, devPort });
7950
+ }
7951
+ }
7883
7952
  listThreadRunScripts(threadRef) {
7884
7953
  const thread = this.requireThread(threadRef);
7885
7954
  return listRunScripts(thread.worktreePath, thread.repoPath);
7886
7955
  }
7887
7956
  getActiveRuns(threadRef) {
7888
- return this.requireThread(threadRef).activeRuns ?? [];
7957
+ const thread = this.requireThread(threadRef);
7958
+ return mergeWorktreeActiveRuns(threadsSharingWorktree(thread.worktreePath)).activeRuns;
7889
7959
  }
7890
7960
  getSetupLog(threadRef) {
7891
7961
  const thread = this.getThread(threadRef);
7892
7962
  if (!thread) return readSetupLog(threadRef);
7893
- const snap = readSetupLog(thread.id);
7894
- if (snap.running && !this.processes.has(`${thread.id}:setup`)) {
7963
+ const siblingIds = [
7964
+ thread.id,
7965
+ ...threadsSharingWorktree(thread.worktreePath).map((t) => t.id)
7966
+ ];
7967
+ const snap = pickRichestSetupLog([
7968
+ readSetupLog(setupLogKeyForWorktree(thread.worktreePath)),
7969
+ ...[...new Set(siblingIds)].map((id) => readSetupLog(id))
7970
+ ]) ?? readSetupLog(thread.id);
7971
+ if (snap.running && !this.hasWorktreeSetupProcess(thread.worktreePath)) {
7895
7972
  return { ...snap, running: false };
7896
7973
  }
7897
7974
  return snap;
7898
7975
  }
7976
+ hasWorktreeSetupProcess(worktreePath) {
7977
+ if (this.processes.has(worktreeSetupProcessKey(worktreePath))) return true;
7978
+ return threadsSharingWorktree(worktreePath).some(
7979
+ (t) => this.processes.has(`${t.id}:setup`)
7980
+ );
7981
+ }
7982
+ writeSetupLog(threadId, worktreePath, fn) {
7983
+ fn(setupLogKeyForWorktree(worktreePath));
7984
+ fn(threadId);
7985
+ }
7899
7986
  async runSetup(threadRef, opts) {
7900
7987
  const thread = this.requireThread(threadRef);
7901
7988
  this.assertNotGlobal(thread, "Setup");
7902
- const key = `${thread.id}:setup`;
7903
- if (this.processes.has(key)) {
7904
- throw new Error("Setup already running for this thread");
7989
+ const key = worktreeSetupProcessKey(thread.worktreePath);
7990
+ if (this.hasWorktreeSetupProcess(thread.worktreePath)) {
7991
+ throw new Error("Setup already running for this worktree");
7905
7992
  }
7906
7993
  const abort = new AbortController();
7907
7994
  this.processes.set(key, {
@@ -7909,13 +7996,17 @@ var Orchestrator = class {
7909
7996
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
7910
7997
  kill: () => abort.abort()
7911
7998
  });
7912
- beginSetupLog(thread.id);
7999
+ this.writeSetupLog(thread.id, thread.worktreePath, beginSetupLog);
7913
8000
  this.emit({ type: "setup_started", threadId: thread.id });
7914
8001
  let finished = false;
7915
8002
  const finish = (exitCode, source) => {
7916
8003
  if (finished) return;
7917
8004
  finished = true;
7918
- finishSetupLog(thread.id, exitCode, source);
8005
+ this.writeSetupLog(
8006
+ thread.id,
8007
+ thread.worktreePath,
8008
+ (logKey) => finishSetupLog(logKey, exitCode, source)
8009
+ );
7919
8010
  this.emit({ type: "setup_finished", threadId: thread.id, exitCode });
7920
8011
  };
7921
8012
  try {
@@ -7923,14 +8014,22 @@ var Orchestrator = class {
7923
8014
  thread.repoPath,
7924
8015
  thread.worktreePath,
7925
8016
  (line) => {
7926
- appendSetupLog(thread.id, line);
8017
+ this.writeSetupLog(
8018
+ thread.id,
8019
+ thread.worktreePath,
8020
+ (logKey) => appendSetupLog(logKey, line)
8021
+ );
7927
8022
  this.emit({ type: "setup_output", threadId: thread.id, line });
7928
8023
  },
7929
8024
  { signal: abort.signal }
7930
8025
  );
7931
8026
  if (!setup.ran) {
7932
8027
  const line = "No setup script in .sideboard/settings.toml, .conductor/settings.toml, .cursor/worktrees.json, or script/setup (bin/setup, scripts/setup)";
7933
- appendSetupLog(thread.id, line);
8028
+ this.writeSetupLog(
8029
+ thread.id,
8030
+ thread.worktreePath,
8031
+ (logKey) => appendSetupLog(logKey, line)
8032
+ );
7934
8033
  this.emit({ type: "setup_output", threadId: thread.id, line });
7935
8034
  finish(null, null);
7936
8035
  throw new Error(line);
@@ -7958,8 +8057,14 @@ var Orchestrator = class {
7958
8057
  }
7959
8058
  cancelSetup(threadRef) {
7960
8059
  const thread = this.requireThread(threadRef);
7961
- const proc = this.processes.get(`${thread.id}:setup`);
7962
- if (proc) proc.kill();
8060
+ const keys = [
8061
+ worktreeSetupProcessKey(thread.worktreePath),
8062
+ ...threadsSharingWorktree(thread.worktreePath).map((t) => `${t.id}:setup`)
8063
+ ];
8064
+ for (const key of [...new Set(keys)]) {
8065
+ const proc = this.processes.get(key);
8066
+ if (proc) proc.kill();
8067
+ }
7963
8068
  }
7964
8069
  async applyIntoMain(threadRef, opts) {
7965
8070
  const thread = this.requireThread(threadRef);