@sideboard-ai/core 0.1.127 → 0.1.132

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.
@@ -18,7 +18,7 @@ import {
18
18
  stripBrightsyNdjsonNoise,
19
19
  sumUsageList,
20
20
  toolDescription
21
- } from "./chunk-NV73AO7Q.js";
21
+ } from "./chunk-MGYCQAZ6.js";
22
22
  import {
23
23
  addWorkspace,
24
24
  ensureWorkspace,
@@ -56,15 +56,17 @@ import {
56
56
  import {
57
57
  applyAgentRunnerHeapEnv,
58
58
  fallbackTurnFailDetail,
59
+ formatAgentErrorContinuePrompt,
59
60
  formatTurnExitError,
60
61
  looksLikeAgentFailureMessage,
61
62
  looksLikeInvalidAgentSession,
62
63
  looksLikeV8Oom,
63
64
  pushTurnStderr,
65
+ shouldFeedErrorBackToAgent,
64
66
  shouldRetryFailedAgentTurn,
65
67
  summarizeTurnStderr,
66
68
  turnFailChatText
67
- } from "./chunk-IUJOI7KK.js";
69
+ } from "./chunk-FSCQXMGI.js";
68
70
  import {
69
71
  releaseCaffeinateHoldForThread
70
72
  } from "./chunk-DCOZABNT.js";
@@ -441,7 +443,7 @@ async function continueSourceThread(threadId, prompt) {
441
443
  await continueOnReply(threadId, prompt);
442
444
  return;
443
445
  }
444
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-6GUCE4LY.js");
446
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-5TN2DJWO.js");
445
447
  await getOrchestrator2().send(threadId, prompt);
446
448
  } catch {
447
449
  }
@@ -2320,7 +2322,49 @@ function isMergedPrState(prUrl, prState) {
2320
2322
  if (!prUrl?.trim()) return false;
2321
2323
  return (prState ?? "").trim().toUpperCase() === "MERGED";
2322
2324
  }
2323
- function groupHomeBoardWorktrees(threads) {
2325
+ var DEFAULT_WORKTREE_SORT = "created";
2326
+ function groupCreatedAt(group) {
2327
+ let min = group[0]?.createdAt ?? "";
2328
+ for (const t of group) {
2329
+ if (t.createdAt && t.createdAt < min) min = t.createdAt;
2330
+ }
2331
+ return min;
2332
+ }
2333
+ function groupUpdatedAt(group) {
2334
+ let max = group[0]?.updatedAt ?? "";
2335
+ for (const t of group) {
2336
+ if (t.updatedAt && t.updatedAt > max) max = t.updatedAt;
2337
+ }
2338
+ return max;
2339
+ }
2340
+ function sortThreadsInWorktree(list) {
2341
+ return [...list].sort((a, b) => {
2342
+ const created = a.createdAt.localeCompare(b.createdAt);
2343
+ if (created !== 0) return created;
2344
+ return a.id.localeCompare(b.id);
2345
+ });
2346
+ }
2347
+ function compareWorktreeGroups(a, b, mode, labelOf) {
2348
+ if (mode === "name") {
2349
+ const named = (labelOf?.(a) ?? a[0]?.id ?? "").localeCompare(
2350
+ labelOf?.(b) ?? b[0]?.id ?? "",
2351
+ void 0,
2352
+ { sensitivity: "base" }
2353
+ );
2354
+ if (named !== 0) return named;
2355
+ } else if (mode === "activity") {
2356
+ const activity = groupUpdatedAt(b).localeCompare(groupUpdatedAt(a));
2357
+ if (activity !== 0) return activity;
2358
+ } else {
2359
+ const created = groupCreatedAt(b).localeCompare(groupCreatedAt(a));
2360
+ if (created !== 0) return created;
2361
+ }
2362
+ return (a[0]?.id ?? "").localeCompare(b[0]?.id ?? "");
2363
+ }
2364
+ function defaultWorktreeGroupLabel(group) {
2365
+ return worktreeDisplayLabelForGroup(group);
2366
+ }
2367
+ function groupHomeBoardWorktrees(threads, sort = DEFAULT_WORKTREE_SORT, labelOf = defaultWorktreeGroupLabel) {
2324
2368
  const map = /* @__PURE__ */ new Map();
2325
2369
  for (const t of threads) {
2326
2370
  const key = normalizeWorktreePath(t.worktreePath);
@@ -2328,7 +2372,7 @@ function groupHomeBoardWorktrees(threads) {
2328
2372
  list.push(t);
2329
2373
  map.set(key, list);
2330
2374
  }
2331
- return [...map.values()].map((list) => list.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))).sort((a, b) => (b[0]?.updatedAt ?? "").localeCompare(a[0]?.updatedAt ?? ""));
2375
+ return [...map.values()].map((list) => sortThreadsInWorktree(list)).sort((a, b) => compareWorktreeGroups(a, b, sort, labelOf));
2332
2376
  }
2333
2377
  function classifyWorktreeColumn(group) {
2334
2378
  if (group.some((t) => isMergedPrState(t.prUrl, t.prState))) return "done";
@@ -2636,8 +2680,7 @@ function assembleHomeBoard(input) {
2636
2680
  const kind = input.kind ?? "all";
2637
2681
  const limit = Math.max(1, input.limit ?? BOARD_PAGE_SIZE);
2638
2682
  const wsName = input.workspaceName ?? (() => "");
2639
- const byUpdated = (a, b) => b.updatedAt.localeCompare(a.updatedAt);
2640
- const live = input.threads.filter((t) => t.status !== "archived" && isHomeBoardThread(t)).sort(byUpdated);
2683
+ const live = input.threads.filter((t) => t.status !== "archived" && isHomeBoardThread(t));
2641
2684
  const columns = emptyColumns();
2642
2685
  const hidden = emptyHidden();
2643
2686
  const totals = {
@@ -2835,7 +2878,7 @@ async function createThread(input, _onSetupLine) {
2835
2878
  return readThread(thread.id) ?? thread;
2836
2879
  }
2837
2880
  async function listLinearIssues(agent, repoPath) {
2838
- const { getAdapter: getAdapter2 } = await import("./agents-X2I3RNCY.js");
2881
+ const { getAdapter: getAdapter2 } = await import("./agents-CI6NGKRO.js");
2839
2882
  await requireAgent(agent, { requireLinear: true });
2840
2883
  const adapter = getAdapter2(agent);
2841
2884
  if (!adapter.listLinearIssues) {
@@ -5145,7 +5188,8 @@ function formatArtifactDirective() {
5145
5188
  "```html",
5146
5189
  "<!DOCTYPE html><html><head><title>Demo</title></head><body><h1>Hi</h1></body></html>",
5147
5190
  "```",
5148
- "2) Or call Sideboard MCP `present_artifact` with title, type (html|svg|markdown), and content \u2014 not both a fence and this tool for the same body.",
5191
+ "2) Or call Sideboard MCP `present_artifact` with title, type (html|svg|markdown|react|log), and content \u2014 not both a fence and this tool for the same body.",
5192
+ " type=log is append-only: same `artifact_id`, `content` = new lines only (plus optional status/phase). Do not resend the full log or wrap it in HTML.",
5149
5193
  "CMS / JSON Schema forms & tables (Brightsy or any schema+schemaUi source):",
5150
5194
  "3) Call Sideboard MCP `present_schema` when the user needs to filter, edit, publish, or persist rows \u2014 including after you already showed a markdown table, if they then ask for an editable table. If they only need to read the data, a markdown table is enough. When you do call it, pass title, mode (table|form), and either:",
5151
5195
  " - datasource=brightsy + resource_id (record type UUID) after fetching types via Brightsy MCP, or",
@@ -5160,7 +5204,7 @@ function formatArtifactDirective() {
5160
5204
  ].join("\n");
5161
5205
  }
5162
5206
  function formatUiReminder() {
5163
- return "Sideboard UI: markdown table is enough to read data; present_schema if they ask to edit/filter (even after markdown); present_files for the file manager. html fence or present_artifact, not both for the same document. ask_user only for a real multiple-choice (not hellos or \u201Cwhat next?\u201D) \u2014 reply in chat. Do not say artifacts/CMS UI are unavailable.";
5207
+ return "Sideboard UI: markdown table is enough to read data; present_schema if they ask to edit/filter (even after markdown); present_files for the file manager. html fence or present_artifact, not both for the same document. type=log appends (same artifact_id, new lines only). ask_user only for a real multiple-choice (not hellos or \u201Cwhat next?\u201D) \u2014 reply in chat. Do not say artifacts/CMS UI are unavailable.";
5164
5208
  }
5165
5209
  var FILES_BY_AGENT = {
5166
5210
  claude: [
@@ -5505,7 +5549,7 @@ function formatScheduledPrompt(name, prompt) {
5505
5549
  ${prompt}`;
5506
5550
  }
5507
5551
  async function defaultDeps() {
5508
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-6GUCE4LY.js");
5552
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-5TN2DJWO.js");
5509
5553
  const orch = getOrchestrator2();
5510
5554
  return {
5511
5555
  findThread: (id) => findThreadByRef(id),
@@ -5656,6 +5700,11 @@ var Orchestrator = class {
5656
5700
  turnBaselines = /* @__PURE__ */ new Map();
5657
5701
  /** Timers for orchestration session-quota auto-resume. */
5658
5702
  quotaResumeTimers = /* @__PURE__ */ new Map();
5703
+ /**
5704
+ * Threads that already got a crash-continue turn. Cleared on a successful
5705
+ * finish or a user send so a later crash can recover again.
5706
+ */
5707
+ crashContinued = /* @__PURE__ */ new Set();
5659
5708
  maxConcurrent;
5660
5709
  runningCount = 0;
5661
5710
  constructor(opts) {
@@ -5869,6 +5918,29 @@ var Orchestrator = class {
5869
5918
  );
5870
5919
  }
5871
5920
  }
5921
+ /**
5922
+ * Cursor-style recovery: after a runner crash, feed the error back as the
5923
+ * next prompt so the same agent can continue instead of sitting on lastError.
5924
+ */
5925
+ maybeEnqueueCrashContinue(threadId, opts) {
5926
+ if (this.crashContinued.has(threadId)) return;
5927
+ if (this.haltDrain.has(threadId)) return;
5928
+ if (!shouldFeedErrorBackToAgent({
5929
+ detail: opts.detail,
5930
+ assistantText: opts.assistantText,
5931
+ partsCount: opts.partsCount
5932
+ })) {
5933
+ return;
5934
+ }
5935
+ const thread = readThread(threadId);
5936
+ if (!thread || thread.status === "archived") return;
5937
+ this.crashContinued.add(threadId);
5938
+ const prompt = formatAgentErrorContinuePrompt(opts.detail);
5939
+ const queue = [prompt, ...thread.queue];
5940
+ updateThread(threadId, { queue });
5941
+ this.emit({ type: "queue_changed", threadId, queue });
5942
+ this.haltDrain.delete(threadId);
5943
+ }
5872
5944
  getThreads(includeArchived = false) {
5873
5945
  return listThreads({ includeArchived });
5874
5946
  }
@@ -5968,15 +6040,20 @@ var Orchestrator = class {
5968
6040
  throw new Error(`Thread is archived: ${thread.id}`);
5969
6041
  }
5970
6042
  const queue = [...current.queue, prompt];
6043
+ this.crashContinued.delete(thread.id);
5971
6044
  this.haltDrain.delete(thread.id);
5972
- const patch = { queue, status: "queued" };
6045
+ const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id) || current.status === "running";
6046
+ const patch = { queue };
6047
+ if (!inFlight) patch.status = "queued";
5973
6048
  const pid = current.agentPid;
5974
6049
  if (typeof pid === "number" && pid > 0 && !isPidAlive(pid)) {
5975
6050
  patch.agentPid = null;
5976
6051
  }
5977
6052
  updateThread(thread.id, patch);
5978
6053
  this.emit({ type: "queue_changed", threadId: thread.id, queue });
5979
- this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
6054
+ if (!inFlight) {
6055
+ this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
6056
+ }
5980
6057
  if (thisProcessShouldDrainAgentQueues()) {
5981
6058
  void this.drainQueue(thread.id);
5982
6059
  }
@@ -6297,7 +6374,7 @@ var Orchestrator = class {
6297
6374
  let parts = result.parts;
6298
6375
  let usage = result.usage ?? void 0;
6299
6376
  let exitCode = result.exitCode;
6300
- if (this.requireThread(threadId).agent === "cursor" && exitCode !== 0 && !assistantText && parts.length === 0) {
6377
+ if (!this.stoppedTurns.has(threadId) && this.requireThread(threadId).agent === "cursor" && exitCode !== 0 && !assistantText && parts.length === 0) {
6301
6378
  const sessionId = result.sessionId || this.requireThread(threadId).sessionId || "";
6302
6379
  if (sessionId) {
6303
6380
  const { recoverFinishedCursorRun } = await import("./cursor-recover-QSLWEZNA.js");
@@ -6318,7 +6395,7 @@ var Orchestrator = class {
6318
6395
  }
6319
6396
  let lastStderr = summarizeTurnStderr(stderrTail);
6320
6397
  let detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
6321
- if (exitCode !== 0 && !assistantText && parts.length === 0 && !this.stoppedTurns.has(threadId) && this.requireThread(threadId).agent !== "brightsy" && shouldRetryFailedAgentTurn(detail, {
6398
+ if (exitCode !== 0 && !assistantText && parts.length === 0 && !this.stoppedTurns.has(threadId) && shouldRetryFailedAgentTurn(detail, {
6322
6399
  hasSession: Boolean(this.requireThread(threadId).sessionId)
6323
6400
  })) {
6324
6401
  updateThread(threadId, { sessionId: null });
@@ -6414,7 +6491,11 @@ var Orchestrator = class {
6414
6491
  )) {
6415
6492
  updateThread(threadId, { sessionId: null });
6416
6493
  }
6417
- await syncThreadBranchFromGit(threadId);
6494
+ if (this.stoppedTurns.has(threadId)) {
6495
+ void syncThreadBranchFromGit(threadId).catch(() => void 0);
6496
+ } else {
6497
+ await syncThreadBranchFromGit(threadId);
6498
+ }
6418
6499
  if (this.stoppedTurns.has(threadId)) {
6419
6500
  const stopped = writeLiveStatus(threadId, "stopped");
6420
6501
  if (stopped?.status === "stopped") {
@@ -6438,9 +6519,16 @@ var Orchestrator = class {
6438
6519
  });
6439
6520
  }
6440
6521
  this.emit({ type: "turn_finished", threadId, exitCode });
6441
- if (exitCode !== 0) {
6522
+ if (exitCode === 0) {
6523
+ this.crashContinued.delete(threadId);
6524
+ } else {
6442
6525
  const blob = [chatText, detail].filter(Boolean).join("\n");
6443
6526
  void this.maybeHandleOrchestrationQuotaFailover(threadId, blob);
6527
+ this.maybeEnqueueCrashContinue(threadId, {
6528
+ detail: detail || failDetail,
6529
+ assistantText: chatText,
6530
+ partsCount: parts.length
6531
+ });
6444
6532
  }
6445
6533
  }
6446
6534
  } catch (err) {
@@ -6460,6 +6548,11 @@ var Orchestrator = class {
6460
6548
  }
6461
6549
  this.emit({ type: "turn_finished", threadId, exitCode: 1 });
6462
6550
  void this.maybeHandleOrchestrationQuotaFailover(threadId, message);
6551
+ this.maybeEnqueueCrashContinue(threadId, {
6552
+ detail: message,
6553
+ assistantText: "",
6554
+ partsCount: 0
6555
+ });
6463
6556
  }
6464
6557
  } finally {
6465
6558
  this.startingTurns.delete(threadId);
@@ -7592,6 +7685,7 @@ export {
7592
7685
  isHomeBoardThread,
7593
7686
  BOARD_COLUMN_DEFS,
7594
7687
  HOME_BOARD_CACHE_TTL_MS,
7688
+ DEFAULT_WORKTREE_SORT,
7595
7689
  groupHomeBoardWorktrees,
7596
7690
  classifyWorktreeColumn,
7597
7691
  worktreeBoardStatus,
@@ -9,7 +9,8 @@ import {
9
9
  ensureConnectedBrightsyTeamTokens,
10
10
  getConnectedBrightsyTeamsRaw,
11
11
  listConnectedBrightsyTeams
12
- } from "./chunk-YMRT2DU6.js";
12
+ } from "./chunk-LLX3OMHU.js";
13
+ import "./chunk-R7FGPBKL.js";
13
14
  import "./chunk-KPIYENTF.js";
14
15
  import "./chunk-BD2ICC4D.js";
15
16
  export {
@@ -7,7 +7,8 @@ import {
7
7
  ensureConnectedBrightsyTeamTokens,
8
8
  getConnectedBrightsyTeamsRaw,
9
9
  listConnectedBrightsyTeams
10
- } from "./chunk-EKIDHL2T.js";
10
+ } from "./chunk-SIPDL62B.js";
11
+ import "./chunk-AQESMTCZ.js";
11
12
  import "./chunk-QZQEXME2.js";
12
13
  import "./chunk-DVM4ID64.js";
13
14
  export {