sidekick-agent-hub 0.17.0 → 0.17.2

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 (2) hide show
  1. package/dist/sidekick-cli.mjs +869 -439
  2. package/package.json +1 -1
@@ -1157,7 +1157,7 @@ var require_command = __commonJS({
1157
1157
  "node_modules/commander/lib/command.js"(exports) {
1158
1158
  var EventEmitter3 = __require("node:events").EventEmitter;
1159
1159
  var childProcess = __require("node:child_process");
1160
- var path7 = __require("node:path");
1160
+ var path8 = __require("node:path");
1161
1161
  var fs9 = __require("node:fs");
1162
1162
  var process14 = __require("node:process");
1163
1163
  var { Argument: Argument2, humanReadableArgName } = require_argument();
@@ -2157,9 +2157,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
2157
2157
  let launchWithNode = false;
2158
2158
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
2159
2159
  function findFile(baseDir, baseName) {
2160
- const localBin = path7.resolve(baseDir, baseName);
2160
+ const localBin = path8.resolve(baseDir, baseName);
2161
2161
  if (fs9.existsSync(localBin)) return localBin;
2162
- if (sourceExt.includes(path7.extname(baseName))) return void 0;
2162
+ if (sourceExt.includes(path8.extname(baseName))) return void 0;
2163
2163
  const foundExt = sourceExt.find(
2164
2164
  (ext) => fs9.existsSync(`${localBin}${ext}`)
2165
2165
  );
@@ -2177,17 +2177,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
2177
2177
  } catch {
2178
2178
  resolvedScriptPath = this._scriptPath;
2179
2179
  }
2180
- executableDir = path7.resolve(
2181
- path7.dirname(resolvedScriptPath),
2180
+ executableDir = path8.resolve(
2181
+ path8.dirname(resolvedScriptPath),
2182
2182
  executableDir
2183
2183
  );
2184
2184
  }
2185
2185
  if (executableDir) {
2186
2186
  let localFile = findFile(executableDir, executableFile);
2187
2187
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
2188
- const legacyName = path7.basename(
2188
+ const legacyName = path8.basename(
2189
2189
  this._scriptPath,
2190
- path7.extname(this._scriptPath)
2190
+ path8.extname(this._scriptPath)
2191
2191
  );
2192
2192
  if (legacyName !== this._name) {
2193
2193
  localFile = findFile(
@@ -2198,7 +2198,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2198
2198
  }
2199
2199
  executableFile = localFile || executableFile;
2200
2200
  }
2201
- launchWithNode = sourceExt.includes(path7.extname(executableFile));
2201
+ launchWithNode = sourceExt.includes(path8.extname(executableFile));
2202
2202
  let proc;
2203
2203
  if (process14.platform !== "win32") {
2204
2204
  if (launchWithNode) {
@@ -3045,7 +3045,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3045
3045
  * @return {Command}
3046
3046
  */
3047
3047
  nameFromFilename(filename) {
3048
- this._name = path7.basename(filename, path7.extname(filename));
3048
+ this._name = path8.basename(filename, path8.extname(filename));
3049
3049
  return this;
3050
3050
  }
3051
3051
  /**
@@ -3059,9 +3059,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3059
3059
  * @param {string} [path]
3060
3060
  * @return {(string|null|Command)}
3061
3061
  */
3062
- executableDir(path8) {
3063
- if (path8 === void 0) return this._executableDir;
3064
- this._executableDir = path8;
3062
+ executableDir(path9) {
3063
+ if (path9 === void 0) return this._executableDir;
3064
+ this._executableDir = path9;
3065
3065
  return this;
3066
3066
  }
3067
3067
  /**
@@ -3412,7 +3412,7 @@ var require_historicalData = __commonJS({
3412
3412
  Object.defineProperty(exports, "__esModule", { value: true });
3413
3413
  exports.HISTORICAL_DATA_SCHEMA_VERSION = void 0;
3414
3414
  exports.createEmptyTokenTotals = createEmptyTokenTotals;
3415
- exports.HISTORICAL_DATA_SCHEMA_VERSION = 1;
3415
+ exports.HISTORICAL_DATA_SCHEMA_VERSION = 2;
3416
3416
  function createEmptyTokenTotals() {
3417
3417
  return {
3418
3418
  inputTokens: 0,
@@ -3484,19 +3484,19 @@ var require_paths = __commonJS({
3484
3484
  exports.getProjectSlug = getProjectSlug7;
3485
3485
  exports.getProjectSlugRaw = getProjectSlugRaw7;
3486
3486
  var fs9 = __importStar(__require("fs"));
3487
- var path7 = __importStar(__require("path"));
3488
- var os5 = __importStar(__require("os"));
3487
+ var path8 = __importStar(__require("path"));
3488
+ var os6 = __importStar(__require("os"));
3489
3489
  function getConfigDir2() {
3490
3490
  if (process.platform === "win32") {
3491
- return path7.join(process.env.APPDATA || os5.homedir(), "sidekick");
3491
+ return path8.join(process.env.APPDATA || os6.homedir(), "sidekick");
3492
3492
  }
3493
- return path7.join(os5.homedir(), ".config", "sidekick");
3493
+ return path8.join(os6.homedir(), ".config", "sidekick");
3494
3494
  }
3495
3495
  function getProjectDataPath(slug, subdomain) {
3496
- return path7.join(getConfigDir2(), subdomain, `${slug}.json`);
3496
+ return path8.join(getConfigDir2(), subdomain, `${slug}.json`);
3497
3497
  }
3498
3498
  function getGlobalDataPath(filename) {
3499
- return path7.join(getConfigDir2(), filename);
3499
+ return path8.join(getConfigDir2(), filename);
3500
3500
  }
3501
3501
  function encodeWorkspacePath(workspacePath) {
3502
3502
  const normalized = workspacePath.replace(/\\/g, "/");
@@ -3508,13 +3508,13 @@ var require_paths = __commonJS({
3508
3508
  try {
3509
3509
  resolved = fs9.realpathSync(dir);
3510
3510
  } catch {
3511
- resolved = path7.resolve(dir);
3511
+ resolved = path8.resolve(dir);
3512
3512
  }
3513
3513
  return encodeWorkspacePath(resolved);
3514
3514
  }
3515
3515
  function getProjectSlugRaw7(cwd2) {
3516
3516
  const dir = cwd2 || process.cwd();
3517
- return encodeWorkspacePath(path7.resolve(dir));
3517
+ return encodeWorkspacePath(path8.resolve(dir));
3518
3518
  }
3519
3519
  }
3520
3520
  });
@@ -4077,8 +4077,8 @@ var require_plans = __commonJS({
4077
4077
  exports.writePlans = writePlans;
4078
4078
  exports.readClaudeCodePlanFiles = readClaudeCodePlanFiles2;
4079
4079
  var fs9 = __importStar(__require("fs"));
4080
- var path7 = __importStar(__require("path"));
4081
- var os5 = __importStar(__require("os"));
4080
+ var path8 = __importStar(__require("path"));
4081
+ var os6 = __importStar(__require("os"));
4082
4082
  var plan_1 = require_plan();
4083
4083
  var paths_1 = require_paths();
4084
4084
  var helpers_1 = require_helpers();
@@ -4196,11 +4196,11 @@ var require_plans = __commonJS({
4196
4196
  await fs9.promises.writeFile(filePath, JSON.stringify(store, null, 2), "utf-8");
4197
4197
  }
4198
4198
  async function readClaudeCodePlanFiles2(workspacePath) {
4199
- const claudePlansDir = path7.join(os5.homedir(), ".claude", "plans");
4200
- const claudeProjectsDir = path7.join(os5.homedir(), ".claude", "projects");
4199
+ const claudePlansDir = path8.join(os6.homedir(), ".claude", "plans");
4200
+ const claudeProjectsDir = path8.join(os6.homedir(), ".claude", "projects");
4201
4201
  const cwd2 = workspacePath || process.cwd();
4202
- const encoded = (0, paths_1.encodeWorkspacePath)(path7.resolve(cwd2));
4203
- const projectDir = path7.join(claudeProjectsDir, encoded);
4202
+ const encoded = (0, paths_1.encodeWorkspacePath)(path8.resolve(cwd2));
4203
+ const projectDir = path8.join(claudeProjectsDir, encoded);
4204
4204
  if (!fs9.existsSync(projectDir) || !fs9.existsSync(claudePlansDir)) {
4205
4205
  return [];
4206
4206
  }
@@ -4214,7 +4214,7 @@ var require_plans = __commonJS({
4214
4214
  const SLUG_RE = /"slug":"([^"]+)"/;
4215
4215
  await Promise.all(jsonlFiles.map(async (f) => {
4216
4216
  try {
4217
- const filePath = path7.join(projectDir, f);
4217
+ const filePath = path8.join(projectDir, f);
4218
4218
  const handle = await fs9.promises.open(filePath, "r");
4219
4219
  try {
4220
4220
  const buf = Buffer.alloc(32768);
@@ -4237,7 +4237,7 @@ var require_plans = __commonJS({
4237
4237
  return [];
4238
4238
  const plans = [];
4239
4239
  for (const slug of slugSet) {
4240
- const planPath = path7.join(claudePlansDir, `${slug}.md`);
4240
+ const planPath = path8.join(claudePlansDir, `${slug}.md`);
4241
4241
  try {
4242
4242
  const stat = await fs9.promises.stat(planPath);
4243
4243
  const content = await fs9.promises.readFile(planPath, "utf-8");
@@ -4324,13 +4324,13 @@ var require_accountRegistry = __commonJS({
4324
4324
  exports.replaceSavedAccountProfiles = replaceSavedAccountProfiles;
4325
4325
  exports.removeSavedAccountProfile = removeSavedAccountProfile;
4326
4326
  var fs9 = __importStar(__require("fs"));
4327
- var path7 = __importStar(__require("path"));
4327
+ var path8 = __importStar(__require("path"));
4328
4328
  var paths_1 = require_paths();
4329
4329
  function getAccountsDir() {
4330
- return path7.join((0, paths_1.getConfigDir)(), "accounts");
4330
+ return path8.join((0, paths_1.getConfigDir)(), "accounts");
4331
4331
  }
4332
4332
  function getRegistryPath() {
4333
- return path7.join(getAccountsDir(), "accounts.json");
4333
+ return path8.join(getAccountsDir(), "accounts.json");
4334
4334
  }
4335
4335
  function ensureAccountsDir() {
4336
4336
  fs9.mkdirSync(getAccountsDir(), { recursive: true, mode: 448 });
@@ -4507,6 +4507,8 @@ var require_codexProfiles = __commonJS({
4507
4507
  };
4508
4508
  }();
4509
4509
  Object.defineProperty(exports, "__esModule", { value: true });
4510
+ exports.getSystemCodexHome = getSystemCodexHome;
4511
+ exports.getCodexMonitoringHomes = getCodexMonitoringHomes;
4510
4512
  exports.getCodexProfilesDir = getCodexProfilesDir;
4511
4513
  exports.getCodexProfileHome = getCodexProfileHome;
4512
4514
  exports.listCodexAccounts = listCodexAccounts2;
@@ -4518,25 +4520,56 @@ var require_codexProfiles = __commonJS({
4518
4520
  exports.switchToCodexAccount = switchToCodexAccount2;
4519
4521
  exports.removeCodexAccount = removeCodexAccount2;
4520
4522
  var fs9 = __importStar(__require("fs"));
4521
- var os5 = __importStar(__require("os"));
4522
- var path7 = __importStar(__require("path"));
4523
+ var os6 = __importStar(__require("os"));
4524
+ var path8 = __importStar(__require("path"));
4523
4525
  var child_process_1 = __require("child_process");
4524
4526
  var crypto_1 = __require("crypto");
4525
4527
  var accountRegistry_1 = require_accountRegistry();
4528
+ function getDefaultSystemCodexHome() {
4529
+ return path8.join(os6.homedir(), ".codex");
4530
+ }
4531
+ function getExplicitCodexHome() {
4532
+ const explicitHome = process.env.CODEX_HOME?.trim();
4533
+ return explicitHome ? explicitHome : null;
4534
+ }
4535
+ function dedupePaths(paths) {
4536
+ const seen = /* @__PURE__ */ new Set();
4537
+ const unique = [];
4538
+ for (const candidate of paths) {
4539
+ const normalized = path8.resolve(candidate);
4540
+ if (seen.has(normalized))
4541
+ continue;
4542
+ seen.add(normalized);
4543
+ unique.push(candidate);
4544
+ }
4545
+ return unique;
4546
+ }
4526
4547
  function getSystemCodexHome() {
4527
- return process.env.CODEX_HOME ?? path7.join(os5.homedir(), ".codex");
4548
+ return getExplicitCodexHome() ?? getDefaultSystemCodexHome();
4549
+ }
4550
+ function getCodexMonitoringHomes() {
4551
+ const explicitHome = getExplicitCodexHome();
4552
+ if (explicitHome)
4553
+ return [explicitHome];
4554
+ const homes = [];
4555
+ const active = getActiveCodexAccount3();
4556
+ if (active) {
4557
+ homes.push(getCodexProfileHome(active.id));
4558
+ }
4559
+ homes.push(getDefaultSystemCodexHome());
4560
+ return dedupePaths(homes);
4528
4561
  }
4529
4562
  function getCodexProfilesDir() {
4530
- return path7.join((0, accountRegistry_1.getAccountsDir)(), "codex", "profiles");
4563
+ return path8.join((0, accountRegistry_1.getAccountsDir)(), "codex", "profiles");
4531
4564
  }
4532
4565
  function getCodexProfileDir(profileId) {
4533
- return path7.join(getCodexProfilesDir(), profileId);
4566
+ return path8.join(getCodexProfilesDir(), profileId);
4534
4567
  }
4535
4568
  function getCodexProfileHome(profileId) {
4536
- return path7.join(getCodexProfileDir(profileId), "codex-home");
4569
+ return path8.join(getCodexProfileDir(profileId), "codex-home");
4537
4570
  }
4538
4571
  function getCodexProfileStatePath(profileId) {
4539
- return path7.join(getCodexProfileDir(profileId), "profile.json");
4572
+ return path8.join(getCodexProfileDir(profileId), "profile.json");
4540
4573
  }
4541
4574
  function ensureCodexProfileDirs(profileId) {
4542
4575
  fs9.mkdirSync(getCodexProfileHome(profileId), { recursive: true, mode: 448 });
@@ -4562,16 +4595,16 @@ var require_codexProfiles = __commonJS({
4562
4595
  function copyIfExists(source, destination) {
4563
4596
  if (!fs9.existsSync(source))
4564
4597
  return false;
4565
- fs9.mkdirSync(path7.dirname(destination), { recursive: true, mode: 448 });
4598
+ fs9.mkdirSync(path8.dirname(destination), { recursive: true, mode: 448 });
4566
4599
  fs9.copyFileSync(source, destination);
4567
4600
  return true;
4568
4601
  }
4569
4602
  function copySourceCodexConfig(sourceHome, targetHome) {
4570
- copyIfExists(path7.join(sourceHome, "config.toml"), path7.join(targetHome, "config.toml"));
4603
+ copyIfExists(path8.join(sourceHome, "config.toml"), path8.join(targetHome, "config.toml"));
4571
4604
  }
4572
4605
  function importCurrentCodexAuth(sourceHome, targetHome) {
4573
- const authCopied = copyIfExists(path7.join(sourceHome, "auth.json"), path7.join(targetHome, "auth.json"));
4574
- const legacyCredsCopied = copyIfExists(path7.join(sourceHome, ".credentials.json"), path7.join(targetHome, ".credentials.json"));
4606
+ const authCopied = copyIfExists(path8.join(sourceHome, "auth.json"), path8.join(targetHome, "auth.json"));
4607
+ const legacyCredsCopied = copyIfExists(path8.join(sourceHome, ".credentials.json"), path8.join(targetHome, ".credentials.json"));
4575
4608
  return authCopied || legacyCredsCopied;
4576
4609
  }
4577
4610
  function parseJwtPayload(jwt) {
@@ -4586,7 +4619,7 @@ var require_codexProfiles = __commonJS({
4586
4619
  }
4587
4620
  }
4588
4621
  function readMetadataFromAuthJson(codexHome) {
4589
- const authPath = path7.join(codexHome, "auth.json");
4622
+ const authPath = path8.join(codexHome, "auth.json");
4590
4623
  if (!fs9.existsSync(authPath))
4591
4624
  return {};
4592
4625
  try {
@@ -4610,7 +4643,7 @@ var require_codexProfiles = __commonJS({
4610
4643
  }
4611
4644
  }
4612
4645
  function readMetadataFromLegacyCredentials(codexHome) {
4613
- const legacyPath = path7.join(codexHome, ".credentials.json");
4646
+ const legacyPath = path8.join(codexHome, ".credentials.json");
4614
4647
  if (!fs9.existsSync(legacyPath))
4615
4648
  return {};
4616
4649
  try {
@@ -4661,7 +4694,7 @@ var require_codexProfiles = __commonJS({
4661
4694
  return {};
4662
4695
  }
4663
4696
  function isCodexProfileAuthenticated(codexHome) {
4664
- if (fs9.existsSync(path7.join(codexHome, "auth.json")) || fs9.existsSync(path7.join(codexHome, ".credentials.json"))) {
4697
+ if (fs9.existsSync(path8.join(codexHome, "auth.json")) || fs9.existsSync(path8.join(codexHome, ".credentials.json"))) {
4665
4698
  return true;
4666
4699
  }
4667
4700
  return getCodexLoginStatus(codexHome).loggedIn;
@@ -4815,22 +4848,22 @@ var require_detect = __commonJS({
4815
4848
  exports.getAllDetectedProviders = getAllDetectedProviders2;
4816
4849
  exports.detectProvider = detectProvider2;
4817
4850
  var fs9 = __importStar(__require("fs"));
4818
- var os5 = __importStar(__require("os"));
4819
- var path7 = __importStar(__require("path"));
4851
+ var os6 = __importStar(__require("os"));
4852
+ var path8 = __importStar(__require("path"));
4820
4853
  var codexProfiles_1 = require_codexProfiles();
4821
4854
  function getOpenCodeDataDir() {
4822
4855
  const xdg = process.env.XDG_DATA_HOME;
4823
4856
  if (xdg)
4824
- return path7.join(xdg, "opencode");
4857
+ return path8.join(xdg, "opencode");
4825
4858
  if (process.platform === "darwin")
4826
- return path7.join(os5.homedir(), "Library", "Application Support", "opencode");
4859
+ return path8.join(os6.homedir(), "Library", "Application Support", "opencode");
4827
4860
  if (process.platform === "win32") {
4828
- return path7.join(process.env.LOCALAPPDATA || process.env.APPDATA || path7.join(os5.homedir(), "AppData", "Local"), "opencode");
4861
+ return path8.join(process.env.LOCALAPPDATA || process.env.APPDATA || path8.join(os6.homedir(), "AppData", "Local"), "opencode");
4829
4862
  }
4830
- return path7.join(os5.homedir(), ".local", "share", "opencode");
4863
+ return path8.join(os6.homedir(), ".local", "share", "opencode");
4831
4864
  }
4832
- function getCodexHome() {
4833
- return (0, codexProfiles_1.resolveSidekickCodexHome)();
4865
+ function getCodexHomes() {
4866
+ return (0, codexProfiles_1.getCodexMonitoringHomes)();
4834
4867
  }
4835
4868
  function getMostRecentMtime(dir) {
4836
4869
  try {
@@ -4840,7 +4873,7 @@ var require_detect = __commonJS({
4840
4873
  const entries = fs9.readdirSync(dir);
4841
4874
  for (const entry of entries) {
4842
4875
  try {
4843
- const stats = fs9.statSync(path7.join(dir, entry));
4876
+ const stats = fs9.statSync(path8.join(dir, entry));
4844
4877
  if (stats.mtime.getTime() > latest) {
4845
4878
  latest = stats.mtime.getTime();
4846
4879
  }
@@ -4854,46 +4887,51 @@ var require_detect = __commonJS({
4854
4887
  }
4855
4888
  function getOpenCodeActivityMtime() {
4856
4889
  const dataDir = getOpenCodeDataDir();
4857
- const dbPath = path7.join(dataDir, "opencode.db");
4890
+ const dbPath = path8.join(dataDir, "opencode.db");
4858
4891
  try {
4859
4892
  const dbMtime = fs9.statSync(dbPath).mtime.getTime();
4860
4893
  if (dbMtime > 0)
4861
4894
  return dbMtime;
4862
4895
  } catch {
4863
4896
  }
4864
- const storageDir = path7.join(dataDir, "storage");
4865
- const sessionMtime = getMostRecentMtime(path7.join(storageDir, "session"));
4866
- const messageMtime = getMostRecentMtime(path7.join(storageDir, "message"));
4867
- const partMtime = getMostRecentMtime(path7.join(storageDir, "part"));
4897
+ const storageDir = path8.join(dataDir, "storage");
4898
+ const sessionMtime = getMostRecentMtime(path8.join(storageDir, "session"));
4899
+ const messageMtime = getMostRecentMtime(path8.join(storageDir, "message"));
4900
+ const partMtime = getMostRecentMtime(path8.join(storageDir, "part"));
4868
4901
  return Math.max(sessionMtime, messageMtime, partMtime);
4869
4902
  }
4870
4903
  function getCodexActivityMtime() {
4871
- const codexHome = getCodexHome();
4872
- const dbPath = path7.join(codexHome, "state.sqlite");
4873
- try {
4874
- const dbMtime = fs9.statSync(dbPath).mtime.getTime();
4875
- if (dbMtime > 0)
4876
- return dbMtime;
4877
- } catch {
4904
+ let latest = 0;
4905
+ for (const codexHome of getCodexHomes()) {
4906
+ const dbPath = path8.join(codexHome, "state.sqlite");
4907
+ try {
4908
+ const dbMtime = fs9.statSync(dbPath).mtime.getTime();
4909
+ if (dbMtime > latest)
4910
+ latest = dbMtime;
4911
+ } catch {
4912
+ }
4913
+ const sessionsMtime = getMostRecentMtime(path8.join(codexHome, "sessions"));
4914
+ if (sessionsMtime > latest)
4915
+ latest = sessionsMtime;
4878
4916
  }
4879
- return getMostRecentMtime(path7.join(codexHome, "sessions"));
4917
+ return latest;
4880
4918
  }
4881
4919
  function getProviderPaths() {
4882
- const claudeBase = path7.join(os5.homedir(), ".claude", "projects");
4920
+ const claudeBase = path8.join(os6.homedir(), ".claude", "projects");
4883
4921
  const openCodeDataDir = getOpenCodeDataDir();
4922
+ const codexHomes = getCodexHomes();
4884
4923
  return {
4885
4924
  claudeBase,
4886
- openCodeDbPath: path7.join(openCodeDataDir, "opencode.db"),
4887
- openCodeStorageDir: path7.join(openCodeDataDir, "storage"),
4888
- codexSessionsDir: path7.join(getCodexHome(), "sessions"),
4889
- codexDbPath: path7.join(getCodexHome(), "state.sqlite")
4925
+ openCodeDbPath: path8.join(openCodeDataDir, "opencode.db"),
4926
+ openCodeStorageDir: path8.join(openCodeDataDir, "storage"),
4927
+ codexHomes
4890
4928
  };
4891
4929
  }
4892
4930
  function getAllDetectedProviders2() {
4893
- const { claudeBase, openCodeDbPath, openCodeStorageDir, codexSessionsDir, codexDbPath } = getProviderPaths();
4931
+ const { claudeBase, openCodeDbPath, openCodeStorageDir, codexHomes } = getProviderPaths();
4894
4932
  const hasClaude = fs9.existsSync(claudeBase);
4895
4933
  const hasOpenCode = fs9.existsSync(openCodeStorageDir) || fs9.existsSync(openCodeDbPath);
4896
- const hasCodex = fs9.existsSync(codexSessionsDir) || fs9.existsSync(codexDbPath);
4934
+ const hasCodex = codexHomes.some((codexHome) => fs9.existsSync(path8.join(codexHome, "sessions")) || fs9.existsSync(path8.join(codexHome, "state.sqlite")));
4897
4935
  const available = [];
4898
4936
  if (hasClaude)
4899
4937
  available.push({ id: "claude-code", mtime: getMostRecentMtime(claudeBase) });
@@ -4907,10 +4945,10 @@ var require_detect = __commonJS({
4907
4945
  function detectProvider2(override) {
4908
4946
  if (override && override !== "auto")
4909
4947
  return override;
4910
- const { claudeBase, openCodeDbPath, openCodeStorageDir, codexSessionsDir, codexDbPath } = getProviderPaths();
4948
+ const { claudeBase, openCodeDbPath, openCodeStorageDir, codexHomes } = getProviderPaths();
4911
4949
  const hasClaude = fs9.existsSync(claudeBase);
4912
4950
  const hasOpenCode = fs9.existsSync(openCodeStorageDir) || fs9.existsSync(openCodeDbPath);
4913
- const hasCodex = fs9.existsSync(codexSessionsDir) || fs9.existsSync(codexDbPath);
4951
+ const hasCodex = codexHomes.some((codexHome) => fs9.existsSync(path8.join(codexHome, "sessions")) || fs9.existsSync(path8.join(codexHome, "state.sqlite")));
4914
4952
  const available = [];
4915
4953
  if (hasClaude) {
4916
4954
  available.push({ id: "claude-code", mtime: getMostRecentMtime(claudeBase) });
@@ -5055,26 +5093,26 @@ var require_sessionPathResolver = __commonJS({
5055
5093
  exports.discoverWorktreeSiblings = discoverWorktreeSiblings;
5056
5094
  exports.findAllSessionsWithWorktrees = findAllSessionsWithWorktrees;
5057
5095
  var fs9 = __importStar(__require("fs"));
5058
- var path7 = __importStar(__require("path"));
5059
- var os5 = __importStar(__require("os"));
5096
+ var path8 = __importStar(__require("path"));
5097
+ var os6 = __importStar(__require("os"));
5060
5098
  function encodeWorkspacePath(workspacePath) {
5061
5099
  const normalized = workspacePath.replace(/\\/g, "/");
5062
5100
  return normalized.replace(/[:/_]/g, "-");
5063
5101
  }
5064
5102
  function getSessionDirectory(workspacePath) {
5065
5103
  const encoded = encodeWorkspacePath(workspacePath);
5066
- return path7.join(os5.homedir(), ".claude", "projects", encoded);
5104
+ return path8.join(os6.homedir(), ".claude", "projects", encoded);
5067
5105
  }
5068
5106
  var ACTIVE_SESSION_THRESHOLD_MS = 5 * 60 * 1e3;
5069
5107
  function findSubdirectorySessionDirs(workspacePath) {
5070
- const projectsDir = path7.join(os5.homedir(), ".claude", "projects");
5108
+ const projectsDir = path8.join(os6.homedir(), ".claude", "projects");
5071
5109
  try {
5072
5110
  if (!fs9.existsSync(projectsDir)) {
5073
5111
  return [];
5074
5112
  }
5075
5113
  const encodedPrefix = encodeWorkspacePath(workspacePath).toLowerCase();
5076
5114
  const allDirs = fs9.readdirSync(projectsDir).filter((name) => {
5077
- const fullPath = path7.join(projectsDir, name);
5115
+ const fullPath = path8.join(projectsDir, name);
5078
5116
  try {
5079
5117
  return fs9.statSync(fullPath).isDirectory();
5080
5118
  } catch {
@@ -5085,7 +5123,7 @@ var require_sessionPathResolver = __commonJS({
5085
5123
  for (const dir of allDirs) {
5086
5124
  const dirLower = dir.toLowerCase();
5087
5125
  if (dirLower.startsWith(encodedPrefix + "-")) {
5088
- matches.push(path7.join(projectsDir, dir));
5126
+ matches.push(path8.join(projectsDir, dir));
5089
5127
  }
5090
5128
  }
5091
5129
  return matches;
@@ -5101,7 +5139,7 @@ var require_sessionPathResolver = __commonJS({
5101
5139
  const files = fs9.readdirSync(dir).filter((file) => file.endsWith(".jsonl"));
5102
5140
  for (const file of files) {
5103
5141
  try {
5104
- const fullPath = path7.join(dir, file);
5142
+ const fullPath = path8.join(dir, file);
5105
5143
  const stats = fs9.statSync(fullPath);
5106
5144
  if (stats.size > 0 && stats.mtime.getTime() > mostRecentMtime) {
5107
5145
  mostRecentMtime = stats.mtime.getTime();
@@ -5116,7 +5154,7 @@ var require_sessionPathResolver = __commonJS({
5116
5154
  return mostRecentDir;
5117
5155
  }
5118
5156
  function discoverSessionDirectory(workspacePath) {
5119
- const projectsDir = path7.join(os5.homedir(), ".claude", "projects");
5157
+ const projectsDir = path8.join(os6.homedir(), ".claude", "projects");
5120
5158
  const computedDir = getSessionDirectory(workspacePath);
5121
5159
  if (fs9.existsSync(computedDir)) {
5122
5160
  return computedDir;
@@ -5131,7 +5169,7 @@ var require_sessionPathResolver = __commonJS({
5131
5169
  try {
5132
5170
  if (fs9.existsSync(projectsDir)) {
5133
5171
  const existingDirs = fs9.readdirSync(projectsDir).filter((name) => {
5134
- const fullPath = path7.join(projectsDir, name);
5172
+ const fullPath = path8.join(projectsDir, name);
5135
5173
  try {
5136
5174
  return fs9.statSync(fullPath).isDirectory();
5137
5175
  } catch {
@@ -5141,35 +5179,35 @@ var require_sessionPathResolver = __commonJS({
5141
5179
  const normalizedWorkspace = workspacePath.replace(/\\/g, "/").replace(/:/g, "-").replace(/_/g, "-").replace(/\//g, "-").toLowerCase();
5142
5180
  for (const dir of existingDirs) {
5143
5181
  if (dir.toLowerCase() === normalizedWorkspace) {
5144
- return path7.join(projectsDir, dir);
5182
+ return path8.join(projectsDir, dir);
5145
5183
  }
5146
5184
  }
5147
- const workspaceBasename = path7.basename(workspacePath).replace(/_/g, "-").toLowerCase();
5185
+ const workspaceBasename = path8.basename(workspacePath).replace(/_/g, "-").toLowerCase();
5148
5186
  for (const dir of existingDirs) {
5149
5187
  const dirLower = dir.toLowerCase();
5150
5188
  if (dirLower.endsWith("-" + workspaceBasename) || dirLower === workspaceBasename) {
5151
- return path7.join(projectsDir, dir);
5189
+ return path8.join(projectsDir, dir);
5152
5190
  }
5153
5191
  }
5154
5192
  }
5155
5193
  } catch {
5156
5194
  }
5157
5195
  try {
5158
- const claudeTempDir = path7.join(os5.tmpdir(), "claude");
5196
+ const claudeTempDir = path8.join(os6.tmpdir(), "claude");
5159
5197
  if (fs9.existsSync(claudeTempDir)) {
5160
5198
  const tempDirs = fs9.readdirSync(claudeTempDir).filter((name) => {
5161
- const fullPath = path7.join(claudeTempDir, name);
5199
+ const fullPath = path8.join(claudeTempDir, name);
5162
5200
  try {
5163
5201
  return fs9.statSync(fullPath).isDirectory();
5164
5202
  } catch {
5165
5203
  return false;
5166
5204
  }
5167
5205
  });
5168
- const workspaceBasename = path7.basename(workspacePath).replace(/_/g, "-").toLowerCase();
5206
+ const workspaceBasename = path8.basename(workspacePath).replace(/_/g, "-").toLowerCase();
5169
5207
  for (const encodedDir of tempDirs) {
5170
5208
  const encodedLower = encodedDir.toLowerCase();
5171
5209
  if (encodedLower.endsWith("-" + workspaceBasename) || encodedLower === workspaceBasename) {
5172
- const sessionDir = path7.join(projectsDir, encodedDir);
5210
+ const sessionDir = path8.join(projectsDir, encodedDir);
5173
5211
  if (fs9.existsSync(sessionDir)) {
5174
5212
  return sessionDir;
5175
5213
  }
@@ -5188,7 +5226,7 @@ var require_sessionPathResolver = __commonJS({
5188
5226
  }
5189
5227
  const now = Date.now();
5190
5228
  const files = fs9.readdirSync(sessionDir).filter((file) => file.endsWith(".jsonl")).map((file) => {
5191
- const fullPath = path7.join(sessionDir, file);
5229
+ const fullPath = path8.join(sessionDir, file);
5192
5230
  const stats = fs9.statSync(fullPath);
5193
5231
  const mtime = stats.mtime.getTime();
5194
5232
  return {
@@ -5221,7 +5259,7 @@ var require_sessionPathResolver = __commonJS({
5221
5259
  return [];
5222
5260
  }
5223
5261
  const files = fs9.readdirSync(sessionDir).filter((file) => file.endsWith(".jsonl")).map((file) => {
5224
- const fullPath = path7.join(sessionDir, file);
5262
+ const fullPath = path8.join(sessionDir, file);
5225
5263
  const stats = fs9.statSync(fullPath);
5226
5264
  return {
5227
5265
  path: fullPath,
@@ -5248,7 +5286,7 @@ var require_sessionPathResolver = __commonJS({
5248
5286
  return encoded.replace(/-/g, "/");
5249
5287
  }
5250
5288
  function getAllProjectFolders(workspacePath) {
5251
- const projectsDir = path7.join(os5.homedir(), ".claude", "projects");
5289
+ const projectsDir = path8.join(os6.homedir(), ".claude", "projects");
5252
5290
  const folders = [];
5253
5291
  try {
5254
5292
  if (!fs9.existsSync(projectsDir)) {
@@ -5256,7 +5294,7 @@ var require_sessionPathResolver = __commonJS({
5256
5294
  }
5257
5295
  const entries = fs9.readdirSync(projectsDir);
5258
5296
  for (const entry of entries) {
5259
- const fullPath = path7.join(projectsDir, entry);
5297
+ const fullPath = path8.join(projectsDir, entry);
5260
5298
  try {
5261
5299
  const stats = fs9.statSync(fullPath);
5262
5300
  if (!stats.isDirectory()) {
@@ -5267,7 +5305,7 @@ var require_sessionPathResolver = __commonJS({
5267
5305
  let sessionCount = 0;
5268
5306
  for (const sessionFile of sessionFiles) {
5269
5307
  try {
5270
- const sessionPath = path7.join(fullPath, sessionFile);
5308
+ const sessionPath = path8.join(fullPath, sessionFile);
5271
5309
  const sessionStats = fs9.statSync(sessionPath);
5272
5310
  if (sessionStats.size > 0) {
5273
5311
  sessionCount++;
@@ -5319,7 +5357,7 @@ var require_sessionPathResolver = __commonJS({
5319
5357
  return [];
5320
5358
  }
5321
5359
  const files = fs9.readdirSync(sessionDir).filter((file) => file.endsWith(".jsonl")).map((file) => {
5322
- const fullPath = path7.join(sessionDir, file);
5360
+ const fullPath = path8.join(sessionDir, file);
5323
5361
  try {
5324
5362
  const stats = fs9.statSync(fullPath);
5325
5363
  return {
@@ -5337,7 +5375,7 @@ var require_sessionPathResolver = __commonJS({
5337
5375
  }
5338
5376
  }
5339
5377
  function resolveWorktreeMainRepo(workspacePath) {
5340
- const gitPath = path7.join(workspacePath, ".git");
5378
+ const gitPath = path8.join(workspacePath, ".git");
5341
5379
  try {
5342
5380
  const stat = fs9.statSync(gitPath);
5343
5381
  if (stat.isDirectory()) {
@@ -5348,31 +5386,31 @@ var require_sessionPathResolver = __commonJS({
5348
5386
  if (!match)
5349
5387
  return null;
5350
5388
  const gitdir = match[1].trim();
5351
- const resolvedGitdir = path7.isAbsolute(gitdir) ? gitdir : path7.resolve(workspacePath, gitdir);
5352
- const worktreesDir = path7.dirname(resolvedGitdir);
5353
- if (path7.basename(worktreesDir) !== "worktrees")
5389
+ const resolvedGitdir = path8.isAbsolute(gitdir) ? gitdir : path8.resolve(workspacePath, gitdir);
5390
+ const worktreesDir = path8.dirname(resolvedGitdir);
5391
+ if (path8.basename(worktreesDir) !== "worktrees")
5354
5392
  return null;
5355
- const dotGit = path7.dirname(worktreesDir);
5356
- if (path7.basename(dotGit) !== ".git")
5393
+ const dotGit = path8.dirname(worktreesDir);
5394
+ if (path8.basename(dotGit) !== ".git")
5357
5395
  return null;
5358
- return path7.dirname(dotGit);
5396
+ return path8.dirname(dotGit);
5359
5397
  } catch {
5360
5398
  return null;
5361
5399
  }
5362
5400
  }
5363
5401
  function discoverWorktreeSiblings(mainRepoPath) {
5364
- const worktreesDir = path7.join(mainRepoPath, ".git", "worktrees");
5402
+ const worktreesDir = path8.join(mainRepoPath, ".git", "worktrees");
5365
5403
  const siblings = [];
5366
5404
  try {
5367
5405
  if (!fs9.existsSync(worktreesDir))
5368
5406
  return siblings;
5369
5407
  const entries = fs9.readdirSync(worktreesDir);
5370
5408
  for (const entry of entries) {
5371
- const gitdirFile = path7.join(worktreesDir, entry, "gitdir");
5409
+ const gitdirFile = path8.join(worktreesDir, entry, "gitdir");
5372
5410
  try {
5373
5411
  const content = fs9.readFileSync(gitdirFile, "utf-8").trim();
5374
- const worktreeGit = path7.isAbsolute(content) ? content : path7.resolve(worktreesDir, entry, content);
5375
- const worktreeDir = path7.dirname(worktreeGit);
5412
+ const worktreeGit = path8.isAbsolute(content) ? content : path8.resolve(worktreesDir, entry, content);
5413
+ const worktreeDir = path8.dirname(worktreeGit);
5376
5414
  if (fs9.existsSync(worktreeDir)) {
5377
5415
  siblings.push(worktreeDir);
5378
5416
  }
@@ -5466,7 +5504,7 @@ var require_subagentScanner = __commonJS({
5466
5504
  exports.handleTaskUpdate = handleTaskUpdate;
5467
5505
  exports.extractTaskInfo = extractTaskInfo;
5468
5506
  var fs9 = __importStar(__require("fs"));
5469
- var path7 = __importStar(__require("path"));
5507
+ var path8 = __importStar(__require("path"));
5470
5508
  var AGENT_FILE_PATTERN = /^agent-(.+)\.jsonl$/;
5471
5509
  var TASK_TOOLS = ["TaskCreate", "TaskUpdate", "TaskGet", "TaskList"];
5472
5510
  function extractTaskIdFromResult(resultContent) {
@@ -5485,7 +5523,7 @@ var require_subagentScanner = __commonJS({
5485
5523
  function scanSubagentDir(sessionDir, sessionId, logger) {
5486
5524
  const log = logger || (() => {
5487
5525
  });
5488
- const subagentsDir = path7.join(sessionDir, sessionId, "subagents");
5526
+ const subagentsDir = path8.join(sessionDir, sessionId, "subagents");
5489
5527
  const results = [];
5490
5528
  try {
5491
5529
  if (!fs9.existsSync(subagentsDir)) {
@@ -5504,7 +5542,7 @@ var require_subagentScanner = __commonJS({
5504
5542
  continue;
5505
5543
  }
5506
5544
  const agentId = match[1];
5507
- const filePath = path7.join(subagentsDir, file);
5545
+ const filePath = path8.join(subagentsDir, file);
5508
5546
  const agentStats = parseAgentFile(filePath, agentId, log);
5509
5547
  if (agentStats) {
5510
5548
  log(`[SubagentScanner] Agent ${agentId}: ${agentStats.toolCalls.length} tool calls`);
@@ -5714,8 +5752,10 @@ var require_modelContext = __commonJS({
5714
5752
  exports.DEFAULT_CONTEXT_WINDOW = void 0;
5715
5753
  exports.getModelContextWindowSize = getModelContextWindowSize2;
5716
5754
  var MODEL_CONTEXT_SIZES = {
5717
- // Claude 4.6 family (1M context)
5755
+ // Claude — native 1M context (Opus 4.6+, Sonnet 4.6+)
5756
+ "claude-opus-4-7": 1e6,
5718
5757
  "claude-opus-4-6": 1e6,
5758
+ "claude-sonnet-4-7": 1e6,
5719
5759
  "claude-sonnet-4-6": 1e6,
5720
5760
  // Claude 4 family
5721
5761
  "claude-opus-4": 2e5,
@@ -5731,7 +5771,11 @@ var require_modelContext = __commonJS({
5731
5771
  "claude-3-haiku": 2e5,
5732
5772
  // OpenAI GPT-4.1 series (1M context)
5733
5773
  "gpt-4.1": 1048576,
5734
- // OpenAI GPT-5 series
5774
+ // OpenAI GPT-5 series (keys sorted longest-first below; explicit entries
5775
+ // for every variant so prefix matching can't misclassify a new one)
5776
+ "gpt-5.4": 105e4,
5777
+ "gpt-5.3-codex-spark": 128e3,
5778
+ "gpt-5.3-codex": 4e5,
5735
5779
  "gpt-5": 4e5,
5736
5780
  // OpenAI reasoning
5737
5781
  "o1": 2e5,
@@ -5751,11 +5795,14 @@ var require_modelContext = __commonJS({
5751
5795
  function getModelContextWindowSize2(modelId) {
5752
5796
  if (!modelId)
5753
5797
  return exports.DEFAULT_CONTEXT_WINDOW;
5754
- if (MODEL_CONTEXT_SIZES[modelId] !== void 0) {
5755
- return MODEL_CONTEXT_SIZES[modelId];
5798
+ if (/\[1m\]/i.test(modelId))
5799
+ return 1e6;
5800
+ const normalized = modelId.replace(/\[1m\]/gi, "");
5801
+ if (MODEL_CONTEXT_SIZES[normalized] !== void 0) {
5802
+ return MODEL_CONTEXT_SIZES[normalized];
5756
5803
  }
5757
5804
  for (const key of SORTED_KEYS) {
5758
- if (modelId.startsWith(key)) {
5805
+ if (normalized.startsWith(key)) {
5759
5806
  return MODEL_CONTEXT_SIZES[key];
5760
5807
  }
5761
5808
  }
@@ -5808,8 +5855,8 @@ var require_claudeCode = __commonJS({
5808
5855
  Object.defineProperty(exports, "__esModule", { value: true });
5809
5856
  exports.ClaudeCodeProvider = void 0;
5810
5857
  var fs9 = __importStar(__require("fs"));
5811
- var os5 = __importStar(__require("os"));
5812
- var path7 = __importStar(__require("path"));
5858
+ var os6 = __importStar(__require("os"));
5859
+ var path8 = __importStar(__require("path"));
5813
5860
  var jsonl_1 = require_jsonl();
5814
5861
  var sessionPathResolver_1 = require_sessionPathResolver();
5815
5862
  var subagentScanner_1 = require_subagentScanner();
@@ -5946,7 +5993,7 @@ var require_claudeCode = __commonJS({
5946
5993
  return filename.endsWith(".jsonl");
5947
5994
  }
5948
5995
  getSessionId(sessionPath) {
5949
- return path7.basename(sessionPath, ".jsonl");
5996
+ return path8.basename(sessionPath, ".jsonl");
5950
5997
  }
5951
5998
  encodeWorkspacePath(workspacePath) {
5952
5999
  return (0, sessionPathResolver_1.encodeWorkspacePath)(workspacePath);
@@ -6011,7 +6058,7 @@ var require_claudeCode = __commonJS({
6011
6058
  try {
6012
6059
  const content = fs9.readFileSync(sessionPath, "utf8");
6013
6060
  const lines = content.split("\n");
6014
- const projectDir = path7.basename(path7.dirname(sessionPath));
6061
+ const projectDir = path8.basename(path8.dirname(sessionPath));
6015
6062
  const projectPath = (0, sessionPathResolver_1.decodeEncodedPath)(projectDir);
6016
6063
  for (const line of lines) {
6017
6064
  if (results.length >= maxResults)
@@ -6045,11 +6092,11 @@ var require_claudeCode = __commonJS({
6045
6092
  return results;
6046
6093
  }
6047
6094
  getProjectsBaseDir() {
6048
- return path7.join(os5.homedir(), ".claude", "projects");
6095
+ return path8.join(os6.homedir(), ".claude", "projects");
6049
6096
  }
6050
6097
  // --- Stats ---
6051
6098
  readSessionStats(sessionPath) {
6052
- const sessionId = path7.basename(sessionPath, ".jsonl");
6099
+ const sessionId = path8.basename(sessionPath, ".jsonl");
6053
6100
  let messageCount = 0;
6054
6101
  let startTime = "";
6055
6102
  let endTime = "";
@@ -6701,13 +6748,13 @@ var require_openCodeDatabase = __commonJS({
6701
6748
  Object.defineProperty(exports, "__esModule", { value: true });
6702
6749
  exports.OpenCodeDatabase = void 0;
6703
6750
  var fs9 = __importStar(__require("fs"));
6704
- var path7 = __importStar(__require("path"));
6751
+ var path8 = __importStar(__require("path"));
6705
6752
  var child_process_1 = __require("child_process");
6706
6753
  var OpenCodeDatabase = class {
6707
6754
  dbPath;
6708
6755
  runtimeStatus = null;
6709
6756
  constructor(dataDir) {
6710
- this.dbPath = path7.join(dataDir, "opencode.db");
6757
+ this.dbPath = path8.join(dataDir, "opencode.db");
6711
6758
  }
6712
6759
  isAvailable() {
6713
6760
  return fs9.existsSync(this.dbPath);
@@ -6926,7 +6973,7 @@ var require_openCodeDatabase = __commonJS({
6926
6973
  try {
6927
6974
  return fs9.realpathSync(input);
6928
6975
  } catch {
6929
- return path7.resolve(input);
6976
+ return path8.resolve(input);
6930
6977
  }
6931
6978
  }
6932
6979
  function parseStringArray(value) {
@@ -6950,8 +6997,8 @@ var require_openCodeDatabase = __commonJS({
6950
6997
  const normalizedWorkspace = normalizeForCompare(workspacePath);
6951
6998
  if (normalizedCandidate === normalizedWorkspace)
6952
6999
  return 1e4 + normalizedCandidate.length;
6953
- const candidatePrefix = normalizedCandidate.endsWith(path7.sep) ? normalizedCandidate : normalizedCandidate + path7.sep;
6954
- const workspacePrefix = normalizedWorkspace.endsWith(path7.sep) ? normalizedWorkspace : normalizedWorkspace + path7.sep;
7000
+ const candidatePrefix = normalizedCandidate.endsWith(path8.sep) ? normalizedCandidate : normalizedCandidate + path8.sep;
7001
+ const workspacePrefix = normalizedWorkspace.endsWith(path8.sep) ? normalizedWorkspace : normalizedWorkspace + path8.sep;
6955
7002
  if (normalizedWorkspace.startsWith(candidatePrefix))
6956
7003
  return 5e3 + normalizedCandidate.length;
6957
7004
  if (normalizedCandidate.startsWith(workspacePrefix))
@@ -7018,8 +7065,8 @@ var require_openCode = __commonJS({
7018
7065
  Object.defineProperty(exports, "__esModule", { value: true });
7019
7066
  exports.OpenCodeProvider = void 0;
7020
7067
  var fs9 = __importStar(__require("fs"));
7021
- var os5 = __importStar(__require("os"));
7022
- var path7 = __importStar(__require("path"));
7068
+ var os6 = __importStar(__require("os"));
7069
+ var path8 = __importStar(__require("path"));
7023
7070
  var child_process_1 = __require("child_process");
7024
7071
  var openCodeParser_1 = require_openCodeParser();
7025
7072
  var openCodeDatabase_1 = require_openCodeDatabase();
@@ -7027,18 +7074,18 @@ var require_openCode = __commonJS({
7027
7074
  function getOpenCodeDataDir() {
7028
7075
  const xdg = process.env.XDG_DATA_HOME;
7029
7076
  if (xdg) {
7030
- return path7.join(xdg, "opencode");
7077
+ return path8.join(xdg, "opencode");
7031
7078
  }
7032
7079
  if (process.platform === "darwin") {
7033
- return path7.join(os5.homedir(), "Library", "Application Support", "opencode");
7080
+ return path8.join(os6.homedir(), "Library", "Application Support", "opencode");
7034
7081
  }
7035
7082
  if (process.platform === "win32") {
7036
- return path7.join(process.env.LOCALAPPDATA || process.env.APPDATA || path7.join(os5.homedir(), "AppData", "Local"), "opencode");
7083
+ return path8.join(process.env.LOCALAPPDATA || process.env.APPDATA || path8.join(os6.homedir(), "AppData", "Local"), "opencode");
7037
7084
  }
7038
- return path7.join(os5.homedir(), ".local", "share", "opencode");
7085
+ return path8.join(os6.homedir(), ".local", "share", "opencode");
7039
7086
  }
7040
7087
  function getStorageDir() {
7041
- return path7.join(getOpenCodeDataDir(), "storage");
7088
+ return path8.join(getOpenCodeDataDir(), "storage");
7042
7089
  }
7043
7090
  var GENERIC_AGENT_RE = /^(gpt-|claude-|o[1-9]|gemini|default|agent|worker)/i;
7044
7091
  function isGenericAgentType(type) {
@@ -7069,18 +7116,18 @@ var require_openCode = __commonJS({
7069
7116
  }
7070
7117
  var DB_SESSION_PREFIX = "db-sessions";
7071
7118
  function makeDbSessionPath(dataDir, projectId, sessionId) {
7072
- return path7.join(dataDir, DB_SESSION_PREFIX, projectId, `${sessionId}.json`);
7119
+ return path8.join(dataDir, DB_SESSION_PREFIX, projectId, `${sessionId}.json`);
7073
7120
  }
7074
7121
  function isDbSessionPath(sessionPath) {
7075
- return sessionPath.includes(path7.sep + DB_SESSION_PREFIX + path7.sep);
7122
+ return sessionPath.includes(path8.sep + DB_SESSION_PREFIX + path8.sep);
7076
7123
  }
7077
7124
  function extractProjectIdFromDbPath(sessionPath) {
7078
- const prefix = path7.sep + DB_SESSION_PREFIX + path7.sep;
7125
+ const prefix = path8.sep + DB_SESSION_PREFIX + path8.sep;
7079
7126
  const idx = sessionPath.indexOf(prefix);
7080
7127
  if (idx < 0)
7081
7128
  return null;
7082
7129
  const rest = sessionPath.substring(idx + prefix.length);
7083
- const slashIdx = rest.indexOf(path7.sep);
7130
+ const slashIdx = rest.indexOf(path8.sep);
7084
7131
  return slashIdx > 0 ? rest.substring(0, slashIdx) : null;
7085
7132
  }
7086
7133
  function extractRoleFromDbMessage(row) {
@@ -7118,25 +7165,25 @@ var require_openCode = __commonJS({
7118
7165
  function resolveProjectIdFromFiles(workspacePath) {
7119
7166
  const workspaceResolved = normalizePath(workspacePath);
7120
7167
  try {
7121
- const projectDir = path7.join(getStorageDir(), "project");
7168
+ const projectDir = path8.join(getStorageDir(), "project");
7122
7169
  if (!fs9.existsSync(projectDir))
7123
7170
  return resolveProjectIdFromGit(workspacePath);
7124
7171
  const files = fs9.readdirSync(projectDir).filter((f) => f.endsWith(".json"));
7125
7172
  const matches = [];
7126
7173
  for (const file of files) {
7127
7174
  try {
7128
- const project = JSON.parse(fs9.readFileSync(path7.join(projectDir, file), "utf-8"));
7175
+ const project = JSON.parse(fs9.readFileSync(path8.join(projectDir, file), "utf-8"));
7129
7176
  if (project.path) {
7130
7177
  const projectPath = normalizePath(project.path);
7131
7178
  if (projectPath === workspaceResolved) {
7132
7179
  matches.push({ id: project.id, path: projectPath });
7133
7180
  continue;
7134
7181
  }
7135
- if (workspaceResolved.startsWith(projectPath + path7.sep)) {
7182
+ if (workspaceResolved.startsWith(projectPath + path8.sep)) {
7136
7183
  matches.push({ id: project.id, path: projectPath });
7137
7184
  continue;
7138
7185
  }
7139
- if (projectPath.startsWith(workspaceResolved + path7.sep)) {
7186
+ if (projectPath.startsWith(workspaceResolved + path8.sep)) {
7140
7187
  matches.push({ id: project.id, path: projectPath });
7141
7188
  }
7142
7189
  }
@@ -7184,7 +7231,7 @@ var require_openCode = __commonJS({
7184
7231
  try {
7185
7232
  return fs9.realpathSync(input);
7186
7233
  } catch {
7187
- return path7.resolve(input);
7234
+ return path8.resolve(input);
7188
7235
  }
7189
7236
  }
7190
7237
  var OpenCodeFileReader = class {
@@ -7196,7 +7243,7 @@ var require_openCode = __commonJS({
7196
7243
  this.storageBase = getStorageDir();
7197
7244
  }
7198
7245
  readNew() {
7199
- const messageDir = path7.join(this.storageBase, "message", this.sessionId);
7246
+ const messageDir = path8.join(this.storageBase, "message", this.sessionId);
7200
7247
  if (!fs9.existsSync(messageDir))
7201
7248
  return [];
7202
7249
  let messageFiles;
@@ -7207,8 +7254,8 @@ var require_openCode = __commonJS({
7207
7254
  }
7208
7255
  const newEvents = [];
7209
7256
  for (const file of messageFiles) {
7210
- const msgId = path7.basename(file, ".json");
7211
- const messagePath = path7.join(messageDir, file);
7257
+ const msgId = path8.basename(file, ".json");
7258
+ const messagePath = path8.join(messageDir, file);
7212
7259
  const message = readJsonSafe(messagePath);
7213
7260
  if (!message)
7214
7261
  continue;
@@ -7218,11 +7265,11 @@ var require_openCode = __commonJS({
7218
7265
  } catch {
7219
7266
  messageMtimeMs = 0;
7220
7267
  }
7221
- const partDir = path7.join(this.storageBase, "part", msgId);
7268
+ const partDir = path8.join(this.storageBase, "part", msgId);
7222
7269
  let parts = [];
7223
7270
  if (fs9.existsSync(partDir)) {
7224
7271
  try {
7225
- parts = fs9.readdirSync(partDir).filter((f) => f.endsWith(".json")).map((f) => readJsonSafe(path7.join(partDir, f))).filter((p) => p !== null);
7272
+ parts = fs9.readdirSync(partDir).filter((f) => f.endsWith(".json")).map((f) => readJsonSafe(path8.join(partDir, f))).filter((p) => p !== null);
7226
7273
  } catch {
7227
7274
  }
7228
7275
  }
@@ -7259,7 +7306,7 @@ var require_openCode = __commonJS({
7259
7306
  this.seenMessages.clear();
7260
7307
  }
7261
7308
  exists() {
7262
- return fs9.existsSync(path7.join(this.storageBase, "message", this.sessionId));
7309
+ return fs9.existsSync(path8.join(this.storageBase, "message", this.sessionId));
7263
7310
  }
7264
7311
  flush() {
7265
7312
  }
@@ -7476,7 +7523,7 @@ var require_openCode = __commonJS({
7476
7523
  const db = this.ensureDb();
7477
7524
  if (!db)
7478
7525
  return false;
7479
- const projectId = extractProjectIdFromDbPath(dir + path7.sep + "dummy.json") || path7.basename(dir);
7526
+ const projectId = extractProjectIdFromDbPath(dir + path8.sep + "dummy.json") || path8.basename(dir);
7480
7527
  return projectId.length > 0 && db.hasProject(projectId);
7481
7528
  }
7482
7529
  mapDbSessions(projectId) {
@@ -7501,14 +7548,14 @@ var require_openCode = __commonJS({
7501
7548
  const projectId = resolveProjectId(workspacePath, db, dbStatus);
7502
7549
  if (projectId) {
7503
7550
  if (db) {
7504
- return path7.join(getOpenCodeDataDir(), DB_SESSION_PREFIX, projectId);
7551
+ return path8.join(getOpenCodeDataDir(), DB_SESSION_PREFIX, projectId);
7505
7552
  }
7506
- return path7.join(getStorageDir(), "session", projectId);
7553
+ return path8.join(getStorageDir(), "session", projectId);
7507
7554
  }
7508
7555
  if (dbStatus.kind !== "db_missing") {
7509
- return path7.join(getOpenCodeDataDir(), DB_SESSION_PREFIX);
7556
+ return path8.join(getOpenCodeDataDir(), DB_SESSION_PREFIX);
7510
7557
  }
7511
- return path7.join(getStorageDir(), "session");
7558
+ return path8.join(getStorageDir(), "session");
7512
7559
  }
7513
7560
  discoverSessionDirectory(workspacePath) {
7514
7561
  const db = this.ensureDb();
@@ -7518,13 +7565,13 @@ var require_openCode = __commonJS({
7518
7565
  return null;
7519
7566
  if (db) {
7520
7567
  if (db.hasProject(projectId)) {
7521
- return path7.join(getOpenCodeDataDir(), DB_SESSION_PREFIX, projectId);
7568
+ return path8.join(getOpenCodeDataDir(), DB_SESSION_PREFIX, projectId);
7522
7569
  }
7523
7570
  }
7524
7571
  if (dbStatus.kind !== "db_missing") {
7525
7572
  return null;
7526
7573
  }
7527
- const dir = path7.join(getStorageDir(), "session", projectId);
7574
+ const dir = path8.join(getStorageDir(), "session", projectId);
7528
7575
  return fs9.existsSync(dir) ? dir : null;
7529
7576
  }
7530
7577
  // --- Session discovery ---
@@ -7551,7 +7598,7 @@ var require_openCode = __commonJS({
7551
7598
  return this.findActiveSessionFromFiles(projectId);
7552
7599
  }
7553
7600
  findActiveSessionFromFiles(projectId) {
7554
- const sessionDir = path7.join(getStorageDir(), "session", projectId);
7601
+ const sessionDir = path8.join(getStorageDir(), "session", projectId);
7555
7602
  if (!fs9.existsSync(sessionDir))
7556
7603
  return null;
7557
7604
  let bestPath = null;
@@ -7559,7 +7606,7 @@ var require_openCode = __commonJS({
7559
7606
  try {
7560
7607
  const files = fs9.readdirSync(sessionDir).filter((f) => f.endsWith(".json"));
7561
7608
  for (const file of files) {
7562
- const fullPath = path7.join(sessionDir, file);
7609
+ const fullPath = path8.join(sessionDir, file);
7563
7610
  try {
7564
7611
  const stats = fs9.statSync(fullPath);
7565
7612
  if (stats.size > 0 && stats.mtime.getTime() > bestMtime) {
@@ -7573,14 +7620,14 @@ var require_openCode = __commonJS({
7573
7620
  return null;
7574
7621
  }
7575
7622
  if (bestPath) {
7576
- const sessionId = path7.basename(bestPath, ".json");
7577
- const messageDir = path7.join(getStorageDir(), "message", sessionId);
7623
+ const sessionId = path8.basename(bestPath, ".json");
7624
+ const messageDir = path8.join(getStorageDir(), "message", sessionId);
7578
7625
  if (fs9.existsSync(messageDir)) {
7579
7626
  try {
7580
7627
  const messageFiles = fs9.readdirSync(messageDir).filter((f) => f.endsWith(".json"));
7581
7628
  for (const mf of messageFiles) {
7582
7629
  try {
7583
- const mstat = fs9.statSync(path7.join(messageDir, mf));
7630
+ const mstat = fs9.statSync(path8.join(messageDir, mf));
7584
7631
  if (mstat.mtime.getTime() > bestMtime) {
7585
7632
  bestMtime = mstat.mtime.getTime();
7586
7633
  }
@@ -7610,20 +7657,20 @@ var require_openCode = __commonJS({
7610
7657
  if (dbStatus.kind !== "db_missing") {
7611
7658
  return [];
7612
7659
  }
7613
- const sessionDir = path7.join(getStorageDir(), "session", projectId);
7660
+ const sessionDir = path8.join(getStorageDir(), "session", projectId);
7614
7661
  return this.findSessionsInDirectoryFromFiles(sessionDir);
7615
7662
  }
7616
7663
  findSessionsInDirectory(dir) {
7617
7664
  const db = this.ensureDb();
7618
7665
  const dbStatus = this.getRuntimeStatus();
7619
- if (db && dir.includes(path7.sep + DB_SESSION_PREFIX + path7.sep)) {
7620
- const projectId = extractProjectIdFromDbPath(dir + path7.sep + "dummy.json");
7666
+ if (db && dir.includes(path8.sep + DB_SESSION_PREFIX + path8.sep)) {
7667
+ const projectId = extractProjectIdFromDbPath(dir + path8.sep + "dummy.json");
7621
7668
  if (projectId) {
7622
7669
  return this.mapDbSessions(projectId);
7623
7670
  }
7624
7671
  }
7625
7672
  if (db) {
7626
- const dirName = path7.basename(dir);
7673
+ const dirName = path8.basename(dir);
7627
7674
  if (db.hasProject(dirName)) {
7628
7675
  return this.mapDbSessions(dirName);
7629
7676
  }
@@ -7638,7 +7685,7 @@ var require_openCode = __commonJS({
7638
7685
  if (!fs9.existsSync(dir))
7639
7686
  return [];
7640
7687
  return fs9.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
7641
- const fullPath = path7.join(dir, f);
7688
+ const fullPath = path8.join(dir, f);
7642
7689
  try {
7643
7690
  const stats = fs9.statSync(fullPath);
7644
7691
  return { path: fullPath, mtime: stats.mtime.getTime(), size: stats.size };
@@ -7668,7 +7715,7 @@ var require_openCode = __commonJS({
7668
7715
  if (!projStats || projStats.sessionCount === 0)
7669
7716
  continue;
7670
7717
  folders.push({
7671
- dir: path7.join(dataDir, DB_SESSION_PREFIX, project.id),
7718
+ dir: path8.join(dataDir, DB_SESSION_PREFIX, project.id),
7672
7719
  name: project.worktree || project.name || project.id,
7673
7720
  encodedName: project.id,
7674
7721
  sessionCount: projStats.sessionCount,
@@ -7696,23 +7743,23 @@ var require_openCode = __commonJS({
7696
7743
  }
7697
7744
  getAllProjectFoldersFromFiles(workspacePath) {
7698
7745
  const folders = [];
7699
- const sessionBase = path7.join(getStorageDir(), "session");
7746
+ const sessionBase = path8.join(getStorageDir(), "session");
7700
7747
  try {
7701
7748
  if (!fs9.existsSync(sessionBase))
7702
7749
  return [];
7703
7750
  const projectIds = fs9.readdirSync(sessionBase).filter((name) => {
7704
7751
  try {
7705
- return fs9.statSync(path7.join(sessionBase, name)).isDirectory();
7752
+ return fs9.statSync(path8.join(sessionBase, name)).isDirectory();
7706
7753
  } catch {
7707
7754
  return false;
7708
7755
  }
7709
7756
  });
7710
- const projectDir = path7.join(getStorageDir(), "project");
7757
+ const projectDir = path8.join(getStorageDir(), "project");
7711
7758
  const projectNames = /* @__PURE__ */ new Map();
7712
7759
  if (fs9.existsSync(projectDir)) {
7713
7760
  try {
7714
7761
  for (const file of fs9.readdirSync(projectDir).filter((f) => f.endsWith(".json"))) {
7715
- const proj = readJsonSafe(path7.join(projectDir, file));
7762
+ const proj = readJsonSafe(path8.join(projectDir, file));
7716
7763
  if (proj) {
7717
7764
  projectNames.set(proj.id, proj.path || proj.name || proj.id);
7718
7765
  }
@@ -7725,14 +7772,14 @@ var require_openCode = __commonJS({
7725
7772
  currentProjectId = resolveProjectIdFromFiles(workspacePath);
7726
7773
  }
7727
7774
  for (const projectId of projectIds) {
7728
- const projSessionDir = path7.join(sessionBase, projectId);
7775
+ const projSessionDir = path8.join(sessionBase, projectId);
7729
7776
  let sessionCount = 0;
7730
7777
  let lastModified = /* @__PURE__ */ new Date(0);
7731
7778
  try {
7732
7779
  const sessions = fs9.readdirSync(projSessionDir).filter((f) => f.endsWith(".json"));
7733
7780
  for (const session of sessions) {
7734
7781
  try {
7735
- const fstats = fs9.statSync(path7.join(projSessionDir, session));
7782
+ const fstats = fs9.statSync(path8.join(projSessionDir, session));
7736
7783
  if (fstats.size > 0) {
7737
7784
  sessionCount++;
7738
7785
  if (fstats.mtime > lastModified) {
@@ -7773,7 +7820,7 @@ var require_openCode = __commonJS({
7773
7820
  return filename.endsWith(".json");
7774
7821
  }
7775
7822
  getSessionId(sessionPath) {
7776
- return path7.basename(sessionPath, ".json");
7823
+ return path8.basename(sessionPath, ".json");
7777
7824
  }
7778
7825
  encodeWorkspacePath(workspacePath) {
7779
7826
  const db = this.ensureDb();
@@ -7801,19 +7848,19 @@ var require_openCode = __commonJS({
7801
7848
  if (session?.title) {
7802
7849
  return truncateTitle(session.title);
7803
7850
  }
7804
- const messageDir = path7.join(getStorageDir(), "message", sessionId);
7851
+ const messageDir = path8.join(getStorageDir(), "message", sessionId);
7805
7852
  if (!fs9.existsSync(messageDir))
7806
7853
  return null;
7807
7854
  try {
7808
7855
  const files = fs9.readdirSync(messageDir).filter((f) => f.endsWith(".json")).slice(0, 5);
7809
7856
  for (const file of files) {
7810
- const msg = readJsonSafe(path7.join(messageDir, file));
7857
+ const msg = readJsonSafe(path8.join(messageDir, file));
7811
7858
  if (msg?.role === "user") {
7812
- const partDir = path7.join(getStorageDir(), "part", msg.id);
7859
+ const partDir = path8.join(getStorageDir(), "part", msg.id);
7813
7860
  if (fs9.existsSync(partDir)) {
7814
7861
  const partFiles = fs9.readdirSync(partDir).filter((f) => f.endsWith(".json"));
7815
7862
  for (const pf of partFiles) {
7816
- const part = readJsonSafe(path7.join(partDir, pf));
7863
+ const part = readJsonSafe(path8.join(partDir, pf));
7817
7864
  if (part?.type === "text" && part.text.trim().length > 0) {
7818
7865
  let text = part.text.trim().replace(/\s+/g, " ");
7819
7866
  if (text.length > 60) {
@@ -7997,7 +8044,7 @@ var require_openCode = __commonJS({
7997
8044
  }
7998
8045
  searchInSessionFromFiles(sessionPath, sessionId, queryLower, query, maxResults) {
7999
8046
  const results = [];
8000
- const messageDir = path7.join(getStorageDir(), "message", sessionId);
8047
+ const messageDir = path8.join(getStorageDir(), "message", sessionId);
8001
8048
  if (!fs9.existsSync(messageDir))
8002
8049
  return results;
8003
8050
  try {
@@ -8007,10 +8054,10 @@ var require_openCode = __commonJS({
8007
8054
  for (const file of messageFiles) {
8008
8055
  if (results.length >= maxResults)
8009
8056
  break;
8010
- const msg = readJsonSafe(path7.join(messageDir, file));
8057
+ const msg = readJsonSafe(path8.join(messageDir, file));
8011
8058
  if (!msg)
8012
8059
  continue;
8013
- const partDir = path7.join(getStorageDir(), "part", msg.id);
8060
+ const partDir = path8.join(getStorageDir(), "part", msg.id);
8014
8061
  if (!fs9.existsSync(partDir))
8015
8062
  continue;
8016
8063
  try {
@@ -8018,7 +8065,7 @@ var require_openCode = __commonJS({
8018
8065
  for (const pf of partFiles) {
8019
8066
  if (results.length >= maxResults)
8020
8067
  break;
8021
- const part = readJsonSafe(path7.join(partDir, pf));
8068
+ const part = readJsonSafe(path8.join(partDir, pf));
8022
8069
  if (!part)
8023
8070
  continue;
8024
8071
  let text = "";
@@ -8055,11 +8102,11 @@ var require_openCode = __commonJS({
8055
8102
  return results;
8056
8103
  }
8057
8104
  getProjectsBaseDir() {
8058
- return path7.join(getStorageDir(), "session");
8105
+ return path8.join(getStorageDir(), "session");
8059
8106
  }
8060
8107
  // --- Stats ---
8061
8108
  readSessionStats(sessionPath) {
8062
- const sessionId = path7.basename(sessionPath, ".json");
8109
+ const sessionId = path8.basename(sessionPath, ".json");
8063
8110
  const db = this.ensureDb();
8064
8111
  let messageCount = 0;
8065
8112
  let startTime = "";
@@ -8931,13 +8978,13 @@ var require_codexDatabase = __commonJS({
8931
8978
  Object.defineProperty(exports, "__esModule", { value: true });
8932
8979
  exports.CodexDatabase = void 0;
8933
8980
  var fs9 = __importStar(__require("fs"));
8934
- var path7 = __importStar(__require("path"));
8981
+ var path8 = __importStar(__require("path"));
8935
8982
  var child_process_1 = __require("child_process");
8936
8983
  var CodexDatabase = class {
8937
8984
  dbPath;
8938
8985
  sqlite3Available = null;
8939
8986
  constructor(codexHome) {
8940
- this.dbPath = path7.join(codexHome, "state.sqlite");
8987
+ this.dbPath = path8.join(codexHome, "state.sqlite");
8941
8988
  }
8942
8989
  isAvailable() {
8943
8990
  try {
@@ -9002,7 +9049,7 @@ var require_codexDatabase = __commonJS({
9002
9049
  const all = this.query("SELECT * FROM threads ORDER BY updated_at DESC");
9003
9050
  return all.filter((t) => {
9004
9051
  const threadCwd = normalizePath(t.cwd);
9005
- return threadCwd === normalized || normalized.startsWith(threadCwd + path7.sep) || threadCwd.startsWith(normalized + path7.sep);
9052
+ return threadCwd === normalized || normalized.startsWith(threadCwd + path8.sep) || threadCwd.startsWith(normalized + path8.sep);
9006
9053
  });
9007
9054
  }
9008
9055
  getMostRecentThread(cwd2) {
@@ -9032,7 +9079,7 @@ var require_codexDatabase = __commonJS({
9032
9079
  try {
9033
9080
  return fs9.realpathSync(input);
9034
9081
  } catch {
9035
- return path7.resolve(input);
9082
+ return path8.resolve(input);
9036
9083
  }
9037
9084
  }
9038
9085
  }
@@ -9082,22 +9129,22 @@ var require_codex = __commonJS({
9082
9129
  Object.defineProperty(exports, "__esModule", { value: true });
9083
9130
  exports.CodexProvider = void 0;
9084
9131
  var fs9 = __importStar(__require("fs"));
9085
- var path7 = __importStar(__require("path"));
9132
+ var path8 = __importStar(__require("path"));
9086
9133
  var codexParser_1 = require_codexParser();
9087
9134
  var codexDatabase_1 = require_codexDatabase();
9088
9135
  var codexProfiles_1 = require_codexProfiles();
9089
9136
  var modelContext_1 = require_modelContext();
9090
- function getCodexHome() {
9091
- return (0, codexProfiles_1.resolveSidekickCodexHome)();
9137
+ function getCodexHomes() {
9138
+ return (0, codexProfiles_1.getCodexMonitoringHomes)();
9092
9139
  }
9093
- function getSessionsDir() {
9094
- return path7.join(getCodexHome(), "sessions");
9140
+ function getSessionsDirs() {
9141
+ return getCodexHomes().map((home) => path8.join(home, "sessions"));
9095
9142
  }
9096
9143
  function isRolloutFile(filename) {
9097
9144
  return filename.startsWith("rollout-") && filename.endsWith(".jsonl");
9098
9145
  }
9099
9146
  function extractSessionId(filename) {
9100
- const base = path7.basename(filename, ".jsonl");
9147
+ const base = path8.basename(filename, ".jsonl");
9101
9148
  const parts = base.split("-");
9102
9149
  if (parts.length >= 6) {
9103
9150
  const possibleUuid = parts.slice(-5).join("-");
@@ -9121,7 +9168,7 @@ var require_codex = __commonJS({
9121
9168
  return results;
9122
9169
  const entries = fs9.readdirSync(dir, { withFileTypes: true });
9123
9170
  for (const entry of entries) {
9124
- const fullPath = path7.join(dir, entry.name);
9171
+ const fullPath = path8.join(dir, entry.name);
9125
9172
  if (entry.isDirectory()) {
9126
9173
  results.push(...findRolloutFiles(fullPath));
9127
9174
  } else if (entry.isFile() && isRolloutFile(entry.name)) {
@@ -9138,6 +9185,31 @@ var require_codex = __commonJS({
9138
9185
  }
9139
9186
  return results;
9140
9187
  }
9188
+ function findRolloutFilesInConfiguredHomes() {
9189
+ const results = [];
9190
+ const seen = /* @__PURE__ */ new Set();
9191
+ for (const sessionsDir of getSessionsDirs()) {
9192
+ for (const file of findRolloutFiles(sessionsDir)) {
9193
+ if (seen.has(file.path))
9194
+ continue;
9195
+ seen.add(file.path);
9196
+ results.push(file);
9197
+ }
9198
+ }
9199
+ return results;
9200
+ }
9201
+ function getSessionsDir() {
9202
+ const dirs = getSessionsDirs();
9203
+ for (const dir of dirs) {
9204
+ if (findRolloutFiles(dir).length > 0) {
9205
+ return dir;
9206
+ }
9207
+ }
9208
+ return dirs[0] ?? path8.join(process.cwd(), ".codex", "sessions");
9209
+ }
9210
+ function getCodexHome() {
9211
+ return path8.dirname(getSessionsDir());
9212
+ }
9141
9213
  function isSystemInjection(text) {
9142
9214
  const t = text.trimStart();
9143
9215
  return t.startsWith("<") || t.startsWith("#");
@@ -9202,13 +9274,13 @@ var require_codex = __commonJS({
9202
9274
  function cwdMatches(sessionCwd, workspacePath) {
9203
9275
  const normalizedSession = normalizePath(sessionCwd);
9204
9276
  const normalizedWorkspace = normalizePath(workspacePath);
9205
- return normalizedSession === normalizedWorkspace || normalizedWorkspace.startsWith(normalizedSession + path7.sep) || normalizedSession.startsWith(normalizedWorkspace + path7.sep);
9277
+ return normalizedSession === normalizedWorkspace || normalizedWorkspace.startsWith(normalizedSession + path8.sep) || normalizedSession.startsWith(normalizedWorkspace + path8.sep);
9206
9278
  }
9207
9279
  function normalizePath(input) {
9208
9280
  try {
9209
9281
  return fs9.realpathSync(input);
9210
9282
  } catch {
9211
- return path7.resolve(input);
9283
+ return path8.resolve(input);
9212
9284
  }
9213
9285
  }
9214
9286
  function extractFirstUserMessage(rolloutPath) {
@@ -9430,7 +9502,7 @@ var require_codex = __commonJS({
9430
9502
  if (db) {
9431
9503
  const thread = db.getMostRecentThread(workspacePath);
9432
9504
  if (thread?.rollout_path) {
9433
- return path7.dirname(thread.rollout_path);
9505
+ return path8.dirname(thread.rollout_path);
9434
9506
  }
9435
9507
  }
9436
9508
  return getSessionsDir();
@@ -9440,23 +9512,20 @@ var require_codex = __commonJS({
9440
9512
  if (db) {
9441
9513
  const thread = db.getMostRecentThread(workspacePath);
9442
9514
  if (thread?.rollout_path && fs9.existsSync(thread.rollout_path)) {
9443
- return path7.dirname(thread.rollout_path);
9515
+ return path8.dirname(thread.rollout_path);
9444
9516
  }
9445
9517
  }
9446
- const sessionsDir = getSessionsDir();
9447
- if (!fs9.existsSync(sessionsDir))
9448
- return null;
9449
- const files = findRolloutFiles(sessionsDir);
9518
+ const files = findRolloutFilesInConfiguredHomes();
9450
9519
  if (files.length === 0)
9451
9520
  return null;
9452
9521
  files.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
9453
9522
  for (const file of files) {
9454
9523
  const meta = readSessionMeta(file.path);
9455
9524
  if (meta && cwdMatches(meta.cwd, workspacePath)) {
9456
- return path7.dirname(file.path);
9525
+ return path8.dirname(file.path);
9457
9526
  }
9458
9527
  }
9459
- return fs9.existsSync(sessionsDir) ? sessionsDir : null;
9528
+ return getSessionsDir();
9460
9529
  }
9461
9530
  // --- Session discovery ---
9462
9531
  findActiveSession(workspacePath) {
@@ -9467,10 +9536,7 @@ var require_codex = __commonJS({
9467
9536
  return thread.rollout_path;
9468
9537
  }
9469
9538
  }
9470
- const sessionsDir = getSessionsDir();
9471
- if (!fs9.existsSync(sessionsDir))
9472
- return null;
9473
- const files = findRolloutFiles(sessionsDir);
9539
+ const files = findRolloutFilesInConfiguredHomes();
9474
9540
  files.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
9475
9541
  for (const file of files) {
9476
9542
  const meta = readSessionMeta(file.path);
@@ -9488,10 +9554,7 @@ var require_codex = __commonJS({
9488
9554
  if (dbPaths.length > 0)
9489
9555
  return dbPaths;
9490
9556
  }
9491
- const sessionsDir = getSessionsDir();
9492
- if (!fs9.existsSync(sessionsDir))
9493
- return [];
9494
- const files = findRolloutFiles(sessionsDir);
9557
+ const files = findRolloutFilesInConfiguredHomes();
9495
9558
  files.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
9496
9559
  return files.filter((f) => {
9497
9560
  const meta = readSessionMeta(f.path);
@@ -9523,27 +9586,23 @@ var require_codex = __commonJS({
9523
9586
  });
9524
9587
  }
9525
9588
  }
9526
- const sessionsDir = getSessionsDir();
9527
- if (fs9.existsSync(sessionsDir)) {
9528
- const files = findRolloutFiles(sessionsDir);
9529
- for (const file of files) {
9530
- const meta = readSessionMeta(file.path);
9531
- if (!meta?.cwd)
9532
- continue;
9533
- const existing = seenCwds.get(meta.cwd);
9534
- if (existing) {
9535
- if (file.mtime > existing.lastModified) {
9536
- existing.lastModified = file.mtime;
9537
- }
9538
- } else {
9539
- seenCwds.set(meta.cwd, {
9540
- dir: path7.dirname(file.path),
9541
- name: meta.cwd,
9542
- encodedName: meta.cwd,
9543
- sessionCount: 1,
9544
- lastModified: file.mtime
9545
- });
9589
+ for (const file of findRolloutFilesInConfiguredHomes()) {
9590
+ const meta = readSessionMeta(file.path);
9591
+ if (!meta?.cwd)
9592
+ continue;
9593
+ const existing = seenCwds.get(meta.cwd);
9594
+ if (existing) {
9595
+ if (file.mtime > existing.lastModified) {
9596
+ existing.lastModified = file.mtime;
9546
9597
  }
9598
+ } else {
9599
+ seenCwds.set(meta.cwd, {
9600
+ dir: path8.dirname(file.path),
9601
+ name: meta.cwd,
9602
+ encodedName: meta.cwd,
9603
+ sessionCount: 1,
9604
+ lastModified: file.mtime
9605
+ });
9547
9606
  }
9548
9607
  }
9549
9608
  folders.push(...seenCwds.values());
@@ -9566,7 +9625,7 @@ var require_codex = __commonJS({
9566
9625
  return isRolloutFile(filename);
9567
9626
  }
9568
9627
  getSessionId(sessionPath) {
9569
- return extractSessionId(path7.basename(sessionPath));
9628
+ return extractSessionId(path8.basename(sessionPath));
9570
9629
  }
9571
9630
  encodeWorkspacePath(workspacePath) {
9572
9631
  return workspacePath;
@@ -9607,16 +9666,13 @@ var require_codex = __commonJS({
9607
9666
  if (forkedThreads.length > 0)
9608
9667
  return subagents;
9609
9668
  }
9610
- const sessionsDir = getSessionsDir();
9611
- if (!fs9.existsSync(sessionsDir))
9612
- return subagents;
9613
- const files = findRolloutFiles(sessionsDir);
9669
+ const files = findRolloutFilesInConfiguredHomes();
9614
9670
  files.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
9615
9671
  const filesToCheck = files.slice(0, 50);
9616
9672
  for (const file of filesToCheck) {
9617
9673
  const meta = readSessionMeta(file.path);
9618
9674
  if (meta?.forked_from_id === sessionId) {
9619
- const childId = extractSessionId(path7.basename(file.path));
9675
+ const childId = extractSessionId(path8.basename(file.path));
9620
9676
  const stats = this.buildSubagentStats(childId, file.path, meta.source);
9621
9677
  if (stats)
9622
9678
  subagents.push(stats);
@@ -9728,7 +9784,7 @@ var require_codex = __commonJS({
9728
9784
  }
9729
9785
  // --- Stats ---
9730
9786
  readSessionStats(sessionPath) {
9731
- const sessionId = extractSessionId(path7.basename(sessionPath));
9787
+ const sessionId = extractSessionId(path8.basename(sessionPath));
9732
9788
  let messageCount = 0;
9733
9789
  let startTime = "";
9734
9790
  let endTime = "";
@@ -10070,24 +10126,24 @@ var require_toolSummary = __commonJS({
10070
10126
  if (!pattern)
10071
10127
  return "";
10072
10128
  const glob = input.glob;
10073
- const path7 = input.path;
10129
+ const path8 = input.path;
10074
10130
  const type = input.type;
10075
10131
  const parts = [truncate2(pattern, 40)];
10076
10132
  if (glob)
10077
10133
  parts.push(`in ${glob}`);
10078
10134
  else if (type)
10079
10135
  parts.push(`in *.${type}`);
10080
- else if (path7)
10081
- parts.push(`in ${basename5(path7)}`);
10136
+ else if (path8)
10137
+ parts.push(`in ${basename5(path8)}`);
10082
10138
  return parts.join(" ");
10083
10139
  };
10084
10140
  var formatGlob = (input) => {
10085
10141
  const pattern = input.pattern;
10086
10142
  if (!pattern)
10087
10143
  return "";
10088
- const path7 = input.path;
10089
- if (path7)
10090
- return `${pattern} in ${basename5(path7)}`;
10144
+ const path8 = input.path;
10145
+ if (path8)
10146
+ return `${pattern} in ${basename5(path8)}`;
10091
10147
  return pattern;
10092
10148
  };
10093
10149
  var formatTask = (input) => {
@@ -10396,12 +10452,12 @@ var require_subagentTraceParser = __commonJS({
10396
10452
  Object.defineProperty(exports, "__esModule", { value: true });
10397
10453
  exports.scanSubagentTraces = scanSubagentTraces;
10398
10454
  var fs9 = __importStar(__require("fs"));
10399
- var path7 = __importStar(__require("path"));
10455
+ var path8 = __importStar(__require("path"));
10400
10456
  var toolSummary_1 = require_toolSummary();
10401
10457
  var noiseClassifier_1 = require_noiseClassifier();
10402
10458
  var AGENT_FILE_PATTERN = /^agent-(.+)\.jsonl$/;
10403
10459
  function scanSubagentTraces(sessionDir, sessionId) {
10404
- const subagentsDir = path7.join(sessionDir, sessionId, "subagents");
10460
+ const subagentsDir = path8.join(sessionDir, sessionId, "subagents");
10405
10461
  try {
10406
10462
  if (!fs9.existsSync(subagentsDir))
10407
10463
  return [];
@@ -10412,7 +10468,7 @@ var require_subagentTraceParser = __commonJS({
10412
10468
  if (!match)
10413
10469
  continue;
10414
10470
  const agentId = match[1];
10415
- const filePath = path7.join(subagentsDir, file);
10471
+ const filePath = path8.join(subagentsDir, file);
10416
10472
  const trace = parseAgentTrace(filePath, agentId);
10417
10473
  if (trace)
10418
10474
  traces.push(trace);
@@ -10696,8 +10752,8 @@ var require_debugLogParser = __commonJS({
10696
10752
  exports.collapseDuplicates = collapseDuplicates;
10697
10753
  exports.discoverDebugLogs = discoverDebugLogs;
10698
10754
  var fs9 = __importStar(__require("fs"));
10699
- var path7 = __importStar(__require("path"));
10700
- var os5 = __importStar(__require("os"));
10755
+ var path8 = __importStar(__require("path"));
10756
+ var os6 = __importStar(__require("os"));
10701
10757
  var LOG_LINE_PATTERN = /^(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}[.\d]*Z?)\s+(DEBUG|INFO|WARN|ERROR)\s+(.*)$/;
10702
10758
  var LEVEL_ORDER = {
10703
10759
  DEBUG: 0,
@@ -10751,12 +10807,12 @@ var require_debugLogParser = __commonJS({
10751
10807
  return result;
10752
10808
  }
10753
10809
  function discoverDebugLogs() {
10754
- const debugDir = path7.join(os5.homedir(), ".claude", "debug");
10810
+ const debugDir = path8.join(os6.homedir(), ".claude", "debug");
10755
10811
  try {
10756
10812
  if (!fs9.existsSync(debugDir))
10757
10813
  return [];
10758
10814
  return fs9.readdirSync(debugDir).filter((f) => f.endsWith(".log") || f.endsWith(".txt")).map((name) => {
10759
- const fullPath = path7.join(debugDir, name);
10815
+ const fullPath = path8.join(debugDir, name);
10760
10816
  try {
10761
10817
  const stat = fs9.statSync(fullPath);
10762
10818
  return {
@@ -10820,7 +10876,7 @@ var require_sessionSearch = __commonJS({
10820
10876
  Object.defineProperty(exports, "__esModule", { value: true });
10821
10877
  exports.searchSessions = searchSessions2;
10822
10878
  var fs9 = __importStar(__require("fs"));
10823
- var path7 = __importStar(__require("path"));
10879
+ var path8 = __importStar(__require("path"));
10824
10880
  async function searchSessions2(provider, query, opts) {
10825
10881
  const maxResults = opts?.maxResults ?? 50;
10826
10882
  const results = [];
@@ -10839,7 +10895,7 @@ var require_sessionSearch = __commonJS({
10839
10895
  const dir = folder.dir;
10840
10896
  if (fs9.existsSync(dir)) {
10841
10897
  const entries = fs9.readdirSync(dir).filter((f) => f.endsWith(".jsonl") || f.endsWith(".json"));
10842
- sessionFiles = entries.map((f) => path7.join(dir, f));
10898
+ sessionFiles = entries.map((f) => path8.join(dir, f));
10843
10899
  }
10844
10900
  } catch {
10845
10901
  }
@@ -11711,7 +11767,7 @@ var require_sqliteWatcher = __commonJS({
11711
11767
  Object.defineProperty(exports, "__esModule", { value: true });
11712
11768
  exports.SqliteSessionWatcher = void 0;
11713
11769
  var fs9 = __importStar(__require("fs"));
11714
- var path7 = __importStar(__require("path"));
11770
+ var path8 = __importStar(__require("path"));
11715
11771
  var openCodeDatabase_1 = require_openCodeDatabase();
11716
11772
  var openCodeParser_1 = require_openCodeParser();
11717
11773
  var DEBOUNCE_MS = 200;
@@ -11836,7 +11892,7 @@ var require_sqliteWatcher = __commonJS({
11836
11892
  this.dbPath = dbPath;
11837
11893
  this.sessionId = sessionId;
11838
11894
  this.callbacks = callbacks;
11839
- this.db = new openCodeDatabase_1.OpenCodeDatabase(path7.dirname(dbPath));
11895
+ this.db = new openCodeDatabase_1.OpenCodeDatabase(path8.dirname(dbPath));
11840
11896
  }
11841
11897
  get isActive() {
11842
11898
  return this._isActive;
@@ -12001,15 +12057,15 @@ var require_factory = __commonJS({
12001
12057
  }();
12002
12058
  Object.defineProperty(exports, "__esModule", { value: true });
12003
12059
  exports.createWatcher = createWatcher5;
12004
- var os5 = __importStar(__require("os"));
12005
- var path7 = __importStar(__require("path"));
12060
+ var os6 = __importStar(__require("os"));
12061
+ var path8 = __importStar(__require("path"));
12006
12062
  var jsonlWatcher_1 = require_jsonlWatcher();
12007
12063
  var sqliteWatcher_1 = require_sqliteWatcher();
12008
12064
  function getOpenCodeDataDir() {
12009
12065
  const xdg = process.env.XDG_DATA_HOME;
12010
12066
  if (xdg)
12011
- return path7.join(xdg, "opencode");
12012
- return path7.join(os5.homedir(), ".local", "share", "opencode");
12067
+ return path8.join(xdg, "opencode");
12068
+ return path8.join(os6.homedir(), ".local", "share", "opencode");
12013
12069
  }
12014
12070
  function createWatcher5(options) {
12015
12071
  const { provider, workspacePath, sessionId, callbacks } = options;
@@ -12021,7 +12077,7 @@ var require_factory = __commonJS({
12021
12077
  if (sessionId) {
12022
12078
  const match = sessions.find((s) => s.includes(sessionId));
12023
12079
  if (!match) {
12024
- throw new Error(`Session ${sessionId} not found. Available: ${sessions.slice(0, 5).map((s) => path7.basename(s)).join(", ")}`);
12080
+ throw new Error(`Session ${sessionId} not found. Available: ${sessions.slice(0, 5).map((s) => path8.basename(s)).join(", ")}`);
12025
12081
  }
12026
12082
  sessionPath = match;
12027
12083
  } else {
@@ -12037,8 +12093,8 @@ var require_factory = __commonJS({
12037
12093
  return new jsonlWatcher_1.JsonlSessionWatcher(providerId, sessionPath, callbacks);
12038
12094
  case "opencode": {
12039
12095
  const dataDir = getOpenCodeDataDir();
12040
- const dbPath = path7.join(dataDir, "opencode.db");
12041
- const sid = path7.basename(sessionPath, ".json");
12096
+ const dbPath = path8.join(dataDir, "opencode.db");
12097
+ const sid = path8.basename(sessionPath, ".json");
12042
12098
  return new sqliteWatcher_1.SqliteSessionWatcher(dbPath, sid, callbacks);
12043
12099
  }
12044
12100
  default:
@@ -14433,6 +14489,243 @@ var require_PatternExtractor = __commonJS({
14433
14489
  }
14434
14490
  });
14435
14491
 
14492
+ // ../sidekick-shared/dist/modelInfo.js
14493
+ var require_modelInfo = __commonJS({
14494
+ "../sidekick-shared/dist/modelInfo.js"(exports) {
14495
+ "use strict";
14496
+ Object.defineProperty(exports, "__esModule", { value: true });
14497
+ exports._setPricingOverrides = _setPricingOverrides;
14498
+ exports._getPricingOverrides = _getPricingOverrides;
14499
+ exports._clearPricingOverrides = _clearPricingOverrides;
14500
+ exports.parseModelId = parseModelId;
14501
+ exports.getModelPricing = getModelPricing;
14502
+ exports.getModelInfo = getModelInfo2;
14503
+ exports.calculateCostWithPricing = calculateCostWithPricing;
14504
+ exports.calculateCost = calculateCost;
14505
+ exports.formatCost = formatCost5;
14506
+ var modelContext_1 = require_modelContext();
14507
+ var PRICING_TABLE = {
14508
+ // ── Anthropic: Claude ──
14509
+ "claude-haiku-4.5": {
14510
+ inputCostPerMillion: 1,
14511
+ outputCostPerMillion: 5,
14512
+ cacheWriteCostPerMillion: 1.25,
14513
+ cacheReadCostPerMillion: 0.1
14514
+ },
14515
+ "claude-haiku-3.5": {
14516
+ inputCostPerMillion: 0.8,
14517
+ outputCostPerMillion: 4,
14518
+ cacheWriteCostPerMillion: 1,
14519
+ cacheReadCostPerMillion: 0.08
14520
+ },
14521
+ "claude-sonnet-4.6": {
14522
+ inputCostPerMillion: 3,
14523
+ outputCostPerMillion: 15,
14524
+ cacheWriteCostPerMillion: 3.75,
14525
+ cacheReadCostPerMillion: 0.3
14526
+ },
14527
+ "claude-sonnet-4.5": {
14528
+ inputCostPerMillion: 3,
14529
+ outputCostPerMillion: 15,
14530
+ cacheWriteCostPerMillion: 3.75,
14531
+ cacheReadCostPerMillion: 0.3
14532
+ },
14533
+ "claude-sonnet-4": {
14534
+ inputCostPerMillion: 3,
14535
+ outputCostPerMillion: 15,
14536
+ cacheWriteCostPerMillion: 3.75,
14537
+ cacheReadCostPerMillion: 0.3
14538
+ },
14539
+ "claude-opus-4.6": {
14540
+ inputCostPerMillion: 15,
14541
+ outputCostPerMillion: 75,
14542
+ cacheWriteCostPerMillion: 18.75,
14543
+ cacheReadCostPerMillion: 1.5
14544
+ },
14545
+ "claude-opus-4.5": {
14546
+ inputCostPerMillion: 5,
14547
+ outputCostPerMillion: 25,
14548
+ cacheWriteCostPerMillion: 6.25,
14549
+ cacheReadCostPerMillion: 0.5
14550
+ },
14551
+ "claude-opus-4": {
14552
+ inputCostPerMillion: 15,
14553
+ outputCostPerMillion: 75,
14554
+ cacheWriteCostPerMillion: 18.75,
14555
+ cacheReadCostPerMillion: 1.5
14556
+ },
14557
+ // ── OpenAI: GPT-4.x family ──
14558
+ "gpt-4.1": {
14559
+ inputCostPerMillion: 2,
14560
+ outputCostPerMillion: 8,
14561
+ cacheWriteCostPerMillion: 0,
14562
+ cacheReadCostPerMillion: 0.5
14563
+ },
14564
+ "gpt-4o-mini": {
14565
+ inputCostPerMillion: 0.15,
14566
+ outputCostPerMillion: 0.6,
14567
+ cacheWriteCostPerMillion: 0,
14568
+ cacheReadCostPerMillion: 0.075
14569
+ },
14570
+ "gpt-4o": {
14571
+ inputCostPerMillion: 2.5,
14572
+ outputCostPerMillion: 10,
14573
+ cacheWriteCostPerMillion: 0,
14574
+ cacheReadCostPerMillion: 1.25
14575
+ },
14576
+ "gpt-4-turbo": {
14577
+ inputCostPerMillion: 10,
14578
+ outputCostPerMillion: 30,
14579
+ cacheWriteCostPerMillion: 0,
14580
+ cacheReadCostPerMillion: 0
14581
+ },
14582
+ // ── OpenAI: GPT-5 family (baseline estimate; LiteLLM catalog overrides) ──
14583
+ // Codex emits `gpt-5.4`, `gpt-5.3-codex`, `gpt-5`. Anchoring the static
14584
+ // estimate to gpt-4o-tier rates so offline users see a reasonable ballpark.
14585
+ "gpt-5.4": {
14586
+ inputCostPerMillion: 1.25,
14587
+ outputCostPerMillion: 10,
14588
+ cacheWriteCostPerMillion: 0,
14589
+ cacheReadCostPerMillion: 0.125
14590
+ },
14591
+ "gpt-5.3-codex": {
14592
+ inputCostPerMillion: 1.25,
14593
+ outputCostPerMillion: 10,
14594
+ cacheWriteCostPerMillion: 0,
14595
+ cacheReadCostPerMillion: 0.125
14596
+ },
14597
+ "gpt-5.3": {
14598
+ inputCostPerMillion: 1.25,
14599
+ outputCostPerMillion: 10,
14600
+ cacheWriteCostPerMillion: 0,
14601
+ cacheReadCostPerMillion: 0.125
14602
+ },
14603
+ "gpt-5": {
14604
+ inputCostPerMillion: 1.25,
14605
+ outputCostPerMillion: 10,
14606
+ cacheWriteCostPerMillion: 0,
14607
+ cacheReadCostPerMillion: 0.125
14608
+ },
14609
+ // ── OpenAI: o-series (reasoning models) ──
14610
+ "o3-mini": {
14611
+ inputCostPerMillion: 1.1,
14612
+ outputCostPerMillion: 4.4,
14613
+ cacheWriteCostPerMillion: 0,
14614
+ cacheReadCostPerMillion: 0.55
14615
+ },
14616
+ "o3": {
14617
+ inputCostPerMillion: 2,
14618
+ outputCostPerMillion: 8,
14619
+ cacheWriteCostPerMillion: 0,
14620
+ cacheReadCostPerMillion: 0.5
14621
+ },
14622
+ "o1-mini": {
14623
+ inputCostPerMillion: 3,
14624
+ outputCostPerMillion: 12,
14625
+ cacheWriteCostPerMillion: 0,
14626
+ cacheReadCostPerMillion: 1.5
14627
+ },
14628
+ "o1": {
14629
+ inputCostPerMillion: 15,
14630
+ outputCostPerMillion: 60,
14631
+ cacheWriteCostPerMillion: 0,
14632
+ cacheReadCostPerMillion: 7.5
14633
+ }
14634
+ };
14635
+ var STATIC_SORTED_KEYS = Object.keys(PRICING_TABLE).sort((a, b) => b.length - a.length);
14636
+ var overrideTable = {};
14637
+ var overrideSortedKeys = [];
14638
+ function _setPricingOverrides(overrides) {
14639
+ overrideTable = { ...overrides };
14640
+ overrideSortedKeys = Object.keys(overrideTable).sort((a, b) => b.length - a.length);
14641
+ }
14642
+ function _getPricingOverrides() {
14643
+ return { ...overrideTable };
14644
+ }
14645
+ function _clearPricingOverrides() {
14646
+ overrideTable = {};
14647
+ overrideSortedKeys = [];
14648
+ }
14649
+ var CLAUDE_RE = /^claude-(haiku|sonnet|opus)-([0-9.]+)/i;
14650
+ var GPT_RE = /^gpt-([0-9][0-9.A-Za-z-]*)/i;
14651
+ var O_SERIES_RE = /^o([0-9]+)(-mini|-pro)?/i;
14652
+ var GEMINI_RE = /^gemini-([0-9][0-9.A-Za-z-]*)/i;
14653
+ function parseModelId(modelId) {
14654
+ if (!modelId)
14655
+ return null;
14656
+ const normalized = modelId.replace(/\[1m\]/gi, "");
14657
+ const claude = normalized.match(CLAUDE_RE);
14658
+ if (claude) {
14659
+ return { provider: "anthropic", family: claude[1].toLowerCase(), version: claude[2] };
14660
+ }
14661
+ const gpt = normalized.match(GPT_RE);
14662
+ if (gpt) {
14663
+ return { provider: "openai", family: "gpt", version: gpt[1] };
14664
+ }
14665
+ const oSeries = normalized.match(O_SERIES_RE);
14666
+ if (oSeries) {
14667
+ const suffix = oSeries[2] ? oSeries[2] : "";
14668
+ return { provider: "openai", family: "o", version: `${oSeries[1]}${suffix}` };
14669
+ }
14670
+ const gemini = normalized.match(GEMINI_RE);
14671
+ if (gemini) {
14672
+ return { provider: "google", family: "gemini", version: gemini[1] };
14673
+ }
14674
+ return null;
14675
+ }
14676
+ function findLongestPrefix(keys, modelId) {
14677
+ for (const key of keys) {
14678
+ if (modelId === key || modelId.startsWith(key))
14679
+ return key;
14680
+ }
14681
+ return null;
14682
+ }
14683
+ function getModelPricing(modelId) {
14684
+ if (!modelId)
14685
+ return null;
14686
+ const normalized = modelId.replace(/\[1m\]/gi, "");
14687
+ if (overrideTable[normalized])
14688
+ return overrideTable[normalized];
14689
+ const overridePrefix = findLongestPrefix(overrideSortedKeys, normalized);
14690
+ if (overridePrefix)
14691
+ return overrideTable[overridePrefix];
14692
+ if (PRICING_TABLE[normalized])
14693
+ return PRICING_TABLE[normalized];
14694
+ const staticPrefix = findLongestPrefix(STATIC_SORTED_KEYS, normalized);
14695
+ if (staticPrefix)
14696
+ return PRICING_TABLE[staticPrefix];
14697
+ return null;
14698
+ }
14699
+ function getModelInfo2(modelId) {
14700
+ const parsed = parseModelId(modelId);
14701
+ return {
14702
+ provider: parsed?.provider ?? null,
14703
+ family: parsed?.family ?? null,
14704
+ version: parsed?.version ?? null,
14705
+ contextWindow: (0, modelContext_1.getModelContextWindowSize)(modelId),
14706
+ pricing: getModelPricing(modelId)
14707
+ };
14708
+ }
14709
+ function calculateCostWithPricing(usage, pricing) {
14710
+ const reasoning = usage.reasoningTokens ?? 0;
14711
+ return usage.inputTokens / 1e6 * pricing.inputCostPerMillion + usage.outputTokens / 1e6 * pricing.outputCostPerMillion + reasoning / 1e6 * pricing.outputCostPerMillion + usage.cacheWriteTokens / 1e6 * pricing.cacheWriteCostPerMillion + usage.cacheReadTokens / 1e6 * pricing.cacheReadCostPerMillion;
14712
+ }
14713
+ function calculateCost(usage, modelId) {
14714
+ const pricing = getModelPricing(modelId);
14715
+ if (!pricing)
14716
+ return null;
14717
+ return calculateCostWithPricing(usage, pricing);
14718
+ }
14719
+ function formatCost5(cost) {
14720
+ if (cost === null || cost === void 0)
14721
+ return "\u2014";
14722
+ if (cost < 0.01)
14723
+ return `$${cost.toFixed(4)}`;
14724
+ return `$${cost.toFixed(2)}`;
14725
+ }
14726
+ }
14727
+ });
14728
+
14436
14729
  // ../sidekick-shared/dist/aggregation/EventAggregator.js
14437
14730
  var require_EventAggregator = __commonJS({
14438
14731
  "../sidekick-shared/dist/aggregation/EventAggregator.js"(exports) {
@@ -14448,6 +14741,7 @@ var require_EventAggregator = __commonJS({
14448
14741
  var FrequencyTracker_1 = require_FrequencyTracker();
14449
14742
  var HeatmapTracker_1 = require_HeatmapTracker();
14450
14743
  var PatternExtractor_1 = require_PatternExtractor();
14744
+ var modelInfo_1 = require_modelInfo();
14451
14745
  var DEFAULT_TIMELINE_CAP = 200;
14452
14746
  var DEFAULT_LATENCY_CAP = 100;
14453
14747
  var DEFAULT_BURN_WINDOW_MS = 5 * 6e4;
@@ -14639,6 +14933,14 @@ var require_EventAggregator = __commonJS({
14639
14933
  this.currentContextSize = contextSize;
14640
14934
  if (event.model) {
14641
14935
  const model = event.model;
14936
+ const pricing = event.cost ? null : (0, modelInfo_1.getModelPricing)(model);
14937
+ const computed = event.cost ? event.cost : pricing ? (0, modelInfo_1.calculateCostWithPricing)({
14938
+ inputTokens: inputTok,
14939
+ outputTokens: outputTok,
14940
+ cacheWriteTokens: cacheWrite,
14941
+ cacheReadTokens: cacheRead
14942
+ }, pricing) : 0;
14943
+ const evPriced = event.cost != null || pricing != null;
14642
14944
  const acc = this.modelUsage.get(model) ?? {
14643
14945
  calls: 0,
14644
14946
  tokens: 0,
@@ -14646,7 +14948,8 @@ var require_EventAggregator = __commonJS({
14646
14948
  outputTokens: 0,
14647
14949
  cacheWriteTokens: 0,
14648
14950
  cacheReadTokens: 0,
14649
- cost: 0
14951
+ cost: 0,
14952
+ priced: true
14650
14953
  };
14651
14954
  acc.calls++;
14652
14955
  acc.tokens += inputTok + outputTok;
@@ -14654,8 +14957,9 @@ var require_EventAggregator = __commonJS({
14654
14957
  acc.outputTokens += outputTok;
14655
14958
  acc.cacheWriteTokens += cacheWrite;
14656
14959
  acc.cacheReadTokens += cacheRead;
14657
- if (event.cost)
14658
- acc.cost += event.cost;
14960
+ acc.cost += computed;
14961
+ if (!evPriced)
14962
+ acc.priced = false;
14659
14963
  this.modelUsage.set(model, acc);
14660
14964
  }
14661
14965
  }
@@ -14956,7 +15260,26 @@ var require_EventAggregator = __commonJS({
14956
15260
  const outputTok = usage.output_tokens;
14957
15261
  const cacheWrite = usage.cache_creation_input_tokens ?? 0;
14958
15262
  const cacheRead = usage.cache_read_input_tokens ?? 0;
14959
- const cost = usage.reported_cost ?? 0;
15263
+ const reasoningTok = usage.reasoning_tokens ?? 0;
15264
+ const reported = usage.reported_cost ?? 0;
15265
+ const modelKey = model ?? this.currentModel;
15266
+ const pricing = modelKey ? (0, modelInfo_1.getModelPricing)(modelKey) : null;
15267
+ let priced = reported > 0 || pricing !== null;
15268
+ let cost;
15269
+ if (reported > 0) {
15270
+ cost = reported;
15271
+ } else if (pricing) {
15272
+ cost = (0, modelInfo_1.calculateCostWithPricing)({
15273
+ inputTokens: inputTok,
15274
+ outputTokens: outputTok,
15275
+ cacheWriteTokens: cacheWrite,
15276
+ cacheReadTokens: cacheRead,
15277
+ reasoningTokens: reasoningTok
15278
+ }, pricing);
15279
+ } else {
15280
+ cost = 0;
15281
+ priced = false;
15282
+ }
14960
15283
  this.inputTokens += inputTok;
14961
15284
  this.outputTokens += outputTok;
14962
15285
  this.cacheWriteTokens += cacheWrite;
@@ -14990,15 +15313,17 @@ var require_EventAggregator = __commonJS({
14990
15313
  inputTokens: contextSize,
14991
15314
  turnIndex: this.contextTurnIndex++
14992
15315
  });
14993
- const modelKey = model ?? this.currentModel ?? "unknown";
14994
- const acc = this.modelUsage.get(modelKey) ?? {
15316
+ const perModelKey = model ?? this.currentModel ?? "unknown";
15317
+ const acc = this.modelUsage.get(perModelKey) ?? {
14995
15318
  calls: 0,
14996
15319
  tokens: 0,
14997
15320
  inputTokens: 0,
14998
15321
  outputTokens: 0,
14999
15322
  cacheWriteTokens: 0,
15000
15323
  cacheReadTokens: 0,
15001
- cost: 0
15324
+ reasoningTokens: 0,
15325
+ cost: 0,
15326
+ priced: true
15002
15327
  };
15003
15328
  acc.calls++;
15004
15329
  acc.tokens += inputTok + outputTok;
@@ -15006,8 +15331,11 @@ var require_EventAggregator = __commonJS({
15006
15331
  acc.outputTokens += outputTok;
15007
15332
  acc.cacheWriteTokens += cacheWrite;
15008
15333
  acc.cacheReadTokens += cacheRead;
15334
+ acc.reasoningTokens = (acc.reasoningTokens ?? 0) + reasoningTok;
15009
15335
  acc.cost += cost;
15010
- this.modelUsage.set(modelKey, acc);
15336
+ if (!priced)
15337
+ acc.priced = false;
15338
+ this.modelUsage.set(perModelKey, acc);
15011
15339
  this.updateBurnRate(timestamp);
15012
15340
  }
15013
15341
  // ═══════════════════════════════════════════════════════════════════════
@@ -16059,15 +16387,15 @@ var require_snapshot = __commonJS({
16059
16387
  exports.deleteSnapshot = deleteSnapshot2;
16060
16388
  exports.isSnapshotValid = isSnapshotValid2;
16061
16389
  var fs9 = __importStar(__require("fs"));
16062
- var path7 = __importStar(__require("path"));
16390
+ var path8 = __importStar(__require("path"));
16063
16391
  var paths_1 = require_paths();
16064
16392
  var SNAPSHOT_VERSION = 1;
16065
16393
  function getSnapshotsDir() {
16066
- return path7.join((0, paths_1.getConfigDir)(), "snapshots");
16394
+ return path8.join((0, paths_1.getConfigDir)(), "snapshots");
16067
16395
  }
16068
16396
  function getSnapshotPath(sessionId) {
16069
16397
  const safe = sessionId.replace(/[/\\:]/g, "_");
16070
- return path7.join(getSnapshotsDir(), `${safe}.json`);
16398
+ return path8.join(getSnapshotsDir(), `${safe}.json`);
16071
16399
  }
16072
16400
  function saveSnapshot2(snapshot) {
16073
16401
  try {
@@ -17286,12 +17614,12 @@ var require_credentialIO = __commonJS({
17286
17614
  exports.readActiveCredentials = readActiveCredentials;
17287
17615
  exports.writeActiveCredentials = writeActiveCredentials;
17288
17616
  var fs9 = __importStar(__require("fs"));
17289
- var path7 = __importStar(__require("path"));
17290
- var os5 = __importStar(__require("os"));
17617
+ var path8 = __importStar(__require("path"));
17618
+ var os6 = __importStar(__require("os"));
17291
17619
  var child_process_1 = __require("child_process");
17292
17620
  var KEYCHAIN_SERVICE = "Claude Code-credentials";
17293
17621
  function getCredentialsFilePath() {
17294
- return path7.join(os5.homedir(), ".claude", ".credentials.json");
17622
+ return path8.join(os6.homedir(), ".claude", ".credentials.json");
17295
17623
  }
17296
17624
  function readActiveCredentials() {
17297
17625
  if (process.platform === "darwin") {
@@ -17436,28 +17764,28 @@ var require_accounts = __commonJS({
17436
17764
  exports.getActiveAccount = getActiveAccount3;
17437
17765
  exports.isMultiAccountEnabled = isMultiAccountEnabled;
17438
17766
  var fs9 = __importStar(__require("fs"));
17439
- var path7 = __importStar(__require("path"));
17440
- var os5 = __importStar(__require("os"));
17767
+ var path8 = __importStar(__require("path"));
17768
+ var os6 = __importStar(__require("os"));
17441
17769
  var credentialIO_1 = require_credentialIO();
17442
17770
  var accountRegistry_1 = require_accountRegistry();
17443
17771
  function getClaudeDir() {
17444
- return path7.join(os5.homedir(), ".claude");
17772
+ return path8.join(os6.homedir(), ".claude");
17445
17773
  }
17446
17774
  function getClaudeConfigPath() {
17447
- const primary = path7.join(getClaudeDir(), ".claude.json");
17775
+ const primary = path8.join(getClaudeDir(), ".claude.json");
17448
17776
  try {
17449
17777
  const data = JSON.parse(fs9.readFileSync(primary, "utf8"));
17450
17778
  if (data?.oauthAccount)
17451
17779
  return primary;
17452
17780
  } catch {
17453
17781
  }
17454
- return path7.join(os5.homedir(), ".claude.json");
17782
+ return path8.join(os6.homedir(), ".claude.json");
17455
17783
  }
17456
17784
  function getCredentialsDir() {
17457
- return path7.join((0, accountRegistry_1.getAccountsDir)(), "credentials");
17785
+ return path8.join((0, accountRegistry_1.getAccountsDir)(), "credentials");
17458
17786
  }
17459
17787
  function getConfigsDir() {
17460
- return path7.join((0, accountRegistry_1.getAccountsDir)(), "configs");
17788
+ return path8.join((0, accountRegistry_1.getAccountsDir)(), "configs");
17461
17789
  }
17462
17790
  function ensureDirs() {
17463
17791
  for (const dir of [(0, accountRegistry_1.getAccountsDir)(), getCredentialsDir(), getConfigsDir()]) {
@@ -17540,14 +17868,14 @@ var require_accounts = __commonJS({
17540
17868
  if (existing) {
17541
17869
  if (label !== void 0)
17542
17870
  existing.label = label;
17543
- atomicWriteJson(path7.join(getCredentialsDir(), `${active.uuid}.credentials.json`), credBlob);
17544
- atomicWriteJson(path7.join(getConfigsDir(), `${active.uuid}.config.json`), configBlob);
17871
+ atomicWriteJson(path8.join(getCredentialsDir(), `${active.uuid}.credentials.json`), credBlob);
17872
+ atomicWriteJson(path8.join(getConfigsDir(), `${active.uuid}.config.json`), configBlob);
17545
17873
  registry.activeAccountUuid = active.uuid;
17546
17874
  writeAccountRegistry(registry);
17547
17875
  return { success: true };
17548
17876
  }
17549
- atomicWriteJson(path7.join(getCredentialsDir(), `${active.uuid}.credentials.json`), credBlob);
17550
- atomicWriteJson(path7.join(getConfigsDir(), `${active.uuid}.config.json`), configBlob);
17877
+ atomicWriteJson(path8.join(getCredentialsDir(), `${active.uuid}.credentials.json`), credBlob);
17878
+ atomicWriteJson(path8.join(getConfigsDir(), `${active.uuid}.config.json`), configBlob);
17551
17879
  registry.accounts.push({
17552
17880
  uuid: active.uuid,
17553
17881
  email: active.email,
@@ -17581,7 +17909,7 @@ var require_accounts = __commonJS({
17581
17909
  if (currentActive) {
17582
17910
  if (originalCreds) {
17583
17911
  try {
17584
- atomicWriteJson(path7.join(getCredentialsDir(), `${currentActive.uuid}.credentials.json`), originalCreds);
17912
+ atomicWriteJson(path8.join(getCredentialsDir(), `${currentActive.uuid}.credentials.json`), originalCreds);
17585
17913
  } catch {
17586
17914
  }
17587
17915
  }
@@ -17589,7 +17917,7 @@ var require_accounts = __commonJS({
17589
17917
  try {
17590
17918
  const parsed = JSON.parse(originalConfig);
17591
17919
  if (parsed.oauthAccount) {
17592
- atomicWriteJson(path7.join(getConfigsDir(), `${currentActive.uuid}.config.json`), parsed.oauthAccount);
17920
+ atomicWriteJson(path8.join(getConfigsDir(), `${currentActive.uuid}.config.json`), parsed.oauthAccount);
17593
17921
  }
17594
17922
  } catch {
17595
17923
  }
@@ -17598,12 +17926,12 @@ var require_accounts = __commonJS({
17598
17926
  let targetCreds;
17599
17927
  let targetOauthAccount;
17600
17928
  try {
17601
- targetCreds = JSON.parse(fs9.readFileSync(path7.join(getCredentialsDir(), `${uuid}.credentials.json`), "utf8"));
17929
+ targetCreds = JSON.parse(fs9.readFileSync(path8.join(getCredentialsDir(), `${uuid}.credentials.json`), "utf8"));
17602
17930
  } catch {
17603
17931
  return { success: false, error: `Backed-up credentials for ${target.email} not found.` };
17604
17932
  }
17605
17933
  try {
17606
- targetOauthAccount = JSON.parse(fs9.readFileSync(path7.join(getConfigsDir(), `${uuid}.config.json`), "utf8"));
17934
+ targetOauthAccount = JSON.parse(fs9.readFileSync(path8.join(getConfigsDir(), `${uuid}.config.json`), "utf8"));
17607
17935
  } catch {
17608
17936
  return { success: false, error: `Backed-up config for ${target.email} not found.` };
17609
17937
  }
@@ -17655,11 +17983,11 @@ var require_accounts = __commonJS({
17655
17983
  return { success: false, error: `Account ${uuid} not found.` };
17656
17984
  }
17657
17985
  try {
17658
- fs9.unlinkSync(path7.join(getCredentialsDir(), `${uuid}.credentials.json`));
17986
+ fs9.unlinkSync(path8.join(getCredentialsDir(), `${uuid}.credentials.json`));
17659
17987
  } catch {
17660
17988
  }
17661
17989
  try {
17662
- fs9.unlinkSync(path7.join(getConfigsDir(), `${uuid}.config.json`));
17990
+ fs9.unlinkSync(path8.join(getConfigsDir(), `${uuid}.config.json`));
17663
17991
  } catch {
17664
17992
  }
17665
17993
  registry.accounts.splice(idx, 1);
@@ -18052,10 +18380,10 @@ var require_quotaSnapshots = __commonJS({
18052
18380
  exports.writeQuotaSnapshot = writeQuotaSnapshot2;
18053
18381
  exports.readQuotaSnapshot = readQuotaSnapshot2;
18054
18382
  var fs9 = __importStar(__require("fs"));
18055
- var path7 = __importStar(__require("path"));
18383
+ var path8 = __importStar(__require("path"));
18056
18384
  var paths_1 = require_paths();
18057
18385
  function getQuotaSnapshotPath() {
18058
- return path7.join((0, paths_1.getConfigDir)(), "quota-snapshots.json");
18386
+ return path8.join((0, paths_1.getConfigDir)(), "quota-snapshots.json");
18059
18387
  }
18060
18388
  function ensureConfigDir() {
18061
18389
  fs9.mkdirSync((0, paths_1.getConfigDir)(), { recursive: true, mode: 448 });
@@ -18147,109 +18475,175 @@ var require_codexQuota = __commonJS({
18147
18475
  }
18148
18476
  });
18149
18477
 
18150
- // ../sidekick-shared/dist/modelInfo.js
18151
- var require_modelInfo = __commonJS({
18152
- "../sidekick-shared/dist/modelInfo.js"(exports) {
18478
+ // ../sidekick-shared/dist/pricingCatalog.js
18479
+ var require_pricingCatalog = __commonJS({
18480
+ "../sidekick-shared/dist/pricingCatalog.js"(exports) {
18153
18481
  "use strict";
18482
+ var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
18483
+ if (k2 === void 0) k2 = k;
18484
+ var desc = Object.getOwnPropertyDescriptor(m, k);
18485
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18486
+ desc = { enumerable: true, get: function() {
18487
+ return m[k];
18488
+ } };
18489
+ }
18490
+ Object.defineProperty(o, k2, desc);
18491
+ } : function(o, m, k, k2) {
18492
+ if (k2 === void 0) k2 = k;
18493
+ o[k2] = m[k];
18494
+ });
18495
+ var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
18496
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18497
+ } : function(o, v) {
18498
+ o["default"] = v;
18499
+ });
18500
+ var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
18501
+ var ownKeys = function(o) {
18502
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
18503
+ var ar = [];
18504
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
18505
+ return ar;
18506
+ };
18507
+ return ownKeys(o);
18508
+ };
18509
+ return function(mod) {
18510
+ if (mod && mod.__esModule) return mod;
18511
+ var result = {};
18512
+ if (mod != null) {
18513
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
18514
+ }
18515
+ __setModuleDefault(result, mod);
18516
+ return result;
18517
+ };
18518
+ }();
18154
18519
  Object.defineProperty(exports, "__esModule", { value: true });
18155
- exports.parseModelId = parseModelId;
18156
- exports.getModelPricing = getModelPricing;
18157
- exports.getModelInfo = getModelInfo2;
18158
- exports.calculateCost = calculateCost;
18159
- exports.calculateCostWithPricing = calculateCostWithPricing;
18160
- exports.formatCost = formatCost5;
18161
- var modelContext_1 = require_modelContext();
18162
- var PRICING_TABLE = {
18163
- "haiku-4.5": {
18164
- inputCostPerMillion: 1,
18165
- outputCostPerMillion: 5,
18166
- cacheWriteCostPerMillion: 1.25,
18167
- cacheReadCostPerMillion: 0.1
18168
- },
18169
- "haiku-3.5": {
18170
- inputCostPerMillion: 0.8,
18171
- outputCostPerMillion: 4,
18172
- cacheWriteCostPerMillion: 1,
18173
- cacheReadCostPerMillion: 0.08
18174
- },
18175
- "sonnet-4.5": {
18176
- inputCostPerMillion: 3,
18177
- outputCostPerMillion: 15,
18178
- cacheWriteCostPerMillion: 3.75,
18179
- cacheReadCostPerMillion: 0.3
18180
- },
18181
- "sonnet-4": {
18182
- inputCostPerMillion: 3,
18183
- outputCostPerMillion: 15,
18184
- cacheWriteCostPerMillion: 3.75,
18185
- cacheReadCostPerMillion: 0.3
18186
- },
18187
- "sonnet-4.6": {
18188
- inputCostPerMillion: 3,
18189
- outputCostPerMillion: 15,
18190
- cacheWriteCostPerMillion: 3.75,
18191
- cacheReadCostPerMillion: 0.3
18192
- },
18193
- "opus-4.5": {
18194
- inputCostPerMillion: 5,
18195
- outputCostPerMillion: 25,
18196
- cacheWriteCostPerMillion: 6.25,
18197
- cacheReadCostPerMillion: 0.5
18198
- },
18199
- "opus-4": {
18200
- inputCostPerMillion: 15,
18201
- outputCostPerMillion: 75,
18202
- cacheWriteCostPerMillion: 18.75,
18203
- cacheReadCostPerMillion: 1.5
18204
- },
18205
- "opus-4.6": {
18206
- inputCostPerMillion: 15,
18207
- outputCostPerMillion: 75,
18208
- cacheWriteCostPerMillion: 18.75,
18209
- cacheReadCostPerMillion: 1.5
18520
+ exports.LITELLM_CATALOG_URL = void 0;
18521
+ exports.hydratePricingCatalog = hydratePricingCatalog2;
18522
+ exports.normalizeLiteLlmCatalog = normalizeLiteLlmCatalog;
18523
+ var node_fs_1 = __require("node:fs");
18524
+ var path8 = __importStar(__require("node:path"));
18525
+ var modelInfo_1 = require_modelInfo();
18526
+ exports.LITELLM_CATALOG_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
18527
+ var CACHE_FILE_NAME = "pricing-catalog.json";
18528
+ var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
18529
+ var DEFAULT_TIMEOUT_MS = 3e3;
18530
+ async function hydratePricingCatalog2(options) {
18531
+ const { cacheDir, fetchImpl = globalThis.fetch, ttlMs = DEFAULT_TTL_MS, timeoutMs = DEFAULT_TIMEOUT_MS, url = exports.LITELLM_CATALOG_URL, logger } = options;
18532
+ const cachePath = path8.join(cacheDir, CACHE_FILE_NAME);
18533
+ const now = Date.now();
18534
+ const cached = await readCache(cachePath);
18535
+ if (cached && now - Date.parse(cached.fetchedAt) < ttlMs) {
18536
+ (0, modelInfo_1._setPricingOverrides)(cached.overrides);
18537
+ return {
18538
+ source: "cache",
18539
+ entries: Object.keys(cached.overrides).length,
18540
+ fetchedAt: cached.fetchedAt
18541
+ };
18210
18542
  }
18211
- };
18212
- function parseModelId(modelId) {
18213
- const match = modelId.match(/claude-(haiku|sonnet|opus)-([0-9.]+)/i);
18214
- if (!match)
18215
- return null;
18216
- return { family: match[1].toLowerCase(), version: match[2] };
18543
+ if (fetchImpl) {
18544
+ const fetched = await fetchCatalog(url, fetchImpl, timeoutMs, logger);
18545
+ if (fetched) {
18546
+ const overrides = normalizeLiteLlmCatalog(fetched);
18547
+ const payload = {
18548
+ fetchedAt: new Date(now).toISOString(),
18549
+ url,
18550
+ overrides
18551
+ };
18552
+ await writeCache(cachePath, payload, logger);
18553
+ (0, modelInfo_1._setPricingOverrides)(overrides);
18554
+ return {
18555
+ source: "network",
18556
+ entries: Object.keys(overrides).length,
18557
+ fetchedAt: payload.fetchedAt
18558
+ };
18559
+ }
18560
+ }
18561
+ if (cached) {
18562
+ (0, modelInfo_1._setPricingOverrides)(cached.overrides);
18563
+ return {
18564
+ source: "cache",
18565
+ entries: Object.keys(cached.overrides).length,
18566
+ fetchedAt: cached.fetchedAt
18567
+ };
18568
+ }
18569
+ return { source: "offline", entries: 0, fetchedAt: new Date(now).toISOString() };
18217
18570
  }
18218
- function getModelPricing(modelId) {
18219
- const parsed = parseModelId(modelId);
18220
- if (!parsed)
18221
- return null;
18222
- const { family, version } = parsed;
18223
- const key = `${family}-${version}`;
18224
- if (PRICING_TABLE[key])
18225
- return PRICING_TABLE[key];
18226
- const familyKeys = Object.keys(PRICING_TABLE).filter((k) => k.startsWith(family)).sort().reverse();
18227
- if (familyKeys.length > 0)
18228
- return PRICING_TABLE[familyKeys[0]];
18229
- return PRICING_TABLE["sonnet-4.5"];
18571
+ function normalizeLiteLlmCatalog(raw) {
18572
+ const out = {};
18573
+ if (!raw || typeof raw !== "object")
18574
+ return out;
18575
+ for (const [key, entry] of Object.entries(raw)) {
18576
+ if (key === "sample_spec")
18577
+ continue;
18578
+ if (!entry || typeof entry !== "object")
18579
+ continue;
18580
+ const pricing = liteLlmEntryToPricing(entry);
18581
+ if (!pricing)
18582
+ continue;
18583
+ out[key] = pricing;
18584
+ const bare = stripProviderPrefix(key);
18585
+ if (bare && bare !== key && !out[bare]) {
18586
+ out[bare] = pricing;
18587
+ }
18588
+ }
18589
+ return out;
18230
18590
  }
18231
- function getModelInfo2(modelId) {
18232
- const parsed = parseModelId(modelId);
18591
+ function liteLlmEntryToPricing(entry) {
18592
+ const input = entry.input_cost_per_token;
18593
+ const output = entry.output_cost_per_token;
18594
+ if (typeof input !== "number" || typeof output !== "number")
18595
+ return null;
18596
+ const cacheRead = typeof entry.cache_read_input_token_cost === "number" ? entry.cache_read_input_token_cost : typeof entry.input_cost_per_token_cached === "number" ? entry.input_cost_per_token_cached : 0;
18597
+ const cacheWrite = typeof entry.cache_creation_input_token_cost === "number" ? entry.cache_creation_input_token_cost : 0;
18233
18598
  return {
18234
- family: parsed?.family ?? null,
18235
- version: parsed?.version ?? null,
18236
- contextWindow: (0, modelContext_1.getModelContextWindowSize)(modelId),
18237
- pricing: getModelPricing(modelId)
18599
+ inputCostPerMillion: input * 1e6,
18600
+ outputCostPerMillion: output * 1e6,
18601
+ cacheWriteCostPerMillion: cacheWrite * 1e6,
18602
+ cacheReadCostPerMillion: cacheRead * 1e6
18238
18603
  };
18239
18604
  }
18240
- function calculateCost(usage, modelId) {
18241
- const pricing = getModelPricing(modelId);
18242
- if (!pricing)
18243
- return 0;
18244
- return usage.inputTokens / 1e6 * pricing.inputCostPerMillion + usage.outputTokens / 1e6 * pricing.outputCostPerMillion + usage.cacheWriteTokens / 1e6 * pricing.cacheWriteCostPerMillion + usage.cacheReadTokens / 1e6 * pricing.cacheReadCostPerMillion;
18605
+ function stripProviderPrefix(key) {
18606
+ const slash = key.indexOf("/");
18607
+ if (slash < 0)
18608
+ return null;
18609
+ return key.slice(slash + 1);
18245
18610
  }
18246
- function calculateCostWithPricing(usage, pricing) {
18247
- return usage.inputTokens / 1e6 * pricing.inputCostPerMillion + usage.outputTokens / 1e6 * pricing.outputCostPerMillion + usage.cacheWriteTokens / 1e6 * pricing.cacheWriteCostPerMillion + usage.cacheReadTokens / 1e6 * pricing.cacheReadCostPerMillion;
18611
+ async function readCache(cachePath) {
18612
+ try {
18613
+ const raw = await node_fs_1.promises.readFile(cachePath, "utf8");
18614
+ const parsed = JSON.parse(raw);
18615
+ if (!parsed || typeof parsed !== "object" || typeof parsed.fetchedAt !== "string" || !parsed.overrides || typeof parsed.overrides !== "object") {
18616
+ return null;
18617
+ }
18618
+ return parsed;
18619
+ } catch {
18620
+ return null;
18621
+ }
18248
18622
  }
18249
- function formatCost5(cost) {
18250
- if (cost < 0.01)
18251
- return `$${cost.toFixed(4)}`;
18252
- return `$${cost.toFixed(2)}`;
18623
+ async function writeCache(cachePath, payload, logger) {
18624
+ try {
18625
+ await node_fs_1.promises.mkdir(path8.dirname(cachePath), { recursive: true });
18626
+ await node_fs_1.promises.writeFile(cachePath, JSON.stringify(payload, null, 2), "utf8");
18627
+ } catch (err) {
18628
+ logger?.(`pricingCatalog: failed to write cache: ${String(err)}`);
18629
+ }
18630
+ }
18631
+ async function fetchCatalog(url, fetchImpl, timeoutMs, logger) {
18632
+ const controller = new AbortController();
18633
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
18634
+ try {
18635
+ const res = await fetchImpl(url, { signal: controller.signal });
18636
+ if (!res.ok) {
18637
+ logger?.(`pricingCatalog: HTTP ${res.status} from ${url}`);
18638
+ return null;
18639
+ }
18640
+ return await res.json();
18641
+ } catch (err) {
18642
+ logger?.(`pricingCatalog: fetch failed: ${String(err)}`);
18643
+ return null;
18644
+ } finally {
18645
+ clearTimeout(timer);
18646
+ }
18253
18647
  }
18254
18648
  }
18255
18649
  });
@@ -18562,10 +18956,10 @@ var require_util = __commonJS({
18562
18956
  function cloneDef(schema) {
18563
18957
  return mergeDefs(schema._zod.def);
18564
18958
  }
18565
- function getElementAtPath(obj, path7) {
18566
- if (!path7)
18959
+ function getElementAtPath(obj, path8) {
18960
+ if (!path8)
18567
18961
  return obj;
18568
- return path7.reduce((acc, key) => acc?.[key], obj);
18962
+ return path8.reduce((acc, key) => acc?.[key], obj);
18569
18963
  }
18570
18964
  function promiseAllObject(promisesObj) {
18571
18965
  const keys = Object.keys(promisesObj);
@@ -18949,11 +19343,11 @@ var require_util = __commonJS({
18949
19343
  }
18950
19344
  return false;
18951
19345
  }
18952
- function prefixIssues(path7, issues) {
19346
+ function prefixIssues(path8, issues) {
18953
19347
  return issues.map((iss) => {
18954
19348
  var _a;
18955
19349
  (_a = iss).path ?? (_a.path = []);
18956
- iss.path.unshift(path7);
19350
+ iss.path.unshift(path8);
18957
19351
  return iss;
18958
19352
  });
18959
19353
  }
@@ -19178,7 +19572,7 @@ var require_errors = __commonJS({
19178
19572
  }
19179
19573
  function treeifyError(error, mapper = (issue) => issue.message) {
19180
19574
  const result = { errors: [] };
19181
- const processError = (error2, path7 = []) => {
19575
+ const processError = (error2, path8 = []) => {
19182
19576
  var _a, _b;
19183
19577
  for (const issue of error2.issues) {
19184
19578
  if (issue.code === "invalid_union" && issue.errors.length) {
@@ -19188,7 +19582,7 @@ var require_errors = __commonJS({
19188
19582
  } else if (issue.code === "invalid_element") {
19189
19583
  processError({ issues: issue.issues }, issue.path);
19190
19584
  } else {
19191
- const fullpath = [...path7, ...issue.path];
19585
+ const fullpath = [...path8, ...issue.path];
19192
19586
  if (fullpath.length === 0) {
19193
19587
  result.errors.push(mapper(issue));
19194
19588
  continue;
@@ -19220,8 +19614,8 @@ var require_errors = __commonJS({
19220
19614
  }
19221
19615
  function toDotPath(_path) {
19222
19616
  const segs = [];
19223
- const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
19224
- for (const seg of path7) {
19617
+ const path8 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
19618
+ for (const seg of path8) {
19225
19619
  if (typeof seg === "number")
19226
19620
  segs.push(`[${seg}]`);
19227
19621
  else if (typeof seg === "symbol")
@@ -33722,13 +34116,13 @@ var require_from_json_schema = __commonJS({
33722
34116
  if (!ref.startsWith("#")) {
33723
34117
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
33724
34118
  }
33725
- const path7 = ref.slice(1).split("/").filter(Boolean);
33726
- if (path7.length === 0) {
34119
+ const path8 = ref.slice(1).split("/").filter(Boolean);
34120
+ if (path8.length === 0) {
33727
34121
  return ctx.rootSchema;
33728
34122
  }
33729
34123
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
33730
- if (path7[0] === defsKey) {
33731
- const key = path7[1];
34124
+ if (path8[0] === defsKey) {
34125
+ const key = path8[1];
33732
34126
  if (!key || !ctx.defs[key]) {
33733
34127
  throw new Error(`Reference not found: ${ref}`);
33734
34128
  }
@@ -34436,8 +34830,8 @@ var require_dist = __commonJS({
34436
34830
  Object.defineProperty(exports, "__esModule", { value: true });
34437
34831
  exports.findActiveClaudeSession = exports.discoverSessionDirectory = exports.getClaudeSessionDirectory = exports.encodeClaudeWorkspacePath = exports.detectSessionActivity = exports.extractTaskInfo = exports.scanSubagentDir = exports.normalizeCodexToolInput = exports.normalizeCodexToolName = exports.extractPatchFilePaths = exports.CodexRolloutParser = exports.parseDbPartData = exports.parseDbMessageData = exports.convertOpenCodeMessage = exports.detectPlanModeFromText = exports.normalizeToolInput = exports.normalizeToolName = exports.TRUNCATION_PATTERNS = exports.JsonlParser = exports.CodexProvider = exports.OpenCodeProvider = exports.ClaudeCodeProvider = exports.getAllDetectedProviders = exports.detectProvider = exports.readClaudeCodePlanFiles = exports.getPlanAnalytics = exports.writePlans = exports.getLatestPlan = exports.readPlans = exports.readLatestHandoff = exports.readHistory = exports.readNotes = exports.readDecisions = exports.readTasks = exports.getProjectSlugRaw = exports.getProjectSlug = exports.encodeWorkspacePath = exports.getGlobalDataPath = exports.getProjectDataPath = exports.getConfigDir = exports.MAX_PLANS_PER_PROJECT = exports.PLAN_SCHEMA_VERSION = exports.createEmptyTokenTotals = exports.HISTORICAL_DATA_SCHEMA_VERSION = exports.STALENESS_THRESHOLDS = exports.IMPORTANCE_DECAY_FACTORS = exports.KNOWLEDGE_NOTE_SCHEMA_VERSION = exports.DECISION_LOG_SCHEMA_VERSION = exports.normalizeTaskStatus = exports.TASK_PERSISTENCE_SCHEMA_VERSION = void 0;
34438
34832
  exports.HeatmapTracker = exports.FrequencyTracker = exports.getSnapshotPath = exports.isSnapshotValid = exports.deleteSnapshot = exports.loadSnapshot = exports.saveSnapshot = exports.parseTodoDependencies = exports.EventAggregator = exports.getRandomPhrase = exports.ALL_PHRASES = exports.HIGHLIGHT_CSS = exports.clearHighlightCache = exports.highlightEvent = exports.formatSessionJson = exports.formatSessionMarkdown = exports.formatSessionText = exports.classifyNoise = exports.shouldMergeWithPrevious = exports.classifyFollowEvent = exports.classifyMessage = exports.getSoftNoiseReason = exports.isHardNoiseFollowEvent = exports.isHardNoise = exports.formatToolSummary = exports.toFollowEvents = exports.createWatcher = exports.parseChangelog = exports.extractProposedPlanShared = exports.parsePlanMarkdownShared = exports.PlanExtractor = exports.composeContext = exports.FilterEngine = exports.searchSessions = exports.CodexDatabase = exports.OpenCodeDatabase = exports.discoverDebugLogs = exports.collapseDuplicates = exports.filterByLevel = exports.parseDebugLog = exports.scanSubagentTraces = exports.findAllSessionsWithWorktrees = exports.discoverWorktreeSiblings = exports.resolveWorktreeMainRepo = exports.getAllClaudeProjectFolders = exports.decodeEncodedPath = exports.getMostRecentlyActiveSessionDir = exports.findSubdirectorySessionDirs = exports.findSessionsInDirectory = exports.findAllClaudeSessions = void 0;
34439
- exports.formatCost = exports.calculateCostWithPricing = exports.calculateCost = exports.getModelInfo = exports.getModelPricing = exports.parseModelId = exports.DEFAULT_CONTEXT_WINDOW = exports.getModelContextWindowSize = exports.quotaFromCodexRateLimits = exports.writeQuotaSnapshot = exports.readQuotaSnapshot = exports.QuotaPoller = exports.describeQuotaFailure = exports.fetchQuota = exports.removeCodexAccount = exports.switchToCodexAccount = exports.finalizeCodexAccount = exports.prepareCodexAccount = exports.getCodexExecutionEnv = exports.resolveSidekickCodexHome = exports.getActiveCodexAccount = exports.listCodexAccounts = exports.getCodexProfileHome = exports.getCodexProfilesDir = exports.removeSavedAccountProfile = exports.replaceSavedAccountProfiles = exports.setActiveSavedAccount = exports.upsertSavedAccountProfile = exports.getActiveSavedAccount = exports.listSavedAccountProfiles = exports.writeSavedAccountRegistry = exports.readSavedAccountRegistry = exports.getAccountsDir = exports.isMultiAccountEnabled = exports.getActiveAccount = exports.listAccounts = exports.removeAccount = exports.switchToAccount = exports.addCurrentAccount = exports.readActiveClaudeAccount = exports.writeAccountRegistry = exports.readAccountRegistry = exports.readClaudeMaxAccessTokenSync = exports.readClaudeMaxCredentials = exports.writeActiveCredentials = exports.readActiveCredentials = exports.openInBrowser = exports.parseTranscript = exports.generateHtmlReport = exports.PatternExtractor = void 0;
34440
- exports.fetchOpenAIStatus = exports.fetchProviderStatus = exports.permissionModeSchema = exports.sessionEventSchema = exports.sessionMessageSchema = exports.messageUsageSchema = exports.extractToolCalls = exports.extractTokenUsage = void 0;
34833
+ exports.calculateCost = exports.getModelInfo = exports.getModelPricing = exports.parseModelId = exports.DEFAULT_CONTEXT_WINDOW = exports.getModelContextWindowSize = exports.quotaFromCodexRateLimits = exports.writeQuotaSnapshot = exports.readQuotaSnapshot = exports.QuotaPoller = exports.describeQuotaFailure = exports.fetchQuota = exports.removeCodexAccount = exports.switchToCodexAccount = exports.finalizeCodexAccount = exports.prepareCodexAccount = exports.getCodexExecutionEnv = exports.resolveSidekickCodexHome = exports.getActiveCodexAccount = exports.listCodexAccounts = exports.getSystemCodexHome = exports.getCodexMonitoringHomes = exports.getCodexProfileHome = exports.getCodexProfilesDir = exports.removeSavedAccountProfile = exports.replaceSavedAccountProfiles = exports.setActiveSavedAccount = exports.upsertSavedAccountProfile = exports.getActiveSavedAccount = exports.listSavedAccountProfiles = exports.writeSavedAccountRegistry = exports.readSavedAccountRegistry = exports.getAccountsDir = exports.isMultiAccountEnabled = exports.getActiveAccount = exports.listAccounts = exports.removeAccount = exports.switchToAccount = exports.addCurrentAccount = exports.readActiveClaudeAccount = exports.writeAccountRegistry = exports.readAccountRegistry = exports.readClaudeMaxAccessTokenSync = exports.readClaudeMaxCredentials = exports.writeActiveCredentials = exports.readActiveCredentials = exports.openInBrowser = exports.parseTranscript = exports.generateHtmlReport = exports.PatternExtractor = void 0;
34834
+ exports.fetchOpenAIStatus = exports.fetchProviderStatus = exports.permissionModeSchema = exports.sessionEventSchema = exports.sessionMessageSchema = exports.messageUsageSchema = exports.extractToolCalls = exports.extractTokenUsage = exports.LITELLM_CATALOG_URL = exports.normalizeLiteLlmCatalog = exports.hydratePricingCatalog = exports.formatCost = exports.calculateCostWithPricing = void 0;
34441
34835
  var taskPersistence_1 = require_taskPersistence();
34442
34836
  Object.defineProperty(exports, "TASK_PERSISTENCE_SCHEMA_VERSION", { enumerable: true, get: function() {
34443
34837
  return taskPersistence_1.TASK_PERSISTENCE_SCHEMA_VERSION;
@@ -34871,6 +35265,12 @@ var require_dist = __commonJS({
34871
35265
  Object.defineProperty(exports, "getCodexProfileHome", { enumerable: true, get: function() {
34872
35266
  return codexProfiles_1.getCodexProfileHome;
34873
35267
  } });
35268
+ Object.defineProperty(exports, "getCodexMonitoringHomes", { enumerable: true, get: function() {
35269
+ return codexProfiles_1.getCodexMonitoringHomes;
35270
+ } });
35271
+ Object.defineProperty(exports, "getSystemCodexHome", { enumerable: true, get: function() {
35272
+ return codexProfiles_1.getSystemCodexHome;
35273
+ } });
34874
35274
  Object.defineProperty(exports, "listCodexAccounts", { enumerable: true, get: function() {
34875
35275
  return codexProfiles_1.listCodexAccounts;
34876
35276
  } });
@@ -34944,6 +35344,16 @@ var require_dist = __commonJS({
34944
35344
  Object.defineProperty(exports, "formatCost", { enumerable: true, get: function() {
34945
35345
  return modelInfo_1.formatCost;
34946
35346
  } });
35347
+ var pricingCatalog_1 = require_pricingCatalog();
35348
+ Object.defineProperty(exports, "hydratePricingCatalog", { enumerable: true, get: function() {
35349
+ return pricingCatalog_1.hydratePricingCatalog;
35350
+ } });
35351
+ Object.defineProperty(exports, "normalizeLiteLlmCatalog", { enumerable: true, get: function() {
35352
+ return pricingCatalog_1.normalizeLiteLlmCatalog;
35353
+ } });
35354
+ Object.defineProperty(exports, "LITELLM_CATALOG_URL", { enumerable: true, get: function() {
35355
+ return pricingCatalog_1.LITELLM_CATALOG_URL;
35356
+ } });
34947
35357
  var tokenUsage_1 = require_tokenUsage();
34948
35358
  Object.defineProperty(exports, "extractTokenUsage", { enumerable: true, get: function() {
34949
35359
  return tokenUsage_1.extractTokenUsage;
@@ -36682,7 +37092,8 @@ var init_DashboardState = __esm({
36682
37092
  model: ms.model,
36683
37093
  calls: ms.calls,
36684
37094
  tokens: ms.tokens,
36685
- cost: ms.cost
37095
+ cost: ms.cost,
37096
+ priced: ms.priced
36686
37097
  }));
36687
37098
  const taskMap = /* @__PURE__ */ new Map();
36688
37099
  for (const t of m.taskState.tasks.values()) {
@@ -37187,7 +37598,7 @@ var init_UpdateCheckService = __esm({
37187
37598
  /** Run the update check (one-shot). */
37188
37599
  async check() {
37189
37600
  try {
37190
- const current = "0.17.0";
37601
+ const current = "0.17.2";
37191
37602
  const cached = this.readCache();
37192
37603
  let latest;
37193
37604
  if (cached && Date.now() - cached.checkedAt < CACHE_TTL_MS) {
@@ -38757,7 +39168,8 @@ ${hint}{/grey-fg}`;
38757
39168
  lines.push("", sectionHeader("Model Usage", w2));
38758
39169
  for (const ms of m.modelStats) {
38759
39170
  const modelName = ms.model.length > 20 ? ms.model.substring(0, 17) + "..." : ms.model;
38760
- lines.push(` ${modelName.padEnd(20)} {bold}${String(ms.calls).padStart(4)}{/bold}{grey-fg} calls{/grey-fg} {green-fg}${(0, import_sidekick_shared11.formatCost)(ms.cost)}{/green-fg}`);
39171
+ const costDisplay = ms.priced === false ? "{yellow-fg}\u2014{/yellow-fg}" : `{green-fg}${(0, import_sidekick_shared11.formatCost)(ms.cost)}{/green-fg}`;
39172
+ lines.push(` ${modelName.padEnd(20)} {bold}${String(ms.calls).padStart(4)}{/bold}{grey-fg} calls{/grey-fg} ${costDisplay}`);
38761
39173
  }
38762
39174
  }
38763
39175
  const quotaLabel = m.providerId === "codex" ? "Rate Limits" : "Quota";
@@ -51571,10 +51983,10 @@ var require_react_reconciler_development = __commonJS({
51571
51983
  fiber = fiber.next, id--;
51572
51984
  return fiber;
51573
51985
  }
51574
- function copyWithSetImpl(obj, path7, index, value) {
51575
- if (index >= path7.length) return value;
51576
- var key = path7[index], updated = isArrayImpl(obj) ? obj.slice() : assign({}, obj);
51577
- updated[key] = copyWithSetImpl(obj[key], path7, index + 1, value);
51986
+ function copyWithSetImpl(obj, path8, index, value) {
51987
+ if (index >= path8.length) return value;
51988
+ var key = path8[index], updated = isArrayImpl(obj) ? obj.slice() : assign({}, obj);
51989
+ updated[key] = copyWithSetImpl(obj[key], path8, index + 1, value);
51578
51990
  return updated;
51579
51991
  }
51580
51992
  function copyWithRename(obj, oldPath, newPath) {
@@ -51601,11 +52013,11 @@ var require_react_reconciler_development = __commonJS({
51601
52013
  );
51602
52014
  return updated;
51603
52015
  }
51604
- function copyWithDeleteImpl(obj, path7, index) {
51605
- var key = path7[index], updated = isArrayImpl(obj) ? obj.slice() : assign({}, obj);
51606
- if (index + 1 === path7.length)
52016
+ function copyWithDeleteImpl(obj, path8, index) {
52017
+ var key = path8[index], updated = isArrayImpl(obj) ? obj.slice() : assign({}, obj);
52018
+ if (index + 1 === path8.length)
51607
52019
  return isArrayImpl(updated) ? updated.splice(key, 1) : delete updated[key], updated;
51608
- updated[key] = copyWithDeleteImpl(obj[key], path7, index + 1);
52020
+ updated[key] = copyWithDeleteImpl(obj[key], path8, index + 1);
51609
52021
  return updated;
51610
52022
  }
51611
52023
  function shouldSuspendImpl() {
@@ -64882,29 +65294,29 @@ var require_react_reconciler_development = __commonJS({
64882
65294
  var didWarnAboutNestedUpdates = false;
64883
65295
  var didWarnAboutFindNodeInStrictMode = {};
64884
65296
  var overrideHookState = null, overrideHookStateDeletePath = null, overrideHookStateRenamePath = null, overrideProps = null, overridePropsDeletePath = null, overridePropsRenamePath = null, scheduleUpdate = null, scheduleRetry = null, setErrorHandler = null, setSuspenseHandler = null;
64885
- overrideHookState = function(fiber, id, path7, value) {
65297
+ overrideHookState = function(fiber, id, path8, value) {
64886
65298
  id = findHook(fiber, id);
64887
- null !== id && (path7 = copyWithSetImpl(id.memoizedState, path7, 0, value), id.memoizedState = path7, id.baseState = path7, fiber.memoizedProps = assign({}, fiber.memoizedProps), path7 = enqueueConcurrentRenderForLane(fiber, 2), null !== path7 && scheduleUpdateOnFiber(path7, fiber, 2));
65299
+ null !== id && (path8 = copyWithSetImpl(id.memoizedState, path8, 0, value), id.memoizedState = path8, id.baseState = path8, fiber.memoizedProps = assign({}, fiber.memoizedProps), path8 = enqueueConcurrentRenderForLane(fiber, 2), null !== path8 && scheduleUpdateOnFiber(path8, fiber, 2));
64888
65300
  };
64889
- overrideHookStateDeletePath = function(fiber, id, path7) {
65301
+ overrideHookStateDeletePath = function(fiber, id, path8) {
64890
65302
  id = findHook(fiber, id);
64891
- null !== id && (path7 = copyWithDeleteImpl(id.memoizedState, path7, 0), id.memoizedState = path7, id.baseState = path7, fiber.memoizedProps = assign({}, fiber.memoizedProps), path7 = enqueueConcurrentRenderForLane(fiber, 2), null !== path7 && scheduleUpdateOnFiber(path7, fiber, 2));
65303
+ null !== id && (path8 = copyWithDeleteImpl(id.memoizedState, path8, 0), id.memoizedState = path8, id.baseState = path8, fiber.memoizedProps = assign({}, fiber.memoizedProps), path8 = enqueueConcurrentRenderForLane(fiber, 2), null !== path8 && scheduleUpdateOnFiber(path8, fiber, 2));
64892
65304
  };
64893
65305
  overrideHookStateRenamePath = function(fiber, id, oldPath, newPath) {
64894
65306
  id = findHook(fiber, id);
64895
65307
  null !== id && (oldPath = copyWithRename(id.memoizedState, oldPath, newPath), id.memoizedState = oldPath, id.baseState = oldPath, fiber.memoizedProps = assign({}, fiber.memoizedProps), oldPath = enqueueConcurrentRenderForLane(fiber, 2), null !== oldPath && scheduleUpdateOnFiber(oldPath, fiber, 2));
64896
65308
  };
64897
- overrideProps = function(fiber, path7, value) {
64898
- fiber.pendingProps = copyWithSetImpl(fiber.memoizedProps, path7, 0, value);
65309
+ overrideProps = function(fiber, path8, value) {
65310
+ fiber.pendingProps = copyWithSetImpl(fiber.memoizedProps, path8, 0, value);
64899
65311
  fiber.alternate && (fiber.alternate.pendingProps = fiber.pendingProps);
64900
- path7 = enqueueConcurrentRenderForLane(fiber, 2);
64901
- null !== path7 && scheduleUpdateOnFiber(path7, fiber, 2);
65312
+ path8 = enqueueConcurrentRenderForLane(fiber, 2);
65313
+ null !== path8 && scheduleUpdateOnFiber(path8, fiber, 2);
64902
65314
  };
64903
- overridePropsDeletePath = function(fiber, path7) {
64904
- fiber.pendingProps = copyWithDeleteImpl(fiber.memoizedProps, path7, 0);
65315
+ overridePropsDeletePath = function(fiber, path8) {
65316
+ fiber.pendingProps = copyWithDeleteImpl(fiber.memoizedProps, path8, 0);
64905
65317
  fiber.alternate && (fiber.alternate.pendingProps = fiber.pendingProps);
64906
- path7 = enqueueConcurrentRenderForLane(fiber, 2);
64907
- null !== path7 && scheduleUpdateOnFiber(path7, fiber, 2);
65318
+ path8 = enqueueConcurrentRenderForLane(fiber, 2);
65319
+ null !== path8 && scheduleUpdateOnFiber(path8, fiber, 2);
64908
65320
  };
64909
65321
  overridePropsRenamePath = function(fiber, oldPath, newPath) {
64910
65322
  fiber.pendingProps = copyWithRename(
@@ -74821,8 +75233,8 @@ var init_ErrorOverview = __esm({
74821
75233
  init_dist3();
74822
75234
  init_Box();
74823
75235
  init_Text();
74824
- cleanupPath = (path7) => {
74825
- return path7?.replace(`file://${cwd()}/`, "");
75236
+ cleanupPath = (path8) => {
75237
+ return path8?.replace(`file://${cwd()}/`, "");
74826
75238
  };
74827
75239
  stackUtils = new import_stack_utils.default({
74828
75240
  cwd: cwd(),
@@ -77804,7 +78216,7 @@ function StatusBar({
77804
78216
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: parseBlessedTags(BRAND_INLINE) }),
77805
78217
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { dimColor: true, children: [
77806
78218
  " v",
77807
- "0.17.0"
78219
+ "0.17.2"
77808
78220
  ] }),
77809
78221
  updateInfo && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { color: "yellow", children: [
77810
78222
  " (v",
@@ -78194,7 +78606,7 @@ function ChangelogOverlay({ entries, scrollOffset }) {
78194
78606
  " ",
78195
78607
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { bold: true, color: "cyan", children: [
78196
78608
  "Terminal Dashboard v",
78197
- "0.17.0"
78609
+ "0.17.2"
78198
78610
  ] }),
78199
78611
  latestDate ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { color: "gray", children: [
78200
78612
  " \u2014 ",
@@ -78516,7 +78928,7 @@ var init_mouse = __esm({
78516
78928
  var CHANGELOG_default;
78517
78929
  var init_CHANGELOG = __esm({
78518
78930
  "CHANGELOG.md"() {
78519
- CHANGELOG_default = '# Changelog\n\nAll notable changes to the Sidekick Agent Hub CLI will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [0.17.0] - 2026-04-13\n\n### Added\n\n- **Multi-provider account management**: `sidekick account` now supports `--provider codex` for Codex profile management alongside Claude Code accounts\n- **Codex account lifecycle**: `--add` prepares a profile and spawns `codex login`; `--switch-to` and `--remove` accept email, label, or profile ID\n- **Quota snapshot fallback**: `sidekick quota` for Codex shows cached rate-limit snapshots when no active session exists, with "cached from" timestamp\n\n### Fixed\n\n- **Email normalization**: Claude account lookup normalizes email case for reliable matching\n\n## [0.16.1] - 2026-03-27\n\n### Fixed\n\n- **Dashboard provider status scoping**: The TUI now shows degraded-service notices only for the monitored provider \u2014 Claude for Claude Code sessions, OpenAI for Codex sessions, and no status banner for OpenCode\n\n## [0.16.0] - 2026-03-23\n\n### Changed\n\n- **Consistent cost formatting**: All cost displays (`stats`, `context`, Sessions panel, narrative prompt) now use shared `formatCost()` with intelligent decimal precision (4 places for < $0.01, 2 otherwise)\n- **QuotaService**: Rewritten to wrap shared `QuotaPoller` with exponential backoff instead of manual polling loop\n- **modelContext**: Now re-exports `getModelInfo` from shared library alongside `getContextWindowSize`\n\n## [0.15.2] - 2026-03-18\n\n### Fixed\n\n- **CLI help descriptions**: Updated `quota` and `status` command descriptions to reflect provider-aware behavior\n- **`sidekick quota --provider`**: Added local `--provider` option so `sidekick quota --provider codex` works naturally\n\n## [0.15.0] - 2026-03-18\n\n### Added\n\n- **OpenAI status page monitoring**: CLI dashboard now shows OpenAI API status alongside Claude API status\n- **Codex rate limits in dashboard**: Sessions panel displays Codex rate-limit data with "Rate Limits" header instead of "Quota"\n- **Provider-aware `sidekick quota` command**: Detects active provider and shows Codex rate limits, Claude subscription quota, or an informational message for OpenCode\n\n### Fixed\n\n- **QuotaService polling for Codex**: Dashboard no longer starts Claude OAuth quota polling when the active provider is Codex\n\n## [0.14.2] - 2026-03-16\n\n### Fixed\n\n- **Quota polling interval**: Reduced quota refresh from every 30 seconds to every 5 minutes to avoid unnecessary API calls\n- **SessionsPanel `detailWidth()` call**: Removed unused parameter from `detailWidth()` in the Sessions panel quota rendering\n\n## [0.14.1] - 2026-03-14\n\n### Fixed\n\n- **Per-model context window sizes**: Dashboard context gauge now shows correct utilization for Claude Opus 4.6 (1M context) and other models with non-200K windows\n\n### Changed\n\n- **Shared model context lookup**: CLI dashboard now uses the centralized `getModelContextWindowSize()` from `sidekick-shared` instead of a local duplicate map\n\n## [0.14.0] - 2026-03-12\n\n### Added\n\n- **`sidekick account` Command**: Manage Claude Code accounts from the terminal \u2014 list saved accounts, add the current account with an optional label, switch to the next or a specific account, and remove accounts. Supports `--json` output for scripting\n- **Quota Account Label**: `sidekick quota` now shows the active account email and label above the quota bars when multi-account is enabled\n- **macOS Keychain Support**: `sidekick account` and `sidekick quota` now read and write credentials via the system Keychain on macOS, fixing account switching and quota checks on Mac\n\n## [0.13.8] - 2026-03-12\n\n### Changed\n\n- **Structured quota failure output**: `sidekick quota` now renders consistent auth, rate-limit, server, network, and unexpected-failure copy from shared quota failure descriptors while preserving `--json` machine-readable output\n- **Dashboard unavailable quota rendering**: The Sessions panel now shows Claude Code quota failures inline instead of hiding the quota section whenever subscription data is unavailable\n- **Quota transition toasts**: The Ink dashboard now fires low-noise toast notifications only when Claude Code quota failure state changes, avoiding repeated alerts every polling interval\n\n## [0.13.7] - 2026-03-11\n\n### Changed\n\n- **npm README sync**: Updated the published CLI package README to reflect current OpenCode monitoring behavior, platform-specific data directories, and the `sqlite3` runtime requirement\n- **README badge cleanup**: Removed the Ask DeepWiki badge from the published CLI package README; the repo root README still keeps it\n\n## [0.13.6] - 2026-03-11\n\n### Changed\n\n- **Refreshed CLI Dashboard Wordmark**: Updated the dashboard wordmark/header styling for a cleaner splash and dashboard identity\n\n### Fixed\n\n- **OpenCode dashboard startup**: OpenCode DB-backed session discovery now resolves projects by worktree, sandboxes, and session directory instead of quietly behaving like no session exists\n- **OpenCode runtime notices**: The CLI now prints an OpenCode-only actionable notice when `opencode.db` exists but `sqlite3` is missing, blocked, or otherwise unusable in the current shell environment\n\n## [0.13.5] - 2026-03-10\n\n### Added\n\n- **`sidekick status` Command**: One-shot Claude API status check with color-coded text output and `--json` mode\n- **Dashboard Status Banner**: Status bar shows a colored `\u25CF API minor/major/critical` indicator when Claude is degraded; Sessions panel Summary tab shows an "API Status" section with affected components and active incident details. Polls every 60s\n\n## [0.13.4] - 2026-03-08\n\n### Fixed\n\n- **Onboarding Phrase Spam**: Splash screen and detail pane motivational phrases memoized \u2014 no longer flicker every render tick (fixes [#13](https://github.com/cesarandreslopez/sidekick-agent-hub/issues/13))\n\n### Changed\n\n- **Simplified Logo**: Replaced 6-line ASCII robot art with compact text header in splash, help, and changelog overlays\n- **Removed Dead Code**: Removed unused `getSplashContent()` and `HELP_HEADER` exports from branding module\n\n## [0.13.3] - 2026-03-04\n\n_No CLI-specific changes in this release._\n\n## [0.13.2] - 2026-03-04\n\n_No CLI-specific changes in this release._\n\n## [0.13.1] - 2026-03-04\n\n### Added\n\n- **`sidekick quota` Command**: One-shot subscription quota check showing 5-hour and 7-day utilization with color-coded progress bars and reset countdowns \u2014 supports `--json` for machine-readable output\n- **Quota Projections**: Elapsed-time projections shown in `sidekick quota` output and TUI dashboard quota section \u2014 displays projected end-of-window utilization next to current value (e.g., `40% \u2192 100%`), included in `--json` output as `projectedFiveHour` / `projectedSevenDay`\n\n## [0.13.0] - 2026-03-03\n\n_No CLI-specific changes in this release._\n\n## [0.12.10] - 2026-03-01\n\n### Added\n\n- **Events Panel** (key 7): Scrollable live event stream with colored type badges (`[USR]`, `[AST]`, `[TOOL]`, `[RES]`), timestamps, and keyword-highlighted summaries; detail tabs for full event JSON and surrounding context\n- **Charts Panel** (key 8): Tool frequency horizontal bars, event type distribution, 60-minute activity heatmap using `\u2591\u2592\u2593\u2588` intensity characters, and pattern analysis with frequency bars and template text\n- **Multi-Mode Filter**: `/` filter overlay now supports four modes \u2014 substring, fuzzy, regex, and date range \u2014 Tab cycles modes, regex mode shows red validation errors\n- **Search Term Highlighting**: Active filter terms highlighted in blue within side list items\n- **Timeline Keyword Coloring**: Event summaries in the Sessions panel Timeline tab now use semantic keyword coloring \u2014 errors red, success green, tool names cyan, file paths magenta\n\n### Removed\n\n- **Search Panel**: Removed redundant Search panel (previously key 7) \u2014 the `/` filter with multi-mode support serves the same purpose\n\n## [0.12.9] - 2026-02-28\n\n### Added\n\n- **Standalone Data Commands**: `sidekick tasks`, `sidekick decisions`, `sidekick notes`, `sidekick stats`, `sidekick handoff` for accessing project data without launching the TUI\n- **`sidekick search <query>`**: Cross-session full-text search from the terminal\n- **`sidekick context`**: Composite output of tasks, decisions, notes, and handoff for piping into other tools\n- **`--list` flag on `sidekick dump`**: Discover available session IDs before requiring `--session <id>`\n- **Search Panel**: Search panel (panel 7) wired into the TUI dashboard\n\n### Changed\n\n- **`taskMerger` utility**: Duplicate `mergeTasks` logic extracted into shared `taskMerger` utility\n- **Model constants**: Hardcoded model IDs extracted to named constants\n\n### Fixed\n\n- **`convention` icon**: Notes panel icon replaced with valid `tip` type\n- **Linux clipboard**: Now supports Wayland (`wl-copy`) and `xsel` fallbacks, with error messages instead of silent failure\n- **`provider.dispose()`**: Added to `dump` and `report` commands (prevents SQLite connection leaks)\n\n## [0.12.8] - 2026-02-28\n\n### Changed\n\n- **Dashboard UI/UX Polish**: Visual overhaul for better hierarchy, consistency, and readability\n - Splash screen and help overlay now display the robot ASCII logo\n - Toast notifications show severity icons (\u2718 error, \u26A0 warning, \u25CF info) with inner padding\n - Focused pane uses double-border for clear focus indication\n - Section dividers (`\u2500\u2500 Title \u2500\u2500\u2500\u2500`) replace bare bold headers in summary, agents, and context attribution\n - Tab bar: active tab underlined in magenta, inactive tabs dimmed, bracket syntax removed\n - Status bar: segmented layout with `\u2502` separators; keys bold, labels dim\n - Summary metrics condensed: elapsed/events/compactions on one line, tokens on one line with cache rate and cost\n - Sparklines display peak metadata annotations\n - Progress bars use blessed color tags for consistent coloring\n - Help overlay uses dot-leader alignment for all keybinding rows\n - Empty state hints per panel (e.g. "Tasks appear as your agent works.")\n - Session picker groups sessions by provider with section headers when multiple providers are present\n\n## [0.12.7] - 2026-02-27\n\n### Added\n\n- **HTML Session Report**: `sidekick report` command generates a self-contained HTML report and opens it in the default browser\n - Options: `--session`, `--output`, `--theme` (dark/light), `--no-open`, `--no-thinking`\n - TUI Dashboard: press `r` to generate and open an HTML report for the current session\n\n## [0.12.6] - 2026-02-26\n\n### Added\n\n- **Session Dump Command**: `sidekick dump` exports session data in text, markdown, or JSON format with `--format`, `--width`, and `--expand` options\n- **Plans Panel Re-enabled**: Plans panel restored in CLI dashboard with plan file discovery from `~/.claude/plans/`\n- **Enhanced Status Bar**: Session info display improved with richer metadata\n\n### Fixed\n\n- **Old snapshot format migration**: Restoring pre-0.12.3 session snapshots no longer shows empty timeline entries\n\n### Changed\n\n- **Phrase library moved to shared**: CLI-specific phrase formatting kept local, all phrase content now from `sidekick-shared`\n\n## [0.12.5] - 2026-02-24\n\n### Fixed\n\n- **Update check too slow to notice new versions**: Reduced npm registry cache TTL from 24 hours to 4 hours so upgrade notices appear sooner after a new release\n\n## [0.12.4] - 2026-02-24\n\n### Fixed\n\n- **Session crash on upgrade**: Fixed `d.timestamp.getTime is not a function` error when restoring tool call data from session snapshots \u2014 `Date` objects were serialized to strings by JSON but not rehydrated on restore, causing the session monitor to crash on first run after upgrading from 0.12.2 to 0.12.3\n\n## [0.12.3] - 2026-02-24\n\n### Added\n\n- **Latest-node indicator**: The most recently added node in tree and boxed mind map views is now marked with a yellow indicator\n- **Plan analytics in mind map**: Tree and boxed views now display plan progress and per-step metrics\n - Tree view: plan header shows completion stats; steps show complexity, duration, tokens, tool calls, and errors in metadata brackets\n - Box view: progress bar with completion percentage; steps show right-aligned metrics; subtitle shows step count and total duration\n- **Cross-provider plan extraction**: Shared `PlanExtractor` now handles Claude Code (EnterPlanMode/ExitPlanMode) and OpenCode (`<proposed_plan>` XML) plans \u2014 previously only Codex plans were shown\n- **Enriched plan data model**: Plan steps include duration, token count, tool call count, and error messages\n- **Phase-grouped plan display**: When a plan has phase structure, tree and boxed views group steps under phase headers with context lines from the original plan markdown\n- **Node type filter**: Press `f` on the Mind Map tab to cycle through node type filters (file, tool, task, subagent, command, plan, knowledge-note) \u2014 non-matching sections render dimmed in grey\n\n### Fixed\n\n- **Kanban board regression**: Subagent and plan-step tasks now correctly appear in the kanban board\n\n### Changed\n\n- **Plans panel temporarily disabled**: The Plans panel in the CLI dashboard is disabled until plan-mode event capture is reliably working end-to-end. Plan nodes in the mind map remain active.\n- `DashboardState` now delegates to shared `EventAggregator` instead of maintaining its own aggregation logic\n\n## [0.12.2] - 2026-02-23\n\n### Added\n\n- **Update notifications**: The dashboard now checks the npm registry for newer versions on startup and shows a yellow banner in the status bar when an update is available (e.g., `v0.13.0 available \u2014 npm i -g sidekick-agent-hub`). Results are cached for 24 hours to avoid repeated network requests.\n\n## [0.12.1] - 2026-02-23\n\n### Fixed\n\n- **VS Code integration**: Fixed exit code 127 when the extension launches the CLI dashboard on systems using nvm or volta (node binary not found when shell init is bypassed)\n\n## [0.12.0] - 2026-02-22\n\n### Added\n\n- **"Open CLI Dashboard" VS Code Integration**: New VS Code command `Sidekick: Open CLI Dashboard` launches the TUI dashboard in an integrated terminal\n - Install the CLI with `npm install -g sidekick-agent-hub`\n\n## [0.11.0] - 2026-02-19\n\n### Added\n\n- **Initial Release**: Full-screen TUI dashboard for monitoring agent sessions from the terminal\n - Ink-based terminal UI with panels for sessions, tasks, kanban, mind map, notes, decisions, search, files, and git diff\n - Multi-provider support: auto-detects Claude Code, OpenCode, and Codex sessions\n - Reads from `~/.config/sidekick/` \u2014 the same data files the VS Code extension writes\n - Usage: `sidekick dashboard [--project <path>] [--provider <id>]`\n';
78931
+ CHANGELOG_default = '# Changelog\n\nAll notable changes to the Sidekick Agent Hub CLI will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n## [0.17.2] - 2026-04-17\n\n### Added\n\n- **LiteLLM pricing hydration on startup**: The CLI now fetches the LiteLLM pricing catalog on startup and caches to `~/.config/sidekick/pricing-catalog.json` with a 24-hour TTL, 3s timeout, and stale-cache fallback \u2014 new model prices are picked up without a CLI upgrade\n- **Expanded pricing coverage**: GPT-4o, GPT-4.1, GPT-5.x, o1, o3, and o3-mini families are now priced alongside the existing Claude entries\n- **Real-dollar Codex / Claude Code costs**: `EventAggregator` computes cost from the pricing table when the session provider doesn\'t report one, so `sidekick` live dashboards now show actual dollars for Codex and Claude Code sessions\n- **`stats` footer lists unpriced models**: `sidekick stats` prints any models encountered with no pricing entry so missing coverage is visible\n\n### Fixed\n\n- **Context-gauge % wrong for Opus 4.7 (1M) and other new models**: The dashboard\'s context gauge was dividing by 200K for Claude Opus 4.7 (native 1M), inflating the displayed %. The shared model \u2192 context-window map now includes Opus/Sonnet 4.7 (1M), GPT-5.4 (1.05M), GPT-5.3-Codex (400K), and GPT-5.3-Codex-Spark (128K). Claude Code\'s `[1m]` suffix is now also honored as an explicit 1M marker\n- **Silent Sonnet-priced fallback for unknown models**: Codex, GPT-5.x, and o-series rows were being rendered at Sonnet rates. Unknown-model rows now render as `\u2014` in yellow instead of inventing a dollar figure\n\n### Changed\n\n- **`historical-data.json` schema v2**: reads `priced` flag and `unpricedModelIds` from records written by the latest VS Code extension; v1 records still read correctly\n\n## [0.17.1] - 2026-04-13\n\n### Fixed\n\n- **Codex multi-home session discovery**: Provider detection now scans all candidate Codex home directories, fixing missed sessions when the managed profile home is empty but the system `~/.codex/` has activity\n\n## [0.17.0] - 2026-04-13\n\n### Added\n\n- **Multi-provider account management**: `sidekick account` now supports `--provider codex` for Codex profile management alongside Claude Code accounts\n- **Codex account lifecycle**: `--add` prepares a profile and spawns `codex login`; `--switch-to` and `--remove` accept email, label, or profile ID\n- **Quota snapshot fallback**: `sidekick quota` for Codex shows cached rate-limit snapshots when no active session exists, with "cached from" timestamp\n\n### Fixed\n\n- **Email normalization**: Claude account lookup normalizes email case for reliable matching\n\n## [0.16.1] - 2026-03-27\n\n### Fixed\n\n- **Dashboard provider status scoping**: The TUI now shows degraded-service notices only for the monitored provider \u2014 Claude for Claude Code sessions, OpenAI for Codex sessions, and no status banner for OpenCode\n\n## [0.16.0] - 2026-03-23\n\n### Changed\n\n- **Consistent cost formatting**: All cost displays (`stats`, `context`, Sessions panel, narrative prompt) now use shared `formatCost()` with intelligent decimal precision (4 places for < $0.01, 2 otherwise)\n- **QuotaService**: Rewritten to wrap shared `QuotaPoller` with exponential backoff instead of manual polling loop\n- **modelContext**: Now re-exports `getModelInfo` from shared library alongside `getContextWindowSize`\n\n## [0.15.2] - 2026-03-18\n\n### Fixed\n\n- **CLI help descriptions**: Updated `quota` and `status` command descriptions to reflect provider-aware behavior\n- **`sidekick quota --provider`**: Added local `--provider` option so `sidekick quota --provider codex` works naturally\n\n## [0.15.0] - 2026-03-18\n\n### Added\n\n- **OpenAI status page monitoring**: CLI dashboard now shows OpenAI API status alongside Claude API status\n- **Codex rate limits in dashboard**: Sessions panel displays Codex rate-limit data with "Rate Limits" header instead of "Quota"\n- **Provider-aware `sidekick quota` command**: Detects active provider and shows Codex rate limits, Claude subscription quota, or an informational message for OpenCode\n\n### Fixed\n\n- **QuotaService polling for Codex**: Dashboard no longer starts Claude OAuth quota polling when the active provider is Codex\n\n## [0.14.2] - 2026-03-16\n\n### Fixed\n\n- **Quota polling interval**: Reduced quota refresh from every 30 seconds to every 5 minutes to avoid unnecessary API calls\n- **SessionsPanel `detailWidth()` call**: Removed unused parameter from `detailWidth()` in the Sessions panel quota rendering\n\n## [0.14.1] - 2026-03-14\n\n### Fixed\n\n- **Per-model context window sizes**: Dashboard context gauge now shows correct utilization for Claude Opus 4.6 (1M context) and other models with non-200K windows\n\n### Changed\n\n- **Shared model context lookup**: CLI dashboard now uses the centralized `getModelContextWindowSize()` from `sidekick-shared` instead of a local duplicate map\n\n## [0.14.0] - 2026-03-12\n\n### Added\n\n- **`sidekick account` Command**: Manage Claude Code accounts from the terminal \u2014 list saved accounts, add the current account with an optional label, switch to the next or a specific account, and remove accounts. Supports `--json` output for scripting\n- **Quota Account Label**: `sidekick quota` now shows the active account email and label above the quota bars when multi-account is enabled\n- **macOS Keychain Support**: `sidekick account` and `sidekick quota` now read and write credentials via the system Keychain on macOS, fixing account switching and quota checks on Mac\n\n## [0.13.8] - 2026-03-12\n\n### Changed\n\n- **Structured quota failure output**: `sidekick quota` now renders consistent auth, rate-limit, server, network, and unexpected-failure copy from shared quota failure descriptors while preserving `--json` machine-readable output\n- **Dashboard unavailable quota rendering**: The Sessions panel now shows Claude Code quota failures inline instead of hiding the quota section whenever subscription data is unavailable\n- **Quota transition toasts**: The Ink dashboard now fires low-noise toast notifications only when Claude Code quota failure state changes, avoiding repeated alerts every polling interval\n\n## [0.13.7] - 2026-03-11\n\n### Changed\n\n- **npm README sync**: Updated the published CLI package README to reflect current OpenCode monitoring behavior, platform-specific data directories, and the `sqlite3` runtime requirement\n- **README badge cleanup**: Removed the Ask DeepWiki badge from the published CLI package README; the repo root README still keeps it\n\n## [0.13.6] - 2026-03-11\n\n### Changed\n\n- **Refreshed CLI Dashboard Wordmark**: Updated the dashboard wordmark/header styling for a cleaner splash and dashboard identity\n\n### Fixed\n\n- **OpenCode dashboard startup**: OpenCode DB-backed session discovery now resolves projects by worktree, sandboxes, and session directory instead of quietly behaving like no session exists\n- **OpenCode runtime notices**: The CLI now prints an OpenCode-only actionable notice when `opencode.db` exists but `sqlite3` is missing, blocked, or otherwise unusable in the current shell environment\n\n## [0.13.5] - 2026-03-10\n\n### Added\n\n- **`sidekick status` Command**: One-shot Claude API status check with color-coded text output and `--json` mode\n- **Dashboard Status Banner**: Status bar shows a colored `\u25CF API minor/major/critical` indicator when Claude is degraded; Sessions panel Summary tab shows an "API Status" section with affected components and active incident details. Polls every 60s\n\n## [0.13.4] - 2026-03-08\n\n### Fixed\n\n- **Onboarding Phrase Spam**: Splash screen and detail pane motivational phrases memoized \u2014 no longer flicker every render tick (fixes [#13](https://github.com/cesarandreslopez/sidekick-agent-hub/issues/13))\n\n### Changed\n\n- **Simplified Logo**: Replaced 6-line ASCII robot art with compact text header in splash, help, and changelog overlays\n- **Removed Dead Code**: Removed unused `getSplashContent()` and `HELP_HEADER` exports from branding module\n\n## [0.13.3] - 2026-03-04\n\n_No CLI-specific changes in this release._\n\n## [0.13.2] - 2026-03-04\n\n_No CLI-specific changes in this release._\n\n## [0.13.1] - 2026-03-04\n\n### Added\n\n- **`sidekick quota` Command**: One-shot subscription quota check showing 5-hour and 7-day utilization with color-coded progress bars and reset countdowns \u2014 supports `--json` for machine-readable output\n- **Quota Projections**: Elapsed-time projections shown in `sidekick quota` output and TUI dashboard quota section \u2014 displays projected end-of-window utilization next to current value (e.g., `40% \u2192 100%`), included in `--json` output as `projectedFiveHour` / `projectedSevenDay`\n\n## [0.13.0] - 2026-03-03\n\n_No CLI-specific changes in this release._\n\n## [0.12.10] - 2026-03-01\n\n### Added\n\n- **Events Panel** (key 7): Scrollable live event stream with colored type badges (`[USR]`, `[AST]`, `[TOOL]`, `[RES]`), timestamps, and keyword-highlighted summaries; detail tabs for full event JSON and surrounding context\n- **Charts Panel** (key 8): Tool frequency horizontal bars, event type distribution, 60-minute activity heatmap using `\u2591\u2592\u2593\u2588` intensity characters, and pattern analysis with frequency bars and template text\n- **Multi-Mode Filter**: `/` filter overlay now supports four modes \u2014 substring, fuzzy, regex, and date range \u2014 Tab cycles modes, regex mode shows red validation errors\n- **Search Term Highlighting**: Active filter terms highlighted in blue within side list items\n- **Timeline Keyword Coloring**: Event summaries in the Sessions panel Timeline tab now use semantic keyword coloring \u2014 errors red, success green, tool names cyan, file paths magenta\n\n### Removed\n\n- **Search Panel**: Removed redundant Search panel (previously key 7) \u2014 the `/` filter with multi-mode support serves the same purpose\n\n## [0.12.9] - 2026-02-28\n\n### Added\n\n- **Standalone Data Commands**: `sidekick tasks`, `sidekick decisions`, `sidekick notes`, `sidekick stats`, `sidekick handoff` for accessing project data without launching the TUI\n- **`sidekick search <query>`**: Cross-session full-text search from the terminal\n- **`sidekick context`**: Composite output of tasks, decisions, notes, and handoff for piping into other tools\n- **`--list` flag on `sidekick dump`**: Discover available session IDs before requiring `--session <id>`\n- **Search Panel**: Search panel (panel 7) wired into the TUI dashboard\n\n### Changed\n\n- **`taskMerger` utility**: Duplicate `mergeTasks` logic extracted into shared `taskMerger` utility\n- **Model constants**: Hardcoded model IDs extracted to named constants\n\n### Fixed\n\n- **`convention` icon**: Notes panel icon replaced with valid `tip` type\n- **Linux clipboard**: Now supports Wayland (`wl-copy`) and `xsel` fallbacks, with error messages instead of silent failure\n- **`provider.dispose()`**: Added to `dump` and `report` commands (prevents SQLite connection leaks)\n\n## [0.12.8] - 2026-02-28\n\n### Changed\n\n- **Dashboard UI/UX Polish**: Visual overhaul for better hierarchy, consistency, and readability\n - Splash screen and help overlay now display the robot ASCII logo\n - Toast notifications show severity icons (\u2718 error, \u26A0 warning, \u25CF info) with inner padding\n - Focused pane uses double-border for clear focus indication\n - Section dividers (`\u2500\u2500 Title \u2500\u2500\u2500\u2500`) replace bare bold headers in summary, agents, and context attribution\n - Tab bar: active tab underlined in magenta, inactive tabs dimmed, bracket syntax removed\n - Status bar: segmented layout with `\u2502` separators; keys bold, labels dim\n - Summary metrics condensed: elapsed/events/compactions on one line, tokens on one line with cache rate and cost\n - Sparklines display peak metadata annotations\n - Progress bars use blessed color tags for consistent coloring\n - Help overlay uses dot-leader alignment for all keybinding rows\n - Empty state hints per panel (e.g. "Tasks appear as your agent works.")\n - Session picker groups sessions by provider with section headers when multiple providers are present\n\n## [0.12.7] - 2026-02-27\n\n### Added\n\n- **HTML Session Report**: `sidekick report` command generates a self-contained HTML report and opens it in the default browser\n - Options: `--session`, `--output`, `--theme` (dark/light), `--no-open`, `--no-thinking`\n - TUI Dashboard: press `r` to generate and open an HTML report for the current session\n\n## [0.12.6] - 2026-02-26\n\n### Added\n\n- **Session Dump Command**: `sidekick dump` exports session data in text, markdown, or JSON format with `--format`, `--width`, and `--expand` options\n- **Plans Panel Re-enabled**: Plans panel restored in CLI dashboard with plan file discovery from `~/.claude/plans/`\n- **Enhanced Status Bar**: Session info display improved with richer metadata\n\n### Fixed\n\n- **Old snapshot format migration**: Restoring pre-0.12.3 session snapshots no longer shows empty timeline entries\n\n### Changed\n\n- **Phrase library moved to shared**: CLI-specific phrase formatting kept local, all phrase content now from `sidekick-shared`\n\n## [0.12.5] - 2026-02-24\n\n### Fixed\n\n- **Update check too slow to notice new versions**: Reduced npm registry cache TTL from 24 hours to 4 hours so upgrade notices appear sooner after a new release\n\n## [0.12.4] - 2026-02-24\n\n### Fixed\n\n- **Session crash on upgrade**: Fixed `d.timestamp.getTime is not a function` error when restoring tool call data from session snapshots \u2014 `Date` objects were serialized to strings by JSON but not rehydrated on restore, causing the session monitor to crash on first run after upgrading from 0.12.2 to 0.12.3\n\n## [0.12.3] - 2026-02-24\n\n### Added\n\n- **Latest-node indicator**: The most recently added node in tree and boxed mind map views is now marked with a yellow indicator\n- **Plan analytics in mind map**: Tree and boxed views now display plan progress and per-step metrics\n - Tree view: plan header shows completion stats; steps show complexity, duration, tokens, tool calls, and errors in metadata brackets\n - Box view: progress bar with completion percentage; steps show right-aligned metrics; subtitle shows step count and total duration\n- **Cross-provider plan extraction**: Shared `PlanExtractor` now handles Claude Code (EnterPlanMode/ExitPlanMode) and OpenCode (`<proposed_plan>` XML) plans \u2014 previously only Codex plans were shown\n- **Enriched plan data model**: Plan steps include duration, token count, tool call count, and error messages\n- **Phase-grouped plan display**: When a plan has phase structure, tree and boxed views group steps under phase headers with context lines from the original plan markdown\n- **Node type filter**: Press `f` on the Mind Map tab to cycle through node type filters (file, tool, task, subagent, command, plan, knowledge-note) \u2014 non-matching sections render dimmed in grey\n\n### Fixed\n\n- **Kanban board regression**: Subagent and plan-step tasks now correctly appear in the kanban board\n\n### Changed\n\n- **Plans panel temporarily disabled**: The Plans panel in the CLI dashboard is disabled until plan-mode event capture is reliably working end-to-end. Plan nodes in the mind map remain active.\n- `DashboardState` now delegates to shared `EventAggregator` instead of maintaining its own aggregation logic\n\n## [0.12.2] - 2026-02-23\n\n### Added\n\n- **Update notifications**: The dashboard now checks the npm registry for newer versions on startup and shows a yellow banner in the status bar when an update is available (e.g., `v0.13.0 available \u2014 npm i -g sidekick-agent-hub`). Results are cached for 24 hours to avoid repeated network requests.\n\n## [0.12.1] - 2026-02-23\n\n### Fixed\n\n- **VS Code integration**: Fixed exit code 127 when the extension launches the CLI dashboard on systems using nvm or volta (node binary not found when shell init is bypassed)\n\n## [0.12.0] - 2026-02-22\n\n### Added\n\n- **"Open CLI Dashboard" VS Code Integration**: New VS Code command `Sidekick: Open CLI Dashboard` launches the TUI dashboard in an integrated terminal\n - Install the CLI with `npm install -g sidekick-agent-hub`\n\n## [0.11.0] - 2026-02-19\n\n### Added\n\n- **Initial Release**: Full-screen TUI dashboard for monitoring agent sessions from the terminal\n - Ink-based terminal UI with panels for sessions, tasks, kanban, mind map, notes, decisions, search, files, and git diff\n - Multi-provider support: auto-detects Claude Code, OpenCode, and Codex sessions\n - Reads from `~/.config/sidekick/` \u2014 the same data files the VS Code extension writes\n - Usage: `sidekick dashboard [--project <path>] [--provider <id>]`\n';
78520
78932
  }
78521
78933
  });
78522
78934
 
@@ -80149,8 +80561,8 @@ async function searchAction(_opts, cmd) {
80149
80561
  const projectPath = globalOpts.project || void 0;
80150
80562
  let projectSlug;
80151
80563
  if (projectPath) {
80152
- const path7 = await import("path");
80153
- const resolved = path7.resolve(projectPath);
80564
+ const path8 = await import("path");
80565
+ const resolved = path8.resolve(projectPath);
80154
80566
  const { encodeWorkspacePath } = await Promise.resolve().then(() => __toESM(require_dist(), 1));
80155
80567
  projectSlug = encodeWorkspacePath(resolved);
80156
80568
  }
@@ -80511,10 +80923,22 @@ function printStatsSummary(history) {
80511
80923
  process.stdout.write(source_default.bold("Model Usage\n"));
80512
80924
  process.stdout.write(source_default.dim("\u2500".repeat(50) + "\n"));
80513
80925
  const sorted = [...at.modelUsage].sort((a, b) => b.calls - a.calls);
80926
+ const unpricedModels = [];
80514
80927
  for (const m of sorted) {
80515
- const costStr = m.cost > 0 ? source_default.dim(` (${(0, import_sidekick_shared27.formatCost)(m.cost)})`) : "";
80928
+ let costStr = "";
80929
+ if (m.priced === false) {
80930
+ costStr = source_default.yellow(" (\u2014)");
80931
+ unpricedModels.push(m.model);
80932
+ } else if (m.cost > 0) {
80933
+ costStr = source_default.dim(` (${(0, import_sidekick_shared27.formatCost)(m.cost)})`);
80934
+ }
80516
80935
  process.stdout.write(` ${source_default.cyan(m.model.padEnd(30))} ${formatNumber(m.calls).padStart(8)} calls${costStr}
80517
80936
  `);
80937
+ }
80938
+ if (unpricedModels.length > 0) {
80939
+ const label = unpricedModels.length === 1 ? "1 model unpriced" : `${unpricedModels.length} models unpriced`;
80940
+ process.stdout.write(source_default.dim(` \u26A0 ${label}: ${unpricedModels.join(", ")} \u2014 no pricing catalog entry.
80941
+ `));
80518
80942
  }
80519
80943
  process.stdout.write("\n");
80520
80944
  }
@@ -81229,6 +81653,8 @@ var init_handoff = __esm({
81229
81653
  });
81230
81654
 
81231
81655
  // src/cli.ts
81656
+ import * as os5 from "node:os";
81657
+ import * as path7 from "node:path";
81232
81658
  function resolveProviderId(opts, defaultProvider = "auto") {
81233
81659
  if (opts.provider && opts.provider !== "auto") {
81234
81660
  return opts.provider;
@@ -81256,8 +81682,12 @@ var init_cli = __esm({
81256
81682
  init_esm();
81257
81683
  import_sidekick_shared32 = __toESM(require_dist(), 1);
81258
81684
  import_sidekick_shared33 = __toESM(require_dist(), 1);
81685
+ (0, import_sidekick_shared32.hydratePricingCatalog)({
81686
+ cacheDir: path7.join(os5.homedir(), ".config", "sidekick")
81687
+ }).catch(() => {
81688
+ });
81259
81689
  program2 = new Command();
81260
- program2.name("sidekick").description("Query Sidekick project intelligence from the command line").version("0.17.0").option("--json", "Output as JSON").option("--project <path>", "Override project path (default: cwd)").option("--provider <id>", "Provider: claude-code, opencode, codex, auto (default: auto)");
81690
+ program2.name("sidekick").description("Query Sidekick project intelligence from the command line").version("0.17.2").option("--json", "Output as JSON").option("--project <path>", "Override project path (default: cwd)").option("--provider <id>", "Provider: claude-code, opencode, codex, auto (default: auto)");
81261
81691
  dashCmd = new Command("dashboard").description("Full-screen TUI dashboard with live session metrics").option("--session <id>", "Follow a specific session (default: most recent)").option("--replay", "Replay existing events before streaming new ones").action(async (_opts, cmd) => {
81262
81692
  const { dashboardAction: dashboardAction2 } = await init_dashboard().then(() => dashboard_exports);
81263
81693
  return dashboardAction2(_opts, cmd);