claudish 7.45.0 → 7.47.0

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/index.js +3467 -403
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -729,7 +729,7 @@ var init_onepassword_config = __esm(() => {
729
729
  });
730
730
 
731
731
  // src/version.ts
732
- var VERSION = "7.45.0";
732
+ var VERSION = "7.47.0";
733
733
 
734
734
  // src/logger.ts
735
735
  var exports_logger = {};
@@ -1219,11 +1219,6 @@ function writeJsonlCapped(payload) {
1219
1219
  `);
1220
1220
  }
1221
1221
  }
1222
- function printSlowLine(payload) {
1223
- const top = [...payload.spans].sort((a, b) => b.durMs - a.durMs).slice(0, 3).map((s) => `${s.name} ${fmtDur(s.durMs)}${fmtWaitExec(s.meta)}`);
1224
- const detail = top.length > 0 ? ` \u2014 ${top.join(", ")}` : "";
1225
- seams.stderr(`[claudish] slow start ${fmtDur(payload.totalMs)}${detail} \u2026 full data: ` + `${displayPath(seams.outPath)} (CLAUDISH_STARTUP_TRACE=1 for live detail)`);
1226
- }
1227
1222
  function printTable(payload) {
1228
1223
  seams.stderr(`[claudish] startup trace (${payload.argvKind}) \u2014 total ${fmtDur(payload.totalMs)}` + ` \xB7 auth ${payload.authKind} \xB7 v${payload.version}`);
1229
1224
  seams.stderr(" start dur span");
@@ -1234,7 +1229,7 @@ function printTable(payload) {
1234
1229
  }
1235
1230
  seams.stderr(` metrics: ${displayPath(seams.outPath)}`);
1236
1231
  }
1237
- function finalizeStartupTrace(context, opts = {}) {
1232
+ function finalizeStartupTrace(context, _opts = {}) {
1238
1233
  try {
1239
1234
  if (finalized)
1240
1235
  return;
@@ -1254,8 +1249,6 @@ function finalizeStartupTrace(context, opts = {}) {
1254
1249
  try {
1255
1250
  if (terminalSuppressed) {} else if (traceModeOn()) {
1256
1251
  printTable(payload);
1257
- } else if (totalMs > seams.slowThresholdMs && !opts.quiet) {
1258
- printSlowLine(payload);
1259
1252
  }
1260
1253
  } catch {}
1261
1254
  } catch {}
@@ -1345,6 +1338,20 @@ function isAbandoned(path) {
1345
1338
  return true;
1346
1339
  return Date.now() - holder.at > timing.staleMs;
1347
1340
  }
1341
+ function peerHoldsHandshakeLock() {
1342
+ if (lastHandshake?.heldByUs)
1343
+ return false;
1344
+ const holder = readHolder(currentLockPath());
1345
+ if (!holder || holder.pid === process.pid)
1346
+ return false;
1347
+ if (!holderAlive(holder.pid))
1348
+ return false;
1349
+ if (Date.now() - holder.at > timing.staleMs)
1350
+ return false;
1351
+ if (lastHandshake && holder.at > lastHandshake.startedAt)
1352
+ return false;
1353
+ return true;
1354
+ }
1348
1355
  async function acquire(path) {
1349
1356
  const deadline = Date.now() + timing.timeoutMs;
1350
1357
  let madeDir = false;
@@ -1385,6 +1392,7 @@ function release(path) {
1385
1392
  async function withHandshakeLock(handshake) {
1386
1393
  if (process.env.CLAUDISH_NO_OP_HANDSHAKE_LOCK === "1") {
1387
1394
  trace("bypassed (CLAUDISH_NO_OP_HANDSHAKE_LOCK=1)");
1395
+ lastHandshake = { heldByUs: false, startedAt: Date.now() };
1388
1396
  return handshake();
1389
1397
  }
1390
1398
  const path = currentLockPath();
@@ -1396,6 +1404,7 @@ async function withHandshakeLock(handshake) {
1396
1404
  held = false;
1397
1405
  }
1398
1406
  trace(`${held ? "acquired" : "NOT held (timeout or unwritable)"} after ${Date.now() - t0}ms`);
1407
+ lastHandshake = { heldByUs: held, startedAt: t0 };
1399
1408
  try {
1400
1409
  return await handshake();
1401
1410
  } finally {
@@ -1404,7 +1413,7 @@ async function withHandshakeLock(handshake) {
1404
1413
  trace(`handshake done after ${Date.now() - t0}ms${held ? ", released" : ""}`);
1405
1414
  }
1406
1415
  }
1407
- var DEFAULT_STALE_MS = 120000, DEFAULT_TIMEOUT_MS = 45000, DEFAULT_POLL_MS = 60, timing, lockPath, sleep = (ms) => new Promise((r) => setTimeout(r, ms));
1416
+ var DEFAULT_STALE_MS = 120000, DEFAULT_TIMEOUT_MS = 45000, DEFAULT_POLL_MS = 60, timing, lockPath, sleep = (ms) => new Promise((r) => setTimeout(r, ms)), lastHandshake;
1408
1417
  var init_onepassword_handshake_lock = __esm(() => {
1409
1418
  timing = {
1410
1419
  staleMs: DEFAULT_STALE_MS,
@@ -3897,6 +3906,7 @@ __export(exports_onepassword, {
3897
3906
  wasOpAuthorizationDenied: () => wasOpAuthorizationDenied,
3898
3907
  valueTail: () => valueTail,
3899
3908
  setScreenLockProbe: () => setScreenLockProbe,
3909
+ setPeerLockProbe: () => setPeerLockProbe,
3900
3910
  setLockRetryTiming: () => setLockRetryTiming,
3901
3911
  setAppLockProbe: () => setAppLockProbe,
3902
3912
  resolveSecretsPartial: () => resolveSecretsPartial,
@@ -3920,11 +3930,13 @@ __export(exports_onepassword, {
3920
3930
  listItems: () => listItems,
3921
3931
  isTransientSdkError: () => isTransientSdkError,
3922
3932
  isScreenLocked: () => isScreenLocked,
3933
+ isPeerHoldingPrompt: () => isPeerHoldingPrompt,
3923
3934
  isOpReference: () => isOpReference,
3924
3935
  isOpHydratedVar: () => isOpHydratedVar,
3925
3936
  isLockedDenial: () => isLockedDenial,
3926
3937
  isGlobImport: () => isGlobImport,
3927
3938
  isAppLocked: () => isAppLocked,
3939
+ humanizeOpError: () => humanizeOpError,
3928
3940
  globToRegExp: () => globToRegExp,
3929
3941
  getOpFailures: () => getOpFailures,
3930
3942
  filterGlobFields: () => filterGlobFields,
@@ -3938,6 +3950,7 @@ __export(exports_onepassword, {
3938
3950
  defaultAppLockProbe: () => defaultAppLockProbe,
3939
3951
  currentLockCause: () => currentLockCause,
3940
3952
  collectConfigImports: () => collectConfigImports,
3953
+ clearOpSkip: () => clearOpSkip,
3941
3954
  classifyLockedDenial: () => classifyLockedDenial,
3942
3955
  buildAuthError: () => buildAuthError,
3943
3956
  appLockedFromSettings: () => appLockedFromSettings,
@@ -3971,6 +3984,21 @@ function resetOpFailures() {
3971
3984
  function wasOpAuthorizationDenied() {
3972
3985
  return opSourceFailures.some((f) => /denied authorization/i.test(f.message));
3973
3986
  }
3987
+ function humanizeOpError(err) {
3988
+ const raw = (err instanceof Error ? err.message : String(err)).trim();
3989
+ if (/denied authorization/i.test(raw)) {
3990
+ const cause = currentLockCause();
3991
+ if (cause === "screen")
3992
+ return "your Mac is locked, so the approval prompt couldn't be shown";
3993
+ if (cause === "app")
3994
+ return "the 1Password app is locked, so it declined without prompting";
3995
+ if (cause === "peer")
3996
+ return "another claudish process is holding the 1Password prompt";
3997
+ return "the 1Password approval prompt was dismissed (or never answered)";
3998
+ }
3999
+ const unwrapped = raw.match(/Error\s*\{\s*msg:\s*(.*?)(?:,\s*inner:|\s*\})/s);
4000
+ return (unwrapped?.[1] ?? raw).trim();
4001
+ }
3974
4002
  function renderOpFailureNotice(envVar) {
3975
4003
  return renderOpFailureBlock(`for ${envVar} this run`);
3976
4004
  }
@@ -3997,6 +4025,12 @@ function renderOpFailureBlock(subject) {
3997
4025
  lines.push("");
3998
4026
  lines.push(" Fix: unlock 1Password (Touch ID is enough) and re-run.");
3999
4027
  lines.push(" To stop it re-locking mid-session, raise Settings \u2192 Security \u2192 auto-lock.");
4028
+ } else if (cause === "peer") {
4029
+ lines.push(" Another claudish process is at the 1Password prompt right now. 1Password");
4030
+ lines.push(" authorizes ONE client at a time and denies every peer instantly, so this");
4031
+ lines.push(" run lost a race rather than being refused.");
4032
+ lines.push("");
4033
+ lines.push(" Fix: approve the prompt in the other window, then re-run this one.");
4000
4034
  } else {
4001
4035
  lines.push(" The 1Password desktop app declined to release secrets. The approval prompt");
4002
4036
  lines.push(" was most likely dismissed.");
@@ -4373,11 +4407,19 @@ function setAppLockProbe(probe) {
4373
4407
  function isAppLocked() {
4374
4408
  return appLockProbe();
4375
4409
  }
4410
+ function setPeerLockProbe(probe) {
4411
+ peerLockProbe = probe ?? peerHoldsHandshakeLock;
4412
+ }
4413
+ function isPeerHoldingPrompt() {
4414
+ return peerLockProbe();
4415
+ }
4376
4416
  function currentLockCause() {
4377
4417
  if (isScreenLocked())
4378
4418
  return "screen";
4379
4419
  if (isAppLocked())
4380
4420
  return "app";
4421
+ if (isPeerHoldingPrompt())
4422
+ return "peer";
4381
4423
  return null;
4382
4424
  }
4383
4425
  function classifyLockedDenial(err, env = process.env) {
@@ -4395,23 +4437,28 @@ async function countdownForUnlock(round, rounds, cause) {
4395
4437
  const ttyOut = process.stderr.isTTY === true;
4396
4438
  const ttyIn = process.stdin.isTTY === true;
4397
4439
  let cancelled = false;
4440
+ let skipped = false;
4398
4441
  if (round === 1) {
4399
4442
  const explain = cause === "screen" ? `${bold("\uD83D\uDD10 1Password needs your OK \u2014 but your Mac is locked, so it can't ask.")}
4400
- Unlock your Mac and approve the popup. Claudish picks it up from there.` : `${bold("\uD83D\uDD10 1Password is locked, so it turned claudish away without asking you.")}
4443
+ Unlock your Mac and approve the popup. Claudish picks it up from there.` : cause === "app" ? `${bold("\uD83D\uDD10 1Password is locked, so it turned claudish away without asking you.")}
4401
4444
  Unlock 1Password (Touch ID is enough). Claudish retries on its own \u2014
4402
- no popup will appear until it's unlocked.`;
4445
+ no popup will appear until it's unlocked.` : `${bold("\uD83D\uDD10 Another claudish is already at the 1Password prompt.")}
4446
+ 1Password only lets one through at a time, so this run has to queue.
4447
+ Approve it in the other window (or wait \u2014 it usually takes a second).`;
4403
4448
  process.stderr.write(`
4404
4449
  ${explain}
4405
4450
 
4406
4451
  `);
4407
4452
  }
4408
- const stillLocked = () => cause === "screen" ? isScreenLocked() : isAppLocked();
4453
+ const stillLocked = () => cause === "screen" ? isScreenLocked() : cause === "app" ? isAppLocked() : isPeerHoldingPrompt();
4409
4454
  let restoreInput = () => {};
4410
4455
  if (ttyIn) {
4411
4456
  const onKey = (buf) => {
4412
4457
  const k = buf.toString();
4413
4458
  if (k === "\x1B" || k === "q" || k === "\x03")
4414
4459
  cancelled = true;
4460
+ if (k === "s" || k === "S")
4461
+ skipped = true;
4415
4462
  };
4416
4463
  const wasRaw = process.stdin.isRaw === true;
4417
4464
  try {
@@ -4427,14 +4474,20 @@ ${explain}
4427
4474
  restoreInput = () => {};
4428
4475
  }
4429
4476
  }
4430
- const stop = ttyIn ? "Esc to stop waiting" : "Ctrl-C to stop waiting";
4431
- const line = (secs) => dim(` waiting ${secs}s \xB7 try ${round} of ${rounds} \xB7 ${stop}`);
4477
+ const stop = ttyIn ? "Esc stop waiting" : "Ctrl-C stop waiting";
4478
+ const skip = ttyIn ? " \xB7 s skip 1Password" : "";
4479
+ const bar = (secs) => {
4480
+ const total = Math.max(1, lockRetrySeconds);
4481
+ const filled = Math.round((total - secs) / total * 10);
4482
+ return `${"\u2501".repeat(filled)}${"\u2500".repeat(Math.max(0, 10 - filled))}`;
4483
+ };
4484
+ const line = (secs) => dim(` ${bar(secs)} ${secs}s \xB7 try ${round}/${rounds} \xB7 ${stop}${skip}`);
4432
4485
  if (!ttyOut)
4433
4486
  process.stderr.write(`${line(lockRetrySeconds)}
4434
4487
  `);
4435
4488
  try {
4436
4489
  for (let remaining = lockRetrySeconds;remaining > 0; remaining--) {
4437
- if (cancelled)
4490
+ if (cancelled || skipped)
4438
4491
  break;
4439
4492
  if (!stillLocked())
4440
4493
  break;
@@ -4447,6 +4500,12 @@ ${explain}
4447
4500
  if (ttyOut)
4448
4501
  process.stderr.write("\r\x1B[2K");
4449
4502
  }
4503
+ if (skipped) {
4504
+ process.stderr.write(` Skipping 1Password for this run. Keys already in your environment still work;
4505
+ ` + ` anything only 1Password has will report as missing.
4506
+ `);
4507
+ return "skip";
4508
+ }
4450
4509
  if (cancelled) {
4451
4510
  process.stderr.write(` OK, not waiting. You can unlock and re-run any time.
4452
4511
  `);
@@ -4454,6 +4513,17 @@ ${explain}
4454
4513
  }
4455
4514
  return "retry";
4456
4515
  }
4516
+ function latchOpSkip() {
4517
+ opSkipLatchedByPrompt = true;
4518
+ process.env.CLAUDISH_DISABLE_OP = "1";
4519
+ }
4520
+ function clearOpSkip() {
4521
+ if (!opSkipLatchedByPrompt)
4522
+ return false;
4523
+ opSkipLatchedByPrompt = false;
4524
+ delete process.env.CLAUDISH_DISABLE_OP;
4525
+ return true;
4526
+ }
4457
4527
  async function withSdkRetry(op, label = "op:sdk-op") {
4458
4528
  for (let round = 1;; round++) {
4459
4529
  try {
@@ -4462,7 +4532,12 @@ async function withSdkRetry(op, label = "op:sdk-op") {
4462
4532
  const cause = classifyLockedDenial(err);
4463
4533
  if (round > LOCK_RETRY_ROUNDS || cause === null)
4464
4534
  throw err;
4465
- if (await countdownForUnlock(round, LOCK_RETRY_ROUNDS, cause) === "cancel")
4535
+ const choice = await countdownForUnlock(round, LOCK_RETRY_ROUNDS, cause);
4536
+ if (choice === "skip") {
4537
+ latchOpSkip();
4538
+ throw err;
4539
+ }
4540
+ if (choice === "cancel")
4466
4541
  throw err;
4467
4542
  resetSdkClientCache();
4468
4543
  }
@@ -4680,7 +4755,7 @@ var OP_REF_RE, opHydratedVars, opSourceFailures, ENV_VAR_NAME_RE, sdkClientCache
4680
4755
  } catch {
4681
4756
  return false;
4682
4757
  }
4683
- }, screenLockProbe, defaultAppLockProbe = () => false, appLockProbe, OP_PROBE_TIMEOUT_MS = 5000, defaultOpAccountLister = () => {
4758
+ }, screenLockProbe, defaultAppLockProbe = () => false, appLockProbe, peerLockProbe, opSkipLatchedByPrompt = false, OP_PROBE_TIMEOUT_MS = 5000, defaultOpAccountLister = () => {
4684
4759
  try {
4685
4760
  const res = spawnSync("op", ["--cache=false", "account", "list", "--format=json"], {
4686
4761
  encoding: "utf-8",
@@ -4723,6 +4798,7 @@ var init_onepassword = __esm(() => {
4723
4798
  lockRetrySeconds = LOCK_RETRY_SECONDS;
4724
4799
  screenLockProbe = defaultScreenLockProbe;
4725
4800
  appLockProbe = defaultAppLockProbe;
4801
+ peerLockProbe = peerHoldsHandshakeLock;
4726
4802
  });
4727
4803
 
4728
4804
  // src/auth/credentials/op-source.ts
@@ -4859,6 +4935,8 @@ function readConfigRaw() {
4859
4935
  }
4860
4936
  }
4861
4937
  function hasOpSources() {
4938
+ if (process.env.CLAUDISH_DISABLE_OP === "1")
4939
+ return false;
4862
4940
  if (sniffed !== undefined)
4863
4941
  return sniffed;
4864
4942
  sniffed = computeHasOpSources();
@@ -5137,7 +5215,8 @@ async function resolveOpKeyForEnvVarsInner(wanted, opts = {}, span) {
5137
5215
  resolveSecrets: resolveSecrets2,
5138
5216
  recordOpHydratedVars: recordOpHydratedVars2,
5139
5217
  recordOpFailure: recordOpFailure2,
5140
- withSdkRetry: withSdkRetry2
5218
+ withSdkRetry: withSdkRetry2,
5219
+ humanizeOpError: humanizeOpError2
5141
5220
  } = await Promise.resolve().then(() => (init_onepassword(), exports_onepassword));
5142
5221
  const cfg = readConfigRaw();
5143
5222
  const out = {};
@@ -5198,7 +5277,7 @@ async function resolveOpKeyForEnvVarsInner(wanted, opts = {}, span) {
5198
5277
  }
5199
5278
  } catch (globErr) {
5200
5279
  const m = globErr instanceof Error ? globErr.message : String(globErr);
5201
- warnOnce(`[claudish] 1Password import skipped: ${m}`);
5280
+ warnOnce(`[claudish] 1Password import skipped: ${humanizeOpError2(globErr)}`);
5202
5281
  recordOpFailure2({ kind: "import", source: globPath, message: m });
5203
5282
  }
5204
5283
  }
@@ -5239,7 +5318,7 @@ async function resolveOpKeyForEnvVarsInner(wanted, opts = {}, span) {
5239
5318
  }
5240
5319
  } catch (envErr) {
5241
5320
  const m = envErr instanceof Error ? envErr.message : String(envErr);
5242
- warnOnce(`[claudish] 1Password environment skipped: ${m}`);
5321
+ warnOnce(`[claudish] 1Password environment skipped: ${humanizeOpError2(envErr)}`);
5243
5322
  recordOpFailure2({ kind: "environment", source: envId, message: m });
5244
5323
  }
5245
5324
  }
@@ -5251,7 +5330,7 @@ async function resolveOpKeyForEnvVarsInner(wanted, opts = {}, span) {
5251
5330
  return out;
5252
5331
  }
5253
5332
  const message = err instanceof Error ? err.message : String(err);
5254
- warnOnce(`[claudish] 1Password secret resolution failed: ${message}`);
5333
+ warnOnce(`[claudish] 1Password secret resolution failed: ${humanizeOpError2(err)}`);
5255
5334
  recordOpFailure2({ kind: "reference", message });
5256
5335
  if (onAuthFailure === "throw")
5257
5336
  throw err;
@@ -28811,7 +28890,9 @@ var init_remote_provider_types = __esm(() => {
28811
28890
  "kimi-coding",
28812
28891
  "glm-coding",
28813
28892
  "qwen-cloud",
28814
- "devin"
28893
+ "devin",
28894
+ "antigravity",
28895
+ "sakana-subscription"
28815
28896
  ]);
28816
28897
  PROVIDER_ALIAS = {
28817
28898
  google: "gemini",
@@ -29136,6 +29217,11 @@ function removeUriFormat(schema) {
29136
29217
  }
29137
29218
  return result;
29138
29219
  }
29220
+ function stripBillingHeader(text) {
29221
+ if (!text || !text.includes("x-anthropic-billing-header"))
29222
+ return text;
29223
+ return text.replace(BILLING_HEADER_RE, "");
29224
+ }
29139
29225
  function transformOpenAIToClaude(claudeRequestInput) {
29140
29226
  const req = JSON.parse(JSON.stringify(claudeRequestInput));
29141
29227
  const isO3Model = typeof req.model === "string" && (req.model.includes("o3") || req.model.includes("o1"));
@@ -29159,9 +29245,11 @@ function transformOpenAIToClaude(claudeRequestInput) {
29159
29245
  }
29160
29246
  }
29161
29247
  return JSON.stringify(item);
29162
- }).filter((text) => text && text.trim() !== "").join(`
29248
+ }).map((text) => stripBillingHeader(text)).filter((text) => text && text.trim() !== "").join(`
29163
29249
 
29164
29250
  `);
29251
+ } else if (typeof req.system === "string") {
29252
+ req.system = stripBillingHeader(req.system);
29165
29253
  }
29166
29254
  if (!Array.isArray(req.messages)) {
29167
29255
  if (req.messages == null)
@@ -29183,7 +29271,10 @@ function transformOpenAIToClaude(claudeRequestInput) {
29183
29271
  isO3Model
29184
29272
  };
29185
29273
  }
29186
- var init_transform = () => {};
29274
+ var BILLING_HEADER_RE;
29275
+ var init_transform = __esm(() => {
29276
+ BILLING_HEADER_RE = /x-anthropic-billing-header:[^\n]*\n?/gi;
29277
+ });
29187
29278
 
29188
29279
  // src/handlers/shared/format/openai-tools.ts
29189
29280
  function emptyParamsSchema() {
@@ -29911,23 +30002,33 @@ import { execFileSync } from "child_process";
29911
30002
  import { existsSync as existsSync7 } from "fs";
29912
30003
  import { homedir as homedir9 } from "os";
29913
30004
  import { join as join9 } from "path";
30005
+ function invalidateReadStoreMemo() {
30006
+ cachedRawStore = null;
30007
+ }
29914
30008
  function defaultReadStore() {
29915
30009
  if (process.platform !== "darwin") {
29916
30010
  logStderr("[Antigravity] Shared token store is macOS-only for now (other keyring backends are a follow-up).");
29917
30011
  return null;
29918
30012
  }
30013
+ const now = Date.now();
30014
+ if (cachedRawStore && now - cachedRawStore.at < READ_STORE_TTL_MS)
30015
+ return cachedRawStore.value;
30016
+ let value = null;
29919
30017
  try {
29920
30018
  const out = execFileSync("security", ["find-generic-password", "-s", KC_SERVICE, "-a", KC_ACCOUNT, "-w"], { encoding: "utf8" });
29921
30019
  const trimmed2 = out.trim();
29922
- return trimmed2.length > 0 ? trimmed2 : null;
30020
+ value = trimmed2.length > 0 ? trimmed2 : null;
29923
30021
  } catch {
29924
- return null;
30022
+ value = null;
29925
30023
  }
30024
+ cachedRawStore = { at: now, value };
30025
+ return value;
29926
30026
  }
29927
30027
  function defaultWriteStore(rawValue) {
29928
30028
  if (process.platform !== "darwin") {
29929
30029
  throw new Error("[Antigravity] Cannot write the shared token store on a non-macOS platform.");
29930
30030
  }
30031
+ invalidateReadStoreMemo();
29931
30032
  execFileSync("security", ["add-generic-password", "-U", "-s", KC_SERVICE, "-a", KC_ACCOUNT, "-w", rawValue], { stdio: ["ignore", "ignore", "ignore"] });
29932
30033
  }
29933
30034
  function locateAgyBinary() {
@@ -29942,6 +30043,7 @@ function locateAgyBinary() {
29942
30043
  function defaultDeleteStore() {
29943
30044
  if (process.platform !== "darwin")
29944
30045
  return;
30046
+ invalidateReadStoreMemo();
29945
30047
  try {
29946
30048
  execFileSync("security", ["delete-generic-password", "-s", KC_SERVICE, "-a", KC_ACCOUNT], {
29947
30049
  stdio: ["ignore", "ignore", "ignore"]
@@ -29957,7 +30059,9 @@ function defaultRunAgyRefresh() {
29957
30059
  stdio: ["ignore", "ignore", "ignore"],
29958
30060
  timeout: AGY_REFRESH_TIMEOUT_MS
29959
30061
  });
29960
- } catch {}
30062
+ } catch {} finally {
30063
+ invalidateReadStoreMemo();
30064
+ }
29961
30065
  }
29962
30066
  function parseRecord(raw) {
29963
30067
  if (!raw)
@@ -30035,8 +30139,9 @@ function getValidAntigravityAccessToken(deps = defaultDeps) {
30035
30139
  function _resetAntigravityTokenState() {
30036
30140
  inFlight = null;
30037
30141
  cachedHasToken = null;
30142
+ invalidateReadStoreMemo();
30038
30143
  }
30039
- var KC_SERVICE = "gemini", KC_ACCOUNT = "antigravity", PREFIX = "go-keyring-base64:", EXPIRY_SKEW_MS = 120000, AGY_REFRESH_TIMEOUT_MS = 40000, defaultDeps, cachedHasToken = null, HAS_TOKEN_TTL_MS = 5000, inFlight = null;
30144
+ var KC_SERVICE = "gemini", KC_ACCOUNT = "antigravity", PREFIX = "go-keyring-base64:", EXPIRY_SKEW_MS = 120000, AGY_REFRESH_TIMEOUT_MS = 40000, READ_STORE_TTL_MS = 3000, cachedRawStore = null, defaultDeps, cachedHasToken = null, HAS_TOKEN_TTL_MS = 5000, inFlight = null;
30040
30145
  var init_antigravity_token = __esm(() => {
30041
30146
  init_logger();
30042
30147
  defaultDeps = {
@@ -33042,6 +33147,13 @@ function createStreamingResponseHandler(c, response, adapter, target, middleware
33042
33147
  async start(controller) {
33043
33148
  const send = (e, d) => {
33044
33149
  if (!isClosed) {
33150
+ if (e === "content_block_start" && d?.content_block?.type === "tool_use") {
33151
+ try {
33152
+ behavior?.onToolCallObserved?.(String(d.content_block.name ?? ""));
33153
+ } catch (err) {
33154
+ log(`[Streaming] onToolCallObserved threw: ${err}`);
33155
+ }
33156
+ }
33045
33157
  controller.enqueue(encoder.encode(`event: ${e}
33046
33158
  data: ${JSON.stringify(d)}
33047
33159
 
@@ -33069,54 +33181,98 @@ data: ${JSON.stringify(d)}
33069
33181
  send("ping", { type: "ping" });
33070
33182
  }
33071
33183
  }, 1000);
33184
+ const teardown = () => {
33185
+ if (!isClosed) {
33186
+ try {
33187
+ controller.enqueue(encoder.encode(`data: [DONE]
33188
+
33189
+
33190
+ `));
33191
+ } catch {}
33192
+ try {
33193
+ controller.close();
33194
+ } catch {}
33195
+ isClosed = true;
33196
+ }
33197
+ if (ping) {
33198
+ clearInterval(ping);
33199
+ ping = null;
33200
+ }
33201
+ };
33072
33202
  const finalize = async (reason, err) => {
33073
- if (state.finalized)
33203
+ if (state.finalized) {
33204
+ teardown();
33074
33205
  return;
33206
+ }
33075
33207
  state.finalized = true;
33076
- if (state.accumulatedText.length > 0) {
33077
- const preview = state.accumulatedText.slice(0, 500).replace(/\n/g, "\\n");
33078
- log(`[Streaming] Accumulated text (${state.accumulatedText.length} chars): ${preview}...`);
33079
- }
33080
- const textToolCalls = extractToolCallsFromText(state.accumulatedText);
33081
- log(`[Streaming] Text-based tool calls found: ${textToolCalls.length}`);
33082
- if (textToolCalls.length > 0) {
33083
- log(`[Streaming] Found ${textToolCalls.length} text-based tool call(s), converting to structured format`);
33208
+ try {
33209
+ if (state.accumulatedText.length > 0) {
33210
+ const preview = state.accumulatedText.slice(0, 500).replace(/\n/g, "\\n");
33211
+ log(`[Streaming] Accumulated text (${state.accumulatedText.length} chars): ${preview}...`);
33212
+ }
33213
+ const textToolCalls = extractToolCallsFromText(state.accumulatedText);
33214
+ log(`[Streaming] Text-based tool calls found: ${textToolCalls.length}`);
33215
+ if (textToolCalls.length > 0) {
33216
+ log(`[Streaming] Found ${textToolCalls.length} text-based tool call(s), converting to structured format`);
33217
+ if (state.textStarted) {
33218
+ send("content_block_stop", { type: "content_block_stop", index: state.textIdx });
33219
+ state.textStarted = false;
33220
+ }
33221
+ for (const tc of textToolCalls) {
33222
+ const toolIdx = state.curIdx++;
33223
+ const toolId = `tool_${Date.now()}_${toolIdx}`;
33224
+ send("content_block_start", {
33225
+ type: "content_block_start",
33226
+ index: toolIdx,
33227
+ content_block: { type: "tool_use", id: toolId, name: tc.name }
33228
+ });
33229
+ send("content_block_delta", {
33230
+ type: "content_block_delta",
33231
+ index: toolIdx,
33232
+ delta: {
33233
+ type: "input_json_delta",
33234
+ partial_json: repairArgs(tc.name, JSON.stringify(tc.arguments))
33235
+ }
33236
+ });
33237
+ send("content_block_stop", { type: "content_block_stop", index: toolIdx });
33238
+ }
33239
+ }
33240
+ if (state.reasoningStarted) {
33241
+ send("content_block_stop", { type: "content_block_stop", index: state.reasoningIdx });
33242
+ }
33084
33243
  if (state.textStarted) {
33085
33244
  send("content_block_stop", { type: "content_block_stop", index: state.textIdx });
33086
- state.textStarted = false;
33087
33245
  }
33088
- for (const tc of textToolCalls) {
33089
- const toolIdx = state.curIdx++;
33090
- const toolId = `tool_${Date.now()}_${toolIdx}`;
33091
- send("content_block_start", {
33092
- type: "content_block_start",
33093
- index: toolIdx,
33094
- content_block: { type: "tool_use", id: toolId, name: tc.name }
33095
- });
33096
- send("content_block_delta", {
33097
- type: "content_block_delta",
33098
- index: toolIdx,
33099
- delta: {
33100
- type: "input_json_delta",
33101
- partial_json: repairArgs(tc.name, JSON.stringify(tc.arguments))
33102
- }
33103
- });
33104
- send("content_block_stop", { type: "content_block_stop", index: toolIdx });
33105
- }
33106
- }
33107
- if (state.reasoningStarted) {
33108
- send("content_block_stop", { type: "content_block_stop", index: state.reasoningIdx });
33109
- }
33110
- if (state.textStarted) {
33111
- send("content_block_stop", { type: "content_block_stop", index: state.textIdx });
33112
- }
33113
- for (const t of Array.from(state.tools.values())) {
33114
- if (!t.closed && t.buffered && !t.started) {
33115
- if (toolSchemas && toolSchemas.length > 0) {
33116
- const validation = validateToolArguments(t.name, t.arguments, toolSchemas, state.accumulatedText);
33117
- if (validation.valid || validation.repaired && validation.repairedArgs) {
33118
- const argsJson = repairArgs(t.name, JSON.stringify(validation.repaired ? validation.repairedArgs : validation.parsedArgs));
33119
- log(`[Streaming] Sending buffered tool call (finish_reason!=tool_calls): ${t.name} with args: ${argsJson}`);
33246
+ for (const t of Array.from(state.tools.values())) {
33247
+ if (!t.closed && t.buffered && !t.started) {
33248
+ if (toolSchemas && toolSchemas.length > 0) {
33249
+ const validation = validateToolArguments(t.name, t.arguments, toolSchemas, state.accumulatedText);
33250
+ if (validation.valid || validation.repaired && validation.repairedArgs) {
33251
+ const argsJson = repairArgs(t.name, JSON.stringify(validation.repaired ? validation.repairedArgs : validation.parsedArgs));
33252
+ log(`[Streaming] Sending buffered tool call (finish_reason!=tool_calls): ${t.name} with args: ${argsJson}`);
33253
+ send("content_block_start", {
33254
+ type: "content_block_start",
33255
+ index: t.blockIndex,
33256
+ content_block: { type: "tool_use", id: t.id, name: t.name }
33257
+ });
33258
+ send("content_block_delta", {
33259
+ type: "content_block_delta",
33260
+ index: t.blockIndex,
33261
+ delta: { type: "input_json_delta", partial_json: argsJson }
33262
+ });
33263
+ send("content_block_stop", {
33264
+ type: "content_block_stop",
33265
+ index: t.blockIndex
33266
+ });
33267
+ t.started = true;
33268
+ t.closed = true;
33269
+ } else {
33270
+ log(`[Streaming] Buffered tool call ${t.name} failed validation, skipping: ${validation.missingParams.join(", ")}`);
33271
+ t.closed = true;
33272
+ }
33273
+ } else {
33274
+ const argsJson = repairArgs(t.name, t.arguments || "{}");
33275
+ log(`[Streaming] Sending buffered tool call (no validation): ${t.name} with args: ${argsJson}`);
33120
33276
  send("content_block_start", {
33121
33277
  type: "content_block_start",
33122
33278
  index: t.blockIndex,
@@ -33133,83 +33289,51 @@ data: ${JSON.stringify(d)}
33133
33289
  });
33134
33290
  t.started = true;
33135
33291
  t.closed = true;
33136
- } else {
33137
- log(`[Streaming] Buffered tool call ${t.name} failed validation, skipping: ${validation.missingParams.join(", ")}`);
33138
- t.closed = true;
33139
33292
  }
33140
- } else {
33141
- const argsJson = repairArgs(t.name, t.arguments || "{}");
33142
- log(`[Streaming] Sending buffered tool call (no validation): ${t.name} with args: ${argsJson}`);
33143
- send("content_block_start", {
33144
- type: "content_block_start",
33145
- index: t.blockIndex,
33146
- content_block: { type: "tool_use", id: t.id, name: t.name }
33147
- });
33148
- send("content_block_delta", {
33149
- type: "content_block_delta",
33150
- index: t.blockIndex,
33151
- delta: { type: "input_json_delta", partial_json: argsJson }
33152
- });
33153
- send("content_block_stop", {
33154
- type: "content_block_stop",
33155
- index: t.blockIndex
33156
- });
33157
- t.started = true;
33293
+ }
33294
+ }
33295
+ for (const t of Array.from(state.tools.values())) {
33296
+ if (t.started && !t.closed) {
33297
+ send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
33158
33298
  t.closed = true;
33159
33299
  }
33160
33300
  }
33161
- }
33162
- for (const t of Array.from(state.tools.values())) {
33163
- if (t.started && !t.closed) {
33164
- send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
33165
- t.closed = true;
33301
+ if (middlewareManager) {
33302
+ await middlewareManager.afterStreamComplete(target, streamMetadata);
33166
33303
  }
33167
- }
33168
- if (middlewareManager) {
33169
- await middlewareManager.afterStreamComplete(target, streamMetadata);
33170
- }
33171
- if (reason === "error") {
33172
- send("error", { type: "error", error: { type: "api_error", message: err } });
33173
- } else {
33174
- const hasStructuredTools = Array.from(state.tools.values()).some((t) => t.started);
33175
- const truncated = state.finishReason === "length";
33176
- const refused = state.finishReason === "content_filter";
33177
- const stopReason = refused ? "refusal" : truncated ? "max_tokens" : textToolCalls.length > 0 || hasStructuredTools ? "tool_use" : "end_turn";
33178
- if (truncated || refused) {
33179
- log(`[Streaming] Upstream finish_reason=${state.finishReason} \u2192 stop_reason=${stopReason} (${state.accumulatedText.length} chars produced)`);
33304
+ if (reason === "error") {
33305
+ send("error", { type: "error", error: { type: "api_error", message: err } });
33306
+ } else {
33307
+ const hasStructuredTools = Array.from(state.tools.values()).some((t) => t.started);
33308
+ const truncated = state.finishReason === "length";
33309
+ const refused = state.finishReason === "content_filter";
33310
+ const stopReason = refused ? "refusal" : truncated ? "max_tokens" : textToolCalls.length > 0 || hasStructuredTools ? "tool_use" : "end_turn";
33311
+ if (truncated || refused) {
33312
+ log(`[Streaming] Upstream finish_reason=${state.finishReason} \u2192 stop_reason=${stopReason} (${state.accumulatedText.length} chars produced)`);
33313
+ }
33314
+ send("message_delta", {
33315
+ type: "message_delta",
33316
+ delta: { stop_reason: stopReason, stop_sequence: null },
33317
+ usage: {
33318
+ ...state.usage?.prompt_tokens ? { input_tokens: state.usage.prompt_tokens } : {},
33319
+ output_tokens: state.usage?.completion_tokens || 0
33320
+ }
33321
+ });
33322
+ behavior?.onTurnEnd?.();
33323
+ send("message_stop", { type: "message_stop" });
33180
33324
  }
33181
- send("message_delta", {
33182
- type: "message_delta",
33183
- delta: { stop_reason: stopReason, stop_sequence: null },
33184
- usage: {
33185
- ...state.usage?.prompt_tokens ? { input_tokens: state.usage.prompt_tokens } : {},
33186
- output_tokens: state.usage?.completion_tokens || 0
33325
+ if (onTokenUpdate) {
33326
+ if (state.usage) {
33327
+ log(`[Streaming] Final usage: prompt=${state.usage.prompt_tokens || 0}, completion=${state.usage.completion_tokens || 0}`);
33328
+ onTokenUpdate(state.usage.prompt_tokens || 0, state.usage.completion_tokens || 0);
33329
+ } else {
33330
+ const estimatedOutputTokens = Math.ceil(state.accumulatedText.length / 4);
33331
+ log(`[Streaming] No usage data from provider, estimating: ~${estimatedOutputTokens} output tokens`);
33332
+ onTokenUpdate(priorInputTokens || 100, estimatedOutputTokens);
33187
33333
  }
33188
- });
33189
- behavior?.onTurnEnd?.();
33190
- send("message_stop", { type: "message_stop" });
33191
- }
33192
- if (onTokenUpdate) {
33193
- if (state.usage) {
33194
- log(`[Streaming] Final usage: prompt=${state.usage.prompt_tokens || 0}, completion=${state.usage.completion_tokens || 0}`);
33195
- onTokenUpdate(state.usage.prompt_tokens || 0, state.usage.completion_tokens || 0);
33196
- } else {
33197
- const estimatedOutputTokens = Math.ceil(state.accumulatedText.length / 4);
33198
- log(`[Streaming] No usage data from provider, estimating: ~${estimatedOutputTokens} output tokens`);
33199
- onTokenUpdate(priorInputTokens || 100, estimatedOutputTokens);
33200
33334
  }
33201
- }
33202
- if (!isClosed) {
33203
- try {
33204
- controller.enqueue(encoder.encode(`data: [DONE]
33205
-
33206
-
33207
- `));
33208
- } catch (e) {}
33209
- controller.close();
33210
- isClosed = true;
33211
- if (ping)
33212
- clearInterval(ping);
33335
+ } finally {
33336
+ teardown();
33213
33337
  }
33214
33338
  };
33215
33339
  try {
@@ -39089,62 +39213,70 @@ data: ${JSON.stringify(data)}
39089
39213
  send("ping", { type: "ping" });
39090
39214
  }
39091
39215
  }, 1000);
39216
+ const teardown = () => {
39217
+ if (!isClosed) {
39218
+ isClosed = true;
39219
+ try {
39220
+ controller.close();
39221
+ } catch {}
39222
+ }
39223
+ if (pingInterval) {
39224
+ clearInterval(pingInterval);
39225
+ pingInterval = null;
39226
+ }
39227
+ };
39092
39228
  const finalize = async (reason, err) => {
39093
- if (finalized2)
39229
+ if (finalized2) {
39230
+ teardown();
39094
39231
  return;
39095
- finalized2 = true;
39096
- if (thinkingStarted) {
39097
- send("content_block_stop", { type: "content_block_stop", index: thinkingIdx });
39098
39232
  }
39099
- if (textStarted) {
39100
- send("content_block_stop", { type: "content_block_stop", index: textIdx });
39101
- }
39102
- for (const t of toolCalls.values()) {
39103
- if (t.started && !t.closed) {
39104
- send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
39105
- t.closed = true;
39233
+ finalized2 = true;
39234
+ try {
39235
+ if (thinkingStarted) {
39236
+ send("content_block_stop", { type: "content_block_stop", index: thinkingIdx });
39106
39237
  }
39107
- }
39108
- if (opts.middlewareManager) {
39109
- await opts.middlewareManager.afterStreamComplete(opts.modelName, new Map);
39110
- }
39111
- const inputTokens = usage?.promptTokenCount || 0;
39112
- const outputTokens = usage?.candidatesTokenCount || 0;
39113
- if (usage) {
39114
- log(`[GeminiSSE] Usage: prompt=${inputTokens}, completion=${outputTokens}`);
39115
- }
39116
- if (opts.onTokenUpdate) {
39117
- opts.onTokenUpdate(inputTokens, outputTokens);
39118
- }
39119
- if (reason === "error") {
39120
- log(`[GeminiSSE] Stream error: ${err}`);
39121
- send("error", { type: "error", error: { type: "api_error", message: err } });
39122
- } else {
39123
- const hasToolCalls = toolCalls.size > 0;
39124
- const stopReason = truncated ? "max_tokens" : hasToolCalls ? "tool_use" : "end_turn";
39125
- if (truncated) {
39126
- log("[GeminiSSE] finishReason=MAX_TOKENS \u2192 stop_reason=max_tokens");
39238
+ if (textStarted) {
39239
+ send("content_block_stop", { type: "content_block_stop", index: textIdx });
39127
39240
  }
39128
- send("message_delta", {
39129
- type: "message_delta",
39130
- delta: { stop_reason: stopReason, stop_sequence: null },
39131
- usage: {
39132
- ...inputTokens > 0 ? { input_tokens: inputTokens } : {},
39133
- output_tokens: outputTokens
39241
+ for (const t of toolCalls.values()) {
39242
+ if (t.started && !t.closed) {
39243
+ send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
39244
+ t.closed = true;
39134
39245
  }
39135
- });
39136
- opts.onTurnEnd?.();
39137
- send("message_stop", { type: "message_stop" });
39138
- }
39139
- if (!isClosed) {
39140
- isClosed = true;
39141
- if (pingInterval) {
39142
- clearInterval(pingInterval);
39143
- pingInterval = null;
39144
39246
  }
39145
- try {
39146
- controller.close();
39147
- } catch {}
39247
+ if (opts.middlewareManager) {
39248
+ await opts.middlewareManager.afterStreamComplete(opts.modelName, new Map);
39249
+ }
39250
+ const inputTokens = usage?.promptTokenCount || 0;
39251
+ const outputTokens = usage?.candidatesTokenCount || 0;
39252
+ if (usage) {
39253
+ log(`[GeminiSSE] Usage: prompt=${inputTokens}, completion=${outputTokens}`);
39254
+ }
39255
+ if (opts.onTokenUpdate) {
39256
+ opts.onTokenUpdate(inputTokens, outputTokens);
39257
+ }
39258
+ if (reason === "error") {
39259
+ log(`[GeminiSSE] Stream error: ${err}`);
39260
+ send("error", { type: "error", error: { type: "api_error", message: err } });
39261
+ } else {
39262
+ const hasToolCalls = toolCalls.size > 0;
39263
+ const stopReason = truncated ? "max_tokens" : hasToolCalls ? "tool_use" : "end_turn";
39264
+ if (truncated) {
39265
+ log("[GeminiSSE] finishReason=MAX_TOKENS \u2192 stop_reason=max_tokens");
39266
+ }
39267
+ send("message_delta", {
39268
+ type: "message_delta",
39269
+ delta: { stop_reason: stopReason, stop_sequence: null },
39270
+ usage: {
39271
+ ...inputTokens > 0 ? { input_tokens: inputTokens } : {},
39272
+ output_tokens: outputTokens
39273
+ }
39274
+ });
39275
+ opts.onTurnEnd?.();
39276
+ send("message_stop", { type: "message_stop" });
39277
+ }
39278
+ } finally {
39279
+ teardown();
39148
39280
  }
39149
39281
  };
39150
39282
  try {
@@ -39362,38 +39494,48 @@ data: ${JSON.stringify(data)}
39362
39494
  send("ping", { type: "ping" });
39363
39495
  }
39364
39496
  }, 1000);
39365
- const finalize = (reason, err) => {
39366
- if (isClosed)
39367
- return;
39368
- if (textStarted) {
39369
- send("content_block_stop", { type: "content_block_stop", index: 0 });
39370
- }
39371
- if (reason === "error") {
39372
- send("error", { type: "error", error: { type: "api_error", message: err } });
39373
- } else {
39374
- send("message_delta", {
39375
- type: "message_delta",
39376
- delta: { stop_reason: "end_turn", stop_sequence: null },
39377
- usage: {
39378
- ...promptTokens > 0 ? { input_tokens: promptTokens } : {},
39379
- output_tokens: completionTokens
39380
- }
39381
- });
39382
- send("message_stop", { type: "message_stop" });
39383
- }
39384
- if (opts.onTokenUpdate) {
39385
- opts.onTokenUpdate(promptTokens, completionTokens);
39386
- }
39497
+ let finalized2 = false;
39498
+ const teardown = () => {
39387
39499
  if (!isClosed) {
39388
39500
  isClosed = true;
39389
- if (pingInterval) {
39390
- clearInterval(pingInterval);
39391
- pingInterval = null;
39392
- }
39393
39501
  try {
39394
39502
  controller.close();
39395
39503
  } catch {}
39396
39504
  }
39505
+ if (pingInterval) {
39506
+ clearInterval(pingInterval);
39507
+ pingInterval = null;
39508
+ }
39509
+ };
39510
+ const finalize = (reason, err) => {
39511
+ if (finalized2) {
39512
+ teardown();
39513
+ return;
39514
+ }
39515
+ finalized2 = true;
39516
+ try {
39517
+ if (textStarted) {
39518
+ send("content_block_stop", { type: "content_block_stop", index: 0 });
39519
+ }
39520
+ if (reason === "error") {
39521
+ send("error", { type: "error", error: { type: "api_error", message: err } });
39522
+ } else {
39523
+ send("message_delta", {
39524
+ type: "message_delta",
39525
+ delta: { stop_reason: "end_turn", stop_sequence: null },
39526
+ usage: {
39527
+ ...promptTokens > 0 ? { input_tokens: promptTokens } : {},
39528
+ output_tokens: completionTokens
39529
+ }
39530
+ });
39531
+ send("message_stop", { type: "message_stop" });
39532
+ }
39533
+ if (opts.onTokenUpdate) {
39534
+ opts.onTokenUpdate(promptTokens, completionTokens);
39535
+ }
39536
+ } finally {
39537
+ teardown();
39538
+ }
39397
39539
  };
39398
39540
  try {
39399
39541
  const reader = response.body.getReader();
@@ -39880,10 +40022,26 @@ class TokenTracker {
39880
40022
  modelNameOverride;
39881
40023
  planUsage;
39882
40024
  lastPlanSerialized = "";
40025
+ toolCallsByName = new Map;
40026
+ startedAt = Date.now();
40027
+ sessionBilledInputTokens = 0;
39883
40028
  constructor(port, config2) {
39884
40029
  this.port = port;
39885
40030
  this.config = config2;
39886
40031
  }
40032
+ recordToolUse(name) {
40033
+ const key = name.trim() || "unknown";
40034
+ this.toolCallsByName.set(key, (this.toolCallsByName.get(key) ?? 0) + 1);
40035
+ }
40036
+ getToolCallCount() {
40037
+ let n = 0;
40038
+ for (const v of this.toolCallsByName.values())
40039
+ n += v;
40040
+ return n;
40041
+ }
40042
+ getToolCalls() {
40043
+ return [...this.toolCallsByName].map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));
40044
+ }
39887
40045
  setActiveModelName(name) {
39888
40046
  this.modelNameOverride = name;
39889
40047
  }
@@ -39905,6 +40063,7 @@ class TokenTracker {
39905
40063
  this.sessionInputTokens = inputTokens;
39906
40064
  this.lastInputTokens = inputTokens;
39907
40065
  this.sessionOutputTokens += outputTokens;
40066
+ this.sessionBilledInputTokens += inputTokens;
39908
40067
  const pricing = this.getPricing();
39909
40068
  const cost = inputTokens / 1e6 * pricing.inputCostPer1M + outputTokens / 1e6 * pricing.outputCostPer1M;
39910
40069
  this.sessionTotalCost += cost;
@@ -39917,6 +40076,7 @@ class TokenTracker {
39917
40076
  const pricing = this.getPricing();
39918
40077
  const cost = this.sessionInputTokens / 1e6 * pricing.inputCostPer1M + this.sessionOutputTokens / 1e6 * pricing.outputCostPer1M;
39919
40078
  this.sessionTotalCost = cost;
40079
+ this.sessionBilledInputTokens = this.sessionInputTokens;
39920
40080
  this.writeFile(this.sessionInputTokens, this.sessionOutputTokens, pricing.isEstimate);
39921
40081
  }
39922
40082
  updateWithDelta(inputTokens, outputTokens) {
@@ -39935,6 +40095,7 @@ class TokenTracker {
39935
40095
  }
39936
40096
  this.sessionOutputTokens += outputTokens;
39937
40097
  const pricing = this.getPricing();
40098
+ this.sessionBilledInputTokens += incrementalInputTokens;
39938
40099
  const cost = incrementalInputTokens / 1e6 * pricing.inputCostPer1M + outputTokens / 1e6 * pricing.outputCostPer1M;
39939
40100
  this.sessionTotalCost += cost;
39940
40101
  this.writeFile(inputTokens, this.sessionOutputTokens, pricing.isEstimate);
@@ -39943,6 +40104,7 @@ class TokenTracker {
39943
40104
  this.sessionInputTokens = inputTokens;
39944
40105
  this.lastInputTokens = inputTokens;
39945
40106
  this.sessionOutputTokens += outputTokens;
40107
+ this.sessionBilledInputTokens += inputTokens;
39946
40108
  if (typeof actualCost === "number" && actualCost > 0) {
39947
40109
  this.sessionTotalCost += actualCost;
39948
40110
  log(`[TokenTracker] Actual cost from API: $${actualCost.toFixed(6)}`);
@@ -39960,6 +40122,7 @@ class TokenTracker {
39960
40122
  this.lastInputTokens = inputTokens;
39961
40123
  }
39962
40124
  this.sessionOutputTokens += outputTokens;
40125
+ this.sessionBilledInputTokens += inputTokens;
39963
40126
  this.writeFile(this.sessionInputTokens, this.sessionOutputTokens);
39964
40127
  }
39965
40128
  setContextWindow(contextWindow) {
@@ -40012,7 +40175,12 @@ class TokenTracker {
40012
40175
  provider_name: this.getDisplayName(),
40013
40176
  updated_at: Date.now(),
40014
40177
  is_free: isFreeModel,
40015
- is_estimated: isEstimate || false
40178
+ is_estimated: isEstimate || false,
40179
+ started_at: this.startedAt,
40180
+ tool_calls: this.getToolCalls(),
40181
+ billed_input_tokens: this.sessionBilledInputTokens,
40182
+ input_per_m: pricing.inputCostPer1M,
40183
+ output_per_m: pricing.outputCostPer1M
40016
40184
  };
40017
40185
  const displayModel = stripProviderPrefix(this.modelNameOverride || this.config.modelName || "");
40018
40186
  if (displayModel) {
@@ -40718,14 +40886,18 @@ class ComposedHandler {
40718
40886
  };
40719
40887
  const streamFormat = this.resolveStreamFormat();
40720
40888
  const priorInputTokens = this.tokenTracker.getLastInputTokens();
40889
+ const observeToolCall = (name) => {
40890
+ this.tokenTracker.recordToolUse(name);
40891
+ behaviorSession?.observeToolCall(name);
40892
+ };
40721
40893
  switch (streamFormat) {
40722
40894
  case "openai-sse":
40723
- return createStreamingResponseHandler(c, response, adapter, this.bareModelName, this.middlewareManager, onTokenUpdate, claudeRequest.tools, toolNameMap, priorInputTokens, behaviorSession && {
40724
- shouldBufferTool: (name) => behaviorSession.interceptsTool(name),
40725
- onToolCall: (name, argsJson) => behaviorSession.repairToolCall(name, argsJson),
40726
- onAssistantText: (text, kind) => behaviorSession.observeText(text, kind),
40727
- onToolCallObserved: (name) => behaviorSession.observeToolCall(name),
40728
- onTurnEnd: () => behaviorSession.finishTurn()
40895
+ return createStreamingResponseHandler(c, response, adapter, this.bareModelName, this.middlewareManager, onTokenUpdate, claudeRequest.tools, toolNameMap, priorInputTokens, {
40896
+ shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
40897
+ onToolCall: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
40898
+ onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
40899
+ onToolCallObserved: observeToolCall,
40900
+ onTurnEnd: () => behaviorSession?.finishTurn()
40729
40901
  });
40730
40902
  case "openai-responses-sse":
40731
40903
  return createResponsesStreamHandler(c, response, {
@@ -40739,7 +40911,7 @@ class ComposedHandler {
40739
40911
  shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
40740
40912
  onToolCall: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
40741
40913
  onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
40742
- onToolCallObserved: (name) => behaviorSession?.observeToolCall(name),
40914
+ onToolCallObserved: observeToolCall,
40743
40915
  onTurnEnd: () => behaviorSession?.finishTurn()
40744
40916
  });
40745
40917
  case "anthropic-sse":
@@ -40750,7 +40922,7 @@ class ComposedHandler {
40750
40922
  shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
40751
40923
  repairToolArgs: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
40752
40924
  onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
40753
- onToolCallObserved: (name) => behaviorSession?.observeToolCall(name),
40925
+ onToolCallObserved: observeToolCall,
40754
40926
  onTurnEnd: () => behaviorSession?.finishTurn()
40755
40927
  });
40756
40928
  case "gemini-sse": {
@@ -40767,7 +40939,7 @@ class ComposedHandler {
40767
40939
  onToolCall,
40768
40940
  repairToolArgs: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
40769
40941
  onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
40770
- onToolCallObserved: (name) => behaviorSession?.observeToolCall(name),
40942
+ onToolCallObserved: observeToolCall,
40771
40943
  onTurnEnd: () => behaviorSession?.finishTurn(),
40772
40944
  unwrapResponse: this.options.unwrapGeminiResponse,
40773
40945
  priorInputTokens
@@ -40787,7 +40959,7 @@ class ComposedHandler {
40787
40959
  repairToolArgs: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
40788
40960
  shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
40789
40961
  onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
40790
- onToolCallObserved: (name) => behaviorSession?.observeToolCall(name),
40962
+ onToolCallObserved: observeToolCall,
40791
40963
  onTurnEnd: () => behaviorSession?.finishTurn()
40792
40964
  });
40793
40965
  case "ollama-jsonl":
@@ -42796,6 +42968,9 @@ async function routeBare(model, nativeProvider, rules, defaultProvider, cachePat
42796
42968
  const [primary, ...fallbacks] = credentialed;
42797
42969
  return { kind: "ok", primary, fallbacks };
42798
42970
  }
42971
+ function normalizeGlmSlug(model) {
42972
+ return model.replace(/^glm-(\d+)-(\d+)(-.*)?$/i, (_m, major, minor, suffix) => `glm-${major}.${minor}${suffix ?? ""}`);
42973
+ }
42799
42974
  async function route(modelSpec, rulesOverride, defaultProviderOverride, cachePath) {
42800
42975
  const parsed = parseModelSpec(modelSpec);
42801
42976
  if (parsed.isExplicitProvider) {
@@ -42803,7 +42978,7 @@ async function route(modelSpec, rulesOverride, defaultProviderOverride, cachePat
42803
42978
  }
42804
42979
  const rules = rulesOverride ?? loadRoutingRules();
42805
42980
  const defaultProvider = defaultProviderOverride !== undefined ? defaultProviderOverride : rulesOverride !== undefined ? undefined : loadConfig().defaultProvider;
42806
- return routeBare(parsed.model, parsed.provider, rules, defaultProvider, cachePath);
42981
+ return routeBare(normalizeGlmSlug(parsed.model), parsed.provider, rules, defaultProvider, cachePath);
42807
42982
  }
42808
42983
  var init_routing_rules = __esm(() => {
42809
42984
  init_model_catalog();
@@ -64519,8 +64694,11 @@ __export(exports_model_selector, {
64519
64694
  promptForApiKey: () => promptForApiKey,
64520
64695
  pickerProviderToFirebaseSlug: () => pickerProviderToFirebaseSlug,
64521
64696
  isUserDeployedProvider: () => isUserDeployedProvider,
64697
+ isPickableProvider: () => isPickableProvider,
64698
+ getProviderFilterAliases: () => getProviderFilterAliases,
64522
64699
  confirmAction: () => confirmAction,
64523
64700
  compareByReleaseDateDesc: () => compareByReleaseDateDesc,
64701
+ buildProviderChoices: () => buildProviderChoices,
64524
64702
  buildExplicitModelSpec: () => buildExplicitModelSpec,
64525
64703
  buildDiscoveredModelRows: () => buildDiscoveredModelRows
64526
64704
  });
@@ -64679,6 +64857,18 @@ function dedupeModels(models) {
64679
64857
  }
64680
64858
  return deduped;
64681
64859
  }
64860
+ function dedupeByProviderSpec(provider, models) {
64861
+ const seen = new Set;
64862
+ const deduped = [];
64863
+ for (const model of models) {
64864
+ const spec = buildExplicitModelSpec(provider, resolveProviderExternalId(provider, model));
64865
+ if (seen.has(spec))
64866
+ continue;
64867
+ seen.add(spec);
64868
+ deduped.push(model);
64869
+ }
64870
+ return deduped;
64871
+ }
64682
64872
  function sortModelsNewestFirst(models) {
64683
64873
  return [...models].sort(compareByReleaseDateDesc);
64684
64874
  }
@@ -64729,6 +64919,16 @@ function formatModelChoiceAsSpec(model, spec, priceStr) {
64729
64919
  const dateStr = model.releaseDate ? `, ${model.releaseDate.slice(0, 7)}` : "";
64730
64920
  return `${spec} (${priceStr}, ${ctxStr}${capsStr}${dateStr})`;
64731
64921
  }
64922
+ function getProviderFilterAliases() {
64923
+ const aliases = {};
64924
+ for (const def of pickableProvidersInPickerOrder()) {
64925
+ aliases[def.name.toLowerCase()] = def.name;
64926
+ for (const shortcut of def.shortcuts) {
64927
+ aliases[shortcut.toLowerCase()] = def.name;
64928
+ }
64929
+ }
64930
+ return { ...aliases, ...PROVIDER_FILTER_ALIAS_EXTRA };
64931
+ }
64732
64932
  function parseProviderFilter(term, providers = []) {
64733
64933
  if (!term.startsWith("@")) {
64734
64934
  return { provider: null, searchTerm: term };
@@ -64744,7 +64944,7 @@ function parseProviderFilter(term, providers = []) {
64744
64944
  prefix = withoutAt.slice(0, spaceIdx);
64745
64945
  rest = withoutAt.slice(spaceIdx + 1).trim();
64746
64946
  }
64747
- const source = PROVIDER_FILTER_ALIASES[prefix.toLowerCase()];
64947
+ const source = getProviderFilterAliases()[prefix.toLowerCase()];
64748
64948
  if (source) {
64749
64949
  return { provider: source, searchTerm: rest };
64750
64950
  }
@@ -64752,7 +64952,7 @@ function parseProviderFilter(term, providers = []) {
64752
64952
  if (exactMatch) {
64753
64953
  return { provider: exactMatch.slug, searchTerm: rest };
64754
64954
  }
64755
- const partialMatch = Object.entries(PROVIDER_FILTER_ALIASES).find(([alias]) => alias.startsWith(prefix.toLowerCase()));
64955
+ const partialMatch = Object.entries(getProviderFilterAliases()).find(([alias]) => alias.startsWith(prefix.toLowerCase()));
64756
64956
  if (partialMatch) {
64757
64957
  return { provider: partialMatch[1], searchTerm: rest };
64758
64958
  }
@@ -64766,7 +64966,7 @@ async function fetchPickerModels(providerSlug, searchTerm, defaultModels, catalo
64766
64966
  if (providerSlug) {
64767
64967
  const firebaseSlug = pickerProviderToFirebaseSlug[providerSlug] ?? providerSlug;
64768
64968
  const vendorModels = await catalog.modelsByVendor(firebaseSlug);
64769
- const infos = sortModelsNewestFirst(dedupeModels(vendorModels.map(catalogModelToModelInfo)));
64969
+ const infos = dedupeByProviderSpec(providerSlug, sortModelsNewestFirst(dedupeModels(vendorModels.map(catalogModelToModelInfo))));
64770
64970
  if (!searchTerm)
64771
64971
  return infos;
64772
64972
  const needle = searchTerm.toLowerCase();
@@ -64784,6 +64984,7 @@ async function selectModel(options = {}) {
64784
64984
  let models;
64785
64985
  let recommendedModels = [];
64786
64986
  let pickerProviders = [];
64987
+ let interactiveProviderChoices = [];
64787
64988
  const remoteQueryCache = new Map;
64788
64989
  if (freeOnly) {
64789
64990
  models = await getFreeModels();
@@ -64798,7 +64999,8 @@ async function selectModel(options = {}) {
64798
64999
  const topModels = top100Result.status === "fulfilled" ? sortModelsNewestFirst(dedupeModels(top100Result.value.models.map(modelDocToModelInfo))) : [];
64799
65000
  recommendedModels = recommendedResult.status === "fulfilled" ? recommendedResult.value : [];
64800
65001
  models = topModels.length > 0 ? topModels : recommendedModels;
64801
- pickerProviders = toPickerProviders(await getInteractiveProviderChoices());
65002
+ interactiveProviderChoices = await getInteractiveProviderChoices();
65003
+ pickerProviders = toPickerProviders(interactiveProviderChoices);
64802
65004
  }
64803
65005
  const loadRemoteModels = async (providerSlug, searchTerm) => {
64804
65006
  const cacheKey = `${providerSlug || "__all__"}::${searchTerm}`;
@@ -64827,7 +65029,6 @@ async function selectModel(options = {}) {
64827
65029
  const cleanupKeypress = () => process.stdin.removeListener("data", onData);
64828
65030
  try {
64829
65031
  if (!freeOnly && !message && pickerProviders.length > 1) {
64830
- const interactiveProviderChoices = await getInteractiveProviderChoices();
64831
65032
  const providerChoices = [
64832
65033
  {
64833
65034
  name: "All providers",
@@ -64860,11 +65061,14 @@ async function selectModel(options = {}) {
64860
65061
  const { provider: filterProvider, searchTerm } = parseProviderFilter(normalizedTerm, pickerProviders);
64861
65062
  const effectiveProvider = filterProvider;
64862
65063
  const remoteModels = await loadRemoteModels(effectiveProvider, searchTerm);
64863
- return remoteModels.slice(0, 100).map((model) => ({
64864
- name: formatModelChoice(model, true),
64865
- value: effectiveProvider ? buildExplicitModelSpec(effectiveProvider, model.id) : model.id,
64866
- description: model.description?.slice(0, 160)
64867
- }));
65064
+ return remoteModels.slice(0, 100).map((model) => {
65065
+ const spec = effectiveProvider ? buildExplicitModelSpec(effectiveProvider, resolveProviderExternalId(effectiveProvider, model)) : model.id;
65066
+ return {
65067
+ name: effectiveProvider ? formatModelChoiceAsSpec(model, spec, resolveProviderDisplayPrice(effectiveProvider, model)) : formatModelChoice(model, true),
65068
+ value: spec,
65069
+ description: model.description?.slice(0, 160)
65070
+ };
65071
+ });
64868
65072
  }
64869
65073
  }, { signal: ac.signal });
64870
65074
  return selected;
@@ -64878,13 +65082,59 @@ async function selectModel(options = {}) {
64878
65082
  cleanupKeypress();
64879
65083
  }
64880
65084
  }
65085
+ function isPickableProvider(def) {
65086
+ return def.shortcuts.length > 0;
65087
+ }
65088
+ function pickableProvidersInPickerOrder() {
65089
+ const rank = new Map(PICKER_ORDER.map((name, i) => [name, i]));
65090
+ return getAllProviders().filter(isPickableProvider).sort((a, b) => {
65091
+ const ra = rank.get(a.name) ?? Number.MAX_SAFE_INTEGER;
65092
+ const rb = rank.get(b.name) ?? Number.MAX_SAFE_INTEGER;
65093
+ return ra !== rb ? ra - rb : a.displayName.localeCompare(b.displayName);
65094
+ });
65095
+ }
65096
+ function buildProviderChoices() {
65097
+ const derived = pickableProvidersInPickerOrder().map((def) => {
65098
+ const copy = PICKER_COPY[def.name] ?? {};
65099
+ return {
65100
+ name: copy.name ?? def.displayName,
65101
+ value: def.name,
65102
+ description: copy.description ?? def.description ?? "",
65103
+ provider: def.name
65104
+ };
65105
+ });
65106
+ return [
65107
+ {
65108
+ name: "Skip (keep Claude default)",
65109
+ value: "skip",
65110
+ description: "Use native Claude model for this tier"
65111
+ },
65112
+ ...derived,
65113
+ {
65114
+ name: "Enter custom model",
65115
+ value: "custom",
65116
+ description: "Type a provider@model specification"
65117
+ }
65118
+ ];
65119
+ }
64881
65120
  async function getProviderChoices() {
64882
- const checks4 = await Promise.all(ALL_PROVIDER_CHOICES.map(async (choice) => {
65121
+ const all = buildProviderChoices();
65122
+ const checks4 = await Promise.all(all.map(async (choice) => {
64883
65123
  if (!choice.provider)
64884
65124
  return true;
64885
65125
  return credentials.isAvailable(choice.provider);
64886
65126
  }));
64887
- return ALL_PROVIDER_CHOICES.filter((_, i) => checks4[i]);
65127
+ return all.filter((_, i) => checks4[i]);
65128
+ }
65129
+ function pickerModelPrefix(provider) {
65130
+ const override = PROVIDER_MODEL_PREFIX_OVERRIDE[provider];
65131
+ if (override)
65132
+ return override;
65133
+ const def = getProviderByName(provider);
65134
+ if (!def || !isPickableProvider(def))
65135
+ return;
65136
+ const prefix = def.shortestPrefix || def.shortcuts[0];
65137
+ return prefix ? `${prefix}@` : undefined;
64888
65138
  }
64889
65139
  async function getInteractiveProviderChoices() {
64890
65140
  return (await getProviderChoices()).filter((choice) => choice.value !== "skip");
@@ -64897,7 +65147,7 @@ function toPickerProviders(choices) {
64897
65147
  }));
64898
65148
  }
64899
65149
  function buildExplicitModelSpec(provider, modelId) {
64900
- const prefix = PROVIDER_MODEL_PREFIX[provider];
65150
+ const prefix = pickerModelPrefix(provider);
64901
65151
  if (!prefix) {
64902
65152
  return modelId;
64903
65153
  }
@@ -64916,6 +65166,8 @@ function resolveProviderAggregatorEntry(provider, model) {
64916
65166
  return model.aggregators.find((a) => a.provider.toLowerCase() === firebaseSlug.toLowerCase());
64917
65167
  }
64918
65168
  function resolveProviderDisplayPrice(provider, model) {
65169
+ if (isSubscriptionProvider(provider))
65170
+ return "SUB";
64919
65171
  const entry = resolveProviderAggregatorEntry(provider, model);
64920
65172
  const entryPrice = formatAveragePricing(entry?.pricing);
64921
65173
  if (entryPrice?.average)
@@ -64923,7 +65175,7 @@ function resolveProviderDisplayPrice(provider, model) {
64923
65175
  return model.pricing?.average || "N/A";
64924
65176
  }
64925
65177
  function getPickerDisplayName(providerValue) {
64926
- const choice = ALL_PROVIDER_CHOICES.find((c) => c.value === providerValue);
65178
+ const choice = buildProviderChoices().find((c) => c.value === providerValue);
64927
65179
  if (choice)
64928
65180
  return choice.name;
64929
65181
  return getDisplayName(providerValue);
@@ -64932,7 +65184,7 @@ async function loadModelsForPickerProvider(providerValue, catalog) {
64932
65184
  const firebaseSlug = pickerProviderToFirebaseSlug[providerValue] ?? providerValue;
64933
65185
  try {
64934
65186
  const vendorModels = await catalog.modelsByVendor(firebaseSlug);
64935
- return sortModelsNewestFirst(dedupeModels(vendorModels.map(catalogModelToModelInfo)));
65187
+ return dedupeByProviderSpec(providerValue, sortModelsNewestFirst(dedupeModels(vendorModels.map(catalogModelToModelInfo))));
64936
65188
  } catch {
64937
65189
  return [];
64938
65190
  }
@@ -65024,7 +65276,7 @@ async function buildDiscoveredModelRows(provider, displayName, catalog) {
65024
65276
  return sortModelsNewestFirst(rows);
65025
65277
  }
65026
65278
  async function selectModelFromProvider(provider, tierName, recommendedModels, _forceUpdate, catalog) {
65027
- const prefix = PROVIDER_MODEL_PREFIX[provider] || `${provider}@`;
65279
+ const prefix = pickerModelPrefix(provider) ?? `${provider}@`;
65028
65280
  const displayName = getPickerDisplayName(provider);
65029
65281
  const def = getProviderByName(provider);
65030
65282
  if (def?.modelDiscovery) {
@@ -65189,7 +65441,7 @@ async function selectProfile(profiles) {
65189
65441
  async function confirmAction(message) {
65190
65442
  return dist_default4({ message, default: false });
65191
65443
  }
65192
- var pickerProviderToFirebaseSlug, LOCAL_OR_USER_DEPLOYED, SUBSCRIPTION_PRICING, PROVIDER_FILTER_ALIASES, ALL_PROVIDER_CHOICES, PROVIDER_MODEL_PREFIX;
65444
+ var pickerProviderToFirebaseSlug, LOCAL_OR_USER_DEPLOYED, SUBSCRIPTION_PRICING, PROVIDER_FILTER_ALIAS_EXTRA, PICKER_COPY, PICKER_ORDER, PROVIDER_MODEL_PREFIX_OVERRIDE;
65193
65445
  var init_model_selector = __esm(() => {
65194
65446
  init_dist16();
65195
65447
  init_model_catalog();
@@ -65227,160 +65479,73 @@ var init_model_selector = __esm(() => {
65227
65479
  output: "SUB",
65228
65480
  average: "SUB"
65229
65481
  };
65230
- PROVIDER_FILTER_ALIASES = {
65231
- openrouter: "openrouter",
65232
- or: "openrouter",
65233
- google: "google",
65234
- gemini: "google",
65482
+ PROVIDER_FILTER_ALIAS_EXTRA = {
65235
65483
  gem: "google",
65236
- openai: "openai",
65237
- oai: "openai",
65238
- codex: "openai-codex",
65239
- cx: "openai-codex",
65240
- "x-ai": "x-ai",
65241
- xai: "x-ai",
65242
- grok: "x-ai",
65243
- minimax: "minimax",
65244
- mm: "minimax",
65245
- "minimax-coding": "minimax-coding",
65246
- mmc: "minimax-coding",
65247
- kimi: "kimi",
65248
- moon: "kimi",
65249
- moonshot: "kimi",
65250
- "kimi-coding": "kimi-coding",
65251
- kc: "kimi-coding",
65252
- glm: "glm",
65253
- "glm-coding": "glm-coding",
65254
- gc: "glm-coding",
65255
- "z-ai": "z-ai",
65256
- zai: "z-ai",
65257
- zen: "zen",
65258
- ollamacloud: "ollamacloud",
65259
- oc: "ollamacloud",
65260
- litellm: "litellm",
65261
- ll: "litellm",
65262
- deepseek: "deepseek",
65263
- mistralai: "mistralai",
65264
- mistral: "mistralai",
65265
- sakana: "sakana",
65266
- fugu: "sakana",
65267
- "sakana-subscription": "sakana-subscription",
65268
- sc: "sakana-subscription",
65269
- "qwen-cloud": "qwen-cloud",
65270
- qc: "qwen-cloud"
65271
- };
65272
- ALL_PROVIDER_CHOICES = [
65273
- {
65274
- name: "Skip (keep Claude default)",
65275
- value: "skip",
65276
- description: "Use native Claude model for this tier"
65277
- },
65278
- {
65279
- name: "OpenRouter",
65280
- value: "openrouter",
65281
- description: "580+ models via unified API",
65282
- provider: "openrouter"
65283
- },
65284
- {
65285
- name: "OpenCode Zen",
65286
- value: "zen",
65287
- description: "Free models, no API key needed",
65288
- provider: "opencode-zen"
65289
- },
65290
- { name: "Google Gemini", value: "google", description: "Direct API", provider: "google" },
65291
- { name: "OpenAI", value: "openai", description: "Direct API", provider: "openai" },
65292
- {
65293
- name: "OpenAI Codex",
65294
- value: "openai-codex",
65295
- description: "ChatGPT Plus/Pro subscription (Responses API)",
65296
- provider: "openai-codex"
65297
- },
65298
- { name: "xAI / Grok", value: "x-ai", description: "Direct API", provider: "x-ai" },
65299
- { name: "DeepSeek", value: "deepseek", description: "Direct API", provider: "deepseek" },
65300
- { name: "Mistral", value: "mistralai", description: "Direct API", provider: "mistralai" },
65301
- { name: "Sakana Fugu", value: "sakana", description: "Direct API", provider: "sakana" },
65302
- {
65303
- name: "Sakana Fugu Subscription",
65304
- value: "sakana-subscription",
65305
- description: "Subscription plan",
65306
- provider: "sakana-subscription"
65307
- },
65308
- { name: "MiniMax", value: "minimax", description: "Direct API", provider: "minimax" },
65309
- {
65310
- name: "MiniMax Coding",
65311
- value: "minimax-coding",
65312
- description: "Coding subscription",
65313
- provider: "minimax-coding"
65314
- },
65315
- { name: "Kimi / Moonshot", value: "kimi", description: "Direct API", provider: "kimi" },
65316
- {
65317
- name: "Kimi Coding",
65318
- value: "kimi-coding",
65319
- description: "Coding subscription",
65320
- provider: "kimi-coding"
65321
- },
65322
- {
65323
- name: "Qwen Plan",
65324
- value: "qwen-cloud",
65325
- description: "Alibaba Model Studio subscription",
65326
- provider: "qwen-cloud"
65327
- },
65328
- { name: "GLM / Zhipu", value: "glm", description: "Direct API", provider: "glm" },
65329
- {
65330
- name: "GLM Coding Plan",
65331
- value: "glm-coding",
65332
- description: "Coding subscription",
65333
- provider: "glm-coding"
65334
- },
65335
- { name: "Z.AI", value: "z-ai", description: "Direct API", provider: "z-ai" },
65336
- {
65337
- name: "OllamaCloud",
65338
- value: "ollamacloud",
65339
- description: "Cloud models",
65340
- provider: "ollamacloud"
65341
- },
65342
- { name: "LiteLLM", value: "litellm", description: "Configured proxy", provider: "litellm" },
65343
- {
65344
- name: "Ollama (local)",
65345
- value: "ollama",
65346
- description: "Local Ollama instance",
65347
- provider: "ollama"
65348
- },
65349
- {
65350
- name: "LM Studio (local)",
65351
- value: "lmstudio",
65352
- description: "Local LM Studio instance",
65353
- provider: "lmstudio"
65354
- },
65355
- {
65356
- name: "Enter custom model",
65357
- value: "custom",
65358
- description: "Type a provider@model specification"
65359
- }
65484
+ zen: "opencode-zen"
65485
+ };
65486
+ PICKER_COPY = {
65487
+ openrouter: { description: "580+ models via unified API" },
65488
+ "opencode-zen": { name: "OpenCode Zen", description: "Free models, no API key needed" },
65489
+ google: { name: "Google Gemini", description: "Direct API" },
65490
+ openai: { description: "Direct API" },
65491
+ "openai-codex": { description: "ChatGPT Plus/Pro subscription (Responses API)" },
65492
+ "x-ai": { name: "xAI / Grok", description: "Direct API" },
65493
+ deepseek: { description: "Direct API" },
65494
+ mistralai: { name: "Mistral", description: "Direct API" },
65495
+ sakana: { name: "Sakana Fugu", description: "Direct API" },
65496
+ "sakana-subscription": { name: "Sakana Fugu Subscription", description: "Subscription plan" },
65497
+ minimax: { description: "Direct API" },
65498
+ "minimax-coding": { name: "MiniMax Coding", description: "Coding subscription" },
65499
+ kimi: { name: "Kimi / Moonshot", description: "Direct API" },
65500
+ "kimi-coding": { name: "Kimi Coding", description: "Coding subscription" },
65501
+ "qwen-cloud": { name: "Qwen Plan", description: "Alibaba Model Studio subscription" },
65502
+ glm: { name: "GLM / Zhipu", description: "Direct API" },
65503
+ "glm-coding": { name: "GLM Coding Plan", description: "Coding subscription" },
65504
+ "z-ai": { name: "Z.AI", description: "Direct API" },
65505
+ ollamacloud: { name: "OllamaCloud", description: "Cloud models" },
65506
+ litellm: { description: "Configured proxy" },
65507
+ ollama: { name: "Ollama (local)", description: "Local Ollama instance" },
65508
+ lmstudio: { name: "LM Studio (local)", description: "Local LM Studio instance" },
65509
+ vllm: { name: "vLLM (local)", description: "Local vLLM server" },
65510
+ mlx: { name: "MLX (local)", description: "Local MLX server" }
65511
+ };
65512
+ PICKER_ORDER = [
65513
+ "openrouter",
65514
+ "opencode-zen",
65515
+ "opencode-zen-go",
65516
+ "google",
65517
+ "antigravity",
65518
+ "openai",
65519
+ "openai-codex",
65520
+ "devin",
65521
+ "x-ai",
65522
+ "deepseek",
65523
+ "mistralai",
65524
+ "sakana",
65525
+ "sakana-subscription",
65526
+ "minimax",
65527
+ "minimax-coding",
65528
+ "kimi",
65529
+ "kimi-coding",
65530
+ "qwen-cloud",
65531
+ "glm",
65532
+ "glm-coding",
65533
+ "z-ai",
65534
+ "ollamacloud",
65535
+ "poe",
65536
+ "vertex",
65537
+ "litellm",
65538
+ "ollama",
65539
+ "lmstudio",
65540
+ "vllm",
65541
+ "mlx"
65360
65542
  ];
65361
- PROVIDER_MODEL_PREFIX = {
65543
+ PROVIDER_MODEL_PREFIX_OVERRIDE = {
65362
65544
  google: "google@",
65363
- antigravity: "ag@",
65364
- openai: "oai@",
65365
- "openai-codex": "cx@",
65366
- "x-ai": "x-ai@",
65367
- deepseek: "ds@",
65368
- mistralai: "mistral@",
65369
- sakana: "sakana@",
65370
- "sakana-subscription": "sc@",
65371
- minimax: "mm@",
65372
- kimi: "kimi@",
65373
- "minimax-coding": "mmc@",
65374
- "kimi-coding": "kc@",
65375
- "qwen-cloud": "qc@",
65376
- glm: "glm@",
65377
- "glm-coding": "gc@",
65378
- "z-ai": "z-ai@",
65379
- ollamacloud: "oc@",
65380
- ollama: "ollama@",
65545
+ openrouter: "openrouter@",
65381
65546
  lmstudio: "lmstudio@",
65382
- zen: "zen@",
65383
- openrouter: "openrouter@"
65547
+ sakana: "sakana@",
65548
+ zen: "zen@"
65384
65549
  };
65385
65550
  });
65386
65551
 
@@ -68424,6 +68589,8 @@ Usage: claudish --models --provider <slug>`);
68424
68589
  if (rest.length > 0)
68425
68590
  config3._hasPositionalPrompt = true;
68426
68591
  break;
68592
+ } else if (arg === "--resume" && (i + 1 >= args.length || args[i + 1].startsWith("-"))) {
68593
+ config3._resumePicker = true;
68427
68594
  } else if (arg.startsWith("-")) {
68428
68595
  config3.claudeArgs.push(arg);
68429
68596
  if (arg === "-p" || arg === "--print") {
@@ -75521,6 +75688,7 @@ function App({ requestLogin } = {}) {
75521
75688
  setOpFieldCursor(idx < 0 ? 0 : idx);
75522
75689
  }, [opFieldOptionsFiltered, opFieldCursor, mode]);
75523
75690
  const acquireOpAuth = useCallback3(async () => {
75691
+ clearOpSkip();
75524
75692
  const auth = await resolveSdkAuth({
75525
75693
  interactive: true,
75526
75694
  configAccount: readOnepasswordAccount(),
@@ -77636,7 +77804,10 @@ async function runClaudeWithProxy(config3, proxyUrl, onCleanup) {
77636
77804
  setSessionContextWindow2(realWindow);
77637
77805
  } catch {}
77638
77806
  if (contextEnv.notice && !config3.quiet) {
77639
- console.error(contextEnv.notice);
77807
+ if (config3.interactive)
77808
+ console.log(contextEnv.notice);
77809
+ else
77810
+ console.error(contextEnv.notice);
77640
77811
  }
77641
77812
  }
77642
77813
  }
@@ -78322,12 +78493,2864 @@ var init_team_grid = __esm(() => {
78322
78493
  ];
78323
78494
  });
78324
78495
 
78496
+ // src/tui/viz/text.ts
78497
+ function columns(n, fn, arg = "width") {
78498
+ if (!Number.isFinite(n))
78499
+ throw new RangeError(`${fn}: ${arg} must be a finite number, got ${n}`);
78500
+ return Math.max(0, Math.floor(n));
78501
+ }
78502
+ function isWide2(cp) {
78503
+ let lo = 0;
78504
+ let hi = WIDE.length - 1;
78505
+ while (lo <= hi) {
78506
+ const mid = lo + hi >> 1;
78507
+ const [a, b] = WIDE[mid];
78508
+ if (cp < a)
78509
+ hi = mid - 1;
78510
+ else if (cp > b)
78511
+ lo = mid + 1;
78512
+ else
78513
+ return true;
78514
+ }
78515
+ return false;
78516
+ }
78517
+ function fallbackClusterWidth(cluster) {
78518
+ const cp = cluster.codePointAt(0) ?? 0;
78519
+ if (cp < 32 || cp >= 127 && cp <= 159 || LEADING_FORMAT.test(cluster))
78520
+ return 0;
78521
+ return isWide2(cp) || LEADING_EMOJI_PRESENTATION.test(cluster) || cluster.includes(VS16) ? 2 : 1;
78522
+ }
78523
+ function displayWidth(s) {
78524
+ if (NATIVE_WIDTH)
78525
+ return NATIVE_WIDTH(s);
78526
+ let w = 0;
78527
+ for (const { segment } of seg.segment(s))
78528
+ w += fallbackClusterWidth(segment);
78529
+ return w;
78530
+ }
78531
+ function truncate3(s, width) {
78532
+ const n = columns(width, "truncate");
78533
+ if (n === 0)
78534
+ return "";
78535
+ const clean = sanitize2(s);
78536
+ if (displayWidth(clean) <= n)
78537
+ return clean;
78538
+ let out = "";
78539
+ let w = 0;
78540
+ for (const { segment } of seg.segment(clean)) {
78541
+ const cw = displayWidth(segment);
78542
+ if (w + cw > n - 1)
78543
+ break;
78544
+ out += segment;
78545
+ w += cw;
78546
+ }
78547
+ return `${out}\u2026`;
78548
+ }
78549
+ function padTo(s, width) {
78550
+ const n = columns(width, "padTo");
78551
+ if (n === 0)
78552
+ return "";
78553
+ const clean = sanitize2(s);
78554
+ const clipped = displayWidth(clean) > n ? truncate3(clean, n) : clean;
78555
+ return clipped + " ".repeat(Math.max(0, n - displayWidth(clipped)));
78556
+ }
78557
+ function padStartTo(s, width) {
78558
+ const n = columns(width, "padStartTo");
78559
+ if (n === 0)
78560
+ return "";
78561
+ const clean = sanitize2(s);
78562
+ const clipped = displayWidth(clean) > n ? truncate3(clean, n) : clean;
78563
+ return " ".repeat(Math.max(0, n - displayWidth(clipped))) + clipped;
78564
+ }
78565
+ function splitCells(parts, cells) {
78566
+ const n = columns(cells, "splitCells", "cells");
78567
+ const finite = parts.map((p) => Number.isFinite(p) && p > 0 ? p : 0);
78568
+ const out = finite.map(() => 0);
78569
+ let safe = finite;
78570
+ let sum = finite.reduce((a, b) => a + b, 0);
78571
+ if (!Number.isFinite(sum)) {
78572
+ const max = finite.reduce((a, b) => b > a ? b : a, 0);
78573
+ safe = finite.map((p) => p / max);
78574
+ sum = safe.reduce((a, b) => a + b, 0);
78575
+ }
78576
+ if (n <= 0 || sum <= 0)
78577
+ return out;
78578
+ const byShare = safe.map((p, i) => ({ p, i })).sort((a, b) => b.p - a.p);
78579
+ if (n < byShare.filter(({ p }) => p > 0).length) {
78580
+ for (const { i } of byShare.slice(0, n))
78581
+ out[i] = 1;
78582
+ return out;
78583
+ }
78584
+ const exact = safe.map((p) => n * (p / sum));
78585
+ exact.forEach((e, i) => {
78586
+ out[i] = Math.floor(e);
78587
+ });
78588
+ const rank = exact.map((e, i) => ({ rem: e % 1, i })).sort((a, b) => b.rem - a.rem);
78589
+ for (let k = 0, left = n - out.reduce((a, b) => a + b, 0);left > 0; k++, left--)
78590
+ out[rank[k % rank.length].i] += 1;
78591
+ for (const { p, i } of byShare)
78592
+ if (p > 0 && out[i] === 0) {
78593
+ const d = byShare.reduce((m, c) => out[c.i] > out[m] ? c.i : m, 0);
78594
+ if (out[d] > 1) {
78595
+ out[d] -= 1;
78596
+ out[i] = 1;
78597
+ }
78598
+ }
78599
+ return out;
78600
+ }
78601
+ var NATIVE_WIDTH, WIDE, seg, VS16 = "\uFE0F", LEADING_FORMAT, LEADING_EMOJI_PRESENTATION, CONTROL, sanitize2 = (s) => s.replace(CONTROL, " ");
78602
+ var init_text = __esm(() => {
78603
+ NATIVE_WIDTH = (() => {
78604
+ const b = globalThis.Bun;
78605
+ return typeof b?.stringWidth === "function" ? b.stringWidth.bind(b) : null;
78606
+ })();
78607
+ WIDE = [
78608
+ [4352, 4447],
78609
+ [9001, 9002],
78610
+ [11904, 12350],
78611
+ [12353, 12771],
78612
+ [12774, 12871],
78613
+ [12880, 13311],
78614
+ [13312, 19903],
78615
+ [19968, 40959],
78616
+ [40960, 42191],
78617
+ [43360, 43391],
78618
+ [44032, 55203],
78619
+ [63744, 64255],
78620
+ [65040, 65049],
78621
+ [65072, 65135],
78622
+ [65280, 65376],
78623
+ [65504, 65510],
78624
+ [94176, 94180],
78625
+ [94192, 94193],
78626
+ [94208, 100343],
78627
+ [100352, 101589],
78628
+ [101632, 101640],
78629
+ [110576, 110579],
78630
+ [110581, 110587],
78631
+ [110589, 110590],
78632
+ [110592, 110882],
78633
+ [110898, 110898],
78634
+ [110928, 110930],
78635
+ [110933, 110933],
78636
+ [110948, 110951],
78637
+ [110960, 111355],
78638
+ [127488, 127490],
78639
+ [127504, 127547],
78640
+ [127552, 127560],
78641
+ [127568, 127569],
78642
+ [127584, 127589],
78643
+ [131072, 196605],
78644
+ [196608, 262141]
78645
+ ];
78646
+ seg = new Intl.Segmenter(undefined, { granularity: "grapheme" });
78647
+ LEADING_FORMAT = /^\p{Cf}/u;
78648
+ LEADING_EMOJI_PRESENTATION = /^\p{Emoji_Presentation}/u;
78649
+ CONTROL = /[\u0000-\u001f\u007f-\u009f]/g;
78650
+ });
78651
+
78652
+ // src/tui/viz/tokens.ts
78653
+ var tokens, ramps;
78654
+ var init_tokens = __esm(() => {
78655
+ init_theme2();
78656
+ tokens = {
78657
+ fatal: C.red,
78658
+ error: C.red,
78659
+ warn: C.orange,
78660
+ info: C.cyan,
78661
+ debug: C.fgMuted,
78662
+ trace: C.dim,
78663
+ success: C.green,
78664
+ running: C.blue,
78665
+ idle: C.fgMuted,
78666
+ dead: C.dim,
78667
+ border: C.border,
78668
+ subtle: C.dim,
78669
+ text: C.fg,
78670
+ accent: C.focusBorder,
78671
+ bgPanel: C.bgAlt,
78672
+ ink: C.black
78673
+ };
78674
+ ramps = {
78675
+ load: [tokens.success, C.yellow, tokens.error],
78676
+ temperature: [tokens.running, tokens.success, C.orange, tokens.error],
78677
+ network: [tokens.success, C.yellow, tokens.error],
78678
+ savings: [tokens.error, C.yellow, tokens.success],
78679
+ volume: [C.border, C.blue, C.cyan]
78680
+ };
78681
+ });
78682
+
78683
+ // src/tui/viz/color.ts
78684
+ import { RGBA, parseColor, rgbToHex } from "@opentui/core";
78685
+ function mix(a, b, t) {
78686
+ const k = clamp01(t);
78687
+ return hex3(RGBA.fromValues(a.r + (b.r - a.r) * k, a.g + (b.g - a.g) * k, a.b + (b.b - a.b) * k, a.a + (b.a - a.a) * k));
78688
+ }
78689
+ function blend1D(steps, from, to) {
78690
+ if (!Number.isInteger(steps) || steps <= 0)
78691
+ return [];
78692
+ const a = rgba(from);
78693
+ if (steps === 1)
78694
+ return [hex3(a)];
78695
+ const b = rgba(to);
78696
+ return Array.from({ length: steps }, (_, i) => mix(a, b, i / (steps - 1)));
78697
+ }
78698
+ function blendStops(steps, ...stops) {
78699
+ if (!Number.isInteger(steps) || steps <= 0 || stops.length === 0)
78700
+ return [];
78701
+ if (stops.length === 1 || steps === 1)
78702
+ return blend1D(steps, stops[0], stops.at(-1));
78703
+ const pts = stops.map(rgba);
78704
+ const segs = pts.length - 1;
78705
+ return Array.from({ length: steps }, (_, i) => {
78706
+ const p = i / (steps - 1) * segs;
78707
+ const s = Math.min(segs - 1, Math.floor(p));
78708
+ return mix(pts[s], pts[s + 1], p - s);
78709
+ });
78710
+ }
78711
+ function luminance(c) {
78712
+ const f = (v) => v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
78713
+ return 0.2126 * f(clamp01(c.r)) + 0.7152 * f(clamp01(c.g)) + 0.0722 * f(clamp01(c.b));
78714
+ }
78715
+ function contrastRatio(a, b) {
78716
+ const [x, y] = [luminance(a), luminance(b)];
78717
+ return x >= y ? (x + 0.05) / (y + 0.05) : (y + 0.05) / (x + 0.05);
78718
+ }
78719
+ function over(fg, bg) {
78720
+ const a = clamp01(fg.a);
78721
+ if (a >= 1)
78722
+ return fg;
78723
+ return RGBA.fromValues(fg.r * a + bg.r * (1 - a), fg.g * a + bg.g * (1 - a), fg.b * a + bg.b * (1 - a), 1);
78724
+ }
78725
+ function pickInk(bg, dark = tokens.ink, light = tokens.text) {
78726
+ const surface = over(rgba(bg), rgba(tokens.bgPanel));
78727
+ const inkDark = over(rgba(dark), surface);
78728
+ const inkLight = over(rgba(light), surface);
78729
+ return hex3(contrastRatio(inkDark, surface) >= contrastRatio(inkLight, surface) ? inkDark : inkLight);
78730
+ }
78731
+ var rgba = (c) => parseColor(c), hex3 = (c) => rgbToHex(c), clamp01 = (n) => Number.isFinite(n) ? Math.min(1, Math.max(0, n)) : 0;
78732
+ var init_color = __esm(() => {
78733
+ init_tokens();
78734
+ });
78735
+
78736
+ // src/tui/viz/widgets.tsx
78737
+ import { createTextAttributes as createTextAttributes2 } from "@opentui/core";
78738
+ import { jsxDEV as jsxDEV18, Fragment as Fragment11 } from "@opentui/react/jsx-dev-runtime";
78739
+ function rampFor(width, stops) {
78740
+ const use = stops.length > 0 ? stops : ramps.load;
78741
+ const key = `${width}|${use.join(":")}`;
78742
+ const hit = RAMP_CACHE.get(key);
78743
+ if (hit)
78744
+ return hit;
78745
+ const built = blendStops(width, ...use);
78746
+ if (RAMP_CACHE.size >= RAMP_CACHE_MAX)
78747
+ RAMP_CACHE.delete(RAMP_CACHE.keys().next().value);
78748
+ RAMP_CACHE.set(key, built);
78749
+ return built;
78750
+ }
78751
+ function fillCells(pct, width) {
78752
+ const cells = Math.floor(width);
78753
+ if (!Number.isFinite(cells) || cells <= 0 || Number.isNaN(pct))
78754
+ return 0;
78755
+ return Math.round(Math.min(100, Math.max(0, pct)) / 100 * cells);
78756
+ }
78757
+ function MeterSpan({
78758
+ pct,
78759
+ width,
78760
+ ramp = ramps.load
78761
+ }) {
78762
+ const cells = Math.floor(width);
78763
+ if (!Number.isFinite(cells) || cells <= 0)
78764
+ return null;
78765
+ if (Number.isNaN(pct))
78766
+ return /* @__PURE__ */ jsxDEV18("span", {
78767
+ fg: tokens.dead,
78768
+ children: NODATA.repeat(cells)
78769
+ }, undefined, false, undefined, this);
78770
+ const cols = rampFor(cells, ramp);
78771
+ const filled = fillCells(pct, cells);
78772
+ return /* @__PURE__ */ jsxDEV18(Fragment11, {
78773
+ children: Array.from({ length: cells }, (_, i) => /* @__PURE__ */ jsxDEV18("span", {
78774
+ fg: i < filled ? cols[i] : tokens.border,
78775
+ children: i < filled ? FILL : TRACK
78776
+ }, i, false, undefined, this))
78777
+ }, undefined, false, undefined, this);
78778
+ }
78779
+ function Sparkline({
78780
+ values,
78781
+ fg = tokens.info,
78782
+ style,
78783
+ ...layout
78784
+ }) {
78785
+ const row = sparkGlyphs(values);
78786
+ if (row === null)
78787
+ return null;
78788
+ return /* @__PURE__ */ jsxDEV18("text", {
78789
+ fg,
78790
+ flexShrink: 0,
78791
+ ...layout,
78792
+ style,
78793
+ children: row
78794
+ }, undefined, false, undefined, this);
78795
+ }
78796
+ function sparkGlyphs(values) {
78797
+ if (values.length === 0)
78798
+ return null;
78799
+ let max = Number.NEGATIVE_INFINITY;
78800
+ let min = Number.POSITIVE_INFINITY;
78801
+ for (const v of values)
78802
+ if (Number.isFinite(v)) {
78803
+ max = Math.max(max, v);
78804
+ min = Math.min(min, v);
78805
+ }
78806
+ if (max === Number.NEGATIVE_INFINITY)
78807
+ return GAP.repeat(values.length);
78808
+ const half = max / 2 - min / 2;
78809
+ const mid = SPARK[Math.floor((SPARK.length - 1) / 2)];
78810
+ const top = SPARK.length - 1;
78811
+ const glyph = (v) => SPARK[Math.min(top, Math.max(0, Math.round((v / 2 - min / 2) / half * top)))];
78812
+ return values.map((v) => !Number.isFinite(v) ? GAP : half > 0 ? glyph(v) : mid).join("");
78813
+ }
78814
+ function SparklineSpan({
78815
+ values,
78816
+ fg = tokens.info
78817
+ }) {
78818
+ const row = sparkGlyphs(values);
78819
+ return row === null ? null : /* @__PURE__ */ jsxDEV18("span", {
78820
+ fg,
78821
+ children: row
78822
+ }, undefined, false, undefined, this);
78823
+ }
78824
+ function badgePad(label, width) {
78825
+ const pad2 = Math.max(0, (width ?? 0) - displayWidth(label) - 2);
78826
+ return pad2 > 0 ? /* @__PURE__ */ jsxDEV18("span", {
78827
+ children: " ".repeat(pad2)
78828
+ }, undefined, false, undefined, this) : null;
78829
+ }
78830
+ function BadgeSpan({ label, bg, width }) {
78831
+ return /* @__PURE__ */ jsxDEV18(Fragment11, {
78832
+ children: [
78833
+ /* @__PURE__ */ jsxDEV18("span", {
78834
+ fg: pickInk(bg),
78835
+ bg,
78836
+ attributes: BOLD4,
78837
+ children: ` ${label} `
78838
+ }, undefined, false, undefined, this),
78839
+ badgePad(label, width)
78840
+ ]
78841
+ }, undefined, true, undefined, this);
78842
+ }
78843
+ function Panel({
78844
+ title,
78845
+ focused = false,
78846
+ flush = false,
78847
+ children,
78848
+ style,
78849
+ ...layout
78850
+ }) {
78851
+ return /* @__PURE__ */ jsxDEV18("box", {
78852
+ border: true,
78853
+ borderStyle: "rounded",
78854
+ borderColor: focused ? tokens.accent : tokens.border,
78855
+ backgroundColor: tokens.bgPanel,
78856
+ title,
78857
+ titleAlignment: "left",
78858
+ flexDirection: "column",
78859
+ overflow: "hidden",
78860
+ paddingLeft: flush ? 0 : 1,
78861
+ paddingRight: flush ? 0 : 1,
78862
+ ...layout,
78863
+ style,
78864
+ children
78865
+ }, undefined, false, undefined, this);
78866
+ }
78867
+ var BOLD4, FILL = "\u2588", TRACK = "\u2591", SPARK, GAP = " ", NODATA = "\u254C", RAMP_CACHE, RAMP_CACHE_MAX = 64;
78868
+ var init_widgets = __esm(() => {
78869
+ init_color();
78870
+ init_text();
78871
+ init_tokens();
78872
+ BOLD4 = createTextAttributes2({ bold: true });
78873
+ SPARK = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
78874
+ RAMP_CACHE = new Map;
78875
+ });
78876
+
78877
+ // src/session/session-discovery.ts
78878
+ var exports_session_discovery = {};
78879
+ __export(exports_session_discovery, {
78880
+ slugForPath: () => slugForPath,
78881
+ sessionLabel: () => sessionLabel,
78882
+ mainConversationTurn: () => mainConversationTurn,
78883
+ isHarnessNoise: () => isHarnessNoise,
78884
+ isAgentSession: () => isAgentSession,
78885
+ isActive: () => isActive,
78886
+ hydrateSession: () => hydrateSession,
78887
+ hydrateConversation: () => hydrateConversation,
78888
+ getRepoContext: () => getRepoContext,
78889
+ findLatestSessionId: () => findLatestSessionId,
78890
+ enrichWorktreeGit: () => enrichWorktreeGit,
78891
+ discoverWorktreeGroups: () => discoverWorktreeGroups,
78892
+ PROJECTS_DIR: () => PROJECTS_DIR,
78893
+ ACTIVE_WINDOW_MS: () => ACTIVE_WINDOW_MS
78894
+ });
78895
+ import { execFile, execFileSync as execFileSync2 } from "child_process";
78896
+ import { closeSync as closeSync5, openSync as openSync5, readSync, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
78897
+ import { homedir as homedir34 } from "os";
78898
+ import { basename, join as join37 } from "path";
78899
+ function slugForPath(absPath) {
78900
+ return absPath.replace(/[/.]/g, "-");
78901
+ }
78902
+ function isAgentSession(row) {
78903
+ return row.entrypoint !== undefined && row.entrypoint !== "cli";
78904
+ }
78905
+ function getRepoContext(cwd = process.cwd()) {
78906
+ const git = (args) => {
78907
+ try {
78908
+ return execFileSync2("git", args, {
78909
+ cwd,
78910
+ encoding: "utf-8",
78911
+ stdio: ["ignore", "pipe", "ignore"]
78912
+ }).trim();
78913
+ } catch {
78914
+ return null;
78915
+ }
78916
+ };
78917
+ const current = git(["rev-parse", "--show-toplevel"]);
78918
+ const commonDir = git(["rev-parse", "--git-common-dir"]);
78919
+ if (!current || !commonDir)
78920
+ return null;
78921
+ const root = commonDir.endsWith("/.git") ? commonDir.slice(0, -"/.git".length) : current;
78922
+ const liveWorktrees = [];
78923
+ const branchByPath = new Map;
78924
+ const porcelain = git(["worktree", "list", "--porcelain"]);
78925
+ if (porcelain) {
78926
+ let currentPath = null;
78927
+ for (const line of porcelain.split(`
78928
+ `)) {
78929
+ if (line.startsWith("worktree ")) {
78930
+ currentPath = line.slice("worktree ".length);
78931
+ liveWorktrees.push(currentPath);
78932
+ } else if (line.startsWith("branch ") && currentPath) {
78933
+ branchByPath.set(currentPath, line.slice("branch ".length).replace(/^refs\/heads\//, ""));
78934
+ }
78935
+ }
78936
+ }
78937
+ return { root, current, liveWorktrees, branchByPath };
78938
+ }
78939
+ function projectDirs() {
78940
+ try {
78941
+ return readdirSync7(PROJECTS_DIR, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
78942
+ } catch {
78943
+ return [];
78944
+ }
78945
+ }
78946
+ function sessionsIn(dirName) {
78947
+ const dir = join37(PROJECTS_DIR, dirName);
78948
+ let names;
78949
+ try {
78950
+ names = readdirSync7(dir).filter((n) => n.endsWith(".jsonl"));
78951
+ } catch {
78952
+ return [];
78953
+ }
78954
+ const rows = [];
78955
+ for (const n of names) {
78956
+ const file2 = join37(dir, n);
78957
+ try {
78958
+ const st = statSync6(file2);
78959
+ if (st.size === 0)
78960
+ continue;
78961
+ const row = {
78962
+ id: basename(n, ".jsonl"),
78963
+ file: file2,
78964
+ mtimeMs: st.mtimeMs,
78965
+ sizeBytes: st.size
78966
+ };
78967
+ const head = readChunk(file2, 0, Math.min(ENTRYPOINT_BYTES, st.size));
78968
+ const m = /"entrypoint":"([a-z-]+)"/.exec(head);
78969
+ if (m)
78970
+ row.entrypoint = m[1];
78971
+ rows.push(row);
78972
+ } catch {}
78973
+ }
78974
+ return rows;
78975
+ }
78976
+ async function enrichWorktreeGit(groups, repoRoot) {
78977
+ const run = (cwd, args) => new Promise((resolve5) => {
78978
+ execFile("git", args, { cwd, encoding: "utf-8" }, (err, stdout) => resolve5(err ? "" : stdout));
78979
+ });
78980
+ const trackByBranch = new Map;
78981
+ const refs = await run(repoRoot, [
78982
+ "for-each-ref",
78983
+ "--format=%(refname:short)\t%(upstream:track)",
78984
+ "refs/heads/"
78985
+ ]);
78986
+ for (const line of refs.split(`
78987
+ `)) {
78988
+ const [name, track] = line.split("\t");
78989
+ if (!name || !track)
78990
+ continue;
78991
+ const ahead = /ahead (\d+)/.exec(track)?.[1];
78992
+ const behind = /behind (\d+)/.exec(track)?.[1];
78993
+ if (ahead || behind) {
78994
+ trackByBranch.set(name, {
78995
+ ...ahead ? { ahead: Number(ahead) } : {},
78996
+ ...behind ? { behind: Number(behind) } : {}
78997
+ });
78998
+ }
78999
+ }
79000
+ await Promise.all(groups.map(async (g) => {
79001
+ if (g.branch)
79002
+ Object.assign(g, trackByBranch.get(g.branch) ?? {});
79003
+ if (!g.path || !g.live)
79004
+ return;
79005
+ const out = await run(g.path, ["status", "--porcelain"]);
79006
+ g.dirty = out.split(`
79007
+ `).filter((l) => l.trim().length > 0).length;
79008
+ }));
79009
+ }
79010
+ function isUnder(p, root) {
79011
+ return p === root || p.startsWith(`${root}/`);
79012
+ }
79013
+ function readProjectCwd(dirName) {
79014
+ const rows = sessionsIn(dirName);
79015
+ if (rows.length === 0)
79016
+ return null;
79017
+ rows.sort((a, b) => b.mtimeMs - a.mtimeMs);
79018
+ for (const row of rows.slice(0, 2)) {
79019
+ for (const r of parseRecords(readChunk(row.file, 0, Math.min(HEAD_BYTES, row.sizeBytes)), false)) {
79020
+ if (typeof r.cwd === "string" && r.cwd)
79021
+ return r.cwd;
79022
+ }
79023
+ }
79024
+ return null;
79025
+ }
79026
+ function isActive(row, now = Date.now()) {
79027
+ return now - row.mtimeMs < ACTIVE_WINDOW_MS;
79028
+ }
79029
+ function discoverWorktreeGroups(repo) {
79030
+ const rootSlug = slugForPath(repo.root);
79031
+ const mine = projectDirs();
79032
+ const known = [...repo.liveWorktrees].map((p) => ({ path: p, slug: slugForPath(p) })).sort((a, b) => b.slug.length - a.slug.length);
79033
+ const groups = new Map;
79034
+ const groupCwd = new Map;
79035
+ const upsert = (name, path2, live) => {
79036
+ let g = groups.get(name);
79037
+ if (!g) {
79038
+ g = {
79039
+ name,
79040
+ path: path2,
79041
+ live,
79042
+ current: path2 !== null && path2 === repo.current,
79043
+ sessions: [],
79044
+ lastActiveMs: 0,
79045
+ activeNow: false,
79046
+ ...path2 ? { branch: repo.branchByPath.get(path2) } : {}
79047
+ };
79048
+ groups.set(name, g);
79049
+ }
79050
+ return g;
79051
+ };
79052
+ const WORKTREE_MARK = "--claude-worktrees-";
79053
+ const worktreeMatches = known.filter((k) => k.path !== repo.root);
79054
+ for (const dir of mine) {
79055
+ const at = dir.indexOf(WORKTREE_MARK);
79056
+ let name;
79057
+ let path2;
79058
+ let live;
79059
+ const hit = worktreeMatches.find((k) => dir === k.slug || dir.startsWith(`${k.slug}-`));
79060
+ if (hit) {
79061
+ path2 = hit.path;
79062
+ live = true;
79063
+ name = hit.path === repo.root ? "(root)" : basename(hit.path);
79064
+ } else if (at !== -1 && dir.startsWith(rootSlug)) {
79065
+ path2 = null;
79066
+ live = false;
79067
+ name = dir.slice(at + WORKTREE_MARK.length);
79068
+ } else if (dir === rootSlug) {
79069
+ name = "(root)";
79070
+ path2 = repo.root;
79071
+ live = true;
79072
+ } else if (dir.startsWith(`${rootSlug}-`) && at === -1) {
79073
+ const cwd = readProjectCwd(dir);
79074
+ if (!cwd || !isUnder(cwd, repo.root))
79075
+ continue;
79076
+ name = "(root)";
79077
+ path2 = repo.root;
79078
+ live = true;
79079
+ } else {
79080
+ continue;
79081
+ }
79082
+ const g = upsert(name, path2, live);
79083
+ if (path2)
79084
+ groupCwd.set(name, path2);
79085
+ else if (!groupCwd.has(name)) {
79086
+ const cwd = readProjectCwd(dir);
79087
+ if (cwd)
79088
+ groupCwd.set(name, cwd);
79089
+ }
79090
+ for (const s of sessionsIn(dir)) {
79091
+ g.sessions.push(s);
79092
+ if (s.mtimeMs > g.lastActiveMs)
79093
+ g.lastActiveMs = s.mtimeMs;
79094
+ }
79095
+ }
79096
+ const names = [...groups.keys()].sort((a, b) => b.length - a.length);
79097
+ for (const name of [...groups.keys()]) {
79098
+ const g = groups.get(name);
79099
+ if (!g || g.live)
79100
+ continue;
79101
+ const parent = names.find((n) => n !== name && name.startsWith(`${n}-`) && groups.has(n));
79102
+ if (!parent)
79103
+ continue;
79104
+ const into = groups.get(parent);
79105
+ const childCwd = groupCwd.get(name);
79106
+ const parentCwd = groupCwd.get(parent);
79107
+ if (!childCwd || !parentCwd || !isUnder(childCwd, parentCwd))
79108
+ continue;
79109
+ into.sessions.push(...g.sessions);
79110
+ into.lastActiveMs = Math.max(into.lastActiveMs, g.lastActiveMs);
79111
+ groups.delete(name);
79112
+ }
79113
+ for (const g of groups.values()) {
79114
+ g.sessions.sort((a, b) => b.mtimeMs - a.mtimeMs);
79115
+ g.activeNow = g.sessions.some((s) => isActive(s));
79116
+ if (g.path) {
79117
+ try {
79118
+ g.createdMs = statSync6(g.path).birthtimeMs;
79119
+ } catch {}
79120
+ }
79121
+ if (!g.createdMs && g.sessions.length > 0) {
79122
+ g.createdMs = g.sessions.reduce((m, s) => Math.min(m, s.mtimeMs), Number.POSITIVE_INFINITY);
79123
+ }
79124
+ }
79125
+ return [...groups.values()].filter((g) => g.sessions.length > 0).sort((a, b) => {
79126
+ if (a.current !== b.current)
79127
+ return a.current ? -1 : 1;
79128
+ return b.lastActiveMs - a.lastActiveMs;
79129
+ });
79130
+ }
79131
+ function readChunk(file2, pos, len) {
79132
+ if (len <= 0)
79133
+ return "";
79134
+ let fd = null;
79135
+ try {
79136
+ fd = openSync5(file2, "r");
79137
+ const buf = Buffer.allocUnsafe(len);
79138
+ const n = readSync(fd, buf, 0, len, pos);
79139
+ return buf.subarray(0, n).toString("utf-8");
79140
+ } catch {
79141
+ return "";
79142
+ } finally {
79143
+ if (fd !== null) {
79144
+ try {
79145
+ closeSync5(fd);
79146
+ } catch {}
79147
+ }
79148
+ }
79149
+ }
79150
+ function parseRecords(chunk, dropFirstPartial) {
79151
+ const lines = chunk.split(`
79152
+ `);
79153
+ if (dropFirstPartial)
79154
+ lines.shift();
79155
+ else
79156
+ lines.pop();
79157
+ const out = [];
79158
+ for (const l of lines) {
79159
+ if (!l)
79160
+ continue;
79161
+ try {
79162
+ const o = JSON.parse(l);
79163
+ if (o && typeof o === "object")
79164
+ out.push(o);
79165
+ } catch {}
79166
+ }
79167
+ return out;
79168
+ }
79169
+ function contentText(content) {
79170
+ if (typeof content === "string")
79171
+ return content;
79172
+ if (!Array.isArray(content))
79173
+ return "";
79174
+ return content.map((b) => b && typeof b === "object" && typeof b.text === "string" ? b.text : "").join(`
79175
+ `);
79176
+ }
79177
+ function isHarnessNoise(raw2) {
79178
+ const t = raw2.trimStart();
79179
+ if (t.startsWith("[Request interrupted by user"))
79180
+ return true;
79181
+ return HARNESS_ENVELOPES.some((e) => t.startsWith(e));
79182
+ }
79183
+ function mainConversationTurn(r) {
79184
+ if (r.type !== "user" && r.type !== "assistant")
79185
+ return null;
79186
+ if (r.isMeta || r.isSidechain)
79187
+ return null;
79188
+ const content = r.message?.content;
79189
+ if (Array.isArray(content) && content.some((b) => b?.type === "tool_result")) {
79190
+ return null;
79191
+ }
79192
+ const raw2 = contentText(content);
79193
+ if (!raw2.trim() || isHarnessNoise(raw2))
79194
+ return null;
79195
+ return { role: r.type === "user" ? "user" : "assistant", raw: raw2 };
79196
+ }
79197
+ function cleanPrompt(text) {
79198
+ return text.replace(/<command-[a-z-]+>[\s\S]*?<\/command-[a-z-]+>/g, " ").replace(/<local-command-[a-z-]+>[\s\S]*?<\/local-command-[a-z-]+>/g, " ").replace(/<[^>]{1,40}>/g, " ").replace(/\s+/g, " ").trim();
79199
+ }
79200
+ function hydrateSession(row) {
79201
+ if (row.hydrated)
79202
+ return row;
79203
+ row.hydrated = true;
79204
+ if (isActive(row)) {
79205
+ try {
79206
+ row.sizeBytes = statSync6(row.file).size;
79207
+ } catch {}
79208
+ }
79209
+ const head = parseRecords(readChunk(row.file, 0, Math.min(HEAD_BYTES, row.sizeBytes)), false);
79210
+ for (const r of head) {
79211
+ if (!row.gitBranch && typeof r.gitBranch === "string")
79212
+ row.gitBranch = r.gitBranch;
79213
+ if (!row.firstPrompt && r.type === "user" && !r.isMeta) {
79214
+ const raw2 = contentText(r.message?.content);
79215
+ const t = isHarnessNoise(raw2) ? "" : cleanPrompt(raw2);
79216
+ if (t)
79217
+ row.firstPrompt = t;
79218
+ }
79219
+ if (row.gitBranch && row.firstPrompt)
79220
+ break;
79221
+ }
79222
+ const tailStart = Math.max(0, row.sizeBytes - TAIL_BYTES);
79223
+ const tail = parseRecords(readChunk(row.file, tailStart, row.sizeBytes - tailStart), tailStart > 0);
79224
+ for (let i = tail.length - 1;i >= 0; i--) {
79225
+ const r = tail[i];
79226
+ if (!row.title && r.type === "ai-title" && typeof r.aiTitle === "string" && r.aiTitle.trim()) {
79227
+ row.title = r.aiTitle.trim();
79228
+ }
79229
+ if (row.lastMessageChars === undefined && r.type === "user" && !r.isMeta) {
79230
+ const raw2 = contentText(r.message?.content);
79231
+ const t = isHarnessNoise(raw2) ? "" : cleanPrompt(raw2);
79232
+ if (t)
79233
+ row.lastMessageChars = t.length;
79234
+ }
79235
+ }
79236
+ row.recentTurns = extractRecentTurns(tail);
79237
+ return row;
79238
+ }
79239
+ function extractRecentTurns(records) {
79240
+ const out = [];
79241
+ let ai = 0;
79242
+ let user = 0;
79243
+ for (let i = records.length - 1;i >= 0; i--) {
79244
+ if (ai >= RECENT_AI_TURNS && user >= RECENT_USER_TURNS)
79245
+ break;
79246
+ const r = records[i];
79247
+ if (r.type !== "user" && r.type !== "assistant")
79248
+ continue;
79249
+ const role = r.type === "user" ? "user" : "assistant";
79250
+ if (role === "assistant" ? ai >= RECENT_AI_TURNS : user >= RECENT_USER_TURNS)
79251
+ continue;
79252
+ const turn = mainConversationTurn(r);
79253
+ if (!turn)
79254
+ continue;
79255
+ const text = cleanPrompt(turn.raw);
79256
+ if (!text)
79257
+ continue;
79258
+ out.push({ role, text });
79259
+ if (role === "assistant")
79260
+ ai++;
79261
+ else
79262
+ user++;
79263
+ }
79264
+ return out.reverse();
79265
+ }
79266
+ function hydrateConversation(row) {
79267
+ if (row.conversationDeepened)
79268
+ return row;
79269
+ row.conversationDeepened = true;
79270
+ const turns = row.recentTurns;
79271
+ if (!turns || turns.some((t) => t.role === "user"))
79272
+ return row;
79273
+ const start = Math.max(0, row.sizeBytes - DEEP_TAIL_BYTES);
79274
+ const chunk = readChunk(row.file, start, row.sizeBytes - start);
79275
+ if (!chunk)
79276
+ return row;
79277
+ const lines = chunk.split(`
79278
+ `);
79279
+ if (start > 0)
79280
+ lines.shift();
79281
+ for (let i = lines.length - 1;i >= 0; i--) {
79282
+ const line = lines[i];
79283
+ if (!line.includes('"type":"user"'))
79284
+ continue;
79285
+ let r;
79286
+ try {
79287
+ r = JSON.parse(line);
79288
+ } catch {
79289
+ continue;
79290
+ }
79291
+ const turn = mainConversationTurn(r);
79292
+ if (!turn || turn.role !== "user")
79293
+ continue;
79294
+ const text = cleanPrompt(turn.raw);
79295
+ if (!text)
79296
+ continue;
79297
+ turns.unshift({ role: "user", text });
79298
+ if (row.lastMessageChars === undefined)
79299
+ row.lastMessageChars = text.length;
79300
+ return row;
79301
+ }
79302
+ return row;
79303
+ }
79304
+ function sessionLabel(row) {
79305
+ return row.title || row.firstPrompt || row.id;
79306
+ }
79307
+ function findLatestSessionId(cwd = process.cwd(), sinceMs = 0) {
79308
+ const rows = sessionsIn(slugForPath(cwd)).filter((r) => r.mtimeMs >= sinceMs);
79309
+ if (rows.length === 0)
79310
+ return null;
79311
+ return rows.reduce((a, b) => b.mtimeMs > a.mtimeMs ? b : a).id;
79312
+ }
79313
+ var ENTRYPOINT_BYTES = 8192, PROJECTS_DIR, ACTIVE_WINDOW_MS = 120000, HEAD_BYTES, TAIL_BYTES, HARNESS_ENVELOPES, DEEP_TAIL_BYTES, RECENT_AI_TURNS = 5, RECENT_USER_TURNS = 1;
79314
+ var init_session_discovery = __esm(() => {
79315
+ PROJECTS_DIR = join37(homedir34(), ".claude", "projects");
79316
+ HEAD_BYTES = 64 * 1024;
79317
+ TAIL_BYTES = 128 * 1024;
79318
+ HARNESS_ENVELOPES = [
79319
+ "<task-notification>",
79320
+ "<system-reminder>",
79321
+ "<local-command-stdout>",
79322
+ "<user-prompt-submit-hook>"
79323
+ ];
79324
+ DEEP_TAIL_BYTES = 4 * 1024 * 1024;
79325
+ });
79326
+
79327
+ // src/session/conversation.ts
79328
+ import { closeSync as closeSync6, openSync as openSync6, readSync as readSync2, statSync as statSync7 } from "fs";
79329
+ import { StringDecoder } from "string_decoder";
79330
+ function looksLikeTurn(line) {
79331
+ const assistant = line.includes('"type":"assistant"');
79332
+ if (!assistant && !line.includes('"type":"user"'))
79333
+ return false;
79334
+ if (line.includes('"isSidechain":true'))
79335
+ return false;
79336
+ if (line.includes('"isMeta":true'))
79337
+ return false;
79338
+ if (line.includes('"type":"tool_result"'))
79339
+ return false;
79340
+ if (assistant && !line.includes('"type":"text"'))
79341
+ return false;
79342
+ return true;
79343
+ }
79344
+ function cleanTurnText(raw2) {
79345
+ let t = raw2;
79346
+ for (const re of INLINE_ENVELOPES)
79347
+ t = t.replace(re, "");
79348
+ t = t.replace(/<command-name>([\s\S]*?)<\/command-name>/g, "/$1");
79349
+ return t.replace(/\t/g, " ").replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, "").replace(/\r\n?/g, `
79350
+ `).split(`
79351
+ `).map((l) => l.replace(/\s+$/, "")).join(`
79352
+ `).replace(/\n{3,}/g, `
79353
+
79354
+ `).trim();
79355
+ }
79356
+ function readConversation(file2, opts = {}) {
79357
+ const maxTurn = opts.maxTurnChars ?? MAX_TURN_CHARS;
79358
+ const maxTotal = opts.maxTotalChars ?? MAX_TOTAL_CHARS;
79359
+ const chunkBytes = opts.chunkBytes ?? CHUNK_BYTES;
79360
+ const started = Date.now();
79361
+ const turns = [];
79362
+ let chars = 0;
79363
+ let dropped = 0;
79364
+ let anyElided = false;
79365
+ let bytes2 = 0;
79366
+ const take = (turn) => {
79367
+ turns.push(turn);
79368
+ chars += turn.text.length;
79369
+ if (chars <= maxTotal)
79370
+ return;
79371
+ let freed = 0;
79372
+ let n = 0;
79373
+ while (n < turns.length - 1 && chars - freed > maxTotal) {
79374
+ freed += turns[n].text.length;
79375
+ n++;
79376
+ }
79377
+ turns.splice(0, n);
79378
+ chars -= freed;
79379
+ dropped += n;
79380
+ };
79381
+ let fd = null;
79382
+ try {
79383
+ const size = statSync7(file2).size;
79384
+ fd = openSync6(file2, "r");
79385
+ const buf = Buffer.allocUnsafe(chunkBytes);
79386
+ const decoder = new StringDecoder("utf-8");
79387
+ let pending = "";
79388
+ let pos = 0;
79389
+ const consume = (line) => {
79390
+ if (!line || !looksLikeTurn(line))
79391
+ return;
79392
+ let record4;
79393
+ try {
79394
+ record4 = JSON.parse(line);
79395
+ } catch {
79396
+ return;
79397
+ }
79398
+ if (!record4 || typeof record4 !== "object")
79399
+ return;
79400
+ const raw2 = mainConversationTurn(record4);
79401
+ if (!raw2)
79402
+ return;
79403
+ let text = cleanTurnText(raw2.raw);
79404
+ if (!text)
79405
+ return;
79406
+ const elided = text.length > maxTurn;
79407
+ if (elided) {
79408
+ anyElided = true;
79409
+ text = `${text.slice(0, maxTurn)}
79410
+
79411
+ \u2026 turn truncated at ${maxTurn.toLocaleString()} characters`;
79412
+ }
79413
+ take({ role: raw2.role, text, elided });
79414
+ };
79415
+ while (pos < size) {
79416
+ const n = readSync2(fd, buf, 0, Math.min(chunkBytes, size - pos), pos);
79417
+ if (n <= 0)
79418
+ break;
79419
+ pos += n;
79420
+ bytes2 += n;
79421
+ pending += decoder.write(buf.subarray(0, n));
79422
+ let from = 0;
79423
+ let nl = pending.indexOf(`
79424
+ `, from);
79425
+ while (nl !== -1) {
79426
+ consume(pending.slice(from, nl));
79427
+ from = nl + 1;
79428
+ nl = pending.indexOf(`
79429
+ `, from);
79430
+ }
79431
+ if (from > 0)
79432
+ pending = pending.slice(from);
79433
+ }
79434
+ pending += decoder.end();
79435
+ if (pending)
79436
+ consume(pending);
79437
+ } catch {} finally {
79438
+ if (fd !== null) {
79439
+ try {
79440
+ closeSync6(fd);
79441
+ } catch {}
79442
+ }
79443
+ }
79444
+ return { turns, bytes: bytes2, elapsedMs: Date.now() - started, chars, dropped, anyElided };
79445
+ }
79446
+ function cpWidth(cp) {
79447
+ if (cp >= 32 && cp < 127)
79448
+ return 1;
79449
+ return displayWidth(String.fromCodePoint(cp));
79450
+ }
79451
+ function wrapOffsets(text, width) {
79452
+ const out = [];
79453
+ const w = Math.max(1, Math.floor(width));
79454
+ const n = text.length;
79455
+ let paraStart = 0;
79456
+ for (;; ) {
79457
+ let nl = text.indexOf(`
79458
+ `, paraStart);
79459
+ if (nl === -1)
79460
+ nl = n;
79461
+ let s = paraStart;
79462
+ for (;; ) {
79463
+ if (s >= nl) {
79464
+ if (s === paraStart || paraStart === nl)
79465
+ out.push({ start: s, end: nl });
79466
+ break;
79467
+ }
79468
+ let cols = 0;
79469
+ let j = s;
79470
+ let lastSpace = -1;
79471
+ while (j < nl) {
79472
+ const cp = text.codePointAt(j);
79473
+ const size = cp > 65535 ? 2 : 1;
79474
+ const cw = cpWidth(cp);
79475
+ if (cols + cw > w)
79476
+ break;
79477
+ if (cp === 32 && j > s && text.charCodeAt(j - 1) !== 32)
79478
+ lastSpace = j;
79479
+ cols += cw;
79480
+ j += size;
79481
+ }
79482
+ if (j >= nl) {
79483
+ out.push({ start: s, end: nl });
79484
+ break;
79485
+ }
79486
+ const brk = text.charCodeAt(j) === 32 ? j : lastSpace > s ? lastSpace : j;
79487
+ out.push({ start: s, end: brk });
79488
+ s = brk === j && text.charCodeAt(j) !== 32 ? j : brk + 1;
79489
+ while (s < nl && text.charCodeAt(s) === 32)
79490
+ s++;
79491
+ }
79492
+ if (nl >= n)
79493
+ break;
79494
+ paraStart = nl + 1;
79495
+ if (paraStart > n)
79496
+ break;
79497
+ if (paraStart === n) {
79498
+ out.push({ start: n, end: n });
79499
+ break;
79500
+ }
79501
+ }
79502
+ return out;
79503
+ }
79504
+ var CHUNK_BYTES, MAX_TURN_CHARS = 32000, MAX_TOTAL_CHARS = 8000000, INLINE_ENVELOPES;
79505
+ var init_conversation = __esm(() => {
79506
+ init_text();
79507
+ init_session_discovery();
79508
+ CHUNK_BYTES = 1024 * 1024;
79509
+ INLINE_ENVELOPES = [
79510
+ /<system-reminder>[\s\S]*?<\/system-reminder>/g,
79511
+ /<command-message>[\s\S]*?<\/command-message>/g,
79512
+ /<command-args>[\s\S]*?<\/command-args>/g,
79513
+ /<local-command-stdout>[\s\S]*?<\/local-command-stdout>/g,
79514
+ /<user-prompt-submit-hook>[\s\S]*?<\/user-prompt-submit-hook>/g
79515
+ ];
79516
+ });
79517
+
79518
+ // src/session/conversation-reader.tsx
79519
+ import { useKeyboard as useKeyboard3 } from "@opentui/react";
79520
+ import { useEffect as useEffect5, useMemo as useMemo3, useState as useState6 } from "react";
79521
+ import { jsxDEV as jsxDEV19, Fragment as Fragment12 } from "@opentui/react/jsx-dev-runtime";
79522
+ function layoutRows(turns, textWidth) {
79523
+ const rows = [];
79524
+ const turnStart = [];
79525
+ const turnEnd = [];
79526
+ for (let t = 0;t < turns.length; t++) {
79527
+ turnStart.push(rows.length);
79528
+ const slices = wrapOffsets(turns[t].text, textWidth);
79529
+ for (let i = 0;i < slices.length; i++) {
79530
+ rows.push({ turn: t, first: i === 0, start: slices[i].start, end: slices[i].end });
79531
+ }
79532
+ turnEnd.push(rows.length);
79533
+ if (t < turns.length - 1)
79534
+ rows.push({ turn: -1, first: false, start: 0, end: 0 });
79535
+ }
79536
+ return { rows, turnStart, turnEnd };
79537
+ }
79538
+ function buildSearch(turns, rows, turnStart, turnEnd, query) {
79539
+ if (!query)
79540
+ return EMPTY_SEARCH;
79541
+ const q = query.toLowerCase();
79542
+ const hits = [];
79543
+ const ranges = new Map;
79544
+ let capped = false;
79545
+ for (let t = 0;t < turns.length && !capped; t++) {
79546
+ const hay = turns[t].text.toLowerCase();
79547
+ let at = hay.indexOf(q);
79548
+ if (at === -1)
79549
+ continue;
79550
+ const lo = turnStart[t];
79551
+ const hi = turnEnd[t];
79552
+ let r = lo;
79553
+ while (at !== -1) {
79554
+ if (hits.length >= MAX_MATCHES) {
79555
+ capped = true;
79556
+ break;
79557
+ }
79558
+ const end = at + q.length;
79559
+ while (r < hi - 1 && rows[r].end <= at)
79560
+ r++;
79561
+ const hit = hits.length;
79562
+ hits.push(r);
79563
+ for (let k = r;k < hi; k++) {
79564
+ const row = rows[k];
79565
+ if (row.start >= end)
79566
+ break;
79567
+ const s = Math.max(at, row.start);
79568
+ const e = Math.min(end, row.end);
79569
+ if (e > s) {
79570
+ const list = ranges.get(k);
79571
+ if (list)
79572
+ list.push({ s: s - row.start, e: e - row.start, hit });
79573
+ else
79574
+ ranges.set(k, [{ s: s - row.start, e: e - row.start, hit }]);
79575
+ }
79576
+ }
79577
+ at = hay.indexOf(q, at + 1);
79578
+ }
79579
+ }
79580
+ return { hits, ranges, capped };
79581
+ }
79582
+ function ConversationReader({
79583
+ row,
79584
+ conv,
79585
+ width,
79586
+ height: height2,
79587
+ onClose,
79588
+ onResume,
79589
+ onCancel
79590
+ }) {
79591
+ const [top, setTop] = useState6(0);
79592
+ const [query, setQuery] = useState6("");
79593
+ const [typing, setTyping] = useState6(false);
79594
+ const [matchIdx, setMatchIdx] = useState6(0);
79595
+ const CHROME_ROWS = 5;
79596
+ const viewport = Math.max(1, height2 - CHROME_ROWS);
79597
+ const inner = Math.max(20, width - 2);
79598
+ const contentW = inner - BAR_W;
79599
+ const textW = Math.max(8, contentW - GUTTER - 1);
79600
+ const turns = useMemo3(() => conv?.turns ?? NO_TURNS, [conv]);
79601
+ const { rows, turnStart, turnEnd } = useMemo3(() => layoutRows(turns, textW), [turns, textW]);
79602
+ const search = useMemo3(() => buildSearch(turns, rows, turnStart, turnEnd, query), [turns, rows, turnStart, turnEnd, query]);
79603
+ const maxTop = Math.max(0, rows.length - viewport);
79604
+ const clampTop = (v) => Math.max(0, Math.min(v, maxTop));
79605
+ const reveal = (r) => setTop(clampTop(r - Math.floor(viewport / 3)));
79606
+ useEffect5(() => {
79607
+ setTop(Math.max(0, rows.length - viewport));
79608
+ }, [rows.length, viewport]);
79609
+ useEffect5(() => {
79610
+ if (search.hits.length === 0)
79611
+ return;
79612
+ let i = search.hits.findIndex((r) => r >= top);
79613
+ if (i === -1)
79614
+ i = 0;
79615
+ setMatchIdx(i);
79616
+ reveal(search.hits[i]);
79617
+ }, [search]);
79618
+ const step = (d) => {
79619
+ if (search.hits.length === 0)
79620
+ return;
79621
+ const i = (matchIdx + d + search.hits.length) % search.hits.length;
79622
+ setMatchIdx(i);
79623
+ reveal(search.hits[i]);
79624
+ };
79625
+ useKeyboard3((key) => {
79626
+ const name = key.name;
79627
+ if (key.ctrl && name === "c") {
79628
+ onCancel();
79629
+ return;
79630
+ }
79631
+ if (name === "escape") {
79632
+ if (typing || query) {
79633
+ setTyping(false);
79634
+ setQuery("");
79635
+ return;
79636
+ }
79637
+ onClose();
79638
+ return;
79639
+ }
79640
+ if (name === "return" || name === "enter") {
79641
+ if (typing) {
79642
+ setTyping(false);
79643
+ return;
79644
+ }
79645
+ onResume();
79646
+ return;
79647
+ }
79648
+ if (name === "up")
79649
+ return setTop((t) => clampTop(t - 1));
79650
+ if (name === "down")
79651
+ return setTop((t) => clampTop(t + 1));
79652
+ if (name === "pageup")
79653
+ return setTop((t) => clampTop(t - viewport));
79654
+ if (name === "pagedown")
79655
+ return setTop((t) => clampTop(t + viewport));
79656
+ if (name === "home")
79657
+ return setTop(0);
79658
+ if (name === "end")
79659
+ return setTop(maxTop);
79660
+ if (name === "backspace") {
79661
+ if (typing)
79662
+ setQuery((q) => q.slice(0, -1));
79663
+ return;
79664
+ }
79665
+ const ch = key.raw;
79666
+ const printable = ch && ch.length === 1 && ch >= " " && ch !== "\x7F";
79667
+ if (typing) {
79668
+ if (printable)
79669
+ setQuery((q) => q + ch);
79670
+ return;
79671
+ }
79672
+ if (name === "slash" || ch === "/") {
79673
+ setTyping(true);
79674
+ return;
79675
+ }
79676
+ if (name === "n")
79677
+ return step(key.shift ? -1 : 1);
79678
+ if (ch === "g")
79679
+ return setTop(0);
79680
+ if (ch === "G")
79681
+ return setTop(maxTop);
79682
+ if (name === "space")
79683
+ return setTop((t) => clampTop(t + viewport));
79684
+ });
79685
+ const label = sessionLabel(row);
79686
+ const mb = row.sizeBytes / 1048576;
79687
+ const size = mb >= 0.1 ? `${mb.toFixed(1)} MB` : `${Math.max(1, Math.round(row.sizeBytes / 1024))} KB`;
79688
+ const stats = conv ? `${turns.length} turns \xB7 ${rows.length} lines \xB7 ${size} transcript` : `reading ${size}\u2026`;
79689
+ const visible = rows.slice(top, top + viewport);
79690
+ const barCells = useMemo3(() => scrollbarCells(viewport, rows.length, top, search.hits), [viewport, rows.length, top, search.hits]);
79691
+ const pct = rows.length <= viewport ? 100 : top / maxTop * 100;
79692
+ const POSITION_COLS = 26;
79693
+ const matchLabel = search.hits.length ? `${matchIdx + 1} of ${search.hits.length}${search.capped ? "+" : ""} matches` : query ? "no matches" : "";
79694
+ return /* @__PURE__ */ jsxDEV19("box", {
79695
+ flexDirection: "column",
79696
+ height: height2,
79697
+ backgroundColor: C.bg,
79698
+ children: [
79699
+ /* @__PURE__ */ jsxDEV19("box", {
79700
+ flexDirection: "row",
79701
+ justifyContent: "space-between",
79702
+ height: 1,
79703
+ paddingX: 1,
79704
+ children: [
79705
+ /* @__PURE__ */ jsxDEV19("text", {
79706
+ children: [
79707
+ /* @__PURE__ */ jsxDEV19("span", {
79708
+ fg: tokens.accent,
79709
+ attributes: A.bold,
79710
+ children: "claudish"
79711
+ }, undefined, false, undefined, this),
79712
+ /* @__PURE__ */ jsxDEV19("span", {
79713
+ fg: tokens.subtle,
79714
+ children: " reader"
79715
+ }, undefined, false, undefined, this),
79716
+ conv && conv.dropped > 0 ? /* @__PURE__ */ jsxDEV19("span", {
79717
+ fg: tokens.warn,
79718
+ children: ` ${conv.dropped} older turns dropped (cap)`
79719
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {}, undefined, false, undefined, this),
79720
+ conv?.anyElided ? /* @__PURE__ */ jsxDEV19("span", {
79721
+ fg: tokens.warn,
79722
+ children: " long turns truncated"
79723
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {}, undefined, false, undefined, this)
79724
+ ]
79725
+ }, undefined, true, undefined, this),
79726
+ /* @__PURE__ */ jsxDEV19("text", {
79727
+ children: /* @__PURE__ */ jsxDEV19("span", {
79728
+ fg: tokens.subtle,
79729
+ children: stats
79730
+ }, undefined, false, undefined, this)
79731
+ }, undefined, false, undefined, this)
79732
+ ]
79733
+ }, undefined, true, undefined, this),
79734
+ /* @__PURE__ */ jsxDEV19(Panel, {
79735
+ title: `conversation \xB7 ${truncate3(label, Math.max(10, width - 20))}`,
79736
+ flush: true,
79737
+ flexGrow: 1,
79738
+ flexBasis: 0,
79739
+ children: !conv ? /* @__PURE__ */ jsxDEV19("text", {
79740
+ fg: tokens.subtle,
79741
+ children: " reading transcript\u2026"
79742
+ }, undefined, false, undefined, this) : rows.length === 0 ? /* @__PURE__ */ jsxDEV19("text", {
79743
+ fg: tokens.trace,
79744
+ children: " no conversation recorded \u2014 this transcript has no main-thread prose"
79745
+ }, undefined, false, undefined, this) : visible.map((r, i) => {
79746
+ const line = top + i;
79747
+ return /* @__PURE__ */ jsxDEV19(ReaderRow, {
79748
+ row: r,
79749
+ turn: r.turn >= 0 ? turns[r.turn] : undefined,
79750
+ hl: search.ranges.get(line),
79751
+ current: matchIdx,
79752
+ width: contentW,
79753
+ bar: barCells[i] ?? "track"
79754
+ }, line, false, undefined, this);
79755
+ })
79756
+ }, undefined, false, undefined, this),
79757
+ /* @__PURE__ */ jsxDEV19("box", {
79758
+ flexDirection: "row",
79759
+ justifyContent: "space-between",
79760
+ height: 1,
79761
+ paddingX: 1,
79762
+ children: [
79763
+ /* @__PURE__ */ jsxDEV19("text", {
79764
+ children: query || typing ? /* @__PURE__ */ jsxDEV19(Fragment12, {
79765
+ children: [
79766
+ /* @__PURE__ */ jsxDEV19("span", {
79767
+ fg: tokens.warn,
79768
+ attributes: A.bold,
79769
+ children: truncate3(`/${query}`, Math.max(4, width - POSITION_COLS - displayWidth(matchLabel) - 4))
79770
+ }, undefined, false, undefined, this),
79771
+ /* @__PURE__ */ jsxDEV19("span", {
79772
+ fg: typing ? tokens.warn : tokens.trace,
79773
+ children: typing ? "\u258C" : " "
79774
+ }, undefined, false, undefined, this),
79775
+ /* @__PURE__ */ jsxDEV19("span", {
79776
+ fg: search.hits.length ? tokens.subtle : tokens.trace,
79777
+ children: matchLabel ? ` ${matchLabel}` : ""
79778
+ }, undefined, false, undefined, this)
79779
+ ]
79780
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {
79781
+ fg: tokens.trace,
79782
+ children: "/ to search"
79783
+ }, undefined, false, undefined, this)
79784
+ }, undefined, false, undefined, this),
79785
+ /* @__PURE__ */ jsxDEV19("text", {
79786
+ children: [
79787
+ /* @__PURE__ */ jsxDEV19("span", {
79788
+ fg: tokens.subtle,
79789
+ children: `${Math.min(rows.length, top + viewport)}/${rows.length} `
79790
+ }, undefined, false, undefined, this),
79791
+ /* @__PURE__ */ jsxDEV19(MeterSpan, {
79792
+ pct,
79793
+ width: 12,
79794
+ ramp: ramps.volume
79795
+ }, undefined, false, undefined, this)
79796
+ ]
79797
+ }, undefined, true, undefined, this)
79798
+ ]
79799
+ }, undefined, true, undefined, this),
79800
+ /* @__PURE__ */ jsxDEV19("box", {
79801
+ flexDirection: "row",
79802
+ height: 1,
79803
+ paddingX: 1,
79804
+ gap: 2,
79805
+ children: [
79806
+ /* @__PURE__ */ jsxDEV19("text", {
79807
+ fg: tokens.subtle,
79808
+ children: "\u2191\u2193 \u21DE\u21DF scroll"
79809
+ }, undefined, false, undefined, this),
79810
+ /* @__PURE__ */ jsxDEV19("text", {
79811
+ fg: tokens.subtle,
79812
+ children: "g/G ends"
79813
+ }, undefined, false, undefined, this),
79814
+ /* @__PURE__ */ jsxDEV19("text", {
79815
+ fg: typing ? tokens.warn : tokens.subtle,
79816
+ children: "/ search"
79817
+ }, undefined, false, undefined, this),
79818
+ /* @__PURE__ */ jsxDEV19("text", {
79819
+ fg: search.hits.length ? tokens.subtle : tokens.trace,
79820
+ children: "n/N match"
79821
+ }, undefined, false, undefined, this),
79822
+ /* @__PURE__ */ jsxDEV19("text", {
79823
+ fg: tokens.accent,
79824
+ children: "\u23CE resume"
79825
+ }, undefined, false, undefined, this),
79826
+ /* @__PURE__ */ jsxDEV19("text", {
79827
+ fg: tokens.subtle,
79828
+ children: "esc back"
79829
+ }, undefined, false, undefined, this)
79830
+ ]
79831
+ }, undefined, true, undefined, this)
79832
+ ]
79833
+ }, undefined, true, undefined, this);
79834
+ }
79835
+ function scrollbarCells(viewport, total, top, hits) {
79836
+ const cells = new Array(viewport).fill("track");
79837
+ if (total <= 0)
79838
+ return cells;
79839
+ if (total > viewport) {
79840
+ const size = Math.max(1, Math.round(viewport / total * viewport));
79841
+ const span = Math.max(1, total - viewport);
79842
+ const start = Math.min(viewport - size, Math.round(top / span * (viewport - size)));
79843
+ for (let i = 0;i < size; i++)
79844
+ cells[start + i] = "thumb";
79845
+ } else {
79846
+ cells.fill("thumb");
79847
+ }
79848
+ for (const h of hits) {
79849
+ const i = Math.min(viewport - 1, Math.floor(h / total * viewport));
79850
+ if (i >= 0)
79851
+ cells[i] = "hit";
79852
+ }
79853
+ return cells;
79854
+ }
79855
+ function ReaderRow({
79856
+ row,
79857
+ turn,
79858
+ hl,
79859
+ current,
79860
+ width,
79861
+ bar
79862
+ }) {
79863
+ if (!turn) {
79864
+ return /* @__PURE__ */ jsxDEV19("text", {
79865
+ children: [
79866
+ /* @__PURE__ */ jsxDEV19("span", {
79867
+ children: " ".repeat(Math.max(0, width))
79868
+ }, undefined, false, undefined, this),
79869
+ /* @__PURE__ */ jsxDEV19("span", {
79870
+ bg: BAR_COLOR[bar],
79871
+ children: " "
79872
+ }, undefined, false, undefined, this)
79873
+ ]
79874
+ }, undefined, true, undefined, this);
79875
+ }
79876
+ const text = turn.text.slice(row.start, row.end);
79877
+ const fg = turn.role === "user" ? tokens.text : C.fgMuted;
79878
+ const railFg = turn.role === "user" ? SPEAKER.you : SPEAKER.ai;
79879
+ const pad2 = Math.max(0, width - GUTTER - displayWidth(text));
79880
+ return /* @__PURE__ */ jsxDEV19("text", {
79881
+ children: [
79882
+ /* @__PURE__ */ jsxDEV19("span", {
79883
+ fg: railFg,
79884
+ children: RAIL
79885
+ }, undefined, false, undefined, this),
79886
+ row.first ? /* @__PURE__ */ jsxDEV19(BadgeSpan, {
79887
+ label: turn.role === "user" ? "you" : "ai",
79888
+ bg: turn.role === "user" ? SPEAKER.you : SPEAKER.ai,
79889
+ width: ROLE_W
79890
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {
79891
+ children: " ".repeat(ROLE_W)
79892
+ }, undefined, false, undefined, this),
79893
+ hl && hl.length > 0 ? highlighted(text, hl, current, fg) : /* @__PURE__ */ jsxDEV19("span", {
79894
+ fg,
79895
+ children: text
79896
+ }, undefined, false, undefined, this),
79897
+ /* @__PURE__ */ jsxDEV19("span", {
79898
+ children: " ".repeat(pad2)
79899
+ }, undefined, false, undefined, this),
79900
+ /* @__PURE__ */ jsxDEV19("span", {
79901
+ bg: BAR_COLOR[bar],
79902
+ children: " "
79903
+ }, undefined, false, undefined, this)
79904
+ ]
79905
+ }, undefined, true, undefined, this);
79906
+ }
79907
+ function highlighted(text, hl, current, fg) {
79908
+ const out = [];
79909
+ let from = 0;
79910
+ for (let i = 0;i < hl.length; i++) {
79911
+ const { s, e, hit } = hl[i];
79912
+ if (e <= from)
79913
+ continue;
79914
+ const start = Math.max(from, s);
79915
+ if (start > from)
79916
+ out.push(/* @__PURE__ */ jsxDEV19("span", {
79917
+ fg,
79918
+ children: text.slice(from, start)
79919
+ }, `p${i}`, false, undefined, this));
79920
+ const bg = hit === current ? tokens.accent : tokens.warn;
79921
+ out.push(/* @__PURE__ */ jsxDEV19("span", {
79922
+ fg: tokens.ink,
79923
+ bg,
79924
+ attributes: A.bold,
79925
+ children: text.slice(start, e)
79926
+ }, `h${i}`, false, undefined, this));
79927
+ from = e;
79928
+ }
79929
+ if (from < text.length)
79930
+ out.push(/* @__PURE__ */ jsxDEV19("span", {
79931
+ fg,
79932
+ children: text.slice(from)
79933
+ }, "tail", false, undefined, this));
79934
+ return /* @__PURE__ */ jsxDEV19(Fragment12, {
79935
+ children: out
79936
+ }, undefined, false, undefined, this);
79937
+ }
79938
+ var RAIL = "\u258D", RAIL_W = 2, ROLE_W = 6, GUTTER, BAR_W = 1, SPEAKER, MAX_MATCHES = 5000, EMPTY_SEARCH, NO_TURNS, BAR_COLOR;
79939
+ var init_conversation_reader = __esm(() => {
79940
+ init_theme2();
79941
+ init_text();
79942
+ init_tokens();
79943
+ init_widgets();
79944
+ init_conversation();
79945
+ init_session_discovery();
79946
+ GUTTER = RAIL_W + ROLE_W;
79947
+ SPEAKER = {
79948
+ you: "#39d353",
79949
+ ai: "#39c5cf"
79950
+ };
79951
+ EMPTY_SEARCH = { hits: [], ranges: new Map, capped: false };
79952
+ NO_TURNS = [];
79953
+ BAR_COLOR = {
79954
+ track: tokens.border,
79955
+ thumb: tokens.accent,
79956
+ hit: tokens.warn
79957
+ };
79958
+ });
79959
+
79960
+ // src/session/resume-picker.tsx
79961
+ import { useKeyboard as useKeyboard4, useTerminalDimensions as useTerminalDimensions3 } from "@opentui/react";
79962
+ import { useEffect as useEffect6, useMemo as useMemo4, useState as useState7 } from "react";
79963
+ import { jsxDEV as jsxDEV20 } from "@opentui/react/jsx-dev-runtime";
79964
+ function detailTurns(height2) {
79965
+ return height2 >= 40 ? 6 : height2 >= 30 ? 4 : 2;
79966
+ }
79967
+ function fuzzy(needle, hay) {
79968
+ if (!needle)
79969
+ return true;
79970
+ const n = needle.toLowerCase();
79971
+ const h = hay.toLowerCase();
79972
+ let i = 0;
79973
+ for (let j = 0;j < h.length && i < n.length; j++)
79974
+ if (h[j] === n[i])
79975
+ i++;
79976
+ return i === n.length;
79977
+ }
79978
+ function age(ms) {
79979
+ const s = Math.max(0, Math.round((Date.now() - ms) / 1000));
79980
+ if (s < 60)
79981
+ return `${s}s`;
79982
+ const m = Math.floor(s / 60);
79983
+ if (m < 60)
79984
+ return `${m}m`;
79985
+ const h = Math.floor(m / 60);
79986
+ if (h < 24)
79987
+ return `${h}h`;
79988
+ return `${Math.floor(h / 24)}d`;
79989
+ }
79990
+ function chipW(label) {
79991
+ return label + 2;
79992
+ }
79993
+ function blockWidth(c) {
79994
+ return LIVE_W + chipW(c.age) + chipW(c.count) + (c.dirty > 0 ? chipW(c.dirty + 1) : 0) + (c.sync > 0 ? 2 * chipW(c.sync + 1) : 0);
79995
+ }
79996
+ function sizePct(bytes2, max) {
79997
+ const lo = Math.log(SIZE_FLOOR_BYTES);
79998
+ const hi = Math.log(Math.max(max, SIZE_FLOOR_BYTES * 2));
79999
+ const v = Math.log(Math.max(bytes2, SIZE_FLOOR_BYTES));
80000
+ return Math.max(0, Math.min(100, (v - lo) / (hi - lo) * 100));
80001
+ }
80002
+ function Slot({
80003
+ label,
80004
+ bg,
80005
+ labelW
80006
+ }) {
80007
+ if (!label || !bg)
80008
+ return /* @__PURE__ */ jsxDEV20("span", {
80009
+ children: " ".repeat(chipW(labelW))
80010
+ }, undefined, false, undefined, this);
80011
+ return /* @__PURE__ */ jsxDEV20(BadgeSpan, {
80012
+ label,
80013
+ bg
80014
+ }, undefined, false, undefined, this);
80015
+ }
80016
+ function WorktreeRow({
80017
+ g,
80018
+ cursor,
80019
+ width,
80020
+ count,
80021
+ cols
80022
+ }) {
80023
+ const nameColor = !g.live ? tokens.dead : g.current ? tokens.success : tokens.text;
80024
+ const stale = !g.lastActiveMs || Date.now() - g.lastActiveMs >= STALE_MS;
80025
+ const room = Math.max(0, width - blockWidth(cols));
80026
+ const series = room >= SPARK_MIN_DAYS ? activitySeries(g.sessions, room) : null;
80027
+ const spark = series && hasActivity(series) ? series : null;
80028
+ const chips = [];
80029
+ if (cols.sync > 0 && g.ahead) {
80030
+ chips.push({
80031
+ label: `\u2191${padStartTo(String(g.ahead), cols.sync)}`,
80032
+ bg: CHIP.ahead,
80033
+ labelW: cols.sync + 1
80034
+ });
80035
+ }
80036
+ if (cols.sync > 0 && g.behind) {
80037
+ chips.push({
80038
+ label: `\u2193${padStartTo(String(g.behind), cols.sync)}`,
80039
+ bg: CHIP.behind,
80040
+ labelW: cols.sync + 1
80041
+ });
80042
+ }
80043
+ if (cols.dirty > 0 && g.dirty) {
80044
+ chips.push({
80045
+ label: `${DIRTY_GLYPH}${padStartTo(String(g.dirty), cols.dirty)}`,
80046
+ bg: CHIP.dirty,
80047
+ labelW: cols.dirty + 1
80048
+ });
80049
+ }
80050
+ chips.push({ label: padStartTo(String(count), cols.count), bg: CHIP.count, labelW: cols.count });
80051
+ chips.push({
80052
+ label: padStartTo(g.lastActiveMs ? age(g.lastActiveMs) : "\u2014", cols.age),
80053
+ bg: stale ? CHIP.stale : CHIP.fresh,
80054
+ labelW: cols.age
80055
+ });
80056
+ const used = chips.reduce((w, c) => w + c.labelW + 2, 0) + LIVE_W;
80057
+ const gap = Math.max(0, width - room - used);
80058
+ return /* @__PURE__ */ jsxDEV20("box", {
80059
+ flexDirection: "column",
80060
+ height: 2,
80061
+ backgroundColor: cursor ? C.bgHighlight : undefined,
80062
+ children: [
80063
+ /* @__PURE__ */ jsxDEV20("text", {
80064
+ children: /* @__PURE__ */ jsxDEV20("span", {
80065
+ fg: nameColor,
80066
+ attributes: cursor || g.current ? A.bold : undefined,
80067
+ children: truncate3(g.name, width)
80068
+ }, undefined, false, undefined, this)
80069
+ }, undefined, false, undefined, this),
80070
+ /* @__PURE__ */ jsxDEV20("box", {
80071
+ height: 1,
80072
+ children: /* @__PURE__ */ jsxDEV20("text", {
80073
+ children: [
80074
+ spark ? /* @__PURE__ */ jsxDEV20(SparklineSpan, {
80075
+ values: spark,
80076
+ fg: SPARK_FG
80077
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("span", {
80078
+ children: " ".repeat(room)
80079
+ }, undefined, false, undefined, this),
80080
+ /* @__PURE__ */ jsxDEV20("span", {
80081
+ children: " ".repeat(gap)
80082
+ }, undefined, false, undefined, this),
80083
+ /* @__PURE__ */ jsxDEV20("span", {
80084
+ fg: tokens.success,
80085
+ children: g.activeNow ? "\u25CF " : " "
80086
+ }, undefined, false, undefined, this),
80087
+ chips.map((c) => /* @__PURE__ */ jsxDEV20(Slot, {
80088
+ label: c.label,
80089
+ bg: c.bg,
80090
+ labelW: c.labelW
80091
+ }, c.bg + c.label, false, undefined, this))
80092
+ ]
80093
+ }, undefined, true, undefined, this)
80094
+ }, undefined, false, undefined, this)
80095
+ ]
80096
+ }, undefined, true, undefined, this);
80097
+ }
80098
+ function dailyActivity(groups, days) {
80099
+ const day = 86400000;
80100
+ const today = Math.floor(Date.now() / day);
80101
+ const buckets = new Array(days).fill(0);
80102
+ for (const g of groups) {
80103
+ for (const s of g.sessions) {
80104
+ const idx = days - 1 - (today - Math.floor(s.mtimeMs / day));
80105
+ if (idx >= 0 && idx < days)
80106
+ buckets[idx] += 1;
80107
+ }
80108
+ }
80109
+ return buckets;
80110
+ }
80111
+ function activityLevels(days) {
80112
+ const nz = days.filter((v) => v > 0).sort((a, b) => a - b);
80113
+ if (nz.length === 0)
80114
+ return days.map(() => 0);
80115
+ const at = (p) => nz[Math.min(nz.length - 1, Math.floor(nz.length * p))];
80116
+ const q1 = at(0.25);
80117
+ const q2 = at(0.5);
80118
+ const q3 = at(0.75);
80119
+ return days.map((v) => v <= 0 ? 0 : v <= q1 ? 1 : v <= q2 ? 2 : v <= q3 ? 3 : 4);
80120
+ }
80121
+ function ActivityCalendar({
80122
+ days,
80123
+ width
80124
+ }) {
80125
+ const levels = activityLevels(days);
80126
+ const title = `activity \xB7 ${ACTIVITY_WEEKS}w `;
80127
+ const grid = Math.max(WEEK_DAYS, width - WEEK_LABEL_W);
80128
+ const base = Math.floor(grid / WEEK_DAYS);
80129
+ const extra = grid - base * WEEK_DAYS;
80130
+ return /* @__PURE__ */ jsxDEV20("box", {
80131
+ flexDirection: "column",
80132
+ flexShrink: 0,
80133
+ paddingTop: 1,
80134
+ children: [
80135
+ /* @__PURE__ */ jsxDEV20("text", {
80136
+ children: [
80137
+ /* @__PURE__ */ jsxDEV20("span", {
80138
+ fg: tokens.subtle,
80139
+ attributes: A.bold,
80140
+ children: title
80141
+ }, undefined, false, undefined, this),
80142
+ /* @__PURE__ */ jsxDEV20("span", {
80143
+ fg: tokens.border,
80144
+ children: "\u2500".repeat(Math.max(0, width - title.length))
80145
+ }, undefined, false, undefined, this)
80146
+ ]
80147
+ }, undefined, true, undefined, this),
80148
+ Array.from({ length: ACTIVITY_WEEKS }, (_, w) => {
80149
+ const ago = ACTIVITY_WEEKS - 1 - w;
80150
+ return /* @__PURE__ */ jsxDEV20("text", {
80151
+ children: [
80152
+ /* @__PURE__ */ jsxDEV20("span", {
80153
+ fg: tokens.trace,
80154
+ children: padTo(ago === 0 ? "now" : `-${ago}w`, WEEK_LABEL_W)
80155
+ }, undefined, false, undefined, this),
80156
+ Array.from({ length: WEEK_DAYS }, (_2, d) => /* @__PURE__ */ jsxDEV20("span", {
80157
+ bg: GH_LEVELS[levels[w * WEEK_DAYS + d] ?? 0],
80158
+ children: " ".repeat(base + (d < extra ? 1 : 0))
80159
+ }, d, false, undefined, this))
80160
+ ]
80161
+ }, w, true, undefined, this);
80162
+ })
80163
+ ]
80164
+ }, undefined, true, undefined, this);
80165
+ }
80166
+ function SectionHeader({ label, width }) {
80167
+ const text = `${label} `;
80168
+ return /* @__PURE__ */ jsxDEV20("box", {
80169
+ flexDirection: "column",
80170
+ height: 2,
80171
+ children: [
80172
+ /* @__PURE__ */ jsxDEV20("text", {
80173
+ children: " "
80174
+ }, undefined, false, undefined, this),
80175
+ /* @__PURE__ */ jsxDEV20("text", {
80176
+ children: [
80177
+ /* @__PURE__ */ jsxDEV20("span", {
80178
+ fg: tokens.subtle,
80179
+ attributes: A.bold,
80180
+ children: text
80181
+ }, undefined, false, undefined, this),
80182
+ /* @__PURE__ */ jsxDEV20("span", {
80183
+ fg: tokens.border,
80184
+ children: "\u2500".repeat(Math.max(0, width - text.length))
80185
+ }, undefined, false, undefined, this)
80186
+ ]
80187
+ }, undefined, true, undefined, this)
80188
+ ]
80189
+ }, undefined, true, undefined, this);
80190
+ }
80191
+ function hasActivity(series) {
80192
+ return series.some((v) => v > 0);
80193
+ }
80194
+ function activitySeries(sessions2, days = WEEK_DAYS * 2) {
80195
+ const day = 86400000;
80196
+ const today = Math.floor(Date.now() / day);
80197
+ const buckets = new Array(days).fill(0);
80198
+ for (const s of sessions2) {
80199
+ const idx = days - 1 - (today - Math.floor(s.mtimeMs / day));
80200
+ if (idx >= 0 && idx < days)
80201
+ buckets[idx] += 1;
80202
+ }
80203
+ return buckets;
80204
+ }
80205
+ function SessionRowView({
80206
+ row,
80207
+ cursor,
80208
+ width,
80209
+ even,
80210
+ indent = 0,
80211
+ maxSize,
80212
+ meterW
80213
+ }) {
80214
+ const live = isActive(row);
80215
+ const pad2 = " ".repeat(indent);
80216
+ const mb = row.sizeBytes / 1048576;
80217
+ const size = mb >= 0.1 ? `${mb.toFixed(1)} MB` : `${Math.max(1, Math.round(row.sizeBytes / 1024))} KB`;
80218
+ const titleW = Math.max(10, width - indent - 2);
80219
+ const metaPad = `${pad2} `;
80220
+ const SIZE_COL = 8;
80221
+ return /* @__PURE__ */ jsxDEV20("box", {
80222
+ flexDirection: "column",
80223
+ height: 2,
80224
+ backgroundColor: cursor ? C.bgHighlight : even ? undefined : C.bgAlt,
80225
+ children: [
80226
+ /* @__PURE__ */ jsxDEV20("text", {
80227
+ children: [
80228
+ /* @__PURE__ */ jsxDEV20("span", {
80229
+ fg: live ? tokens.success : tokens.border,
80230
+ children: `${pad2}${live ? "\u25CF" : "\xB7"} `
80231
+ }, undefined, false, undefined, this),
80232
+ /* @__PURE__ */ jsxDEV20("span", {
80233
+ fg: tokens.text,
80234
+ attributes: cursor ? A.bold : undefined,
80235
+ children: truncate3(sessionLabel(row), titleW)
80236
+ }, undefined, false, undefined, this)
80237
+ ]
80238
+ }, undefined, true, undefined, this),
80239
+ /* @__PURE__ */ jsxDEV20("text", {
80240
+ children: [
80241
+ /* @__PURE__ */ jsxDEV20("span", {
80242
+ children: metaPad
80243
+ }, undefined, false, undefined, this),
80244
+ /* @__PURE__ */ jsxDEV20(BadgeSpan, {
80245
+ label: padStartTo(age(row.mtimeMs), SESSION_AGE_W),
80246
+ bg: Date.now() - row.mtimeMs < STALE_MS ? CHIP.fresh : CHIP.stale,
80247
+ width: SESSION_AGE_COL
80248
+ }, undefined, false, undefined, this),
80249
+ /* @__PURE__ */ jsxDEV20(MeterSpan, {
80250
+ pct: sizePct(row.sizeBytes, maxSize),
80251
+ width: meterW,
80252
+ ramp: ramps.volume
80253
+ }, undefined, false, undefined, this),
80254
+ /* @__PURE__ */ jsxDEV20("span", {
80255
+ fg: MUTED,
80256
+ children: padStartTo(size, SIZE_COL)
80257
+ }, undefined, false, undefined, this),
80258
+ row.gitBranch ? /* @__PURE__ */ jsxDEV20("span", {
80259
+ fg: tokens.trace,
80260
+ children: ` ${BRANCH_ICON} ${truncate3(row.gitBranch, Math.max(6, width - indent - 2 - SESSION_AGE_COL - meterW - SIZE_COL - BRANCH_LEAD_DENSE))}`
80261
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("span", {}, undefined, false, undefined, this)
80262
+ ]
80263
+ }, undefined, true, undefined, this)
80264
+ ]
80265
+ }, undefined, true, undefined, this);
80266
+ }
80267
+ function AgentNode({
80268
+ count,
80269
+ open,
80270
+ cursor,
80271
+ width
80272
+ }) {
80273
+ const label = `${count} agent session${count === 1 ? "" : "s"}`;
80274
+ const hint = open ? "enter to collapse" : "enter to expand \xB7 showing 3";
80275
+ return /* @__PURE__ */ jsxDEV20("box", {
80276
+ height: 1,
80277
+ backgroundColor: cursor ? C.bgHighlight : undefined,
80278
+ children: /* @__PURE__ */ jsxDEV20("text", {
80279
+ children: [
80280
+ /* @__PURE__ */ jsxDEV20("span", {
80281
+ fg: cursor ? tokens.accent : tokens.trace,
80282
+ children: `${cursor ? "\u258D" : " "} `
80283
+ }, undefined, false, undefined, this),
80284
+ /* @__PURE__ */ jsxDEV20("span", {
80285
+ fg: tokens.warn,
80286
+ children: open ? "\u25BE " : "\u25B8 "
80287
+ }, undefined, false, undefined, this),
80288
+ /* @__PURE__ */ jsxDEV20("span", {
80289
+ fg: cursor ? tokens.text : tokens.subtle,
80290
+ children: label
80291
+ }, undefined, false, undefined, this),
80292
+ /* @__PURE__ */ jsxDEV20("span", {
80293
+ fg: tokens.trace,
80294
+ children: truncate3(` ${hint}`, Math.max(0, width - label.length - 6))
80295
+ }, undefined, false, undefined, this)
80296
+ ]
80297
+ }, undefined, true, undefined, this)
80298
+ }, undefined, false, undefined, this);
80299
+ }
80300
+ function ResumePicker({ groups, onDone }) {
80301
+ const { width, height: height2 } = useTerminalDimensions3();
80302
+ const [pane, setPane] = useState7("worktrees");
80303
+ const [wtCursor, setWtCursor] = useState7(0);
80304
+ const [sessCursor, setSessCursor] = useState7(0);
80305
+ const [filter, setFilter] = useState7("");
80306
+ const [agentsOpen, setAgentsOpen] = useState7(false);
80307
+ const [reader, setReader] = useState7(null);
80308
+ const [, setTick] = useState7(0);
80309
+ const listed = useMemo4(() => {
80310
+ const m = new Map;
80311
+ for (const g of groups)
80312
+ m.set(g.name, g.sessions.filter((s) => !isAgentSession(s)));
80313
+ return m;
80314
+ }, [groups]);
80315
+ const { fresh, stale, visibleGroups } = useMemo4(() => {
80316
+ const withSessions = groups.filter((g) => (listed.get(g.name)?.length ?? 0) > 0);
80317
+ const matching = filter ? withSessions.filter((g) => fuzzy(filter, g.name)) : withSessions;
80318
+ const now = Date.now();
80319
+ const byRecency = (a, b) => b.lastActiveMs - a.lastActiveMs;
80320
+ const f = matching.filter((g) => g.current || now - g.lastActiveMs < STALE_MS).sort((a, b) => a.current !== b.current ? a.current ? -1 : 1 : byRecency(a, b));
80321
+ const st = matching.filter((g) => !g.current && now - g.lastActiveMs >= STALE_MS).sort(byRecency);
80322
+ return { fresh: f, stale: st, visibleGroups: [...f, ...st] };
80323
+ }, [groups, filter, listed]);
80324
+ const group = visibleGroups[Math.min(wtCursor, visibleGroups.length - 1)];
80325
+ const sessions2 = useMemo4(() => {
80326
+ if (!group)
80327
+ return [];
80328
+ const base = listed.get(group.name) ?? [];
80329
+ if (!filter)
80330
+ return base;
80331
+ if (fuzzy(filter, group.name))
80332
+ return base;
80333
+ return base.filter((s) => fuzzy(filter, sessionLabel(s)));
80334
+ }, [group, filter, listed]);
80335
+ const agentRows = useMemo4(() => group ? group.sessions.filter(isAgentSession) : [], [group]);
80336
+ const items = useMemo4(() => {
80337
+ const out = sessions2.map((row) => ({ kind: "session", row }));
80338
+ if (agentRows.length > 0 && !filter) {
80339
+ out.push({ kind: "agents", count: agentRows.length });
80340
+ for (const row of agentsOpen ? agentRows : agentRows.slice(0, 3)) {
80341
+ out.push({ kind: "agent", row });
80342
+ }
80343
+ }
80344
+ return out;
80345
+ }, [sessions2, agentRows, agentsOpen, filter]);
80346
+ const cursorItem = items[Math.min(sessCursor, items.length - 1)];
80347
+ const selected = cursorItem && cursorItem.kind !== "agents" ? cursorItem.row : undefined;
80348
+ const turns = detailTurns(height2);
80349
+ const sessionDetailH = DETAIL_CHROME + turns;
80350
+ const listRows = Math.max(3, height2 - sessionDetailH - WORKTREE_DETAIL_H - 4);
80351
+ useEffect6(() => {
80352
+ const start = Math.max(0, Math.min(sessCursor - 2, items.length - listRows));
80353
+ for (const it of items.slice(start, start + listRows + 2)) {
80354
+ if (it.kind !== "agents")
80355
+ hydrateSession(it.row);
80356
+ }
80357
+ if (selected) {
80358
+ hydrateSession(selected);
80359
+ hydrateConversation(selected);
80360
+ }
80361
+ setTick((t) => t + 1);
80362
+ }, [items, sessCursor, listRows, selected]);
80363
+ useEffect6(() => {
80364
+ if (!reader || reader.conv)
80365
+ return;
80366
+ const file2 = reader.row.file;
80367
+ const timer = setTimeout(() => {
80368
+ const conv = readConversation(file2);
80369
+ setReader((r) => r && r.row.file === file2 && !r.conv ? { row: r.row, conv } : r);
80370
+ }, 0);
80371
+ return () => clearTimeout(timer);
80372
+ }, [reader]);
80373
+ const clamp = (v, len) => Math.max(0, Math.min(v, len - 1));
80374
+ useKeyboard4((key) => {
80375
+ if (reader)
80376
+ return;
80377
+ const name = key.name;
80378
+ if (name === "escape") {
80379
+ if (filter) {
80380
+ setFilter("");
80381
+ return;
80382
+ }
80383
+ onDone(null);
80384
+ return;
80385
+ }
80386
+ if (key.ctrl && name === "c") {
80387
+ onDone(null);
80388
+ return;
80389
+ }
80390
+ if (name === "return" || name === "enter") {
80391
+ if (pane === "worktrees") {
80392
+ setPane("sessions");
80393
+ setSessCursor(0);
80394
+ return;
80395
+ }
80396
+ if (cursorItem?.kind === "agents") {
80397
+ setAgentsOpen((v) => !v);
80398
+ return;
80399
+ }
80400
+ if (selected)
80401
+ onDone(selected.id);
80402
+ return;
80403
+ }
80404
+ if (name === "tab") {
80405
+ setPane((p) => p === "worktrees" ? "sessions" : "worktrees");
80406
+ return;
80407
+ }
80408
+ if (name === "left") {
80409
+ setPane("worktrees");
80410
+ return;
80411
+ }
80412
+ if (name === "right") {
80413
+ setPane("sessions");
80414
+ return;
80415
+ }
80416
+ if (name === "up" || name === "down") {
80417
+ const d = name === "up" ? -1 : 1;
80418
+ if (pane === "worktrees") {
80419
+ setWtCursor((c) => clamp(c + d, visibleGroups.length));
80420
+ setSessCursor(0);
80421
+ } else {
80422
+ setSessCursor((c) => clamp(c + d, items.length));
80423
+ }
80424
+ return;
80425
+ }
80426
+ if (name === "a" && !key.ctrl && !key.meta) {
80427
+ setAgentsOpen((v) => !v);
80428
+ return;
80429
+ }
80430
+ if (name === "v" && !key.ctrl && !key.meta) {
80431
+ if (selected)
80432
+ setReader({ row: selected, conv: null });
80433
+ return;
80434
+ }
80435
+ if (name === "backspace") {
80436
+ setFilter((f) => f.slice(0, -1));
80437
+ setWtCursor(0);
80438
+ setSessCursor(0);
80439
+ return;
80440
+ }
80441
+ const ch = key.raw;
80442
+ if (ch && ch.length === 1 && ch >= " " && ch !== "\x7F" && ch !== "a" && ch !== "v") {
80443
+ setFilter((f) => f + ch);
80444
+ setWtCursor(0);
80445
+ setSessCursor(0);
80446
+ }
80447
+ });
80448
+ const bodyW = width;
80449
+ const sidebarW = Math.max(SIDEBAR_MIN, Math.min(SIDEBAR_MAX, Math.round(width * 0.34)));
80450
+ const rightW = Math.max(30, bodyW - sidebarW);
80451
+ const sessInner = rightW - PANEL_BORDER - SCROLL_CHROME;
80452
+ const sideInner = sidebarW - PANEL_BORDER - SCROLL_CHROME - 1;
80453
+ const sessionDetailInner = rightW - PANEL_CHROME;
80454
+ const worktreeDetailInner = width - PANEL_CHROME;
80455
+ const chipCols = useMemo4(() => {
80456
+ let cols = { age: 1, count: 1, dirty: 0, sync: 0 };
80457
+ for (const g of visibleGroups) {
80458
+ cols.age = Math.max(cols.age, displayWidth(g.lastActiveMs ? age(g.lastActiveMs) : "\u2014"));
80459
+ cols.count = Math.max(cols.count, String(listed.get(g.name)?.length ?? 0).length);
80460
+ if (g.dirty)
80461
+ cols.dirty = Math.max(cols.dirty, String(g.dirty).length);
80462
+ if (g.ahead)
80463
+ cols.sync = Math.max(cols.sync, String(g.ahead).length);
80464
+ if (g.behind)
80465
+ cols.sync = Math.max(cols.sync, String(g.behind).length);
80466
+ }
80467
+ if (blockWidth(cols) > sideInner)
80468
+ cols = { ...cols, sync: 0 };
80469
+ if (blockWidth(cols) > sideInner)
80470
+ cols = { ...cols, dirty: 0 };
80471
+ return cols;
80472
+ }, [visibleGroups, listed, sideInner]);
80473
+ const maxSize = Math.max(1, ...sessions2.slice(0, 400).map((s) => s.sizeBytes));
80474
+ const meterW = Math.max(6, Math.min(14, Math.round(sessInner * 0.16)));
80475
+ const sidebarInnerH = height2 - 1 - WORKTREE_DETAIL_H - 1 - PANEL_BORDER;
80476
+ const listContentH = visibleGroups.length * 2 + (stale.length > 0 ? 2 : 0);
80477
+ const showActivity = sidebarInnerH - listContentH >= ACTIVITY_H;
80478
+ const activityDays = useMemo4(() => dailyActivity(visibleGroups, ACTIVITY_WEEKS * WEEK_DAYS), [visibleGroups]);
80479
+ const shownSessions = [...listed.values()].reduce((a, v) => a + v.length, 0);
80480
+ if (reader) {
80481
+ return /* @__PURE__ */ jsxDEV20(ConversationReader, {
80482
+ row: reader.row,
80483
+ conv: reader.conv,
80484
+ width,
80485
+ height: height2,
80486
+ onClose: () => setReader(null),
80487
+ onResume: () => onDone(reader.row.id),
80488
+ onCancel: () => onDone(null)
80489
+ }, undefined, false, undefined, this);
80490
+ }
80491
+ return /* @__PURE__ */ jsxDEV20("box", {
80492
+ flexDirection: "column",
80493
+ height: height2,
80494
+ backgroundColor: C.bg,
80495
+ children: [
80496
+ /* @__PURE__ */ jsxDEV20("box", {
80497
+ flexDirection: "row",
80498
+ justifyContent: "space-between",
80499
+ height: 1,
80500
+ paddingX: 1,
80501
+ children: [
80502
+ /* @__PURE__ */ jsxDEV20("text", {
80503
+ children: [
80504
+ /* @__PURE__ */ jsxDEV20("span", {
80505
+ fg: tokens.accent,
80506
+ attributes: 1,
80507
+ children: "claudish"
80508
+ }, undefined, false, undefined, this),
80509
+ /* @__PURE__ */ jsxDEV20("span", {
80510
+ fg: tokens.subtle,
80511
+ children: " resume"
80512
+ }, undefined, false, undefined, this)
80513
+ ]
80514
+ }, undefined, true, undefined, this),
80515
+ /* @__PURE__ */ jsxDEV20("text", {
80516
+ children: [
80517
+ /* @__PURE__ */ jsxDEV20("span", {
80518
+ fg: tokens.subtle,
80519
+ children: `${visibleGroups.length} worktrees \xB7 ${shownSessions} sessions`
80520
+ }, undefined, false, undefined, this),
80521
+ filter ? /* @__PURE__ */ jsxDEV20("span", {
80522
+ fg: tokens.warn,
80523
+ children: ` /${filter}`
80524
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("span", {}, undefined, false, undefined, this)
80525
+ ]
80526
+ }, undefined, true, undefined, this)
80527
+ ]
80528
+ }, undefined, true, undefined, this),
80529
+ /* @__PURE__ */ jsxDEV20("box", {
80530
+ flexDirection: "row",
80531
+ flexGrow: 1,
80532
+ minHeight: 0,
80533
+ children: [
80534
+ /* @__PURE__ */ jsxDEV20("box", {
80535
+ width: sidebarW,
80536
+ flexDirection: "column",
80537
+ minHeight: 0,
80538
+ children: /* @__PURE__ */ jsxDEV20(Panel, {
80539
+ title: "worktrees",
80540
+ focused: pane === "worktrees",
80541
+ flush: true,
80542
+ flexGrow: 1,
80543
+ flexBasis: 0,
80544
+ children: [
80545
+ /* @__PURE__ */ jsxDEV20("scrollbox", {
80546
+ focused: false,
80547
+ flexGrow: 1,
80548
+ scrollbarOptions: SCROLLBAR,
80549
+ children: [
80550
+ fresh.map((g, i) => /* @__PURE__ */ jsxDEV20(WorktreeRow, {
80551
+ g,
80552
+ cursor: i === wtCursor,
80553
+ width: sideInner,
80554
+ count: listed.get(g.name)?.length ?? 0,
80555
+ cols: chipCols
80556
+ }, g.name, false, undefined, this)),
80557
+ stale.length > 0 ? /* @__PURE__ */ jsxDEV20(SectionHeader, {
80558
+ label: `stale \xB7 ${stale.length} \xB7 idle 3d+`,
80559
+ width: sideInner
80560
+ }, undefined, false, undefined, this) : null,
80561
+ stale.map((g, i) => /* @__PURE__ */ jsxDEV20(WorktreeRow, {
80562
+ g,
80563
+ cursor: fresh.length + i === wtCursor,
80564
+ width: sideInner,
80565
+ count: listed.get(g.name)?.length ?? 0,
80566
+ cols: chipCols
80567
+ }, g.name, false, undefined, this))
80568
+ ]
80569
+ }, undefined, true, undefined, this),
80570
+ showActivity ? /* @__PURE__ */ jsxDEV20(ActivityCalendar, {
80571
+ days: activityDays,
80572
+ width: sideInner
80573
+ }, undefined, false, undefined, this) : null
80574
+ ]
80575
+ }, undefined, true, undefined, this)
80576
+ }, undefined, false, undefined, this),
80577
+ /* @__PURE__ */ jsxDEV20("box", {
80578
+ flexDirection: "column",
80579
+ flexGrow: 1,
80580
+ minWidth: 0,
80581
+ minHeight: 0,
80582
+ children: [
80583
+ /* @__PURE__ */ jsxDEV20(Panel, {
80584
+ title: group ? `sessions \xB7 ${truncate3(group.name, 28)}` : "sessions",
80585
+ focused: pane === "sessions",
80586
+ flush: true,
80587
+ flexGrow: 1,
80588
+ flexBasis: 0,
80589
+ children: /* @__PURE__ */ jsxDEV20("scrollbox", {
80590
+ focused: false,
80591
+ flexGrow: 1,
80592
+ scrollbarOptions: SCROLLBAR,
80593
+ children: items.length === 0 ? /* @__PURE__ */ jsxDEV20("text", {
80594
+ fg: tokens.subtle,
80595
+ children: " no sessions match"
80596
+ }, undefined, false, undefined, this) : items.map((it, i) => it.kind === "agents" ? /* @__PURE__ */ jsxDEV20(AgentNode, {
80597
+ count: it.count,
80598
+ open: agentsOpen,
80599
+ cursor: i === sessCursor && pane === "sessions",
80600
+ width: sessInner
80601
+ }, "agents", false, undefined, this) : /* @__PURE__ */ jsxDEV20(SessionRowView, {
80602
+ row: it.row,
80603
+ cursor: i === sessCursor && pane === "sessions",
80604
+ width: sessInner,
80605
+ even: i % 2 === 0,
80606
+ indent: it.kind === "agent" ? 2 : 0,
80607
+ maxSize,
80608
+ meterW
80609
+ }, it.row.id, false, undefined, this))
80610
+ }, undefined, false, undefined, this)
80611
+ }, undefined, false, undefined, this),
80612
+ /* @__PURE__ */ jsxDEV20("box", {
80613
+ height: sessionDetailH,
80614
+ flexShrink: 0,
80615
+ children: /* @__PURE__ */ jsxDEV20(Panel, {
80616
+ title: "session",
80617
+ flexGrow: 1,
80618
+ children: selected ? /* @__PURE__ */ jsxDEV20(SessionDetail, {
80619
+ row: selected,
80620
+ width: sessionDetailInner,
80621
+ turns
80622
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("text", {
80623
+ fg: tokens.subtle,
80624
+ children: cursorItem?.kind === "agents" ? "agent sessions \u2014 enter to expand" : "nothing selected"
80625
+ }, undefined, false, undefined, this)
80626
+ }, undefined, false, undefined, this)
80627
+ }, undefined, false, undefined, this)
80628
+ ]
80629
+ }, undefined, true, undefined, this)
80630
+ ]
80631
+ }, undefined, true, undefined, this),
80632
+ /* @__PURE__ */ jsxDEV20("box", {
80633
+ height: WORKTREE_DETAIL_H,
80634
+ flexShrink: 0,
80635
+ children: /* @__PURE__ */ jsxDEV20(Panel, {
80636
+ title: "worktree",
80637
+ flexGrow: 1,
80638
+ children: /* @__PURE__ */ jsxDEV20(WorktreeDetail, {
80639
+ group,
80640
+ width: worktreeDetailInner,
80641
+ count: sessions2.length
80642
+ }, undefined, false, undefined, this)
80643
+ }, undefined, false, undefined, this)
80644
+ }, undefined, false, undefined, this),
80645
+ /* @__PURE__ */ jsxDEV20("box", {
80646
+ flexDirection: "row",
80647
+ height: 1,
80648
+ paddingX: 1,
80649
+ gap: 2,
80650
+ children: [
80651
+ /* @__PURE__ */ jsxDEV20("text", {
80652
+ fg: tokens.subtle,
80653
+ children: "\u2191\u2193 move"
80654
+ }, undefined, false, undefined, this),
80655
+ /* @__PURE__ */ jsxDEV20("text", {
80656
+ fg: tokens.subtle,
80657
+ children: "\u21E5 pane"
80658
+ }, undefined, false, undefined, this),
80659
+ /* @__PURE__ */ jsxDEV20("text", {
80660
+ fg: tokens.subtle,
80661
+ children: "type to filter"
80662
+ }, undefined, false, undefined, this),
80663
+ /* @__PURE__ */ jsxDEV20("text", {
80664
+ fg: tokens.accent,
80665
+ children: "\u23CE resume"
80666
+ }, undefined, false, undefined, this),
80667
+ /* @__PURE__ */ jsxDEV20("text", {
80668
+ fg: selected ? tokens.info : tokens.trace,
80669
+ children: "v read"
80670
+ }, undefined, false, undefined, this),
80671
+ /* @__PURE__ */ jsxDEV20("text", {
80672
+ fg: agentRows.length > 0 ? tokens.subtle : tokens.trace,
80673
+ children: agentRows.length > 0 ? `a ${agentsOpen ? "collapse" : "expand"} agents` : ""
80674
+ }, undefined, false, undefined, this),
80675
+ /* @__PURE__ */ jsxDEV20("text", {
80676
+ fg: tokens.subtle,
80677
+ children: "esc cancel"
80678
+ }, undefined, false, undefined, this)
80679
+ ]
80680
+ }, undefined, true, undefined, this)
80681
+ ]
80682
+ }, undefined, true, undefined, this);
80683
+ }
80684
+ function Field({
80685
+ label,
80686
+ width,
80687
+ children
80688
+ }) {
80689
+ return /* @__PURE__ */ jsxDEV20("box", {
80690
+ flexDirection: "row",
80691
+ height: 1,
80692
+ gap: 1,
80693
+ children: [
80694
+ /* @__PURE__ */ jsxDEV20("text", {
80695
+ fg: tokens.subtle,
80696
+ children: padTo(label, width)
80697
+ }, undefined, false, undefined, this),
80698
+ children
80699
+ ]
80700
+ }, undefined, true, undefined, this);
80701
+ }
80702
+ function WorktreeDetail({
80703
+ group,
80704
+ width,
80705
+ count
80706
+ }) {
80707
+ if (!group)
80708
+ return /* @__PURE__ */ jsxDEV20("text", {
80709
+ fg: tokens.subtle,
80710
+ children: "no worktree selected"
80711
+ }, undefined, false, undefined, this);
80712
+ const L = 9;
80713
+ const badges = [];
80714
+ if (group.dirty !== undefined) {
80715
+ badges.push({
80716
+ label: group.dirty > 0 ? `${DIRTY_GLYPH}${group.dirty} uncommitted` : "clean",
80717
+ bg: group.dirty > 0 ? CHIP.dirty : CHIP.clean
80718
+ });
80719
+ }
80720
+ if (group.ahead)
80721
+ badges.push({ label: `\u2191${group.ahead}`, bg: CHIP.ahead });
80722
+ if (group.behind)
80723
+ badges.push({ label: `\u2193${group.behind}`, bg: CHIP.behind });
80724
+ const badgeW = badges.reduce((w, b) => w + displayWidth(b.label) + 2, 0);
80725
+ const marker = group.current ? " \u25B6 you are here" : !group.live ? " worktree deleted" : "";
80726
+ const branch = group.branch ?? (group.live ? "detached" : "\u2014");
80727
+ const idAvail = Math.max(12, width - L - displayWidth(marker) - (BRANCH_LEAD + 1) - badgeW - 1);
80728
+ const nameW = Math.max(8, Math.min(displayWidth(group.name), Math.floor(idAvail / 2)));
80729
+ const branchW = Math.max(6, idAvail - nameW);
80730
+ const spark = activitySeries(group.sessions);
80731
+ const summary = ` ${count} session${count === 1 ? "" : "s"} \xB7 created ${group.createdMs ? age(group.createdMs) : "?"} ago \xB7 used ${group.lastActiveMs ? age(group.lastActiveMs) : "?"} ago`;
80732
+ const locAvail = Math.max(20, width - L - spark.length - 2);
80733
+ const pathW = Math.min(displayWidth(group.path ?? "\u2014"), Math.max(16, Math.floor(locAvail * 0.45)));
80734
+ return /* @__PURE__ */ jsxDEV20("box", {
80735
+ flexDirection: "column",
80736
+ children: [
80737
+ /* @__PURE__ */ jsxDEV20("text", {
80738
+ children: [
80739
+ /* @__PURE__ */ jsxDEV20("span", {
80740
+ fg: tokens.subtle,
80741
+ children: padTo("worktree", L)
80742
+ }, undefined, false, undefined, this),
80743
+ /* @__PURE__ */ jsxDEV20("span", {
80744
+ fg: tokens.text,
80745
+ attributes: A.bold,
80746
+ children: truncate3(group.name, nameW)
80747
+ }, undefined, false, undefined, this),
80748
+ /* @__PURE__ */ jsxDEV20("span", {
80749
+ fg: group.current ? HERE_FG : tokens.dead,
80750
+ children: marker
80751
+ }, undefined, false, undefined, this),
80752
+ /* @__PURE__ */ jsxDEV20("span", {
80753
+ fg: tokens.subtle,
80754
+ children: ` ${BRANCH_ICON} `
80755
+ }, undefined, false, undefined, this),
80756
+ /* @__PURE__ */ jsxDEV20("span", {
80757
+ fg: group.live ? tokens.info : tokens.dead,
80758
+ children: `${truncate3(branch, branchW)} `
80759
+ }, undefined, false, undefined, this),
80760
+ badges.map((b) => /* @__PURE__ */ jsxDEV20(BadgeSpan, {
80761
+ label: b.label,
80762
+ bg: b.bg
80763
+ }, b.label, false, undefined, this))
80764
+ ]
80765
+ }, undefined, true, undefined, this),
80766
+ /* @__PURE__ */ jsxDEV20("box", {
80767
+ flexDirection: "row",
80768
+ height: 1,
80769
+ children: [
80770
+ /* @__PURE__ */ jsxDEV20("text", {
80771
+ fg: tokens.subtle,
80772
+ flexShrink: 0,
80773
+ children: padTo("path", L)
80774
+ }, undefined, false, undefined, this),
80775
+ /* @__PURE__ */ jsxDEV20("text", {
80776
+ fg: tokens.trace,
80777
+ flexShrink: 0,
80778
+ children: `${padTo(truncate3(group.path ?? "\u2014", pathW), pathW)} `
80779
+ }, undefined, false, undefined, this),
80780
+ /* @__PURE__ */ jsxDEV20(Sparkline, {
80781
+ values: hasActivity(spark) ? spark : [],
80782
+ fg: SPARK_FG
80783
+ }, undefined, false, undefined, this),
80784
+ /* @__PURE__ */ jsxDEV20("text", {
80785
+ fg: tokens.trace,
80786
+ flexShrink: 0,
80787
+ children: truncate3(summary, Math.max(0, locAvail - pathW))
80788
+ }, undefined, false, undefined, this)
80789
+ ]
80790
+ }, undefined, true, undefined, this)
80791
+ ]
80792
+ }, undefined, true, undefined, this);
80793
+ }
80794
+ function SessionDetail({
80795
+ row,
80796
+ width,
80797
+ turns
80798
+ }) {
80799
+ const mb = row.sizeBytes / 1048576;
80800
+ const L = 9;
80801
+ const size = mb >= 0.1 ? `${mb.toFixed(1)} MB` : `${Math.round(row.sizeBytes / 1024)} KB`;
80802
+ const full = `\xB7 ${size}${row.lastMessageChars !== undefined ? ` \xB7 last msg ${row.lastMessageChars} ch` : ""}`;
80803
+ const suffixRoom = width - L - 1 - row.id.length - 1;
80804
+ const suffix = displayWidth(full) <= suffixRoom ? full : displayWidth(`\xB7 ${size}`) <= suffixRoom ? `\xB7 ${size}` : "";
80805
+ return /* @__PURE__ */ jsxDEV20("box", {
80806
+ flexDirection: "column",
80807
+ children: [
80808
+ /* @__PURE__ */ jsxDEV20(Field, {
80809
+ label: "title",
80810
+ width: L,
80811
+ children: /* @__PURE__ */ jsxDEV20("text", {
80812
+ fg: tokens.text,
80813
+ attributes: A.bold,
80814
+ children: truncate3(sessionLabel(row), width - L)
80815
+ }, undefined, false, undefined, this)
80816
+ }, undefined, false, undefined, this),
80817
+ /* @__PURE__ */ jsxDEV20(Field, {
80818
+ label: "id",
80819
+ width: L,
80820
+ children: [
80821
+ /* @__PURE__ */ jsxDEV20("text", {
80822
+ fg: tokens.info,
80823
+ flexShrink: 0,
80824
+ children: row.id
80825
+ }, undefined, false, undefined, this),
80826
+ suffix ? /* @__PURE__ */ jsxDEV20("text", {
80827
+ fg: tokens.trace,
80828
+ flexShrink: 0,
80829
+ children: suffix
80830
+ }, undefined, false, undefined, this) : null
80831
+ ]
80832
+ }, undefined, true, undefined, this),
80833
+ /* @__PURE__ */ jsxDEV20("box", {
80834
+ height: 1
80835
+ }, undefined, false, undefined, this),
80836
+ /* @__PURE__ */ jsxDEV20(Conversation, {
80837
+ turns: row.recentTurns ?? [],
80838
+ width,
80839
+ max: turns
80840
+ }, undefined, false, undefined, this)
80841
+ ]
80842
+ }, undefined, true, undefined, this);
80843
+ }
80844
+ function Conversation({
80845
+ turns,
80846
+ width,
80847
+ max
80848
+ }) {
80849
+ if (turns.length === 0) {
80850
+ return /* @__PURE__ */ jsxDEV20("box", {
80851
+ flexDirection: "column",
80852
+ width,
80853
+ children: /* @__PURE__ */ jsxDEV20("text", {
80854
+ fg: tokens.trace,
80855
+ children: "no conversation recorded"
80856
+ }, undefined, false, undefined, this)
80857
+ }, undefined, false, undefined, this);
80858
+ }
80859
+ const ROLE_W2 = 6;
80860
+ const textW = Math.max(10, width - ROLE_W2 - 2);
80861
+ const shown = turns.slice(-max);
80862
+ return /* @__PURE__ */ jsxDEV20("box", {
80863
+ flexDirection: "column",
80864
+ width,
80865
+ flexShrink: 0,
80866
+ children: shown.map((t, i) => /* @__PURE__ */ jsxDEV20("text", {
80867
+ children: [
80868
+ /* @__PURE__ */ jsxDEV20("span", {
80869
+ fg: tokens.trace,
80870
+ children: "\u258D"
80871
+ }, undefined, false, undefined, this),
80872
+ /* @__PURE__ */ jsxDEV20(BadgeSpan, {
80873
+ label: t.role === "user" ? "you" : "ai",
80874
+ bg: t.role === "user" ? SPEAKER.you : SPEAKER.ai,
80875
+ width: ROLE_W2
80876
+ }, undefined, false, undefined, this),
80877
+ /* @__PURE__ */ jsxDEV20("span", {
80878
+ fg: t.role === "user" ? tokens.text : MUTED,
80879
+ children: truncate3(t.text, textW)
80880
+ }, undefined, false, undefined, this)
80881
+ ]
80882
+ }, `${i}-${t.text.slice(0, 12)}`, true, undefined, this))
80883
+ }, undefined, false, undefined, this);
80884
+ }
80885
+ var PANEL_CHROME = 4, PANEL_BORDER = 2, SCROLL_CHROME = 1, SIDEBAR_MIN = 28, SIDEBAR_MAX = 50, DETAIL_CHROME = 5, WORKTREE_DETAIL_H = 4, STALE_MS, GH_LEVELS, CHIP, HERE_FG, LIVE_W = 2, DIRTY_GLYPH = "+", SESSION_AGE_W = 3, SESSION_AGE_COL, SIZE_FLOOR_BYTES, MUTED, SCROLLBAR, WEEK_DAYS = 7, ACTIVITY_WEEKS = 6, BRANCH_ICON = "\u2387", BRANCH_LEAD = 5, BRANCH_LEAD_DENSE = 6, WEEK_LABEL_W = 4, ACTIVITY_H, SPARK_MIN_DAYS = 7, SPARK_FG = "#3f6f9e";
80886
+ var init_resume_picker = __esm(() => {
80887
+ init_theme2();
80888
+ init_text();
80889
+ init_tokens();
80890
+ init_widgets();
80891
+ init_conversation_reader();
80892
+ init_conversation();
80893
+ init_session_discovery();
80894
+ STALE_MS = 3 * 86400000;
80895
+ GH_LEVELS = ["#21262d", "#0e4429", "#006d32", "#26a641", "#39d353"];
80896
+ CHIP = {
80897
+ fresh: GH_LEVELS[4],
80898
+ stale: "#a1a9b3",
80899
+ count: "#58a6ff",
80900
+ dirty: "#d29922",
80901
+ clean: GH_LEVELS[3],
80902
+ ahead: "#bc8cff",
80903
+ behind: "#d2a8ff"
80904
+ };
80905
+ HERE_FG = GH_LEVELS[4];
80906
+ SESSION_AGE_COL = SESSION_AGE_W + 3;
80907
+ SIZE_FLOOR_BYTES = 16 * 1024;
80908
+ MUTED = C.fgMuted;
80909
+ SCROLLBAR = {
80910
+ showArrows: false,
80911
+ trackOptions: { backgroundColor: tokens.bgPanel, foregroundColor: tokens.border }
80912
+ };
80913
+ ACTIVITY_H = 2 + ACTIVITY_WEEKS;
80914
+ });
80915
+
80916
+ // src/session/resume-picker-run.tsx
80917
+ var exports_resume_picker_run = {};
80918
+ __export(exports_resume_picker_run, {
80919
+ runResumePicker: () => runResumePicker
80920
+ });
80921
+ import { createCliRenderer as createCliRenderer3 } from "@opentui/core";
80922
+ import { createRoot as createRoot3 } from "@opentui/react";
80923
+ import { jsxDEV as jsxDEV21 } from "@opentui/react/jsx-dev-runtime";
80924
+ async function runResumePicker(cwd = process.cwd()) {
80925
+ const repo = getRepoContext(cwd);
80926
+ if (!repo)
80927
+ return { sessionId: null, hadSessions: false };
80928
+ const groups = discoverWorktreeGroups(repo);
80929
+ if (groups.length === 0)
80930
+ return { sessionId: null, hadSessions: false };
80931
+ await enrichWorktreeGit(groups, repo.root);
80932
+ setStderrQuiet(true);
80933
+ const renderer = await createCliRenderer3({
80934
+ useAlternateScreen: true,
80935
+ exitOnCtrlC: false
80936
+ });
80937
+ const root = createRoot3(renderer);
80938
+ let chosen = null;
80939
+ try {
80940
+ await new Promise((resolve5) => {
80941
+ let settled = false;
80942
+ const done = (id) => {
80943
+ if (settled)
80944
+ return;
80945
+ settled = true;
80946
+ chosen = id;
80947
+ resolve5();
80948
+ };
80949
+ root.render(/* @__PURE__ */ jsxDEV21(ResumePicker, {
80950
+ groups,
80951
+ onDone: done
80952
+ }, undefined, false, undefined, this));
80953
+ });
80954
+ } finally {
80955
+ try {
80956
+ root.unmount();
80957
+ } catch {}
80958
+ try {
80959
+ renderer.destroy();
80960
+ } catch {}
80961
+ setStderrQuiet(false);
80962
+ }
80963
+ return { sessionId: chosen, hadSessions: true };
80964
+ }
80965
+ var init_resume_picker_run = __esm(() => {
80966
+ init_logger();
80967
+ init_resume_picker();
80968
+ init_session_discovery();
80969
+ });
80970
+
80971
+ // src/session/baseline-pricing.ts
80972
+ function resolveBaseline(alias, label) {
80973
+ const entry = findEntryByAlias(alias);
80974
+ if (!entry)
80975
+ return null;
80976
+ const firstParty = entry.aggregators?.find((a) => a.provider === FIRST_PARTY && typeof a.pricing?.input === "number");
80977
+ const input = firstParty?.pricing?.input;
80978
+ const output = firstParty?.pricing?.output;
80979
+ if (typeof input !== "number" || typeof output !== "number")
80980
+ return null;
80981
+ if (input <= 0 || output <= 0)
80982
+ return null;
80983
+ return { modelId: entry.modelId, label, inputPerM: input, outputPerM: output };
80984
+ }
80985
+ function getBaselines() {
80986
+ return BASELINE_ALIASES.map(({ alias, label }) => resolveBaseline(alias, label)).filter((b) => b !== null);
80987
+ }
80988
+ function baselineCost(b, inputTokens, outputTokens) {
80989
+ return inputTokens / 1e6 * b.inputPerM + outputTokens / 1e6 * b.outputPerM;
80990
+ }
80991
+ var BASELINE_ALIASES, FIRST_PARTY = "anthropic";
80992
+ var init_baseline_pricing = __esm(() => {
80993
+ init_catalog_query();
80994
+ BASELINE_ALIASES = [
80995
+ { alias: "~anthropic/claude-sonnet-latest", label: "Sonnet" },
80996
+ { alias: "~anthropic/claude-opus-latest", label: "Opus" }
80997
+ ];
80998
+ });
80999
+
81000
+ // src/session/session-stats.ts
81001
+ var exports_session_stats = {};
81002
+ __export(exports_session_stats, {
81003
+ tokenFilePath: () => tokenFilePath,
81004
+ readSessionStats: () => readSessionStats,
81005
+ computeSavings: () => computeSavings
81006
+ });
81007
+ import { readFileSync as readFileSync28 } from "fs";
81008
+ import { homedir as homedir35 } from "os";
81009
+ import { join as join38 } from "path";
81010
+ function tokenFilePath(port) {
81011
+ return process.env.CLAUDISH_TOKEN_FILE || join38(homedir35(), ".claudish", `tokens-${port}.json`);
81012
+ }
81013
+ function readSessionStats(port) {
81014
+ let raw2;
81015
+ try {
81016
+ raw2 = JSON.parse(readFileSync28(tokenFilePath(port), "utf-8"));
81017
+ } catch {
81018
+ return null;
81019
+ }
81020
+ if (!raw2 || typeof raw2 !== "object")
81021
+ return null;
81022
+ const d = raw2;
81023
+ const inputTokens = num(d.input_tokens);
81024
+ const outputTokens = num(d.output_tokens);
81025
+ if (inputTokens <= 0 && outputTokens <= 0)
81026
+ return null;
81027
+ const costUsd = num(d.total_cost);
81028
+ const isFree = d.is_free === true;
81029
+ const contextWindow = typeof d.context_window === "number" ? d.context_window : null;
81030
+ const contextUsed = contextWindow && contextWindow > 0 ? Math.min(1, Math.max(0, inputTokens / contextWindow)) : null;
81031
+ const toolCalls = Array.isArray(d.tool_calls) ? d.tool_calls.map((t) => t).filter((t) => typeof t?.name === "string" && num(t.count) > 0).map((t) => ({ name: String(t.name), count: num(t.count) })) : [];
81032
+ const startedAt = num(d.started_at);
81033
+ const updatedAt = num(d.updated_at);
81034
+ const durationMs = startedAt > 0 && updatedAt > startedAt ? updatedAt - startedAt : 0;
81035
+ const billedInputTokens = num(d.billed_input_tokens) || inputTokens;
81036
+ const rawIn = billedInputTokens / 1e6 * num(d.input_per_m);
81037
+ const rawOut = outputTokens / 1e6 * num(d.output_per_m);
81038
+ const rawTotal = rawIn + rawOut;
81039
+ const scale = rawTotal > 0 && costUsd > 0 ? costUsd / rawTotal : 0;
81040
+ return {
81041
+ inputTokens,
81042
+ outputTokens,
81043
+ totalTokens: num(d.total_tokens) || inputTokens + outputTokens,
81044
+ costUsd,
81045
+ isFree,
81046
+ isEstimated: d.is_estimated === true,
81047
+ providerName: typeof d.provider_name === "string" ? d.provider_name : "",
81048
+ modelName: typeof d.model_name === "string" ? d.model_name : "",
81049
+ contextWindow,
81050
+ contextUsed,
81051
+ toolCalls,
81052
+ toolCallTotal: toolCalls.reduce((a, t) => a + t.count, 0),
81053
+ durationMs,
81054
+ savings: computeSavings(billedInputTokens, outputTokens, costUsd),
81055
+ inputCostUsd: rawIn * scale,
81056
+ outputCostUsd: rawOut * scale,
81057
+ billedInputTokens
81058
+ };
81059
+ }
81060
+ function computeSavings(inputTokens, outputTokens, actualUsd) {
81061
+ return getBaselines().map((b) => {
81062
+ const baselineUsd = baselineCost(b, inputTokens, outputTokens);
81063
+ return {
81064
+ label: b.label,
81065
+ modelId: b.modelId,
81066
+ baselineUsd,
81067
+ savedUsd: baselineUsd - actualUsd
81068
+ };
81069
+ });
81070
+ }
81071
+ var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
81072
+ var init_session_stats = __esm(() => {
81073
+ init_baseline_pricing();
81074
+ });
81075
+
81076
+ // src/session/ansi-viz.ts
81077
+ function rgb(hex4) {
81078
+ return [
81079
+ Number.parseInt(hex4.slice(1, 3), 16),
81080
+ Number.parseInt(hex4.slice(3, 5), 16),
81081
+ Number.parseInt(hex4.slice(5, 7), 16)
81082
+ ];
81083
+ }
81084
+ function fg(hex4) {
81085
+ const [r, g, b] = rgb(hex4);
81086
+ return `\x1B[38;2;${r};${g};${b}m`;
81087
+ }
81088
+ function bg(hex4) {
81089
+ const [r, g, b] = rgb(hex4);
81090
+ return `\x1B[48;2;${r};${g};${b}m`;
81091
+ }
81092
+ function paint(text, hex4, bold4 = false) {
81093
+ return `${bold4 ? BOLD5 : ""}${fg(hex4)}${text}${RESET4}`;
81094
+ }
81095
+ function meter(pct, width, ramp = ramps.load) {
81096
+ const cells = Math.floor(width);
81097
+ if (!Number.isFinite(cells) || cells <= 0)
81098
+ return "";
81099
+ if (Number.isNaN(pct))
81100
+ return paint(NODATA2.repeat(cells), tokens.dead);
81101
+ const cols = rampFor(cells, ramp);
81102
+ const filled = fillCells(pct, cells);
81103
+ let out = "";
81104
+ let last = "";
81105
+ for (let i = 0;i < cells; i++) {
81106
+ const hex4 = i < filled ? cols[i] : tokens.border;
81107
+ if (hex4 !== last) {
81108
+ out += fg(hex4);
81109
+ last = hex4;
81110
+ }
81111
+ out += i < filled ? FILL2 : TRACK2;
81112
+ }
81113
+ return out + RESET4;
81114
+ }
81115
+ function stackedBar(segments, width) {
81116
+ const cells = Math.floor(width);
81117
+ if (!Number.isFinite(cells) || cells <= 0 || segments.length === 0)
81118
+ return "";
81119
+ const split = splitCells(segments.map((s) => s.value), cells);
81120
+ if (split.reduce((a, b) => a + b, 0) === 0)
81121
+ return paint(TRACK2.repeat(cells), tokens.border);
81122
+ let out = "";
81123
+ for (let i = 0;i < split.length; i++) {
81124
+ const n = split[i];
81125
+ if (n > 0)
81126
+ out += `${bg(segments[i].color)}${" ".repeat(n)}`;
81127
+ }
81128
+ return out + RESET4;
81129
+ }
81130
+ function badge(label, hex4) {
81131
+ return `${BOLD5}${fg(pickInk(hex4))}${bg(hex4)} ${label} ${RESET4}`;
81132
+ }
81133
+ function stripAnsi4(s) {
81134
+ return s.replace(ANSI_RE3, "");
81135
+ }
81136
+ function visibleWidth(s) {
81137
+ return displayWidth(stripAnsi4(s));
81138
+ }
81139
+ function clipStyled(s, width) {
81140
+ if (width <= 0)
81141
+ return "";
81142
+ if (visibleWidth(s) <= width)
81143
+ return s;
81144
+ let out = "";
81145
+ let w = 0;
81146
+ let i = 0;
81147
+ while (i < s.length) {
81148
+ if (s[i] === "\x1B") {
81149
+ const end = s.indexOf("m", i);
81150
+ if (end === -1)
81151
+ break;
81152
+ out += s.slice(i, end + 1);
81153
+ i = end + 1;
81154
+ continue;
81155
+ }
81156
+ const ch = [...s.slice(i)][0] ?? "";
81157
+ const cw = displayWidth(ch);
81158
+ if (w + cw > width)
81159
+ break;
81160
+ out += ch;
81161
+ w += cw;
81162
+ i += ch.length;
81163
+ }
81164
+ return out + RESET4;
81165
+ }
81166
+ function padVisible2(s, width, align = "left") {
81167
+ const clipped = clipStyled(s, width);
81168
+ const pad2 = Math.max(0, width - visibleWidth(clipped));
81169
+ return align === "left" ? clipped + " ".repeat(pad2) : " ".repeat(pad2) + clipped;
81170
+ }
81171
+ function compact(n) {
81172
+ if (!Number.isFinite(n))
81173
+ return "\u2014";
81174
+ const a = Math.abs(n);
81175
+ if (a >= 1e9)
81176
+ return `${(n / 1e9).toFixed(1)}G`;
81177
+ if (a >= 1e6)
81178
+ return `${(n / 1e6).toFixed(1)}M`;
81179
+ if (a >= 1000)
81180
+ return `${(n / 1000).toFixed(1)}K`;
81181
+ return String(Math.round(n));
81182
+ }
81183
+ function duration3(ms) {
81184
+ if (!Number.isFinite(ms) || ms <= 0)
81185
+ return "\u2014";
81186
+ const s = Math.round(ms / 1000);
81187
+ if (s < 60)
81188
+ return `${s}s`;
81189
+ const m = Math.floor(s / 60);
81190
+ if (m < 60)
81191
+ return `${m}m ${String(s % 60).padStart(2, "0")}s`;
81192
+ return `${Math.floor(m / 60)}h ${String(m % 60).padStart(2, "0")}m`;
81193
+ }
81194
+ function usd(n) {
81195
+ if (!Number.isFinite(n))
81196
+ return "\u2014";
81197
+ const a = Math.abs(n);
81198
+ if (a === 0)
81199
+ return "$0";
81200
+ if (a < 0.01)
81201
+ return `$${n.toFixed(4)}`;
81202
+ if (a < 1)
81203
+ return `$${n.toFixed(3)}`;
81204
+ return `$${n.toFixed(2)}`;
81205
+ }
81206
+ var RESET4 = "\x1B[0m", BOLD5 = "\x1B[1m", FILL2 = "\u2588", TRACK2 = "\u2591", NODATA2 = "\u254C", ANSI_RE3;
81207
+ var init_ansi_viz = __esm(() => {
81208
+ init_color();
81209
+ init_text();
81210
+ init_tokens();
81211
+ init_widgets();
81212
+ ANSI_RE3 = /\x1b\[[0-9;]*m/g;
81213
+ });
81214
+
81215
+ // src/session/session-summary.ts
81216
+ var exports_session_summary = {};
81217
+ __export(exports_session_summary, {
81218
+ renderSessionSummary: () => renderSessionSummary,
81219
+ printSessionSummary: () => printSessionSummary
81220
+ });
81221
+ function cardWidth() {
81222
+ const cols = process.stdout.columns || 80;
81223
+ return Math.max(MIN_W, Math.min(MAX_W, cols - 2));
81224
+ }
81225
+ function renderSessionSummary(input) {
81226
+ const { stats, modelSpec, resumeModelSpec, resumeId, exitCode } = input;
81227
+ const W2 = cardWidth();
81228
+ const inner = W2 - CHROME;
81229
+ const out = [];
81230
+ const dim3 = (s) => paint(s, tokens.subtle);
81231
+ const body = (s) => paint(s, tokens.text);
81232
+ const titleText = exitCode === 0 ? " session " : " session \xB7 failed ";
81233
+ const titleHex = exitCode === 0 ? tokens.accent : tokens.error;
81234
+ const rule = "\u2500".repeat(Math.max(0, W2 - 2 - visibleWidth(titleText) - 1));
81235
+ out.push(paint("\u256D\u2500", tokens.border) + paint(titleText, titleHex) + paint(rule + "\u256E", tokens.border));
81236
+ const row = (s) => {
81237
+ out.push(`${paint("\u2502", tokens.border)} ${padVisible2(s, inner)} ${paint("\u2502", tokens.border)}`);
81238
+ };
81239
+ const blank = () => row("");
81240
+ const chips = [badge(truncate3(modelSpec, 34), tokens.accent)];
81241
+ if (stats.isFree)
81242
+ chips.push(badge("FREE", C.pillKeyBg));
81243
+ else if (stats.isEstimated)
81244
+ chips.push(badge("EST", "#8a7d1e"));
81245
+ if (exitCode !== 0)
81246
+ chips.push(badge(`EXIT ${exitCode}`, "#9e2b2b"));
81247
+ const right = body(duration3(stats.durationMs));
81248
+ const left = clipStyled(chips.join(" "), Math.max(0, inner - visibleWidth(right) - 1));
81249
+ const gap = Math.max(1, inner - visibleWidth(left) - visibleWidth(right));
81250
+ row(left + " ".repeat(gap) + right);
81251
+ if (stats.providerName)
81252
+ row(dim3(truncate3(stats.providerName, inner)));
81253
+ blank();
81254
+ const VALUE_W = 24;
81255
+ const barW = Math.max(12, inner - LABEL_W - VALUE_W);
81256
+ const dataRow = (label, bar, values) => {
81257
+ row(dim3(padTo(label, LABEL_W)) + bar + padVisible2(values, VALUE_W, "right"));
81258
+ };
81259
+ if (stats.contextUsed !== null && stats.contextWindow) {
81260
+ const pct = stats.contextUsed * 100;
81261
+ dataRow("context", meter(pct, barW, ramps.load), body(padStartTo(`${Math.round(pct)}%`, 4)) + dim3(` ${compact(stats.inputTokens)}/${compact(stats.contextWindow)}`));
81262
+ }
81263
+ dataRow("tokens", stackedBar([
81264
+ { value: stats.inputTokens, color: C.blue },
81265
+ { value: stats.outputTokens, color: C.cyan }
81266
+ ], barW), dim3("in ") + body(compact(stats.inputTokens)) + dim3(" out ") + body(compact(stats.outputTokens)));
81267
+ if (!stats.isFree && stats.inputCostUsd + stats.outputCostUsd > 0) {
81268
+ dataRow("spend", stackedBar([
81269
+ { value: stats.inputCostUsd, color: C.blue },
81270
+ { value: stats.outputCostUsd, color: C.cyan }
81271
+ ], barW), dim3("in ") + body(usd(stats.inputCostUsd)) + dim3(" out ") + body(usd(stats.outputCostUsd)));
81272
+ }
81273
+ if (stats.toolCallTotal > 0) {
81274
+ const shown = stats.toolCalls.slice(0, TOOL_COLORS.length);
81275
+ const rest = stats.toolCalls.slice(TOOL_COLORS.length).reduce((a, t) => a + t.count, 0);
81276
+ const segs = shown.map((t, i) => ({ value: t.count, color: TOOL_COLORS[i] }));
81277
+ if (rest > 0)
81278
+ segs.push({ value: rest, color: TOOL_OTHER });
81279
+ dataRow("tools", stackedBar(segs, barW), body(padStartTo(String(stats.toolCallTotal), 4)) + dim3(" calls"));
81280
+ const legend = shown.map((t, i) => paint(`${t.name} ${t.count}`, TOOL_COLORS[i])).concat(rest > 0 ? [paint(`other ${rest}`, TOOL_OTHER)] : []);
81281
+ for (const line of wrapStyled(legend, dim3(" \xB7 "), inner - LABEL_W)) {
81282
+ row(" ".repeat(LABEL_W) + line);
81283
+ }
81284
+ }
81285
+ blank();
81286
+ row(dim3(padTo("cost", LABEL_W)) + paint(stats.isFree ? "free" : usd(stats.costUsd), stats.isFree ? tokens.success : tokens.text, true) + (stats.isEstimated && !stats.isFree ? dim3(" estimated") : ""));
81287
+ for (const s of stats.savings) {
81288
+ const label = padTo(`vs ${s.label}`, LABEL_W);
81289
+ if (s.savedUsd >= 0) {
81290
+ const pct = s.baselineUsd > 0 ? s.savedUsd / s.baselineUsd * 100 : 0;
81291
+ dataRow(label, meter(pct, barW, ramps.savings), paint(padStartTo(`${Math.round(pct)}%`, 4), tokens.success) + dim3(" saved ") + paint(usd(s.savedUsd), tokens.success));
81292
+ } else {
81293
+ dataRow(label, meter(0, barW, ramps.savings), dim3("over by ") + paint(usd(-s.savedUsd), tokens.error));
81294
+ }
81295
+ }
81296
+ out.push(paint(`\u2570${"\u2500".repeat(W2 - 2)}\u256F`, tokens.border));
81297
+ if (resumeId) {
81298
+ out.push("");
81299
+ out.push(dim3("Resume this session with:"));
81300
+ const modelFlag = resumeModelSpec ? `--model ${resumeModelSpec} ` : "";
81301
+ out.push(`claudish ${modelFlag}--resume ${resumeId}`);
81302
+ }
81303
+ return out;
81304
+ }
81305
+ function wrapStyled(chips, sep, width) {
81306
+ const lines = [];
81307
+ let cur = "";
81308
+ let curW = 0;
81309
+ const sepW = visibleWidth(sep);
81310
+ for (const chip of chips) {
81311
+ const w = visibleWidth(chip);
81312
+ if (cur && curW + sepW + w > width) {
81313
+ lines.push(cur);
81314
+ cur = chip;
81315
+ curW = w;
81316
+ } else {
81317
+ cur = cur ? cur + sep + chip : chip;
81318
+ curW = cur === chip ? w : curW + sepW + w;
81319
+ }
81320
+ }
81321
+ if (cur)
81322
+ lines.push(cur);
81323
+ return lines;
81324
+ }
81325
+ function printSessionSummary(input, write) {
81326
+ for (const line of renderSessionSummary(input))
81327
+ write(line);
81328
+ write(RESET4);
81329
+ }
81330
+ var TOOL_COLORS, TOOL_OTHER, MIN_W = 62, MAX_W = 96, CHROME = 4, LABEL_W = 10;
81331
+ var init_session_summary = __esm(() => {
81332
+ init_theme2();
81333
+ init_text();
81334
+ init_tokens();
81335
+ init_ansi_viz();
81336
+ TOOL_COLORS = [
81337
+ C.blue,
81338
+ C.cyan,
81339
+ "#8a7d1e",
81340
+ "#1f6d75",
81341
+ C.magenta,
81342
+ "#2d6e3e",
81343
+ C.orange
81344
+ ];
81345
+ TOOL_OTHER = C.dim;
81346
+ });
81347
+
78325
81348
  // src/index.ts
78326
81349
  init_op_source();
78327
81350
  init_startup_trace();
78328
81351
  var import_dotenv3 = __toESM(require_main(), 1);
78329
- import { existsSync as existsSync29, readFileSync as readFileSync28 } from "fs";
78330
- import { join as join37, resolve as resolve5 } from "path";
81352
+ import { existsSync as existsSync29, readFileSync as readFileSync29 } from "fs";
81353
+ import { join as join39, resolve as resolve5 } from "path";
78331
81354
  import_dotenv3.config({ quiet: true });
78332
81355
  function classifyStartupKind() {
78333
81356
  const argv = process.argv.slice(2);
@@ -78570,14 +81593,14 @@ async function runCli() {
78570
81593
  if (cliConfig.team && cliConfig.team.length > 0) {
78571
81594
  let prompt = cliConfig.claudeArgs.join(" ");
78572
81595
  if (cliConfig.inputFile) {
78573
- prompt = readFileSync28(cliConfig.inputFile, "utf-8");
81596
+ prompt = readFileSync29(cliConfig.inputFile, "utf-8");
78574
81597
  }
78575
81598
  if (!prompt.trim()) {
78576
81599
  console.error("Error: --team requires a prompt (positional args or -f <file>)");
78577
81600
  process.exit(1);
78578
81601
  }
78579
81602
  const mode = cliConfig.teamMode ?? "default";
78580
- const sessionPath = join37(process.cwd(), `.claudish-team-${Date.now()}`);
81603
+ const sessionPath = join39(process.cwd(), `.claudish-team-${Date.now()}`);
78581
81604
  if (mode === "json") {
78582
81605
  const { setupSession: setupSession2, runModels: runModels2 } = await Promise.resolve().then(() => (init_team_orchestrator(), exports_team_orchestrator));
78583
81606
  setupSession2(sessionPath, cliConfig.team, prompt);
@@ -78587,9 +81610,9 @@ async function runCli() {
78587
81610
  });
78588
81611
  const result = { ...status2, responses: {} };
78589
81612
  for (const anonId of Object.keys(status2.models)) {
78590
- const responsePath = join37(sessionPath, `response-${anonId}.md`);
81613
+ const responsePath = join39(sessionPath, `response-${anonId}.md`);
78591
81614
  try {
78592
- const raw2 = readFileSync28(responsePath, "utf-8").trim();
81615
+ const raw2 = readFileSync29(responsePath, "utf-8").trim();
78593
81616
  try {
78594
81617
  result.responses[anonId] = JSON.parse(raw2);
78595
81618
  } catch {
@@ -78614,8 +81637,8 @@ async function runCli() {
78614
81637
  Team Status`);
78615
81638
  for (const id of modelIds) {
78616
81639
  const m = status.models[id];
78617
- const duration3 = m.startedAt && m.completedAt ? `${Math.round((new Date(m.completedAt).getTime() - new Date(m.startedAt).getTime()) / 1000)}s` : "pending";
78618
- console.log(` ${id} ${m.state.padEnd(10)} ${duration3}`);
81640
+ const duration4 = m.startedAt && m.completedAt ? `${Math.round((new Date(m.completedAt).getTime() - new Date(m.startedAt).getTime()) / 1000)}s` : "pending";
81641
+ console.log(` ${id} ${m.state.padEnd(10)} ${duration4}`);
78619
81642
  }
78620
81643
  process.exit(0);
78621
81644
  }
@@ -78766,6 +81789,28 @@ Team Status`);
78766
81789
  haiku: cliConfig.modelHaiku,
78767
81790
  subagent: cliConfig.modelSubagent
78768
81791
  };
81792
+ let resumedSessionId = (() => {
81793
+ const i = cliConfig.claudeArgs.indexOf("--resume");
81794
+ const v = i !== -1 ? cliConfig.claudeArgs[i + 1] : undefined;
81795
+ return v && !v.startsWith("-") ? v : null;
81796
+ })();
81797
+ if (cliConfig._resumePicker) {
81798
+ const canDrawTui = Boolean(process.stdin.isTTY && process.stdout.isTTY);
81799
+ if (!canDrawTui || cliConfig._hasPrintFlag || !cliConfig.interactive) {
81800
+ cliConfig.claudeArgs.push("--resume");
81801
+ } else {
81802
+ const { runResumePicker: runResumePicker2 } = await Promise.resolve().then(() => (init_resume_picker_run(), exports_resume_picker_run));
81803
+ const outcome = await runResumePicker2();
81804
+ if (!outcome.hadSessions) {
81805
+ cliConfig.claudeArgs.push("--resume");
81806
+ } else if (!outcome.sessionId) {
81807
+ process.exit(0);
81808
+ } else {
81809
+ cliConfig.claudeArgs.push("--resume", outcome.sessionId);
81810
+ resumedSessionId = outcome.sessionId;
81811
+ }
81812
+ }
81813
+ }
78769
81814
  const proxy = await traceSpan("startup:proxy-start", () => createProxyServer2(port, cliConfig.monitor ? undefined : cliConfig.openrouterApiKey, cliConfig.monitor ? undefined : explicitModel, cliConfig.monitor, cliConfig.anthropicApiKey, modelMap, {
78770
81815
  summarizeTools: cliConfig.summarizeTools,
78771
81816
  quiet: cliConfig.quiet,
@@ -78799,6 +81844,25 @@ Team Status`);
78799
81844
  const write = cliConfig.interactive ? console.log : console.error;
78800
81845
  write(`[claudish] Done
78801
81846
  `);
81847
+ try {
81848
+ const [{ readSessionStats: readSessionStats2 }, { printSessionSummary: printSessionSummary2 }, { findLatestSessionId: findLatestSessionId2 }] = await Promise.all([
81849
+ Promise.resolve().then(() => (init_session_stats(), exports_session_stats)),
81850
+ Promise.resolve().then(() => (init_session_summary(), exports_session_summary)),
81851
+ Promise.resolve().then(() => (init_session_discovery(), exports_session_discovery))
81852
+ ]);
81853
+ const stats = readSessionStats2(port);
81854
+ if (stats) {
81855
+ printSessionSummary2({
81856
+ stats,
81857
+ modelSpec: explicitModel || stats.modelName || "",
81858
+ resumeModelSpec: explicitModel ?? null,
81859
+ resumeId: resumedSessionId ?? findLatestSessionId2(process.cwd(), Date.now() - stats.durationMs),
81860
+ exitCode
81861
+ }, write);
81862
+ }
81863
+ } catch (e) {
81864
+ console.error(`[claudish] session summary unavailable: ${e}`);
81865
+ }
78802
81866
  }
78803
81867
  const sessionLogPath = getAlwaysOnLogPath2();
78804
81868
  if (exitCode !== 0 && sessionLogPath && !cliConfig.quiet) {