@sideboard-ai/core 0.1.197 → 0.1.199

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.
Files changed (38) hide show
  1. package/dist/{abletime-JINF5KJV.js → abletime-3CSCNAXU.js} +2 -2
  2. package/dist/{abletime-HR6P4GJH.js → abletime-H74RNIKV.js} +2 -2
  3. package/dist/agents/cursor-runner.cjs +49 -45
  4. package/dist/agents/cursor-runner.js +5 -3
  5. package/dist/{agents-IVDCAG7T.js → agents-AVH44CBS.js} +27 -5
  6. package/dist/{agents-BGT5DUGU.js → agents-BOYOK56K.js} +26 -4
  7. package/dist/{app-settings-J775CYOO.js → app-settings-FMTDH5RD.js} +3 -1
  8. package/dist/{app-settings-5KHVKJKO.js → app-settings-MMPLJHWF.js} +3 -1
  9. package/dist/{chunk-S5GLSRLP.js → chunk-2IQ6W47G.js} +10 -0
  10. package/dist/{chunk-HZTHWU6A.js → chunk-BLOKJY4U.js} +2 -2
  11. package/dist/{chunk-E37JHCO3.js → chunk-CA7PWMQ7.js} +1 -1
  12. package/dist/{chunk-NLHFAMC2.js → chunk-CQ5R3VPP.js} +15 -1
  13. package/dist/{chunk-ZG7RONNQ.js → chunk-CTABTALL.js} +22 -21
  14. package/dist/{chunk-PQBRCBIE.js → chunk-DU2ICKLA.js} +382 -6
  15. package/dist/{chunk-S4QCBBXS.js → chunk-DU4ID3DA.js} +1 -1
  16. package/dist/{chunk-AH6WIBLY.js → chunk-HQKGK4KO.js} +10 -0
  17. package/dist/{chunk-N5JBG3BF.js → chunk-KI4YI72Y.js} +371 -6
  18. package/dist/{chunk-SIIDQV2B.js → chunk-PPVN3APP.js} +24 -23
  19. package/dist/{chunk-KTHRWHA3.js → chunk-PTAMHXBT.js} +1 -1
  20. package/dist/{chunk-DP4TKAMZ.js → chunk-QFABSE3Q.js} +1 -1
  21. package/dist/{chunk-6YTI2HBG.js → chunk-S4EEYOSU.js} +2 -2
  22. package/dist/{coordinator-prompt-IX66YUJ2.js → coordinator-prompt-LB3GA3U4.js} +3 -3
  23. package/dist/{coordinator-prompt-LYKS3YNG.js → coordinator-prompt-ROWPLVQ3.js} +3 -3
  24. package/dist/{global-workspace-SNJ2Z6SL.js → global-workspace-C5XKWCRH.js} +3 -3
  25. package/dist/{global-workspace-4QX6UEOY.js → global-workspace-V2A4YKZ6.js} +3 -3
  26. package/dist/index.cjs +688 -262
  27. package/dist/index.d.cts +78 -1
  28. package/dist/index.d.ts +78 -1
  29. package/dist/index.js +33 -9
  30. package/dist/mcp/run-stdio.cjs +317 -258
  31. package/dist/mcp/run-stdio.js +8 -8
  32. package/dist/{orchestrator-SANGGLQV.js → orchestrator-AJGDZEAS.js} +5 -5
  33. package/dist/{orchestrator-HB4LZ2AA.js → orchestrator-NCGYNVYN.js} +6 -6
  34. package/dist/{workspaces-URMUVGAQ.js → workspaces-GWEKIS3B.js} +3 -3
  35. package/dist/{workspaces-UTOYGBYB.js → workspaces-WIRJQ6HA.js} +3 -3
  36. package/dist/{worktree-RM6UWNGZ.js → worktree-QA4J75FI.js} +2 -2
  37. package/dist/{worktree-R43JOR4V.js → worktree-UI5WJC3A.js} +2 -2
  38. package/package.json +1 -1
@@ -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-DU2ICKLA.js";
41
42
  import {
42
43
  SLACK_REPLY_FORMATTING,
43
44
  addWorkspace,
@@ -60,7 +61,7 @@ import {
60
61
  stageAbsolutePathsAsAttachments,
61
62
  stageBuffersAsAttachments,
62
63
  syncWorkspacesFromThreads
63
- } from "./chunk-6YTI2HBG.js";
64
+ } from "./chunk-S4EEYOSU.js";
64
65
  import {
65
66
  extractPresentedPlan,
66
67
  readPlanFile,
@@ -115,7 +116,7 @@ import {
115
116
  worktreeDisplayLabelForGroup,
116
117
  worktreeNameFromPath,
117
118
  worktreeSlugForTicket
118
- } from "./chunk-DP4TKAMZ.js";
119
+ } from "./chunk-QFABSE3Q.js";
119
120
  import {
120
121
  ATTACHMENTS_DIR,
121
122
  DETACHED_JOBS_DIR,
@@ -169,7 +170,7 @@ import {
169
170
  stripNestedElectronEnv,
170
171
  updateAdvancedSettings,
171
172
  writeSecureJson
172
- } from "./chunk-AH6WIBLY.js";
173
+ } from "./chunk-HQKGK4KO.js";
173
174
  import {
174
175
  writePrivateFile
175
176
  } from "./chunk-NSTQ6QKD.js";
@@ -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-AJGDZEAS.js");
411
412
  await getOrchestrator2().send(threadId, prompt);
412
413
  } catch {
413
414
  }
@@ -800,9 +801,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
800
801
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
801
802
  );
802
803
  }
803
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-SNJ2Z6SL.js");
804
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-C5XKWCRH.js");
804
805
  if (isGlobalThread2(thread)) {
805
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-LYKS3YNG.js");
806
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-ROWPLVQ3.js");
806
807
  ensureGlobalCoordinatorCwd2(
807
808
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
808
809
  );
@@ -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 {
@@ -3598,7 +3599,7 @@ async function createThread(input, _onSetupLine) {
3598
3599
  });
3599
3600
  await requireAgent(resolved.agent);
3600
3601
  if (input.cowboy) {
3601
- const { cowboyModeEnabled } = await import("./app-settings-J775CYOO.js");
3602
+ const { cowboyModeEnabled } = await import("./app-settings-FMTDH5RD.js");
3602
3603
  if (!cowboyModeEnabled()) {
3603
3604
  throw new Error(
3604
3605
  "Cowboy mode is off. Enable it in Settings \u2192 Advanced."
@@ -3656,7 +3657,7 @@ async function createThread(input, _onSetupLine) {
3656
3657
  let sourceIsFork = false;
3657
3658
  if (!input.cowboy && sourceType === "branch" && (!sourceRef || sourceRef === "HEAD" || sourceRef === "default")) {
3658
3659
  if (isAbleTimeConnected() && getIssueSource() === "abletime") {
3659
- const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-HR6P4GJH.js");
3660
+ const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-H74RNIKV.js");
3660
3661
  const task = await ensureAbleTimeTask({
3661
3662
  title: title?.trim() || "Untitled work",
3662
3663
  description: input.prompt?.trim() || void 0
@@ -4168,7 +4169,7 @@ async function adoptThread(input) {
4168
4169
  messages: input.messages ?? []
4169
4170
  });
4170
4171
  writeThread(thread);
4171
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-UTOYGBYB.js");
4172
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-WIRJQ6HA.js");
4172
4173
  await ensureWorkspace2(repoPath);
4173
4174
  const { ensureWorktreeSideboardIgnored: ensureWorktreeSideboardIgnored2 } = await import("./worktree-exclude-N5X5I422.js");
4174
4175
  await ensureWorktreeSideboardIgnored2(input.worktreePath);
@@ -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-AJGDZEAS.js");
6550
6551
  const orch = getOrchestrator2();
6551
6552
  return {
6552
6553
  findThread: (id) => findThreadByRef(id),
@@ -6821,7 +6822,7 @@ var Orchestrator = class {
6821
6822
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
6822
6823
  });
6823
6824
  }
6824
- const { autoCleanupOrphansEnabled } = await import("./app-settings-J775CYOO.js");
6825
+ const { autoCleanupOrphansEnabled } = await import("./app-settings-FMTDH5RD.js");
6825
6826
  if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
6826
6827
  await cleanupOrphanWorktrees({ repoPaths });
6827
6828
  }
@@ -7100,7 +7101,7 @@ var Orchestrator = class {
7100
7101
  }
7101
7102
  await setup;
7102
7103
  if (skipSetup) return;
7103
- const { autoRunAfterSetupEnabled } = await import("./app-settings-J775CYOO.js");
7104
+ const { autoRunAfterSetupEnabled } = await import("./app-settings-FMTDH5RD.js");
7104
7105
  if (autoRunAfterSetupEnabled()) {
7105
7106
  try {
7106
7107
  await this.startDev(threadId);
@@ -7424,7 +7425,7 @@ var Orchestrator = class {
7424
7425
  }
7425
7426
  const isBrightsy = fresh.agent === "brightsy";
7426
7427
  const isOrchestration = isOrchestratorThread(fresh);
7427
- const { autoRenameBranchEnabled, getGithubGitAuthMode, gitBranchPrefixSetting } = await import("./app-settings-J775CYOO.js");
7428
+ const { autoRenameBranchEnabled, getGithubGitAuthMode, gitBranchPrefixSetting } = await import("./app-settings-FMTDH5RD.js");
7428
7429
  const gitAuthMode = getGithubGitAuthMode();
7429
7430
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
7430
7431
  githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
@@ -8409,7 +8410,7 @@ var Orchestrator = class {
8409
8410
  updateThread(thread.id, { title: meta.title });
8410
8411
  }
8411
8412
  }
8412
- const { autoArchiveOnMergeEnabled } = await import("./app-settings-J775CYOO.js");
8413
+ const { autoArchiveOnMergeEnabled } = await import("./app-settings-FMTDH5RD.js");
8413
8414
  const latest = this.requireThread(thread.id);
8414
8415
  if (!shouldAutoArchiveOnPrMerge({
8415
8416
  previousPrState: prevState || null,
@@ -8701,7 +8702,7 @@ var Orchestrator = class {
8701
8702
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
8702
8703
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
8703
8704
  try {
8704
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-UTOYGBYB.js");
8705
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-WIRJQ6HA.js");
8705
8706
  await ensureWorkspace2(thread.repoPath);
8706
8707
  } catch {
8707
8708
  }
@@ -8728,7 +8729,7 @@ var Orchestrator = class {
8728
8729
  await runArchiveScript(thread.repoPath, thread.worktreePath);
8729
8730
  } catch {
8730
8731
  }
8731
- const { deleteBranchOnPurgeEnabled } = await import("./app-settings-J775CYOO.js");
8732
+ const { deleteBranchOnPurgeEnabled } = await import("./app-settings-FMTDH5RD.js");
8732
8733
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
8733
8734
  await removeWorktree(thread.repoPath, thread.worktreePath, {
8734
8735
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -8755,7 +8756,7 @@ var Orchestrator = class {
8755
8756
  `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
8756
8757
  );
8757
8758
  }
8758
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-R43JOR4V.js");
8759
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-UI5WJC3A.js");
8759
8760
  const slug = thread.worktreePath.split("/").pop();
8760
8761
  const dest = thread.worktreePath;
8761
8762
  await withRepoGitLock(thread.repoPath, async () => {
@@ -8865,7 +8866,7 @@ async function startOrchestration(opts) {
8865
8866
  sourceRef: "default",
8866
8867
  ...createOpts
8867
8868
  }).catch(async () => {
8868
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-R43JOR4V.js");
8869
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-UI5WJC3A.js");
8869
8870
  const repo = await resolveRepoRoot2(repoPath);
8870
8871
  const def = await resolveDefaultBranch2(repo);
8871
8872
  return orch.createThread({
@@ -2,7 +2,7 @@
2
2
 
3
3
  import {
4
4
  isOrchestratorThread
5
- } from "./chunk-6YTI2HBG.js";
5
+ } from "./chunk-S4EEYOSU.js";
6
6
  import {
7
7
  applyConnectedTeamToCli,
8
8
  brightsyMcpServerName,
@@ -17,7 +17,7 @@ import {
17
17
  mergeAgentGitAuthEnv,
18
18
  resolveAgentGitAuthEnv,
19
19
  resolveCodexGitWritableRoots
20
- } from "./chunk-DP4TKAMZ.js";
20
+ } from "./chunk-QFABSE3Q.js";
21
21
  import {
22
22
  enrichPathWithNpmGlobalBin,
23
23
  isConductorBundledCli,
@@ -35,7 +35,7 @@ import {
35
35
  resolveClaudeExecutable,
36
36
  unwrapStrippedElectronLaunch,
37
37
  wrapElectronAsNodeLaunch
38
- } from "./chunk-AH6WIBLY.js";
38
+ } from "./chunk-HQKGK4KO.js";
39
39
  import {
40
40
  appDataDir
41
41
  } from "./chunk-EQNRWDLN.js";
@@ -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 = [
@@ -2396,7 +2420,7 @@ var claudeAdapter = {
2396
2420
  );
2397
2421
  }
2398
2422
  const mode = permissionMode(thread);
2399
- const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-SNJ2Z6SL.js");
2423
+ const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-C5XKWCRH.js");
2400
2424
  const isOrchestrator = isOrchestratorThread2(thread);
2401
2425
  const injectedServers = await buildInjectedMcpServers({
2402
2426
  includeSideboard: true,
@@ -4098,6 +4122,346 @@ function resolveQuotaFallbackAgent(current, preferred) {
4098
4122
  return ordered.find((a) => a !== current) ?? (current === "cursor" ? "codex" : "cursor");
4099
4123
  }
4100
4124
 
4125
+ // src/agents/claude-usage.ts
4126
+ var WINDOW_META = {
4127
+ five_hour: {
4128
+ label: "5-hour session",
4129
+ shortLabel: "5h",
4130
+ detail: "Rolling 5-hour limit across all models"
4131
+ },
4132
+ seven_day: {
4133
+ label: "Weekly",
4134
+ shortLabel: "wk",
4135
+ detail: "7-day cap across all models"
4136
+ },
4137
+ seven_day_opus: {
4138
+ label: "Opus",
4139
+ shortLabel: "Opus",
4140
+ detail: "Weekly Opus-only cap"
4141
+ },
4142
+ seven_day_sonnet: {
4143
+ label: "Sonnet",
4144
+ shortLabel: "Sonnet",
4145
+ detail: "Weekly Sonnet-only cap"
4146
+ }
4147
+ };
4148
+ var WINDOW_ORDER = [
4149
+ "five_hour",
4150
+ "seven_day",
4151
+ "seven_day_opus",
4152
+ "seven_day_sonnet"
4153
+ ];
4154
+ function parseClaudeResetsAt(value) {
4155
+ if (typeof value === "number" && Number.isFinite(value)) {
4156
+ const ms = value > 1e12 ? value : value * 1e3;
4157
+ const d = new Date(ms);
4158
+ return Number.isNaN(d.getTime()) ? null : d.toISOString();
4159
+ }
4160
+ if (typeof value === "string") {
4161
+ const trimmed = value.trim();
4162
+ if (!trimmed) return null;
4163
+ if (/^\d+(\.\d+)?$/.test(trimmed)) {
4164
+ return parseClaudeResetsAt(Number(trimmed));
4165
+ }
4166
+ const d = new Date(trimmed);
4167
+ return Number.isNaN(d.getTime()) ? null : d.toISOString();
4168
+ }
4169
+ return null;
4170
+ }
4171
+ function parseClaudeUtilization(value) {
4172
+ if (typeof value !== "number" || !Number.isFinite(value)) return null;
4173
+ return Math.max(0, Math.min(100, value));
4174
+ }
4175
+ function windowFromRaw(id, raw) {
4176
+ if (!raw || typeof raw !== "object") return null;
4177
+ const row = raw;
4178
+ const used = parseClaudeUtilization(row.utilization ?? row.used_percentage);
4179
+ if (used == null) return null;
4180
+ const meta = WINDOW_META[id];
4181
+ return {
4182
+ id,
4183
+ label: meta.label,
4184
+ shortLabel: meta.shortLabel,
4185
+ detail: meta.detail,
4186
+ usedPercent: used,
4187
+ remainingPercent: Math.max(0, Math.min(100, 100 - used)),
4188
+ resetsAt: parseClaudeResetsAt(row.resets_at)
4189
+ };
4190
+ }
4191
+ function extraUsageFromRaw(raw) {
4192
+ if (!raw || typeof raw !== "object") return void 0;
4193
+ const row = raw;
4194
+ const enabled = row.is_enabled === true || row.enabled === true;
4195
+ if (!enabled && row.is_enabled !== false && row.enabled !== false) return void 0;
4196
+ const usedCredits = numberOrUndef(row.used_credits ?? row.used);
4197
+ const monthlyLimit = numberOrUndef(row.monthly_limit);
4198
+ const usedPercent = parseClaudeUtilization(row.utilization) ?? void 0;
4199
+ return {
4200
+ enabled,
4201
+ ...usedCredits != null ? { usedCredits } : {},
4202
+ ...monthlyLimit != null ? { monthlyLimit } : {},
4203
+ ...usedPercent != null ? { usedPercent } : {}
4204
+ };
4205
+ }
4206
+ function numberOrUndef(value) {
4207
+ if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
4208
+ return value;
4209
+ }
4210
+ function parseClaudeUsagePayload(raw, fetchedAt = (/* @__PURE__ */ new Date()).toISOString()) {
4211
+ if (!raw || typeof raw !== "object") return null;
4212
+ const obj = raw;
4213
+ const source = obj.rate_limits && typeof obj.rate_limits === "object" ? obj.rate_limits : obj;
4214
+ const windows = [];
4215
+ for (const id of WINDOW_ORDER) {
4216
+ const parsed = windowFromRaw(id, source[id]);
4217
+ if (parsed) windows.push(parsed);
4218
+ }
4219
+ if (windows.length === 0) return null;
4220
+ const extra = extraUsageFromRaw(obj.extra_usage ?? source.extra_usage);
4221
+ return {
4222
+ windows,
4223
+ ...extra ? { extraUsage: extra } : {},
4224
+ fetchedAt
4225
+ };
4226
+ }
4227
+ function hottestClaudeWindow(usage) {
4228
+ if (usage.windows.length === 0) return null;
4229
+ return usage.windows.reduce((a, b) => b.usedPercent > a.usedPercent ? b : a);
4230
+ }
4231
+ function formatClaudeUsageReset(resetsAt, now = /* @__PURE__ */ new Date()) {
4232
+ if (!resetsAt) return "";
4233
+ const at = new Date(resetsAt);
4234
+ if (Number.isNaN(at.getTime())) return "";
4235
+ const ms = at.getTime() - now.getTime();
4236
+ if (ms <= 0) return "resets soon";
4237
+ const minutes = Math.round(ms / 6e4);
4238
+ if (minutes < 60) return `resets in ${Math.max(1, minutes)}m`;
4239
+ const hours = Math.round(minutes / 60);
4240
+ if (hours < 36) return `resets in ${hours}h`;
4241
+ try {
4242
+ return `resets ${at.toLocaleString(void 0, {
4243
+ weekday: "short",
4244
+ hour: "numeric",
4245
+ minute: "2-digit"
4246
+ })}`;
4247
+ } catch {
4248
+ return `resets ${at.toISOString()}`;
4249
+ }
4250
+ }
4251
+ function formatClaudeUsageResetClock(resetsAt) {
4252
+ if (!resetsAt) return "";
4253
+ const at = new Date(resetsAt);
4254
+ if (Number.isNaN(at.getTime())) return "";
4255
+ try {
4256
+ return at.toLocaleString(void 0, {
4257
+ weekday: "short",
4258
+ month: "short",
4259
+ day: "numeric",
4260
+ hour: "numeric",
4261
+ minute: "2-digit"
4262
+ });
4263
+ } catch {
4264
+ return at.toISOString();
4265
+ }
4266
+ }
4267
+ function formatClaudeUsageFetchedAt(fetchedAt, now = /* @__PURE__ */ new Date()) {
4268
+ const at = new Date(fetchedAt);
4269
+ if (Number.isNaN(at.getTime())) return "";
4270
+ const minutes = Math.max(0, Math.round((now.getTime() - at.getTime()) / 6e4));
4271
+ if (minutes < 1) return "Updated just now";
4272
+ if (minutes < 60) return `Updated ${minutes}m ago`;
4273
+ const hours = Math.round(minutes / 60);
4274
+ if (hours < 36) return `Updated ${hours}h ago`;
4275
+ return `Updated ${formatClaudeUsageResetClock(fetchedAt)}`;
4276
+ }
4277
+ function formatClaudeExtraUsageDetail(extra) {
4278
+ if (!extra.enabled) return "Extra usage off";
4279
+ const bits = ["Extra usage on"];
4280
+ if (extra.usedCredits != null && extra.monthlyLimit != null) {
4281
+ bits.push(`${extra.usedCredits} / ${extra.monthlyLimit} credits this month`);
4282
+ } else if (extra.usedCredits != null) {
4283
+ bits.push(`${extra.usedCredits} credits used`);
4284
+ }
4285
+ if (extra.usedPercent != null) {
4286
+ bits.push(`${Math.round(extra.usedPercent)}% of extra pool`);
4287
+ }
4288
+ return bits.join(" \xB7 ");
4289
+ }
4290
+ function formatClaudeUsageWindowLine(window, now = /* @__PURE__ */ new Date()) {
4291
+ const reset = formatClaudeUsageReset(window.resetsAt, now);
4292
+ const clock = formatClaudeUsageResetClock(window.resetsAt);
4293
+ const left = `${Math.round(window.remainingPercent)}% left`;
4294
+ const used = `${Math.round(window.usedPercent)}% used`;
4295
+ const resetBit = reset ? clock && !reset.includes(clock) ? `${reset} (${clock})` : reset : "";
4296
+ return resetBit ? `${window.label} \u2014 ${window.detail}. ${left} \xB7 ${used} \xB7 ${resetBit}` : `${window.label} \u2014 ${window.detail}. ${left} \xB7 ${used}`;
4297
+ }
4298
+ function formatClaudeUsageTooltip(usage, now = /* @__PURE__ */ new Date()) {
4299
+ const lines = [
4300
+ "Claude Code plan",
4301
+ ...usage.windows.map((w) => formatClaudeUsageWindowLine(w, now))
4302
+ ];
4303
+ if (usage.extraUsage) {
4304
+ lines.push(formatClaudeExtraUsageDetail(usage.extraUsage));
4305
+ }
4306
+ const fetched = formatClaudeUsageFetchedAt(usage.fetchedAt, now);
4307
+ if (fetched) lines.push(fetched);
4308
+ return lines.join("\n");
4309
+ }
4310
+ function formatClaudeUsageCompact(window) {
4311
+ return `${window.shortLabel} ${Math.round(window.remainingPercent)}%`;
4312
+ }
4313
+ function claudeUsageWindowOverLimit(window) {
4314
+ return window.remainingPercent <= 0 || window.usedPercent >= 100;
4315
+ }
4316
+ function claudeUsageOverLimitWindows(usage) {
4317
+ if (!usage) return [];
4318
+ return usage.windows.filter(claudeUsageWindowOverLimit);
4319
+ }
4320
+ function formatClaudeUsageOverLimitConfirm(windows, now = /* @__PURE__ */ new Date()) {
4321
+ if (windows.length === 0) return "";
4322
+ const parts = windows.map((w) => {
4323
+ const reset = formatClaudeUsageReset(w.resetsAt, now);
4324
+ return reset ? `${w.label} (${reset})` : w.label;
4325
+ });
4326
+ const list = parts.join(", ");
4327
+ const verb = windows.length === 1 ? "is" : "are";
4328
+ return `Claude Code ${list} ${verb} at or over the plan limit. Send this message anyway?`;
4329
+ }
4330
+
4331
+ // src/agents/claude-usage-client.ts
4332
+ import { existsSync as existsSync13, readFileSync as readFileSync5 } from "fs";
4333
+ import { homedir as homedir5 } from "os";
4334
+ import { join as join10 } from "path";
4335
+ var CLAUDE_OAUTH_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
4336
+ var CLAUDE_USAGE_TTL_MS = 9e4;
4337
+ var CLAUDE_USAGE_ERROR_TTL_MS = 5 * 6e4;
4338
+ var CLAUDE_USAGE_STALE_OK_MS = 30 * 6e4;
4339
+ var KEYCHAIN_SERVICES = ["Claude Code-credentials", "Claude Code"];
4340
+ var cache = null;
4341
+ var errorUntil = 0;
4342
+ var inflight = null;
4343
+ var lastGood = null;
4344
+ var claudeVersion = null;
4345
+ function accessTokenFromCredentialsJson(raw) {
4346
+ const trimmed = raw.trim();
4347
+ if (!trimmed) return null;
4348
+ if (trimmed.startsWith("sk-ant-")) {
4349
+ const token = trimmed.split(/\s+/)[0];
4350
+ return token || null;
4351
+ }
4352
+ try {
4353
+ const parsed = JSON.parse(trimmed);
4354
+ const oauth = parsed.claudeAiOauth && typeof parsed.claudeAiOauth === "object" ? parsed.claudeAiOauth : parsed;
4355
+ const token = oauth.accessToken ?? oauth.access_token;
4356
+ return typeof token === "string" && token.trim() ? token.trim() : null;
4357
+ } catch {
4358
+ return null;
4359
+ }
4360
+ }
4361
+ function claudeCredentialsPath() {
4362
+ const root = process.env.CLAUDE_CONFIG_DIR?.trim() || join10(homedir5(), ".claude");
4363
+ return join10(root, ".credentials.json");
4364
+ }
4365
+ async function readFileAccessToken() {
4366
+ const path = claudeCredentialsPath();
4367
+ if (!existsSync13(path)) return null;
4368
+ try {
4369
+ return accessTokenFromCredentialsJson(readFileSync5(path, "utf8"));
4370
+ } catch {
4371
+ return null;
4372
+ }
4373
+ }
4374
+ async function readKeychainAccessToken() {
4375
+ if (process.platform !== "darwin") return null;
4376
+ for (const service of KEYCHAIN_SERVICES) {
4377
+ const result = await run("security", ["find-generic-password", "-s", service, "-w"], {
4378
+ reject: false,
4379
+ timeoutMs: 4e3
4380
+ });
4381
+ if (result.exitCode !== 0 || !result.stdout.trim()) continue;
4382
+ const token = accessTokenFromCredentialsJson(result.stdout);
4383
+ if (token) return token;
4384
+ }
4385
+ return null;
4386
+ }
4387
+ async function readClaudeAccessToken() {
4388
+ const envToken = process.env.CLAUDE_CODE_OAUTH_TOKEN?.trim();
4389
+ if (envToken) return envToken;
4390
+ const fromFile = await readFileAccessToken();
4391
+ if (fromFile) return fromFile;
4392
+ return readKeychainAccessToken();
4393
+ }
4394
+ async function resolveClaudeUserAgent() {
4395
+ if (claudeVersion) return `claude-code/${claudeVersion}`;
4396
+ try {
4397
+ const claude = resolveClaudeExecutable();
4398
+ const result = await run(claude, ["--version"], { reject: false, timeoutMs: 4e3 });
4399
+ const match = `${result.stdout}
4400
+ ${result.stderr}`.match(/(\d+\.\d+\.\d+)/);
4401
+ claudeVersion = match?.[1] ?? "2.1.80";
4402
+ } catch {
4403
+ claudeVersion = "2.1.80";
4404
+ }
4405
+ return `claude-code/${claudeVersion}`;
4406
+ }
4407
+ function staleOk(now) {
4408
+ if (!lastGood) return null;
4409
+ if (now - lastGood.at > CLAUDE_USAGE_STALE_OK_MS) return null;
4410
+ return lastGood.value;
4411
+ }
4412
+ async function getClaudePlanUsage(opts = {}) {
4413
+ const now = opts.now ?? Date.now();
4414
+ if (!opts.force) {
4415
+ if (cache && now - cache.at < CLAUDE_USAGE_TTL_MS) return cache.value;
4416
+ if (now < errorUntil) return staleOk(now);
4417
+ }
4418
+ if (inflight) return inflight;
4419
+ inflight = (async () => {
4420
+ try {
4421
+ const token = await (opts.readAccessToken ?? readClaudeAccessToken)();
4422
+ if (!token) {
4423
+ cache = { at: now, value: null };
4424
+ return null;
4425
+ }
4426
+ const fetchFn = opts.fetch ?? globalThis.fetch;
4427
+ const userAgent = opts.userAgent ?? await resolveClaudeUserAgent();
4428
+ const ac = new AbortController();
4429
+ const timer = setTimeout(() => ac.abort(), 8e3);
4430
+ let res;
4431
+ try {
4432
+ res = await fetchFn(CLAUDE_OAUTH_USAGE_URL, {
4433
+ headers: {
4434
+ Authorization: `Bearer ${token}`,
4435
+ "anthropic-beta": "oauth-2025-04-20",
4436
+ "User-Agent": userAgent
4437
+ },
4438
+ signal: ac.signal
4439
+ });
4440
+ } finally {
4441
+ clearTimeout(timer);
4442
+ }
4443
+ if (res.status === 401 || res.status === 403) {
4444
+ cache = { at: now, value: null };
4445
+ return null;
4446
+ }
4447
+ if (!res.ok) {
4448
+ errorUntil = now + CLAUDE_USAGE_ERROR_TTL_MS;
4449
+ return staleOk(now);
4450
+ }
4451
+ const usage = parseClaudeUsagePayload(await res.json(), new Date(now).toISOString());
4452
+ cache = { at: now, value: usage };
4453
+ if (usage) lastGood = { at: now, value: usage };
4454
+ return usage;
4455
+ } catch {
4456
+ errorUntil = now + CLAUDE_USAGE_ERROR_TTL_MS;
4457
+ return staleOk(now);
4458
+ } finally {
4459
+ inflight = null;
4460
+ }
4461
+ })();
4462
+ return inflight;
4463
+ }
4464
+
4101
4465
  // src/agents/install.ts
4102
4466
  var SETUP = {
4103
4467
  claude: {
@@ -4364,6 +4728,7 @@ export {
4364
4728
  encodeBrightsyTarget,
4365
4729
  decodeBrightsyTarget,
4366
4730
  parseBrightsyCliLine,
4731
+ adjustBrightsyTurnExit,
4367
4732
  listBrightsyChatTargets,
4368
4733
  brightsyAdapter,
4369
4734
  toolDescription,
@@ -4401,6 +4766,17 @@ export {
4401
4766
  isSessionQuotaLimit,
4402
4767
  parseSessionQuotaResetAt,
4403
4768
  resolveQuotaFallbackAgent,
4769
+ parseClaudeUsagePayload,
4770
+ hottestClaudeWindow,
4771
+ formatClaudeUsageReset,
4772
+ formatClaudeUsageResetClock,
4773
+ formatClaudeUsageFetchedAt,
4774
+ formatClaudeExtraUsageDetail,
4775
+ formatClaudeUsageTooltip,
4776
+ formatClaudeUsageCompact,
4777
+ claudeUsageOverLimitWindows,
4778
+ formatClaudeUsageOverLimitConfirm,
4779
+ getClaudePlanUsage,
4404
4780
  getAgentSetupInfo,
4405
4781
  listAgentSetupInfo,
4406
4782
  resolveLoginCommand,
@@ -7,7 +7,7 @@ import {
7
7
  import {
8
8
  getGithubGitAuthMode,
9
9
  getGithubPat
10
- } from "./chunk-S5GLSRLP.js";
10
+ } from "./chunk-2IQ6W47G.js";
11
11
  import {
12
12
  listThreads
13
13
  } from "./chunk-KJBYIIJU.js";