sidekick-agent-hub 0.17.1 → 0.17.3

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 +767 -390
  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 });
@@ -4520,13 +4520,13 @@ var require_codexProfiles = __commonJS({
4520
4520
  exports.switchToCodexAccount = switchToCodexAccount2;
4521
4521
  exports.removeCodexAccount = removeCodexAccount2;
4522
4522
  var fs9 = __importStar(__require("fs"));
4523
- var os5 = __importStar(__require("os"));
4524
- var path7 = __importStar(__require("path"));
4523
+ var os6 = __importStar(__require("os"));
4524
+ var path8 = __importStar(__require("path"));
4525
4525
  var child_process_1 = __require("child_process");
4526
4526
  var crypto_1 = __require("crypto");
4527
4527
  var accountRegistry_1 = require_accountRegistry();
4528
4528
  function getDefaultSystemCodexHome() {
4529
- return path7.join(os5.homedir(), ".codex");
4529
+ return path8.join(os6.homedir(), ".codex");
4530
4530
  }
4531
4531
  function getExplicitCodexHome() {
4532
4532
  const explicitHome = process.env.CODEX_HOME?.trim();
@@ -4536,7 +4536,7 @@ var require_codexProfiles = __commonJS({
4536
4536
  const seen = /* @__PURE__ */ new Set();
4537
4537
  const unique = [];
4538
4538
  for (const candidate of paths) {
4539
- const normalized = path7.resolve(candidate);
4539
+ const normalized = path8.resolve(candidate);
4540
4540
  if (seen.has(normalized))
4541
4541
  continue;
4542
4542
  seen.add(normalized);
@@ -4560,16 +4560,16 @@ var require_codexProfiles = __commonJS({
4560
4560
  return dedupePaths(homes);
4561
4561
  }
4562
4562
  function getCodexProfilesDir() {
4563
- return path7.join((0, accountRegistry_1.getAccountsDir)(), "codex", "profiles");
4563
+ return path8.join((0, accountRegistry_1.getAccountsDir)(), "codex", "profiles");
4564
4564
  }
4565
4565
  function getCodexProfileDir(profileId) {
4566
- return path7.join(getCodexProfilesDir(), profileId);
4566
+ return path8.join(getCodexProfilesDir(), profileId);
4567
4567
  }
4568
4568
  function getCodexProfileHome(profileId) {
4569
- return path7.join(getCodexProfileDir(profileId), "codex-home");
4569
+ return path8.join(getCodexProfileDir(profileId), "codex-home");
4570
4570
  }
4571
4571
  function getCodexProfileStatePath(profileId) {
4572
- return path7.join(getCodexProfileDir(profileId), "profile.json");
4572
+ return path8.join(getCodexProfileDir(profileId), "profile.json");
4573
4573
  }
4574
4574
  function ensureCodexProfileDirs(profileId) {
4575
4575
  fs9.mkdirSync(getCodexProfileHome(profileId), { recursive: true, mode: 448 });
@@ -4595,16 +4595,16 @@ var require_codexProfiles = __commonJS({
4595
4595
  function copyIfExists(source, destination) {
4596
4596
  if (!fs9.existsSync(source))
4597
4597
  return false;
4598
- fs9.mkdirSync(path7.dirname(destination), { recursive: true, mode: 448 });
4598
+ fs9.mkdirSync(path8.dirname(destination), { recursive: true, mode: 448 });
4599
4599
  fs9.copyFileSync(source, destination);
4600
4600
  return true;
4601
4601
  }
4602
4602
  function copySourceCodexConfig(sourceHome, targetHome) {
4603
- copyIfExists(path7.join(sourceHome, "config.toml"), path7.join(targetHome, "config.toml"));
4603
+ copyIfExists(path8.join(sourceHome, "config.toml"), path8.join(targetHome, "config.toml"));
4604
4604
  }
4605
4605
  function importCurrentCodexAuth(sourceHome, targetHome) {
4606
- const authCopied = copyIfExists(path7.join(sourceHome, "auth.json"), path7.join(targetHome, "auth.json"));
4607
- 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"));
4608
4608
  return authCopied || legacyCredsCopied;
4609
4609
  }
4610
4610
  function parseJwtPayload(jwt) {
@@ -4619,7 +4619,7 @@ var require_codexProfiles = __commonJS({
4619
4619
  }
4620
4620
  }
4621
4621
  function readMetadataFromAuthJson(codexHome) {
4622
- const authPath = path7.join(codexHome, "auth.json");
4622
+ const authPath = path8.join(codexHome, "auth.json");
4623
4623
  if (!fs9.existsSync(authPath))
4624
4624
  return {};
4625
4625
  try {
@@ -4643,7 +4643,7 @@ var require_codexProfiles = __commonJS({
4643
4643
  }
4644
4644
  }
4645
4645
  function readMetadataFromLegacyCredentials(codexHome) {
4646
- const legacyPath = path7.join(codexHome, ".credentials.json");
4646
+ const legacyPath = path8.join(codexHome, ".credentials.json");
4647
4647
  if (!fs9.existsSync(legacyPath))
4648
4648
  return {};
4649
4649
  try {
@@ -4694,7 +4694,7 @@ var require_codexProfiles = __commonJS({
4694
4694
  return {};
4695
4695
  }
4696
4696
  function isCodexProfileAuthenticated(codexHome) {
4697
- 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"))) {
4698
4698
  return true;
4699
4699
  }
4700
4700
  return getCodexLoginStatus(codexHome).loggedIn;
@@ -4848,19 +4848,19 @@ var require_detect = __commonJS({
4848
4848
  exports.getAllDetectedProviders = getAllDetectedProviders2;
4849
4849
  exports.detectProvider = detectProvider2;
4850
4850
  var fs9 = __importStar(__require("fs"));
4851
- var os5 = __importStar(__require("os"));
4852
- var path7 = __importStar(__require("path"));
4851
+ var os6 = __importStar(__require("os"));
4852
+ var path8 = __importStar(__require("path"));
4853
4853
  var codexProfiles_1 = require_codexProfiles();
4854
4854
  function getOpenCodeDataDir() {
4855
4855
  const xdg = process.env.XDG_DATA_HOME;
4856
4856
  if (xdg)
4857
- return path7.join(xdg, "opencode");
4857
+ return path8.join(xdg, "opencode");
4858
4858
  if (process.platform === "darwin")
4859
- return path7.join(os5.homedir(), "Library", "Application Support", "opencode");
4859
+ return path8.join(os6.homedir(), "Library", "Application Support", "opencode");
4860
4860
  if (process.platform === "win32") {
4861
- 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");
4862
4862
  }
4863
- return path7.join(os5.homedir(), ".local", "share", "opencode");
4863
+ return path8.join(os6.homedir(), ".local", "share", "opencode");
4864
4864
  }
4865
4865
  function getCodexHomes() {
4866
4866
  return (0, codexProfiles_1.getCodexMonitoringHomes)();
@@ -4873,7 +4873,7 @@ var require_detect = __commonJS({
4873
4873
  const entries = fs9.readdirSync(dir);
4874
4874
  for (const entry of entries) {
4875
4875
  try {
4876
- const stats = fs9.statSync(path7.join(dir, entry));
4876
+ const stats = fs9.statSync(path8.join(dir, entry));
4877
4877
  if (stats.mtime.getTime() > latest) {
4878
4878
  latest = stats.mtime.getTime();
4879
4879
  }
@@ -4887,43 +4887,43 @@ var require_detect = __commonJS({
4887
4887
  }
4888
4888
  function getOpenCodeActivityMtime() {
4889
4889
  const dataDir = getOpenCodeDataDir();
4890
- const dbPath = path7.join(dataDir, "opencode.db");
4890
+ const dbPath = path8.join(dataDir, "opencode.db");
4891
4891
  try {
4892
4892
  const dbMtime = fs9.statSync(dbPath).mtime.getTime();
4893
4893
  if (dbMtime > 0)
4894
4894
  return dbMtime;
4895
4895
  } catch {
4896
4896
  }
4897
- const storageDir = path7.join(dataDir, "storage");
4898
- const sessionMtime = getMostRecentMtime(path7.join(storageDir, "session"));
4899
- const messageMtime = getMostRecentMtime(path7.join(storageDir, "message"));
4900
- 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"));
4901
4901
  return Math.max(sessionMtime, messageMtime, partMtime);
4902
4902
  }
4903
4903
  function getCodexActivityMtime() {
4904
4904
  let latest = 0;
4905
4905
  for (const codexHome of getCodexHomes()) {
4906
- const dbPath = path7.join(codexHome, "state.sqlite");
4906
+ const dbPath = path8.join(codexHome, "state.sqlite");
4907
4907
  try {
4908
4908
  const dbMtime = fs9.statSync(dbPath).mtime.getTime();
4909
4909
  if (dbMtime > latest)
4910
4910
  latest = dbMtime;
4911
4911
  } catch {
4912
4912
  }
4913
- const sessionsMtime = getMostRecentMtime(path7.join(codexHome, "sessions"));
4913
+ const sessionsMtime = getMostRecentMtime(path8.join(codexHome, "sessions"));
4914
4914
  if (sessionsMtime > latest)
4915
4915
  latest = sessionsMtime;
4916
4916
  }
4917
4917
  return latest;
4918
4918
  }
4919
4919
  function getProviderPaths() {
4920
- const claudeBase = path7.join(os5.homedir(), ".claude", "projects");
4920
+ const claudeBase = path8.join(os6.homedir(), ".claude", "projects");
4921
4921
  const openCodeDataDir = getOpenCodeDataDir();
4922
4922
  const codexHomes = getCodexHomes();
4923
4923
  return {
4924
4924
  claudeBase,
4925
- openCodeDbPath: path7.join(openCodeDataDir, "opencode.db"),
4926
- openCodeStorageDir: path7.join(openCodeDataDir, "storage"),
4925
+ openCodeDbPath: path8.join(openCodeDataDir, "opencode.db"),
4926
+ openCodeStorageDir: path8.join(openCodeDataDir, "storage"),
4927
4927
  codexHomes
4928
4928
  };
4929
4929
  }
@@ -4931,7 +4931,7 @@ var require_detect = __commonJS({
4931
4931
  const { claudeBase, openCodeDbPath, openCodeStorageDir, codexHomes } = getProviderPaths();
4932
4932
  const hasClaude = fs9.existsSync(claudeBase);
4933
4933
  const hasOpenCode = fs9.existsSync(openCodeStorageDir) || fs9.existsSync(openCodeDbPath);
4934
- const hasCodex = codexHomes.some((codexHome) => fs9.existsSync(path7.join(codexHome, "sessions")) || fs9.existsSync(path7.join(codexHome, "state.sqlite")));
4934
+ const hasCodex = codexHomes.some((codexHome) => fs9.existsSync(path8.join(codexHome, "sessions")) || fs9.existsSync(path8.join(codexHome, "state.sqlite")));
4935
4935
  const available = [];
4936
4936
  if (hasClaude)
4937
4937
  available.push({ id: "claude-code", mtime: getMostRecentMtime(claudeBase) });
@@ -4948,7 +4948,7 @@ var require_detect = __commonJS({
4948
4948
  const { claudeBase, openCodeDbPath, openCodeStorageDir, codexHomes } = getProviderPaths();
4949
4949
  const hasClaude = fs9.existsSync(claudeBase);
4950
4950
  const hasOpenCode = fs9.existsSync(openCodeStorageDir) || fs9.existsSync(openCodeDbPath);
4951
- const hasCodex = codexHomes.some((codexHome) => fs9.existsSync(path7.join(codexHome, "sessions")) || fs9.existsSync(path7.join(codexHome, "state.sqlite")));
4951
+ const hasCodex = codexHomes.some((codexHome) => fs9.existsSync(path8.join(codexHome, "sessions")) || fs9.existsSync(path8.join(codexHome, "state.sqlite")));
4952
4952
  const available = [];
4953
4953
  if (hasClaude) {
4954
4954
  available.push({ id: "claude-code", mtime: getMostRecentMtime(claudeBase) });
@@ -5093,26 +5093,26 @@ var require_sessionPathResolver = __commonJS({
5093
5093
  exports.discoverWorktreeSiblings = discoverWorktreeSiblings;
5094
5094
  exports.findAllSessionsWithWorktrees = findAllSessionsWithWorktrees;
5095
5095
  var fs9 = __importStar(__require("fs"));
5096
- var path7 = __importStar(__require("path"));
5097
- var os5 = __importStar(__require("os"));
5096
+ var path8 = __importStar(__require("path"));
5097
+ var os6 = __importStar(__require("os"));
5098
5098
  function encodeWorkspacePath(workspacePath) {
5099
5099
  const normalized = workspacePath.replace(/\\/g, "/");
5100
5100
  return normalized.replace(/[:/_]/g, "-");
5101
5101
  }
5102
5102
  function getSessionDirectory(workspacePath) {
5103
5103
  const encoded = encodeWorkspacePath(workspacePath);
5104
- return path7.join(os5.homedir(), ".claude", "projects", encoded);
5104
+ return path8.join(os6.homedir(), ".claude", "projects", encoded);
5105
5105
  }
5106
5106
  var ACTIVE_SESSION_THRESHOLD_MS = 5 * 60 * 1e3;
5107
5107
  function findSubdirectorySessionDirs(workspacePath) {
5108
- const projectsDir = path7.join(os5.homedir(), ".claude", "projects");
5108
+ const projectsDir = path8.join(os6.homedir(), ".claude", "projects");
5109
5109
  try {
5110
5110
  if (!fs9.existsSync(projectsDir)) {
5111
5111
  return [];
5112
5112
  }
5113
5113
  const encodedPrefix = encodeWorkspacePath(workspacePath).toLowerCase();
5114
5114
  const allDirs = fs9.readdirSync(projectsDir).filter((name) => {
5115
- const fullPath = path7.join(projectsDir, name);
5115
+ const fullPath = path8.join(projectsDir, name);
5116
5116
  try {
5117
5117
  return fs9.statSync(fullPath).isDirectory();
5118
5118
  } catch {
@@ -5123,7 +5123,7 @@ var require_sessionPathResolver = __commonJS({
5123
5123
  for (const dir of allDirs) {
5124
5124
  const dirLower = dir.toLowerCase();
5125
5125
  if (dirLower.startsWith(encodedPrefix + "-")) {
5126
- matches.push(path7.join(projectsDir, dir));
5126
+ matches.push(path8.join(projectsDir, dir));
5127
5127
  }
5128
5128
  }
5129
5129
  return matches;
@@ -5139,7 +5139,7 @@ var require_sessionPathResolver = __commonJS({
5139
5139
  const files = fs9.readdirSync(dir).filter((file) => file.endsWith(".jsonl"));
5140
5140
  for (const file of files) {
5141
5141
  try {
5142
- const fullPath = path7.join(dir, file);
5142
+ const fullPath = path8.join(dir, file);
5143
5143
  const stats = fs9.statSync(fullPath);
5144
5144
  if (stats.size > 0 && stats.mtime.getTime() > mostRecentMtime) {
5145
5145
  mostRecentMtime = stats.mtime.getTime();
@@ -5154,7 +5154,7 @@ var require_sessionPathResolver = __commonJS({
5154
5154
  return mostRecentDir;
5155
5155
  }
5156
5156
  function discoverSessionDirectory(workspacePath) {
5157
- const projectsDir = path7.join(os5.homedir(), ".claude", "projects");
5157
+ const projectsDir = path8.join(os6.homedir(), ".claude", "projects");
5158
5158
  const computedDir = getSessionDirectory(workspacePath);
5159
5159
  if (fs9.existsSync(computedDir)) {
5160
5160
  return computedDir;
@@ -5169,7 +5169,7 @@ var require_sessionPathResolver = __commonJS({
5169
5169
  try {
5170
5170
  if (fs9.existsSync(projectsDir)) {
5171
5171
  const existingDirs = fs9.readdirSync(projectsDir).filter((name) => {
5172
- const fullPath = path7.join(projectsDir, name);
5172
+ const fullPath = path8.join(projectsDir, name);
5173
5173
  try {
5174
5174
  return fs9.statSync(fullPath).isDirectory();
5175
5175
  } catch {
@@ -5179,35 +5179,35 @@ var require_sessionPathResolver = __commonJS({
5179
5179
  const normalizedWorkspace = workspacePath.replace(/\\/g, "/").replace(/:/g, "-").replace(/_/g, "-").replace(/\//g, "-").toLowerCase();
5180
5180
  for (const dir of existingDirs) {
5181
5181
  if (dir.toLowerCase() === normalizedWorkspace) {
5182
- return path7.join(projectsDir, dir);
5182
+ return path8.join(projectsDir, dir);
5183
5183
  }
5184
5184
  }
5185
- const workspaceBasename = path7.basename(workspacePath).replace(/_/g, "-").toLowerCase();
5185
+ const workspaceBasename = path8.basename(workspacePath).replace(/_/g, "-").toLowerCase();
5186
5186
  for (const dir of existingDirs) {
5187
5187
  const dirLower = dir.toLowerCase();
5188
5188
  if (dirLower.endsWith("-" + workspaceBasename) || dirLower === workspaceBasename) {
5189
- return path7.join(projectsDir, dir);
5189
+ return path8.join(projectsDir, dir);
5190
5190
  }
5191
5191
  }
5192
5192
  }
5193
5193
  } catch {
5194
5194
  }
5195
5195
  try {
5196
- const claudeTempDir = path7.join(os5.tmpdir(), "claude");
5196
+ const claudeTempDir = path8.join(os6.tmpdir(), "claude");
5197
5197
  if (fs9.existsSync(claudeTempDir)) {
5198
5198
  const tempDirs = fs9.readdirSync(claudeTempDir).filter((name) => {
5199
- const fullPath = path7.join(claudeTempDir, name);
5199
+ const fullPath = path8.join(claudeTempDir, name);
5200
5200
  try {
5201
5201
  return fs9.statSync(fullPath).isDirectory();
5202
5202
  } catch {
5203
5203
  return false;
5204
5204
  }
5205
5205
  });
5206
- const workspaceBasename = path7.basename(workspacePath).replace(/_/g, "-").toLowerCase();
5206
+ const workspaceBasename = path8.basename(workspacePath).replace(/_/g, "-").toLowerCase();
5207
5207
  for (const encodedDir of tempDirs) {
5208
5208
  const encodedLower = encodedDir.toLowerCase();
5209
5209
  if (encodedLower.endsWith("-" + workspaceBasename) || encodedLower === workspaceBasename) {
5210
- const sessionDir = path7.join(projectsDir, encodedDir);
5210
+ const sessionDir = path8.join(projectsDir, encodedDir);
5211
5211
  if (fs9.existsSync(sessionDir)) {
5212
5212
  return sessionDir;
5213
5213
  }
@@ -5226,7 +5226,7 @@ var require_sessionPathResolver = __commonJS({
5226
5226
  }
5227
5227
  const now = Date.now();
5228
5228
  const files = fs9.readdirSync(sessionDir).filter((file) => file.endsWith(".jsonl")).map((file) => {
5229
- const fullPath = path7.join(sessionDir, file);
5229
+ const fullPath = path8.join(sessionDir, file);
5230
5230
  const stats = fs9.statSync(fullPath);
5231
5231
  const mtime = stats.mtime.getTime();
5232
5232
  return {
@@ -5259,7 +5259,7 @@ var require_sessionPathResolver = __commonJS({
5259
5259
  return [];
5260
5260
  }
5261
5261
  const files = fs9.readdirSync(sessionDir).filter((file) => file.endsWith(".jsonl")).map((file) => {
5262
- const fullPath = path7.join(sessionDir, file);
5262
+ const fullPath = path8.join(sessionDir, file);
5263
5263
  const stats = fs9.statSync(fullPath);
5264
5264
  return {
5265
5265
  path: fullPath,
@@ -5286,7 +5286,7 @@ var require_sessionPathResolver = __commonJS({
5286
5286
  return encoded.replace(/-/g, "/");
5287
5287
  }
5288
5288
  function getAllProjectFolders(workspacePath) {
5289
- const projectsDir = path7.join(os5.homedir(), ".claude", "projects");
5289
+ const projectsDir = path8.join(os6.homedir(), ".claude", "projects");
5290
5290
  const folders = [];
5291
5291
  try {
5292
5292
  if (!fs9.existsSync(projectsDir)) {
@@ -5294,7 +5294,7 @@ var require_sessionPathResolver = __commonJS({
5294
5294
  }
5295
5295
  const entries = fs9.readdirSync(projectsDir);
5296
5296
  for (const entry of entries) {
5297
- const fullPath = path7.join(projectsDir, entry);
5297
+ const fullPath = path8.join(projectsDir, entry);
5298
5298
  try {
5299
5299
  const stats = fs9.statSync(fullPath);
5300
5300
  if (!stats.isDirectory()) {
@@ -5305,7 +5305,7 @@ var require_sessionPathResolver = __commonJS({
5305
5305
  let sessionCount = 0;
5306
5306
  for (const sessionFile of sessionFiles) {
5307
5307
  try {
5308
- const sessionPath = path7.join(fullPath, sessionFile);
5308
+ const sessionPath = path8.join(fullPath, sessionFile);
5309
5309
  const sessionStats = fs9.statSync(sessionPath);
5310
5310
  if (sessionStats.size > 0) {
5311
5311
  sessionCount++;
@@ -5357,7 +5357,7 @@ var require_sessionPathResolver = __commonJS({
5357
5357
  return [];
5358
5358
  }
5359
5359
  const files = fs9.readdirSync(sessionDir).filter((file) => file.endsWith(".jsonl")).map((file) => {
5360
- const fullPath = path7.join(sessionDir, file);
5360
+ const fullPath = path8.join(sessionDir, file);
5361
5361
  try {
5362
5362
  const stats = fs9.statSync(fullPath);
5363
5363
  return {
@@ -5375,7 +5375,7 @@ var require_sessionPathResolver = __commonJS({
5375
5375
  }
5376
5376
  }
5377
5377
  function resolveWorktreeMainRepo(workspacePath) {
5378
- const gitPath = path7.join(workspacePath, ".git");
5378
+ const gitPath = path8.join(workspacePath, ".git");
5379
5379
  try {
5380
5380
  const stat = fs9.statSync(gitPath);
5381
5381
  if (stat.isDirectory()) {
@@ -5386,31 +5386,31 @@ var require_sessionPathResolver = __commonJS({
5386
5386
  if (!match)
5387
5387
  return null;
5388
5388
  const gitdir = match[1].trim();
5389
- const resolvedGitdir = path7.isAbsolute(gitdir) ? gitdir : path7.resolve(workspacePath, gitdir);
5390
- const worktreesDir = path7.dirname(resolvedGitdir);
5391
- 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")
5392
5392
  return null;
5393
- const dotGit = path7.dirname(worktreesDir);
5394
- if (path7.basename(dotGit) !== ".git")
5393
+ const dotGit = path8.dirname(worktreesDir);
5394
+ if (path8.basename(dotGit) !== ".git")
5395
5395
  return null;
5396
- return path7.dirname(dotGit);
5396
+ return path8.dirname(dotGit);
5397
5397
  } catch {
5398
5398
  return null;
5399
5399
  }
5400
5400
  }
5401
5401
  function discoverWorktreeSiblings(mainRepoPath) {
5402
- const worktreesDir = path7.join(mainRepoPath, ".git", "worktrees");
5402
+ const worktreesDir = path8.join(mainRepoPath, ".git", "worktrees");
5403
5403
  const siblings = [];
5404
5404
  try {
5405
5405
  if (!fs9.existsSync(worktreesDir))
5406
5406
  return siblings;
5407
5407
  const entries = fs9.readdirSync(worktreesDir);
5408
5408
  for (const entry of entries) {
5409
- const gitdirFile = path7.join(worktreesDir, entry, "gitdir");
5409
+ const gitdirFile = path8.join(worktreesDir, entry, "gitdir");
5410
5410
  try {
5411
5411
  const content = fs9.readFileSync(gitdirFile, "utf-8").trim();
5412
- const worktreeGit = path7.isAbsolute(content) ? content : path7.resolve(worktreesDir, entry, content);
5413
- const worktreeDir = path7.dirname(worktreeGit);
5412
+ const worktreeGit = path8.isAbsolute(content) ? content : path8.resolve(worktreesDir, entry, content);
5413
+ const worktreeDir = path8.dirname(worktreeGit);
5414
5414
  if (fs9.existsSync(worktreeDir)) {
5415
5415
  siblings.push(worktreeDir);
5416
5416
  }
@@ -5504,7 +5504,7 @@ var require_subagentScanner = __commonJS({
5504
5504
  exports.handleTaskUpdate = handleTaskUpdate;
5505
5505
  exports.extractTaskInfo = extractTaskInfo;
5506
5506
  var fs9 = __importStar(__require("fs"));
5507
- var path7 = __importStar(__require("path"));
5507
+ var path8 = __importStar(__require("path"));
5508
5508
  var AGENT_FILE_PATTERN = /^agent-(.+)\.jsonl$/;
5509
5509
  var TASK_TOOLS = ["TaskCreate", "TaskUpdate", "TaskGet", "TaskList"];
5510
5510
  function extractTaskIdFromResult(resultContent) {
@@ -5523,7 +5523,7 @@ var require_subagentScanner = __commonJS({
5523
5523
  function scanSubagentDir(sessionDir, sessionId, logger) {
5524
5524
  const log = logger || (() => {
5525
5525
  });
5526
- const subagentsDir = path7.join(sessionDir, sessionId, "subagents");
5526
+ const subagentsDir = path8.join(sessionDir, sessionId, "subagents");
5527
5527
  const results = [];
5528
5528
  try {
5529
5529
  if (!fs9.existsSync(subagentsDir)) {
@@ -5542,7 +5542,7 @@ var require_subagentScanner = __commonJS({
5542
5542
  continue;
5543
5543
  }
5544
5544
  const agentId = match[1];
5545
- const filePath = path7.join(subagentsDir, file);
5545
+ const filePath = path8.join(subagentsDir, file);
5546
5546
  const agentStats = parseAgentFile(filePath, agentId, log);
5547
5547
  if (agentStats) {
5548
5548
  log(`[SubagentScanner] Agent ${agentId}: ${agentStats.toolCalls.length} tool calls`);
@@ -5752,8 +5752,10 @@ var require_modelContext = __commonJS({
5752
5752
  exports.DEFAULT_CONTEXT_WINDOW = void 0;
5753
5753
  exports.getModelContextWindowSize = getModelContextWindowSize2;
5754
5754
  var MODEL_CONTEXT_SIZES = {
5755
- // Claude 4.6 family (1M context)
5755
+ // Claude — native 1M context (Opus 4.6+, Sonnet 4.6+)
5756
+ "claude-opus-4-7": 1e6,
5756
5757
  "claude-opus-4-6": 1e6,
5758
+ "claude-sonnet-4-7": 1e6,
5757
5759
  "claude-sonnet-4-6": 1e6,
5758
5760
  // Claude 4 family
5759
5761
  "claude-opus-4": 2e5,
@@ -5769,7 +5771,11 @@ var require_modelContext = __commonJS({
5769
5771
  "claude-3-haiku": 2e5,
5770
5772
  // OpenAI GPT-4.1 series (1M context)
5771
5773
  "gpt-4.1": 1048576,
5772
- // 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,
5773
5779
  "gpt-5": 4e5,
5774
5780
  // OpenAI reasoning
5775
5781
  "o1": 2e5,
@@ -5789,11 +5795,14 @@ var require_modelContext = __commonJS({
5789
5795
  function getModelContextWindowSize2(modelId) {
5790
5796
  if (!modelId)
5791
5797
  return exports.DEFAULT_CONTEXT_WINDOW;
5792
- if (MODEL_CONTEXT_SIZES[modelId] !== void 0) {
5793
- 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];
5794
5803
  }
5795
5804
  for (const key of SORTED_KEYS) {
5796
- if (modelId.startsWith(key)) {
5805
+ if (normalized.startsWith(key)) {
5797
5806
  return MODEL_CONTEXT_SIZES[key];
5798
5807
  }
5799
5808
  }
@@ -5846,8 +5855,8 @@ var require_claudeCode = __commonJS({
5846
5855
  Object.defineProperty(exports, "__esModule", { value: true });
5847
5856
  exports.ClaudeCodeProvider = void 0;
5848
5857
  var fs9 = __importStar(__require("fs"));
5849
- var os5 = __importStar(__require("os"));
5850
- var path7 = __importStar(__require("path"));
5858
+ var os6 = __importStar(__require("os"));
5859
+ var path8 = __importStar(__require("path"));
5851
5860
  var jsonl_1 = require_jsonl();
5852
5861
  var sessionPathResolver_1 = require_sessionPathResolver();
5853
5862
  var subagentScanner_1 = require_subagentScanner();
@@ -5984,7 +5993,7 @@ var require_claudeCode = __commonJS({
5984
5993
  return filename.endsWith(".jsonl");
5985
5994
  }
5986
5995
  getSessionId(sessionPath) {
5987
- return path7.basename(sessionPath, ".jsonl");
5996
+ return path8.basename(sessionPath, ".jsonl");
5988
5997
  }
5989
5998
  encodeWorkspacePath(workspacePath) {
5990
5999
  return (0, sessionPathResolver_1.encodeWorkspacePath)(workspacePath);
@@ -6049,7 +6058,7 @@ var require_claudeCode = __commonJS({
6049
6058
  try {
6050
6059
  const content = fs9.readFileSync(sessionPath, "utf8");
6051
6060
  const lines = content.split("\n");
6052
- const projectDir = path7.basename(path7.dirname(sessionPath));
6061
+ const projectDir = path8.basename(path8.dirname(sessionPath));
6053
6062
  const projectPath = (0, sessionPathResolver_1.decodeEncodedPath)(projectDir);
6054
6063
  for (const line of lines) {
6055
6064
  if (results.length >= maxResults)
@@ -6083,11 +6092,11 @@ var require_claudeCode = __commonJS({
6083
6092
  return results;
6084
6093
  }
6085
6094
  getProjectsBaseDir() {
6086
- return path7.join(os5.homedir(), ".claude", "projects");
6095
+ return path8.join(os6.homedir(), ".claude", "projects");
6087
6096
  }
6088
6097
  // --- Stats ---
6089
6098
  readSessionStats(sessionPath) {
6090
- const sessionId = path7.basename(sessionPath, ".jsonl");
6099
+ const sessionId = path8.basename(sessionPath, ".jsonl");
6091
6100
  let messageCount = 0;
6092
6101
  let startTime = "";
6093
6102
  let endTime = "";
@@ -6739,13 +6748,13 @@ var require_openCodeDatabase = __commonJS({
6739
6748
  Object.defineProperty(exports, "__esModule", { value: true });
6740
6749
  exports.OpenCodeDatabase = void 0;
6741
6750
  var fs9 = __importStar(__require("fs"));
6742
- var path7 = __importStar(__require("path"));
6751
+ var path8 = __importStar(__require("path"));
6743
6752
  var child_process_1 = __require("child_process");
6744
6753
  var OpenCodeDatabase = class {
6745
6754
  dbPath;
6746
6755
  runtimeStatus = null;
6747
6756
  constructor(dataDir) {
6748
- this.dbPath = path7.join(dataDir, "opencode.db");
6757
+ this.dbPath = path8.join(dataDir, "opencode.db");
6749
6758
  }
6750
6759
  isAvailable() {
6751
6760
  return fs9.existsSync(this.dbPath);
@@ -6964,7 +6973,7 @@ var require_openCodeDatabase = __commonJS({
6964
6973
  try {
6965
6974
  return fs9.realpathSync(input);
6966
6975
  } catch {
6967
- return path7.resolve(input);
6976
+ return path8.resolve(input);
6968
6977
  }
6969
6978
  }
6970
6979
  function parseStringArray(value) {
@@ -6988,8 +6997,8 @@ var require_openCodeDatabase = __commonJS({
6988
6997
  const normalizedWorkspace = normalizeForCompare(workspacePath);
6989
6998
  if (normalizedCandidate === normalizedWorkspace)
6990
6999
  return 1e4 + normalizedCandidate.length;
6991
- const candidatePrefix = normalizedCandidate.endsWith(path7.sep) ? normalizedCandidate : normalizedCandidate + path7.sep;
6992
- 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;
6993
7002
  if (normalizedWorkspace.startsWith(candidatePrefix))
6994
7003
  return 5e3 + normalizedCandidate.length;
6995
7004
  if (normalizedCandidate.startsWith(workspacePrefix))
@@ -7056,8 +7065,8 @@ var require_openCode = __commonJS({
7056
7065
  Object.defineProperty(exports, "__esModule", { value: true });
7057
7066
  exports.OpenCodeProvider = void 0;
7058
7067
  var fs9 = __importStar(__require("fs"));
7059
- var os5 = __importStar(__require("os"));
7060
- var path7 = __importStar(__require("path"));
7068
+ var os6 = __importStar(__require("os"));
7069
+ var path8 = __importStar(__require("path"));
7061
7070
  var child_process_1 = __require("child_process");
7062
7071
  var openCodeParser_1 = require_openCodeParser();
7063
7072
  var openCodeDatabase_1 = require_openCodeDatabase();
@@ -7065,18 +7074,18 @@ var require_openCode = __commonJS({
7065
7074
  function getOpenCodeDataDir() {
7066
7075
  const xdg = process.env.XDG_DATA_HOME;
7067
7076
  if (xdg) {
7068
- return path7.join(xdg, "opencode");
7077
+ return path8.join(xdg, "opencode");
7069
7078
  }
7070
7079
  if (process.platform === "darwin") {
7071
- return path7.join(os5.homedir(), "Library", "Application Support", "opencode");
7080
+ return path8.join(os6.homedir(), "Library", "Application Support", "opencode");
7072
7081
  }
7073
7082
  if (process.platform === "win32") {
7074
- 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");
7075
7084
  }
7076
- return path7.join(os5.homedir(), ".local", "share", "opencode");
7085
+ return path8.join(os6.homedir(), ".local", "share", "opencode");
7077
7086
  }
7078
7087
  function getStorageDir() {
7079
- return path7.join(getOpenCodeDataDir(), "storage");
7088
+ return path8.join(getOpenCodeDataDir(), "storage");
7080
7089
  }
7081
7090
  var GENERIC_AGENT_RE = /^(gpt-|claude-|o[1-9]|gemini|default|agent|worker)/i;
7082
7091
  function isGenericAgentType(type) {
@@ -7107,18 +7116,18 @@ var require_openCode = __commonJS({
7107
7116
  }
7108
7117
  var DB_SESSION_PREFIX = "db-sessions";
7109
7118
  function makeDbSessionPath(dataDir, projectId, sessionId) {
7110
- return path7.join(dataDir, DB_SESSION_PREFIX, projectId, `${sessionId}.json`);
7119
+ return path8.join(dataDir, DB_SESSION_PREFIX, projectId, `${sessionId}.json`);
7111
7120
  }
7112
7121
  function isDbSessionPath(sessionPath) {
7113
- return sessionPath.includes(path7.sep + DB_SESSION_PREFIX + path7.sep);
7122
+ return sessionPath.includes(path8.sep + DB_SESSION_PREFIX + path8.sep);
7114
7123
  }
7115
7124
  function extractProjectIdFromDbPath(sessionPath) {
7116
- const prefix = path7.sep + DB_SESSION_PREFIX + path7.sep;
7125
+ const prefix = path8.sep + DB_SESSION_PREFIX + path8.sep;
7117
7126
  const idx = sessionPath.indexOf(prefix);
7118
7127
  if (idx < 0)
7119
7128
  return null;
7120
7129
  const rest = sessionPath.substring(idx + prefix.length);
7121
- const slashIdx = rest.indexOf(path7.sep);
7130
+ const slashIdx = rest.indexOf(path8.sep);
7122
7131
  return slashIdx > 0 ? rest.substring(0, slashIdx) : null;
7123
7132
  }
7124
7133
  function extractRoleFromDbMessage(row) {
@@ -7156,25 +7165,25 @@ var require_openCode = __commonJS({
7156
7165
  function resolveProjectIdFromFiles(workspacePath) {
7157
7166
  const workspaceResolved = normalizePath(workspacePath);
7158
7167
  try {
7159
- const projectDir = path7.join(getStorageDir(), "project");
7168
+ const projectDir = path8.join(getStorageDir(), "project");
7160
7169
  if (!fs9.existsSync(projectDir))
7161
7170
  return resolveProjectIdFromGit(workspacePath);
7162
7171
  const files = fs9.readdirSync(projectDir).filter((f) => f.endsWith(".json"));
7163
7172
  const matches = [];
7164
7173
  for (const file of files) {
7165
7174
  try {
7166
- 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"));
7167
7176
  if (project.path) {
7168
7177
  const projectPath = normalizePath(project.path);
7169
7178
  if (projectPath === workspaceResolved) {
7170
7179
  matches.push({ id: project.id, path: projectPath });
7171
7180
  continue;
7172
7181
  }
7173
- if (workspaceResolved.startsWith(projectPath + path7.sep)) {
7182
+ if (workspaceResolved.startsWith(projectPath + path8.sep)) {
7174
7183
  matches.push({ id: project.id, path: projectPath });
7175
7184
  continue;
7176
7185
  }
7177
- if (projectPath.startsWith(workspaceResolved + path7.sep)) {
7186
+ if (projectPath.startsWith(workspaceResolved + path8.sep)) {
7178
7187
  matches.push({ id: project.id, path: projectPath });
7179
7188
  }
7180
7189
  }
@@ -7222,7 +7231,7 @@ var require_openCode = __commonJS({
7222
7231
  try {
7223
7232
  return fs9.realpathSync(input);
7224
7233
  } catch {
7225
- return path7.resolve(input);
7234
+ return path8.resolve(input);
7226
7235
  }
7227
7236
  }
7228
7237
  var OpenCodeFileReader = class {
@@ -7234,7 +7243,7 @@ var require_openCode = __commonJS({
7234
7243
  this.storageBase = getStorageDir();
7235
7244
  }
7236
7245
  readNew() {
7237
- const messageDir = path7.join(this.storageBase, "message", this.sessionId);
7246
+ const messageDir = path8.join(this.storageBase, "message", this.sessionId);
7238
7247
  if (!fs9.existsSync(messageDir))
7239
7248
  return [];
7240
7249
  let messageFiles;
@@ -7245,8 +7254,8 @@ var require_openCode = __commonJS({
7245
7254
  }
7246
7255
  const newEvents = [];
7247
7256
  for (const file of messageFiles) {
7248
- const msgId = path7.basename(file, ".json");
7249
- const messagePath = path7.join(messageDir, file);
7257
+ const msgId = path8.basename(file, ".json");
7258
+ const messagePath = path8.join(messageDir, file);
7250
7259
  const message = readJsonSafe(messagePath);
7251
7260
  if (!message)
7252
7261
  continue;
@@ -7256,11 +7265,11 @@ var require_openCode = __commonJS({
7256
7265
  } catch {
7257
7266
  messageMtimeMs = 0;
7258
7267
  }
7259
- const partDir = path7.join(this.storageBase, "part", msgId);
7268
+ const partDir = path8.join(this.storageBase, "part", msgId);
7260
7269
  let parts = [];
7261
7270
  if (fs9.existsSync(partDir)) {
7262
7271
  try {
7263
- 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);
7264
7273
  } catch {
7265
7274
  }
7266
7275
  }
@@ -7297,7 +7306,7 @@ var require_openCode = __commonJS({
7297
7306
  this.seenMessages.clear();
7298
7307
  }
7299
7308
  exists() {
7300
- return fs9.existsSync(path7.join(this.storageBase, "message", this.sessionId));
7309
+ return fs9.existsSync(path8.join(this.storageBase, "message", this.sessionId));
7301
7310
  }
7302
7311
  flush() {
7303
7312
  }
@@ -7514,7 +7523,7 @@ var require_openCode = __commonJS({
7514
7523
  const db = this.ensureDb();
7515
7524
  if (!db)
7516
7525
  return false;
7517
- const projectId = extractProjectIdFromDbPath(dir + path7.sep + "dummy.json") || path7.basename(dir);
7526
+ const projectId = extractProjectIdFromDbPath(dir + path8.sep + "dummy.json") || path8.basename(dir);
7518
7527
  return projectId.length > 0 && db.hasProject(projectId);
7519
7528
  }
7520
7529
  mapDbSessions(projectId) {
@@ -7539,14 +7548,14 @@ var require_openCode = __commonJS({
7539
7548
  const projectId = resolveProjectId(workspacePath, db, dbStatus);
7540
7549
  if (projectId) {
7541
7550
  if (db) {
7542
- return path7.join(getOpenCodeDataDir(), DB_SESSION_PREFIX, projectId);
7551
+ return path8.join(getOpenCodeDataDir(), DB_SESSION_PREFIX, projectId);
7543
7552
  }
7544
- return path7.join(getStorageDir(), "session", projectId);
7553
+ return path8.join(getStorageDir(), "session", projectId);
7545
7554
  }
7546
7555
  if (dbStatus.kind !== "db_missing") {
7547
- return path7.join(getOpenCodeDataDir(), DB_SESSION_PREFIX);
7556
+ return path8.join(getOpenCodeDataDir(), DB_SESSION_PREFIX);
7548
7557
  }
7549
- return path7.join(getStorageDir(), "session");
7558
+ return path8.join(getStorageDir(), "session");
7550
7559
  }
7551
7560
  discoverSessionDirectory(workspacePath) {
7552
7561
  const db = this.ensureDb();
@@ -7556,13 +7565,13 @@ var require_openCode = __commonJS({
7556
7565
  return null;
7557
7566
  if (db) {
7558
7567
  if (db.hasProject(projectId)) {
7559
- return path7.join(getOpenCodeDataDir(), DB_SESSION_PREFIX, projectId);
7568
+ return path8.join(getOpenCodeDataDir(), DB_SESSION_PREFIX, projectId);
7560
7569
  }
7561
7570
  }
7562
7571
  if (dbStatus.kind !== "db_missing") {
7563
7572
  return null;
7564
7573
  }
7565
- const dir = path7.join(getStorageDir(), "session", projectId);
7574
+ const dir = path8.join(getStorageDir(), "session", projectId);
7566
7575
  return fs9.existsSync(dir) ? dir : null;
7567
7576
  }
7568
7577
  // --- Session discovery ---
@@ -7589,7 +7598,7 @@ var require_openCode = __commonJS({
7589
7598
  return this.findActiveSessionFromFiles(projectId);
7590
7599
  }
7591
7600
  findActiveSessionFromFiles(projectId) {
7592
- const sessionDir = path7.join(getStorageDir(), "session", projectId);
7601
+ const sessionDir = path8.join(getStorageDir(), "session", projectId);
7593
7602
  if (!fs9.existsSync(sessionDir))
7594
7603
  return null;
7595
7604
  let bestPath = null;
@@ -7597,7 +7606,7 @@ var require_openCode = __commonJS({
7597
7606
  try {
7598
7607
  const files = fs9.readdirSync(sessionDir).filter((f) => f.endsWith(".json"));
7599
7608
  for (const file of files) {
7600
- const fullPath = path7.join(sessionDir, file);
7609
+ const fullPath = path8.join(sessionDir, file);
7601
7610
  try {
7602
7611
  const stats = fs9.statSync(fullPath);
7603
7612
  if (stats.size > 0 && stats.mtime.getTime() > bestMtime) {
@@ -7611,14 +7620,14 @@ var require_openCode = __commonJS({
7611
7620
  return null;
7612
7621
  }
7613
7622
  if (bestPath) {
7614
- const sessionId = path7.basename(bestPath, ".json");
7615
- const messageDir = path7.join(getStorageDir(), "message", sessionId);
7623
+ const sessionId = path8.basename(bestPath, ".json");
7624
+ const messageDir = path8.join(getStorageDir(), "message", sessionId);
7616
7625
  if (fs9.existsSync(messageDir)) {
7617
7626
  try {
7618
7627
  const messageFiles = fs9.readdirSync(messageDir).filter((f) => f.endsWith(".json"));
7619
7628
  for (const mf of messageFiles) {
7620
7629
  try {
7621
- const mstat = fs9.statSync(path7.join(messageDir, mf));
7630
+ const mstat = fs9.statSync(path8.join(messageDir, mf));
7622
7631
  if (mstat.mtime.getTime() > bestMtime) {
7623
7632
  bestMtime = mstat.mtime.getTime();
7624
7633
  }
@@ -7648,20 +7657,20 @@ var require_openCode = __commonJS({
7648
7657
  if (dbStatus.kind !== "db_missing") {
7649
7658
  return [];
7650
7659
  }
7651
- const sessionDir = path7.join(getStorageDir(), "session", projectId);
7660
+ const sessionDir = path8.join(getStorageDir(), "session", projectId);
7652
7661
  return this.findSessionsInDirectoryFromFiles(sessionDir);
7653
7662
  }
7654
7663
  findSessionsInDirectory(dir) {
7655
7664
  const db = this.ensureDb();
7656
7665
  const dbStatus = this.getRuntimeStatus();
7657
- if (db && dir.includes(path7.sep + DB_SESSION_PREFIX + path7.sep)) {
7658
- 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");
7659
7668
  if (projectId) {
7660
7669
  return this.mapDbSessions(projectId);
7661
7670
  }
7662
7671
  }
7663
7672
  if (db) {
7664
- const dirName = path7.basename(dir);
7673
+ const dirName = path8.basename(dir);
7665
7674
  if (db.hasProject(dirName)) {
7666
7675
  return this.mapDbSessions(dirName);
7667
7676
  }
@@ -7676,7 +7685,7 @@ var require_openCode = __commonJS({
7676
7685
  if (!fs9.existsSync(dir))
7677
7686
  return [];
7678
7687
  return fs9.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
7679
- const fullPath = path7.join(dir, f);
7688
+ const fullPath = path8.join(dir, f);
7680
7689
  try {
7681
7690
  const stats = fs9.statSync(fullPath);
7682
7691
  return { path: fullPath, mtime: stats.mtime.getTime(), size: stats.size };
@@ -7706,7 +7715,7 @@ var require_openCode = __commonJS({
7706
7715
  if (!projStats || projStats.sessionCount === 0)
7707
7716
  continue;
7708
7717
  folders.push({
7709
- dir: path7.join(dataDir, DB_SESSION_PREFIX, project.id),
7718
+ dir: path8.join(dataDir, DB_SESSION_PREFIX, project.id),
7710
7719
  name: project.worktree || project.name || project.id,
7711
7720
  encodedName: project.id,
7712
7721
  sessionCount: projStats.sessionCount,
@@ -7734,23 +7743,23 @@ var require_openCode = __commonJS({
7734
7743
  }
7735
7744
  getAllProjectFoldersFromFiles(workspacePath) {
7736
7745
  const folders = [];
7737
- const sessionBase = path7.join(getStorageDir(), "session");
7746
+ const sessionBase = path8.join(getStorageDir(), "session");
7738
7747
  try {
7739
7748
  if (!fs9.existsSync(sessionBase))
7740
7749
  return [];
7741
7750
  const projectIds = fs9.readdirSync(sessionBase).filter((name) => {
7742
7751
  try {
7743
- return fs9.statSync(path7.join(sessionBase, name)).isDirectory();
7752
+ return fs9.statSync(path8.join(sessionBase, name)).isDirectory();
7744
7753
  } catch {
7745
7754
  return false;
7746
7755
  }
7747
7756
  });
7748
- const projectDir = path7.join(getStorageDir(), "project");
7757
+ const projectDir = path8.join(getStorageDir(), "project");
7749
7758
  const projectNames = /* @__PURE__ */ new Map();
7750
7759
  if (fs9.existsSync(projectDir)) {
7751
7760
  try {
7752
7761
  for (const file of fs9.readdirSync(projectDir).filter((f) => f.endsWith(".json"))) {
7753
- const proj = readJsonSafe(path7.join(projectDir, file));
7762
+ const proj = readJsonSafe(path8.join(projectDir, file));
7754
7763
  if (proj) {
7755
7764
  projectNames.set(proj.id, proj.path || proj.name || proj.id);
7756
7765
  }
@@ -7763,14 +7772,14 @@ var require_openCode = __commonJS({
7763
7772
  currentProjectId = resolveProjectIdFromFiles(workspacePath);
7764
7773
  }
7765
7774
  for (const projectId of projectIds) {
7766
- const projSessionDir = path7.join(sessionBase, projectId);
7775
+ const projSessionDir = path8.join(sessionBase, projectId);
7767
7776
  let sessionCount = 0;
7768
7777
  let lastModified = /* @__PURE__ */ new Date(0);
7769
7778
  try {
7770
7779
  const sessions = fs9.readdirSync(projSessionDir).filter((f) => f.endsWith(".json"));
7771
7780
  for (const session of sessions) {
7772
7781
  try {
7773
- const fstats = fs9.statSync(path7.join(projSessionDir, session));
7782
+ const fstats = fs9.statSync(path8.join(projSessionDir, session));
7774
7783
  if (fstats.size > 0) {
7775
7784
  sessionCount++;
7776
7785
  if (fstats.mtime > lastModified) {
@@ -7811,7 +7820,7 @@ var require_openCode = __commonJS({
7811
7820
  return filename.endsWith(".json");
7812
7821
  }
7813
7822
  getSessionId(sessionPath) {
7814
- return path7.basename(sessionPath, ".json");
7823
+ return path8.basename(sessionPath, ".json");
7815
7824
  }
7816
7825
  encodeWorkspacePath(workspacePath) {
7817
7826
  const db = this.ensureDb();
@@ -7839,19 +7848,19 @@ var require_openCode = __commonJS({
7839
7848
  if (session?.title) {
7840
7849
  return truncateTitle(session.title);
7841
7850
  }
7842
- const messageDir = path7.join(getStorageDir(), "message", sessionId);
7851
+ const messageDir = path8.join(getStorageDir(), "message", sessionId);
7843
7852
  if (!fs9.existsSync(messageDir))
7844
7853
  return null;
7845
7854
  try {
7846
7855
  const files = fs9.readdirSync(messageDir).filter((f) => f.endsWith(".json")).slice(0, 5);
7847
7856
  for (const file of files) {
7848
- const msg = readJsonSafe(path7.join(messageDir, file));
7857
+ const msg = readJsonSafe(path8.join(messageDir, file));
7849
7858
  if (msg?.role === "user") {
7850
- const partDir = path7.join(getStorageDir(), "part", msg.id);
7859
+ const partDir = path8.join(getStorageDir(), "part", msg.id);
7851
7860
  if (fs9.existsSync(partDir)) {
7852
7861
  const partFiles = fs9.readdirSync(partDir).filter((f) => f.endsWith(".json"));
7853
7862
  for (const pf of partFiles) {
7854
- const part = readJsonSafe(path7.join(partDir, pf));
7863
+ const part = readJsonSafe(path8.join(partDir, pf));
7855
7864
  if (part?.type === "text" && part.text.trim().length > 0) {
7856
7865
  let text = part.text.trim().replace(/\s+/g, " ");
7857
7866
  if (text.length > 60) {
@@ -8035,7 +8044,7 @@ var require_openCode = __commonJS({
8035
8044
  }
8036
8045
  searchInSessionFromFiles(sessionPath, sessionId, queryLower, query, maxResults) {
8037
8046
  const results = [];
8038
- const messageDir = path7.join(getStorageDir(), "message", sessionId);
8047
+ const messageDir = path8.join(getStorageDir(), "message", sessionId);
8039
8048
  if (!fs9.existsSync(messageDir))
8040
8049
  return results;
8041
8050
  try {
@@ -8045,10 +8054,10 @@ var require_openCode = __commonJS({
8045
8054
  for (const file of messageFiles) {
8046
8055
  if (results.length >= maxResults)
8047
8056
  break;
8048
- const msg = readJsonSafe(path7.join(messageDir, file));
8057
+ const msg = readJsonSafe(path8.join(messageDir, file));
8049
8058
  if (!msg)
8050
8059
  continue;
8051
- const partDir = path7.join(getStorageDir(), "part", msg.id);
8060
+ const partDir = path8.join(getStorageDir(), "part", msg.id);
8052
8061
  if (!fs9.existsSync(partDir))
8053
8062
  continue;
8054
8063
  try {
@@ -8056,7 +8065,7 @@ var require_openCode = __commonJS({
8056
8065
  for (const pf of partFiles) {
8057
8066
  if (results.length >= maxResults)
8058
8067
  break;
8059
- const part = readJsonSafe(path7.join(partDir, pf));
8068
+ const part = readJsonSafe(path8.join(partDir, pf));
8060
8069
  if (!part)
8061
8070
  continue;
8062
8071
  let text = "";
@@ -8093,11 +8102,11 @@ var require_openCode = __commonJS({
8093
8102
  return results;
8094
8103
  }
8095
8104
  getProjectsBaseDir() {
8096
- return path7.join(getStorageDir(), "session");
8105
+ return path8.join(getStorageDir(), "session");
8097
8106
  }
8098
8107
  // --- Stats ---
8099
8108
  readSessionStats(sessionPath) {
8100
- const sessionId = path7.basename(sessionPath, ".json");
8109
+ const sessionId = path8.basename(sessionPath, ".json");
8101
8110
  const db = this.ensureDb();
8102
8111
  let messageCount = 0;
8103
8112
  let startTime = "";
@@ -8969,13 +8978,13 @@ var require_codexDatabase = __commonJS({
8969
8978
  Object.defineProperty(exports, "__esModule", { value: true });
8970
8979
  exports.CodexDatabase = void 0;
8971
8980
  var fs9 = __importStar(__require("fs"));
8972
- var path7 = __importStar(__require("path"));
8981
+ var path8 = __importStar(__require("path"));
8973
8982
  var child_process_1 = __require("child_process");
8974
8983
  var CodexDatabase = class {
8975
8984
  dbPath;
8976
8985
  sqlite3Available = null;
8977
8986
  constructor(codexHome) {
8978
- this.dbPath = path7.join(codexHome, "state.sqlite");
8987
+ this.dbPath = path8.join(codexHome, "state.sqlite");
8979
8988
  }
8980
8989
  isAvailable() {
8981
8990
  try {
@@ -9040,7 +9049,7 @@ var require_codexDatabase = __commonJS({
9040
9049
  const all = this.query("SELECT * FROM threads ORDER BY updated_at DESC");
9041
9050
  return all.filter((t) => {
9042
9051
  const threadCwd = normalizePath(t.cwd);
9043
- 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);
9044
9053
  });
9045
9054
  }
9046
9055
  getMostRecentThread(cwd2) {
@@ -9070,7 +9079,7 @@ var require_codexDatabase = __commonJS({
9070
9079
  try {
9071
9080
  return fs9.realpathSync(input);
9072
9081
  } catch {
9073
- return path7.resolve(input);
9082
+ return path8.resolve(input);
9074
9083
  }
9075
9084
  }
9076
9085
  }
@@ -9120,7 +9129,7 @@ var require_codex = __commonJS({
9120
9129
  Object.defineProperty(exports, "__esModule", { value: true });
9121
9130
  exports.CodexProvider = void 0;
9122
9131
  var fs9 = __importStar(__require("fs"));
9123
- var path7 = __importStar(__require("path"));
9132
+ var path8 = __importStar(__require("path"));
9124
9133
  var codexParser_1 = require_codexParser();
9125
9134
  var codexDatabase_1 = require_codexDatabase();
9126
9135
  var codexProfiles_1 = require_codexProfiles();
@@ -9129,13 +9138,13 @@ var require_codex = __commonJS({
9129
9138
  return (0, codexProfiles_1.getCodexMonitoringHomes)();
9130
9139
  }
9131
9140
  function getSessionsDirs() {
9132
- return getCodexHomes().map((home) => path7.join(home, "sessions"));
9141
+ return getCodexHomes().map((home) => path8.join(home, "sessions"));
9133
9142
  }
9134
9143
  function isRolloutFile(filename) {
9135
9144
  return filename.startsWith("rollout-") && filename.endsWith(".jsonl");
9136
9145
  }
9137
9146
  function extractSessionId(filename) {
9138
- const base = path7.basename(filename, ".jsonl");
9147
+ const base = path8.basename(filename, ".jsonl");
9139
9148
  const parts = base.split("-");
9140
9149
  if (parts.length >= 6) {
9141
9150
  const possibleUuid = parts.slice(-5).join("-");
@@ -9159,7 +9168,7 @@ var require_codex = __commonJS({
9159
9168
  return results;
9160
9169
  const entries = fs9.readdirSync(dir, { withFileTypes: true });
9161
9170
  for (const entry of entries) {
9162
- const fullPath = path7.join(dir, entry.name);
9171
+ const fullPath = path8.join(dir, entry.name);
9163
9172
  if (entry.isDirectory()) {
9164
9173
  results.push(...findRolloutFiles(fullPath));
9165
9174
  } else if (entry.isFile() && isRolloutFile(entry.name)) {
@@ -9196,10 +9205,10 @@ var require_codex = __commonJS({
9196
9205
  return dir;
9197
9206
  }
9198
9207
  }
9199
- return dirs[0] ?? path7.join(process.cwd(), ".codex", "sessions");
9208
+ return dirs[0] ?? path8.join(process.cwd(), ".codex", "sessions");
9200
9209
  }
9201
9210
  function getCodexHome() {
9202
- return path7.dirname(getSessionsDir());
9211
+ return path8.dirname(getSessionsDir());
9203
9212
  }
9204
9213
  function isSystemInjection(text) {
9205
9214
  const t = text.trimStart();
@@ -9265,13 +9274,13 @@ var require_codex = __commonJS({
9265
9274
  function cwdMatches(sessionCwd, workspacePath) {
9266
9275
  const normalizedSession = normalizePath(sessionCwd);
9267
9276
  const normalizedWorkspace = normalizePath(workspacePath);
9268
- 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);
9269
9278
  }
9270
9279
  function normalizePath(input) {
9271
9280
  try {
9272
9281
  return fs9.realpathSync(input);
9273
9282
  } catch {
9274
- return path7.resolve(input);
9283
+ return path8.resolve(input);
9275
9284
  }
9276
9285
  }
9277
9286
  function extractFirstUserMessage(rolloutPath) {
@@ -9493,7 +9502,7 @@ var require_codex = __commonJS({
9493
9502
  if (db) {
9494
9503
  const thread = db.getMostRecentThread(workspacePath);
9495
9504
  if (thread?.rollout_path) {
9496
- return path7.dirname(thread.rollout_path);
9505
+ return path8.dirname(thread.rollout_path);
9497
9506
  }
9498
9507
  }
9499
9508
  return getSessionsDir();
@@ -9503,7 +9512,7 @@ var require_codex = __commonJS({
9503
9512
  if (db) {
9504
9513
  const thread = db.getMostRecentThread(workspacePath);
9505
9514
  if (thread?.rollout_path && fs9.existsSync(thread.rollout_path)) {
9506
- return path7.dirname(thread.rollout_path);
9515
+ return path8.dirname(thread.rollout_path);
9507
9516
  }
9508
9517
  }
9509
9518
  const files = findRolloutFilesInConfiguredHomes();
@@ -9513,7 +9522,7 @@ var require_codex = __commonJS({
9513
9522
  for (const file of files) {
9514
9523
  const meta = readSessionMeta(file.path);
9515
9524
  if (meta && cwdMatches(meta.cwd, workspacePath)) {
9516
- return path7.dirname(file.path);
9525
+ return path8.dirname(file.path);
9517
9526
  }
9518
9527
  }
9519
9528
  return getSessionsDir();
@@ -9588,7 +9597,7 @@ var require_codex = __commonJS({
9588
9597
  }
9589
9598
  } else {
9590
9599
  seenCwds.set(meta.cwd, {
9591
- dir: path7.dirname(file.path),
9600
+ dir: path8.dirname(file.path),
9592
9601
  name: meta.cwd,
9593
9602
  encodedName: meta.cwd,
9594
9603
  sessionCount: 1,
@@ -9616,7 +9625,7 @@ var require_codex = __commonJS({
9616
9625
  return isRolloutFile(filename);
9617
9626
  }
9618
9627
  getSessionId(sessionPath) {
9619
- return extractSessionId(path7.basename(sessionPath));
9628
+ return extractSessionId(path8.basename(sessionPath));
9620
9629
  }
9621
9630
  encodeWorkspacePath(workspacePath) {
9622
9631
  return workspacePath;
@@ -9663,7 +9672,7 @@ var require_codex = __commonJS({
9663
9672
  for (const file of filesToCheck) {
9664
9673
  const meta = readSessionMeta(file.path);
9665
9674
  if (meta?.forked_from_id === sessionId) {
9666
- const childId = extractSessionId(path7.basename(file.path));
9675
+ const childId = extractSessionId(path8.basename(file.path));
9667
9676
  const stats = this.buildSubagentStats(childId, file.path, meta.source);
9668
9677
  if (stats)
9669
9678
  subagents.push(stats);
@@ -9775,7 +9784,7 @@ var require_codex = __commonJS({
9775
9784
  }
9776
9785
  // --- Stats ---
9777
9786
  readSessionStats(sessionPath) {
9778
- const sessionId = extractSessionId(path7.basename(sessionPath));
9787
+ const sessionId = extractSessionId(path8.basename(sessionPath));
9779
9788
  let messageCount = 0;
9780
9789
  let startTime = "";
9781
9790
  let endTime = "";
@@ -10117,24 +10126,24 @@ var require_toolSummary = __commonJS({
10117
10126
  if (!pattern)
10118
10127
  return "";
10119
10128
  const glob = input.glob;
10120
- const path7 = input.path;
10129
+ const path8 = input.path;
10121
10130
  const type = input.type;
10122
10131
  const parts = [truncate2(pattern, 40)];
10123
10132
  if (glob)
10124
10133
  parts.push(`in ${glob}`);
10125
10134
  else if (type)
10126
10135
  parts.push(`in *.${type}`);
10127
- else if (path7)
10128
- parts.push(`in ${basename5(path7)}`);
10136
+ else if (path8)
10137
+ parts.push(`in ${basename5(path8)}`);
10129
10138
  return parts.join(" ");
10130
10139
  };
10131
10140
  var formatGlob = (input) => {
10132
10141
  const pattern = input.pattern;
10133
10142
  if (!pattern)
10134
10143
  return "";
10135
- const path7 = input.path;
10136
- if (path7)
10137
- return `${pattern} in ${basename5(path7)}`;
10144
+ const path8 = input.path;
10145
+ if (path8)
10146
+ return `${pattern} in ${basename5(path8)}`;
10138
10147
  return pattern;
10139
10148
  };
10140
10149
  var formatTask = (input) => {
@@ -10443,12 +10452,12 @@ var require_subagentTraceParser = __commonJS({
10443
10452
  Object.defineProperty(exports, "__esModule", { value: true });
10444
10453
  exports.scanSubagentTraces = scanSubagentTraces;
10445
10454
  var fs9 = __importStar(__require("fs"));
10446
- var path7 = __importStar(__require("path"));
10455
+ var path8 = __importStar(__require("path"));
10447
10456
  var toolSummary_1 = require_toolSummary();
10448
10457
  var noiseClassifier_1 = require_noiseClassifier();
10449
10458
  var AGENT_FILE_PATTERN = /^agent-(.+)\.jsonl$/;
10450
10459
  function scanSubagentTraces(sessionDir, sessionId) {
10451
- const subagentsDir = path7.join(sessionDir, sessionId, "subagents");
10460
+ const subagentsDir = path8.join(sessionDir, sessionId, "subagents");
10452
10461
  try {
10453
10462
  if (!fs9.existsSync(subagentsDir))
10454
10463
  return [];
@@ -10459,7 +10468,7 @@ var require_subagentTraceParser = __commonJS({
10459
10468
  if (!match)
10460
10469
  continue;
10461
10470
  const agentId = match[1];
10462
- const filePath = path7.join(subagentsDir, file);
10471
+ const filePath = path8.join(subagentsDir, file);
10463
10472
  const trace = parseAgentTrace(filePath, agentId);
10464
10473
  if (trace)
10465
10474
  traces.push(trace);
@@ -10743,8 +10752,8 @@ var require_debugLogParser = __commonJS({
10743
10752
  exports.collapseDuplicates = collapseDuplicates;
10744
10753
  exports.discoverDebugLogs = discoverDebugLogs;
10745
10754
  var fs9 = __importStar(__require("fs"));
10746
- var path7 = __importStar(__require("path"));
10747
- var os5 = __importStar(__require("os"));
10755
+ var path8 = __importStar(__require("path"));
10756
+ var os6 = __importStar(__require("os"));
10748
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+(.*)$/;
10749
10758
  var LEVEL_ORDER = {
10750
10759
  DEBUG: 0,
@@ -10798,12 +10807,12 @@ var require_debugLogParser = __commonJS({
10798
10807
  return result;
10799
10808
  }
10800
10809
  function discoverDebugLogs() {
10801
- const debugDir = path7.join(os5.homedir(), ".claude", "debug");
10810
+ const debugDir = path8.join(os6.homedir(), ".claude", "debug");
10802
10811
  try {
10803
10812
  if (!fs9.existsSync(debugDir))
10804
10813
  return [];
10805
10814
  return fs9.readdirSync(debugDir).filter((f) => f.endsWith(".log") || f.endsWith(".txt")).map((name) => {
10806
- const fullPath = path7.join(debugDir, name);
10815
+ const fullPath = path8.join(debugDir, name);
10807
10816
  try {
10808
10817
  const stat = fs9.statSync(fullPath);
10809
10818
  return {
@@ -10867,7 +10876,7 @@ var require_sessionSearch = __commonJS({
10867
10876
  Object.defineProperty(exports, "__esModule", { value: true });
10868
10877
  exports.searchSessions = searchSessions2;
10869
10878
  var fs9 = __importStar(__require("fs"));
10870
- var path7 = __importStar(__require("path"));
10879
+ var path8 = __importStar(__require("path"));
10871
10880
  async function searchSessions2(provider, query, opts) {
10872
10881
  const maxResults = opts?.maxResults ?? 50;
10873
10882
  const results = [];
@@ -10886,7 +10895,7 @@ var require_sessionSearch = __commonJS({
10886
10895
  const dir = folder.dir;
10887
10896
  if (fs9.existsSync(dir)) {
10888
10897
  const entries = fs9.readdirSync(dir).filter((f) => f.endsWith(".jsonl") || f.endsWith(".json"));
10889
- sessionFiles = entries.map((f) => path7.join(dir, f));
10898
+ sessionFiles = entries.map((f) => path8.join(dir, f));
10890
10899
  }
10891
10900
  } catch {
10892
10901
  }
@@ -11758,7 +11767,7 @@ var require_sqliteWatcher = __commonJS({
11758
11767
  Object.defineProperty(exports, "__esModule", { value: true });
11759
11768
  exports.SqliteSessionWatcher = void 0;
11760
11769
  var fs9 = __importStar(__require("fs"));
11761
- var path7 = __importStar(__require("path"));
11770
+ var path8 = __importStar(__require("path"));
11762
11771
  var openCodeDatabase_1 = require_openCodeDatabase();
11763
11772
  var openCodeParser_1 = require_openCodeParser();
11764
11773
  var DEBOUNCE_MS = 200;
@@ -11883,7 +11892,7 @@ var require_sqliteWatcher = __commonJS({
11883
11892
  this.dbPath = dbPath;
11884
11893
  this.sessionId = sessionId;
11885
11894
  this.callbacks = callbacks;
11886
- this.db = new openCodeDatabase_1.OpenCodeDatabase(path7.dirname(dbPath));
11895
+ this.db = new openCodeDatabase_1.OpenCodeDatabase(path8.dirname(dbPath));
11887
11896
  }
11888
11897
  get isActive() {
11889
11898
  return this._isActive;
@@ -12048,15 +12057,15 @@ var require_factory = __commonJS({
12048
12057
  }();
12049
12058
  Object.defineProperty(exports, "__esModule", { value: true });
12050
12059
  exports.createWatcher = createWatcher5;
12051
- var os5 = __importStar(__require("os"));
12052
- var path7 = __importStar(__require("path"));
12060
+ var os6 = __importStar(__require("os"));
12061
+ var path8 = __importStar(__require("path"));
12053
12062
  var jsonlWatcher_1 = require_jsonlWatcher();
12054
12063
  var sqliteWatcher_1 = require_sqliteWatcher();
12055
12064
  function getOpenCodeDataDir() {
12056
12065
  const xdg = process.env.XDG_DATA_HOME;
12057
12066
  if (xdg)
12058
- return path7.join(xdg, "opencode");
12059
- return path7.join(os5.homedir(), ".local", "share", "opencode");
12067
+ return path8.join(xdg, "opencode");
12068
+ return path8.join(os6.homedir(), ".local", "share", "opencode");
12060
12069
  }
12061
12070
  function createWatcher5(options) {
12062
12071
  const { provider, workspacePath, sessionId, callbacks } = options;
@@ -12068,7 +12077,7 @@ var require_factory = __commonJS({
12068
12077
  if (sessionId) {
12069
12078
  const match = sessions.find((s) => s.includes(sessionId));
12070
12079
  if (!match) {
12071
- 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(", ")}`);
12072
12081
  }
12073
12082
  sessionPath = match;
12074
12083
  } else {
@@ -12084,8 +12093,8 @@ var require_factory = __commonJS({
12084
12093
  return new jsonlWatcher_1.JsonlSessionWatcher(providerId, sessionPath, callbacks);
12085
12094
  case "opencode": {
12086
12095
  const dataDir = getOpenCodeDataDir();
12087
- const dbPath = path7.join(dataDir, "opencode.db");
12088
- const sid = path7.basename(sessionPath, ".json");
12096
+ const dbPath = path8.join(dataDir, "opencode.db");
12097
+ const sid = path8.basename(sessionPath, ".json");
12089
12098
  return new sqliteWatcher_1.SqliteSessionWatcher(dbPath, sid, callbacks);
12090
12099
  }
12091
12100
  default:
@@ -14480,6 +14489,243 @@ var require_PatternExtractor = __commonJS({
14480
14489
  }
14481
14490
  });
14482
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
+
14483
14729
  // ../sidekick-shared/dist/aggregation/EventAggregator.js
14484
14730
  var require_EventAggregator = __commonJS({
14485
14731
  "../sidekick-shared/dist/aggregation/EventAggregator.js"(exports) {
@@ -14495,6 +14741,7 @@ var require_EventAggregator = __commonJS({
14495
14741
  var FrequencyTracker_1 = require_FrequencyTracker();
14496
14742
  var HeatmapTracker_1 = require_HeatmapTracker();
14497
14743
  var PatternExtractor_1 = require_PatternExtractor();
14744
+ var modelInfo_1 = require_modelInfo();
14498
14745
  var DEFAULT_TIMELINE_CAP = 200;
14499
14746
  var DEFAULT_LATENCY_CAP = 100;
14500
14747
  var DEFAULT_BURN_WINDOW_MS = 5 * 6e4;
@@ -14686,6 +14933,14 @@ var require_EventAggregator = __commonJS({
14686
14933
  this.currentContextSize = contextSize;
14687
14934
  if (event.model) {
14688
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;
14689
14944
  const acc = this.modelUsage.get(model) ?? {
14690
14945
  calls: 0,
14691
14946
  tokens: 0,
@@ -14693,7 +14948,8 @@ var require_EventAggregator = __commonJS({
14693
14948
  outputTokens: 0,
14694
14949
  cacheWriteTokens: 0,
14695
14950
  cacheReadTokens: 0,
14696
- cost: 0
14951
+ cost: 0,
14952
+ priced: true
14697
14953
  };
14698
14954
  acc.calls++;
14699
14955
  acc.tokens += inputTok + outputTok;
@@ -14701,8 +14957,9 @@ var require_EventAggregator = __commonJS({
14701
14957
  acc.outputTokens += outputTok;
14702
14958
  acc.cacheWriteTokens += cacheWrite;
14703
14959
  acc.cacheReadTokens += cacheRead;
14704
- if (event.cost)
14705
- acc.cost += event.cost;
14960
+ acc.cost += computed;
14961
+ if (!evPriced)
14962
+ acc.priced = false;
14706
14963
  this.modelUsage.set(model, acc);
14707
14964
  }
14708
14965
  }
@@ -15003,7 +15260,26 @@ var require_EventAggregator = __commonJS({
15003
15260
  const outputTok = usage.output_tokens;
15004
15261
  const cacheWrite = usage.cache_creation_input_tokens ?? 0;
15005
15262
  const cacheRead = usage.cache_read_input_tokens ?? 0;
15006
- 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
+ }
15007
15283
  this.inputTokens += inputTok;
15008
15284
  this.outputTokens += outputTok;
15009
15285
  this.cacheWriteTokens += cacheWrite;
@@ -15037,15 +15313,17 @@ var require_EventAggregator = __commonJS({
15037
15313
  inputTokens: contextSize,
15038
15314
  turnIndex: this.contextTurnIndex++
15039
15315
  });
15040
- const modelKey = model ?? this.currentModel ?? "unknown";
15041
- const acc = this.modelUsage.get(modelKey) ?? {
15316
+ const perModelKey = model ?? this.currentModel ?? "unknown";
15317
+ const acc = this.modelUsage.get(perModelKey) ?? {
15042
15318
  calls: 0,
15043
15319
  tokens: 0,
15044
15320
  inputTokens: 0,
15045
15321
  outputTokens: 0,
15046
15322
  cacheWriteTokens: 0,
15047
15323
  cacheReadTokens: 0,
15048
- cost: 0
15324
+ reasoningTokens: 0,
15325
+ cost: 0,
15326
+ priced: true
15049
15327
  };
15050
15328
  acc.calls++;
15051
15329
  acc.tokens += inputTok + outputTok;
@@ -15053,8 +15331,11 @@ var require_EventAggregator = __commonJS({
15053
15331
  acc.outputTokens += outputTok;
15054
15332
  acc.cacheWriteTokens += cacheWrite;
15055
15333
  acc.cacheReadTokens += cacheRead;
15334
+ acc.reasoningTokens = (acc.reasoningTokens ?? 0) + reasoningTok;
15056
15335
  acc.cost += cost;
15057
- this.modelUsage.set(modelKey, acc);
15336
+ if (!priced)
15337
+ acc.priced = false;
15338
+ this.modelUsage.set(perModelKey, acc);
15058
15339
  this.updateBurnRate(timestamp);
15059
15340
  }
15060
15341
  // ═══════════════════════════════════════════════════════════════════════
@@ -16106,15 +16387,15 @@ var require_snapshot = __commonJS({
16106
16387
  exports.deleteSnapshot = deleteSnapshot2;
16107
16388
  exports.isSnapshotValid = isSnapshotValid2;
16108
16389
  var fs9 = __importStar(__require("fs"));
16109
- var path7 = __importStar(__require("path"));
16390
+ var path8 = __importStar(__require("path"));
16110
16391
  var paths_1 = require_paths();
16111
16392
  var SNAPSHOT_VERSION = 1;
16112
16393
  function getSnapshotsDir() {
16113
- return path7.join((0, paths_1.getConfigDir)(), "snapshots");
16394
+ return path8.join((0, paths_1.getConfigDir)(), "snapshots");
16114
16395
  }
16115
16396
  function getSnapshotPath(sessionId) {
16116
16397
  const safe = sessionId.replace(/[/\\:]/g, "_");
16117
- return path7.join(getSnapshotsDir(), `${safe}.json`);
16398
+ return path8.join(getSnapshotsDir(), `${safe}.json`);
16118
16399
  }
16119
16400
  function saveSnapshot2(snapshot) {
16120
16401
  try {
@@ -17333,12 +17614,12 @@ var require_credentialIO = __commonJS({
17333
17614
  exports.readActiveCredentials = readActiveCredentials;
17334
17615
  exports.writeActiveCredentials = writeActiveCredentials;
17335
17616
  var fs9 = __importStar(__require("fs"));
17336
- var path7 = __importStar(__require("path"));
17337
- var os5 = __importStar(__require("os"));
17617
+ var path8 = __importStar(__require("path"));
17618
+ var os6 = __importStar(__require("os"));
17338
17619
  var child_process_1 = __require("child_process");
17339
17620
  var KEYCHAIN_SERVICE = "Claude Code-credentials";
17340
17621
  function getCredentialsFilePath() {
17341
- return path7.join(os5.homedir(), ".claude", ".credentials.json");
17622
+ return path8.join(os6.homedir(), ".claude", ".credentials.json");
17342
17623
  }
17343
17624
  function readActiveCredentials() {
17344
17625
  if (process.platform === "darwin") {
@@ -17483,28 +17764,28 @@ var require_accounts = __commonJS({
17483
17764
  exports.getActiveAccount = getActiveAccount3;
17484
17765
  exports.isMultiAccountEnabled = isMultiAccountEnabled;
17485
17766
  var fs9 = __importStar(__require("fs"));
17486
- var path7 = __importStar(__require("path"));
17487
- var os5 = __importStar(__require("os"));
17767
+ var path8 = __importStar(__require("path"));
17768
+ var os6 = __importStar(__require("os"));
17488
17769
  var credentialIO_1 = require_credentialIO();
17489
17770
  var accountRegistry_1 = require_accountRegistry();
17490
17771
  function getClaudeDir() {
17491
- return path7.join(os5.homedir(), ".claude");
17772
+ return path8.join(os6.homedir(), ".claude");
17492
17773
  }
17493
17774
  function getClaudeConfigPath() {
17494
- const primary = path7.join(getClaudeDir(), ".claude.json");
17775
+ const primary = path8.join(getClaudeDir(), ".claude.json");
17495
17776
  try {
17496
17777
  const data = JSON.parse(fs9.readFileSync(primary, "utf8"));
17497
17778
  if (data?.oauthAccount)
17498
17779
  return primary;
17499
17780
  } catch {
17500
17781
  }
17501
- return path7.join(os5.homedir(), ".claude.json");
17782
+ return path8.join(os6.homedir(), ".claude.json");
17502
17783
  }
17503
17784
  function getCredentialsDir() {
17504
- return path7.join((0, accountRegistry_1.getAccountsDir)(), "credentials");
17785
+ return path8.join((0, accountRegistry_1.getAccountsDir)(), "credentials");
17505
17786
  }
17506
17787
  function getConfigsDir() {
17507
- return path7.join((0, accountRegistry_1.getAccountsDir)(), "configs");
17788
+ return path8.join((0, accountRegistry_1.getAccountsDir)(), "configs");
17508
17789
  }
17509
17790
  function ensureDirs() {
17510
17791
  for (const dir of [(0, accountRegistry_1.getAccountsDir)(), getCredentialsDir(), getConfigsDir()]) {
@@ -17587,14 +17868,14 @@ var require_accounts = __commonJS({
17587
17868
  if (existing) {
17588
17869
  if (label !== void 0)
17589
17870
  existing.label = label;
17590
- atomicWriteJson(path7.join(getCredentialsDir(), `${active.uuid}.credentials.json`), credBlob);
17591
- 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);
17592
17873
  registry.activeAccountUuid = active.uuid;
17593
17874
  writeAccountRegistry(registry);
17594
17875
  return { success: true };
17595
17876
  }
17596
- atomicWriteJson(path7.join(getCredentialsDir(), `${active.uuid}.credentials.json`), credBlob);
17597
- 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);
17598
17879
  registry.accounts.push({
17599
17880
  uuid: active.uuid,
17600
17881
  email: active.email,
@@ -17628,7 +17909,7 @@ var require_accounts = __commonJS({
17628
17909
  if (currentActive) {
17629
17910
  if (originalCreds) {
17630
17911
  try {
17631
- atomicWriteJson(path7.join(getCredentialsDir(), `${currentActive.uuid}.credentials.json`), originalCreds);
17912
+ atomicWriteJson(path8.join(getCredentialsDir(), `${currentActive.uuid}.credentials.json`), originalCreds);
17632
17913
  } catch {
17633
17914
  }
17634
17915
  }
@@ -17636,7 +17917,7 @@ var require_accounts = __commonJS({
17636
17917
  try {
17637
17918
  const parsed = JSON.parse(originalConfig);
17638
17919
  if (parsed.oauthAccount) {
17639
- atomicWriteJson(path7.join(getConfigsDir(), `${currentActive.uuid}.config.json`), parsed.oauthAccount);
17920
+ atomicWriteJson(path8.join(getConfigsDir(), `${currentActive.uuid}.config.json`), parsed.oauthAccount);
17640
17921
  }
17641
17922
  } catch {
17642
17923
  }
@@ -17645,12 +17926,12 @@ var require_accounts = __commonJS({
17645
17926
  let targetCreds;
17646
17927
  let targetOauthAccount;
17647
17928
  try {
17648
- 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"));
17649
17930
  } catch {
17650
17931
  return { success: false, error: `Backed-up credentials for ${target.email} not found.` };
17651
17932
  }
17652
17933
  try {
17653
- 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"));
17654
17935
  } catch {
17655
17936
  return { success: false, error: `Backed-up config for ${target.email} not found.` };
17656
17937
  }
@@ -17702,11 +17983,11 @@ var require_accounts = __commonJS({
17702
17983
  return { success: false, error: `Account ${uuid} not found.` };
17703
17984
  }
17704
17985
  try {
17705
- fs9.unlinkSync(path7.join(getCredentialsDir(), `${uuid}.credentials.json`));
17986
+ fs9.unlinkSync(path8.join(getCredentialsDir(), `${uuid}.credentials.json`));
17706
17987
  } catch {
17707
17988
  }
17708
17989
  try {
17709
- fs9.unlinkSync(path7.join(getConfigsDir(), `${uuid}.config.json`));
17990
+ fs9.unlinkSync(path8.join(getConfigsDir(), `${uuid}.config.json`));
17710
17991
  } catch {
17711
17992
  }
17712
17993
  registry.accounts.splice(idx, 1);
@@ -18099,10 +18380,10 @@ var require_quotaSnapshots = __commonJS({
18099
18380
  exports.writeQuotaSnapshot = writeQuotaSnapshot2;
18100
18381
  exports.readQuotaSnapshot = readQuotaSnapshot2;
18101
18382
  var fs9 = __importStar(__require("fs"));
18102
- var path7 = __importStar(__require("path"));
18383
+ var path8 = __importStar(__require("path"));
18103
18384
  var paths_1 = require_paths();
18104
18385
  function getQuotaSnapshotPath() {
18105
- return path7.join((0, paths_1.getConfigDir)(), "quota-snapshots.json");
18386
+ return path8.join((0, paths_1.getConfigDir)(), "quota-snapshots.json");
18106
18387
  }
18107
18388
  function ensureConfigDir() {
18108
18389
  fs9.mkdirSync((0, paths_1.getConfigDir)(), { recursive: true, mode: 448 });
@@ -18194,109 +18475,175 @@ var require_codexQuota = __commonJS({
18194
18475
  }
18195
18476
  });
18196
18477
 
18197
- // ../sidekick-shared/dist/modelInfo.js
18198
- var require_modelInfo = __commonJS({
18199
- "../sidekick-shared/dist/modelInfo.js"(exports) {
18478
+ // ../sidekick-shared/dist/pricingCatalog.js
18479
+ var require_pricingCatalog = __commonJS({
18480
+ "../sidekick-shared/dist/pricingCatalog.js"(exports) {
18200
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
+ }();
18201
18519
  Object.defineProperty(exports, "__esModule", { value: true });
18202
- exports.parseModelId = parseModelId;
18203
- exports.getModelPricing = getModelPricing;
18204
- exports.getModelInfo = getModelInfo2;
18205
- exports.calculateCost = calculateCost;
18206
- exports.calculateCostWithPricing = calculateCostWithPricing;
18207
- exports.formatCost = formatCost5;
18208
- var modelContext_1 = require_modelContext();
18209
- var PRICING_TABLE = {
18210
- "haiku-4.5": {
18211
- inputCostPerMillion: 1,
18212
- outputCostPerMillion: 5,
18213
- cacheWriteCostPerMillion: 1.25,
18214
- cacheReadCostPerMillion: 0.1
18215
- },
18216
- "haiku-3.5": {
18217
- inputCostPerMillion: 0.8,
18218
- outputCostPerMillion: 4,
18219
- cacheWriteCostPerMillion: 1,
18220
- cacheReadCostPerMillion: 0.08
18221
- },
18222
- "sonnet-4.5": {
18223
- inputCostPerMillion: 3,
18224
- outputCostPerMillion: 15,
18225
- cacheWriteCostPerMillion: 3.75,
18226
- cacheReadCostPerMillion: 0.3
18227
- },
18228
- "sonnet-4": {
18229
- inputCostPerMillion: 3,
18230
- outputCostPerMillion: 15,
18231
- cacheWriteCostPerMillion: 3.75,
18232
- cacheReadCostPerMillion: 0.3
18233
- },
18234
- "sonnet-4.6": {
18235
- inputCostPerMillion: 3,
18236
- outputCostPerMillion: 15,
18237
- cacheWriteCostPerMillion: 3.75,
18238
- cacheReadCostPerMillion: 0.3
18239
- },
18240
- "opus-4.5": {
18241
- inputCostPerMillion: 5,
18242
- outputCostPerMillion: 25,
18243
- cacheWriteCostPerMillion: 6.25,
18244
- cacheReadCostPerMillion: 0.5
18245
- },
18246
- "opus-4": {
18247
- inputCostPerMillion: 15,
18248
- outputCostPerMillion: 75,
18249
- cacheWriteCostPerMillion: 18.75,
18250
- cacheReadCostPerMillion: 1.5
18251
- },
18252
- "opus-4.6": {
18253
- inputCostPerMillion: 15,
18254
- outputCostPerMillion: 75,
18255
- cacheWriteCostPerMillion: 18.75,
18256
- 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
+ };
18257
18542
  }
18258
- };
18259
- function parseModelId(modelId) {
18260
- const match = modelId.match(/claude-(haiku|sonnet|opus)-([0-9.]+)/i);
18261
- if (!match)
18262
- return null;
18263
- 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() };
18264
18570
  }
18265
- function getModelPricing(modelId) {
18266
- const parsed = parseModelId(modelId);
18267
- if (!parsed)
18268
- return null;
18269
- const { family, version } = parsed;
18270
- const key = `${family}-${version}`;
18271
- if (PRICING_TABLE[key])
18272
- return PRICING_TABLE[key];
18273
- const familyKeys = Object.keys(PRICING_TABLE).filter((k) => k.startsWith(family)).sort().reverse();
18274
- if (familyKeys.length > 0)
18275
- return PRICING_TABLE[familyKeys[0]];
18276
- 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;
18277
18590
  }
18278
- function getModelInfo2(modelId) {
18279
- 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;
18280
18598
  return {
18281
- family: parsed?.family ?? null,
18282
- version: parsed?.version ?? null,
18283
- contextWindow: (0, modelContext_1.getModelContextWindowSize)(modelId),
18284
- pricing: getModelPricing(modelId)
18599
+ inputCostPerMillion: input * 1e6,
18600
+ outputCostPerMillion: output * 1e6,
18601
+ cacheWriteCostPerMillion: cacheWrite * 1e6,
18602
+ cacheReadCostPerMillion: cacheRead * 1e6
18285
18603
  };
18286
18604
  }
18287
- function calculateCost(usage, modelId) {
18288
- const pricing = getModelPricing(modelId);
18289
- if (!pricing)
18290
- return 0;
18291
- 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);
18292
18610
  }
18293
- function calculateCostWithPricing(usage, pricing) {
18294
- 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
+ }
18295
18622
  }
18296
- function formatCost5(cost) {
18297
- if (cost < 0.01)
18298
- return `$${cost.toFixed(4)}`;
18299
- 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
+ }
18300
18647
  }
18301
18648
  }
18302
18649
  });
@@ -18609,10 +18956,10 @@ var require_util = __commonJS({
18609
18956
  function cloneDef(schema) {
18610
18957
  return mergeDefs(schema._zod.def);
18611
18958
  }
18612
- function getElementAtPath(obj, path7) {
18613
- if (!path7)
18959
+ function getElementAtPath(obj, path8) {
18960
+ if (!path8)
18614
18961
  return obj;
18615
- return path7.reduce((acc, key) => acc?.[key], obj);
18962
+ return path8.reduce((acc, key) => acc?.[key], obj);
18616
18963
  }
18617
18964
  function promiseAllObject(promisesObj) {
18618
18965
  const keys = Object.keys(promisesObj);
@@ -18996,11 +19343,11 @@ var require_util = __commonJS({
18996
19343
  }
18997
19344
  return false;
18998
19345
  }
18999
- function prefixIssues(path7, issues) {
19346
+ function prefixIssues(path8, issues) {
19000
19347
  return issues.map((iss) => {
19001
19348
  var _a;
19002
19349
  (_a = iss).path ?? (_a.path = []);
19003
- iss.path.unshift(path7);
19350
+ iss.path.unshift(path8);
19004
19351
  return iss;
19005
19352
  });
19006
19353
  }
@@ -19225,7 +19572,7 @@ var require_errors = __commonJS({
19225
19572
  }
19226
19573
  function treeifyError(error, mapper = (issue) => issue.message) {
19227
19574
  const result = { errors: [] };
19228
- const processError = (error2, path7 = []) => {
19575
+ const processError = (error2, path8 = []) => {
19229
19576
  var _a, _b;
19230
19577
  for (const issue of error2.issues) {
19231
19578
  if (issue.code === "invalid_union" && issue.errors.length) {
@@ -19235,7 +19582,7 @@ var require_errors = __commonJS({
19235
19582
  } else if (issue.code === "invalid_element") {
19236
19583
  processError({ issues: issue.issues }, issue.path);
19237
19584
  } else {
19238
- const fullpath = [...path7, ...issue.path];
19585
+ const fullpath = [...path8, ...issue.path];
19239
19586
  if (fullpath.length === 0) {
19240
19587
  result.errors.push(mapper(issue));
19241
19588
  continue;
@@ -19267,8 +19614,8 @@ var require_errors = __commonJS({
19267
19614
  }
19268
19615
  function toDotPath(_path) {
19269
19616
  const segs = [];
19270
- const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
19271
- for (const seg of path7) {
19617
+ const path8 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
19618
+ for (const seg of path8) {
19272
19619
  if (typeof seg === "number")
19273
19620
  segs.push(`[${seg}]`);
19274
19621
  else if (typeof seg === "symbol")
@@ -33769,13 +34116,13 @@ var require_from_json_schema = __commonJS({
33769
34116
  if (!ref.startsWith("#")) {
33770
34117
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
33771
34118
  }
33772
- const path7 = ref.slice(1).split("/").filter(Boolean);
33773
- if (path7.length === 0) {
34119
+ const path8 = ref.slice(1).split("/").filter(Boolean);
34120
+ if (path8.length === 0) {
33774
34121
  return ctx.rootSchema;
33775
34122
  }
33776
34123
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
33777
- if (path7[0] === defsKey) {
33778
- const key = path7[1];
34124
+ if (path8[0] === defsKey) {
34125
+ const key = path8[1];
33779
34126
  if (!key || !ctx.defs[key]) {
33780
34127
  throw new Error(`Reference not found: ${ref}`);
33781
34128
  }
@@ -34484,7 +34831,7 @@ var require_dist = __commonJS({
34484
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;
34485
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;
34486
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;
34487
- exports.fetchOpenAIStatus = exports.fetchProviderStatus = exports.permissionModeSchema = exports.sessionEventSchema = exports.sessionMessageSchema = exports.messageUsageSchema = exports.extractToolCalls = exports.extractTokenUsage = exports.formatCost = exports.calculateCostWithPricing = 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;
34488
34835
  var taskPersistence_1 = require_taskPersistence();
34489
34836
  Object.defineProperty(exports, "TASK_PERSISTENCE_SCHEMA_VERSION", { enumerable: true, get: function() {
34490
34837
  return taskPersistence_1.TASK_PERSISTENCE_SCHEMA_VERSION;
@@ -34997,6 +35344,16 @@ var require_dist = __commonJS({
34997
35344
  Object.defineProperty(exports, "formatCost", { enumerable: true, get: function() {
34998
35345
  return modelInfo_1.formatCost;
34999
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
+ } });
35000
35357
  var tokenUsage_1 = require_tokenUsage();
35001
35358
  Object.defineProperty(exports, "extractTokenUsage", { enumerable: true, get: function() {
35002
35359
  return tokenUsage_1.extractTokenUsage;
@@ -36735,7 +37092,8 @@ var init_DashboardState = __esm({
36735
37092
  model: ms.model,
36736
37093
  calls: ms.calls,
36737
37094
  tokens: ms.tokens,
36738
- cost: ms.cost
37095
+ cost: ms.cost,
37096
+ priced: ms.priced
36739
37097
  }));
36740
37098
  const taskMap = /* @__PURE__ */ new Map();
36741
37099
  for (const t of m.taskState.tasks.values()) {
@@ -37240,7 +37598,7 @@ var init_UpdateCheckService = __esm({
37240
37598
  /** Run the update check (one-shot). */
37241
37599
  async check() {
37242
37600
  try {
37243
- const current = "0.17.1";
37601
+ const current = "0.17.3";
37244
37602
  const cached = this.readCache();
37245
37603
  let latest;
37246
37604
  if (cached && Date.now() - cached.checkedAt < CACHE_TTL_MS) {
@@ -38810,7 +39168,8 @@ ${hint}{/grey-fg}`;
38810
39168
  lines.push("", sectionHeader("Model Usage", w2));
38811
39169
  for (const ms of m.modelStats) {
38812
39170
  const modelName = ms.model.length > 20 ? ms.model.substring(0, 17) + "..." : ms.model;
38813
- 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}`);
38814
39173
  }
38815
39174
  }
38816
39175
  const quotaLabel = m.providerId === "codex" ? "Rate Limits" : "Quota";
@@ -51624,10 +51983,10 @@ var require_react_reconciler_development = __commonJS({
51624
51983
  fiber = fiber.next, id--;
51625
51984
  return fiber;
51626
51985
  }
51627
- function copyWithSetImpl(obj, path7, index, value) {
51628
- if (index >= path7.length) return value;
51629
- var key = path7[index], updated = isArrayImpl(obj) ? obj.slice() : assign({}, obj);
51630
- 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);
51631
51990
  return updated;
51632
51991
  }
51633
51992
  function copyWithRename(obj, oldPath, newPath) {
@@ -51654,11 +52013,11 @@ var require_react_reconciler_development = __commonJS({
51654
52013
  );
51655
52014
  return updated;
51656
52015
  }
51657
- function copyWithDeleteImpl(obj, path7, index) {
51658
- var key = path7[index], updated = isArrayImpl(obj) ? obj.slice() : assign({}, obj);
51659
- 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)
51660
52019
  return isArrayImpl(updated) ? updated.splice(key, 1) : delete updated[key], updated;
51661
- updated[key] = copyWithDeleteImpl(obj[key], path7, index + 1);
52020
+ updated[key] = copyWithDeleteImpl(obj[key], path8, index + 1);
51662
52021
  return updated;
51663
52022
  }
51664
52023
  function shouldSuspendImpl() {
@@ -64935,29 +65294,29 @@ var require_react_reconciler_development = __commonJS({
64935
65294
  var didWarnAboutNestedUpdates = false;
64936
65295
  var didWarnAboutFindNodeInStrictMode = {};
64937
65296
  var overrideHookState = null, overrideHookStateDeletePath = null, overrideHookStateRenamePath = null, overrideProps = null, overridePropsDeletePath = null, overridePropsRenamePath = null, scheduleUpdate = null, scheduleRetry = null, setErrorHandler = null, setSuspenseHandler = null;
64938
- overrideHookState = function(fiber, id, path7, value) {
65297
+ overrideHookState = function(fiber, id, path8, value) {
64939
65298
  id = findHook(fiber, id);
64940
- 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));
64941
65300
  };
64942
- overrideHookStateDeletePath = function(fiber, id, path7) {
65301
+ overrideHookStateDeletePath = function(fiber, id, path8) {
64943
65302
  id = findHook(fiber, id);
64944
- 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));
64945
65304
  };
64946
65305
  overrideHookStateRenamePath = function(fiber, id, oldPath, newPath) {
64947
65306
  id = findHook(fiber, id);
64948
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));
64949
65308
  };
64950
- overrideProps = function(fiber, path7, value) {
64951
- fiber.pendingProps = copyWithSetImpl(fiber.memoizedProps, path7, 0, value);
65309
+ overrideProps = function(fiber, path8, value) {
65310
+ fiber.pendingProps = copyWithSetImpl(fiber.memoizedProps, path8, 0, value);
64952
65311
  fiber.alternate && (fiber.alternate.pendingProps = fiber.pendingProps);
64953
- path7 = enqueueConcurrentRenderForLane(fiber, 2);
64954
- null !== path7 && scheduleUpdateOnFiber(path7, fiber, 2);
65312
+ path8 = enqueueConcurrentRenderForLane(fiber, 2);
65313
+ null !== path8 && scheduleUpdateOnFiber(path8, fiber, 2);
64955
65314
  };
64956
- overridePropsDeletePath = function(fiber, path7) {
64957
- fiber.pendingProps = copyWithDeleteImpl(fiber.memoizedProps, path7, 0);
65315
+ overridePropsDeletePath = function(fiber, path8) {
65316
+ fiber.pendingProps = copyWithDeleteImpl(fiber.memoizedProps, path8, 0);
64958
65317
  fiber.alternate && (fiber.alternate.pendingProps = fiber.pendingProps);
64959
- path7 = enqueueConcurrentRenderForLane(fiber, 2);
64960
- null !== path7 && scheduleUpdateOnFiber(path7, fiber, 2);
65318
+ path8 = enqueueConcurrentRenderForLane(fiber, 2);
65319
+ null !== path8 && scheduleUpdateOnFiber(path8, fiber, 2);
64961
65320
  };
64962
65321
  overridePropsRenamePath = function(fiber, oldPath, newPath) {
64963
65322
  fiber.pendingProps = copyWithRename(
@@ -74874,8 +75233,8 @@ var init_ErrorOverview = __esm({
74874
75233
  init_dist3();
74875
75234
  init_Box();
74876
75235
  init_Text();
74877
- cleanupPath = (path7) => {
74878
- return path7?.replace(`file://${cwd()}/`, "");
75236
+ cleanupPath = (path8) => {
75237
+ return path8?.replace(`file://${cwd()}/`, "");
74879
75238
  };
74880
75239
  stackUtils = new import_stack_utils.default({
74881
75240
  cwd: cwd(),
@@ -77857,7 +78216,7 @@ function StatusBar({
77857
78216
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: parseBlessedTags(BRAND_INLINE) }),
77858
78217
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { dimColor: true, children: [
77859
78218
  " v",
77860
- "0.17.1"
78219
+ "0.17.3"
77861
78220
  ] }),
77862
78221
  updateInfo && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { color: "yellow", children: [
77863
78222
  " (v",
@@ -78247,7 +78606,7 @@ function ChangelogOverlay({ entries, scrollOffset }) {
78247
78606
  " ",
78248
78607
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { bold: true, color: "cyan", children: [
78249
78608
  "Terminal Dashboard v",
78250
- "0.17.1"
78609
+ "0.17.3"
78251
78610
  ] }),
78252
78611
  latestDate ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { color: "gray", children: [
78253
78612
  " \u2014 ",
@@ -78569,7 +78928,7 @@ var init_mouse = __esm({
78569
78928
  var CHANGELOG_default;
78570
78929
  var init_CHANGELOG = __esm({
78571
78930
  "CHANGELOG.md"() {
78572
- 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.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';
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## [0.17.3] - 2026-04-17\n\n### Changed\n\n- **Version sync with the VS Code extension**: Republished to keep CLI, extension, and shared-library versions aligned after a cosmetic changelog fix in 0.17.3. No CLI code changes \u2014 functionally identical to 0.17.2\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';
78573
78932
  }
78574
78933
  });
78575
78934
 
@@ -80202,8 +80561,8 @@ async function searchAction(_opts, cmd) {
80202
80561
  const projectPath = globalOpts.project || void 0;
80203
80562
  let projectSlug;
80204
80563
  if (projectPath) {
80205
- const path7 = await import("path");
80206
- const resolved = path7.resolve(projectPath);
80564
+ const path8 = await import("path");
80565
+ const resolved = path8.resolve(projectPath);
80207
80566
  const { encodeWorkspacePath } = await Promise.resolve().then(() => __toESM(require_dist(), 1));
80208
80567
  projectSlug = encodeWorkspacePath(resolved);
80209
80568
  }
@@ -80564,10 +80923,22 @@ function printStatsSummary(history) {
80564
80923
  process.stdout.write(source_default.bold("Model Usage\n"));
80565
80924
  process.stdout.write(source_default.dim("\u2500".repeat(50) + "\n"));
80566
80925
  const sorted = [...at.modelUsage].sort((a, b) => b.calls - a.calls);
80926
+ const unpricedModels = [];
80567
80927
  for (const m of sorted) {
80568
- 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
+ }
80569
80935
  process.stdout.write(` ${source_default.cyan(m.model.padEnd(30))} ${formatNumber(m.calls).padStart(8)} calls${costStr}
80570
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
+ `));
80571
80942
  }
80572
80943
  process.stdout.write("\n");
80573
80944
  }
@@ -81282,6 +81653,8 @@ var init_handoff = __esm({
81282
81653
  });
81283
81654
 
81284
81655
  // src/cli.ts
81656
+ import * as os5 from "node:os";
81657
+ import * as path7 from "node:path";
81285
81658
  function resolveProviderId(opts, defaultProvider = "auto") {
81286
81659
  if (opts.provider && opts.provider !== "auto") {
81287
81660
  return opts.provider;
@@ -81309,8 +81682,12 @@ var init_cli = __esm({
81309
81682
  init_esm();
81310
81683
  import_sidekick_shared32 = __toESM(require_dist(), 1);
81311
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
+ });
81312
81689
  program2 = new Command();
81313
- program2.name("sidekick").description("Query Sidekick project intelligence from the command line").version("0.17.1").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.3").option("--json", "Output as JSON").option("--project <path>", "Override project path (default: cwd)").option("--provider <id>", "Provider: claude-code, opencode, codex, auto (default: auto)");
81314
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) => {
81315
81692
  const { dashboardAction: dashboardAction2 } = await init_dashboard().then(() => dashboard_exports);
81316
81693
  return dashboardAction2(_opts, cmd);