@sideboard-ai/core 0.1.130 → 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.
@@ -12,7 +12,7 @@ import {
12
12
  ensureCursorRipgrepPath,
13
13
  fetchCursorTurnCostUsd,
14
14
  formatUnknownDetail
15
- } from "../chunk-IUJOI7KK.js";
15
+ } from "../chunk-FSCQXMGI.js";
16
16
  import {
17
17
  dropNestedElectronEnvFromProcess
18
18
  } from "../chunk-4TR3HZFT.js";
@@ -28,8 +28,9 @@ import {
28
28
  resolveCursorModelId,
29
29
  resolveLoginCommand,
30
30
  resolveQuotaFallbackAgent
31
- } from "./chunk-NV73AO7Q.js";
32
- import "./chunk-EKIDHL2T.js";
31
+ } from "./chunk-MGYCQAZ6.js";
32
+ import "./chunk-SIPDL62B.js";
33
+ import "./chunk-AQESMTCZ.js";
33
34
  import {
34
35
  ORCHESTRATOR_AGENT_KINDS,
35
36
  assertOrchestratorCapableAgent,
@@ -42,7 +43,7 @@ import {
42
43
  parseCursorRunnerLine,
43
44
  preferredCursorCostCents,
44
45
  turnCostUsdFromCursorUsage
45
- } from "./chunk-IUJOI7KK.js";
46
+ } from "./chunk-FSCQXMGI.js";
46
47
  import "./chunk-UDQI3N47.js";
47
48
  import "./chunk-FKOIHGKV.js";
48
49
  import "./chunk-HUYEU4GS.js";
@@ -34,8 +34,9 @@ import {
34
34
  resolveLoginCommand,
35
35
  resolveQuotaFallbackAgent,
36
36
  turnCostUsdFromCursorUsage
37
- } from "./chunk-ZMUOPTKZ.js";
38
- import "./chunk-YMRT2DU6.js";
37
+ } from "./chunk-PK4PQUPU.js";
38
+ import "./chunk-LLX3OMHU.js";
39
+ import "./chunk-R7FGPBKL.js";
39
40
  import {
40
41
  ORCHESTRATOR_AGENT_KINDS,
41
42
  assertOrchestratorCapableAgent,
@@ -0,0 +1,129 @@
1
+ // src/http/fetch.ts
2
+ var injected = null;
3
+ function setHttpFetchImpl(fetchImpl) {
4
+ injected = fetchImpl;
5
+ }
6
+ function formatFetchError(err, url) {
7
+ if (!(err instanceof Error)) return `${String(err)} (${url})`;
8
+ const cause = err.cause;
9
+ let detail = "";
10
+ if (cause instanceof Error) {
11
+ const code = typeof cause.code === "string" ? cause.code : void 0;
12
+ detail = code ? ` [${code}: ${cause.message}]` : ` [${cause.message}]`;
13
+ } else if (cause != null) {
14
+ detail = ` [${String(cause)}]`;
15
+ }
16
+ return `${err.message}${detail} (${url})`;
17
+ }
18
+ async function httpFetch(input, init) {
19
+ const url = typeof input === "string" ? input : input.href;
20
+ const fn = injected ?? globalThis.fetch.bind(globalThis);
21
+ try {
22
+ return await fn(url, init);
23
+ } catch (err) {
24
+ throw new Error(formatFetchError(err, url));
25
+ }
26
+ }
27
+
28
+ // src/brightsy/config.ts
29
+ import { existsSync, readFileSync, writeFileSync } from "fs";
30
+ import { homedir } from "os";
31
+ import { join } from "path";
32
+ function brightsyConfigPath() {
33
+ const override = process.env.BRIGHTSY_CONFIG?.trim();
34
+ if (override) return override;
35
+ return join(homedir(), ".brightsy", "config.json");
36
+ }
37
+ function loadBrightsyConfig() {
38
+ const path = brightsyConfigPath();
39
+ if (!existsSync(path)) {
40
+ throw new Error("Brightsy not logged in \u2014 run `brightsy login` first");
41
+ }
42
+ const raw = JSON.parse(readFileSync(path, "utf8"));
43
+ if (!raw.access_token || !raw.account_id) {
44
+ throw new Error("Brightsy config incomplete \u2014 run `brightsy login`");
45
+ }
46
+ return raw;
47
+ }
48
+ function saveBrightsyConfig(cfg) {
49
+ writeFileSync(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
50
+ `, {
51
+ mode: 384
52
+ });
53
+ }
54
+
55
+ // src/brightsy/oauth.ts
56
+ var REFRESH_SKEW_MS = 6e4;
57
+ function brightsyAccessTokenNeedsRefresh(expiresAt, now = Date.now()) {
58
+ if (typeof expiresAt !== "number" || !Number.isFinite(expiresAt) || expiresAt <= 0) {
59
+ return true;
60
+ }
61
+ return now >= expiresAt - REFRESH_SKEW_MS;
62
+ }
63
+ function applyGrant(current, grant) {
64
+ return {
65
+ access_token: grant.access_token,
66
+ refresh_token: grant.refresh_token || current.refresh_token,
67
+ expires_at: grant.expires_at ?? current.expires_at
68
+ };
69
+ }
70
+ async function refreshBrightsyAccessToken(opts) {
71
+ const endpoint = (opts.endpoint || "https://brightsy.ai").replace(/\/$/, "");
72
+ const url = `${endpoint}/oauth/token`;
73
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);
74
+ let res;
75
+ try {
76
+ res = await fetchImpl(url, {
77
+ method: "POST",
78
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
79
+ body: new URLSearchParams({
80
+ grant_type: "refresh_token",
81
+ refresh_token: opts.refreshToken,
82
+ client_id: opts.clientId || "brightsy-cli"
83
+ })
84
+ });
85
+ } catch (err) {
86
+ throw new Error(formatFetchError(err, url));
87
+ }
88
+ if (!res.ok) return null;
89
+ const data = await res.json();
90
+ if (!data.access_token) return null;
91
+ return {
92
+ access_token: data.access_token,
93
+ refresh_token: data.refresh_token,
94
+ expires_at: data.expires_in ? Date.now() + data.expires_in * 1e3 : void 0
95
+ };
96
+ }
97
+ async function ensureBrightsyLocalConfigFresh(opts) {
98
+ let cfg;
99
+ try {
100
+ cfg = loadBrightsyConfig();
101
+ } catch {
102
+ return null;
103
+ }
104
+ if (!cfg.refresh_token || !brightsyAccessTokenNeedsRefresh(cfg.expires_at)) {
105
+ return cfg;
106
+ }
107
+ const grant = await refreshBrightsyAccessToken({
108
+ endpoint: cfg.endpoint || "https://brightsy.ai",
109
+ refreshToken: cfg.refresh_token,
110
+ clientId: cfg.oauth_client_id,
111
+ fetchImpl: opts?.fetchImpl
112
+ });
113
+ if (!grant) return cfg;
114
+ const next = { ...cfg, ...applyGrant(cfg, grant) };
115
+ saveBrightsyConfig(next);
116
+ return next;
117
+ }
118
+
119
+ export {
120
+ brightsyConfigPath,
121
+ loadBrightsyConfig,
122
+ saveBrightsyConfig,
123
+ setHttpFetchImpl,
124
+ formatFetchError,
125
+ httpFetch,
126
+ brightsyAccessTokenNeedsRefresh,
127
+ refreshBrightsyAccessToken,
128
+ ensureBrightsyLocalConfigFresh
129
+ };
@@ -211,11 +211,40 @@ function humanizeAgentFailDetail(detail) {
211
211
  }
212
212
  function turnFailChatText(opts) {
213
213
  const chat = opts.assistantText.trim();
214
- if (chat) return chat;
215
- if (opts.exitCode === 0) return "";
214
+ if (opts.exitCode === 0) return chat;
216
215
  const detail = opts.detail.trim();
217
- if (detail) return humanizeAgentFailDetail(detail);
218
- return formatTurnExitError(opts.exitCode ?? 1, "");
216
+ const fail = detail ? humanizeAgentFailDetail(detail) : formatTurnExitError(opts.exitCode ?? 1, "");
217
+ if (!chat) return fail;
218
+ const failCore = fail.replace(/^exit\s*\d+:\s*/i, "").trim();
219
+ if (looksLikeAgentFailureMessage(chat) || failCore && chat.includes(failCore) || chat.includes(fail)) {
220
+ return chat;
221
+ }
222
+ return `${chat}
223
+
224
+ ${fail}`;
225
+ }
226
+ function shouldFeedErrorBackToAgent(opts) {
227
+ const detail = opts.detail.trim();
228
+ const chat = (opts.assistantText ?? "").trim();
229
+ if (looksLikeAgentFailureMessage(detail) || looksLikeAgentFailureMessage(chat)) {
230
+ return false;
231
+ }
232
+ if (looksLikeInvalidAgentSession(detail) || looksLikeV8Oom(detail) || looksLikeRetryableRunnerCrash(detail)) {
233
+ return true;
234
+ }
235
+ if (/without details|segfault|sig(?:segv|abrt|ill)|fatal error/i.test(detail)) {
236
+ return true;
237
+ }
238
+ return !chat && (opts.partsCount ?? 0) === 0;
239
+ }
240
+ function formatAgentErrorContinuePrompt(detail) {
241
+ const fail = humanizeAgentFailDetail(detail.trim()) || "the agent process exited without details";
242
+ return [
243
+ "The previous agent process ended before it finished. Error:",
244
+ fail,
245
+ "",
246
+ "Continue from where you left off. Use the error to recover \u2014 do not restart the whole task unless the error requires it. If you cannot continue, say why."
247
+ ].join("\n");
219
248
  }
220
249
  function formatTurnExitError(exitCode, stderrSummary) {
221
250
  const code = exitCode ?? 1;
@@ -860,6 +889,8 @@ export {
860
889
  looksLikeAgentFailureMessage,
861
890
  fallbackTurnFailDetail,
862
891
  turnFailChatText,
892
+ shouldFeedErrorBackToAgent,
893
+ formatAgentErrorContinuePrompt,
863
894
  formatTurnExitError,
864
895
  packagedCursorRunnerPath,
865
896
  packagedMcpStdioPath,
@@ -1,5 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import {
4
+ brightsyAccessTokenNeedsRefresh,
5
+ ensureBrightsyLocalConfigFresh,
6
+ loadBrightsyConfig,
7
+ refreshBrightsyAccessToken,
8
+ saveBrightsyConfig
9
+ } from "./chunk-R7FGPBKL.js";
3
10
  import {
4
11
  run
5
12
  } from "./chunk-KPIYENTF.js";
@@ -8,33 +15,8 @@ import {
8
15
  } from "./chunk-BD2ICC4D.js";
9
16
 
10
17
  // src/brightsy/connected-teams.ts
11
- import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
12
- import { join as join2 } from "path";
13
-
14
- // src/brightsy/config.ts
15
- import { existsSync, readFileSync, writeFileSync } from "fs";
16
- import { homedir } from "os";
18
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
17
19
  import { join } from "path";
18
- function brightsyConfigPath() {
19
- return join(homedir(), ".brightsy", "config.json");
20
- }
21
- function loadBrightsyConfig() {
22
- const path = brightsyConfigPath();
23
- if (!existsSync(path)) {
24
- throw new Error("Brightsy not logged in \u2014 run `brightsy login` first");
25
- }
26
- const raw = JSON.parse(readFileSync(path, "utf8"));
27
- if (!raw.access_token || !raw.account_id) {
28
- throw new Error("Brightsy config incomplete \u2014 run `brightsy login`");
29
- }
30
- return raw;
31
- }
32
- function saveBrightsyConfig(cfg) {
33
- writeFileSync(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
34
- `, {
35
- mode: 384
36
- });
37
- }
38
20
 
39
21
  // src/brightsy/accounts.ts
40
22
  async function runBrightsyTeamsJson(args) {
@@ -78,13 +60,13 @@ async function performBrightsyAccountSwitch(accountIdOrSlug) {
78
60
 
79
61
  // src/brightsy/connected-teams.ts
80
62
  function storePath() {
81
- return join2(appDataDir(), "brightsy-teams.json");
63
+ return join(appDataDir(), "brightsy-teams.json");
82
64
  }
83
65
  function readStore() {
84
66
  const path = storePath();
85
- if (!existsSync2(path)) return [];
67
+ if (!existsSync(path)) return [];
86
68
  try {
87
- const parsed = JSON.parse(readFileSync2(path, "utf8"));
69
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
88
70
  return Array.isArray(parsed.teams) ? parsed.teams : [];
89
71
  } catch {
90
72
  return [];
@@ -93,7 +75,7 @@ function readStore() {
93
75
  function writeStore(teams) {
94
76
  mkdirSync(appDataDir(), { recursive: true });
95
77
  const path = storePath();
96
- writeFileSync2(path, `${JSON.stringify({ teams }, null, 2)}
78
+ writeFileSync(path, `${JSON.stringify({ teams }, null, 2)}
97
79
  `, {
98
80
  mode: 384
99
81
  });
@@ -129,7 +111,6 @@ function applyConnectedTeamToCli(team) {
129
111
  }
130
112
  async function refreshTeamToken(team) {
131
113
  if (!team.refresh_token) return team;
132
- const endpoint = (team.endpoint || "https://brightsy.ai").replace(/\/$/, "");
133
114
  const cfg = (() => {
134
115
  try {
135
116
  return loadBrightsyConfig();
@@ -137,39 +118,34 @@ async function refreshTeamToken(team) {
137
118
  return null;
138
119
  }
139
120
  })();
140
- const clientId = cfg?.oauth_client_id || "brightsy-cli";
141
- const res = await fetch(`${endpoint}/oauth/token`, {
142
- method: "POST",
143
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
144
- body: new URLSearchParams({
145
- grant_type: "refresh_token",
146
- refresh_token: team.refresh_token,
147
- client_id: clientId
148
- })
121
+ const grant = await refreshBrightsyAccessToken({
122
+ endpoint: team.endpoint || "https://brightsy.ai",
123
+ refreshToken: team.refresh_token,
124
+ clientId: cfg?.oauth_client_id
149
125
  });
150
- if (!res.ok) return team;
151
- const data = await res.json();
152
- if (!data.access_token) return team;
126
+ if (!grant) return team;
153
127
  return {
154
128
  ...team,
155
- access_token: data.access_token,
156
- refresh_token: data.refresh_token || team.refresh_token,
157
- expires_at: data.expires_in ? Date.now() + data.expires_in * 1e3 : team.expires_at
129
+ access_token: grant.access_token,
130
+ refresh_token: grant.refresh_token || team.refresh_token,
131
+ expires_at: grant.expires_at ?? team.expires_at
158
132
  };
159
133
  }
160
134
  async function ensureConnectedBrightsyTeamTokens() {
135
+ await ensureBrightsyLocalConfigFresh().catch(() => null);
161
136
  const teams = readStore();
162
137
  if (teams.length === 0) return [];
163
138
  const next = [];
164
139
  let changed = false;
165
140
  for (const team of teams) {
166
- const expired = typeof team.expires_at === "number" && Date.now() >= team.expires_at - 6e4;
167
- if (!expired) {
141
+ if (!team.refresh_token || !brightsyAccessTokenNeedsRefresh(team.expires_at)) {
168
142
  next.push(team);
169
143
  continue;
170
144
  }
171
145
  const refreshed = await refreshTeamToken(team);
172
- if (refreshed.access_token !== team.access_token) changed = true;
146
+ if (refreshed.access_token !== team.access_token || refreshed.refresh_token !== team.refresh_token || refreshed.expires_at !== team.expires_at) {
147
+ changed = true;
148
+ }
173
149
  next.push(refreshed);
174
150
  }
175
151
  if (changed) writeStore(next);
@@ -257,7 +233,6 @@ function brightsyMcpServerName(slug) {
257
233
  }
258
234
 
259
235
  export {
260
- loadBrightsyConfig,
261
236
  listConnectedBrightsyTeams,
262
237
  getConnectedBrightsyTeamsRaw,
263
238
  applyConnectedTeamToCli,
@@ -2,9 +2,11 @@ import {
2
2
  applyConnectedTeamToCli,
3
3
  brightsyMcpServerName,
4
4
  ensureCliTeamTracked,
5
- ensureConnectedBrightsyTeamTokens,
5
+ ensureConnectedBrightsyTeamTokens
6
+ } from "./chunk-SIPDL62B.js";
7
+ import {
6
8
  loadBrightsyConfig
7
- } from "./chunk-EKIDHL2T.js";
9
+ } from "./chunk-AQESMTCZ.js";
8
10
  import {
9
11
  isOrchestratorThread
10
12
  } from "./chunk-HULESWLI.js";
@@ -20,7 +22,7 @@ import {
20
22
  packagedMcpStdioPath,
21
23
  parseCursorRunnerLine,
22
24
  resolveNodeLaunch
23
- } from "./chunk-IUJOI7KK.js";
25
+ } from "./chunk-FSCQXMGI.js";
24
26
  import {
25
27
  codexUnattendedGitConfigArgs,
26
28
  mergeAgentGitAuthEnv,
@@ -1174,6 +1176,7 @@ function teamEnv(team) {
1174
1176
  const endpoint = (team.endpoint || "https://brightsy.ai").replace(/\/$/, "");
1175
1177
  return {
1176
1178
  BRIGHTSY_API_TOKEN: team.access_token,
1179
+ ...team.refresh_token ? { BRIGHTSY_REFRESH_TOKEN: team.refresh_token } : {},
1177
1180
  BRIGHTSY_ACCOUNT_ID: team.id,
1178
1181
  BRIGHTSY_API_URL: endpoint
1179
1182
  };
@@ -1439,11 +1442,17 @@ var CLAUDE_CHROME_ALLOWED_TOOLS = [
1439
1442
  "Skill(claude-in-chrome)"
1440
1443
  ];
1441
1444
  var CLAUDE_PROMPT_ARG_MAX = 2e5;
1445
+ var mcpListCache = null;
1442
1446
  async function loadMcpServers() {
1447
+ if (mcpListCache && Date.now() - mcpListCache.at < 3e4) {
1448
+ return mcpListCache.servers;
1449
+ }
1443
1450
  const claude = resolveClaudeExecutable();
1444
1451
  const mcpText = await run(claude, ["mcp", "list"], { reject: false });
1445
- return parseMcpList(`${mcpText.stdout}
1452
+ const servers = parseMcpList(`${mcpText.stdout}
1446
1453
  ${mcpText.stderr}`);
1454
+ mcpListCache = { at: Date.now(), servers };
1455
+ return servers;
1447
1456
  }
1448
1457
  function usageFromClaude(usage) {
1449
1458
  if (!usage) return null;
@@ -2704,10 +2713,13 @@ var opencodeAdapter = {
2704
2713
  const part = obj.part;
2705
2714
  const id = part?.tool_use_id ?? part?.id ?? obj.tool_use_id ?? obj.id;
2706
2715
  if (!id) return null;
2716
+ const flagged = obj.isError === true || obj.is_error === true;
2717
+ const errText = formatUnknownDetail(obj.error);
2707
2718
  return {
2708
2719
  type: "tool_result",
2709
2720
  id,
2710
- content: part?.output ?? part?.content ?? obj.output ?? obj.content
2721
+ content: part?.output ?? part?.content ?? obj.output ?? obj.content ?? (errText || void 0),
2722
+ isError: flagged || Boolean(errText)
2711
2723
  };
2712
2724
  }
2713
2725
  if (obj.type === "step_finish" || obj.type === "step-finish") {
@@ -4,9 +4,11 @@ import {
4
4
  applyConnectedTeamToCli,
5
5
  brightsyMcpServerName,
6
6
  ensureCliTeamTracked,
7
- ensureConnectedBrightsyTeamTokens,
7
+ ensureConnectedBrightsyTeamTokens
8
+ } from "./chunk-LLX3OMHU.js";
9
+ import {
8
10
  loadBrightsyConfig
9
- } from "./chunk-YMRT2DU6.js";
11
+ } from "./chunk-R7FGPBKL.js";
10
12
  import {
11
13
  isOrchestratorThread
12
14
  } from "./chunk-MNL4FSKY.js";
@@ -353,11 +355,40 @@ function humanizeAgentFailDetail(detail) {
353
355
  }
354
356
  function turnFailChatText(opts) {
355
357
  const chat = opts.assistantText.trim();
356
- if (chat) return chat;
357
- if (opts.exitCode === 0) return "";
358
+ if (opts.exitCode === 0) return chat;
358
359
  const detail = opts.detail.trim();
359
- if (detail) return humanizeAgentFailDetail(detail);
360
- return formatTurnExitError(opts.exitCode ?? 1, "");
360
+ const fail = detail ? humanizeAgentFailDetail(detail) : formatTurnExitError(opts.exitCode ?? 1, "");
361
+ if (!chat) return fail;
362
+ const failCore = fail.replace(/^exit\s*\d+:\s*/i, "").trim();
363
+ if (looksLikeAgentFailureMessage(chat) || failCore && chat.includes(failCore) || chat.includes(fail)) {
364
+ return chat;
365
+ }
366
+ return `${chat}
367
+
368
+ ${fail}`;
369
+ }
370
+ function shouldFeedErrorBackToAgent(opts) {
371
+ const detail = opts.detail.trim();
372
+ const chat = (opts.assistantText ?? "").trim();
373
+ if (looksLikeAgentFailureMessage(detail) || looksLikeAgentFailureMessage(chat)) {
374
+ return false;
375
+ }
376
+ if (looksLikeInvalidAgentSession(detail) || looksLikeV8Oom(detail) || looksLikeRetryableRunnerCrash(detail)) {
377
+ return true;
378
+ }
379
+ if (/without details|segfault|sig(?:segv|abrt|ill)|fatal error/i.test(detail)) {
380
+ return true;
381
+ }
382
+ return !chat && (opts.partsCount ?? 0) === 0;
383
+ }
384
+ function formatAgentErrorContinuePrompt(detail) {
385
+ const fail = humanizeAgentFailDetail(detail.trim()) || "the agent process exited without details";
386
+ return [
387
+ "The previous agent process ended before it finished. Error:",
388
+ fail,
389
+ "",
390
+ "Continue from where you left off. Use the error to recover \u2014 do not restart the whole task unless the error requires it. If you cannot continue, say why."
391
+ ].join("\n");
361
392
  }
362
393
  function formatTurnExitError(exitCode, stderrSummary) {
363
394
  const code = exitCode ?? 1;
@@ -1495,6 +1526,7 @@ function teamEnv(team) {
1495
1526
  const endpoint = (team.endpoint || "https://brightsy.ai").replace(/\/$/, "");
1496
1527
  return {
1497
1528
  BRIGHTSY_API_TOKEN: team.access_token,
1529
+ ...team.refresh_token ? { BRIGHTSY_REFRESH_TOKEN: team.refresh_token } : {},
1498
1530
  BRIGHTSY_ACCOUNT_ID: team.id,
1499
1531
  BRIGHTSY_API_URL: endpoint
1500
1532
  };
@@ -1757,11 +1789,17 @@ var CLAUDE_CHROME_ALLOWED_TOOLS = [
1757
1789
  "Skill(claude-in-chrome)"
1758
1790
  ];
1759
1791
  var CLAUDE_PROMPT_ARG_MAX = 2e5;
1792
+ var mcpListCache = null;
1760
1793
  async function loadMcpServers() {
1794
+ if (mcpListCache && Date.now() - mcpListCache.at < 3e4) {
1795
+ return mcpListCache.servers;
1796
+ }
1761
1797
  const claude = resolveClaudeExecutable();
1762
1798
  const mcpText = await run(claude, ["mcp", "list"], { reject: false });
1763
- return parseMcpList(`${mcpText.stdout}
1799
+ const servers = parseMcpList(`${mcpText.stdout}
1764
1800
  ${mcpText.stderr}`);
1801
+ mcpListCache = { at: Date.now(), servers };
1802
+ return servers;
1765
1803
  }
1766
1804
  function usageFromClaude(usage) {
1767
1805
  if (!usage) return null;
@@ -3295,10 +3333,13 @@ var opencodeAdapter = {
3295
3333
  const part = obj.part;
3296
3334
  const id = part?.tool_use_id ?? part?.id ?? obj.tool_use_id ?? obj.id;
3297
3335
  if (!id) return null;
3336
+ const flagged = obj.isError === true || obj.is_error === true;
3337
+ const errText = formatUnknownDetail(obj.error);
3298
3338
  return {
3299
3339
  type: "tool_result",
3300
3340
  id,
3301
- content: part?.output ?? part?.content ?? obj.output ?? obj.content
3341
+ content: part?.output ?? part?.content ?? obj.output ?? obj.content ?? (errText || void 0),
3342
+ isError: flagged || Boolean(errText)
3302
3343
  };
3303
3344
  }
3304
3345
  if (obj.type === "step_finish" || obj.type === "step-finish") {
@@ -3822,6 +3863,8 @@ export {
3822
3863
  looksLikeAgentFailureMessage,
3823
3864
  fallbackTurnFailDetail,
3824
3865
  turnFailChatText,
3866
+ shouldFeedErrorBackToAgent,
3867
+ formatAgentErrorContinuePrompt,
3825
3868
  formatTurnExitError,
3826
3869
  sumUsageList,
3827
3870
  applyTurnUsage,
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+
3
+
4
+ // src/http/fetch.ts
5
+ var injected = null;
6
+ function formatFetchError(err, url) {
7
+ if (!(err instanceof Error)) return `${String(err)} (${url})`;
8
+ const cause = err.cause;
9
+ let detail = "";
10
+ if (cause instanceof Error) {
11
+ const code = typeof cause.code === "string" ? cause.code : void 0;
12
+ detail = code ? ` [${code}: ${cause.message}]` : ` [${cause.message}]`;
13
+ } else if (cause != null) {
14
+ detail = ` [${String(cause)}]`;
15
+ }
16
+ return `${err.message}${detail} (${url})`;
17
+ }
18
+ async function httpFetch(input, init) {
19
+ const url = typeof input === "string" ? input : input.href;
20
+ const fn = injected ?? globalThis.fetch.bind(globalThis);
21
+ try {
22
+ return await fn(url, init);
23
+ } catch (err) {
24
+ throw new Error(formatFetchError(err, url));
25
+ }
26
+ }
27
+
28
+ // src/brightsy/config.ts
29
+ import { existsSync, readFileSync, writeFileSync } from "fs";
30
+ import { homedir } from "os";
31
+ import { join } from "path";
32
+ function brightsyConfigPath() {
33
+ const override = process.env.BRIGHTSY_CONFIG?.trim();
34
+ if (override) return override;
35
+ return join(homedir(), ".brightsy", "config.json");
36
+ }
37
+ function loadBrightsyConfig() {
38
+ const path = brightsyConfigPath();
39
+ if (!existsSync(path)) {
40
+ throw new Error("Brightsy not logged in \u2014 run `brightsy login` first");
41
+ }
42
+ const raw = JSON.parse(readFileSync(path, "utf8"));
43
+ if (!raw.access_token || !raw.account_id) {
44
+ throw new Error("Brightsy config incomplete \u2014 run `brightsy login`");
45
+ }
46
+ return raw;
47
+ }
48
+ function saveBrightsyConfig(cfg) {
49
+ writeFileSync(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
50
+ `, {
51
+ mode: 384
52
+ });
53
+ }
54
+
55
+ // src/brightsy/oauth.ts
56
+ var REFRESH_SKEW_MS = 6e4;
57
+ function brightsyAccessTokenNeedsRefresh(expiresAt, now = Date.now()) {
58
+ if (typeof expiresAt !== "number" || !Number.isFinite(expiresAt) || expiresAt <= 0) {
59
+ return true;
60
+ }
61
+ return now >= expiresAt - REFRESH_SKEW_MS;
62
+ }
63
+ function applyGrant(current, grant) {
64
+ return {
65
+ access_token: grant.access_token,
66
+ refresh_token: grant.refresh_token || current.refresh_token,
67
+ expires_at: grant.expires_at ?? current.expires_at
68
+ };
69
+ }
70
+ async function refreshBrightsyAccessToken(opts) {
71
+ const endpoint = (opts.endpoint || "https://brightsy.ai").replace(/\/$/, "");
72
+ const url = `${endpoint}/oauth/token`;
73
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);
74
+ let res;
75
+ try {
76
+ res = await fetchImpl(url, {
77
+ method: "POST",
78
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
79
+ body: new URLSearchParams({
80
+ grant_type: "refresh_token",
81
+ refresh_token: opts.refreshToken,
82
+ client_id: opts.clientId || "brightsy-cli"
83
+ })
84
+ });
85
+ } catch (err) {
86
+ throw new Error(formatFetchError(err, url));
87
+ }
88
+ if (!res.ok) return null;
89
+ const data = await res.json();
90
+ if (!data.access_token) return null;
91
+ return {
92
+ access_token: data.access_token,
93
+ refresh_token: data.refresh_token,
94
+ expires_at: data.expires_in ? Date.now() + data.expires_in * 1e3 : void 0
95
+ };
96
+ }
97
+ async function ensureBrightsyLocalConfigFresh(opts) {
98
+ let cfg;
99
+ try {
100
+ cfg = loadBrightsyConfig();
101
+ } catch {
102
+ return null;
103
+ }
104
+ if (!cfg.refresh_token || !brightsyAccessTokenNeedsRefresh(cfg.expires_at)) {
105
+ return cfg;
106
+ }
107
+ const grant = await refreshBrightsyAccessToken({
108
+ endpoint: cfg.endpoint || "https://brightsy.ai",
109
+ refreshToken: cfg.refresh_token,
110
+ clientId: cfg.oauth_client_id,
111
+ fetchImpl: opts?.fetchImpl
112
+ });
113
+ if (!grant) return cfg;
114
+ const next = { ...cfg, ...applyGrant(cfg, grant) };
115
+ saveBrightsyConfig(next);
116
+ return next;
117
+ }
118
+
119
+ export {
120
+ loadBrightsyConfig,
121
+ saveBrightsyConfig,
122
+ httpFetch,
123
+ brightsyAccessTokenNeedsRefresh,
124
+ refreshBrightsyAccessToken,
125
+ ensureBrightsyLocalConfigFresh
126
+ };