@sideboard-ai/core 0.1.92 → 0.1.96

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.
@@ -6361,6 +6361,7 @@ function toCursorMcpServers(servers) {
6361
6361
  const env = mcpSpawnEnv(s.env);
6362
6362
  const launch = cursorSafeMcpLaunch(s.command, s.args);
6363
6363
  out[s.name] = {
6364
+ type: "stdio",
6364
6365
  command: launch.command,
6365
6366
  ...launch.args && launch.args.length > 0 ? { args: launch.args } : {},
6366
6367
  ...env ? { env } : {}
@@ -12123,23 +12124,24 @@ async function countUnpushedCommits(worktreePath) {
12123
12124
  reject: false
12124
12125
  });
12125
12126
  const branch = head.stdout.trim();
12127
+ if (branch && branch !== "HEAD") {
12128
+ const remote = await git(
12129
+ ["rev-list", "--count", `origin/${branch}..HEAD`],
12130
+ worktreePath,
12131
+ { reject: false }
12132
+ );
12133
+ if (remote.exitCode === 0) {
12134
+ const n2 = Number(remote.stdout.trim());
12135
+ return Number.isFinite(n2) ? n2 : 0;
12136
+ }
12137
+ }
12126
12138
  const upstream = await git(
12127
12139
  ["rev-list", "--count", "@{upstream}..HEAD"],
12128
12140
  worktreePath,
12129
12141
  { reject: false }
12130
12142
  );
12131
- if (upstream.exitCode === 0) {
12132
- const n2 = Number(upstream.stdout.trim());
12133
- return Number.isFinite(n2) ? n2 : 0;
12134
- }
12135
- if (!branch || branch === "HEAD") return 0;
12136
- const remote = await git(
12137
- ["rev-list", "--count", `origin/${branch}..HEAD`],
12138
- worktreePath,
12139
- { reject: false }
12140
- );
12141
- if (remote.exitCode !== 0) return 0;
12142
- const n = Number(remote.stdout.trim());
12143
+ if (upstream.exitCode !== 0) return 0;
12144
+ const n = Number(upstream.stdout.trim());
12143
12145
  return Number.isFinite(n) ? n : 0;
12144
12146
  }
12145
12147
  function splitCombinedDiff(stdout) {
@@ -13465,6 +13467,11 @@ function isPidAlive(pid) {
13465
13467
  return false;
13466
13468
  }
13467
13469
  }
13470
+ function writeLiveStatus(threadId, status, lastError) {
13471
+ const latest = readThread(threadId);
13472
+ if (!latest || latest.status === "archived") return latest;
13473
+ return setStatus(threadId, status, lastError);
13474
+ }
13468
13475
  var Orchestrator = class {
13469
13476
  events = new import_node_events.EventEmitter();
13470
13477
  processes = /* @__PURE__ */ new Map();
@@ -13770,6 +13777,9 @@ var Orchestrator = class {
13770
13777
  }
13771
13778
  async send(threadRef, prompt) {
13772
13779
  const thread = this.requireThread(threadRef);
13780
+ if (thread.status === "archived") {
13781
+ throw new Error(`Thread is archived: ${thread.id}`);
13782
+ }
13773
13783
  return withThreadLock(thread.id, async () => {
13774
13784
  const current = this.requireThread(thread.id);
13775
13785
  const queue = [...current.queue, prompt];
@@ -13863,7 +13873,7 @@ var Orchestrator = class {
13863
13873
  break;
13864
13874
  }
13865
13875
  const thread = readThread(threadId);
13866
- if (!thread || thread.queue.length === 0) {
13876
+ if (!thread || thread.status === "archived" || thread.queue.length === 0) {
13867
13877
  if (thread && thread.status === "queued") {
13868
13878
  setStatus(threadId, "idle");
13869
13879
  this.emit({ type: "status_changed", threadId, status: "idle" });
@@ -13894,14 +13904,17 @@ var Orchestrator = class {
13894
13904
  }
13895
13905
  }
13896
13906
  async runTurn(threadId, prompt) {
13897
- let thread = this.requireThread(threadId);
13907
+ const existing = readThread(threadId);
13908
+ if (!existing || existing.status === "archived") return;
13909
+ let thread = existing;
13898
13910
  if (isGlobalThread(thread) && thread.sessionId && orchestratorSessionPoisonedByBuiltins(thread)) {
13899
13911
  thread = updateThread(threadId, { sessionId: null });
13900
13912
  }
13913
+ const running = writeLiveStatus(threadId, "running");
13914
+ if (!running || running.status !== "running") return;
13901
13915
  this.runningCount += 1;
13902
13916
  const turnStartedAt = Date.now();
13903
13917
  this.startingTurns.add(threadId);
13904
- setStatus(threadId, "running");
13905
13918
  this.emit({ type: "status_changed", threadId, status: "running" });
13906
13919
  this.emit({ type: "turn_started", threadId, prompt });
13907
13920
  try {
@@ -14172,8 +14185,8 @@ var Orchestrator = class {
14172
14185
  ts: (/* @__PURE__ */ new Date()).toISOString()
14173
14186
  });
14174
14187
  }
14175
- const afterTurn = this.requireThread(threadId);
14176
- if (afterTurn.planMode && afterTurn.worktreePath?.trim()) {
14188
+ const afterTurn = readThread(threadId);
14189
+ if (afterTurn && afterTurn.status !== "archived" && afterTurn.planMode && afterTurn.worktreePath?.trim()) {
14177
14190
  const presented = extractPresentedPlan(parts);
14178
14191
  const exited = parts.some(
14179
14192
  (p) => p.type === "tool" && /exitplanmode/i.test(p.name)
@@ -14186,29 +14199,34 @@ var Orchestrator = class {
14186
14199
  }
14187
14200
  }
14188
14201
  }
14189
- if (afterTurn.planMode && afterTurn.agent === "claude" && parts.some(
14202
+ if (afterTurn && afterTurn.status !== "archived" && afterTurn.planMode && afterTurn.agent === "claude" && parts.some(
14190
14203
  (p) => p.type === "tool" && /exitplanmode/i.test(p.name)
14191
14204
  )) {
14192
14205
  updateThread(threadId, { sessionId: null });
14193
14206
  }
14194
14207
  await syncThreadBranchFromGit(threadId);
14195
14208
  if (this.stoppedTurns.has(threadId)) {
14196
- setStatus(threadId, "stopped");
14197
- this.emit({ type: "status_changed", threadId, status: "stopped" });
14209
+ const stopped = writeLiveStatus(threadId, "stopped");
14210
+ if (stopped?.status === "stopped") {
14211
+ this.emit({ type: "status_changed", threadId, status: "stopped" });
14212
+ }
14198
14213
  this.emit({ type: "turn_finished", threadId, exitCode });
14199
14214
  } else {
14200
14215
  const failDetail = formatTurnExitError(exitCode, detail);
14201
14216
  const explainedInChat = exitCode !== 0 && Boolean(chatText) && (looksLikeAgentFailureMessage(chatText) || failDetail && chatText.includes(failDetail.replace(/^exit\s*\d+:\s*/i, "").trim()));
14202
- setStatus(
14217
+ const nextStatus = exitCode === 0 ? "idle" : "error";
14218
+ const written = writeLiveStatus(
14203
14219
  threadId,
14204
- exitCode === 0 ? "idle" : "error",
14220
+ nextStatus,
14205
14221
  exitCode === 0 || explainedInChat ? null : failDetail
14206
14222
  );
14207
- this.emit({
14208
- type: "status_changed",
14209
- threadId,
14210
- status: exitCode === 0 ? "idle" : "error"
14211
- });
14223
+ if (written && written.status !== "archived") {
14224
+ this.emit({
14225
+ type: "status_changed",
14226
+ threadId,
14227
+ status: written.status
14228
+ });
14229
+ }
14212
14230
  this.emit({ type: "turn_finished", threadId, exitCode });
14213
14231
  if (exitCode !== 0) {
14214
14232
  const blob = [chatText, detail].filter(Boolean).join("\n");
@@ -14219,13 +14237,17 @@ var Orchestrator = class {
14219
14237
  const message = err instanceof Error ? err.message : String(err);
14220
14238
  await syncThreadBranchFromGit(threadId).catch(() => void 0);
14221
14239
  if (this.stoppedTurns.has(threadId)) {
14222
- setStatus(threadId, "stopped");
14223
- this.emit({ type: "status_changed", threadId, status: "stopped" });
14240
+ const stopped = writeLiveStatus(threadId, "stopped");
14241
+ if (stopped?.status === "stopped") {
14242
+ this.emit({ type: "status_changed", threadId, status: "stopped" });
14243
+ }
14224
14244
  this.emit({ type: "turn_finished", threadId, exitCode: 1 });
14225
14245
  } else {
14226
- setStatus(threadId, "error", message);
14227
- this.emit({ type: "error", threadId, message });
14228
- this.emit({ type: "status_changed", threadId, status: "error" });
14246
+ const written = writeLiveStatus(threadId, "error", message);
14247
+ if (written?.status === "error") {
14248
+ this.emit({ type: "error", threadId, message });
14249
+ this.emit({ type: "status_changed", threadId, status: "error" });
14250
+ }
14229
14251
  this.emit({ type: "turn_finished", threadId, exitCode: 1 });
14230
14252
  void this.maybeHandleOrchestrationQuotaFailover(threadId, message);
14231
14253
  }
@@ -14272,9 +14294,11 @@ var Orchestrator = class {
14272
14294
  if (handle) handle.kill();
14273
14295
  const proc = this.processes.get(`${thread.id}:agent`);
14274
14296
  if (proc) proc.kill();
14275
- setStatus(thread.id, "stopped");
14276
- this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
14277
- return this.requireThread(thread.id);
14297
+ const stopped = writeLiveStatus(thread.id, "stopped") ?? readThread(thread.id) ?? thread;
14298
+ if (stopped.status === "stopped") {
14299
+ this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
14300
+ }
14301
+ return stopped;
14278
14302
  }
14279
14303
  async startDev(threadRef, scriptName) {
14280
14304
  const thread = this.requireThread(threadRef);
@@ -14510,11 +14534,21 @@ var Orchestrator = class {
14510
14534
  const off = this.on((event) => {
14511
14535
  if (event.type === "turn_finished" && event.threadId === thread.id) {
14512
14536
  off();
14513
- resolve(this.requireThread(thread.id));
14537
+ const latest = readThread(thread.id);
14538
+ if (!latest) {
14539
+ reject(new Error(`Thread not found: ${thread.id}`));
14540
+ return;
14541
+ }
14542
+ resolve(latest);
14514
14543
  }
14515
14544
  if (event.type === "error" && event.threadId === thread.id) {
14516
14545
  off();
14517
- resolve(this.requireThread(thread.id));
14546
+ const latest = readThread(thread.id);
14547
+ if (!latest) {
14548
+ reject(new Error(`Thread not found: ${thread.id}`));
14549
+ return;
14550
+ }
14551
+ resolve(latest);
14518
14552
  }
14519
14553
  });
14520
14554
  const timer = setInterval(() => {
@@ -14524,7 +14558,13 @@ var Orchestrator = class {
14524
14558
  reject(new Error("wait_for_turn timed out"));
14525
14559
  }
14526
14560
  const current = readThread(thread.id);
14527
- if (current && !["running", "queued"].includes(current.status)) {
14561
+ if (!current) {
14562
+ clearInterval(timer);
14563
+ off();
14564
+ reject(new Error(`Thread not found: ${thread.id}`));
14565
+ return;
14566
+ }
14567
+ if (!["running", "queued"].includes(current.status)) {
14528
14568
  clearInterval(timer);
14529
14569
  off();
14530
14570
  resolve(current);
@@ -17,7 +17,7 @@ import {
17
17
  resolveQuotaFallbackAgent,
18
18
  sideboardMcpProfile,
19
19
  summarizeTurnStderr
20
- } from "../chunk-GJ2HJQIU.js";
20
+ } from "../chunk-WBX46OPD.js";
21
21
  import "../chunk-DKHGWYWR.js";
22
22
  import {
23
23
  addWorkspace,
@@ -1851,7 +1851,7 @@ async function createThread(input, _onSetupLine) {
1851
1851
  return readThread(thread.id) ?? thread;
1852
1852
  }
1853
1853
  async function listLinearIssues(agent, repoPath) {
1854
- const { getAdapter: getAdapter2 } = await import("../agents-JXQ5QHDU.js");
1854
+ const { getAdapter: getAdapter2 } = await import("../agents-HBLA6FEV.js");
1855
1855
  await requireAgent(agent, { requireLinear: true });
1856
1856
  const adapter = getAdapter2(agent);
1857
1857
  if (!adapter.listLinearIssues) {
@@ -3300,23 +3300,24 @@ async function countUnpushedCommits(worktreePath) {
3300
3300
  reject: false
3301
3301
  });
3302
3302
  const branch = head.stdout.trim();
3303
+ if (branch && branch !== "HEAD") {
3304
+ const remote = await git(
3305
+ ["rev-list", "--count", `origin/${branch}..HEAD`],
3306
+ worktreePath,
3307
+ { reject: false }
3308
+ );
3309
+ if (remote.exitCode === 0) {
3310
+ const n2 = Number(remote.stdout.trim());
3311
+ return Number.isFinite(n2) ? n2 : 0;
3312
+ }
3313
+ }
3303
3314
  const upstream = await git(
3304
3315
  ["rev-list", "--count", "@{upstream}..HEAD"],
3305
3316
  worktreePath,
3306
3317
  { reject: false }
3307
3318
  );
3308
- if (upstream.exitCode === 0) {
3309
- const n2 = Number(upstream.stdout.trim());
3310
- return Number.isFinite(n2) ? n2 : 0;
3311
- }
3312
- if (!branch || branch === "HEAD") return 0;
3313
- const remote = await git(
3314
- ["rev-list", "--count", `origin/${branch}..HEAD`],
3315
- worktreePath,
3316
- { reject: false }
3317
- );
3318
- if (remote.exitCode !== 0) return 0;
3319
- const n = Number(remote.stdout.trim());
3319
+ if (upstream.exitCode !== 0) return 0;
3320
+ const n = Number(upstream.stdout.trim());
3320
3321
  return Number.isFinite(n) ? n : 0;
3321
3322
  }
3322
3323
  function splitCombinedDiff(stdout) {
@@ -4626,6 +4627,11 @@ function isPidAlive(pid) {
4626
4627
  return false;
4627
4628
  }
4628
4629
  }
4630
+ function writeLiveStatus(threadId, status, lastError) {
4631
+ const latest = readThread(threadId);
4632
+ if (!latest || latest.status === "archived") return latest;
4633
+ return setStatus(threadId, status, lastError);
4634
+ }
4629
4635
  var Orchestrator = class {
4630
4636
  events = new EventEmitter();
4631
4637
  processes = /* @__PURE__ */ new Map();
@@ -4931,6 +4937,9 @@ var Orchestrator = class {
4931
4937
  }
4932
4938
  async send(threadRef, prompt) {
4933
4939
  const thread = this.requireThread(threadRef);
4940
+ if (thread.status === "archived") {
4941
+ throw new Error(`Thread is archived: ${thread.id}`);
4942
+ }
4934
4943
  return withThreadLock(thread.id, async () => {
4935
4944
  const current = this.requireThread(thread.id);
4936
4945
  const queue = [...current.queue, prompt];
@@ -5024,7 +5033,7 @@ var Orchestrator = class {
5024
5033
  break;
5025
5034
  }
5026
5035
  const thread = readThread(threadId);
5027
- if (!thread || thread.queue.length === 0) {
5036
+ if (!thread || thread.status === "archived" || thread.queue.length === 0) {
5028
5037
  if (thread && thread.status === "queued") {
5029
5038
  setStatus(threadId, "idle");
5030
5039
  this.emit({ type: "status_changed", threadId, status: "idle" });
@@ -5055,14 +5064,17 @@ var Orchestrator = class {
5055
5064
  }
5056
5065
  }
5057
5066
  async runTurn(threadId, prompt) {
5058
- let thread = this.requireThread(threadId);
5067
+ const existing = readThread(threadId);
5068
+ if (!existing || existing.status === "archived") return;
5069
+ let thread = existing;
5059
5070
  if (isGlobalThread(thread) && thread.sessionId && orchestratorSessionPoisonedByBuiltins(thread)) {
5060
5071
  thread = updateThread(threadId, { sessionId: null });
5061
5072
  }
5073
+ const running = writeLiveStatus(threadId, "running");
5074
+ if (!running || running.status !== "running") return;
5062
5075
  this.runningCount += 1;
5063
5076
  const turnStartedAt = Date.now();
5064
5077
  this.startingTurns.add(threadId);
5065
- setStatus(threadId, "running");
5066
5078
  this.emit({ type: "status_changed", threadId, status: "running" });
5067
5079
  this.emit({ type: "turn_started", threadId, prompt });
5068
5080
  try {
@@ -5333,8 +5345,8 @@ var Orchestrator = class {
5333
5345
  ts: (/* @__PURE__ */ new Date()).toISOString()
5334
5346
  });
5335
5347
  }
5336
- const afterTurn = this.requireThread(threadId);
5337
- if (afterTurn.planMode && afterTurn.worktreePath?.trim()) {
5348
+ const afterTurn = readThread(threadId);
5349
+ if (afterTurn && afterTurn.status !== "archived" && afterTurn.planMode && afterTurn.worktreePath?.trim()) {
5338
5350
  const presented = extractPresentedPlan(parts);
5339
5351
  const exited = parts.some(
5340
5352
  (p) => p.type === "tool" && /exitplanmode/i.test(p.name)
@@ -5347,29 +5359,34 @@ var Orchestrator = class {
5347
5359
  }
5348
5360
  }
5349
5361
  }
5350
- if (afterTurn.planMode && afterTurn.agent === "claude" && parts.some(
5362
+ if (afterTurn && afterTurn.status !== "archived" && afterTurn.planMode && afterTurn.agent === "claude" && parts.some(
5351
5363
  (p) => p.type === "tool" && /exitplanmode/i.test(p.name)
5352
5364
  )) {
5353
5365
  updateThread(threadId, { sessionId: null });
5354
5366
  }
5355
5367
  await syncThreadBranchFromGit(threadId);
5356
5368
  if (this.stoppedTurns.has(threadId)) {
5357
- setStatus(threadId, "stopped");
5358
- this.emit({ type: "status_changed", threadId, status: "stopped" });
5369
+ const stopped = writeLiveStatus(threadId, "stopped");
5370
+ if (stopped?.status === "stopped") {
5371
+ this.emit({ type: "status_changed", threadId, status: "stopped" });
5372
+ }
5359
5373
  this.emit({ type: "turn_finished", threadId, exitCode });
5360
5374
  } else {
5361
5375
  const failDetail = formatTurnExitError(exitCode, detail);
5362
5376
  const explainedInChat = exitCode !== 0 && Boolean(chatText) && (looksLikeAgentFailureMessage(chatText) || failDetail && chatText.includes(failDetail.replace(/^exit\s*\d+:\s*/i, "").trim()));
5363
- setStatus(
5377
+ const nextStatus = exitCode === 0 ? "idle" : "error";
5378
+ const written = writeLiveStatus(
5364
5379
  threadId,
5365
- exitCode === 0 ? "idle" : "error",
5380
+ nextStatus,
5366
5381
  exitCode === 0 || explainedInChat ? null : failDetail
5367
5382
  );
5368
- this.emit({
5369
- type: "status_changed",
5370
- threadId,
5371
- status: exitCode === 0 ? "idle" : "error"
5372
- });
5383
+ if (written && written.status !== "archived") {
5384
+ this.emit({
5385
+ type: "status_changed",
5386
+ threadId,
5387
+ status: written.status
5388
+ });
5389
+ }
5373
5390
  this.emit({ type: "turn_finished", threadId, exitCode });
5374
5391
  if (exitCode !== 0) {
5375
5392
  const blob = [chatText, detail].filter(Boolean).join("\n");
@@ -5380,13 +5397,17 @@ var Orchestrator = class {
5380
5397
  const message = err instanceof Error ? err.message : String(err);
5381
5398
  await syncThreadBranchFromGit(threadId).catch(() => void 0);
5382
5399
  if (this.stoppedTurns.has(threadId)) {
5383
- setStatus(threadId, "stopped");
5384
- this.emit({ type: "status_changed", threadId, status: "stopped" });
5400
+ const stopped = writeLiveStatus(threadId, "stopped");
5401
+ if (stopped?.status === "stopped") {
5402
+ this.emit({ type: "status_changed", threadId, status: "stopped" });
5403
+ }
5385
5404
  this.emit({ type: "turn_finished", threadId, exitCode: 1 });
5386
5405
  } else {
5387
- setStatus(threadId, "error", message);
5388
- this.emit({ type: "error", threadId, message });
5389
- this.emit({ type: "status_changed", threadId, status: "error" });
5406
+ const written = writeLiveStatus(threadId, "error", message);
5407
+ if (written?.status === "error") {
5408
+ this.emit({ type: "error", threadId, message });
5409
+ this.emit({ type: "status_changed", threadId, status: "error" });
5410
+ }
5390
5411
  this.emit({ type: "turn_finished", threadId, exitCode: 1 });
5391
5412
  void this.maybeHandleOrchestrationQuotaFailover(threadId, message);
5392
5413
  }
@@ -5433,9 +5454,11 @@ var Orchestrator = class {
5433
5454
  if (handle) handle.kill();
5434
5455
  const proc = this.processes.get(`${thread.id}:agent`);
5435
5456
  if (proc) proc.kill();
5436
- setStatus(thread.id, "stopped");
5437
- this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
5438
- return this.requireThread(thread.id);
5457
+ const stopped = writeLiveStatus(thread.id, "stopped") ?? readThread(thread.id) ?? thread;
5458
+ if (stopped.status === "stopped") {
5459
+ this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
5460
+ }
5461
+ return stopped;
5439
5462
  }
5440
5463
  async startDev(threadRef, scriptName) {
5441
5464
  const thread = this.requireThread(threadRef);
@@ -5671,11 +5694,21 @@ var Orchestrator = class {
5671
5694
  const off = this.on((event) => {
5672
5695
  if (event.type === "turn_finished" && event.threadId === thread.id) {
5673
5696
  off();
5674
- resolve(this.requireThread(thread.id));
5697
+ const latest = readThread(thread.id);
5698
+ if (!latest) {
5699
+ reject(new Error(`Thread not found: ${thread.id}`));
5700
+ return;
5701
+ }
5702
+ resolve(latest);
5675
5703
  }
5676
5704
  if (event.type === "error" && event.threadId === thread.id) {
5677
5705
  off();
5678
- resolve(this.requireThread(thread.id));
5706
+ const latest = readThread(thread.id);
5707
+ if (!latest) {
5708
+ reject(new Error(`Thread not found: ${thread.id}`));
5709
+ return;
5710
+ }
5711
+ resolve(latest);
5679
5712
  }
5680
5713
  });
5681
5714
  const timer = setInterval(() => {
@@ -5685,7 +5718,13 @@ var Orchestrator = class {
5685
5718
  reject(new Error("wait_for_turn timed out"));
5686
5719
  }
5687
5720
  const current = readThread(thread.id);
5688
- if (current && !["running", "queued"].includes(current.status)) {
5721
+ if (!current) {
5722
+ clearInterval(timer);
5723
+ off();
5724
+ reject(new Error(`Thread not found: ${thread.id}`));
5725
+ return;
5726
+ }
5727
+ if (!["running", "queued"].includes(current.status)) {
5689
5728
  clearInterval(timer);
5690
5729
  off();
5691
5730
  resolve(current);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sideboard-ai/core",
3
- "version": "0.1.92",
3
+ "version": "0.1.96",
4
4
  "description": "Sideboard core — orchestration, agents, git worktrees, MCP server",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",