@sideboard-ai/core 0.1.197 → 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.
@@ -220,6 +220,53 @@ function sanitizeCursorStoreSegment(threadId) {
220
220
  return (threadId ?? "").trim().replace(/[^a-zA-Z0-9._-]/g, "_");
221
221
  }
222
222
 
223
+ // src/agents/error-detail.ts
224
+ function formatUnknownDetail(err) {
225
+ if (err == null) return "";
226
+ if (typeof err === "string") return err.trim();
227
+ if (err instanceof Error) {
228
+ const base = err.message.trim() || err.name;
229
+ const code = "code" in err && typeof err.code === "string" ? err.code.trim() : "";
230
+ return code && !base.includes(code) ? `${base} (${code})` : base;
231
+ }
232
+ if (typeof err === "object") {
233
+ const o = err;
234
+ const nested = o.error != null && typeof o.error === "object" ? formatUnknownDetail(o.error) : "";
235
+ const message = typeof o.message === "string" ? o.message.trim() : typeof o.error === "string" ? o.error.trim() : typeof o.result === "string" ? o.result.trim() : nested;
236
+ const code = typeof o.code === "string" ? o.code.trim() : "";
237
+ if (message) return code && !message.includes(code) ? `${message} (${code})` : message;
238
+ try {
239
+ const json = JSON.stringify(err);
240
+ if (json && json !== "{}" && json !== "null") return json;
241
+ } catch {
242
+ }
243
+ }
244
+ const fallback = String(err);
245
+ return fallback === "[object Object]" ? "" : fallback;
246
+ }
247
+ function extractJsonErrorMessage(obj) {
248
+ const nested = obj.error != null && typeof obj.error === "object" ? obj.error : null;
249
+ const candidates = [
250
+ typeof obj.message === "string" ? obj.message : null,
251
+ typeof obj.error === "string" ? obj.error : null,
252
+ nested && typeof nested.message === "string" ? nested.message : null,
253
+ typeof obj.result === "string" ? obj.result : null,
254
+ typeof obj.detail === "string" ? obj.detail : null
255
+ ];
256
+ for (const c of candidates) {
257
+ const t = c?.trim();
258
+ if (t) return t;
259
+ }
260
+ if (Array.isArray(obj.errors)) {
261
+ const parts = obj.errors.map((e) => formatUnknownDetail(e)).map((s) => s.trim()).filter(Boolean);
262
+ if (parts.length) return parts.join("; ");
263
+ }
264
+ return null;
265
+ }
266
+ function looksLikeCursorHttp2StreamClose(text) {
267
+ return /nghttp2/i.test(text.trim());
268
+ }
269
+
223
270
  // src/agents/cursor-session.ts
224
271
  function cursorErrorMessage(err) {
225
272
  if (err instanceof Error) return err.message.trim();
@@ -251,7 +298,8 @@ function isRetryableCursorTransportError(err) {
251
298
  if (err && typeof err === "object" && "isRetryable" in err && err.isRetryable === true) {
252
299
  return true;
253
300
  }
254
- return /network request failed/i.test(cursorErrorMessage(err));
301
+ const message = cursorErrorMessage(err);
302
+ return /network request failed/i.test(message) || looksLikeCursorHttp2StreamClose(message);
255
303
  }
256
304
  function looksLikeNonRetryableCursorFailure(message) {
257
305
  const lower = message.toLowerCase();
@@ -324,50 +372,6 @@ async function* iterateUntilIdle(iterable, idleMs, onIdle, activity) {
324
372
  }
325
373
  }
326
374
 
327
- // src/agents/error-detail.ts
328
- function formatUnknownDetail(err) {
329
- if (err == null) return "";
330
- if (typeof err === "string") return err.trim();
331
- if (err instanceof Error) {
332
- const base = err.message.trim() || err.name;
333
- const code = "code" in err && typeof err.code === "string" ? err.code.trim() : "";
334
- return code && !base.includes(code) ? `${base} (${code})` : base;
335
- }
336
- if (typeof err === "object") {
337
- const o = err;
338
- const nested = o.error != null && typeof o.error === "object" ? formatUnknownDetail(o.error) : "";
339
- const message = typeof o.message === "string" ? o.message.trim() : typeof o.error === "string" ? o.error.trim() : typeof o.result === "string" ? o.result.trim() : nested;
340
- const code = typeof o.code === "string" ? o.code.trim() : "";
341
- if (message) return code && !message.includes(code) ? `${message} (${code})` : message;
342
- try {
343
- const json = JSON.stringify(err);
344
- if (json && json !== "{}" && json !== "null") return json;
345
- } catch {
346
- }
347
- }
348
- const fallback = String(err);
349
- return fallback === "[object Object]" ? "" : fallback;
350
- }
351
- function extractJsonErrorMessage(obj) {
352
- const nested = obj.error != null && typeof obj.error === "object" ? obj.error : null;
353
- const candidates = [
354
- typeof obj.message === "string" ? obj.message : null,
355
- typeof obj.error === "string" ? obj.error : null,
356
- nested && typeof nested.message === "string" ? nested.message : null,
357
- typeof obj.result === "string" ? obj.result : null,
358
- typeof obj.detail === "string" ? obj.detail : null
359
- ];
360
- for (const c of candidates) {
361
- const t = c?.trim();
362
- if (t) return t;
363
- }
364
- if (Array.isArray(obj.errors)) {
365
- const parts = obj.errors.map((e) => formatUnknownDetail(e)).map((s) => s.trim()).filter(Boolean);
366
- if (parts.length) return parts.join("; ");
367
- }
368
- return null;
369
- }
370
-
371
375
  // src/agents/cursor-events.ts
372
376
  function usageFromCursor(usage) {
373
377
  if (!usage) return null;
@@ -11,8 +11,9 @@ import {
11
11
  cursorSdkMessageToEvents,
12
12
  ensureCursorRipgrepPath,
13
13
  fetchCursorTurnCostUsd,
14
- formatUnknownDetail
15
- } from "../chunk-NLHFAMC2.js";
14
+ formatUnknownDetail,
15
+ looksLikeCursorHttp2StreamClose
16
+ } from "../chunk-CQ5R3VPP.js";
16
17
  import {
17
18
  dropNestedElectronEnvFromProcess
18
19
  } from "../chunk-4TR3HZFT.js";
@@ -55,7 +56,8 @@ function isRetryableCursorTransportError(err) {
55
56
  if (err && typeof err === "object" && "isRetryable" in err && err.isRetryable === true) {
56
57
  return true;
57
58
  }
58
- return /network request failed/i.test(cursorErrorMessage(err));
59
+ const message = cursorErrorMessage(err);
60
+ return /network request failed/i.test(message) || looksLikeCursorHttp2StreamClose(message);
59
61
  }
60
62
  function looksLikeNonRetryableCursorFailure(message) {
61
63
  const lower = message.toLowerCase();
@@ -38,7 +38,7 @@ import {
38
38
  resolveQuotaFallbackAgent,
39
39
  turnCostUsdFromCursorUsage,
40
40
  whichOnPath
41
- } from "./chunk-PQBRCBIE.js";
41
+ } from "./chunk-SYXSSU7G.js";
42
42
  import "./chunk-6YTI2HBG.js";
43
43
  import "./chunk-3OHR73VK.js";
44
44
  import "./chunk-Y4MIXGPA.js";
@@ -32,7 +32,7 @@ import {
32
32
  resolveLoginCommand,
33
33
  resolveQuotaFallbackAgent,
34
34
  whichOnPath
35
- } from "./chunk-N5JBG3BF.js";
35
+ } from "./chunk-EA67VQM2.js";
36
36
  import "./chunk-HZTHWU6A.js";
37
37
  import "./chunk-S4QCBBXS.js";
38
38
  import "./chunk-6K64FJDD.js";
@@ -45,7 +45,7 @@ import {
45
45
  parseCursorRunnerLine,
46
46
  preferredCursorCostCents,
47
47
  turnCostUsdFromCursorUsage
48
- } from "./chunk-NLHFAMC2.js";
48
+ } from "./chunk-CQ5R3VPP.js";
49
49
  import {
50
50
  ORCHESTRATOR_AGENT_KINDS,
51
51
  assertOrchestratorCapableAgent,
@@ -5,6 +5,7 @@ import {
5
5
  } from "./chunk-DJU54RYW.js";
6
6
  import {
7
7
  PLAN_MODE_INSTRUCTION,
8
+ adjustBrightsyTurnExit,
8
9
  allAdapters,
9
10
  applyAgentEvent,
10
11
  applyTurnUsage,
@@ -24,7 +25,7 @@ import {
24
25
  stripBrightsyNdjsonNoise,
25
26
  sumUsageList,
26
27
  toolDescription
27
- } from "./chunk-N5JBG3BF.js";
28
+ } from "./chunk-EA67VQM2.js";
28
29
  import {
29
30
  SLACK_REPLY_FORMATTING,
30
31
  addWorkspace,
@@ -128,7 +129,7 @@ import {
128
129
  shouldRetryFailedAgentTurn,
129
130
  summarizeTurnStderr,
130
131
  turnFailChatText
131
- } from "./chunk-NLHFAMC2.js";
132
+ } from "./chunk-CQ5R3VPP.js";
132
133
  import {
133
134
  assertOrchestratorCapableAgent,
134
135
  childEnvWithAppSettings,
@@ -472,7 +473,7 @@ async function continueSourceThread(threadId, prompt) {
472
473
  await continueOnReply(threadId, prompt);
473
474
  return;
474
475
  }
475
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-HB4LZ2AA.js");
476
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-6ZNKCPNL.js");
476
477
  await getOrchestrator2().send(threadId, prompt);
477
478
  } catch {
478
479
  }
@@ -904,11 +905,11 @@ async function spawnAgentTurn(thread, input, onEvent) {
904
905
  consume(child.stderr, "stderr");
905
906
  const done = child.then((result) => {
906
907
  outbound.flush();
907
- const exitCode = result.exitCode ?? null;
908
- onEvent({ type: "exit", data: exitCode });
909
908
  const finalized = finalizeParts(parts);
910
909
  const rawText = assistantText.trim() || partsToAssistantText(finalized);
911
910
  const text = thread.agent === "brightsy" ? stripBrightsyNdjsonNoise(rawText) : rawText;
911
+ const exitCode = thread.agent === "brightsy" ? adjustBrightsyTurnExit(result.exitCode ?? null, text) : result.exitCode ?? null;
912
+ onEvent({ type: "exit", data: exitCode });
912
913
  return { exitCode, sessionId, assistantText: text, parts: finalized, usage };
913
914
  });
914
915
  return {
@@ -3887,7 +3888,7 @@ async function createThread(input, _onSetupLine) {
3887
3888
  return readThread(thread.id) ?? thread;
3888
3889
  }
3889
3890
  async function listLinearIssues(agent, repoPath) {
3890
- const { getAdapter: getAdapter2 } = await import("./agents-IVDCAG7T.js");
3891
+ const { getAdapter: getAdapter2 } = await import("./agents-AQQKPD46.js");
3891
3892
  await requireAgent(agent, { requireLinear: true });
3892
3893
  const adapter = getAdapter2(agent);
3893
3894
  if (!adapter.listLinearIssues) {
@@ -6893,7 +6894,7 @@ function formatScheduledPrompt(name, prompt) {
6893
6894
  ${prompt}`;
6894
6895
  }
6895
6896
  async function defaultDeps() {
6896
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-HB4LZ2AA.js");
6897
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-6ZNKCPNL.js");
6897
6898
  const orch = getOrchestrator2();
6898
6899
  return {
6899
6900
  findThread: (id) => findThreadByRef(id),
@@ -207,11 +207,20 @@ function looksLikeInvalidAgentSession(text) {
207
207
  if (!lower) return false;
208
208
  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);
209
209
  }
210
+ function looksLikeCursorHttp2StreamClose(text) {
211
+ return /nghttp2/i.test(text.trim());
212
+ }
213
+ function looksLikeBrightsyEmptyCompletion(text) {
214
+ const core = text.trim().replace(/^exit\s*\d+:\s*/i, "").replace(/^error:\s*/i, "").trim();
215
+ return /^i did not receive a valid model response\. please try again\.$/i.test(core);
216
+ }
210
217
  function looksLikeRetryableRunnerCrash(text) {
211
218
  if (looksLikeAgentFailureMessage(text)) return false;
212
219
  if (looksLikeInvalidAgentSession(text)) return false;
213
220
  if (looksLikeV8Oom(text)) return false;
214
221
  if (looksLikeHugeToolResultDump(text)) return true;
222
+ if (looksLikeCursorHttp2StreamClose(text)) return true;
223
+ if (looksLikeBrightsyEmptyCompletion(text)) return true;
215
224
  const lower = text.trim().toLowerCase();
216
225
  if (/cannot find (?:package|module)|err_module_not_found/.test(lower)) return false;
217
226
  if (!lower) return true;
@@ -284,9 +293,12 @@ function humanizeAgentFailDetail(detail) {
284
293
  if (/corrupt local agent checkpoint|missing root blob|truncated crash dump/.test(lower)) {
285
294
  return `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
286
295
  }
287
- if (/connection stalled/.test(lower)) {
296
+ if (/connection stalled/.test(lower) || looksLikeCursorHttp2StreamClose(raw)) {
288
297
  return /retry the turn/i.test(raw) ? raw : `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
289
298
  }
299
+ if (looksLikeBrightsyEmptyCompletion(raw)) {
300
+ return /retry the turn/i.test(raw) ? raw : `${raw} \u2014 retry the turn (Brightsy returned an empty model completion).`;
301
+ }
290
302
  if (/network request failed|cursor startup failed:.+\(retryable\)/.test(lower)) {
291
303
  return /retry the turn/i.test(raw) ? raw : `${raw} \u2014 retry the turn (a transient Cursor SDK network error).`;
292
304
  }
@@ -1098,6 +1110,8 @@ export {
1098
1110
  looksLikeV8Oom,
1099
1111
  summarizeTurnStderr,
1100
1112
  looksLikeInvalidAgentSession,
1113
+ looksLikeCursorHttp2StreamClose,
1114
+ looksLikeBrightsyEmptyCompletion,
1101
1115
  shouldRetryFailedAgentTurn,
1102
1116
  looksLikeAgentFailureMessage,
1103
1117
  fallbackTurnFailDetail,
@@ -5,6 +5,7 @@ import {
5
5
  } from "./chunk-YWI3IBP2.js";
6
6
  import {
7
7
  PLAN_MODE_INSTRUCTION,
8
+ adjustBrightsyTurnExit,
8
9
  applyAgentEvent,
9
10
  applyAgentRunnerHeapEnv,
10
11
  applyTurnUsage,
@@ -37,7 +38,7 @@ import {
37
38
  summarizeTurnStderr,
38
39
  toolDescription,
39
40
  turnFailChatText
40
- } from "./chunk-PQBRCBIE.js";
41
+ } from "./chunk-SYXSSU7G.js";
41
42
  import {
42
43
  SLACK_REPLY_FORMATTING,
43
44
  addWorkspace,
@@ -407,7 +408,7 @@ async function continueSourceThread(threadId, prompt) {
407
408
  await continueOnReply(threadId, prompt);
408
409
  return;
409
410
  }
410
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-SANGGLQV.js");
411
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-L2H42HPW.js");
411
412
  await getOrchestrator2().send(threadId, prompt);
412
413
  } catch {
413
414
  }
@@ -898,11 +899,11 @@ async function spawnAgentTurn(thread, input, onEvent) {
898
899
  consume(child.stderr, "stderr");
899
900
  const done = child.then((result) => {
900
901
  outbound.flush();
901
- const exitCode = result.exitCode ?? null;
902
- onEvent({ type: "exit", data: exitCode });
903
902
  const finalized = finalizeParts(parts);
904
903
  const rawText = assistantText.trim() || partsToAssistantText(finalized);
905
904
  const text = thread.agent === "brightsy" ? stripBrightsyNdjsonNoise(rawText) : rawText;
905
+ const exitCode = thread.agent === "brightsy" ? adjustBrightsyTurnExit(result.exitCode ?? null, text) : result.exitCode ?? null;
906
+ onEvent({ type: "exit", data: exitCode });
906
907
  return { exitCode, sessionId, assistantText: text, parts: finalized, usage };
907
908
  });
908
909
  return {
@@ -6546,7 +6547,7 @@ function formatScheduledPrompt(name, prompt) {
6546
6547
  ${prompt}`;
6547
6548
  }
6548
6549
  async function defaultDeps() {
6549
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-SANGGLQV.js");
6550
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-L2H42HPW.js");
6550
6551
  const orch = getOrchestrator2();
6551
6552
  return {
6552
6553
  findThread: (id) => findThreadByRef(id),
@@ -25,11 +25,12 @@ import {
25
25
  formatUnknownDetail,
26
26
  isAsarPath,
27
27
  looksLikeAgentFailureMessage,
28
+ looksLikeBrightsyEmptyCompletion,
28
29
  packagedCursorRunnerPath,
29
30
  packagedMcpStdioPath,
30
31
  parseCursorRunnerLine,
31
32
  resolveNodeLaunch
32
- } from "./chunk-NLHFAMC2.js";
33
+ } from "./chunk-CQ5R3VPP.js";
33
34
  import {
34
35
  claudeChromeEnabled,
35
36
  isAbleTimeConnected,
@@ -268,6 +269,12 @@ function parseBrightsyCliLine(line) {
268
269
  try {
269
270
  const obj = JSON.parse(trimmed);
270
271
  if (obj.type === "text" && typeof obj.text === "string") {
272
+ if (looksLikeBrightsyEmptyCompletion(obj.text)) {
273
+ return [
274
+ { type: "stderr", data: obj.text },
275
+ { type: "stdout", data: `Error: ${obj.text}` }
276
+ ];
277
+ }
271
278
  return { type: "stdout", data: obj.text };
272
279
  }
273
280
  if (obj.type === "thinking" && typeof obj.text === "string") {
@@ -311,7 +318,7 @@ function parseBrightsyCliLine(line) {
311
318
  if (trimmed.startsWith("{") && /"type"\s*:\s*"(tool_use|tool_result|tool|text|thinking|usage|done|error)"/.test(trimmed)) {
312
319
  return null;
313
320
  }
314
- if (/error|failed|unauthorized|quota|limit|not logged in/i.test(trimmed)) {
321
+ if (looksLikeBrightsyEmptyCompletion(trimmed) || /error|failed|unauthorized|quota|limit|not logged in/i.test(trimmed)) {
315
322
  return [
316
323
  { type: "stderr", data: trimmed },
317
324
  { type: "stdout", data: `Error: ${trimmed}` }
@@ -320,6 +327,12 @@ function parseBrightsyCliLine(line) {
320
327
  return { type: "stdout", data: line };
321
328
  }
322
329
  }
330
+ function adjustBrightsyTurnExit(exitCode, assistantText) {
331
+ if (looksLikeBrightsyEmptyCompletion(assistantText) && (exitCode === 0 || exitCode == null)) {
332
+ return 2;
333
+ }
334
+ return exitCode;
335
+ }
323
336
  async function fetchTeamChatTargets(team) {
324
337
  const endpoint = (team.endpoint || "https://brightsy.ai").replace(/\/$/, "");
325
338
  let agents = [
@@ -3678,6 +3691,7 @@ export {
3678
3691
  countCacheControlBlocks,
3679
3692
  MAX_ANTHROPIC_CACHE_CONTROL_BLOCKS,
3680
3693
  parseBrightsyCliLine,
3694
+ adjustBrightsyTurnExit,
3681
3695
  listBrightsyChatTargets,
3682
3696
  brightsyAdapter,
3683
3697
  parseMcpList,
@@ -350,11 +350,20 @@ function looksLikeInvalidAgentSession(text) {
350
350
  if (!lower) return false;
351
351
  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);
352
352
  }
353
+ function looksLikeCursorHttp2StreamClose(text) {
354
+ return /nghttp2/i.test(text.trim());
355
+ }
356
+ function looksLikeBrightsyEmptyCompletion(text) {
357
+ const core = text.trim().replace(/^exit\s*\d+:\s*/i, "").replace(/^error:\s*/i, "").trim();
358
+ return /^i did not receive a valid model response\. please try again\.$/i.test(core);
359
+ }
353
360
  function looksLikeRetryableRunnerCrash(text) {
354
361
  if (looksLikeAgentFailureMessage(text)) return false;
355
362
  if (looksLikeInvalidAgentSession(text)) return false;
356
363
  if (looksLikeV8Oom(text)) return false;
357
364
  if (looksLikeHugeToolResultDump(text)) return true;
365
+ if (looksLikeCursorHttp2StreamClose(text)) return true;
366
+ if (looksLikeBrightsyEmptyCompletion(text)) return true;
358
367
  const lower = text.trim().toLowerCase();
359
368
  if (/cannot find (?:package|module)|err_module_not_found/.test(lower)) return false;
360
369
  if (!lower) return true;
@@ -427,9 +436,12 @@ function humanizeAgentFailDetail(detail) {
427
436
  if (/corrupt local agent checkpoint|missing root blob|truncated crash dump/.test(lower)) {
428
437
  return `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
429
438
  }
430
- if (/connection stalled/.test(lower)) {
439
+ if (/connection stalled/.test(lower) || looksLikeCursorHttp2StreamClose(raw)) {
431
440
  return /retry the turn/i.test(raw) ? raw : `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
432
441
  }
442
+ if (looksLikeBrightsyEmptyCompletion(raw)) {
443
+ return /retry the turn/i.test(raw) ? raw : `${raw} \u2014 retry the turn (Brightsy returned an empty model completion).`;
444
+ }
433
445
  if (/network request failed|cursor startup failed:.+\(retryable\)/.test(lower)) {
434
446
  return /retry the turn/i.test(raw) ? raw : `${raw} \u2014 retry the turn (a transient Cursor SDK network error).`;
435
447
  }
@@ -546,6 +558,12 @@ function parseBrightsyCliLine(line) {
546
558
  try {
547
559
  const obj = JSON.parse(trimmed);
548
560
  if (obj.type === "text" && typeof obj.text === "string") {
561
+ if (looksLikeBrightsyEmptyCompletion(obj.text)) {
562
+ return [
563
+ { type: "stderr", data: obj.text },
564
+ { type: "stdout", data: `Error: ${obj.text}` }
565
+ ];
566
+ }
549
567
  return { type: "stdout", data: obj.text };
550
568
  }
551
569
  if (obj.type === "thinking" && typeof obj.text === "string") {
@@ -589,7 +607,7 @@ function parseBrightsyCliLine(line) {
589
607
  if (trimmed.startsWith("{") && /"type"\s*:\s*"(tool_use|tool_result|tool|text|thinking|usage|done|error)"/.test(trimmed)) {
590
608
  return null;
591
609
  }
592
- if (/error|failed|unauthorized|quota|limit|not logged in/i.test(trimmed)) {
610
+ if (looksLikeBrightsyEmptyCompletion(trimmed) || /error|failed|unauthorized|quota|limit|not logged in/i.test(trimmed)) {
593
611
  return [
594
612
  { type: "stderr", data: trimmed },
595
613
  { type: "stdout", data: `Error: ${trimmed}` }
@@ -598,6 +616,12 @@ function parseBrightsyCliLine(line) {
598
616
  return { type: "stdout", data: line };
599
617
  }
600
618
  }
619
+ function adjustBrightsyTurnExit(exitCode, assistantText) {
620
+ if (looksLikeBrightsyEmptyCompletion(assistantText) && (exitCode === 0 || exitCode == null)) {
621
+ return 2;
622
+ }
623
+ return exitCode;
624
+ }
601
625
  async function fetchTeamChatTargets(team) {
602
626
  const endpoint = (team.endpoint || "https://brightsy.ai").replace(/\/$/, "");
603
627
  let agents = [
@@ -4364,6 +4388,7 @@ export {
4364
4388
  encodeBrightsyTarget,
4365
4389
  decodeBrightsyTarget,
4366
4390
  parseBrightsyCliLine,
4391
+ adjustBrightsyTurnExit,
4367
4392
  listBrightsyChatTargets,
4368
4393
  brightsyAdapter,
4369
4394
  toolDescription,
package/dist/index.cjs CHANGED
@@ -8864,11 +8864,20 @@ function looksLikeInvalidAgentSession(text7) {
8864
8864
  if (!lower) return false;
8865
8865
  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);
8866
8866
  }
8867
+ function looksLikeCursorHttp2StreamClose(text7) {
8868
+ return /nghttp2/i.test(text7.trim());
8869
+ }
8870
+ function looksLikeBrightsyEmptyCompletion(text7) {
8871
+ const core = text7.trim().replace(/^exit\s*\d+:\s*/i, "").replace(/^error:\s*/i, "").trim();
8872
+ return /^i did not receive a valid model response\. please try again\.$/i.test(core);
8873
+ }
8867
8874
  function looksLikeRetryableRunnerCrash(text7) {
8868
8875
  if (looksLikeAgentFailureMessage(text7)) return false;
8869
8876
  if (looksLikeInvalidAgentSession(text7)) return false;
8870
8877
  if (looksLikeV8Oom(text7)) return false;
8871
8878
  if (looksLikeHugeToolResultDump(text7)) return true;
8879
+ if (looksLikeCursorHttp2StreamClose(text7)) return true;
8880
+ if (looksLikeBrightsyEmptyCompletion(text7)) return true;
8872
8881
  const lower = text7.trim().toLowerCase();
8873
8882
  if (/cannot find (?:package|module)|err_module_not_found/.test(lower)) return false;
8874
8883
  if (!lower) return true;
@@ -8941,9 +8950,12 @@ function humanizeAgentFailDetail(detail) {
8941
8950
  if (/corrupt local agent checkpoint|missing root blob|truncated crash dump/.test(lower)) {
8942
8951
  return `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
8943
8952
  }
8944
- if (/connection stalled/.test(lower)) {
8953
+ if (/connection stalled/.test(lower) || looksLikeCursorHttp2StreamClose(raw)) {
8945
8954
  return /retry the turn/i.test(raw) ? raw : `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
8946
8955
  }
8956
+ if (looksLikeBrightsyEmptyCompletion(raw)) {
8957
+ return /retry the turn/i.test(raw) ? raw : `${raw} \u2014 retry the turn (Brightsy returned an empty model completion).`;
8958
+ }
8947
8959
  if (/network request failed|cursor startup failed:.+\(retryable\)/.test(lower)) {
8948
8960
  return /retry the turn/i.test(raw) ? raw : `${raw} \u2014 retry the turn (a transient Cursor SDK network error).`;
8949
8961
  }
@@ -9685,6 +9697,12 @@ function parseBrightsyCliLine(line) {
9685
9697
  try {
9686
9698
  const obj = JSON.parse(trimmed);
9687
9699
  if (obj.type === "text" && typeof obj.text === "string") {
9700
+ if (looksLikeBrightsyEmptyCompletion(obj.text)) {
9701
+ return [
9702
+ { type: "stderr", data: obj.text },
9703
+ { type: "stdout", data: `Error: ${obj.text}` }
9704
+ ];
9705
+ }
9688
9706
  return { type: "stdout", data: obj.text };
9689
9707
  }
9690
9708
  if (obj.type === "thinking" && typeof obj.text === "string") {
@@ -9728,7 +9746,7 @@ function parseBrightsyCliLine(line) {
9728
9746
  if (trimmed.startsWith("{") && /"type"\s*:\s*"(tool_use|tool_result|tool|text|thinking|usage|done|error)"/.test(trimmed)) {
9729
9747
  return null;
9730
9748
  }
9731
- if (/error|failed|unauthorized|quota|limit|not logged in/i.test(trimmed)) {
9749
+ if (looksLikeBrightsyEmptyCompletion(trimmed) || /error|failed|unauthorized|quota|limit|not logged in/i.test(trimmed)) {
9732
9750
  return [
9733
9751
  { type: "stderr", data: trimmed },
9734
9752
  { type: "stdout", data: `Error: ${trimmed}` }
@@ -9737,6 +9755,12 @@ function parseBrightsyCliLine(line) {
9737
9755
  return { type: "stdout", data: line };
9738
9756
  }
9739
9757
  }
9758
+ function adjustBrightsyTurnExit(exitCode, assistantText) {
9759
+ if (looksLikeBrightsyEmptyCompletion(assistantText) && (exitCode === 0 || exitCode == null)) {
9760
+ return 2;
9761
+ }
9762
+ return exitCode;
9763
+ }
9740
9764
  async function fetchTeamChatTargets(team) {
9741
9765
  const endpoint = (team.endpoint || "https://brightsy.ai").replace(/\/$/, "");
9742
9766
  let agents = [
@@ -14215,11 +14239,11 @@ async function spawnAgentTurn(thread, input, onEvent) {
14215
14239
  consume(child.stderr, "stderr");
14216
14240
  const done = child.then((result) => {
14217
14241
  outbound.flush();
14218
- const exitCode = result.exitCode ?? null;
14219
- onEvent({ type: "exit", data: exitCode });
14220
14242
  const finalized = finalizeParts(parts);
14221
14243
  const rawText = assistantText.trim() || partsToAssistantText(finalized);
14222
14244
  const text7 = thread.agent === "brightsy" ? stripBrightsyNdjsonNoise(rawText) : rawText;
14245
+ const exitCode = thread.agent === "brightsy" ? adjustBrightsyTurnExit(result.exitCode ?? null, text7) : result.exitCode ?? null;
14246
+ onEvent({ type: "exit", data: exitCode });
14223
14247
  return { exitCode, sessionId, assistantText: text7, parts: finalized, usage };
14224
14248
  });
14225
14249
  return {
package/dist/index.js CHANGED
@@ -268,7 +268,7 @@ import {
268
268
  worktreeCleanupSettings,
269
269
  wrapReviewSkillMarkdown,
270
270
  writeWorktreeFile
271
- } from "./chunk-SIIDQV2B.js";
271
+ } from "./chunk-6YS5IKD2.js";
272
272
  import {
273
273
  LEGACY_PLAN_FILE_REL,
274
274
  PLAN_FILE_NAME,
@@ -376,7 +376,7 @@ import {
376
376
  withEventParentId,
377
377
  withEventsParentId,
378
378
  writeInjectedMcpConfig
379
- } from "./chunk-N5JBG3BF.js";
379
+ } from "./chunk-EA67VQM2.js";
380
380
  import {
381
381
  CLOUD_COORDINATOR_BUSY_REPLY,
382
382
  CLOUD_COORDINATOR_STOPPED_REPLY,
@@ -562,7 +562,7 @@ import {
562
562
  trustSystemCertificates,
563
563
  turnCostUsdFromCursorUsage,
564
564
  withMaxOldSpaceSize
565
- } from "./chunk-NLHFAMC2.js";
565
+ } from "./chunk-CQ5R3VPP.js";
566
566
  import {
567
567
  FOLLOW_UP_BEHAVIORS,
568
568
  GITHUB_GIT_AUTH_MODES,
@@ -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 = [
@@ -12448,11 +12472,11 @@ async function spawnAgentTurn(thread, input, onEvent) {
12448
12472
  consume(child.stderr, "stderr");
12449
12473
  const done = child.then((result) => {
12450
12474
  outbound.flush();
12451
- const exitCode = result.exitCode ?? null;
12452
- onEvent({ type: "exit", data: exitCode });
12453
12475
  const finalized = finalizeParts(parts);
12454
12476
  const rawText = assistantText.trim() || partsToAssistantText(finalized);
12455
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 });
12456
12480
  return { exitCode, sessionId, assistantText: text7, parts: finalized, usage };
12457
12481
  });
12458
12482
  return {
@@ -38,7 +38,7 @@ import {
38
38
  syncBoardPins,
39
39
  updateSchedule,
40
40
  waitForDetachedJob
41
- } from "../chunk-ZG7RONNQ.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-PQBRCBIE.js";
51
+ } from "../chunk-SYXSSU7G.js";
52
52
  import {
53
53
  GLOBAL_WORKSPACE_ID,
54
54
  isCloudCoordinatorThread,
@@ -7,9 +7,9 @@ import {
7
7
  resolveSendFollowUp,
8
8
  startOrchestration,
9
9
  waitForPidExit
10
- } from "./chunk-SIIDQV2B.js";
10
+ } from "./chunk-6YS5IKD2.js";
11
11
  import "./chunk-DJU54RYW.js";
12
- import "./chunk-N5JBG3BF.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-ZG7RONNQ.js";
12
+ } from "./chunk-DROELG52.js";
13
13
  import "./chunk-YWI3IBP2.js";
14
- import "./chunk-PQBRCBIE.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.197",
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",