claudish 14.9.0 → 14.10.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 +254 -47
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -1716,6 +1716,12 @@ function handleIntersectionResults(result, left, right) {
1716
1716
  result.value = merged.data;
1717
1717
  return result;
1718
1718
  }
1719
+ function handleTupleResult(result, final, index) {
1720
+ if (result.issues.length) {
1721
+ final.issues.push(...prefixIssues(index, result.issues));
1722
+ }
1723
+ final.value[index] = result.value;
1724
+ }
1719
1725
  function handleOptionalResult(result, input) {
1720
1726
  if (result.issues.length && input === undefined) {
1721
1727
  return { issues: [], value: undefined };
@@ -1764,7 +1770,7 @@ function handleRefineResult(result, payload, input, inst) {
1764
1770
  payload.issues.push(issue(_iss));
1765
1771
  }
1766
1772
  }
1767
- var $ZodType, $ZodString, $ZodStringFormat, $ZodGUID, $ZodUUID, $ZodEmail, $ZodURL, $ZodEmoji, $ZodNanoID, $ZodCUID, $ZodCUID2, $ZodULID, $ZodXID, $ZodKSUID, $ZodISODateTime, $ZodISODate, $ZodISOTime, $ZodISODuration, $ZodIPv4, $ZodIPv6, $ZodCIDRv4, $ZodCIDRv6, $ZodBase64, $ZodBase64URL, $ZodE164, $ZodJWT, $ZodNumber, $ZodNumberFormat, $ZodBoolean, $ZodNull, $ZodUnknown, $ZodNever, $ZodArray, $ZodObject, $ZodObjectJIT, $ZodUnion, $ZodDiscriminatedUnion, $ZodIntersection, $ZodRecord, $ZodEnum, $ZodLiteral, $ZodTransform, $ZodOptional, $ZodNullable, $ZodDefault, $ZodPrefault, $ZodNonOptional, $ZodCatch, $ZodPipe, $ZodReadonly, $ZodCustom;
1773
+ var $ZodType, $ZodString, $ZodStringFormat, $ZodGUID, $ZodUUID, $ZodEmail, $ZodURL, $ZodEmoji, $ZodNanoID, $ZodCUID, $ZodCUID2, $ZodULID, $ZodXID, $ZodKSUID, $ZodISODateTime, $ZodISODate, $ZodISOTime, $ZodISODuration, $ZodIPv4, $ZodIPv6, $ZodCIDRv4, $ZodCIDRv6, $ZodBase64, $ZodBase64URL, $ZodE164, $ZodJWT, $ZodNumber, $ZodNumberFormat, $ZodBoolean, $ZodNull, $ZodUnknown, $ZodNever, $ZodArray, $ZodObject, $ZodObjectJIT, $ZodUnion, $ZodDiscriminatedUnion, $ZodIntersection, $ZodTuple, $ZodRecord, $ZodEnum, $ZodLiteral, $ZodTransform, $ZodOptional, $ZodNullable, $ZodDefault, $ZodPrefault, $ZodNonOptional, $ZodCatch, $ZodPipe, $ZodReadonly, $ZodCustom;
1768
1774
  var init_schemas = __esm(() => {
1769
1775
  init_checks();
1770
1776
  init_core();
@@ -2510,6 +2516,74 @@ var init_schemas = __esm(() => {
2510
2516
  return handleIntersectionResults(payload, left, right);
2511
2517
  };
2512
2518
  });
2519
+ $ZodTuple = /* @__PURE__ */ $constructor("$ZodTuple", (inst, def) => {
2520
+ $ZodType.init(inst, def);
2521
+ const items = def.items;
2522
+ inst._zod.parse = (payload, ctx) => {
2523
+ const input = payload.value;
2524
+ if (!Array.isArray(input)) {
2525
+ payload.issues.push({
2526
+ input,
2527
+ inst,
2528
+ expected: "tuple",
2529
+ code: "invalid_type"
2530
+ });
2531
+ return payload;
2532
+ }
2533
+ payload.value = [];
2534
+ const proms = [];
2535
+ const reversedIndex = [...items].reverse().findIndex((item) => item._zod.optin !== "optional");
2536
+ const optStart = reversedIndex === -1 ? 0 : items.length - reversedIndex;
2537
+ if (!def.rest) {
2538
+ const tooBig = input.length > items.length;
2539
+ const tooSmall = input.length < optStart - 1;
2540
+ if (tooBig || tooSmall) {
2541
+ payload.issues.push({
2542
+ ...tooBig ? { code: "too_big", maximum: items.length } : { code: "too_small", minimum: items.length },
2543
+ input,
2544
+ inst,
2545
+ origin: "array"
2546
+ });
2547
+ return payload;
2548
+ }
2549
+ }
2550
+ let i = -1;
2551
+ for (const item of items) {
2552
+ i++;
2553
+ if (i >= input.length) {
2554
+ if (i >= optStart)
2555
+ continue;
2556
+ }
2557
+ const result = item._zod.run({
2558
+ value: input[i],
2559
+ issues: []
2560
+ }, ctx);
2561
+ if (result instanceof Promise) {
2562
+ proms.push(result.then((result) => handleTupleResult(result, payload, i)));
2563
+ } else {
2564
+ handleTupleResult(result, payload, i);
2565
+ }
2566
+ }
2567
+ if (def.rest) {
2568
+ const rest = input.slice(items.length);
2569
+ for (const el of rest) {
2570
+ i++;
2571
+ const result = def.rest._zod.run({
2572
+ value: el,
2573
+ issues: []
2574
+ }, ctx);
2575
+ if (result instanceof Promise) {
2576
+ proms.push(result.then((result) => handleTupleResult(result, payload, i)));
2577
+ } else {
2578
+ handleTupleResult(result, payload, i);
2579
+ }
2580
+ }
2581
+ }
2582
+ if (proms.length)
2583
+ return Promise.all(proms).then(() => payload);
2584
+ return payload;
2585
+ };
2586
+ });
2513
2587
  $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
2514
2588
  $ZodType.init(inst, def);
2515
2589
  inst._zod.parse = (payload, ctx) => {
@@ -3701,6 +3775,17 @@ function intersection(left, right) {
3701
3775
  right
3702
3776
  });
3703
3777
  }
3778
+ function tuple(items, _paramsOrRest, _params) {
3779
+ const hasRest = _paramsOrRest instanceof $ZodType;
3780
+ const params = hasRest ? _params : _paramsOrRest;
3781
+ const rest = hasRest ? _paramsOrRest : null;
3782
+ return new ZodTuple({
3783
+ type: "tuple",
3784
+ items,
3785
+ rest,
3786
+ ...normalizeParams(params)
3787
+ });
3788
+ }
3704
3789
  function record(keyType, valueType, params) {
3705
3790
  return new ZodRecord({
3706
3791
  type: "record",
@@ -3799,7 +3884,7 @@ function superRefine(fn) {
3799
3884
  function preprocess(fn, schema) {
3800
3885
  return pipe(transform(fn), schema);
3801
3886
  }
3802
- var ZodType, _ZodString, ZodString, ZodStringFormat, ZodEmail, ZodGUID, ZodUUID, ZodURL, ZodEmoji, ZodNanoID, ZodCUID, ZodCUID2, ZodULID, ZodXID, ZodKSUID, ZodIPv4, ZodIPv6, ZodCIDRv4, ZodCIDRv6, ZodBase64, ZodBase64URL, ZodE164, ZodJWT, ZodNumber, ZodNumberFormat, ZodBoolean, ZodNull, ZodUnknown, ZodNever, ZodArray, ZodObject, ZodUnion, ZodDiscriminatedUnion, ZodIntersection, ZodRecord, ZodEnum, ZodLiteral, ZodTransform, ZodOptional, ZodNullable, ZodDefault, ZodPrefault, ZodNonOptional, ZodCatch, ZodPipe, ZodReadonly, ZodCustom;
3887
+ var ZodType, _ZodString, ZodString, ZodStringFormat, ZodEmail, ZodGUID, ZodUUID, ZodURL, ZodEmoji, ZodNanoID, ZodCUID, ZodCUID2, ZodULID, ZodXID, ZodKSUID, ZodIPv4, ZodIPv6, ZodCIDRv4, ZodCIDRv6, ZodBase64, ZodBase64URL, ZodE164, ZodJWT, ZodNumber, ZodNumberFormat, ZodBoolean, ZodNull, ZodUnknown, ZodNever, ZodArray, ZodObject, ZodUnion, ZodDiscriminatedUnion, ZodIntersection, ZodTuple, ZodRecord, ZodEnum, ZodLiteral, ZodTransform, ZodOptional, ZodNullable, ZodDefault, ZodPrefault, ZodNonOptional, ZodCatch, ZodPipe, ZodReadonly, ZodCustom;
3803
3888
  var init_schemas2 = __esm(() => {
3804
3889
  init_core2();
3805
3890
  init_core2();
@@ -4103,6 +4188,14 @@ var init_schemas2 = __esm(() => {
4103
4188
  $ZodIntersection.init(inst, def);
4104
4189
  ZodType.init(inst, def);
4105
4190
  });
4191
+ ZodTuple = /* @__PURE__ */ $constructor("ZodTuple", (inst, def) => {
4192
+ $ZodTuple.init(inst, def);
4193
+ ZodType.init(inst, def);
4194
+ inst.rest = (rest) => inst.clone({
4195
+ ...inst._zod.def,
4196
+ rest
4197
+ });
4198
+ });
4106
4199
  ZodRecord = /* @__PURE__ */ $constructor("ZodRecord", (inst, def) => {
4107
4200
  $ZodRecord.init(inst, def);
4108
4201
  ZodType.init(inst, def);
@@ -5398,7 +5491,7 @@ var init_onepassword_config = __esm(() => {
5398
5491
  });
5399
5492
 
5400
5493
  // src/version.ts
5401
- var VERSION = "14.9.0";
5494
+ var VERSION = "14.10.0";
5402
5495
 
5403
5496
  // src/redact.ts
5404
5497
  function isPlaceholder(value) {
@@ -16042,7 +16135,7 @@ var init_minimax_model_dialect = __esm(() => {
16042
16135
  });
16043
16136
 
16044
16137
  // src/auth/accounts/allowance-report-schema.ts
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, switchSchema, blockedSchema, trajectorySchema, sessionSchema, allowanceReportSchema, ALLOWANCE_REPORT_SECTIONS, partialReport, allowanceStatusAnswerSchema;
16138
+ 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, switchSchema, blockedSchema, trajectorySchema, usageSchema, sessionSchema, allowanceReportSchema, ALLOWANCE_REPORT_SECTIONS, ALLOWANCE_REPORT_SECTIONS_V1_BASE, partialReport, allowanceStatusAnswerSchema;
16046
16139
  var init_allowance_report_schema = __esm(() => {
16047
16140
  init_zod();
16048
16141
  iso = exports_iso.datetime({ offset: true });
@@ -16128,6 +16221,18 @@ var init_allowance_report_schema = __esm(() => {
16128
16221
  }).optional(),
16129
16222
  spark48: string2().regex(/^[\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588\u2592\u2593\u250A ]*$/u)
16130
16223
  });
16224
+ usageSchema = object({
16225
+ start: iso,
16226
+ stepHours: literal(1),
16227
+ hours: literal(336),
16228
+ unit: literal("cu"),
16229
+ coveredFrom: iso.nullable(),
16230
+ accounts: array(object({
16231
+ accountId: string2(),
16232
+ cu: array(number2().int().min(0)).length(336)
16233
+ })),
16234
+ censored: array(tuple([number2().int().min(0).max(335), number2().int().min(1).max(60)]))
16235
+ });
16131
16236
  sessionSchema = object({
16132
16237
  session: union([string2().max(8), literal("unattributed")]),
16133
16238
  parent: union([string2().max(8), _null3()]).optional(),
@@ -16170,9 +16275,19 @@ var init_allowance_report_schema = __esm(() => {
16170
16275
  blockedNow: boolean2(),
16171
16276
  status: _enum(["ok", "insufficient-evidence"]),
16172
16277
  trajectory: trajectorySchema,
16173
- sessions: array(sessionSchema)
16278
+ sessions: array(sessionSchema),
16279
+ usage: usageSchema.optional()
16174
16280
  });
16175
16281
  ALLOWANCE_REPORT_SECTIONS = [
16282
+ "accounts",
16283
+ "alerts",
16284
+ "burn",
16285
+ "combined",
16286
+ "sessions",
16287
+ "trajectory",
16288
+ "usage"
16289
+ ];
16290
+ ALLOWANCE_REPORT_SECTIONS_V1_BASE = [
16176
16291
  "accounts",
16177
16292
  "alerts",
16178
16293
  "burn",
@@ -16186,7 +16301,8 @@ var init_allowance_report_schema = __esm(() => {
16186
16301
  burn: true,
16187
16302
  combined: true,
16188
16303
  sessions: true,
16189
- trajectory: true
16304
+ trajectory: true,
16305
+ usage: true
16190
16306
  });
16191
16307
  allowanceStatusAnswerSchema = object({
16192
16308
  contract_version: literal(1),
@@ -16197,7 +16313,7 @@ var init_allowance_report_schema = __esm(() => {
16197
16313
  }).superRefine((answer, ctx) => {
16198
16314
  if (answer.allowance === null)
16199
16315
  return;
16200
- const wanted = answer.sections ?? ALLOWANCE_REPORT_SECTIONS;
16316
+ const wanted = answer.sections ?? ALLOWANCE_REPORT_SECTIONS_V1_BASE;
16201
16317
  for (const section of wanted) {
16202
16318
  if (answer.allowance[section] === undefined) {
16203
16319
  ctx.addIssue({
@@ -16658,7 +16774,7 @@ function cuOf(tokens, cfg) {
16658
16774
  const uncached = Math.max(0, tokens.input - tokens.cacheRead - tokens.cacheWrite);
16659
16775
  return uncached + w.cacheRead * tokens.cacheRead + w.cacheWrite * tokens.cacheWrite + w.output * tokens.output;
16660
16776
  }
16661
- var PROVIDER_COST_PRIORS, MINUTE_MS2 = 60000, HOUR_MS2, DEFAULT_ALLOWANCE_CONFIG, LOOKAHEAD_TRAJ_PATHS = 40, MIN_PATH_SHARE = 0.1, SLOWER_FACTOR, PACE_MAX_MULT = 64, PACE_BISECT_STEPS = 4;
16777
+ var PROVIDER_COST_PRIORS, MINUTE_MS2 = 60000, HOUR_MS2, DEFAULT_ALLOWANCE_CONFIG, LOOKAHEAD_TRAJ_PATHS = 40, MIN_PATH_SHARE = 0.1, SLOWER_FACTOR, PACE_MAX_MULT = 64, PACE_BISECT_STEPS = 4, USAGE_HOURS = 336;
16662
16778
  var init_constants = __esm(() => {
16663
16779
  PROVIDER_COST_PRIORS = {
16664
16780
  "openai-codex": { cacheRead: 0.1, cacheWrite: 1, output: 4 },
@@ -17102,7 +17218,8 @@ function initialState() {
17102
17218
  sessions: {},
17103
17219
  seen: [],
17104
17220
  censorOpenAt: null,
17105
- lastAt: null
17221
+ lastAt: null,
17222
+ recordedFrom: null
17106
17223
  };
17107
17224
  }
17108
17225
  function observeAll(s, es, cfg) {
@@ -17146,6 +17263,7 @@ function fold(s, e, cfg, seen) {
17146
17263
  }
17147
17264
  switch (e.kind) {
17148
17265
  case "response": {
17266
+ record3(s, hourOf(e.at));
17149
17267
  const account = ensureAccount(s, e.account);
17150
17268
  const cu = cuOf(e.tokens, cfg);
17151
17269
  if (cu > 0) {
@@ -17166,6 +17284,7 @@ function fold(s, e, cfg, seen) {
17166
17284
  touch(s, e.at);
17167
17285
  return;
17168
17286
  case "no-eligible":
17287
+ record3(s, hourOf(e.at));
17169
17288
  if (e.until === undefined) {
17170
17289
  if (s.censorOpenAt === null)
17171
17290
  s.censorOpenAt = e.at;
@@ -17195,6 +17314,7 @@ function fold(s, e, cfg, seen) {
17195
17314
  return;
17196
17315
  }
17197
17316
  case "rollup": {
17317
+ record3(s, e.hour);
17198
17318
  const account = ensureAccount(s, e.account);
17199
17319
  const cu = cuOf(e.tokens, cfg);
17200
17320
  const mid = e.hour + HOUR_MS4 / 2;
@@ -17214,6 +17334,11 @@ function fold(s, e, cfg, seen) {
17214
17334
  }
17215
17335
  }
17216
17336
  }
17337
+ function record3(s, hour) {
17338
+ const from = s.recordedFrom ?? null;
17339
+ if (from === null || hour < from)
17340
+ s.recordedFrom = hour;
17341
+ }
17217
17342
  function touch(s, at) {
17218
17343
  if (s.lastAt === null || at > s.lastAt)
17219
17344
  s.lastAt = at;
@@ -20196,6 +20321,55 @@ var init_trajectory = __esm(() => {
20196
20321
  LEVELS = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
20197
20322
  });
20198
20323
 
20324
+ // src/auth/accounts/allowance/usage.ts
20325
+ function usageByHour(s, now, hours = USAGE_HOURS) {
20326
+ const cells = Math.max(1, Math.floor(hours));
20327
+ const start = hourOf(now) - (cells - 1) * HOUR_MS15;
20328
+ const end = start + cells * HOUR_MS15;
20329
+ const cellOf = (at) => Math.floor((at - start) / HOUR_MS15);
20330
+ const accounts = Object.values(s.accounts).map((a) => {
20331
+ const cu = new Array(cells).fill(0);
20332
+ for (const served of a.served) {
20333
+ if (served.at < start || served.at >= end)
20334
+ continue;
20335
+ const i = cellOf(served.at);
20336
+ cu[i] = (cu[i] ?? 0) + served.cu;
20337
+ }
20338
+ return { account: a.id, order: a.order, enabled: a.enabled, cu };
20339
+ });
20340
+ const censoredMinutes = new Array(cells).fill(0);
20341
+ for (const bucket of s.demand) {
20342
+ if (bucket.hour < start || bucket.hour >= end || bucket.censoredMinutes <= 0)
20343
+ continue;
20344
+ const i = cellOf(bucket.hour);
20345
+ censoredMinutes[i] = Math.min(60, (censoredMinutes[i] ?? 0) + bucket.censoredMinutes);
20346
+ }
20347
+ const open = s.censorOpenAt;
20348
+ if (open !== null) {
20349
+ const until = Math.min(now, end);
20350
+ for (let hour = Math.max(hourOf(open), start);hour < until; hour += HOUR_MS15) {
20351
+ const minutes = (Math.min(until, hour + HOUR_MS15) - Math.max(open, hour)) / MINUTE_MS11;
20352
+ if (minutes <= 0)
20353
+ continue;
20354
+ const i = cellOf(hour);
20355
+ censoredMinutes[i] = Math.min(60, (censoredMinutes[i] ?? 0) + minutes);
20356
+ }
20357
+ }
20358
+ const recorded = s.recordedFrom ?? s.demand[0]?.hour ?? null;
20359
+ return {
20360
+ start,
20361
+ hours: cells,
20362
+ accounts,
20363
+ censoredMinutes,
20364
+ coveredFrom: recorded === null ? null : Math.max(recorded, start)
20365
+ };
20366
+ }
20367
+ var MINUTE_MS11 = 60000, HOUR_MS15 = 3600000;
20368
+ var init_usage = __esm(() => {
20369
+ init_constants();
20370
+ init_state();
20371
+ });
20372
+
20199
20373
  // src/auth/accounts/paths.ts
20200
20374
  import { homedir as homedir10 } from "os";
20201
20375
  import { join as join10 } from "path";
@@ -20968,8 +21142,8 @@ function censoredMinutesByHour(rows, dayBegin, dayEnd, openAtStart = new Set) {
20968
21142
  hours = new Map;
20969
21143
  byProvider.set(provider, hours);
20970
21144
  }
20971
- for (let h = Math.floor(from / HOUR_MS15) * HOUR_MS15;h < to; h += HOUR_MS15) {
20972
- const overlap = Math.min(to, h + HOUR_MS15) - Math.max(from, h);
21145
+ for (let h = Math.floor(from / HOUR_MS16) * HOUR_MS16;h < to; h += HOUR_MS16) {
21146
+ const overlap = Math.min(to, h + HOUR_MS16) - Math.max(from, h);
20973
21147
  if (overlap > 0)
20974
21148
  hours.set(h, Math.min(60, (hours.get(h) ?? 0) + overlap / 60000));
20975
21149
  }
@@ -21011,7 +21185,7 @@ function* rollupDaySteps(day, rows, openAtStart) {
21011
21185
  for (const row of responses) {
21012
21186
  if (++n % COMPACT_SLICE_ROWS === 0)
21013
21187
  yield;
21014
- const hour = Math.floor(row.at / HOUR_MS15) * HOUR_MS15;
21188
+ const hour = Math.floor(row.at / HOUR_MS16) * HOUR_MS16;
21015
21189
  const key = [hour, row.provider, row.accountId, row.session ?? "-", row.model].join("\x00");
21016
21190
  const group = groups.get(key);
21017
21191
  if (group)
@@ -21077,7 +21251,7 @@ function* rollupDaySteps(day, rows, openAtStart) {
21077
21251
  `)}
21078
21252
  ` : "", openAtEnd };
21079
21253
  }
21080
- var LEDGER_QUEUE_MAX = 1024, RAW_DAY_MAX_BYTES, RAW_RETENTION_DAYS = 30, ROLLUP_RETENTION_DAYS = 180, COMPACT_LOCK_STALE_MS, COMPACT_DEFER_MS = 60000, COMPACT_RETRY_MS = 60000, COMPACT_FAILED_RETRY_MS, COMPACT_SLICE_ROWS = 2000, DAY_MS5 = 86400000, HOUR_MS15 = 3600000, DAY_FILE2, queue, dropped = 0, draining = null, scheduled = false, fullDays, compactedDay = null, compactTimer = null, idleProbe = null;
21254
+ var LEDGER_QUEUE_MAX = 1024, RAW_DAY_MAX_BYTES, RAW_RETENTION_DAYS = 30, ROLLUP_RETENTION_DAYS = 180, COMPACT_LOCK_STALE_MS, COMPACT_DEFER_MS = 60000, COMPACT_RETRY_MS = 60000, COMPACT_FAILED_RETRY_MS, COMPACT_SLICE_ROWS = 2000, DAY_MS5 = 86400000, HOUR_MS16 = 3600000, DAY_FILE2, queue, dropped = 0, draining = null, scheduled = false, fullDays, compactedDay = null, compactTimer = null, idleProbe = null;
21081
21255
  var init_ledger_writer = __esm(() => {
21082
21256
  init_logger();
21083
21257
  init_atomic_write();
@@ -22109,8 +22283,8 @@ function vendorLabel(provider) {
22109
22283
  }
22110
22284
  function localCalendar(now) {
22111
22285
  const offsets = [];
22112
- const from = Math.floor((now - LOAD_HISTORY_MS) / HOUR_MS16) * HOUR_MS16;
22113
- for (let t = from;t <= now + CALENDAR_FUTURE_MS; t += HOUR_MS16) {
22286
+ const from = Math.floor((now - LOAD_HISTORY_MS) / HOUR_MS17) * HOUR_MS17;
22287
+ for (let t = from;t <= now + CALENDAR_FUTURE_MS; t += HOUR_MS17) {
22114
22288
  const offsetMinutes = -new Date(t).getTimezoneOffset();
22115
22289
  if (offsets.length === 0 || offsets[offsets.length - 1]?.offsetMinutes !== offsetMinutes) {
22116
22290
  offsets.push({ from: offsets.length === 0 ? 0 : t, offsetMinutes });
@@ -22142,7 +22316,7 @@ function allowanceNotice(provider, transition, alert, accounts, formatTime = for
22142
22316
  return null;
22143
22317
  const { start, end } = gapSpan(alert);
22144
22318
  const who = accounts <= 1 ? "the account is" : accounts === 2 ? "both accounts are" : `all ${accounts} accounts are`;
22145
- const hours = Math.round((end - start) / HOUR_MS16);
22319
+ const hours = Math.round((end - start) / HOUR_MS17);
22146
22320
  const remedy = remedyText(alert.remedy, start, formatTime);
22147
22321
  const advice = remedy ? ` ${remedy.charAt(0).toUpperCase()}${remedy.slice(1)}.` : "";
22148
22322
  return `${vendor} allowance: at the current rate ${who} spent ${formatTime(start)} and blocked until ${formatTime(end)} (${hours} h).${advice} \`claudish account forecast\``;
@@ -22498,7 +22672,7 @@ class AllowanceService {
22498
22672
  const now = this.clock();
22499
22673
  const tail = createLedgerTail({ startAtEnd: true, now, env: this.env });
22500
22674
  await tail.tail();
22501
- const handoff = Math.floor((now - LOAD_RAW_MS) / HOUR_MS16) * HOUR_MS16;
22675
+ const handoff = Math.floor((now - LOAD_RAW_MS) / HOUR_MS17) * HOUR_MS17;
22502
22676
  const [older, recent] = await Promise.all([
22503
22677
  readLedger({ sinceMs: now - LOAD_HISTORY_MS, untilMs: handoff, env: this.env }),
22504
22678
  readLedger({ sinceMs: handoff, source: "raw", env: this.env })
@@ -22580,7 +22754,7 @@ class AllowanceService {
22580
22754
  if (!m.state)
22581
22755
  return;
22582
22756
  const now = this.clock();
22583
- const horizon = m.cfg.SIM_MAX_STEPS * HOUR_MS16;
22757
+ const horizon = m.cfg.SIM_MAX_STEPS * HOUR_MS17;
22584
22758
  if (m.fc && m.fc.key === forecastKey(m.state, now, horizon, m.cal, m.cfg))
22585
22759
  return;
22586
22760
  m.fc = await runSliced(forecastSliced(m.state, now, horizon, m.cal, m.cfg));
@@ -22809,6 +22983,9 @@ async function runSliced(gen) {
22809
22983
  await new Promise((resolve) => setImmediate(resolve));
22810
22984
  }
22811
22985
  }
22986
+ function toSignificant(x, digits) {
22987
+ return x === 0 ? 0 : Number(x.toPrecision(digits));
22988
+ }
22812
22989
  function poolRemaining(views) {
22813
22990
  const read = views.filter(isRead);
22814
22991
  if (read.length === 0)
@@ -22836,7 +23013,7 @@ function likelihoodView(l) {
22836
23013
  let last = p.length - 1;
22837
23014
  while (last > first && p[last] === 0)
22838
23015
  last--;
22839
- return { start: iso2(l.start + first * HOUR_MS16), stepHours: 1, p: p.slice(first, last + 1) };
23016
+ return { start: iso2(l.start + first * HOUR_MS17), stepHours: 1, p: p.slice(first, last + 1) };
22840
23017
  }
22841
23018
  function switchView(sw) {
22842
23019
  return {
@@ -22857,9 +23034,37 @@ function sustainableOf(top, pace, burnLong) {
22857
23034
  return top.remedy.kind === "cut" ? top.remedy.cutToFraction * long : 0;
22858
23035
  return pace ? paceInCuPerHour(pace, long) : null;
22859
23036
  }
23037
+ function usageView(s, now, views) {
23038
+ const grid = usageByHour(s, now, USAGE_HOURS);
23039
+ const cell = (cu) => cu > 0 ? Math.max(1, Math.round(cu)) : 0;
23040
+ const byId = new Map(grid.accounts.map((a) => [a.account, a]));
23041
+ const listed = new Set(views.map((v) => v.account));
23042
+ const others = grid.accounts.filter((a) => !listed.has(a.account) && a.cu.some((cu) => cu > 0)).sort((a, b) => a.order - b.order || (a.account < b.account ? -1 : 1));
23043
+ const censored = [];
23044
+ grid.censoredMinutes.forEach((minutes, i) => {
23045
+ const m = Math.min(60, Math.round(minutes));
23046
+ if (m > 0)
23047
+ censored.push([i, m]);
23048
+ });
23049
+ return {
23050
+ start: iso2(grid.start),
23051
+ stepHours: 1,
23052
+ hours: USAGE_HOURS,
23053
+ unit: "cu",
23054
+ coveredFrom: grid.coveredFrom === null ? null : iso2(grid.coveredFrom),
23055
+ accounts: [
23056
+ ...views.map((v) => ({
23057
+ accountId: v.account,
23058
+ cu: (byId.get(v.account)?.cu ?? new Array(grid.hours).fill(0)).map(cell)
23059
+ })),
23060
+ ...others.map((a) => ({ accountId: a.account, cu: a.cu.map(cell) }))
23061
+ ],
23062
+ censored
23063
+ };
23064
+ }
22860
23065
  function buildReport(input) {
22861
23066
  const { s, now, cfg, cal, fc } = input;
22862
- const horizonMs = Math.min(168 * HOUR_MS16, Math.max(HOUR_MS16, input.horizonMs));
23067
+ const horizonMs = Math.min(168 * HOUR_MS17, Math.max(HOUR_MS17, input.horizonMs));
22863
23068
  const evidence = evidenceOf(s, now);
22864
23069
  const none = evidence === "none";
22865
23070
  const views = accountViews(s, now, cfg);
@@ -22883,7 +23088,7 @@ function buildReport(input) {
22883
23088
  evidence,
22884
23089
  unit,
22885
23090
  assumption: unit === "account-fraction" ? "equal-capacity" : null,
22886
- horizonHours: Math.round(horizonMs / HOUR_MS16),
23091
+ horizonHours: Math.round(horizonMs / HOUR_MS17),
22887
23092
  accounts: views.map((v) => {
22888
23093
  const g = v.governing === null ? undefined : v.beta[v.governing];
22889
23094
  const k = v.binding === null || v.binding === v.governing ? undefined : v.kappa[v.binding];
@@ -22940,7 +23145,7 @@ function buildReport(input) {
22940
23145
  gapStartEpoch: start,
22941
23146
  gapStart: a.gap.start ? q(a.gap.start, iso2) : null,
22942
23147
  gapEnd: a.gap.end ? q(a.gap.end, iso2) : null,
22943
- gapHours: a.gap.lengthMs ? q(a.gap.lengthMs, (ms) => ms / HOUR_MS16) : null,
23148
+ gapHours: a.gap.lengthMs ? q(a.gap.lengthMs, (ms) => ms / HOUR_MS17) : null,
22944
23149
  pGap: Math.min(1, Math.max(0, a.pGap)),
22945
23150
  binding: a.gap.atLongRate.binding.map((b) => ({
22946
23151
  account: label(b.account),
@@ -22950,7 +23155,7 @@ function buildReport(input) {
22950
23155
  kind: r.kind,
22951
23156
  cutToPercent: r.kind === "cut" ? Math.round(r.cutToFraction * 100) : null,
22952
23157
  until: r.kind === "not-addressable" ? null : iso2(r.until),
22953
- pauseHours: r.kind === "not-addressable" || r.pauseMs === null ? null : r.pauseMs / HOUR_MS16,
23158
+ pauseHours: r.kind === "not-addressable" || r.pauseMs === null ? null : r.pauseMs / HOUR_MS17,
22954
23159
  reason: r.kind === "not-addressable" ? r.reason : null,
22955
23160
  text: remedyText(r, start, fmt)
22956
23161
  }
@@ -22966,9 +23171,9 @@ function buildReport(input) {
22966
23171
  })),
22967
23172
  forecast: traj.forecast.map((f) => ({
22968
23173
  at: iso2(f.at),
22969
- p10: f.q.p10,
22970
- p50: f.q.p50,
22971
- p90: f.q.p90,
23174
+ p10: toSignificant(f.q.p10, FORECAST_DIGITS),
23175
+ p50: toSignificant(f.q.p50, FORECAST_DIGITS),
23176
+ p90: toSignificant(f.q.p90, FORECAST_DIGITS),
22972
23177
  pBlocked: f.pBlocked
22973
23178
  })),
22974
23179
  resets: traj.resets.map((r) => ({
@@ -22980,7 +23185,7 @@ function buildReport(input) {
22980
23185
  blocked: traj.blocked.map(blockedView),
22981
23186
  historyByAccount: none ? [] : views.filter((v) => traj.historyByAccount[v.account] !== undefined).map((v) => ({
22982
23187
  accountId: v.account,
22983
- 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 }))
23188
+ history: (traj.historyByAccount[v.account] ?? []).filter((h) => h.at >= now - 24 * HOUR_MS17).map((h) => ({ at: iso2(h.at), remaining: h.remaining, provenance: h.provenance }))
22984
23189
  })),
22985
23190
  switches: traj.switches.map(switchView),
22986
23191
  slower: {
@@ -22990,8 +23195,8 @@ function buildReport(input) {
22990
23195
  },
22991
23196
  spark48: sparkline({
22992
23197
  ...traj,
22993
- history: traj.history.filter((h) => h.at >= now - 24 * HOUR_MS16),
22994
- forecast: traj.forecast.filter((f) => f.at <= now + 24 * HOUR_MS16)
23198
+ history: traj.history.filter((h) => h.at >= now - 24 * HOUR_MS17),
23199
+ forecast: traj.forecast.filter((f) => f.at <= now + 24 * HOUR_MS17)
22995
23200
  }, 48)
22996
23201
  },
22997
23202
  sessions: none ? [] : contributions(s, { now, window: "binding" }, cfg).map((c) => ({
@@ -23012,7 +23217,8 @@ function buildReport(input) {
23012
23217
  exactness: c.exactness,
23013
23218
  firstAt: iso2(c.firstAt),
23014
23219
  lastAt: iso2(c.lastAt)
23015
- }))
23220
+ })),
23221
+ usage: usageView(s, now, views)
23016
23222
  };
23017
23223
  return allowanceReportSchema.parse(report);
23018
23224
  }
@@ -23064,7 +23270,7 @@ function getAllowanceService() {
23064
23270
  }
23065
23271
  return instance;
23066
23272
  }
23067
- 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;
23273
+ var THREAD_MAP_MAX = 512, THREAD_IDLE_MS, HOUR_MS17 = 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(), FORECAST_DIGITS = 4, instance, safeRankerWarned = false;
23068
23274
  var init_allowance_service = __esm(() => {
23069
23275
  init_config();
23070
23276
  init_logger();
@@ -23083,6 +23289,7 @@ var init_allowance_service = __esm(() => {
23083
23289
  init_state();
23084
23290
  init_strategy();
23085
23291
  init_trajectory();
23292
+ init_usage();
23086
23293
  init_atomic_write();
23087
23294
  init_file_lock();
23088
23295
  init_ledger_reader();
@@ -23092,12 +23299,12 @@ var init_allowance_service = __esm(() => {
23092
23299
  init_registry();
23093
23300
  init_state_store();
23094
23301
  THREAD_IDLE_MS = 24 * 60 * 60 * 1000;
23095
- DAY_MS6 = 24 * HOUR_MS16;
23302
+ DAY_MS6 = 24 * HOUR_MS17;
23096
23303
  LOAD_HISTORY_MS = 42 * DAY_MS6;
23097
- LOAD_RAW_MS = 48 * HOUR_MS16;
23304
+ LOAD_RAW_MS = 48 * HOUR_MS17;
23098
23305
  SERVED_RECENTLY_MS = DAY_MS6;
23099
23306
  IN_FLIGHT_MAX_MS = 30 * 60000;
23100
- LOAD_RETRY_MAX_MS = HOUR_MS16;
23307
+ LOAD_RETRY_MAX_MS = HOUR_MS17;
23101
23308
  CALENDAR_FUTURE_MS = 9 * DAY_MS6;
23102
23309
  });
23103
23310
 
@@ -92651,22 +92858,22 @@ function dialogKindOf(reason) {
92651
92858
  function str3(v) {
92652
92859
  return typeof v === "string" && v !== "" ? v : null;
92653
92860
  }
92654
- function record4(v) {
92861
+ function record5(v) {
92655
92862
  return v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
92656
92863
  }
92657
92864
  function paneZero(listResult) {
92658
- const panes = record4(listResult)?.panes;
92865
+ const panes = record5(listResult)?.panes;
92659
92866
  if (!Array.isArray(panes))
92660
92867
  return null;
92661
92868
  for (const p of panes) {
92662
- const row = record4(p);
92869
+ const row = record5(p);
92663
92870
  if (row?.pane === 0)
92664
92871
  return row;
92665
92872
  }
92666
92873
  return null;
92667
92874
  }
92668
92875
  function parseGateInput(listResult, now) {
92669
- const input = record4(paneZero(listResult)?.input);
92876
+ const input = record5(paneZero(listResult)?.input);
92670
92877
  if (!input || typeof input.ready !== "boolean")
92671
92878
  return null;
92672
92879
  const reason = input.ready ? null : str3(input.reason);
@@ -92781,7 +92988,7 @@ function notAcceptedOutcome(r) {
92781
92988
  }
92782
92989
  function classifySendReply(r) {
92783
92990
  if (r.ok)
92784
- return okOutcome(record4(r.result) ?? {});
92991
+ return okOutcome(record5(r.result) ?? {});
92785
92992
  switch (r.code) {
92786
92993
  case "busy": {
92787
92994
  const reason = r.reason ?? "";
@@ -102163,7 +102370,7 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
102163
102370
  });
102164
102371
  tools.push({
102165
102372
  name: "allowance_status",
102166
- 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.',
102373
+ 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, 14 days of hourly usage per account), or null with a `reason` when there is no account pool. `sections` limits the report to those parts (accounts, combined, burn, alerts, trajectory, sessions, usage) 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.',
102167
102374
  inputSchema: {
102168
102375
  type: "object",
102169
102376
  properties: {
@@ -102183,7 +102390,7 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
102183
102390
  enum: [...ALLOWANCE_REPORT_SECTIONS]
102184
102391
  },
102185
102392
  minItems: 1,
102186
- description: 'Report only these parts beside the header (default: all). Poll with ["accounts","burn","alerts"]; the trajectory and sessions are most of the bytes.'
102393
+ description: 'Report only these parts beside the header (default: all). Poll with ["accounts","burn","alerts"]; the trajectory, sessions and usage are most of the bytes.'
102187
102394
  }
102188
102395
  }
102189
102396
  },
@@ -104667,7 +104874,7 @@ function graphLines(r, paint) {
104667
104874
  if (points.length === 0)
104668
104875
  return [];
104669
104876
  const start = points[0]?.at ?? 0;
104670
- const span = Math.max(HOUR_MS17, (points.at(-1)?.at ?? start) - start + HOUR_MS17);
104877
+ const span = Math.max(HOUR_MS18, (points.at(-1)?.at ?? start) - start + HOUR_MS18);
104671
104878
  const col = (ms) => Math.min(GRAPH_COLS - 1, Math.max(0, Math.floor((ms - start) / span * GRAPH_COLS)));
104672
104879
  const scale = r.unit === "account-fraction" ? Math.max(1, r.accounts.length) : Math.max(...points.map((p) => Math.max(p.v, p.hi ?? 0)), 0);
104673
104880
  const cols = Array.from({ length: GRAPH_COLS }, () => ({
@@ -104774,7 +104981,7 @@ function renderForecast(r, paint = PLAIN) {
104774
104981
  ...sessionLines(r, paint)
104775
104982
  ];
104776
104983
  }
104777
- var PLAIN, HOUR_MS17 = 3600000, GRAPH_COLS = 60, GRAPH_ROWS = 10, SESSIONS_SHOWN = 10, SHORT_WINDOW_MAX_MIN = 360, DAYS, NO_USAGE_LINE = "No usage observed yet \u2014 the forecast starts after the first pooled response.";
104984
+ var PLAIN, HOUR_MS18 = 3600000, GRAPH_COLS = 60, GRAPH_ROWS = 10, SESSIONS_SHOWN = 10, SHORT_WINDOW_MAX_MIN = 360, DAYS, NO_USAGE_LINE = "No usage observed yet \u2014 the forecast starts after the first pooled response.";
104778
104985
  var init_account_forecast_render = __esm(() => {
104779
104986
  init_constants();
104780
104987
  PLAIN = {
@@ -105025,8 +105232,8 @@ function parseForecastHorizon(value) {
105025
105232
  const m = /^(\d+(?:\.\d+)?)([hd])$/.exec(value.trim());
105026
105233
  if (!m)
105027
105234
  return null;
105028
- const ms = Number(m[1]) * (m[2] === "d" ? 24 : 1) * HOUR_MS18;
105029
- return ms >= HOUR_MS18 && ms <= 168 * HOUR_MS18 ? ms : null;
105235
+ const ms = Number(m[1]) * (m[2] === "d" ? 24 : 1) * HOUR_MS19;
105236
+ return ms >= HOUR_MS19 && ms <= 168 * HOUR_MS19 ? ms : null;
105030
105237
  }
105031
105238
  function forecastProvider(value) {
105032
105239
  if (value === "codex" || value === "openai-codex")
@@ -105039,7 +105246,7 @@ function forecastProvider(value) {
105039
105246
  return `account forecast: unknown provider "${value}"; the only provider with an allowance model is codex.`;
105040
105247
  }
105041
105248
  async function forecastCommand(args) {
105042
- let horizonMs = 168 * HOUR_MS18;
105249
+ let horizonMs = 168 * HOUR_MS19;
105043
105250
  let json = false;
105044
105251
  let provider = "openai-codex";
105045
105252
  if (args.includes("--help") || args.includes("-h")) {
@@ -105130,7 +105337,7 @@ Removing the legacy login (default, legacy-codex-file) requires --delete-file.
105130
105337
  New accounts use the macOS Keychain, or protected plaintext files where there is none.
105131
105338
  Add only accounts you own; vendor terms may restrict switching between subscriptions.
105132
105339
  With Codex accounts configured, use account add/login instead of \`claudish login codex\`.
105133
- Claude account mutations, import and use are not available. No token input or export.`, MUTATION_VERBS, HOUR_MS18 = 3600000, FORECAST_HELP = `Usage:
105340
+ Claude account mutations, import and use are not available. No token input or export.`, MUTATION_VERBS, HOUR_MS19 = 3600000, FORECAST_HELP = `Usage:
105134
105341
  claudish account forecast [--json] [--horizon <h|d>] [--provider codex]
105135
105342
 
105136
105343
  The allowance trajectory, alerts and sessions of a claudish provider's account pool.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudish",
3
- "version": "14.9.0",
3
+ "version": "14.10.0",
4
4
  "description": "Run Claude Code with any model - OpenRouter, Ollama, LM Studio & local models",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -71,10 +71,10 @@
71
71
  "ai"
72
72
  ],
73
73
  "optionalDependencies": {
74
- "@claudish/magmux-darwin-arm64": "14.9.0",
75
- "@claudish/magmux-darwin-x64": "14.9.0",
76
- "@claudish/magmux-linux-arm64": "14.9.0",
77
- "@claudish/magmux-linux-x64": "14.9.0"
74
+ "@claudish/magmux-darwin-arm64": "14.10.0",
75
+ "@claudish/magmux-darwin-x64": "14.10.0",
76
+ "@claudish/magmux-linux-arm64": "14.10.0",
77
+ "@claudish/magmux-linux-x64": "14.10.0"
78
78
  },
79
79
  "author": "Jack Rudenko <i@madappgang.com>",
80
80
  "license": "MIT",