claudish 14.5.2 → 14.7.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 +678 -118
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -114,6 +114,7 @@ var init_launch_env = __esm(() => {
114
114
  "CLAUDISH_PANE_CWD",
115
115
  "CLAUDISH_HOOK_INBOX",
116
116
  "CLAUDISH_HOOK_SESSION",
117
+ "CLAUDISH_PARENT_SESSION",
117
118
  "CLAUDISH_BIN"
118
119
  ];
119
120
  });
@@ -166,6 +167,7 @@ var init_config = __esm(() => {
166
167
  CLAUDISH_PANE_CHILD: "CLAUDISH_PANE_CHILD",
167
168
  CLAUDISH_PANE_ENV: "CLAUDISH_PANE_ENV",
168
169
  CLAUDISH_PANE_CWD: "CLAUDISH_PANE_CWD",
170
+ CLAUDISH_PARENT_SESSION: "CLAUDISH_PARENT_SESSION",
169
171
  CLAUDISH_PANE_ROOT: "CLAUDISH_PANE_ROOT"
170
172
  };
171
173
  });
@@ -216,7 +218,8 @@ function applyPaneEnvSnapshot(env) {
216
218
  ENV.CLAUDISH_PANE_CHILD,
217
219
  ENV.CLAUDISH_PANE_CWD,
218
220
  HOOK_INBOX_VAR,
219
- HOOK_SESSION_VAR
221
+ HOOK_SESSION_VAR,
222
+ ENV.CLAUDISH_PARENT_SESSION
220
223
  ]);
221
224
  for (const key of Object.keys(env)) {
222
225
  if (Object.hasOwn(snapshot, key) || keep.has(key) || isPaneShellManagedKey(key))
@@ -298,7 +301,8 @@ var init_child_env = __esm(() => {
298
301
  ENV.CLAUDISH_PANE_ENV,
299
302
  ENV.CLAUDISH_PANE_CWD,
300
303
  HOOK_INBOX_VAR,
301
- HOOK_SESSION_VAR
304
+ HOOK_SESSION_VAR,
305
+ ENV.CLAUDISH_PARENT_SESSION
302
306
  ];
303
307
  PANE_SHELL_MANAGED_KEYS = [
304
308
  "COLUMNS",
@@ -4266,6 +4270,67 @@ var init_zod = __esm(() => {
4266
4270
  init_external();
4267
4271
  });
4268
4272
 
4273
+ // src/auth/accounts/account-label.ts
4274
+ function cleanPlan(plan) {
4275
+ const cleaned = (plan ?? "").toLowerCase().replace(/[^a-z0-9]/g, "").slice(0, PLAN_MAX);
4276
+ return cleaned || undefined;
4277
+ }
4278
+ function labelTaken(taken, label) {
4279
+ const wanted = label.toLowerCase();
4280
+ for (const existing of taken)
4281
+ if (existing.toLowerCase() === wanted)
4282
+ return true;
4283
+ return false;
4284
+ }
4285
+ function emailMatches(rowEmail, selector) {
4286
+ return rowEmail !== undefined && rowEmail.toLowerCase() === selector.toLowerCase();
4287
+ }
4288
+ function shellWord(label) {
4289
+ if (/^[\w@+.-]+$/.test(label))
4290
+ return label;
4291
+ return `'${label.replace(/'/g, `'\\''`)}'`;
4292
+ }
4293
+ function baseOf(input) {
4294
+ const fromEmail = (input.email ?? "").trim().toLowerCase().replace(/[^a-z0-9_@+.-]/g, "_").replace(/^\W+/, "");
4295
+ if (fromEmail)
4296
+ return fromEmail.slice(0, ACCOUNT_LABEL_MAX);
4297
+ return (input.chatgptAccountId ?? "").toLowerCase().replace(/[^a-z0-9_-]/g, "").replace(/^\W+/, "").slice(0, 8);
4298
+ }
4299
+ function fit(base, suffix) {
4300
+ return base.slice(0, ACCOUNT_LABEL_MAX - suffix.length).replace(/[.\-+@]+$/, "") + suffix;
4301
+ }
4302
+ function deriveAccountLabel(input) {
4303
+ const base = baseOf(input);
4304
+ if (!base)
4305
+ throw new Error("The login carries no email or account id; pass --label.");
4306
+ const taken = new Set;
4307
+ for (const label of input.taken)
4308
+ taken.add(label.toLowerCase());
4309
+ const plan = cleanPlan(input.plan);
4310
+ const planSuffix = plan ? `-${plan}` : "";
4311
+ function* candidates() {
4312
+ yield fit(base, "");
4313
+ if (plan)
4314
+ yield fit(base, planSuffix);
4315
+ for (let n = 2;n <= NUMBER_MAX; n++)
4316
+ yield fit(base, `${planSuffix}-${n}`);
4317
+ }
4318
+ for (const candidate of candidates()) {
4319
+ if (taken.has(candidate.toLowerCase()))
4320
+ continue;
4321
+ if (!ACCOUNT_LABEL_PATTERN.test(candidate)) {
4322
+ throw new Error(`The derived label "${candidate}" is not valid; pass --label.`);
4323
+ }
4324
+ return candidate;
4325
+ }
4326
+ throw new Error("Could not derive an unused label; pass --label.");
4327
+ }
4328
+ var ACCOUNT_LABEL_MAX = 64, ACCOUNT_LABEL_PATTERN, ACCOUNT_LABEL_RULE, PLAN_MAX = 16, NUMBER_MAX = 999;
4329
+ var init_account_label = __esm(() => {
4330
+ ACCOUNT_LABEL_PATTERN = /^\w[\w@+. -]{0,63}$/;
4331
+ ACCOUNT_LABEL_RULE = "An account label starts with a letter, digit or underscore and has at most 64 letters, " + "digits, spaces, and the characters _ @ + . -";
4332
+ });
4333
+
4269
4334
  // src/auth/quota/codex-window-slots.ts
4270
4335
  var CODEX_WINDOW_SLOTS;
4271
4336
  var init_codex_window_slots = __esm(() => {
@@ -4326,6 +4391,7 @@ function requireWireApiForTransport(transport, wire, ctx) {
4326
4391
  var CustomEndpointAuthSchemeSchema, WireValueSchema, WirePointerSchema, WireAssignmentSchema, CustomEndpointWireSchema, CustomEndpointSimpleSchema, CustomEndpointComplexSchema, CustomEndpointSchema, PredefinedEndpointsConfigSchema, AccountIdentitySchema, AccountStoreKindSchema, SubscriptionAccountSchema, CodexWindowSlotSchema, SubscriptionAccountsEntrySchema, SubscriptionAccountsSchema;
4327
4392
  var init_config_schema = __esm(() => {
4328
4393
  init_zod();
4394
+ init_account_label();
4329
4395
  init_codex_window_slots();
4330
4396
  CustomEndpointAuthSchemeSchema = _enum(["bearer", "x-api-key", "none"]);
4331
4397
  WireValueSchema = union([string2(), number2(), boolean2()]);
@@ -4392,7 +4458,7 @@ var init_config_schema = __esm(() => {
4392
4458
  AccountStoreKindSchema = _enum(["keychain", "file", "legacy-codex-file"]);
4393
4459
  SubscriptionAccountSchema = object({
4394
4460
  id: string2().regex(/^[a-z0-9]{12}$/, "account ids are generated, 12 lowercase alphanumerics"),
4395
- label: string2().regex(/^[\w][\w .-]{0,31}$/, "labels are 1-32 word characters, . - or space"),
4461
+ label: string2().regex(ACCOUNT_LABEL_PATTERN, ACCOUNT_LABEL_RULE),
4396
4462
  identity: AccountIdentitySchema.optional(),
4397
4463
  store: AccountStoreKindSchema,
4398
4464
  enabled: boolean2().optional(),
@@ -4586,12 +4652,12 @@ function isAccountProvider(value) {
4586
4652
  function sameAccount(a, b) {
4587
4653
  return a.provider === b.provider && a.id === b.id;
4588
4654
  }
4589
- var ACCOUNT_PROVIDERS, DEFAULT_SWITCH_AT = 0.9, ACCOUNT_ID_PATTERN, LEGACY_CODEX_ACCOUNT_ID = "legacy-codex", ACCOUNT_LABEL_PATTERN;
4655
+ var ACCOUNT_PROVIDERS, DEFAULT_SWITCH_AT = 0.9, ACCOUNT_ID_PATTERN, LEGACY_CODEX_ACCOUNT_ID = "legacy-codex";
4590
4656
  var init_types = __esm(() => {
4591
4657
  init_errors3();
4658
+ init_account_label();
4592
4659
  ACCOUNT_PROVIDERS = ["native-anthropic", "openai-codex"];
4593
4660
  ACCOUNT_ID_PATTERN = /^[a-z0-9]{12}$/;
4594
- ACCOUNT_LABEL_PATTERN = /^[\w][\w .-]{0,31}$/;
4595
4661
  });
4596
4662
 
4597
4663
  // src/auth/accounts/config-validation.ts
@@ -5332,7 +5398,7 @@ var init_onepassword_config = __esm(() => {
5332
5398
  });
5333
5399
 
5334
5400
  // src/version.ts
5335
- var VERSION = "14.5.2";
5401
+ var VERSION = "14.7.0";
5336
5402
 
5337
5403
  // src/redact.ts
5338
5404
  function isPlaceholder(value) {
@@ -15976,7 +16042,7 @@ var init_minimax_model_dialect = __esm(() => {
15976
16042
  });
15977
16043
 
15978
16044
  // src/auth/accounts/allowance-report-schema.ts
15979
- var ALLOWANCE_REPORT_SCHEMA = "claudish.allowance.v1", iso, fraction, finite, nonNegative, quantilesOf = (t) => object({ p10: t, p25: t, p50: t, p75: t, p90: t }), alertLevel, strategyName, evidence, unit, provenance, semantics, gapKind, exactness, accountSchema, remedySchema, alertSchema, trajectorySchema, sessionSchema, allowanceReportSchema, allowanceStatusAnswerSchema;
16045
+ var ALLOWANCE_REPORT_SCHEMA = "claudish.allowance.v1", iso, fraction, finite, nonNegative, quantilesOf = (t) => object({ p10: t, p25: t, p50: t, p75: t, p90: t }), alertLevel, strategyName, evidence, unit, provenance, semantics, gapKind, exactness, accountSchema, remedySchema, alertSchema, historyPoint, trajectorySchema, sessionSchema, allowanceReportSchema, ALLOWANCE_REPORT_SECTIONS, partialReport, allowanceStatusAnswerSchema;
15980
16046
  var init_allowance_report_schema = __esm(() => {
15981
16047
  init_zod();
15982
16048
  iso = exports_iso.datetime({ offset: true });
@@ -16006,7 +16072,10 @@ var init_allowance_report_schema = __esm(() => {
16006
16072
  semantics
16007
16073
  })),
16008
16074
  lambda: union([literal(0), literal(1)]).nullable(),
16009
- calibration: object({ beta: string2(), kappa: string2().nullable() })
16075
+ calibration: object({ beta: string2(), kappa: string2().nullable() }),
16076
+ active: boolean2().optional(),
16077
+ order: number2().int().min(0).optional(),
16078
+ lostAtReset: object({ at: iso, p10: fraction, p50: fraction, p90: fraction }).nullable().optional()
16010
16079
  });
16011
16080
  remedySchema = object({
16012
16081
  kind: _enum(["cut", "pause", "not-addressable"]),
@@ -16027,19 +16096,29 @@ var init_allowance_report_schema = __esm(() => {
16027
16096
  binding: array(object({ account: string2(), window: string2() })),
16028
16097
  remedy: remedySchema
16029
16098
  });
16099
+ historyPoint = object({
16100
+ at: iso,
16101
+ remaining: finite,
16102
+ provenance: _enum(["observed", "reset-inferred"])
16103
+ });
16030
16104
  trajectorySchema = object({
16031
- history: array(object({
16032
- at: iso,
16033
- remaining: finite,
16034
- provenance: _enum(["observed", "reset-inferred"])
16035
- })),
16105
+ history: array(historyPoint),
16106
+ historyByAccount: array(object({ accountId: string2(), history: array(historyPoint) })).optional(),
16036
16107
  forecast: array(object({ at: iso, p10: finite, p50: finite, p90: finite, pBlocked: fraction })),
16037
16108
  resets: array(object({ at: iso, account: string2(), window: string2(), projected: boolean2() })),
16038
16109
  blocked: array(object({ from: iso, to: iso, kind: gapKind, probability: fraction })),
16110
+ switches: array(object({
16111
+ accountId: string2(),
16112
+ soonest: iso,
16113
+ usual: iso,
16114
+ latest: iso,
16115
+ probability: fraction
16116
+ })).optional(),
16039
16117
  spark48: string2().regex(/^[\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588\u2592\u2593\u250A ]*$/u)
16040
16118
  });
16041
16119
  sessionSchema = object({
16042
16120
  session: union([string2().max(8), literal("unattributed")]),
16121
+ parent: union([string2().max(8), _null3()]).optional(),
16043
16122
  account: string2(),
16044
16123
  window: string2(),
16045
16124
  share: fraction,
@@ -16081,11 +16160,41 @@ var init_allowance_report_schema = __esm(() => {
16081
16160
  trajectory: trajectorySchema,
16082
16161
  sessions: array(sessionSchema)
16083
16162
  });
16163
+ ALLOWANCE_REPORT_SECTIONS = [
16164
+ "accounts",
16165
+ "alerts",
16166
+ "burn",
16167
+ "combined",
16168
+ "sessions",
16169
+ "trajectory"
16170
+ ];
16171
+ partialReport = allowanceReportSchema.partial({
16172
+ accounts: true,
16173
+ alerts: true,
16174
+ burn: true,
16175
+ combined: true,
16176
+ sessions: true,
16177
+ trajectory: true
16178
+ });
16084
16179
  allowanceStatusAnswerSchema = object({
16085
16180
  contract_version: literal(1),
16086
16181
  capabilities: array(string2()),
16087
- allowance: allowanceReportSchema.nullable(),
16182
+ sections: array(_enum(ALLOWANCE_REPORT_SECTIONS)).min(1).optional(),
16183
+ allowance: partialReport.nullable(),
16088
16184
  reason: string2().optional()
16185
+ }).superRefine((answer, ctx) => {
16186
+ if (answer.allowance === null)
16187
+ return;
16188
+ const wanted = answer.sections ?? ALLOWANCE_REPORT_SECTIONS;
16189
+ for (const section of wanted) {
16190
+ if (answer.allowance[section] === undefined) {
16191
+ ctx.addIssue({
16192
+ code: "custom",
16193
+ path: ["allowance", section],
16194
+ message: `section ${section} is missing`
16195
+ });
16196
+ }
16197
+ }
16089
16198
  });
16090
16199
  });
16091
16200
 
@@ -16537,7 +16646,7 @@ function cuOf(tokens, cfg) {
16537
16646
  const uncached = Math.max(0, tokens.input - tokens.cacheRead - tokens.cacheWrite);
16538
16647
  return uncached + w.cacheRead * tokens.cacheRead + w.cacheWrite * tokens.cacheWrite + w.output * tokens.output;
16539
16648
  }
16540
- var PROVIDER_COST_PRIORS, MINUTE_MS2 = 60000, HOUR_MS2, DEFAULT_ALLOWANCE_CONFIG, LOOKAHEAD_TRAJ_PATHS = 40;
16649
+ var PROVIDER_COST_PRIORS, MINUTE_MS2 = 60000, HOUR_MS2, DEFAULT_ALLOWANCE_CONFIG, LOOKAHEAD_TRAJ_PATHS = 40, MIN_PATH_SHARE = 0.1, PACE_MAX_MULT = 64, PACE_BISECT_STEPS = 4;
16541
16650
  var init_constants = __esm(() => {
16542
16651
  PROVIDER_COST_PRIORS = {
16543
16652
  "openai-codex": { cacheRead: 0.1, cacheWrite: 1, output: 4 },
@@ -17030,7 +17139,7 @@ function fold(s, e, cfg, seen) {
17030
17139
  insertServed(account, e.at, cu);
17031
17140
  addDemand(s, hourOf(e.at), cu, 1);
17032
17141
  if (e.session !== null)
17033
- addSession(s, e.session, e.at, e.account, cu, e.tokens, e.model, 1);
17142
+ addSession(s, e.session, e.at, e.account, cu, e.tokens, e.model, 1, e.parent ?? null);
17034
17143
  }
17035
17144
  if (e.outcome === "ok")
17036
17145
  closeCensor(s, e.at);
@@ -17079,13 +17188,13 @@ function fold(s, e, cfg, seen) {
17079
17188
  if (cu > 0) {
17080
17189
  insertServed(account, mid, cu);
17081
17190
  if (e.session !== null)
17082
- addSession(s, e.session, mid, e.account, cu, e.tokens, e.model, e.n);
17191
+ addSession(s, e.session, mid, e.account, cu, e.tokens, e.model, e.n, e.parent ?? null);
17083
17192
  }
17084
17193
  const bucket = addDemand(s, e.hour, cu, e.n);
17085
17194
  bucket.censoredMinutes = Math.min(60, Math.max(bucket.censoredMinutes, e.censoredMinutes));
17086
- if (e.first !== null)
17195
+ if (e.first !== null && e.n > 1)
17087
17196
  applySnapshot(account, e.hour, e.first, cfg);
17088
- if (e.last !== null && e.n > 1)
17197
+ if (e.last !== null)
17089
17198
  applySnapshot(account, e.hour + HOUR_MS4 - 1, e.last, cfg);
17090
17199
  touch(s, e.hour + HOUR_MS4 - 1);
17091
17200
  return;
@@ -17163,7 +17272,7 @@ function sealCensor(s, from, to) {
17163
17272
  bucket.censoredMinutes = Math.min(60, Math.max(bucket.censoredMinutes, overlap / MINUTE_MS4));
17164
17273
  }
17165
17274
  }
17166
- function addSession(s, session, at, account, cu, tokens, model, n) {
17275
+ function addSession(s, session, at, account, cu, tokens, model, n, parent) {
17167
17276
  let track = s.sessions[session];
17168
17277
  if (!track) {
17169
17278
  track = {
@@ -17188,6 +17297,8 @@ function addSession(s, session, at, account, cu, tokens, model, n) {
17188
17297
  track.tokens.cacheWrite += tokens.cacheWrite;
17189
17298
  track.tokens.output += tokens.output;
17190
17299
  track.models[model] = (track.models[model] ?? 0) + n;
17300
+ if (parent !== null && (track.parent ?? null) === null)
17301
+ track.parent = parent;
17191
17302
  }
17192
17303
  function applySnapshot(account, at, readings, cfg) {
17193
17304
  const current = account.lastCompleteAt === null || at >= account.lastCompleteAt;
@@ -19159,18 +19270,88 @@ function pauseGrid(horizonMs) {
19159
19270
  out.push(hours * HOUR_MS11);
19160
19271
  return out.filter((p) => p <= Math.max(horizonMs, HOUR_MS11));
19161
19272
  }
19162
- function* remedyScan(ctx, rateLong, cfg) {
19163
- const zero = ctx.factors.map(() => 0);
19164
- const externalStarts = gapsOn(ctx, zero, cfg).filter((g) => g.kind === "7d" && g.lengthMs >= REMEDY_MIN_GAP_MS).map((g) => g.start);
19165
- yield;
19273
+ function* cutScan(ctx, rate, cfg) {
19166
19274
  let cut = null;
19167
19275
  for (const phi of CUT_GRID) {
19168
- const gs = gapsOn(ctx, ctx.factors.map((f) => phi * rateLong * f), cfg);
19276
+ const gs = gapsOn(ctx, ctx.factors.map((f) => phi * rate * f), cfg);
19169
19277
  yield;
19170
19278
  if (hasGap(gs))
19171
19279
  break;
19172
19280
  cut = phi;
19173
19281
  }
19282
+ return cut;
19283
+ }
19284
+ function refillThroughput(ctx) {
19285
+ let total = 0;
19286
+ for (const v of ctx.views.filter(isRead)) {
19287
+ const g = v.windows.find((w) => w.key === v.governing);
19288
+ if (!g || g.lengthMinutes === null || g.lengthMinutes <= 0)
19289
+ continue;
19290
+ const beta = ctx.unit === "cu" ? v.beta[g.key]?.value : undefined;
19291
+ const capacity = beta !== undefined && beta > 0 ? 1 / beta : 1;
19292
+ total += capacity / (g.lengthMinutes / 60);
19293
+ }
19294
+ return total;
19295
+ }
19296
+ function* paceScan(ctx, ev, rates, cfg) {
19297
+ const rhoL = Number.isFinite(rates.long) && rates.long > 0 ? rates.long : 0;
19298
+ if (rhoL > 0 && hasGap(ev.long)) {
19299
+ const cut = yield* cutScan(ctx, rhoL, cfg);
19300
+ return { rate: cut === null ? 0 : cut * rhoL, basis: "cut-scan" };
19301
+ }
19302
+ const anchor = Math.max(rhoL, refillThroughput(ctx));
19303
+ if (!(anchor > 0) || !Number.isFinite(anchor))
19304
+ return null;
19305
+ const clean = (rho) => !hasGap(gapsOn(ctx, ctx.factors.map((f) => rho * f), cfg));
19306
+ if (anchor > rhoL) {
19307
+ const ok = clean(anchor);
19308
+ yield;
19309
+ if (!ok)
19310
+ return { rate: yield* bisect(rhoL, anchor, clean), basis: "bisect" };
19311
+ }
19312
+ const up = yield* bracketUp(anchor, clean);
19313
+ if (up.hi === null)
19314
+ return { rate: up.lo, basis: "capped" };
19315
+ return { rate: yield* bisect(up.lo, up.hi, clean), basis: "bracket" };
19316
+ }
19317
+ function* bracketUp(anchor, clean) {
19318
+ let lo = anchor;
19319
+ for (let mult = 2;mult <= PACE_MAX_MULT; mult *= 2) {
19320
+ const ok = clean(mult * anchor);
19321
+ yield;
19322
+ if (!ok)
19323
+ return { lo, hi: mult * anchor };
19324
+ lo = mult * anchor;
19325
+ }
19326
+ return { lo, hi: null };
19327
+ }
19328
+ function* bisect(lo, hi, clean) {
19329
+ let low = lo;
19330
+ let high = hi;
19331
+ for (let i = 0;i < PACE_BISECT_STEPS; i++) {
19332
+ const mid = (low + high) / 2;
19333
+ const ok = clean(mid);
19334
+ yield;
19335
+ if (ok)
19336
+ low = mid;
19337
+ else
19338
+ high = mid;
19339
+ }
19340
+ return low;
19341
+ }
19342
+ function paceInCuPerHour(pace, burnLong) {
19343
+ if (pace.phi !== null && Number.isFinite(burnLong) && burnLong > 0) {
19344
+ return Math.max(0, pace.phi * burnLong);
19345
+ }
19346
+ if (pace.unit === "cu")
19347
+ return Math.max(0, pace.rate);
19348
+ return null;
19349
+ }
19350
+ function* remedyScan(ctx, rateLong, cfg) {
19351
+ const zero = ctx.factors.map(() => 0);
19352
+ const externalStarts = gapsOn(ctx, zero, cfg).filter((g) => g.kind === "7d" && g.lengthMs >= REMEDY_MIN_GAP_MS).map((g) => g.start);
19353
+ yield;
19354
+ const cut = yield* cutScan(ctx, rateLong, cfg);
19174
19355
  let pauseMs = null;
19175
19356
  const h0 = hourStart(ctx.now);
19176
19357
  for (const p of pauseGrid(ctx.horizonMs)) {
@@ -19211,6 +19392,25 @@ function remedyText(r, gapStart, formatTime) {
19211
19392
  function rateBin(n) {
19212
19393
  return Number.isFinite(n) && n > 0 ? String(Math.round(8 * Math.log2(n))) : "0";
19213
19394
  }
19395
+ function* paceFor(ctx, ev, rates, inputsKey, cfg, opts) {
19396
+ if (!opts.pace || opts.payload === false)
19397
+ return null;
19398
+ const key = fnvHex([inputsKey, "pace"].join("|"));
19399
+ if (opts.paceReuse?.key === key)
19400
+ return opts.paceReuse;
19401
+ const found = yield* paceScan(ctx, ev, rates, cfg);
19402
+ if (found === null)
19403
+ return null;
19404
+ const simLong = rates.long;
19405
+ return {
19406
+ key,
19407
+ rate: found.rate,
19408
+ simLong,
19409
+ phi: Number.isFinite(simLong) && simLong > 0 ? found.rate / simLong : null,
19410
+ unit: ctx.unit,
19411
+ basis: found.basis
19412
+ };
19413
+ }
19214
19414
  function* alertsSliced(s, now, prior, cal, cfg, fc, opts = {}) {
19215
19415
  const base = prior ?? initialAlertMemory();
19216
19416
  if (evidenceOf(s, now) === "none") {
@@ -19219,7 +19419,8 @@ function* alertsSliced(s, now, prior, cal, cfg, fc, opts = {}) {
19219
19419
  active: [],
19220
19420
  transitions: [],
19221
19421
  blockedNow: false,
19222
- status: "insufficient-evidence"
19422
+ status: "insufficient-evidence",
19423
+ pace: null
19223
19424
  };
19224
19425
  }
19225
19426
  const ctx = gapContext(s, now, cal, cfg);
@@ -19256,7 +19457,8 @@ function* alertsSliced(s, now, prior, cal, cfg, fc, opts = {}) {
19256
19457
  const active = [];
19257
19458
  const raised = ["critical", "warn"].filter((l) => memory[l].raised);
19258
19459
  if (raised.length === 0 || opts.payload === false) {
19259
- return { memory, active, transitions, blockedNow, status: "ok" };
19460
+ const pace = yield* paceFor(ctx, ev, rates, inputsKey, cfg, opts);
19461
+ return { memory, active, transitions, blockedNow, status: "ok", pace };
19260
19462
  }
19261
19463
  const seed = seedOf(s, now, cfg);
19262
19464
  const key = fnvHex([inputsKey, seed, raised.join(","), alertPaths(cfg)].join("|"));
@@ -19298,9 +19500,10 @@ function* alertsSliced(s, now, prior, cal, cfg, fc, opts = {}) {
19298
19500
  remedy: r
19299
19501
  });
19300
19502
  }
19301
- return { memory, active, transitions, blockedNow, status: "ok", payload };
19503
+ const pace = active.length === 0 ? yield* paceFor(ctx, ev, rates, inputsKey, cfg, opts) : null;
19504
+ return { memory, active, transitions, blockedNow, status: "ok", payload, pace };
19302
19505
  }
19303
- var HOUR_MS11 = 3600000, REMEDY_MIN_GAP_MS, CUT_GRID, MIN_PATH_SHARE = 0.1, EPISODE_BUCKET_MS;
19506
+ var HOUR_MS11 = 3600000, REMEDY_MIN_GAP_MS, CUT_GRID, EPISODE_BUCKET_MS;
19304
19507
  var init_alerts = __esm(() => {
19305
19508
  init_capacity();
19306
19509
  init_constants();
@@ -19551,6 +19754,7 @@ function contributions(s, q, cfg) {
19551
19754
  const share = calibrated ? observed > 0 ? r.fraction / observed : 0 : totalCu > 0 ? r.cu / totalCu : 0;
19552
19755
  return {
19553
19756
  session: r.session,
19757
+ parent: track?.parent ?? null,
19554
19758
  account: r.accounts.size === 1 && single ? single.account : account,
19555
19759
  window: r.window,
19556
19760
  usedFraction: calibrated && single ? r.fraction : null,
@@ -19569,6 +19773,7 @@ function contributions(s, q, cfg) {
19569
19773
  const lasts = spent.map((x) => x.lastAt).filter((t) => t !== null);
19570
19774
  out.push({
19571
19775
  session: "unattributed",
19776
+ parent: null,
19572
19777
  account,
19573
19778
  window: window2,
19574
19779
  usedFraction: calibrated && single ? unattributed : null,
@@ -19644,7 +19849,7 @@ function deliverableIn(view, unit) {
19644
19849
  const beta = view.governing === null ? undefined : view.beta[view.governing];
19645
19850
  return isStrong(beta) && beta.value > 0 ? d / beta.value : d;
19646
19851
  }
19647
- function historyOf(s, views, now, unit) {
19852
+ function historyFrom(views, now) {
19648
19853
  let from = now - WEEK_MS4;
19649
19854
  for (const v of views) {
19650
19855
  const g = v.windows.find((w) => w.key === v.governing);
@@ -19652,23 +19857,38 @@ function historyOf(s, views, now, unit) {
19652
19857
  continue;
19653
19858
  from = Math.min(from, g.resetAt - g.lengthMinutes * MINUTE_MS10);
19654
19859
  }
19860
+ return from;
19861
+ }
19862
+ function historyAt(s, views, t, unit, byAccount) {
19863
+ let remaining = 0;
19864
+ let any = false;
19865
+ let inferred = false;
19866
+ for (const v of views) {
19867
+ const at = viewAt(s, v, t);
19868
+ if (!at)
19869
+ continue;
19870
+ any = true;
19871
+ inferred ||= at.inferred;
19872
+ const own = deliverableIn(at.view, unit);
19873
+ remaining += own;
19874
+ byAccount[v.account]?.push({ at: t, remaining: own, provenance: provenanceOf(at.inferred) });
19875
+ }
19876
+ return any ? { at: t, remaining, provenance: provenanceOf(inferred) } : null;
19877
+ }
19878
+ function historyOf(s, views, now, unit) {
19879
+ const from = historyFrom(views, now);
19655
19880
  const out = [];
19881
+ const byAccount = {};
19882
+ for (const v of views)
19883
+ byAccount[v.account] = [];
19656
19884
  for (let t = now;t >= from; t -= HOUR_MS14) {
19657
- let remaining = 0;
19658
- let any = false;
19659
- let inferred = false;
19660
- for (const v of views) {
19661
- const at = viewAt(s, v, t);
19662
- if (!at)
19663
- continue;
19664
- any = true;
19665
- inferred ||= at.inferred;
19666
- remaining += deliverableIn(at.view, unit);
19667
- }
19668
- if (any)
19669
- out.push({ at: t, remaining, provenance: inferred ? "reset-inferred" : "observed" });
19885
+ const point = historyAt(s, views, t, unit, byAccount);
19886
+ if (point)
19887
+ out.push(point);
19670
19888
  }
19671
- return out.reverse();
19889
+ for (const list of Object.values(byAccount))
19890
+ list.reverse();
19891
+ return { history: out.reverse(), byAccount };
19672
19892
  }
19673
19893
  function resetsOf(views, now, end) {
19674
19894
  const out = [];
@@ -19717,13 +19937,81 @@ function blockedRuns(results, forecast) {
19717
19937
  }
19718
19938
  return out;
19719
19939
  }
19940
+ function firstTakeovers(r, from, now, end) {
19941
+ const taken = new Map;
19942
+ let prev = from;
19943
+ for (const d of r.decisions) {
19944
+ if (d.account === null || d.cu <= 0 || d.account === prev)
19945
+ continue;
19946
+ const switched = prev !== null;
19947
+ prev = d.account;
19948
+ if (!switched)
19949
+ continue;
19950
+ const t = Math.max(now, r.start + d.k * HOUR_MS14);
19951
+ if (t > end)
19952
+ break;
19953
+ if (!taken.has(d.account))
19954
+ taken.set(d.account, t);
19955
+ }
19956
+ return taken;
19957
+ }
19958
+ function switchesOf2(results, views, poolActive, now, end) {
19959
+ const order = new Map(views.map((v, i) => [v.account, i]));
19960
+ const from = poolActive !== null && order.has(poolActive) ? poolActive : null;
19961
+ const times = new Map;
19962
+ for (const r of results) {
19963
+ for (const [account, t] of firstTakeovers(r, from, now, end)) {
19964
+ const list = times.get(account) ?? [];
19965
+ list.push(t);
19966
+ times.set(account, list);
19967
+ }
19968
+ }
19969
+ const paths = Math.max(1, results.length);
19970
+ const out = [];
19971
+ for (const [account, list] of times) {
19972
+ const probability = list.length / paths;
19973
+ if (probability < MIN_PATH_SHARE)
19974
+ continue;
19975
+ out.push({ account, q: quantilesOf2(list), probability: Math.min(1, probability) });
19976
+ }
19977
+ return out.sort((a, b) => a.q.p50 - b.q.p50 || (order.get(a.account) ?? 0) - (order.get(b.account) ?? 0));
19978
+ }
19979
+ function lostAtResetOf(results, views) {
19980
+ const out = {};
19981
+ const paths = Math.max(1, results.length);
19982
+ for (const v of views) {
19983
+ const g = v.windows.find((w) => w.key === v.governing);
19984
+ if (!g || g.remaining === null || g.resetAt === null || g.semantics === "sliding")
19985
+ continue;
19986
+ const values = [];
19987
+ for (const r of results) {
19988
+ const left = r.leftoverAtReset[v.account];
19989
+ if (left !== null && left !== undefined && Number.isFinite(left)) {
19990
+ values.push(Math.min(1, Math.max(0, left)));
19991
+ }
19992
+ }
19993
+ if (values.length === 0 || values.length < MIN_PATH_SHARE * paths)
19994
+ continue;
19995
+ out[v.account] = { at: g.resetAt, q: quantilesOf2(values) };
19996
+ }
19997
+ return out;
19998
+ }
19720
19999
  function trajectory(s, now, horizonMs, cal, cfg) {
19721
20000
  const all = accountViews(s, now, cfg).filter(isRead);
19722
20001
  const unit = strongBetas(all) ? "cu" : "account-fraction";
19723
- const history = historyOf(s, all, now, unit);
20002
+ const { history, byAccount: historyByAccount } = historyOf(s, all, now, unit);
19724
20003
  const end = now + Math.max(0, horizonMs);
19725
20004
  const resets = resetsOf(all, now, end);
19726
- const empty = { unit, history, forecast: [], resets, blocked: [] };
20005
+ const empty = {
20006
+ unit,
20007
+ history,
20008
+ forecast: [],
20009
+ resets,
20010
+ blocked: [],
20011
+ switches: [],
20012
+ lostAtReset: {},
20013
+ historyByAccount
20014
+ };
19727
20015
  if (evidenceOf(s, now) === "none" || all.length === 0)
19728
20016
  return empty;
19729
20017
  const views = unit === "cu" ? all : all.map(inFractions);
@@ -19761,7 +20049,16 @@ function trajectory(s, now, horizonMs, cal, cfg) {
19761
20049
  }
19762
20050
  forecast.push({ at, q: quantilesOf2(values), pBlocked: blocked / paths });
19763
20051
  }
19764
- return { unit, history, forecast, resets, blocked: blockedRuns(results, forecast) };
20052
+ return {
20053
+ unit,
20054
+ history,
20055
+ forecast,
20056
+ resets,
20057
+ blocked: blockedRuns(results, forecast),
20058
+ switches: switchesOf2(results, views, s.poolActive ?? null, now, end),
20059
+ lostAtReset: lostAtResetOf(results, views),
20060
+ historyByAccount
20061
+ };
19765
20062
  }
19766
20063
  function sparkline(t, cells) {
19767
20064
  const n = Math.max(0, Math.floor(cells));
@@ -19824,7 +20121,7 @@ function sparkline(t, cells) {
19824
20121
  return LEVELS[Math.max(0, level)] ?? " ";
19825
20122
  }).join("");
19826
20123
  }
19827
- var MINUTE_MS10 = 60000, HOUR_MS14 = 3600000, WEEK_MS4, BLOCKED_SHARE = 0.5, WIDE_BAND = 0.15, LEVELS;
20124
+ var MINUTE_MS10 = 60000, HOUR_MS14 = 3600000, WEEK_MS4, BLOCKED_SHARE = 0.5, WIDE_BAND = 0.15, LEVELS, provenanceOf = (inferred) => inferred ? "reset-inferred" : "observed";
19828
20125
  var init_trajectory = __esm(() => {
19829
20126
  init_alerts();
19830
20127
  init_calibrate();
@@ -19970,6 +20267,9 @@ function buildResponseRow(input) {
19970
20267
  finalInTok: count(input.finalInput),
19971
20268
  windows: readingsOf(input.provider, input.usage)
19972
20269
  };
20270
+ const parent = input.parent;
20271
+ if (parent && SESSION_PATTERN.test(parent) && parent !== row.session)
20272
+ row.parent = parent;
19973
20273
  return responseRowSchema.safeParse(row).success ? row : null;
19974
20274
  }
19975
20275
  function buildNoEligibleRow(provider, at) {
@@ -20023,7 +20323,8 @@ function toEvent(row) {
20023
20323
  tokens: tokensOf(row),
20024
20324
  finalInput: row.finalInTok,
20025
20325
  windows: row.windows,
20026
- outcome: row.outcome
20326
+ outcome: row.outcome,
20327
+ parent: row.parent ?? null
20027
20328
  };
20028
20329
  case "no-eligible":
20029
20330
  return row.until === undefined ? { kind: "no-eligible", at: row.at } : {
@@ -20044,7 +20345,8 @@ function toEvent(row) {
20044
20345
  tokens: { ...row.tokens },
20045
20346
  first: row.first,
20046
20347
  last: row.last,
20047
- censoredMinutes: row.censoredMinutes
20348
+ censoredMinutes: row.censoredMinutes,
20349
+ parent: row.parent ?? null
20048
20350
  };
20049
20351
  }
20050
20352
  }
@@ -20127,7 +20429,8 @@ var init_ledger_row = __esm(() => {
20127
20429
  cacheWriteTok: tokenCount,
20128
20430
  outTok: tokenCount,
20129
20431
  finalInTok: tokenCount,
20130
- windows: array(windowReadingSchema).max(16).nullable()
20432
+ windows: array(windowReadingSchema).max(16).nullable(),
20433
+ parent: string2().regex(SESSION_PATTERN).nullable().optional()
20131
20434
  });
20132
20435
  noEligibleRowSchema = object({
20133
20436
  v: literal(LEDGER_ROW_VERSION),
@@ -20156,7 +20459,8 @@ var init_ledger_row = __esm(() => {
20156
20459
  tokens: tokenCountsSchema,
20157
20460
  first: array(windowReadingSchema).max(16).nullable(),
20158
20461
  last: array(windowReadingSchema).max(16).nullable(),
20159
- censoredMinutes: number2().finite().min(0).max(60)
20462
+ censoredMinutes: number2().finite().min(0).max(60),
20463
+ parent: string2().regex(SESSION_PATTERN).nullable().optional()
20160
20464
  });
20161
20465
  ledgerRowSchema = discriminatedUnion("kind", [
20162
20466
  responseRowSchema,
@@ -20674,6 +20978,7 @@ function* rollupDaySteps(day, rows, openAtStart) {
20674
20978
  }
20675
20979
  }
20676
20980
  const hh = String(new Date(hour).getUTCHours()).padStart(2, "0");
20981
+ const parent = group.find((r) => r.parent)?.parent ?? null;
20677
20982
  const rollup = {
20678
20983
  v: LEDGER_ROW_VERSION,
20679
20984
  kind: "rollup",
@@ -20692,7 +20997,8 @@ function* rollupDaySteps(day, rows, openAtStart) {
20692
20997
  },
20693
20998
  first: first.size > 0 ? [...first.values()] : null,
20694
20999
  last: last.size > 0 ? [...last.values()] : null,
20695
- censoredMinutes: censored.get(head.provider)?.get(hour) ?? 0
21000
+ censoredMinutes: censored.get(head.provider)?.get(hour) ?? 0,
21001
+ ...parent ? { parent } : {}
20696
21002
  };
20697
21003
  lines.push(JSON.stringify(rollup));
20698
21004
  }
@@ -21796,6 +22102,12 @@ function readAlertsFile(env) {
21796
22102
  return emptyAlertsFile();
21797
22103
  }
21798
22104
  }
22105
+ function delegatingSession(env) {
22106
+ const value = env[ENV.CLAUDISH_PARENT_SESSION];
22107
+ if (!value || !isPaneChild(env))
22108
+ return null;
22109
+ return SESSION_PATTERN.test(value) ? value : null;
22110
+ }
21799
22111
 
21800
22112
  class AllowanceService {
21801
22113
  env;
@@ -21808,9 +22120,12 @@ class AllowanceService {
21808
22120
  nextFlight = 0;
21809
22121
  threads = new Map;
21810
22122
  models = new Map;
22123
+ reportStateStore = null;
22124
+ parentSession;
21811
22125
  constructor(env = process.env, clock = Date.now) {
21812
22126
  this.env = env;
21813
22127
  this.clock = clock;
22128
+ this.parentSession = delegatingSession(env);
21814
22129
  }
21815
22130
  record(attempt) {
21816
22131
  try {
@@ -21828,7 +22143,8 @@ class AllowanceService {
21828
22143
  outcome: attempt.outcome,
21829
22144
  tokens: attempt.tokens,
21830
22145
  finalInput: attempt.finalInput,
21831
- usage: attempt.usage
22146
+ usage: attempt.usage,
22147
+ parent: this.parentSession
21832
22148
  });
21833
22149
  if (!row) {
21834
22150
  this.warnOnce("a usage row failed its guards and was not written");
@@ -22008,9 +22324,21 @@ class AllowanceService {
22008
22324
  cal: m.cal,
22009
22325
  fc: m.fc,
22010
22326
  alerts: m.lastAlerts,
22011
- labels
22327
+ labels,
22328
+ poolActive: this.activeNow(provider, s)
22012
22329
  });
22013
22330
  }
22331
+ activeNow(provider, s) {
22332
+ try {
22333
+ this.reportStateStore ??= new StateStore({ path: statePath(this.env) });
22334
+ const read = this.reportStateStore.read();
22335
+ if (read.kind === "failed")
22336
+ return s.poolActive ?? null;
22337
+ return read.state.providers[provider]?.active ?? null;
22338
+ } catch {
22339
+ return s.poolActive ?? null;
22340
+ }
22341
+ }
22014
22342
  modelFor(provider) {
22015
22343
  let m = this.models.get(provider);
22016
22344
  if (!m) {
@@ -22041,7 +22369,8 @@ class AllowanceService {
22041
22369
  sinks: new Set,
22042
22370
  label: undefined,
22043
22371
  payload: null,
22044
- lastAlerts: { active: [], blockedNow: false, status: "insufficient-evidence" }
22372
+ pace: null,
22373
+ lastAlerts: { active: [], blockedNow: false, status: "insufficient-evidence", pace: null }
22045
22374
  };
22046
22375
  this.models.set(provider, m);
22047
22376
  }
@@ -22234,14 +22563,21 @@ class AllowanceService {
22234
22563
  let won = [];
22235
22564
  let landed = null;
22236
22565
  for (let attempt = 0;attempt < ALERTS_CAS_ATTEMPTS && landed === null; attempt++) {
22237
- const result = await runSliced(alertsSliced(state, now, peek, m.cal, m.cfg, fc, { reuse: m.payload }));
22566
+ const result = await runSliced(alertsSliced(state, now, peek, m.cal, m.cfg, fc, {
22567
+ reuse: m.payload,
22568
+ pace: !announce,
22569
+ paceReuse: m.pace
22570
+ }));
22238
22571
  if (result.payload)
22239
22572
  m.payload = result.payload;
22573
+ if (result.pace)
22574
+ m.pace = result.pace;
22240
22575
  if (!announce) {
22241
22576
  m.lastAlerts = {
22242
22577
  active: result.active,
22243
22578
  blockedNow: result.blockedNow,
22244
- status: result.status
22579
+ status: result.status,
22580
+ pace: result.pace
22245
22581
  };
22246
22582
  return [];
22247
22583
  }
@@ -22292,7 +22628,12 @@ class AllowanceService {
22292
22628
  log(`[allowance] ${provider}: the alert memory kept moving; step skipped`);
22293
22629
  return [];
22294
22630
  }
22295
- m.lastAlerts = { active: landed.active, blockedNow: landed.blockedNow, status: landed.status };
22631
+ m.lastAlerts = {
22632
+ active: landed.active,
22633
+ blockedNow: landed.blockedNow,
22634
+ status: landed.status,
22635
+ pace: null
22636
+ };
22296
22637
  const active = landed.active;
22297
22638
  const status = landed.status;
22298
22639
  const accounts = accountViews(m.state, now, m.cfg).length;
@@ -22428,19 +22769,27 @@ function poolRemaining(views) {
22428
22769
  }
22429
22770
  return { remainingNow: now.value, capacity, unit: now.unit };
22430
22771
  }
22772
+ function sustainableOf(top, pace, burnLong) {
22773
+ const long = Number.isFinite(burnLong) ? Math.max(0, burnLong) : 0;
22774
+ if (top)
22775
+ return top.remedy.kind === "cut" ? top.remedy.cutToFraction * long : 0;
22776
+ return pace ? paceInCuPerHour(pace, long) : null;
22777
+ }
22431
22778
  function buildReport(input) {
22432
22779
  const { s, now, cfg, cal, fc } = input;
22433
22780
  const horizonMs = Math.min(168 * HOUR_MS16, Math.max(HOUR_MS16, input.horizonMs));
22434
22781
  const evidence = evidenceOf(s, now);
22435
22782
  const none = evidence === "none";
22436
22783
  const views = accountViews(s, now, cfg);
22437
- const traj = trajectory(s, now, horizonMs, cal, cfg);
22784
+ const poolActive = input.poolActive !== undefined ? input.poolActive : s.poolActive ?? null;
22785
+ const traj = trajectory({ ...s, poolActive }, now, horizonMs, cal, cfg);
22438
22786
  const unit = fc?.unit ?? traj.unit;
22439
22787
  const burn = none ? { long: 0, short: 0 } : burnRates(s, now, cal, cfg);
22440
22788
  const ledgerHours = ledgerDays(s, now) * 24;
22441
22789
  const label = (id) => input.labels.get(id) ?? id;
22442
22790
  const pool = poolRemaining(views);
22443
22791
  const top = input.alerts.active[0];
22792
+ const status = none ? "insufficient-evidence" : input.alerts.status;
22444
22793
  const fmt = (at) => formatLocalTime(at);
22445
22794
  const report = {
22446
22795
  schema: ALLOWANCE_REPORT_SCHEMA,
@@ -22456,6 +22805,7 @@ function buildReport(input) {
22456
22805
  accounts: views.map((v) => {
22457
22806
  const g = v.governing === null ? undefined : v.beta[v.governing];
22458
22807
  const k = v.binding === null || v.binding === v.governing ? undefined : v.kappa[v.binding];
22808
+ const lost = traj.lostAtReset[v.account];
22459
22809
  return {
22460
22810
  label: label(v.account),
22461
22811
  id: v.account,
@@ -22471,7 +22821,15 @@ function buildReport(input) {
22471
22821
  semantics: w.semantics
22472
22822
  })),
22473
22823
  lambda: null,
22474
- calibration: { beta: g?.quality ?? "prior", kappa: k?.quality ?? null }
22824
+ calibration: { beta: g?.quality ?? "prior", kappa: k?.quality ?? null },
22825
+ active: poolActive !== null && v.account === poolActive,
22826
+ order: v.order,
22827
+ lostAtReset: lost ? {
22828
+ at: iso2(lost.at),
22829
+ p10: Math.min(1, Math.max(0, lost.q.p10)),
22830
+ p50: Math.min(1, Math.max(0, lost.q.p50)),
22831
+ p90: Math.min(1, Math.max(0, lost.q.p90))
22832
+ } : null
22475
22833
  };
22476
22834
  }),
22477
22835
  combined: {
@@ -22481,7 +22839,7 @@ function buildReport(input) {
22481
22839
  burn: {
22482
22840
  long: ledgerHours < cfg.BURN_LONG_HL_H ? null : burn.long,
22483
22841
  short: ledgerHours < cfg.BURN_SHORT_HL_H ? null : burn.short,
22484
- sustainable: top ? top.burn.sustainable : null,
22842
+ sustainable: status === "ok" ? sustainableOf(top, input.alerts.pace ?? null, burn.long) : null,
22485
22843
  unit: "cu/h"
22486
22844
  },
22487
22845
  alerts: none ? [] : input.alerts.active.map((a) => {
@@ -22517,7 +22875,7 @@ function buildReport(input) {
22517
22875
  };
22518
22876
  }),
22519
22877
  blockedNow: none ? false : input.alerts.blockedNow,
22520
- status: none ? "insufficient-evidence" : input.alerts.status,
22878
+ status,
22521
22879
  trajectory: {
22522
22880
  history: traj.history.map((h) => ({
22523
22881
  at: iso2(h.at),
@@ -22543,6 +22901,17 @@ function buildReport(input) {
22543
22901
  kind: b.kind,
22544
22902
  probability: b.probability
22545
22903
  })),
22904
+ historyByAccount: none ? [] : views.filter((v) => traj.historyByAccount[v.account] !== undefined).map((v) => ({
22905
+ accountId: v.account,
22906
+ history: (traj.historyByAccount[v.account] ?? []).filter((h) => h.at >= now - 24 * HOUR_MS16).map((h) => ({ at: iso2(h.at), remaining: h.remaining, provenance: h.provenance }))
22907
+ })),
22908
+ switches: traj.switches.map((sw) => ({
22909
+ accountId: sw.account,
22910
+ soonest: iso2(sw.q.p10),
22911
+ usual: iso2(sw.q.p50),
22912
+ latest: iso2(sw.q.p90),
22913
+ probability: Math.min(1, Math.max(0, sw.probability))
22914
+ })),
22546
22915
  spark48: sparkline({
22547
22916
  ...traj,
22548
22917
  history: traj.history.filter((h) => h.at >= now - 24 * HOUR_MS16),
@@ -22551,6 +22920,7 @@ function buildReport(input) {
22551
22920
  },
22552
22921
  sessions: none ? [] : contributions(s, { now, window: "binding" }, cfg).map((c) => ({
22553
22922
  session: c.session === "unattributed" ? "unattributed" : c.session.slice(0, 8),
22923
+ parent: c.parent === null ? null : c.parent.slice(0, 8),
22554
22924
  account: c.account === "all" ? "all" : label(c.account),
22555
22925
  window: c.window,
22556
22926
  share: Math.min(1, Math.max(0, c.share)),
@@ -22620,7 +22990,9 @@ function getAllowanceService() {
22620
22990
  }
22621
22991
  var THREAD_MAP_MAX = 512, THREAD_IDLE_MS, HOUR_MS16 = 3600000, DAY_MS6, LOAD_HISTORY_MS, LOAD_RAW_MS, LOAD_DEFER_MS = 1000, REFRESH_MIN_MS = 60000, SERVED_RECENTLY_MS, ALERTS_LOCK_WAIT_MS = 200, ALERTS_LOCK_STALE_MS = 1e4, ALERTS_CAS_ATTEMPTS = 3, IN_FLIGHT_MAX_MS, PENDING_MAX = 4096, LOAD_RETRY_MIN_MS = 60000, LOAD_RETRY_MAX_MS, ANNOUNCED_MAX = 256, CALENDAR_FUTURE_MS, DECLINE = () => null, iso2 = (at) => new Date(at).toISOString(), instance, safeRankerWarned = false;
22622
22992
  var init_allowance_service = __esm(() => {
22993
+ init_config();
22623
22994
  init_logger();
22995
+ init_child_env();
22624
22996
  init_allowance_report_schema();
22625
22997
  init_alerts();
22626
22998
  init_capacity();
@@ -24516,7 +24888,7 @@ function vendorName(provider) {
24516
24888
  }
24517
24889
  function loginCommandFor(ref) {
24518
24890
  const vendor = ref.provider === "openai-codex" ? "codex" : "claude";
24519
- return `claudish account login ${vendor} ${ref.label}`;
24891
+ return `claudish account login ${vendor} ${shellWord(ref.label)}`;
24520
24892
  }
24521
24893
  function rethrowSelectionFailure(err) {
24522
24894
  if (classifyConnectionError(err) || isAccountsError(err))
@@ -24927,6 +25299,7 @@ var LEASE_FAILURE_DEPRIORITIZE_MS = 60000, AccountChangedDuringLeaseError, pools
24927
25299
  var init_pool = __esm(() => {
24928
25300
  init_connection_error();
24929
25301
  init_logger();
25302
+ init_account_label();
24930
25303
  init_allowance_service();
24931
25304
  init_exchange();
24932
25305
  init_paths();
@@ -43758,7 +44131,7 @@ function assertLegacyCodexLogoutPermitted(configPath = activeConfigFile()) {
43758
44131
  const rows = codexEntry(raw)?.accounts ?? [];
43759
44132
  if (rows.length > 0) {
43760
44133
  const legacy = rows.find((row) => row.store === "legacy-codex-file");
43761
- throw new Error(legacy ? `The legacy Codex login is account ${legacy.label}. Remove it with ` + `\`claudish account remove codex ${legacy.label} --delete-file\`.` : "Codex accounts are managed by `claudish account`; remove one with " + "`claudish account remove codex <label>`.");
44134
+ throw new Error(legacy ? `The legacy Codex login is account ${legacy.label}. Remove it with ` + `\`claudish account remove codex ${shellWord(legacy.label)} --delete-file\`.` : "Codex accounts are managed by `claudish account`; remove one with " + "`claudish account remove codex <label>`.");
43762
44135
  }
43763
44136
  return { config: raw, result: undefined };
43764
44137
  });
@@ -43783,10 +44156,23 @@ async function saveLegacyCodexLogin(write, opts = {}) {
43783
44156
  }
43784
44157
  }
43785
44158
  }
43786
- function display(identity) {
44159
+ function display(identity, label) {
43787
44160
  const raw = identity.email || identity.chatgptAccountId || "identity unavailable";
43788
- const shown = [...raw].filter((ch) => ch.charCodeAt(0) > 31 && ch !== "\x7F").join("");
43789
- return identity.plan ? `${shown}, ${identity.plan}` : shown;
44161
+ const shown = [...raw].filter((ch) => {
44162
+ const code = ch.charCodeAt(0);
44163
+ return code > 31 && !(code >= 127 && code <= 159);
44164
+ }).join("");
44165
+ const text = identity.email && label.toLowerCase() === identity.email.toLowerCase() ? "" : shown;
44166
+ if (!identity.plan)
44167
+ return text;
44168
+ return text ? `${text}, ${identity.plan}` : identity.plan;
44169
+ }
44170
+ function note(identity, label) {
44171
+ const text = display(identity, label);
44172
+ return text ? ` (${text})` : "";
44173
+ }
44174
+ function named(row) {
44175
+ return row.legacy ? `${row.label} (legacy login)` : `${row.label} (id ${row.id})`;
43790
44176
  }
43791
44177
  function stableIdentity(identity) {
43792
44178
  const stable = identity.chatgptAccountId?.trim();
@@ -43796,8 +44182,16 @@ function stableIdentity(identity) {
43796
44182
  return stable;
43797
44183
  }
43798
44184
  function validLabel(label) {
43799
- if (!ACCOUNT_LABEL_PATTERN.test(label)) {
43800
- throw new Error("An account label starts with a letter, digit or underscore and has at most 32 letters, " + "digits, spaces, dots, underscores or hyphens.");
44185
+ if (!ACCOUNT_LABEL_PATTERN.test(label))
44186
+ throw new Error(ACCOUNT_LABEL_RULE);
44187
+ }
44188
+ function validSelector(selector) {
44189
+ const control = [...selector].some((ch) => {
44190
+ const code = ch.charCodeAt(0);
44191
+ return code <= 31 || code >= 127 && code <= 159;
44192
+ });
44193
+ if (selector.length < 1 || selector.length > 254 || control) {
44194
+ throw new Error("An account selector is a label or an email: 1 to 254 characters, no control characters.");
43801
44195
  }
43802
44196
  }
43803
44197
  function asRecord(row) {
@@ -43848,16 +44242,41 @@ class CodexAccountManagement {
43848
44242
  result: read(codexEntry(raw) ?? { generation: 0, accounts: [] })
43849
44243
  }));
43850
44244
  }
43851
- resolve(entry, target) {
44245
+ rowsOf(entry) {
43852
44246
  const rows = entry.accounts.map(asRecord);
43853
44247
  const legacy = this.synthesizedLegacy(entry);
43854
44248
  if (legacy)
43855
44249
  rows.push(legacy);
44250
+ return rows;
44251
+ }
44252
+ resolve(entry, target) {
44253
+ const rows = this.rowsOf(entry);
43856
44254
  const matches = rows.filter((row) => (target.id === undefined || row.id === target.id) && (target.label === undefined || row.label === target.label));
43857
44255
  if (matches.length === 1)
43858
44256
  return matches[0];
43859
44257
  throw new Error(matches.length > 1 ? "Ambiguous Codex account; name it with --id <generated-id>." : "No such Codex account; run `claudish account list`.");
43860
44258
  }
44259
+ select(entry, target) {
44260
+ const rows = this.rowsOf(entry).filter((row) => target.id === undefined || row.id === target.id);
44261
+ const { selector } = target;
44262
+ if (selector === undefined) {
44263
+ if (rows.length === 1)
44264
+ return rows[0];
44265
+ throw new Error("No such Codex account; run `claudish account list`.");
44266
+ }
44267
+ const exact = rows.filter((row) => row.label === selector);
44268
+ if (exact.length === 1)
44269
+ return exact[0];
44270
+ if (exact.length > 1) {
44271
+ throw new Error("Ambiguous Codex account; name it with --id <generated-id>.");
44272
+ }
44273
+ const byEmail = rows.filter((row) => emailMatches(row.identity.email, selector));
44274
+ if (byEmail.length === 1)
44275
+ return byEmail[0];
44276
+ if (byEmail.length === 0)
44277
+ throw new Error("No such Codex account; run `claudish account list`.");
44278
+ throw new Error(`More than one Codex account has the email ${selector}: ${byEmail.map(named).join(", ")}. ` + "Name one by label, or pass --id <generated-id>." + (byEmail.some((row) => row.legacy) ? " The legacy login has no --id; name it by its label." : ""));
44279
+ }
43861
44280
  newId(entry, reserved) {
43862
44281
  for (let attempt = 0;attempt < 16; attempt++) {
43863
44282
  const id = this.deps.newId?.() ?? randomBytes8(6).toString("hex");
@@ -43869,21 +44288,39 @@ class CodexAccountManagement {
43869
44288
  throw new Error("Could not generate an unused account id.");
43870
44289
  }
43871
44290
  refuseDuplicate(entry, identity, label, except) {
44291
+ const rows = this.rowsOf(entry);
44292
+ for (const row of rows) {
44293
+ if (row.id !== except && row.label === label) {
44294
+ throw new Error(`A Codex account labelled ${label} already exists.`);
44295
+ }
44296
+ }
44297
+ this.refuseIdentity(rows, identity, except);
44298
+ }
44299
+ refuseIdentity(rows, identity, except) {
43872
44300
  const stable = stableIdentity(identity);
43873
- const rows = entry.accounts.map(asRecord);
43874
- const legacy = this.synthesizedLegacy(entry);
43875
- if (legacy)
43876
- rows.push(legacy);
43877
44301
  for (const row of rows) {
43878
44302
  if (row.id === except)
43879
44303
  continue;
43880
- if (row.label === label)
43881
- throw new Error(`A Codex account labelled ${label} already exists.`);
43882
44304
  if (row.identity.chatgptAccountId === stable) {
43883
44305
  throw new Error(`This Codex identity is already registered as ${row.label}.`);
43884
44306
  }
43885
44307
  }
43886
44308
  }
44309
+ chooseLabel(rows, identity, explicit) {
44310
+ const taken = rows.map((row) => row.label);
44311
+ if (explicit !== undefined) {
44312
+ if (labelTaken(taken, explicit)) {
44313
+ throw new Error(`A Codex account labelled ${explicit} already exists.`);
44314
+ }
44315
+ return explicit;
44316
+ }
44317
+ return deriveAccountLabel({
44318
+ email: identity.email,
44319
+ plan: identity.plan,
44320
+ chatgptAccountId: identity.chatgptAccountId,
44321
+ taken
44322
+ });
44323
+ }
43887
44324
  store(kind) {
43888
44325
  return this.deps.store?.(kind) ?? storeFor(kind, this.env);
43889
44326
  }
@@ -43936,30 +44373,35 @@ class CodexAccountManagement {
43936
44373
  };
43937
44374
  }
43938
44375
  async run(command) {
43939
- if (command.label !== undefined)
43940
- validLabel(command.label);
43941
- if (command.verb === "add")
44376
+ if (command.verb === "add") {
44377
+ if (command.label !== undefined)
44378
+ validLabel(command.label);
43942
44379
  return this.add(command.label);
44380
+ }
44381
+ if (command.verb === "rename")
44382
+ validLabel(command.newLabel);
43943
44383
  if (command.id !== undefined && !ACCOUNT_ID_PATTERN.test(command.id)) {
43944
44384
  throw new Error("--id takes a generated account id, as `claudish account list --json` shows.");
43945
44385
  }
43946
- if (command.label === undefined && command.id === undefined) {
43947
- throw new Error("Name the Codex account by label, or with --id <generated-id>.");
44386
+ if (command.selector === undefined && command.id === undefined) {
44387
+ throw new Error("Name the Codex account by label or email, or with --id <generated-id>.");
43948
44388
  }
43949
- const target = this.inspect((entry) => this.resolve(entry, command));
44389
+ if (command.selector !== undefined)
44390
+ validSelector(command.selector);
44391
+ const target = this.inspect((entry) => this.select(entry, command));
43950
44392
  if (command.verb === "login")
43951
44393
  return this.login(target);
44394
+ if (command.verb === "rename")
44395
+ return this.rename(target, command.newLabel);
43952
44396
  if (command.verb === "remove")
43953
44397
  return this.remove(target, command);
43954
44398
  return this.setEnabled(target, command.verb === "enable");
43955
44399
  }
43956
44400
  async add(requestedLabel) {
43957
- const label = requestedLabel ?? await (this.deps.label ?? (() => dist_default3({ message: "Codex account label:" })))();
43958
- validLabel(label);
43959
44401
  const initial = this.inspect((entry) => {
43960
44402
  const legacy = this.synthesizedLegacy(entry);
43961
- if (entry.accounts.some((row) => row.label === label) || legacy?.label === label) {
43962
- throw new Error(`A Codex account labelled ${label} already exists.`);
44403
+ if (requestedLabel !== undefined && labelTaken(this.rowsOf(entry).map((r) => r.label), requestedLabel)) {
44404
+ throw new Error(`A Codex account labelled ${requestedLabel} already exists.`);
43963
44405
  }
43964
44406
  if (legacy && !legacy.identity.chatgptAccountId) {
43965
44407
  throw new Error("The legacy Codex login records no account id, so enrolling it cannot be checked for duplicates. Run `claudish login codex` once to refresh it, then retry.");
@@ -43971,26 +44413,29 @@ class CodexAccountManagement {
43971
44413
  }
43972
44414
  const acceptedAt = new Date(this.now()).toISOString();
43973
44415
  if (initial.legacy) {
43974
- this.print(`This add also enrolls your working legacy login as ${initial.legacy.label} (${display(initial.legacy.identity)}), store legacy-codex-file. Its credential file stays where it is and is not copied.`);
44416
+ this.print(`This add also enrolls your working legacy login as ${initial.legacy.label}${note(initial.legacy.identity, initial.legacy.label)}, store legacy-codex-file. Its credential file stays where it is and is not copied.`);
43975
44417
  }
43976
44418
  const store = this.deps.newStore?.() ?? defaultAccountStore(this.env);
43977
- this.print(`New Codex account ${label}; store: ${store.kind}.` + (store.kind === "file" ? " Warning: its tokens are kept in plaintext files readable only by you (0600)." : ""));
44419
+ this.print(`New Codex account${requestedLabel === undefined ? "" : ` ${requestedLabel}`}; store: ${store.kind}.` + (store.kind === "file" ? " Warning: its tokens are kept in plaintext files readable only by you (0600)." : ""));
43978
44420
  const result = await (this.deps.pkce ?? loginCodexPkce)();
43979
44421
  stableIdentity(result.identity);
43980
44422
  const row = {
43981
44423
  id: initial.id,
43982
- label,
44424
+ label: "",
43983
44425
  identity: result.identity,
43984
44426
  store: store.kind,
43985
44427
  enabled: true,
43986
44428
  addedAt: new Date(this.now()).toISOString()
43987
44429
  };
43988
44430
  const record = asRecord(row);
44431
+ let finalLabel = "";
43989
44432
  await this.gate(record, async (holds) => {
43990
44433
  this.inspect((entry) => {
43991
44434
  if (entry.accounts.some((a) => a.id === row.id))
43992
44435
  throw new Error("The generated account id is taken; retry the add.");
43993
- this.refuseDuplicate(entry, result.identity, label);
44436
+ const rows = this.rowsOf(entry);
44437
+ this.refuseIdentity(rows, result.identity);
44438
+ this.chooseLabel(rows, result.identity, requestedLabel);
43994
44439
  });
43995
44440
  if (await store.read(record, { uncached: true })) {
43996
44441
  throw new Error("A token item already exists for the generated account id; retry the add.");
@@ -44003,8 +44448,10 @@ class CodexAccountManagement {
44003
44448
  this.fence(holds);
44004
44449
  if (entry.accounts.some((a) => a.id === row.id))
44005
44450
  throw new Error("The generated account id is taken; retry the add.");
44006
- this.refuseDuplicate(entry, result.identity, label);
44007
- const legacy = this.synthesizedLegacy(entry);
44451
+ const rows = this.rowsOf(entry);
44452
+ this.refuseIdentity(rows, result.identity);
44453
+ const label = this.chooseLabel(rows, result.identity, requestedLabel);
44454
+ const legacy = rows.find((r) => r.legacy);
44008
44455
  if (legacy) {
44009
44456
  if (legacy.identity.chatgptAccountId !== initial.legacy?.identity.chatgptAccountId) {
44010
44457
  throw new Error("The legacy Codex login changed during the add; retry to confirm its enrollment.");
@@ -44018,15 +44465,18 @@ class CodexAccountManagement {
44018
44465
  addedAt: legacy.addedAt
44019
44466
  });
44020
44467
  }
44021
- entry.accounts.push(row);
44468
+ entry.accounts.push({ ...row, label });
44022
44469
  entry.noticeAcceptedAt ??= acceptedAt;
44470
+ finalLabel = label;
44023
44471
  });
44024
44472
  } catch (error) {
44025
44473
  await this.cleanupUnpublished(store, record, error);
44026
44474
  }
44027
44475
  });
44028
44476
  await this.invalidate();
44029
- return [`Added Codex account ${label} (${display(result.identity)}); store: ${store.kind}.`];
44477
+ return [
44478
+ `Added Codex account ${finalLabel}${note(result.identity, finalLabel)}; store: ${store.kind}.`
44479
+ ];
44030
44480
  }
44031
44481
  async cleanupUnpublished(store, record, error) {
44032
44482
  let published;
@@ -44052,9 +44502,9 @@ class CodexAccountManagement {
44052
44502
  else
44053
44503
  await (await Promise.resolve().then(() => (init_codex_oauth(), exports_codex_oauth))).CodexOAuth.getInstance().login();
44054
44504
  await this.invalidate();
44055
- return [`Codex account ${target.label}: login saved.`];
44505
+ return [`Codex account ${named(target)}: login saved.`];
44056
44506
  }
44057
- this.print(`Logging in Codex account ${target.label}; store: ${target.store}.`);
44507
+ this.print(`Logging in Codex account ${named(target)}; store: ${target.store}.`);
44058
44508
  const result = await (this.deps.pkce ?? loginCodexPkce)();
44059
44509
  const stable = stableIdentity(result.identity);
44060
44510
  if (target.identity.chatgptAccountId && stable !== target.identity.chatgptAccountId) {
@@ -44081,12 +44531,12 @@ class CodexAccountManagement {
44081
44531
  });
44082
44532
  await this.invalidate();
44083
44533
  return [
44084
- `Codex account ${target.label} (${display(result.identity)}): login saved; store: ${target.store}. A recorded needs-login mark clears on the account's next use.`
44534
+ `Codex account ${named(target)}${note(result.identity, target.label)}: login saved; store: ${target.store}. A recorded needs-login mark clears on the account's next use.`
44085
44535
  ];
44086
44536
  }
44087
44537
  async setEnabled(target, enabled) {
44088
44538
  if (target.legacy) {
44089
- this.print(`This enrolls your legacy login ${target.label} (${display(target.identity)}) as a configured account, store legacy-codex-file; its credential file stays where it is.`);
44539
+ this.print(`This enrolls your legacy login ${named(target)}${note(target.identity, target.label)} as a configured account, store legacy-codex-file; its credential file stays where it is.`);
44090
44540
  }
44091
44541
  let changed = false;
44092
44542
  let poolDisabled = false;
@@ -44115,7 +44565,7 @@ class CodexAccountManagement {
44115
44565
  if (changed)
44116
44566
  await this.invalidate();
44117
44567
  const lines = [
44118
- `Codex account ${target.label} ${changed ? "" : "is already "}${enabled ? "enabled" : "disabled"}.`
44568
+ `Codex account ${named(target)} ${changed ? "" : "is already "}${enabled ? "enabled" : "disabled"}.`
44119
44569
  ];
44120
44570
  if (!enabled) {
44121
44571
  lines.push("Configured Codex accounts still reserve the subscription: with no enabled account, Codex requests fail visibly instead of using OPENAI_CODEX_API_KEY.");
@@ -44125,6 +44575,52 @@ class CodexAccountManagement {
44125
44575
  }
44126
44576
  return lines;
44127
44577
  }
44578
+ async rename(target, newLabel) {
44579
+ if (target.legacy) {
44580
+ if (target.label === newLabel) {
44581
+ return [`Codex account ${named(target)} is already labelled ${newLabel}.`];
44582
+ }
44583
+ if (!target.identity.chatgptAccountId) {
44584
+ throw new Error("The legacy Codex login records no account id, so enrolling it cannot be checked for duplicates. Run `claudish login codex` once to refresh it, then retry.");
44585
+ }
44586
+ }
44587
+ let changed = false;
44588
+ await this.gate(target, async (holds) => {
44589
+ changed = mutateAccountsConfig(this.configPath(), PROVIDER, (entry) => {
44590
+ this.fence(holds);
44591
+ const current = this.resolve(entry, target.legacy ? { label: target.label } : { id: target.id });
44592
+ sameTarget(current, target);
44593
+ if (current.label === newLabel)
44594
+ return;
44595
+ const others = this.rowsOf(entry).filter((row) => row.id !== current.id).map((row) => row.label);
44596
+ if (labelTaken(others, newLabel)) {
44597
+ throw new Error(`A Codex account labelled ${newLabel} already exists.`);
44598
+ }
44599
+ if (current.legacy) {
44600
+ entry.accounts.push({
44601
+ id: this.newId(entry),
44602
+ label: newLabel,
44603
+ identity: current.identity,
44604
+ store: "legacy-codex-file",
44605
+ enabled: true,
44606
+ addedAt: current.addedAt
44607
+ });
44608
+ return;
44609
+ }
44610
+ const row = entry.accounts.find((a) => a.id === target.id);
44611
+ if (row)
44612
+ row.label = newLabel;
44613
+ });
44614
+ });
44615
+ if (changed)
44616
+ await this.invalidate();
44617
+ if (changed && target.legacy) {
44618
+ this.print(`This enrolled your legacy login ${named(target)}${note(target.identity, target.label)} as a configured account, store legacy-codex-file; its credential file stays where it is.`);
44619
+ }
44620
+ return [
44621
+ changed ? `Renamed Codex account ${named(target)} to ${newLabel}.` : `Codex account ${named(target)} is already labelled ${newLabel}.`
44622
+ ];
44623
+ }
44128
44624
  async remove(target, command) {
44129
44625
  const legacyFile = target.store === "legacy-codex-file";
44130
44626
  if (legacyFile && !command.deleteFile) {
@@ -44133,7 +44629,7 @@ class CodexAccountManagement {
44133
44629
  if (!legacyFile && command.deleteFile) {
44134
44630
  throw new Error(`--delete-file applies only to the legacy Codex login, not ${target.label}.`);
44135
44631
  }
44136
- if (!command.yes && !await this.ask(`Remove Codex account ${target.label} (${display(target.identity)})${legacyFile ? " and delete its legacy credential file" : ""}? Requests already in flight cannot be recalled.`)) {
44632
+ if (!command.yes && !await this.ask(`Remove Codex account ${named(target)}${note(target.identity, target.label)}${legacyFile ? " and delete its legacy credential file" : ""}? Requests already in flight cannot be recalled.`)) {
44137
44633
  return ["Cancelled; no account was removed."];
44138
44634
  }
44139
44635
  const warnings = [];
@@ -44175,7 +44671,7 @@ class CodexAccountManagement {
44175
44671
  key: "OPENAI_CODEX_API_KEY if one is configured, otherwise none"
44176
44672
  }[next];
44177
44673
  return [
44178
- `Removed Codex account ${target.label} (${display(target.identity)}). Next Codex credential for new requests: ${nextText}.`,
44674
+ `Removed Codex account ${named(target)}${note(target.identity, target.label)}. Next Codex credential for new requests: ${nextText}.`,
44179
44675
  ...next === "key" ? ["Warning: new Codex requests may now use OPENAI_CODEX_API_KEY, billed per token."] : [],
44180
44676
  ...warnings
44181
44677
  ];
@@ -44189,6 +44685,7 @@ var init_management = __esm(() => {
44189
44685
  init_dist12();
44190
44686
  init_profile_config();
44191
44687
  init_codex_pkce();
44688
+ init_account_label();
44192
44689
  init_config_lock();
44193
44690
  init_file_lock();
44194
44691
  init_paths();
@@ -89664,10 +90161,12 @@ ${h("SUBCOMMANDS")}
89664
90161
  ${bold("Subscription accounts:")}
89665
90162
  ${green("claudish account list")} Configured subscription accounts, per claudish provider
89666
90163
  ${green("claudish account status")} ${yellow("[--json]")} Usage per window, thresholds and blockers
89667
- ${green("claudish account add codex")} ${yellow("[--label <label>]")} Log in a Codex account you own and add it
90164
+ ${green("claudish account add codex")} ${yellow("[--label <label>]")} Log in a Codex account you own and add it ${dim("(label defaults to the login's email)")}
89668
90165
  ${green("claudish account login codex")} ${yellow("<label>")} Log the same Codex identity in again
89669
90166
  ${green("claudish account enable|disable codex")} ${yellow("<label>")} Include or skip it in selection
90167
+ ${green("claudish account rename codex")} ${yellow("<label> <new-label>")} Give an account a new label
89670
90168
  ${green("claudish account remove codex")} ${yellow("<label> [--yes] [--delete-file]")}
90169
+ ${dim("A <label> may also be the account's email.")}
89671
90170
  ${dim("--id <generated-id> may replace or confirm a label. Removing the legacy login (default)")}
89672
90171
  ${dim("requires --delete-file. New accounts use the macOS Keychain, or protected plaintext files")}
89673
90172
  ${dim("where there is none. No token input or export; Claude account mutations are not available.")}
@@ -90715,6 +91214,7 @@ function buildPaneEnv(input) {
90715
91214
  env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = "1";
90716
91215
  if (input.parentEnv.CLAUDE_CODE_NATIVE_CURSOR === undefined)
90717
91216
  env.CLAUDE_CODE_NATIVE_CURSOR = "1";
91217
+ delete env[ENV.CLAUDISH_PARENT_SESSION];
90718
91218
  Object.assign(env, input.slotEnv);
90719
91219
  if (input.configFile !== null)
90720
91220
  env.CLAUDISH_CONFIG = input.configFile;
@@ -97791,7 +98291,8 @@ async function startModelsIn(flight, sessionPath, opts) {
97791
98291
  kind: "team",
97792
98292
  id: `${runId}:${e.id}`,
97793
98293
  role: kind === "judge" ? "judge" : "slot"
97794
- })
98294
+ }),
98295
+ ...opts.parentClaudeSessionId ? { [ENV.CLAUDISH_PARENT_SESSION]: opts.parentClaudeSessionId } : {}
97795
98296
  },
97796
98297
  shape: "one-shot",
97797
98298
  initialPrompt: inputContent,
@@ -98018,6 +98519,7 @@ async function judgeResponses(sessionPath, opts = {}) {
98018
98519
  await runModels(judgePath, {
98019
98520
  claudeFlags: opts.claudeFlags,
98020
98521
  kind: "judge",
98522
+ ...opts.parentClaudeSessionId ? { parentClaudeSessionId: opts.parentClaudeSessionId } : {},
98021
98523
  ...opts.parentEnv ? { parentEnv: opts.parentEnv } : {},
98022
98524
  ...opts.paneTimings ? { paneTimings: opts.paneTimings } : {}
98023
98525
  });
@@ -98837,7 +99339,8 @@ class SessionManager {
98837
99339
  [ENV.CLAUDISH_TOKEN_FILE]: entry.tokenFile,
98838
99340
  [UPSTREAM_ERROR_LOG_ENV]: entry.upstreamErrorLogPath,
98839
99341
  ...entry.inboxPath ? { [HOOK_INBOX_ENV]: entry.inboxPath } : {},
98840
- [HOOK_SESSION_ENV]: JSON.stringify({ kind: "channel", id: info.sessionId, role: null })
99342
+ [HOOK_SESSION_ENV]: JSON.stringify({ kind: "channel", id: info.sessionId, role: null }),
99343
+ ...info.parentClaudeSessionId ? { [ENV.CLAUDISH_PARENT_SESSION]: info.parentClaudeSessionId } : {}
98841
99344
  },
98842
99345
  shape: info.shape,
98843
99346
  ...opts.prompt ? { initialPrompt: opts.prompt } : {},
@@ -100665,9 +101168,32 @@ async function allowanceStatus(horizonMs) {
100665
101168
  return { ...meta, allowance: null, reason: `the allowance report failed: ${message}` };
100666
101169
  }
100667
101170
  }
101171
+ function allowanceSectionsArg(value) {
101172
+ if (value === undefined)
101173
+ return;
101174
+ const known = ALLOWANCE_REPORT_SECTIONS;
101175
+ if (!Array.isArray(value) || value.length === 0)
101176
+ throw new ContractErrorException("invalid_args", `'sections' must be a non-empty array of ${known.join(", ")}`);
101177
+ for (const name of value) {
101178
+ if (typeof name !== "string" || !known.includes(name))
101179
+ throw new ContractErrorException("invalid_args", `'sections' has an unknown section ${JSON.stringify(name)}; known: ${known.join(", ")}`);
101180
+ }
101181
+ return [...new Set(value)].sort();
101182
+ }
101183
+ function allowanceSections(answer, sections) {
101184
+ if (sections === undefined || answer.allowance === null)
101185
+ return answer;
101186
+ const dropped = new Set(ALLOWANCE_REPORT_SECTIONS.filter((section) => !sections.includes(section)));
101187
+ const allowance = Object.fromEntries(Object.entries(answer.allowance).filter(([key]) => !dropped.has(key)));
101188
+ const { contract_version, capabilities, ...rest } = answer;
101189
+ return { contract_version, capabilities, sections, ...rest, allowance };
101190
+ }
100668
101191
  function contractAnswer(value) {
100669
101192
  return { content: [{ type: "text", text: JSON.stringify(value, null, 2) }] };
100670
101193
  }
101194
+ function compactAnswer(value) {
101195
+ return { content: [{ type: "text", text: JSON.stringify(value) }] };
101196
+ }
100671
101197
  function contractErrorAnswer(e) {
100672
101198
  const err = e instanceof ContractErrorException ? e : new ContractErrorException("invalid_args", e instanceof Error ? e.message : String(e));
100673
101199
  return {
@@ -101254,11 +101780,15 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
101254
101780
  models?.[givenModels?.indexOf(m) ?? -1] ?? m,
101255
101781
  level
101256
101782
  ])) : undefined;
101783
+ const runsModels = mode === "run" || mode === "run-and-judge";
101784
+ const runsJudges = mode === "judge" || mode === "run-and-judge";
101785
+ const parentClaudeSessionId = runsModels || runsJudges ? await parentSessionForCall({ hostPid: sessionManager.hostPid }) : undefined;
101257
101786
  const runOpts = {
101258
101787
  requirePattern,
101259
101788
  minOutputBytes,
101260
101789
  claudeFlags: childFlags,
101261
101790
  efforts,
101791
+ parentClaudeSessionId,
101262
101792
  onProgress: (u) => {
101263
101793
  ctx.reportProgress(`team: ${u.phase}`);
101264
101794
  }
@@ -101269,8 +101799,6 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
101269
101799
  const existing = join56(resolved, "input.md");
101270
101800
  return existsSync39(existing) ? readFileSync45(existing, "utf-8") : undefined;
101271
101801
  };
101272
- const runsModels = mode === "run" || mode === "run-and-judge";
101273
- const runsJudges = mode === "judge" || mode === "run-and-judge";
101274
101802
  const choice = modelChoiceStop([
101275
101803
  ...runsModels && givenModels && models ? givenModels.map((given, i) => ({ given, spec: models[i] })) : [],
101276
101804
  ...runsJudges && givenJudges && judges ? givenJudges.map((given, i) => ({ given, spec: judges[i] })) : []
@@ -101295,9 +101823,6 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
101295
101823
  requirePattern
101296
101824
  });
101297
101825
  setupSession(resolved, models, input);
101298
- const parentClaudeSessionId = await parentSessionForCall({
101299
- hostPid: sessionManager.hostPid
101300
- });
101301
101826
  const monitorRecord = sessionManager.recordTeamRun({
101302
101827
  teamPath: resolved,
101303
101828
  slots: models.length,
@@ -101350,7 +101875,11 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
101350
101875
  };
101351
101876
  }
101352
101877
  case "judge": {
101353
- const verdict = await judgeResponses(resolved, { judges, claudeFlags: childFlags });
101878
+ const verdict = await judgeResponses(resolved, {
101879
+ judges,
101880
+ claudeFlags: childFlags,
101881
+ parentClaudeSessionId
101882
+ });
101354
101883
  const out = providerNote ? { ...verdict, provider_note: providerNote } : verdict;
101355
101884
  return { content: [{ type: "text", text: JSON.stringify(out, null, 2) }] };
101356
101885
  }
@@ -101368,7 +101897,11 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
101368
101897
  const handle = await startModels(resolved, runOpts);
101369
101898
  await handle.done;
101370
101899
  const run = teamRunRow(resolved, handle.runId);
101371
- const verdict = await judgeResponses(resolved, { judges, claudeFlags: childFlags });
101900
+ const verdict = await judgeResponses(resolved, {
101901
+ judges,
101902
+ claudeFlags: childFlags,
101903
+ parentClaudeSessionId
101904
+ });
101372
101905
  return {
101373
101906
  content: [
101374
101907
  {
@@ -101554,7 +102087,7 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
101554
102087
  });
101555
102088
  tools.push({
101556
102089
  name: "allowance_status",
101557
- description: "Report the local Codex account pool's allowance: {contract_version, capabilities, allowance, reason?}. `allowance` is the claudish.allowance.v1 report of THIS machine's claudish sessions (accounts, remaining capacity, burn rate, projected gaps with remedies, trajectory, per-session shares), or null with a `reason` when there is no account pool. Read-only; it sends nothing to any vendor.",
102090
+ description: 'Report the local Codex account pool\'s allowance: {contract_version, capabilities, allowance, reason?}. `allowance` is the claudish.allowance.v1 report of THIS machine\'s claudish sessions (accounts, remaining capacity, burn rate, projected gaps with remedies, trajectory, per-session shares), or null with a `reason` when there is no account pool. `sections` limits the report to those parts (accounts, combined, burn, alerts, trajectory, sessions) beside its header, and the answer then lists them in `sections`; a frequent poll should pass ["accounts","burn","alerts"]. Read-only; it sends nothing to any vendor.',
101558
102091
  inputSchema: {
101559
102092
  type: "object",
101560
102093
  properties: {
@@ -101566,6 +102099,15 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
101566
102099
  horizon_hours: {
101567
102100
  type: "number",
101568
102101
  description: "Forecast horizon in hours, 1\u2013168 (default: 168)"
102102
+ },
102103
+ sections: {
102104
+ type: "array",
102105
+ items: {
102106
+ type: "string",
102107
+ enum: [...ALLOWANCE_REPORT_SECTIONS]
102108
+ },
102109
+ minItems: 1,
102110
+ description: 'Report only these parts beside the header (default: all). Poll with ["accounts","burn","alerts"]; the trajectory and sessions are most of the bytes.'
101569
102111
  }
101570
102112
  }
101571
102113
  },
@@ -101578,7 +102120,8 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
101578
102120
  const hours = args.horizon_hours ?? 168;
101579
102121
  if (typeof hours !== "number" || !Number.isFinite(hours) || hours < 1 || hours > 168)
101580
102122
  throw new ContractErrorException("invalid_args", "'horizon_hours' must be a number from 1 to 168");
101581
- return contractAnswer(await allowanceStatus(hours * 3600000));
102123
+ const sections = allowanceSectionsArg(args.sections);
102124
+ return compactAnswer(allowanceSections(await allowanceStatus(hours * 3600000), sections));
101582
102125
  } catch (e) {
101583
102126
  return contractErrorAnswer(e);
101584
102127
  }
@@ -102052,6 +102595,7 @@ var init_mcp_server = __esm(() => {
102052
102595
  init_types3();
102053
102596
  init_base_api_format();
102054
102597
  init_model_catalog();
102598
+ init_allowance_report_schema();
102055
102599
  init_prehydrate();
102056
102600
  init_prompt_role();
102057
102601
  init_fs_atomic();
@@ -104220,7 +104764,7 @@ function renderRow2(row, pool, now) {
104220
104764
  lines.push(` ${warn2(formatReset(row.blocker.resetAt, now, row.blocker.estimated === true))}`);
104221
104765
  }
104222
104766
  if (row.blocker?.kind === "needs-reauth") {
104223
- lines.push(` ${bad2("needs login")}: run \`claudish account login ${vendorOf(row.ref.provider)} ${row.record.label}\``);
104767
+ lines.push(` ${bad2("needs login")}: run \`claudish account login ${vendorOf(row.ref.provider)} ${shellWord(row.record.label)}\``);
104224
104768
  if (row.state.needsReauth?.lastReason) {
104225
104769
  lines.push(` ${dim4(row.state.needsReauth.lastReason)}`);
104226
104770
  }
@@ -104295,7 +104839,7 @@ ${strong2(vendorOf(report.provider))}`);
104295
104839
  if (!any) {
104296
104840
  console.log(`
104297
104841
  No subscription accounts are configured.`);
104298
- console.log(dim4("A Codex login (`claudish login codex`) is shown here as `default (legacy file)` " + "once it exists; add an account with `claudish account add codex --label <label>`."));
104842
+ console.log(dim4("A Codex login (`claudish login codex`) is shown here as `default (legacy file)` " + "once it exists; add an account with `claudish account add codex`."));
104299
104843
  }
104300
104844
  console.log("");
104301
104845
  }
@@ -104378,12 +104922,24 @@ function parseAccountMutation(args) {
104378
104922
  throw usage2("account add codex takes no positional argument; use --label.");
104379
104923
  return { verb: v, label: values.get("--label") };
104380
104924
  }
104925
+ if (v === "rename") {
104926
+ const hasId = values.has("--id");
104927
+ if (positional.length === 2 || positional.length === 1 && hasId) {
104928
+ return {
104929
+ verb: v,
104930
+ selector: positional.length === 2 ? positional[0] : undefined,
104931
+ id: values.get("--id"),
104932
+ newLabel: positional[positional.length - 1]
104933
+ };
104934
+ }
104935
+ throw usage2("account rename codex needs <label> <new-label>, or --id <generated-id> <new-label>.");
104936
+ }
104381
104937
  if (positional.length > 1)
104382
104938
  throw usage2(`account ${v} codex names one account.`);
104383
104939
  if (positional.length === 0 && !values.has("--id")) {
104384
104940
  throw usage2(`account ${v} codex needs a label or --id <generated-id>.`);
104385
104941
  }
104386
- const target = { label: positional[0], id: values.get("--id") };
104942
+ const target = { selector: positional[0], id: values.get("--id") };
104387
104943
  if (v === "remove") {
104388
104944
  return { verb: v, ...target, yes: flags.has("--yes"), deleteFile: flags.has("--delete-file") };
104389
104945
  }
@@ -104488,8 +105044,11 @@ var ACCOUNT_HELP = `Usage:
104488
105044
  claudish account forecast [--json] [--horizon <h|d>] [--provider codex]
104489
105045
  claudish account add codex [--label <label>]
104490
105046
  claudish account login|enable|disable codex <label> [--id <generated-id>]
105047
+ claudish account rename codex <label> <new-label> [--id <generated-id>]
104491
105048
  claudish account remove codex <label> [--id <generated-id>] [--yes] [--delete-file]
104492
105049
 
105050
+ --label on add is optional and overrides the default, the login's email.
105051
+ A command that takes <label> also takes the account's email.
104493
105052
  --id <generated-id> may replace or confirm the label; labels are unique per claudish provider.
104494
105053
  Removing the legacy login (default, legacy-codex-file) requires --delete-file.
104495
105054
  New accounts use the macOS Keychain, or protected plaintext files where there is none.
@@ -104503,13 +105062,14 @@ The allowance trajectory, alerts and sessions of a claudish provider's account p
104503
105062
  --horizon <h|d> how far ahead to project, 1h to 7d (default 7d)
104504
105063
  --provider codex the claudish provider (openai-codex); the only one with a model today`;
104505
105064
  var init_account_command = __esm(() => {
105065
+ init_account_label();
104506
105066
  init_policy2();
104507
105067
  init_pool();
104508
105068
  init_registry();
104509
105069
  init_state_store();
104510
105070
  init_types();
104511
105071
  init_ansi();
104512
- MUTATION_VERBS = ["add", "login", "enable", "disable", "remove"];
105072
+ MUTATION_VERBS = ["add", "login", "enable", "disable", "remove", "rename"];
104513
105073
  });
104514
105074
 
104515
105075
  // src/auth/antigravity-oauth.ts