@sideboard-ai/core 0.1.225 → 0.1.226

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.
@@ -418,7 +418,7 @@ async function continueSourceThread(threadId, prompt) {
418
418
  await continueOnReply(threadId, prompt);
419
419
  return;
420
420
  }
421
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-ZAJ2M4UW.js");
421
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-OTEYZHTZ.js");
422
422
  await getOrchestrator2().send(threadId, prompt);
423
423
  } catch {
424
424
  }
@@ -1197,6 +1197,7 @@ import {
1197
1197
  readdirSync,
1198
1198
  readFileSync as readFileSync4
1199
1199
  } from "fs";
1200
+ import { execFileSync } from "child_process";
1200
1201
  import { createServer } from "net";
1201
1202
  import { basename, dirname as dirname2, join as join7 } from "path";
1202
1203
  import { execa as execa2 } from "execa";
@@ -2177,6 +2178,34 @@ function pipeLines(stream, onLine) {
2177
2178
  const rl = createInterface2({ input: stream });
2178
2179
  rl.on("line", onLine);
2179
2180
  }
2181
+ function killListenersOnPorts(ports) {
2182
+ for (const port of ports) {
2183
+ if (!Number.isFinite(port) || port <= 0) continue;
2184
+ try {
2185
+ if (process.platform === "win32") {
2186
+ execFileSync(
2187
+ "powershell",
2188
+ [
2189
+ "-NoProfile",
2190
+ "-Command",
2191
+ `Get-NetTCPConnection -LocalPort ${port} -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }`
2192
+ ],
2193
+ { stdio: "ignore" }
2194
+ );
2195
+ } else {
2196
+ execFileSync(
2197
+ "zsh",
2198
+ [
2199
+ "-lc",
2200
+ `pids=$(lsof -tiTCP:${port} -sTCP:LISTEN 2>/dev/null); [ -n "$pids" ] && kill -TERM $pids 2>/dev/null; true`
2201
+ ],
2202
+ { stdio: "ignore" }
2203
+ );
2204
+ }
2205
+ } catch {
2206
+ }
2207
+ }
2208
+ }
2180
2209
  function killScriptTree(child, ports = []) {
2181
2210
  const pid = child.pid;
2182
2211
  if (pid) {
@@ -2199,26 +2228,7 @@ function killScriptTree(child, ports = []) {
2199
2228
  }
2200
2229
  }
2201
2230
  }
2202
- for (const port of ports) {
2203
- if (!Number.isFinite(port) || port <= 0) continue;
2204
- if (process.platform === "win32") {
2205
- void execa2(
2206
- "powershell",
2207
- [
2208
- "-NoProfile",
2209
- "-Command",
2210
- `Get-NetTCPConnection -LocalPort ${port} -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }`
2211
- ],
2212
- { reject: false }
2213
- );
2214
- } else {
2215
- void execa2(
2216
- "zsh",
2217
- ["-lc", `pids=$(lsof -tiTCP:${port} -sTCP:LISTEN 2>/dev/null); [ -n "$pids" ] && kill -TERM $pids 2>/dev/null; true`],
2218
- { reject: false }
2219
- );
2220
- }
2221
- }
2231
+ killListenersOnPorts(ports);
2222
2232
  }
2223
2233
  async function spawnWorkspaceScript(command, opts) {
2224
2234
  const loginEnv = await captureLoginEnv();
@@ -4385,7 +4395,7 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
4385
4395
  }
4386
4396
 
4387
4397
  // src/threads/adopt.ts
4388
- import { execFileSync } from "child_process";
4398
+ import { execFileSync as execFileSync2 } from "child_process";
4389
4399
  import {
4390
4400
  copyFileSync as copyFileSync2,
4391
4401
  existsSync as existsSync12,
@@ -4649,7 +4659,7 @@ function importConductorWorkspace(workspaceId) {
4649
4659
  }
4650
4660
  let repoPath = worktreePath;
4651
4661
  try {
4652
- repoPath = execFileSync("git", ["rev-parse", "--show-toplevel"], {
4662
+ repoPath = execFileSync2("git", ["rev-parse", "--show-toplevel"], {
4653
4663
  cwd: worktreePath,
4654
4664
  encoding: "utf8"
4655
4665
  }).trim();
@@ -6957,7 +6967,7 @@ function formatScheduledPrompt(name, prompt) {
6957
6967
  ${prompt}`;
6958
6968
  }
6959
6969
  async function defaultDeps() {
6960
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-ZAJ2M4UW.js");
6970
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-OTEYZHTZ.js");
6961
6971
  const orch = getOrchestrator2();
6962
6972
  return {
6963
6973
  findThread: (id) => findThreadByRef(id),
@@ -7094,6 +7104,16 @@ function writeLiveStatus(threadId, status, lastError) {
7094
7104
  if (!latest || latest.status === "archived") return latest;
7095
7105
  return setStatus(threadId, status, lastError);
7096
7106
  }
7107
+ function collectActiveRunPorts(runs) {
7108
+ const ports = /* @__PURE__ */ new Set();
7109
+ for (const run2 of runs) {
7110
+ const list = run2.ports?.length ? run2.ports : run2.port != null ? [run2.port] : [];
7111
+ for (const p of list) {
7112
+ if (Number.isFinite(p) && p > 0) ports.add(p);
7113
+ }
7114
+ }
7115
+ return [...ports];
7116
+ }
7097
7117
  var Orchestrator = class {
7098
7118
  events = new EventEmitter();
7099
7119
  processes = /* @__PURE__ */ new Map();
@@ -7235,6 +7255,9 @@ var Orchestrator = class {
7235
7255
  this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
7236
7256
  }
7237
7257
  }
7258
+ if (reclaimStaleTurns) {
7259
+ this.reapOrphanedRunScripts();
7260
+ }
7238
7261
  const repoPaths = (repoPath ? [repoPath] : [...new Set(listThreads({ includeArchived: true }).map((t) => t.repoPath))]).filter((p) => !isGlobalRepoPath(p));
7239
7262
  await this.reconcileOrphans(repoPaths);
7240
7263
  try {
@@ -8453,13 +8476,30 @@ var Orchestrator = class {
8453
8476
  worktreeRunProcessKey(thread.worktreePath, scriptName),
8454
8477
  ...siblings.map((t) => `${t.id}:run:${scriptName}`)
8455
8478
  ];
8479
+ let hadHandle2 = false;
8456
8480
  for (const key of keys) {
8457
8481
  const proc = this.processes.get(key);
8458
- if (proc) proc.kill();
8482
+ if (proc) {
8483
+ proc.kill();
8484
+ hadHandle2 = true;
8485
+ }
8459
8486
  this.processes.delete(key);
8460
8487
  }
8488
+ const legacyKey = worktreeDevProcessKey(thread.worktreePath);
8489
+ const legacy = this.processes.get(legacyKey);
8490
+ if (legacy?.scriptName === scriptName || !legacy?.scriptName && scriptName === "dev") {
8491
+ if (legacy && !hadHandle2) {
8492
+ legacy.kill();
8493
+ hadHandle2 = true;
8494
+ }
8495
+ this.processes.delete(legacyKey);
8496
+ }
8497
+ const run2 = shared.activeRuns.find((r) => r.scriptName === scriptName);
8498
+ if (!hadHandle2 && run2) {
8499
+ killListenersOnPorts(collectActiveRunPorts([run2]));
8500
+ }
8461
8501
  const remaining = shared.activeRuns.filter((r) => r.scriptName !== scriptName);
8462
- const isPrimary = shared.devPort != null && shared.activeRuns.find((r) => r.scriptName === scriptName)?.port === shared.devPort;
8502
+ const isPrimary = shared.devPort != null && run2?.port === shared.devPort;
8463
8503
  this.syncWorktreeRuns(
8464
8504
  thread.worktreePath,
8465
8505
  remaining,
@@ -8468,15 +8508,66 @@ var Orchestrator = class {
8468
8508
  this.emit({ type: "dev_server_stopped", threadId: thread.id, scriptName });
8469
8509
  return;
8470
8510
  }
8511
+ let hadHandle = false;
8471
8512
  for (const [key, proc] of [...this.processes.entries()]) {
8472
8513
  if (isWorktreeRunProcessKey(key, thread.worktreePath) || siblings.some((t) => key.startsWith(`${t.id}:run:`) || key === `${t.id}:dev`)) {
8473
8514
  proc.kill();
8515
+ hadHandle = true;
8474
8516
  this.processes.delete(key);
8475
8517
  }
8476
8518
  }
8519
+ if (!hadHandle && shared.activeRuns.length > 0) {
8520
+ killListenersOnPorts(collectActiveRunPorts(shared.activeRuns));
8521
+ }
8477
8522
  this.syncWorktreeRuns(thread.worktreePath, [], null);
8478
8523
  this.emit({ type: "dev_server_stopped", threadId: thread.id });
8479
8524
  }
8525
+ /**
8526
+ * App quit: tear down every registered run/setup child and free persisted
8527
+ * ports so the next launch is not blocked by orphans.
8528
+ */
8529
+ stopAllRunScripts() {
8530
+ for (const [key, proc] of [...this.processes.entries()]) {
8531
+ if (proc.kind !== "dev" && proc.kind !== "setup") continue;
8532
+ try {
8533
+ proc.kill();
8534
+ } catch {
8535
+ }
8536
+ this.processes.delete(key);
8537
+ }
8538
+ this.reapOrphanedRunScripts({ force: true });
8539
+ }
8540
+ /**
8541
+ * After restart (or force quit), in-memory handles are gone but `activeRuns`
8542
+ * and OS listeners may remain. Kill by port and clear disk metadata.
8543
+ * Skips worktrees that still have a live handle unless `force` (quit path).
8544
+ */
8545
+ reapOrphanedRunScripts(opts) {
8546
+ const force = opts?.force === true;
8547
+ const seen = /* @__PURE__ */ new Set();
8548
+ for (const thread of listThreads({ includeArchived: true })) {
8549
+ if (thread.status === "archived") continue;
8550
+ if (isGlobalThread(thread)) continue;
8551
+ const wt = normalizeWorktreePath(thread.worktreePath);
8552
+ if (seen.has(wt)) continue;
8553
+ seen.add(wt);
8554
+ const siblings = threadsSharingWorktree(thread.worktreePath);
8555
+ const shared = mergeWorktreeActiveRuns(siblings);
8556
+ if (shared.activeRuns.length === 0 && shared.devPort == null) continue;
8557
+ const hasLiveHandle = [...this.processes.keys()].some(
8558
+ (k) => isWorktreeRunProcessKey(k, thread.worktreePath) || siblings.some((t) => k.startsWith(`${t.id}:run:`) || k === `${t.id}:dev`)
8559
+ );
8560
+ if (!force && hasLiveHandle) continue;
8561
+ killListenersOnPorts(collectActiveRunPorts(shared.activeRuns));
8562
+ if (shared.devPort != null) {
8563
+ killListenersOnPorts([shared.devPort]);
8564
+ }
8565
+ this.syncWorktreeRuns(thread.worktreePath, [], null);
8566
+ for (const sibling of siblings) {
8567
+ this.emit({ type: "dev_server_stopped", threadId: sibling.id });
8568
+ }
8569
+ }
8570
+ }
8480
8571
  syncWorktreeRuns(worktreePath, activeRuns, devPort) {
8481
8572
  for (const sibling of threadsSharingWorktree(worktreePath)) {
8482
8573
  updateThread(sibling.id, { activeRuns, devPort });
@@ -482,7 +482,7 @@ async function continueSourceThread(threadId, prompt) {
482
482
  await continueOnReply(threadId, prompt);
483
483
  return;
484
484
  }
485
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-O3FNYIRE.js");
485
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-IEU7U3RP.js");
486
486
  await getOrchestrator2().send(threadId, prompt);
487
487
  } catch {
488
488
  }
@@ -1202,6 +1202,7 @@ import {
1202
1202
  readdirSync,
1203
1203
  readFileSync as readFileSync4
1204
1204
  } from "fs";
1205
+ import { execFileSync } from "child_process";
1205
1206
  import { createServer } from "net";
1206
1207
  import { basename, dirname as dirname2, join as join7 } from "path";
1207
1208
  import { execa as execa2 } from "execa";
@@ -2235,6 +2236,34 @@ function pipeLines(stream, onLine) {
2235
2236
  const rl = createInterface2({ input: stream });
2236
2237
  rl.on("line", onLine);
2237
2238
  }
2239
+ function killListenersOnPorts(ports) {
2240
+ for (const port of ports) {
2241
+ if (!Number.isFinite(port) || port <= 0) continue;
2242
+ try {
2243
+ if (process.platform === "win32") {
2244
+ execFileSync(
2245
+ "powershell",
2246
+ [
2247
+ "-NoProfile",
2248
+ "-Command",
2249
+ `Get-NetTCPConnection -LocalPort ${port} -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }`
2250
+ ],
2251
+ { stdio: "ignore" }
2252
+ );
2253
+ } else {
2254
+ execFileSync(
2255
+ "zsh",
2256
+ [
2257
+ "-lc",
2258
+ `pids=$(lsof -tiTCP:${port} -sTCP:LISTEN 2>/dev/null); [ -n "$pids" ] && kill -TERM $pids 2>/dev/null; true`
2259
+ ],
2260
+ { stdio: "ignore" }
2261
+ );
2262
+ }
2263
+ } catch {
2264
+ }
2265
+ }
2266
+ }
2238
2267
  function killScriptTree(child, ports = []) {
2239
2268
  const pid = child.pid;
2240
2269
  if (pid) {
@@ -2257,26 +2286,7 @@ function killScriptTree(child, ports = []) {
2257
2286
  }
2258
2287
  }
2259
2288
  }
2260
- for (const port of ports) {
2261
- if (!Number.isFinite(port) || port <= 0) continue;
2262
- if (process.platform === "win32") {
2263
- void execa2(
2264
- "powershell",
2265
- [
2266
- "-NoProfile",
2267
- "-Command",
2268
- `Get-NetTCPConnection -LocalPort ${port} -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }`
2269
- ],
2270
- { reject: false }
2271
- );
2272
- } else {
2273
- void execa2(
2274
- "zsh",
2275
- ["-lc", `pids=$(lsof -tiTCP:${port} -sTCP:LISTEN 2>/dev/null); [ -n "$pids" ] && kill -TERM $pids 2>/dev/null; true`],
2276
- { reject: false }
2277
- );
2278
- }
2279
- }
2289
+ killListenersOnPorts(ports);
2280
2290
  }
2281
2291
  async function spawnWorkspaceScript(command, opts) {
2282
2292
  const loginEnv = await captureLoginEnv();
@@ -4554,7 +4564,7 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
4554
4564
  }
4555
4565
 
4556
4566
  // src/threads/adopt.ts
4557
- import { execFileSync } from "child_process";
4567
+ import { execFileSync as execFileSync2 } from "child_process";
4558
4568
  import {
4559
4569
  copyFileSync as copyFileSync2,
4560
4570
  existsSync as existsSync12,
@@ -4821,7 +4831,7 @@ function importConductorWorkspace(workspaceId) {
4821
4831
  }
4822
4832
  let repoPath = worktreePath;
4823
4833
  try {
4824
- repoPath = execFileSync("git", ["rev-parse", "--show-toplevel"], {
4834
+ repoPath = execFileSync2("git", ["rev-parse", "--show-toplevel"], {
4825
4835
  cwd: worktreePath,
4826
4836
  encoding: "utf8"
4827
4837
  }).trim();
@@ -7332,7 +7342,7 @@ function formatScheduledPrompt(name, prompt) {
7332
7342
  ${prompt}`;
7333
7343
  }
7334
7344
  async function defaultDeps() {
7335
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-O3FNYIRE.js");
7345
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-IEU7U3RP.js");
7336
7346
  const orch = getOrchestrator2();
7337
7347
  return {
7338
7348
  findThread: (id) => findThreadByRef(id),
@@ -7469,6 +7479,16 @@ function writeLiveStatus(threadId, status, lastError) {
7469
7479
  if (!latest || latest.status === "archived") return latest;
7470
7480
  return setStatus(threadId, status, lastError);
7471
7481
  }
7482
+ function collectActiveRunPorts(runs) {
7483
+ const ports = /* @__PURE__ */ new Set();
7484
+ for (const run2 of runs) {
7485
+ const list = run2.ports?.length ? run2.ports : run2.port != null ? [run2.port] : [];
7486
+ for (const p of list) {
7487
+ if (Number.isFinite(p) && p > 0) ports.add(p);
7488
+ }
7489
+ }
7490
+ return [...ports];
7491
+ }
7472
7492
  var Orchestrator = class {
7473
7493
  events = new EventEmitter();
7474
7494
  processes = /* @__PURE__ */ new Map();
@@ -7610,6 +7630,9 @@ var Orchestrator = class {
7610
7630
  this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
7611
7631
  }
7612
7632
  }
7633
+ if (reclaimStaleTurns) {
7634
+ this.reapOrphanedRunScripts();
7635
+ }
7613
7636
  const repoPaths = (repoPath ? [repoPath] : [...new Set(listThreads({ includeArchived: true }).map((t) => t.repoPath))]).filter((p) => !isGlobalRepoPath(p));
7614
7637
  await this.reconcileOrphans(repoPaths);
7615
7638
  try {
@@ -8828,13 +8851,30 @@ var Orchestrator = class {
8828
8851
  worktreeRunProcessKey(thread.worktreePath, scriptName),
8829
8852
  ...siblings.map((t) => `${t.id}:run:${scriptName}`)
8830
8853
  ];
8854
+ let hadHandle2 = false;
8831
8855
  for (const key of keys) {
8832
8856
  const proc = this.processes.get(key);
8833
- if (proc) proc.kill();
8857
+ if (proc) {
8858
+ proc.kill();
8859
+ hadHandle2 = true;
8860
+ }
8834
8861
  this.processes.delete(key);
8835
8862
  }
8863
+ const legacyKey = worktreeDevProcessKey(thread.worktreePath);
8864
+ const legacy = this.processes.get(legacyKey);
8865
+ if (legacy?.scriptName === scriptName || !legacy?.scriptName && scriptName === "dev") {
8866
+ if (legacy && !hadHandle2) {
8867
+ legacy.kill();
8868
+ hadHandle2 = true;
8869
+ }
8870
+ this.processes.delete(legacyKey);
8871
+ }
8872
+ const run2 = shared.activeRuns.find((r) => r.scriptName === scriptName);
8873
+ if (!hadHandle2 && run2) {
8874
+ killListenersOnPorts(collectActiveRunPorts([run2]));
8875
+ }
8836
8876
  const remaining = shared.activeRuns.filter((r) => r.scriptName !== scriptName);
8837
- const isPrimary = shared.devPort != null && shared.activeRuns.find((r) => r.scriptName === scriptName)?.port === shared.devPort;
8877
+ const isPrimary = shared.devPort != null && run2?.port === shared.devPort;
8838
8878
  this.syncWorktreeRuns(
8839
8879
  thread.worktreePath,
8840
8880
  remaining,
@@ -8843,15 +8883,66 @@ var Orchestrator = class {
8843
8883
  this.emit({ type: "dev_server_stopped", threadId: thread.id, scriptName });
8844
8884
  return;
8845
8885
  }
8886
+ let hadHandle = false;
8846
8887
  for (const [key, proc] of [...this.processes.entries()]) {
8847
8888
  if (isWorktreeRunProcessKey(key, thread.worktreePath) || siblings.some((t) => key.startsWith(`${t.id}:run:`) || key === `${t.id}:dev`)) {
8848
8889
  proc.kill();
8890
+ hadHandle = true;
8849
8891
  this.processes.delete(key);
8850
8892
  }
8851
8893
  }
8894
+ if (!hadHandle && shared.activeRuns.length > 0) {
8895
+ killListenersOnPorts(collectActiveRunPorts(shared.activeRuns));
8896
+ }
8852
8897
  this.syncWorktreeRuns(thread.worktreePath, [], null);
8853
8898
  this.emit({ type: "dev_server_stopped", threadId: thread.id });
8854
8899
  }
8900
+ /**
8901
+ * App quit: tear down every registered run/setup child and free persisted
8902
+ * ports so the next launch is not blocked by orphans.
8903
+ */
8904
+ stopAllRunScripts() {
8905
+ for (const [key, proc] of [...this.processes.entries()]) {
8906
+ if (proc.kind !== "dev" && proc.kind !== "setup") continue;
8907
+ try {
8908
+ proc.kill();
8909
+ } catch {
8910
+ }
8911
+ this.processes.delete(key);
8912
+ }
8913
+ this.reapOrphanedRunScripts({ force: true });
8914
+ }
8915
+ /**
8916
+ * After restart (or force quit), in-memory handles are gone but `activeRuns`
8917
+ * and OS listeners may remain. Kill by port and clear disk metadata.
8918
+ * Skips worktrees that still have a live handle unless `force` (quit path).
8919
+ */
8920
+ reapOrphanedRunScripts(opts) {
8921
+ const force = opts?.force === true;
8922
+ const seen = /* @__PURE__ */ new Set();
8923
+ for (const thread of listThreads({ includeArchived: true })) {
8924
+ if (thread.status === "archived") continue;
8925
+ if (isGlobalThread(thread)) continue;
8926
+ const wt = normalizeWorktreePath(thread.worktreePath);
8927
+ if (seen.has(wt)) continue;
8928
+ seen.add(wt);
8929
+ const siblings = threadsSharingWorktree(thread.worktreePath);
8930
+ const shared = mergeWorktreeActiveRuns(siblings);
8931
+ if (shared.activeRuns.length === 0 && shared.devPort == null) continue;
8932
+ const hasLiveHandle = [...this.processes.keys()].some(
8933
+ (k) => isWorktreeRunProcessKey(k, thread.worktreePath) || siblings.some((t) => k.startsWith(`${t.id}:run:`) || k === `${t.id}:dev`)
8934
+ );
8935
+ if (!force && hasLiveHandle) continue;
8936
+ killListenersOnPorts(collectActiveRunPorts(shared.activeRuns));
8937
+ if (shared.devPort != null) {
8938
+ killListenersOnPorts([shared.devPort]);
8939
+ }
8940
+ this.syncWorktreeRuns(thread.worktreePath, [], null);
8941
+ for (const sibling of siblings) {
8942
+ this.emit({ type: "dev_server_stopped", threadId: sibling.id });
8943
+ }
8944
+ }
8945
+ }
8855
8946
  syncWorktreeRuns(worktreePath, activeRuns, devPort) {
8856
8947
  for (const sibling of threadsSharingWorktree(worktreePath)) {
8857
8948
  updateThread(sibling.id, { activeRuns, devPort });
@@ -9924,6 +10015,7 @@ export {
9924
10015
  copyConfiguredFiles,
9925
10016
  captureLoginEnv,
9926
10017
  buildWorkspaceScriptEnv,
10018
+ killListenersOnPorts,
9927
10019
  runSetupScript,
9928
10020
  runConventionSetup,
9929
10021
  runWorkspaceSetup,