@sideboard-ai/core 0.1.196 → 0.1.198

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.
@@ -1730,11 +1730,20 @@ function looksLikeInvalidAgentSession(text7) {
1730
1730
  if (!lower) return false;
1731
1731
  return /session not found/.test(lower) || /no conversation found/.test(lower) || /conversation .+ not found/.test(lower) || /thread .+ not found/.test(lower) || /unknown session/.test(lower) || /invalid session/.test(lower) || /session .+ (missing|expired|deleted|gone)/.test(lower) || /cannot resume/.test(lower) || /failed to (load|resume|open) session/.test(lower) || /corrupt local agent checkpoint/.test(lower) || /missing root blob/.test(lower) || /\bagent\b.{0,120}\bnot found\b/.test(lower) || /\brun\b.{0,80}\bnot found for agent\b/.test(lower);
1732
1732
  }
1733
+ function looksLikeCursorHttp2StreamClose(text7) {
1734
+ return /nghttp2/i.test(text7.trim());
1735
+ }
1736
+ function looksLikeBrightsyEmptyCompletion(text7) {
1737
+ const core = text7.trim().replace(/^exit\s*\d+:\s*/i, "").replace(/^error:\s*/i, "").trim();
1738
+ return /^i did not receive a valid model response\. please try again\.$/i.test(core);
1739
+ }
1733
1740
  function looksLikeRetryableRunnerCrash(text7) {
1734
1741
  if (looksLikeAgentFailureMessage(text7)) return false;
1735
1742
  if (looksLikeInvalidAgentSession(text7)) return false;
1736
1743
  if (looksLikeV8Oom(text7)) return false;
1737
1744
  if (looksLikeHugeToolResultDump(text7)) return true;
1745
+ if (looksLikeCursorHttp2StreamClose(text7)) return true;
1746
+ if (looksLikeBrightsyEmptyCompletion(text7)) return true;
1738
1747
  const lower = text7.trim().toLowerCase();
1739
1748
  if (/cannot find (?:package|module)|err_module_not_found/.test(lower)) return false;
1740
1749
  if (!lower) return true;
@@ -1807,9 +1816,12 @@ function humanizeAgentFailDetail(detail) {
1807
1816
  if (/corrupt local agent checkpoint|missing root blob|truncated crash dump/.test(lower)) {
1808
1817
  return `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
1809
1818
  }
1810
- if (/connection stalled/.test(lower)) {
1819
+ if (/connection stalled/.test(lower) || looksLikeCursorHttp2StreamClose(raw)) {
1811
1820
  return /retry the turn/i.test(raw) ? raw : `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
1812
1821
  }
1822
+ if (looksLikeBrightsyEmptyCompletion(raw)) {
1823
+ return /retry the turn/i.test(raw) ? raw : `${raw} \u2014 retry the turn (Brightsy returned an empty model completion).`;
1824
+ }
1813
1825
  if (/network request failed|cursor startup failed:.+\(retryable\)/.test(lower)) {
1814
1826
  return /retry the turn/i.test(raw) ? raw : `${raw} \u2014 retry the turn (a transient Cursor SDK network error).`;
1815
1827
  }
@@ -8769,6 +8781,12 @@ function parseBrightsyCliLine(line) {
8769
8781
  try {
8770
8782
  const obj = JSON.parse(trimmed);
8771
8783
  if (obj.type === "text" && typeof obj.text === "string") {
8784
+ if (looksLikeBrightsyEmptyCompletion(obj.text)) {
8785
+ return [
8786
+ { type: "stderr", data: obj.text },
8787
+ { type: "stdout", data: `Error: ${obj.text}` }
8788
+ ];
8789
+ }
8772
8790
  return { type: "stdout", data: obj.text };
8773
8791
  }
8774
8792
  if (obj.type === "thinking" && typeof obj.text === "string") {
@@ -8812,7 +8830,7 @@ function parseBrightsyCliLine(line) {
8812
8830
  if (trimmed.startsWith("{") && /"type"\s*:\s*"(tool_use|tool_result|tool|text|thinking|usage|done|error)"/.test(trimmed)) {
8813
8831
  return null;
8814
8832
  }
8815
- if (/error|failed|unauthorized|quota|limit|not logged in/i.test(trimmed)) {
8833
+ if (looksLikeBrightsyEmptyCompletion(trimmed) || /error|failed|unauthorized|quota|limit|not logged in/i.test(trimmed)) {
8816
8834
  return [
8817
8835
  { type: "stderr", data: trimmed },
8818
8836
  { type: "stdout", data: `Error: ${trimmed}` }
@@ -8821,6 +8839,12 @@ function parseBrightsyCliLine(line) {
8821
8839
  return { type: "stdout", data: line };
8822
8840
  }
8823
8841
  }
8842
+ function adjustBrightsyTurnExit(exitCode, assistantText) {
8843
+ if (looksLikeBrightsyEmptyCompletion(assistantText) && (exitCode === 0 || exitCode == null)) {
8844
+ return 2;
8845
+ }
8846
+ return exitCode;
8847
+ }
8824
8848
  async function fetchTeamChatTargets(team) {
8825
8849
  const endpoint = (team.endpoint || "https://brightsy.ai").replace(/\/$/, "");
8826
8850
  let agents = [
@@ -9341,6 +9365,11 @@ function parseDiffStat(result) {
9341
9365
  }
9342
9366
  return {};
9343
9367
  }
9368
+ function clipThinkingForStore(text7, max = THINKING_PART_MAX_CHARS) {
9369
+ if (text7.length <= max) return text7;
9370
+ const marker = "\u2026(earlier thinking trimmed)\n";
9371
+ return marker + text7.slice(text7.length - Math.max(1, max - marker.length));
9372
+ }
9344
9373
  function withPartTimes(next, prev) {
9345
9374
  const now = Date.now();
9346
9375
  const startedAt = prev && typeof prev.startedAt === "number" ? prev.startedAt : now;
@@ -9383,7 +9412,7 @@ function applyAgentEvent(parts, event) {
9383
9412
  next[i] = withPartTimes(
9384
9413
  {
9385
9414
  type: "thinking",
9386
- text: data,
9415
+ text: clipThinkingForStore(data),
9387
9416
  ...event.parentId ? { parentId: event.parentId } : {}
9388
9417
  },
9389
9418
  prev
@@ -9394,7 +9423,7 @@ function applyAgentEvent(parts, event) {
9394
9423
  next.push(
9395
9424
  withPartTimes({
9396
9425
  type: "thinking",
9397
- text: data,
9426
+ text: clipThinkingForStore(data),
9398
9427
  ...event.parentId ? { parentId: event.parentId } : {}
9399
9428
  })
9400
9429
  );
@@ -9405,7 +9434,7 @@ function applyAgentEvent(parts, event) {
9405
9434
  next[next.length - 1] = withPartTimes(
9406
9435
  {
9407
9436
  type: "thinking",
9408
- text: last.text + data,
9437
+ text: clipThinkingForStore(last.text + data),
9409
9438
  ...event.parentId ? { parentId: event.parentId } : {}
9410
9439
  },
9411
9440
  last
@@ -9556,12 +9585,13 @@ function normalizeParseResult(parsed) {
9556
9585
  if (!parsed) return [];
9557
9586
  return Array.isArray(parsed) ? parsed : [parsed];
9558
9587
  }
9559
- var CHAT_DETAIL_MAX;
9588
+ var CHAT_DETAIL_MAX, THINKING_PART_MAX_CHARS;
9560
9589
  var init_message_parts = __esm({
9561
9590
  "src/agents/message-parts.ts"() {
9562
9591
  "use strict";
9563
9592
  init_error_detail();
9564
9593
  CHAT_DETAIL_MAX = 72;
9594
+ THINKING_PART_MAX_CHARS = 64e3;
9565
9595
  }
9566
9596
  });
9567
9597
 
@@ -12442,11 +12472,11 @@ async function spawnAgentTurn(thread, input, onEvent) {
12442
12472
  consume(child.stderr, "stderr");
12443
12473
  const done = child.then((result) => {
12444
12474
  outbound.flush();
12445
- const exitCode = result.exitCode ?? null;
12446
- onEvent({ type: "exit", data: exitCode });
12447
12475
  const finalized = finalizeParts(parts);
12448
12476
  const rawText = assistantText.trim() || partsToAssistantText(finalized);
12449
12477
  const text7 = thread.agent === "brightsy" ? stripBrightsyNdjsonNoise(rawText) : rawText;
12478
+ const exitCode = thread.agent === "brightsy" ? adjustBrightsyTurnExit(result.exitCode ?? null, text7) : result.exitCode ?? null;
12479
+ onEvent({ type: "exit", data: exitCode });
12450
12480
  return { exitCode, sessionId, assistantText: text7, parts: finalized, usage };
12451
12481
  });
12452
12482
  return {
@@ -12797,7 +12827,7 @@ function formatToolPart(t, detail) {
12797
12827
  if (t.input && Object.keys(t.input).length > 0) {
12798
12828
  lines.push("Input:");
12799
12829
  lines.push("```json");
12800
- lines.push(JSON.stringify(t.input, null, 2));
12830
+ lines.push(clipToolResultForStore(JSON.stringify(t.input, null, 2)) ?? "");
12801
12831
  lines.push("```");
12802
12832
  } else if (t.detail) {
12803
12833
  lines.push(`Detail: ${t.detail}`);
@@ -12845,19 +12875,7 @@ ${m.text}`);
12845
12875
  }
12846
12876
  return bits.join("\n");
12847
12877
  }
12848
- function buildSessionSeed(messages, opts) {
12849
- if (messages.length === 0) return null;
12850
- const tools = opts?.tools ?? "full";
12851
- const fullCount = opts?.fullToolMessages ?? SEED_FULL_TOOL_MESSAGES;
12852
- const maxChars = opts?.maxChars ?? SEED_MAX_CHARS;
12853
- const olderTools = tools === "none" ? "none" : "summary";
12854
- const fullFrom = Math.max(0, messages.length - fullCount);
12855
- const blocks = messages.map(
12856
- (m, i) => formatMessageBlock(m, {
12857
- tools: i >= fullFrom ? tools : olderTools,
12858
- thinking: false
12859
- })
12860
- );
12878
+ function budgetSeedBlocks(messages, blocks, maxChars) {
12861
12879
  const keep = new Array(blocks.length).fill(false);
12862
12880
  let used = 0;
12863
12881
  for (let i = 0; i < blocks.length; i++) {
@@ -12874,20 +12892,34 @@ function buildSessionSeed(messages, opts) {
12874
12892
  used = next;
12875
12893
  }
12876
12894
  const dropped = keep.filter((k) => !k).length;
12895
+ const marker = `_(${dropped} older message${dropped === 1 ? "" : "s"} omitted for length)_`;
12877
12896
  const kept = [];
12878
12897
  let markerPlaced = false;
12879
12898
  for (let i = 0; i < blocks.length; i++) {
12880
12899
  if (!keep[i]) continue;
12881
12900
  if (dropped > 0 && !markerPlaced && messages[i].role !== "summary") {
12882
- kept.push(`_(${dropped} older message${dropped === 1 ? "" : "s"} omitted for length)_`);
12901
+ kept.push(marker);
12883
12902
  markerPlaced = true;
12884
12903
  }
12885
12904
  kept.push(blocks[i]);
12886
12905
  }
12887
- if (dropped > 0 && !markerPlaced) {
12888
- kept.push(`_(${dropped} older message${dropped === 1 ? "" : "s"} omitted for length)_`);
12889
- }
12890
- const body = kept.join("\n\n");
12906
+ if (dropped > 0 && !markerPlaced) kept.push(marker);
12907
+ return kept.join("\n\n");
12908
+ }
12909
+ function buildSessionSeed(messages, opts) {
12910
+ if (messages.length === 0) return null;
12911
+ const tools = opts?.tools ?? "full";
12912
+ const fullCount = opts?.fullToolMessages ?? SEED_FULL_TOOL_MESSAGES;
12913
+ const maxChars = opts?.maxChars ?? SEED_MAX_CHARS;
12914
+ const olderTools = tools === "none" ? "none" : "summary";
12915
+ const fullFrom = Math.max(0, messages.length - fullCount);
12916
+ const blocks = messages.map(
12917
+ (m, i) => formatMessageBlock(m, {
12918
+ tools: i >= fullFrom ? tools : olderTools,
12919
+ thinking: false
12920
+ })
12921
+ );
12922
+ const body = budgetSeedBlocks(messages, blocks, maxChars);
12891
12923
  if (!body.trim()) return null;
12892
12924
  return [
12893
12925
  "Sideboard conversation context (restored after compaction or a new session):",
@@ -12937,10 +12969,12 @@ function findLastBrightsyContextSummary(messages) {
12937
12969
  }
12938
12970
  return null;
12939
12971
  }
12940
- function buildBrightsySessionSeed(messages) {
12972
+ function buildBrightsySessionSeed(messages, opts) {
12941
12973
  const match = findLastBrightsyContextSummary(messages);
12942
12974
  const tail = match ? messages.slice(match.index + 1) : messages;
12943
- const body = formatMessagesAsTranscript(tail, { tools: "none", thinking: false });
12975
+ const budget = Math.max(1e3, (opts?.maxChars ?? SEED_MAX_CHARS) - (match?.text.length ?? 0));
12976
+ const tailBlocks = tail.map((m) => formatMessageBlock(m, { tools: "none", thinking: false }));
12977
+ const body = budgetSeedBlocks(tail, tailBlocks, budget);
12944
12978
  if (!match && !body.trim()) return null;
12945
12979
  const blocks = [
12946
12980
  "Sideboard conversation context (restored after compaction or a new session):",
@@ -13011,6 +13045,7 @@ var CONTEXT_COMPACT_CHARS, CONTEXT_KEEP_RECENT_CHARS, CONTEXT_KEEP_RECENT_MESSAG
13011
13045
  var init_context_compact = __esm({
13012
13046
  "src/composer/context-compact.ts"() {
13013
13047
  "use strict";
13048
+ init_error_detail();
13014
13049
  init_usage();
13015
13050
  init_context_estimate();
13016
13051
  init_summarize();
@@ -13061,6 +13096,10 @@ function pickRichestSetupLog(snaps) {
13061
13096
  }
13062
13097
  return best;
13063
13098
  }
13099
+ function resolveWorktreeSetupLog(shared, legacyChatLogs) {
13100
+ if (shared.output || shared.running || shared.exitCode != null) return shared;
13101
+ return pickRichestSetupLog(legacyChatLogs);
13102
+ }
13064
13103
  function mergeWorktreeActiveRuns(threads) {
13065
13104
  const byName = /* @__PURE__ */ new Map();
13066
13105
  let devPort = null;
@@ -13876,6 +13915,14 @@ var init_conductor = __esm({
13876
13915
  });
13877
13916
 
13878
13917
  // src/store/setup-log.ts
13918
+ function appendSetupOutput(prev, line, max = MAX_SETUP_LOG_CHARS) {
13919
+ const next = prev ? `${prev}
13920
+ ${line}` : line;
13921
+ if (next.length <= max) return next;
13922
+ const cut = next.slice(next.length - max);
13923
+ const nl = cut.indexOf("\n");
13924
+ return nl >= 0 && nl < cut.length - 1 ? cut.slice(nl + 1) : cut;
13925
+ }
13879
13926
  function setupLogPath(threadId) {
13880
13927
  return (0, import_node_path36.join)(threadsDir(), `${threadId}.setup.log.json`);
13881
13928
  }
@@ -13953,8 +14000,7 @@ function appendSetupLog(threadId, line) {
13953
14000
  const current = memory.get(threadId) ?? readSetupLog(threadId);
13954
14001
  const snap = {
13955
14002
  ...current,
13956
- output: current.output ? `${current.output}
13957
- ${line}` : line,
14003
+ output: appendSetupOutput(current.output, line),
13958
14004
  running: true
13959
14005
  };
13960
14006
  memory.set(threadId, snap);
@@ -13973,7 +14019,7 @@ function finishSetupLog(threadId, exitCode, source) {
13973
14019
  persistNow(threadId);
13974
14020
  return clone(snap);
13975
14021
  }
13976
- var import_node_crypto8, import_node_fs36, import_node_path36, EMPTY, memory, persistTimers;
14022
+ var import_node_crypto8, import_node_fs36, import_node_path36, EMPTY, MAX_SETUP_LOG_CHARS, memory, persistTimers;
13977
14023
  var init_setup_log = __esm({
13978
14024
  "src/store/setup-log.ts"() {
13979
14025
  "use strict";
@@ -13988,6 +14034,7 @@ var init_setup_log = __esm({
13988
14034
  exitCode: null,
13989
14035
  source: null
13990
14036
  };
14037
+ MAX_SETUP_LOG_CHARS = 256e3;
13991
14038
  memory = /* @__PURE__ */ new Map();
13992
14039
  persistTimers = /* @__PURE__ */ new Map();
13993
14040
  }
@@ -20310,6 +20357,7 @@ var init_orchestrator = __esm({
20310
20357
  return true;
20311
20358
  });
20312
20359
  if (!promoted) return this.requireThread(thread.id);
20360
+ if (!thisProcessShouldDrainAgentQueues()) return this.requireThread(thread.id);
20313
20361
  const current = this.requireThread(thread.id);
20314
20362
  const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id);
20315
20363
  const livePid = current.agentPid;
@@ -21050,10 +21098,10 @@ var init_orchestrator = __esm({
21050
21098
  thread.id,
21051
21099
  ...threadsSharingWorktree(thread.worktreePath).map((t) => t.id)
21052
21100
  ];
21053
- const snap = pickRichestSetupLog([
21101
+ const snap = resolveWorktreeSetupLog(
21054
21102
  readSetupLog(setupLogKeyForWorktree(thread.worktreePath)),
21055
- ...[...new Set(siblingIds)].map((id) => readSetupLog(id))
21056
- ]) ?? readSetupLog(thread.id);
21103
+ [...new Set(siblingIds)].map((id) => readSetupLog(id))
21104
+ ) ?? readSetupLog(thread.id);
21057
21105
  if (snap.running && !this.hasWorktreeSetupProcess(thread.worktreePath)) {
21058
21106
  return { ...snap, running: false };
21059
21107
  }
@@ -26204,7 +26252,7 @@ async function startMcpServer() {
26204
26252
  );
26205
26253
  server.tool(
26206
26254
  "send_to_thread",
26207
- 'Steer a prompt on a worktree thread chat (Settings \u2192 Follow-up, default steer: interrupt and start now, skip the inbox). Use after create_thread to start or continue a conversation. For commit/push/PR, prefer ask_git (canonical desktop-button phrases). Send "Merge PR." / ask_git merge only when the user explicitly asked to merge. force_stop=true kills the in-flight turn and clears the queue before this prompt \u2014 only when the current request is wrong and must be replaced. Do not send_to_thread to check in, resume after a halt notice, or because wait_for_turn returned stillRunning; that interrupts the child mid-thought. Call wait_for_turn again instead.',
26255
+ 'Steer a prompt on a worktree thread chat (Settings \u2192 Follow-up, default steer: front of the queue, skips the inbox; while the desktop board is running it starts as soon as the in-flight turn yields). Use after create_thread to start or continue a conversation. For commit/push/PR, prefer ask_git (canonical desktop-button phrases). Send "Merge PR." / ask_git merge only when the user explicitly asked to merge. force_stop=true kills the in-flight turn and clears the queue before this prompt \u2014 only when the current request is wrong and must be replaced. Do not send_to_thread to check in, resume after a halt notice, or because wait_for_turn returned stillRunning; that interrupts the child mid-thought. Call wait_for_turn again instead.',
26208
26256
  {
26209
26257
  ref: import_zod9.z.string(),
26210
26258
  prompt: import_zod9.z.string(),
@@ -38,7 +38,7 @@ import {
38
38
  syncBoardPins,
39
39
  updateSchedule,
40
40
  waitForDetachedJob
41
- } from "../chunk-3IGCU4AT.js";
41
+ } from "../chunk-DROELG52.js";
42
42
  import "../chunk-YWI3IBP2.js";
43
43
  import {
44
44
  applySystemCaEnv,
@@ -48,7 +48,7 @@ import {
48
48
  materializeSystemCaBundle,
49
49
  sideboardMcpProfile,
50
50
  trustSystemCertificates
51
- } from "../chunk-5DY2QWVN.js";
51
+ } from "../chunk-SYXSSU7G.js";
52
52
  import {
53
53
  GLOBAL_WORKSPACE_ID,
54
54
  isCloudCoordinatorThread,
@@ -4353,7 +4353,7 @@ async function startMcpServer() {
4353
4353
  );
4354
4354
  server.tool(
4355
4355
  "send_to_thread",
4356
- 'Steer a prompt on a worktree thread chat (Settings \u2192 Follow-up, default steer: interrupt and start now, skip the inbox). Use after create_thread to start or continue a conversation. For commit/push/PR, prefer ask_git (canonical desktop-button phrases). Send "Merge PR." / ask_git merge only when the user explicitly asked to merge. force_stop=true kills the in-flight turn and clears the queue before this prompt \u2014 only when the current request is wrong and must be replaced. Do not send_to_thread to check in, resume after a halt notice, or because wait_for_turn returned stillRunning; that interrupts the child mid-thought. Call wait_for_turn again instead.',
4356
+ 'Steer a prompt on a worktree thread chat (Settings \u2192 Follow-up, default steer: front of the queue, skips the inbox; while the desktop board is running it starts as soon as the in-flight turn yields). Use after create_thread to start or continue a conversation. For commit/push/PR, prefer ask_git (canonical desktop-button phrases). Send "Merge PR." / ask_git merge only when the user explicitly asked to merge. force_stop=true kills the in-flight turn and clears the queue before this prompt \u2014 only when the current request is wrong and must be replaced. Do not send_to_thread to check in, resume after a halt notice, or because wait_for_turn returned stillRunning; that interrupts the child mid-thought. Call wait_for_turn again instead.',
4357
4357
  {
4358
4358
  ref: z9.string(),
4359
4359
  prompt: z9.string(),
@@ -7,9 +7,9 @@ import {
7
7
  resolveSendFollowUp,
8
8
  startOrchestration,
9
9
  waitForPidExit
10
- } from "./chunk-NXXJBRWQ.js";
10
+ } from "./chunk-6YS5IKD2.js";
11
11
  import "./chunk-DJU54RYW.js";
12
- import "./chunk-NWIBA5CP.js";
12
+ import "./chunk-EA67VQM2.js";
13
13
  import "./chunk-HZTHWU6A.js";
14
14
  import "./chunk-S4QCBBXS.js";
15
15
  import "./chunk-6K64FJDD.js";
@@ -18,7 +18,7 @@ import "./chunk-SALM35KD.js";
18
18
  import "./chunk-AN3THU7F.js";
19
19
  import "./chunk-N7WZ6NIN.js";
20
20
  import "./chunk-AXQ4ZWHK.js";
21
- import "./chunk-NLHFAMC2.js";
21
+ import "./chunk-CQ5R3VPP.js";
22
22
  import "./chunk-S5GLSRLP.js";
23
23
  import "./chunk-4TR3HZFT.js";
24
24
  import "./chunk-WDRMZKVB.js";
@@ -9,9 +9,9 @@ import {
9
9
  resolveSendFollowUp,
10
10
  startOrchestration,
11
11
  waitForPidExit
12
- } from "./chunk-3IGCU4AT.js";
12
+ } from "./chunk-DROELG52.js";
13
13
  import "./chunk-YWI3IBP2.js";
14
- import "./chunk-5DY2QWVN.js";
14
+ import "./chunk-SYXSSU7G.js";
15
15
  import "./chunk-6YTI2HBG.js";
16
16
  import "./chunk-3OHR73VK.js";
17
17
  import "./chunk-Y4MIXGPA.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sideboard-ai/core",
3
- "version": "0.1.196",
3
+ "version": "0.1.198",
4
4
  "description": "Sideboard core — orchestration, agents, git worktrees, MCP server",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",