claudish 14.6.0 → 14.8.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 +476 -71
  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",
@@ -5394,7 +5398,7 @@ var init_onepassword_config = __esm(() => {
5394
5398
  });
5395
5399
 
5396
5400
  // src/version.ts
5397
- var VERSION = "14.6.0";
5401
+ var VERSION = "14.8.0";
5398
5402
 
5399
5403
  // src/redact.ts
5400
5404
  function isPlaceholder(value) {
@@ -16038,7 +16042,7 @@ var init_minimax_model_dialect = __esm(() => {
16038
16042
  });
16039
16043
 
16040
16044
  // src/auth/accounts/allowance-report-schema.ts
16041
- 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;
16042
16046
  var init_allowance_report_schema = __esm(() => {
16043
16047
  init_zod();
16044
16048
  iso = exports_iso.datetime({ offset: true });
@@ -16068,7 +16072,10 @@ var init_allowance_report_schema = __esm(() => {
16068
16072
  semantics
16069
16073
  })),
16070
16074
  lambda: union([literal(0), literal(1)]).nullable(),
16071
- 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()
16072
16079
  });
16073
16080
  remedySchema = object({
16074
16081
  kind: _enum(["cut", "pause", "not-addressable"]),
@@ -16089,19 +16096,34 @@ var init_allowance_report_schema = __esm(() => {
16089
16096
  binding: array(object({ account: string2(), window: string2() })),
16090
16097
  remedy: remedySchema
16091
16098
  });
16099
+ historyPoint = object({
16100
+ at: iso,
16101
+ remaining: finite,
16102
+ provenance: _enum(["observed", "reset-inferred"])
16103
+ });
16092
16104
  trajectorySchema = object({
16093
- history: array(object({
16094
- at: iso,
16095
- remaining: finite,
16096
- provenance: _enum(["observed", "reset-inferred"])
16097
- })),
16105
+ history: array(historyPoint),
16106
+ historyByAccount: array(object({ accountId: string2(), history: array(historyPoint) })).optional(),
16098
16107
  forecast: array(object({ at: iso, p10: finite, p50: finite, p90: finite, pBlocked: fraction })),
16099
16108
  resets: array(object({ at: iso, account: string2(), window: string2(), projected: boolean2() })),
16100
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
+ likelihood: object({
16117
+ start: iso,
16118
+ stepHours: literal(1),
16119
+ p: array(number2().min(0).max(1)).min(1)
16120
+ }).optional()
16121
+ })).optional(),
16101
16122
  spark48: string2().regex(/^[\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588\u2592\u2593\u250A ]*$/u)
16102
16123
  });
16103
16124
  sessionSchema = object({
16104
16125
  session: union([string2().max(8), literal("unattributed")]),
16126
+ parent: union([string2().max(8), _null3()]).optional(),
16105
16127
  account: string2(),
16106
16128
  window: string2(),
16107
16129
  share: fraction,
@@ -16143,11 +16165,41 @@ var init_allowance_report_schema = __esm(() => {
16143
16165
  trajectory: trajectorySchema,
16144
16166
  sessions: array(sessionSchema)
16145
16167
  });
16168
+ ALLOWANCE_REPORT_SECTIONS = [
16169
+ "accounts",
16170
+ "alerts",
16171
+ "burn",
16172
+ "combined",
16173
+ "sessions",
16174
+ "trajectory"
16175
+ ];
16176
+ partialReport = allowanceReportSchema.partial({
16177
+ accounts: true,
16178
+ alerts: true,
16179
+ burn: true,
16180
+ combined: true,
16181
+ sessions: true,
16182
+ trajectory: true
16183
+ });
16146
16184
  allowanceStatusAnswerSchema = object({
16147
16185
  contract_version: literal(1),
16148
16186
  capabilities: array(string2()),
16149
- allowance: allowanceReportSchema.nullable(),
16187
+ sections: array(_enum(ALLOWANCE_REPORT_SECTIONS)).min(1).optional(),
16188
+ allowance: partialReport.nullable(),
16150
16189
  reason: string2().optional()
16190
+ }).superRefine((answer, ctx) => {
16191
+ if (answer.allowance === null)
16192
+ return;
16193
+ const wanted = answer.sections ?? ALLOWANCE_REPORT_SECTIONS;
16194
+ for (const section of wanted) {
16195
+ if (answer.allowance[section] === undefined) {
16196
+ ctx.addIssue({
16197
+ code: "custom",
16198
+ path: ["allowance", section],
16199
+ message: `section ${section} is missing`
16200
+ });
16201
+ }
16202
+ }
16151
16203
  });
16152
16204
  });
16153
16205
 
@@ -16599,7 +16651,7 @@ function cuOf(tokens, cfg) {
16599
16651
  const uncached = Math.max(0, tokens.input - tokens.cacheRead - tokens.cacheWrite);
16600
16652
  return uncached + w.cacheRead * tokens.cacheRead + w.cacheWrite * tokens.cacheWrite + w.output * tokens.output;
16601
16653
  }
16602
- var PROVIDER_COST_PRIORS, MINUTE_MS2 = 60000, HOUR_MS2, DEFAULT_ALLOWANCE_CONFIG, LOOKAHEAD_TRAJ_PATHS = 40;
16654
+ 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;
16603
16655
  var init_constants = __esm(() => {
16604
16656
  PROVIDER_COST_PRIORS = {
16605
16657
  "openai-codex": { cacheRead: 0.1, cacheWrite: 1, output: 4 },
@@ -17092,7 +17144,7 @@ function fold(s, e, cfg, seen) {
17092
17144
  insertServed(account, e.at, cu);
17093
17145
  addDemand(s, hourOf(e.at), cu, 1);
17094
17146
  if (e.session !== null)
17095
- addSession(s, e.session, e.at, e.account, cu, e.tokens, e.model, 1);
17147
+ addSession(s, e.session, e.at, e.account, cu, e.tokens, e.model, 1, e.parent ?? null);
17096
17148
  }
17097
17149
  if (e.outcome === "ok")
17098
17150
  closeCensor(s, e.at);
@@ -17141,13 +17193,13 @@ function fold(s, e, cfg, seen) {
17141
17193
  if (cu > 0) {
17142
17194
  insertServed(account, mid, cu);
17143
17195
  if (e.session !== null)
17144
- addSession(s, e.session, mid, e.account, cu, e.tokens, e.model, e.n);
17196
+ addSession(s, e.session, mid, e.account, cu, e.tokens, e.model, e.n, e.parent ?? null);
17145
17197
  }
17146
17198
  const bucket = addDemand(s, e.hour, cu, e.n);
17147
17199
  bucket.censoredMinutes = Math.min(60, Math.max(bucket.censoredMinutes, e.censoredMinutes));
17148
- if (e.first !== null)
17200
+ if (e.first !== null && e.n > 1)
17149
17201
  applySnapshot(account, e.hour, e.first, cfg);
17150
- if (e.last !== null && e.n > 1)
17202
+ if (e.last !== null)
17151
17203
  applySnapshot(account, e.hour + HOUR_MS4 - 1, e.last, cfg);
17152
17204
  touch(s, e.hour + HOUR_MS4 - 1);
17153
17205
  return;
@@ -17225,7 +17277,7 @@ function sealCensor(s, from, to) {
17225
17277
  bucket.censoredMinutes = Math.min(60, Math.max(bucket.censoredMinutes, overlap / MINUTE_MS4));
17226
17278
  }
17227
17279
  }
17228
- function addSession(s, session, at, account, cu, tokens, model, n) {
17280
+ function addSession(s, session, at, account, cu, tokens, model, n, parent) {
17229
17281
  let track = s.sessions[session];
17230
17282
  if (!track) {
17231
17283
  track = {
@@ -17250,6 +17302,8 @@ function addSession(s, session, at, account, cu, tokens, model, n) {
17250
17302
  track.tokens.cacheWrite += tokens.cacheWrite;
17251
17303
  track.tokens.output += tokens.output;
17252
17304
  track.models[model] = (track.models[model] ?? 0) + n;
17305
+ if (parent !== null && (track.parent ?? null) === null)
17306
+ track.parent = parent;
17253
17307
  }
17254
17308
  function applySnapshot(account, at, readings, cfg) {
17255
17309
  const current = account.lastCompleteAt === null || at >= account.lastCompleteAt;
@@ -19221,18 +19275,88 @@ function pauseGrid(horizonMs) {
19221
19275
  out.push(hours * HOUR_MS11);
19222
19276
  return out.filter((p) => p <= Math.max(horizonMs, HOUR_MS11));
19223
19277
  }
19224
- function* remedyScan(ctx, rateLong, cfg) {
19225
- const zero = ctx.factors.map(() => 0);
19226
- const externalStarts = gapsOn(ctx, zero, cfg).filter((g) => g.kind === "7d" && g.lengthMs >= REMEDY_MIN_GAP_MS).map((g) => g.start);
19227
- yield;
19278
+ function* cutScan(ctx, rate, cfg) {
19228
19279
  let cut = null;
19229
19280
  for (const phi of CUT_GRID) {
19230
- const gs = gapsOn(ctx, ctx.factors.map((f) => phi * rateLong * f), cfg);
19281
+ const gs = gapsOn(ctx, ctx.factors.map((f) => phi * rate * f), cfg);
19231
19282
  yield;
19232
19283
  if (hasGap(gs))
19233
19284
  break;
19234
19285
  cut = phi;
19235
19286
  }
19287
+ return cut;
19288
+ }
19289
+ function refillThroughput(ctx) {
19290
+ let total = 0;
19291
+ for (const v of ctx.views.filter(isRead)) {
19292
+ const g = v.windows.find((w) => w.key === v.governing);
19293
+ if (!g || g.lengthMinutes === null || g.lengthMinutes <= 0)
19294
+ continue;
19295
+ const beta = ctx.unit === "cu" ? v.beta[g.key]?.value : undefined;
19296
+ const capacity = beta !== undefined && beta > 0 ? 1 / beta : 1;
19297
+ total += capacity / (g.lengthMinutes / 60);
19298
+ }
19299
+ return total;
19300
+ }
19301
+ function* paceScan(ctx, ev, rates, cfg) {
19302
+ const rhoL = Number.isFinite(rates.long) && rates.long > 0 ? rates.long : 0;
19303
+ if (rhoL > 0 && hasGap(ev.long)) {
19304
+ const cut = yield* cutScan(ctx, rhoL, cfg);
19305
+ return { rate: cut === null ? 0 : cut * rhoL, basis: "cut-scan" };
19306
+ }
19307
+ const anchor = Math.max(rhoL, refillThroughput(ctx));
19308
+ if (!(anchor > 0) || !Number.isFinite(anchor))
19309
+ return null;
19310
+ const clean = (rho) => !hasGap(gapsOn(ctx, ctx.factors.map((f) => rho * f), cfg));
19311
+ if (anchor > rhoL) {
19312
+ const ok = clean(anchor);
19313
+ yield;
19314
+ if (!ok)
19315
+ return { rate: yield* bisect(rhoL, anchor, clean), basis: "bisect" };
19316
+ }
19317
+ const up = yield* bracketUp(anchor, clean);
19318
+ if (up.hi === null)
19319
+ return { rate: up.lo, basis: "capped" };
19320
+ return { rate: yield* bisect(up.lo, up.hi, clean), basis: "bracket" };
19321
+ }
19322
+ function* bracketUp(anchor, clean) {
19323
+ let lo = anchor;
19324
+ for (let mult = 2;mult <= PACE_MAX_MULT; mult *= 2) {
19325
+ const ok = clean(mult * anchor);
19326
+ yield;
19327
+ if (!ok)
19328
+ return { lo, hi: mult * anchor };
19329
+ lo = mult * anchor;
19330
+ }
19331
+ return { lo, hi: null };
19332
+ }
19333
+ function* bisect(lo, hi, clean) {
19334
+ let low = lo;
19335
+ let high = hi;
19336
+ for (let i = 0;i < PACE_BISECT_STEPS; i++) {
19337
+ const mid = (low + high) / 2;
19338
+ const ok = clean(mid);
19339
+ yield;
19340
+ if (ok)
19341
+ low = mid;
19342
+ else
19343
+ high = mid;
19344
+ }
19345
+ return low;
19346
+ }
19347
+ function paceInCuPerHour(pace, burnLong) {
19348
+ if (pace.phi !== null && Number.isFinite(burnLong) && burnLong > 0) {
19349
+ return Math.max(0, pace.phi * burnLong);
19350
+ }
19351
+ if (pace.unit === "cu")
19352
+ return Math.max(0, pace.rate);
19353
+ return null;
19354
+ }
19355
+ function* remedyScan(ctx, rateLong, cfg) {
19356
+ const zero = ctx.factors.map(() => 0);
19357
+ const externalStarts = gapsOn(ctx, zero, cfg).filter((g) => g.kind === "7d" && g.lengthMs >= REMEDY_MIN_GAP_MS).map((g) => g.start);
19358
+ yield;
19359
+ const cut = yield* cutScan(ctx, rateLong, cfg);
19236
19360
  let pauseMs = null;
19237
19361
  const h0 = hourStart(ctx.now);
19238
19362
  for (const p of pauseGrid(ctx.horizonMs)) {
@@ -19273,6 +19397,25 @@ function remedyText(r, gapStart, formatTime) {
19273
19397
  function rateBin(n) {
19274
19398
  return Number.isFinite(n) && n > 0 ? String(Math.round(8 * Math.log2(n))) : "0";
19275
19399
  }
19400
+ function* paceFor(ctx, ev, rates, inputsKey, cfg, opts) {
19401
+ if (!opts.pace || opts.payload === false)
19402
+ return null;
19403
+ const key = fnvHex([inputsKey, "pace"].join("|"));
19404
+ if (opts.paceReuse?.key === key)
19405
+ return opts.paceReuse;
19406
+ const found = yield* paceScan(ctx, ev, rates, cfg);
19407
+ if (found === null)
19408
+ return null;
19409
+ const simLong = rates.long;
19410
+ return {
19411
+ key,
19412
+ rate: found.rate,
19413
+ simLong,
19414
+ phi: Number.isFinite(simLong) && simLong > 0 ? found.rate / simLong : null,
19415
+ unit: ctx.unit,
19416
+ basis: found.basis
19417
+ };
19418
+ }
19276
19419
  function* alertsSliced(s, now, prior, cal, cfg, fc, opts = {}) {
19277
19420
  const base = prior ?? initialAlertMemory();
19278
19421
  if (evidenceOf(s, now) === "none") {
@@ -19281,7 +19424,8 @@ function* alertsSliced(s, now, prior, cal, cfg, fc, opts = {}) {
19281
19424
  active: [],
19282
19425
  transitions: [],
19283
19426
  blockedNow: false,
19284
- status: "insufficient-evidence"
19427
+ status: "insufficient-evidence",
19428
+ pace: null
19285
19429
  };
19286
19430
  }
19287
19431
  const ctx = gapContext(s, now, cal, cfg);
@@ -19318,7 +19462,8 @@ function* alertsSliced(s, now, prior, cal, cfg, fc, opts = {}) {
19318
19462
  const active = [];
19319
19463
  const raised = ["critical", "warn"].filter((l) => memory[l].raised);
19320
19464
  if (raised.length === 0 || opts.payload === false) {
19321
- return { memory, active, transitions, blockedNow, status: "ok" };
19465
+ const pace = yield* paceFor(ctx, ev, rates, inputsKey, cfg, opts);
19466
+ return { memory, active, transitions, blockedNow, status: "ok", pace };
19322
19467
  }
19323
19468
  const seed = seedOf(s, now, cfg);
19324
19469
  const key = fnvHex([inputsKey, seed, raised.join(","), alertPaths(cfg)].join("|"));
@@ -19360,9 +19505,10 @@ function* alertsSliced(s, now, prior, cal, cfg, fc, opts = {}) {
19360
19505
  remedy: r
19361
19506
  });
19362
19507
  }
19363
- return { memory, active, transitions, blockedNow, status: "ok", payload };
19508
+ const pace = active.length === 0 ? yield* paceFor(ctx, ev, rates, inputsKey, cfg, opts) : null;
19509
+ return { memory, active, transitions, blockedNow, status: "ok", payload, pace };
19364
19510
  }
19365
- var HOUR_MS11 = 3600000, REMEDY_MIN_GAP_MS, CUT_GRID, MIN_PATH_SHARE = 0.1, EPISODE_BUCKET_MS;
19511
+ var HOUR_MS11 = 3600000, REMEDY_MIN_GAP_MS, CUT_GRID, EPISODE_BUCKET_MS;
19366
19512
  var init_alerts = __esm(() => {
19367
19513
  init_capacity();
19368
19514
  init_constants();
@@ -19613,6 +19759,7 @@ function contributions(s, q, cfg) {
19613
19759
  const share = calibrated ? observed > 0 ? r.fraction / observed : 0 : totalCu > 0 ? r.cu / totalCu : 0;
19614
19760
  return {
19615
19761
  session: r.session,
19762
+ parent: track?.parent ?? null,
19616
19763
  account: r.accounts.size === 1 && single ? single.account : account,
19617
19764
  window: r.window,
19618
19765
  usedFraction: calibrated && single ? r.fraction : null,
@@ -19631,6 +19778,7 @@ function contributions(s, q, cfg) {
19631
19778
  const lasts = spent.map((x) => x.lastAt).filter((t) => t !== null);
19632
19779
  out.push({
19633
19780
  session: "unattributed",
19781
+ parent: null,
19634
19782
  account,
19635
19783
  window: window2,
19636
19784
  usedFraction: calibrated && single ? unattributed : null,
@@ -19706,7 +19854,7 @@ function deliverableIn(view, unit) {
19706
19854
  const beta = view.governing === null ? undefined : view.beta[view.governing];
19707
19855
  return isStrong(beta) && beta.value > 0 ? d / beta.value : d;
19708
19856
  }
19709
- function historyOf(s, views, now, unit) {
19857
+ function historyFrom(views, now) {
19710
19858
  let from = now - WEEK_MS4;
19711
19859
  for (const v of views) {
19712
19860
  const g = v.windows.find((w) => w.key === v.governing);
@@ -19714,23 +19862,38 @@ function historyOf(s, views, now, unit) {
19714
19862
  continue;
19715
19863
  from = Math.min(from, g.resetAt - g.lengthMinutes * MINUTE_MS10);
19716
19864
  }
19865
+ return from;
19866
+ }
19867
+ function historyAt(s, views, t, unit, byAccount) {
19868
+ let remaining = 0;
19869
+ let any = false;
19870
+ let inferred = false;
19871
+ for (const v of views) {
19872
+ const at = viewAt(s, v, t);
19873
+ if (!at)
19874
+ continue;
19875
+ any = true;
19876
+ inferred ||= at.inferred;
19877
+ const own = deliverableIn(at.view, unit);
19878
+ remaining += own;
19879
+ byAccount[v.account]?.push({ at: t, remaining: own, provenance: provenanceOf(at.inferred) });
19880
+ }
19881
+ return any ? { at: t, remaining, provenance: provenanceOf(inferred) } : null;
19882
+ }
19883
+ function historyOf(s, views, now, unit) {
19884
+ const from = historyFrom(views, now);
19717
19885
  const out = [];
19886
+ const byAccount = {};
19887
+ for (const v of views)
19888
+ byAccount[v.account] = [];
19718
19889
  for (let t = now;t >= from; t -= HOUR_MS14) {
19719
- let remaining = 0;
19720
- let any = false;
19721
- let inferred = false;
19722
- for (const v of views) {
19723
- const at = viewAt(s, v, t);
19724
- if (!at)
19725
- continue;
19726
- any = true;
19727
- inferred ||= at.inferred;
19728
- remaining += deliverableIn(at.view, unit);
19729
- }
19730
- if (any)
19731
- out.push({ at: t, remaining, provenance: inferred ? "reset-inferred" : "observed" });
19890
+ const point = historyAt(s, views, t, unit, byAccount);
19891
+ if (point)
19892
+ out.push(point);
19732
19893
  }
19733
- return out.reverse();
19894
+ for (const list of Object.values(byAccount))
19895
+ list.reverse();
19896
+ return { history: out.reverse(), byAccount };
19734
19897
  }
19735
19898
  function resetsOf(views, now, end) {
19736
19899
  const out = [];
@@ -19779,13 +19942,115 @@ function blockedRuns(results, forecast) {
19779
19942
  }
19780
19943
  return out;
19781
19944
  }
19945
+ function firstTakeovers(r, from, now, end) {
19946
+ const taken = new Map;
19947
+ let prev = from;
19948
+ for (const d of r.decisions) {
19949
+ if (d.account === null || d.cu <= 0 || d.account === prev)
19950
+ continue;
19951
+ const switched = prev !== null;
19952
+ prev = d.account;
19953
+ if (!switched)
19954
+ continue;
19955
+ const t = Math.max(now, r.start + d.k * HOUR_MS14);
19956
+ if (t > end)
19957
+ break;
19958
+ if (!taken.has(d.account))
19959
+ taken.set(d.account, t);
19960
+ }
19961
+ return taken;
19962
+ }
19963
+ function likelihoodOf(times, q, h0, hours) {
19964
+ const x = times.map((t) => (t - h0) / HOUR_MS14);
19965
+ const n = x.length;
19966
+ const mean = x.reduce((a, b) => a + b, 0) / Math.max(1, n);
19967
+ const sd = Math.sqrt(x.reduce((a, b) => a + (b - mean) ** 2, 0) / Math.max(1, n - 1));
19968
+ const iqr = (q.p75 - q.p25) / HOUR_MS14 / 1.34;
19969
+ const spread = iqr > 0 ? Math.min(sd, iqr) : 0;
19970
+ const bandwidth = 0.9 * spread * n ** -0.2;
19971
+ const p = new Array(Math.max(0, hours)).fill(0);
19972
+ if (bandwidth < MIN_BANDWIDTH_H) {
19973
+ for (const xi of x) {
19974
+ const i = Math.floor(xi);
19975
+ if (i >= 0 && i < p.length)
19976
+ p[i] = (p[i] ?? 0) + 1;
19977
+ }
19978
+ } else {
19979
+ for (let i = 0;i < p.length; i++) {
19980
+ let f = 0;
19981
+ for (const xi of x)
19982
+ f += Math.exp(-0.5 * ((i + 0.5 - xi) / bandwidth) ** 2);
19983
+ p[i] = f;
19984
+ }
19985
+ }
19986
+ const peak = Math.max(0, ...p);
19987
+ return peak > 0 ? p.map((v) => v / peak) : p;
19988
+ }
19989
+ function switchesOf2(results, views, poolActive, now, end) {
19990
+ const order = new Map(views.map((v, i) => [v.account, i]));
19991
+ const from = poolActive !== null && order.has(poolActive) ? poolActive : null;
19992
+ const times = new Map;
19993
+ for (const r of results) {
19994
+ for (const [account, t] of firstTakeovers(r, from, now, end)) {
19995
+ const list = times.get(account) ?? [];
19996
+ list.push(t);
19997
+ times.set(account, list);
19998
+ }
19999
+ }
20000
+ const paths = Math.max(1, results.length);
20001
+ const h0 = hourStart(now);
20002
+ const hours = Math.floor((end - h0) / HOUR_MS14) + 1;
20003
+ const out = [];
20004
+ for (const [account, list] of times) {
20005
+ const probability = list.length / paths;
20006
+ if (probability < MIN_PATH_SHARE)
20007
+ continue;
20008
+ const q = quantilesOf2(list);
20009
+ out.push({
20010
+ account,
20011
+ q,
20012
+ probability: Math.min(1, probability),
20013
+ likelihood: { start: h0, p: likelihoodOf(list, q, h0, hours) }
20014
+ });
20015
+ }
20016
+ return out.sort((a, b) => a.q.p50 - b.q.p50 || (order.get(a.account) ?? 0) - (order.get(b.account) ?? 0));
20017
+ }
20018
+ function lostAtResetOf(results, views) {
20019
+ const out = {};
20020
+ const paths = Math.max(1, results.length);
20021
+ for (const v of views) {
20022
+ const g = v.windows.find((w) => w.key === v.governing);
20023
+ if (!g || g.remaining === null || g.resetAt === null || g.semantics === "sliding")
20024
+ continue;
20025
+ const values = [];
20026
+ for (const r of results) {
20027
+ const left = r.leftoverAtReset[v.account];
20028
+ if (left !== null && left !== undefined && Number.isFinite(left)) {
20029
+ values.push(Math.min(1, Math.max(0, left)));
20030
+ }
20031
+ }
20032
+ if (values.length === 0 || values.length < MIN_PATH_SHARE * paths)
20033
+ continue;
20034
+ out[v.account] = { at: g.resetAt, q: quantilesOf2(values) };
20035
+ }
20036
+ return out;
20037
+ }
19782
20038
  function trajectory(s, now, horizonMs, cal, cfg) {
19783
20039
  const all = accountViews(s, now, cfg).filter(isRead);
19784
20040
  const unit = strongBetas(all) ? "cu" : "account-fraction";
19785
- const history = historyOf(s, all, now, unit);
20041
+ const { history, byAccount: historyByAccount } = historyOf(s, all, now, unit);
19786
20042
  const end = now + Math.max(0, horizonMs);
19787
20043
  const resets = resetsOf(all, now, end);
19788
- const empty = { unit, history, forecast: [], resets, blocked: [] };
20044
+ const empty = {
20045
+ unit,
20046
+ history,
20047
+ forecast: [],
20048
+ resets,
20049
+ blocked: [],
20050
+ switches: [],
20051
+ lostAtReset: {},
20052
+ historyByAccount
20053
+ };
19789
20054
  if (evidenceOf(s, now) === "none" || all.length === 0)
19790
20055
  return empty;
19791
20056
  const views = unit === "cu" ? all : all.map(inFractions);
@@ -19823,7 +20088,16 @@ function trajectory(s, now, horizonMs, cal, cfg) {
19823
20088
  }
19824
20089
  forecast.push({ at, q: quantilesOf2(values), pBlocked: blocked / paths });
19825
20090
  }
19826
- return { unit, history, forecast, resets, blocked: blockedRuns(results, forecast) };
20091
+ return {
20092
+ unit,
20093
+ history,
20094
+ forecast,
20095
+ resets,
20096
+ blocked: blockedRuns(results, forecast),
20097
+ switches: switchesOf2(results, views, s.poolActive ?? null, now, end),
20098
+ lostAtReset: lostAtResetOf(results, views),
20099
+ historyByAccount
20100
+ };
19827
20101
  }
19828
20102
  function sparkline(t, cells) {
19829
20103
  const n = Math.max(0, Math.floor(cells));
@@ -19886,7 +20160,7 @@ function sparkline(t, cells) {
19886
20160
  return LEVELS[Math.max(0, level)] ?? " ";
19887
20161
  }).join("");
19888
20162
  }
19889
- var MINUTE_MS10 = 60000, HOUR_MS14 = 3600000, WEEK_MS4, BLOCKED_SHARE = 0.5, WIDE_BAND = 0.15, LEVELS;
20163
+ var MINUTE_MS10 = 60000, HOUR_MS14 = 3600000, WEEK_MS4, BLOCKED_SHARE = 0.5, WIDE_BAND = 0.15, LEVELS, provenanceOf = (inferred) => inferred ? "reset-inferred" : "observed", MIN_BANDWIDTH_H = 0.5;
19890
20164
  var init_trajectory = __esm(() => {
19891
20165
  init_alerts();
19892
20166
  init_calibrate();
@@ -20032,6 +20306,9 @@ function buildResponseRow(input) {
20032
20306
  finalInTok: count(input.finalInput),
20033
20307
  windows: readingsOf(input.provider, input.usage)
20034
20308
  };
20309
+ const parent = input.parent;
20310
+ if (parent && SESSION_PATTERN.test(parent) && parent !== row.session)
20311
+ row.parent = parent;
20035
20312
  return responseRowSchema.safeParse(row).success ? row : null;
20036
20313
  }
20037
20314
  function buildNoEligibleRow(provider, at) {
@@ -20085,7 +20362,8 @@ function toEvent(row) {
20085
20362
  tokens: tokensOf(row),
20086
20363
  finalInput: row.finalInTok,
20087
20364
  windows: row.windows,
20088
- outcome: row.outcome
20365
+ outcome: row.outcome,
20366
+ parent: row.parent ?? null
20089
20367
  };
20090
20368
  case "no-eligible":
20091
20369
  return row.until === undefined ? { kind: "no-eligible", at: row.at } : {
@@ -20106,7 +20384,8 @@ function toEvent(row) {
20106
20384
  tokens: { ...row.tokens },
20107
20385
  first: row.first,
20108
20386
  last: row.last,
20109
- censoredMinutes: row.censoredMinutes
20387
+ censoredMinutes: row.censoredMinutes,
20388
+ parent: row.parent ?? null
20110
20389
  };
20111
20390
  }
20112
20391
  }
@@ -20189,7 +20468,8 @@ var init_ledger_row = __esm(() => {
20189
20468
  cacheWriteTok: tokenCount,
20190
20469
  outTok: tokenCount,
20191
20470
  finalInTok: tokenCount,
20192
- windows: array(windowReadingSchema).max(16).nullable()
20471
+ windows: array(windowReadingSchema).max(16).nullable(),
20472
+ parent: string2().regex(SESSION_PATTERN).nullable().optional()
20193
20473
  });
20194
20474
  noEligibleRowSchema = object({
20195
20475
  v: literal(LEDGER_ROW_VERSION),
@@ -20218,7 +20498,8 @@ var init_ledger_row = __esm(() => {
20218
20498
  tokens: tokenCountsSchema,
20219
20499
  first: array(windowReadingSchema).max(16).nullable(),
20220
20500
  last: array(windowReadingSchema).max(16).nullable(),
20221
- censoredMinutes: number2().finite().min(0).max(60)
20501
+ censoredMinutes: number2().finite().min(0).max(60),
20502
+ parent: string2().regex(SESSION_PATTERN).nullable().optional()
20222
20503
  });
20223
20504
  ledgerRowSchema = discriminatedUnion("kind", [
20224
20505
  responseRowSchema,
@@ -20736,6 +21017,7 @@ function* rollupDaySteps(day, rows, openAtStart) {
20736
21017
  }
20737
21018
  }
20738
21019
  const hh = String(new Date(hour).getUTCHours()).padStart(2, "0");
21020
+ const parent = group.find((r) => r.parent)?.parent ?? null;
20739
21021
  const rollup = {
20740
21022
  v: LEDGER_ROW_VERSION,
20741
21023
  kind: "rollup",
@@ -20754,7 +21036,8 @@ function* rollupDaySteps(day, rows, openAtStart) {
20754
21036
  },
20755
21037
  first: first.size > 0 ? [...first.values()] : null,
20756
21038
  last: last.size > 0 ? [...last.values()] : null,
20757
- censoredMinutes: censored.get(head.provider)?.get(hour) ?? 0
21039
+ censoredMinutes: censored.get(head.provider)?.get(hour) ?? 0,
21040
+ ...parent ? { parent } : {}
20758
21041
  };
20759
21042
  lines.push(JSON.stringify(rollup));
20760
21043
  }
@@ -21858,6 +22141,12 @@ function readAlertsFile(env) {
21858
22141
  return emptyAlertsFile();
21859
22142
  }
21860
22143
  }
22144
+ function delegatingSession(env) {
22145
+ const value = env[ENV.CLAUDISH_PARENT_SESSION];
22146
+ if (!value || !isPaneChild(env))
22147
+ return null;
22148
+ return SESSION_PATTERN.test(value) ? value : null;
22149
+ }
21861
22150
 
21862
22151
  class AllowanceService {
21863
22152
  env;
@@ -21870,9 +22159,12 @@ class AllowanceService {
21870
22159
  nextFlight = 0;
21871
22160
  threads = new Map;
21872
22161
  models = new Map;
22162
+ reportStateStore = null;
22163
+ parentSession;
21873
22164
  constructor(env = process.env, clock = Date.now) {
21874
22165
  this.env = env;
21875
22166
  this.clock = clock;
22167
+ this.parentSession = delegatingSession(env);
21876
22168
  }
21877
22169
  record(attempt) {
21878
22170
  try {
@@ -21890,7 +22182,8 @@ class AllowanceService {
21890
22182
  outcome: attempt.outcome,
21891
22183
  tokens: attempt.tokens,
21892
22184
  finalInput: attempt.finalInput,
21893
- usage: attempt.usage
22185
+ usage: attempt.usage,
22186
+ parent: this.parentSession
21894
22187
  });
21895
22188
  if (!row) {
21896
22189
  this.warnOnce("a usage row failed its guards and was not written");
@@ -22070,9 +22363,21 @@ class AllowanceService {
22070
22363
  cal: m.cal,
22071
22364
  fc: m.fc,
22072
22365
  alerts: m.lastAlerts,
22073
- labels
22366
+ labels,
22367
+ poolActive: this.activeNow(provider, s)
22074
22368
  });
22075
22369
  }
22370
+ activeNow(provider, s) {
22371
+ try {
22372
+ this.reportStateStore ??= new StateStore({ path: statePath(this.env) });
22373
+ const read = this.reportStateStore.read();
22374
+ if (read.kind === "failed")
22375
+ return s.poolActive ?? null;
22376
+ return read.state.providers[provider]?.active ?? null;
22377
+ } catch {
22378
+ return s.poolActive ?? null;
22379
+ }
22380
+ }
22076
22381
  modelFor(provider) {
22077
22382
  let m = this.models.get(provider);
22078
22383
  if (!m) {
@@ -22103,7 +22408,8 @@ class AllowanceService {
22103
22408
  sinks: new Set,
22104
22409
  label: undefined,
22105
22410
  payload: null,
22106
- lastAlerts: { active: [], blockedNow: false, status: "insufficient-evidence" }
22411
+ pace: null,
22412
+ lastAlerts: { active: [], blockedNow: false, status: "insufficient-evidence", pace: null }
22107
22413
  };
22108
22414
  this.models.set(provider, m);
22109
22415
  }
@@ -22296,14 +22602,21 @@ class AllowanceService {
22296
22602
  let won = [];
22297
22603
  let landed = null;
22298
22604
  for (let attempt = 0;attempt < ALERTS_CAS_ATTEMPTS && landed === null; attempt++) {
22299
- const result = await runSliced(alertsSliced(state, now, peek, m.cal, m.cfg, fc, { reuse: m.payload }));
22605
+ const result = await runSliced(alertsSliced(state, now, peek, m.cal, m.cfg, fc, {
22606
+ reuse: m.payload,
22607
+ pace: !announce,
22608
+ paceReuse: m.pace
22609
+ }));
22300
22610
  if (result.payload)
22301
22611
  m.payload = result.payload;
22612
+ if (result.pace)
22613
+ m.pace = result.pace;
22302
22614
  if (!announce) {
22303
22615
  m.lastAlerts = {
22304
22616
  active: result.active,
22305
22617
  blockedNow: result.blockedNow,
22306
- status: result.status
22618
+ status: result.status,
22619
+ pace: result.pace
22307
22620
  };
22308
22621
  return [];
22309
22622
  }
@@ -22354,7 +22667,12 @@ class AllowanceService {
22354
22667
  log(`[allowance] ${provider}: the alert memory kept moving; step skipped`);
22355
22668
  return [];
22356
22669
  }
22357
- m.lastAlerts = { active: landed.active, blockedNow: landed.blockedNow, status: landed.status };
22670
+ m.lastAlerts = {
22671
+ active: landed.active,
22672
+ blockedNow: landed.blockedNow,
22673
+ status: landed.status,
22674
+ pace: null
22675
+ };
22358
22676
  const active = landed.active;
22359
22677
  const status = landed.status;
22360
22678
  const accounts = accountViews(m.state, now, m.cfg).length;
@@ -22490,19 +22808,37 @@ function poolRemaining(views) {
22490
22808
  }
22491
22809
  return { remainingNow: now.value, capacity, unit: now.unit };
22492
22810
  }
22811
+ function likelihoodView(l) {
22812
+ const p = l.p.map((v) => Math.round(Math.min(1, Math.max(0, v)) * 1000) / 1000);
22813
+ const first = p.findIndex((v) => v > 0);
22814
+ if (first < 0)
22815
+ return;
22816
+ let last = p.length - 1;
22817
+ while (last > first && p[last] === 0)
22818
+ last--;
22819
+ return { start: iso2(l.start + first * HOUR_MS16), stepHours: 1, p: p.slice(first, last + 1) };
22820
+ }
22821
+ function sustainableOf(top, pace, burnLong) {
22822
+ const long = Number.isFinite(burnLong) ? Math.max(0, burnLong) : 0;
22823
+ if (top)
22824
+ return top.remedy.kind === "cut" ? top.remedy.cutToFraction * long : 0;
22825
+ return pace ? paceInCuPerHour(pace, long) : null;
22826
+ }
22493
22827
  function buildReport(input) {
22494
22828
  const { s, now, cfg, cal, fc } = input;
22495
22829
  const horizonMs = Math.min(168 * HOUR_MS16, Math.max(HOUR_MS16, input.horizonMs));
22496
22830
  const evidence = evidenceOf(s, now);
22497
22831
  const none = evidence === "none";
22498
22832
  const views = accountViews(s, now, cfg);
22499
- const traj = trajectory(s, now, horizonMs, cal, cfg);
22833
+ const poolActive = input.poolActive !== undefined ? input.poolActive : s.poolActive ?? null;
22834
+ const traj = trajectory({ ...s, poolActive }, now, horizonMs, cal, cfg);
22500
22835
  const unit = fc?.unit ?? traj.unit;
22501
22836
  const burn = none ? { long: 0, short: 0 } : burnRates(s, now, cal, cfg);
22502
22837
  const ledgerHours = ledgerDays(s, now) * 24;
22503
22838
  const label = (id) => input.labels.get(id) ?? id;
22504
22839
  const pool = poolRemaining(views);
22505
22840
  const top = input.alerts.active[0];
22841
+ const status = none ? "insufficient-evidence" : input.alerts.status;
22506
22842
  const fmt = (at) => formatLocalTime(at);
22507
22843
  const report = {
22508
22844
  schema: ALLOWANCE_REPORT_SCHEMA,
@@ -22518,6 +22854,7 @@ function buildReport(input) {
22518
22854
  accounts: views.map((v) => {
22519
22855
  const g = v.governing === null ? undefined : v.beta[v.governing];
22520
22856
  const k = v.binding === null || v.binding === v.governing ? undefined : v.kappa[v.binding];
22857
+ const lost = traj.lostAtReset[v.account];
22521
22858
  return {
22522
22859
  label: label(v.account),
22523
22860
  id: v.account,
@@ -22533,7 +22870,15 @@ function buildReport(input) {
22533
22870
  semantics: w.semantics
22534
22871
  })),
22535
22872
  lambda: null,
22536
- calibration: { beta: g?.quality ?? "prior", kappa: k?.quality ?? null }
22873
+ calibration: { beta: g?.quality ?? "prior", kappa: k?.quality ?? null },
22874
+ active: poolActive !== null && v.account === poolActive,
22875
+ order: v.order,
22876
+ lostAtReset: lost ? {
22877
+ at: iso2(lost.at),
22878
+ p10: Math.min(1, Math.max(0, lost.q.p10)),
22879
+ p50: Math.min(1, Math.max(0, lost.q.p50)),
22880
+ p90: Math.min(1, Math.max(0, lost.q.p90))
22881
+ } : null
22537
22882
  };
22538
22883
  }),
22539
22884
  combined: {
@@ -22543,7 +22888,7 @@ function buildReport(input) {
22543
22888
  burn: {
22544
22889
  long: ledgerHours < cfg.BURN_LONG_HL_H ? null : burn.long,
22545
22890
  short: ledgerHours < cfg.BURN_SHORT_HL_H ? null : burn.short,
22546
- sustainable: top ? top.burn.sustainable : null,
22891
+ sustainable: status === "ok" ? sustainableOf(top, input.alerts.pace ?? null, burn.long) : null,
22547
22892
  unit: "cu/h"
22548
22893
  },
22549
22894
  alerts: none ? [] : input.alerts.active.map((a) => {
@@ -22579,7 +22924,7 @@ function buildReport(input) {
22579
22924
  };
22580
22925
  }),
22581
22926
  blockedNow: none ? false : input.alerts.blockedNow,
22582
- status: none ? "insufficient-evidence" : input.alerts.status,
22927
+ status,
22583
22928
  trajectory: {
22584
22929
  history: traj.history.map((h) => ({
22585
22930
  at: iso2(h.at),
@@ -22605,6 +22950,18 @@ function buildReport(input) {
22605
22950
  kind: b.kind,
22606
22951
  probability: b.probability
22607
22952
  })),
22953
+ historyByAccount: none ? [] : views.filter((v) => traj.historyByAccount[v.account] !== undefined).map((v) => ({
22954
+ accountId: v.account,
22955
+ 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 }))
22956
+ })),
22957
+ switches: traj.switches.map((sw) => ({
22958
+ accountId: sw.account,
22959
+ soonest: iso2(sw.q.p10),
22960
+ usual: iso2(sw.q.p50),
22961
+ latest: iso2(sw.q.p90),
22962
+ probability: Math.min(1, Math.max(0, sw.probability)),
22963
+ likelihood: likelihoodView(sw.likelihood)
22964
+ })),
22608
22965
  spark48: sparkline({
22609
22966
  ...traj,
22610
22967
  history: traj.history.filter((h) => h.at >= now - 24 * HOUR_MS16),
@@ -22613,6 +22970,7 @@ function buildReport(input) {
22613
22970
  },
22614
22971
  sessions: none ? [] : contributions(s, { now, window: "binding" }, cfg).map((c) => ({
22615
22972
  session: c.session === "unattributed" ? "unattributed" : c.session.slice(0, 8),
22973
+ parent: c.parent === null ? null : c.parent.slice(0, 8),
22616
22974
  account: c.account === "all" ? "all" : label(c.account),
22617
22975
  window: c.window,
22618
22976
  share: Math.min(1, Math.max(0, c.share)),
@@ -22682,7 +23040,9 @@ function getAllowanceService() {
22682
23040
  }
22683
23041
  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;
22684
23042
  var init_allowance_service = __esm(() => {
23043
+ init_config();
22685
23044
  init_logger();
23045
+ init_child_env();
22686
23046
  init_allowance_report_schema();
22687
23047
  init_alerts();
22688
23048
  init_capacity();
@@ -90904,6 +91264,7 @@ function buildPaneEnv(input) {
90904
91264
  env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = "1";
90905
91265
  if (input.parentEnv.CLAUDE_CODE_NATIVE_CURSOR === undefined)
90906
91266
  env.CLAUDE_CODE_NATIVE_CURSOR = "1";
91267
+ delete env[ENV.CLAUDISH_PARENT_SESSION];
90907
91268
  Object.assign(env, input.slotEnv);
90908
91269
  if (input.configFile !== null)
90909
91270
  env.CLAUDISH_CONFIG = input.configFile;
@@ -97980,7 +98341,8 @@ async function startModelsIn(flight, sessionPath, opts) {
97980
98341
  kind: "team",
97981
98342
  id: `${runId}:${e.id}`,
97982
98343
  role: kind === "judge" ? "judge" : "slot"
97983
- })
98344
+ }),
98345
+ ...opts.parentClaudeSessionId ? { [ENV.CLAUDISH_PARENT_SESSION]: opts.parentClaudeSessionId } : {}
97984
98346
  },
97985
98347
  shape: "one-shot",
97986
98348
  initialPrompt: inputContent,
@@ -98207,6 +98569,7 @@ async function judgeResponses(sessionPath, opts = {}) {
98207
98569
  await runModels(judgePath, {
98208
98570
  claudeFlags: opts.claudeFlags,
98209
98571
  kind: "judge",
98572
+ ...opts.parentClaudeSessionId ? { parentClaudeSessionId: opts.parentClaudeSessionId } : {},
98210
98573
  ...opts.parentEnv ? { parentEnv: opts.parentEnv } : {},
98211
98574
  ...opts.paneTimings ? { paneTimings: opts.paneTimings } : {}
98212
98575
  });
@@ -99026,7 +99389,8 @@ class SessionManager {
99026
99389
  [ENV.CLAUDISH_TOKEN_FILE]: entry.tokenFile,
99027
99390
  [UPSTREAM_ERROR_LOG_ENV]: entry.upstreamErrorLogPath,
99028
99391
  ...entry.inboxPath ? { [HOOK_INBOX_ENV]: entry.inboxPath } : {},
99029
- [HOOK_SESSION_ENV]: JSON.stringify({ kind: "channel", id: info.sessionId, role: null })
99392
+ [HOOK_SESSION_ENV]: JSON.stringify({ kind: "channel", id: info.sessionId, role: null }),
99393
+ ...info.parentClaudeSessionId ? { [ENV.CLAUDISH_PARENT_SESSION]: info.parentClaudeSessionId } : {}
99030
99394
  },
99031
99395
  shape: info.shape,
99032
99396
  ...opts.prompt ? { initialPrompt: opts.prompt } : {},
@@ -100854,9 +101218,32 @@ async function allowanceStatus(horizonMs) {
100854
101218
  return { ...meta, allowance: null, reason: `the allowance report failed: ${message}` };
100855
101219
  }
100856
101220
  }
101221
+ function allowanceSectionsArg(value) {
101222
+ if (value === undefined)
101223
+ return;
101224
+ const known = ALLOWANCE_REPORT_SECTIONS;
101225
+ if (!Array.isArray(value) || value.length === 0)
101226
+ throw new ContractErrorException("invalid_args", `'sections' must be a non-empty array of ${known.join(", ")}`);
101227
+ for (const name of value) {
101228
+ if (typeof name !== "string" || !known.includes(name))
101229
+ throw new ContractErrorException("invalid_args", `'sections' has an unknown section ${JSON.stringify(name)}; known: ${known.join(", ")}`);
101230
+ }
101231
+ return [...new Set(value)].sort();
101232
+ }
101233
+ function allowanceSections(answer, sections) {
101234
+ if (sections === undefined || answer.allowance === null)
101235
+ return answer;
101236
+ const dropped = new Set(ALLOWANCE_REPORT_SECTIONS.filter((section) => !sections.includes(section)));
101237
+ const allowance = Object.fromEntries(Object.entries(answer.allowance).filter(([key]) => !dropped.has(key)));
101238
+ const { contract_version, capabilities, ...rest } = answer;
101239
+ return { contract_version, capabilities, sections, ...rest, allowance };
101240
+ }
100857
101241
  function contractAnswer(value) {
100858
101242
  return { content: [{ type: "text", text: JSON.stringify(value, null, 2) }] };
100859
101243
  }
101244
+ function compactAnswer(value) {
101245
+ return { content: [{ type: "text", text: JSON.stringify(value) }] };
101246
+ }
100860
101247
  function contractErrorAnswer(e) {
100861
101248
  const err = e instanceof ContractErrorException ? e : new ContractErrorException("invalid_args", e instanceof Error ? e.message : String(e));
100862
101249
  return {
@@ -101443,11 +101830,15 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
101443
101830
  models?.[givenModels?.indexOf(m) ?? -1] ?? m,
101444
101831
  level
101445
101832
  ])) : undefined;
101833
+ const runsModels = mode === "run" || mode === "run-and-judge";
101834
+ const runsJudges = mode === "judge" || mode === "run-and-judge";
101835
+ const parentClaudeSessionId = runsModels || runsJudges ? await parentSessionForCall({ hostPid: sessionManager.hostPid }) : undefined;
101446
101836
  const runOpts = {
101447
101837
  requirePattern,
101448
101838
  minOutputBytes,
101449
101839
  claudeFlags: childFlags,
101450
101840
  efforts,
101841
+ parentClaudeSessionId,
101451
101842
  onProgress: (u) => {
101452
101843
  ctx.reportProgress(`team: ${u.phase}`);
101453
101844
  }
@@ -101458,8 +101849,6 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
101458
101849
  const existing = join56(resolved, "input.md");
101459
101850
  return existsSync39(existing) ? readFileSync45(existing, "utf-8") : undefined;
101460
101851
  };
101461
- const runsModels = mode === "run" || mode === "run-and-judge";
101462
- const runsJudges = mode === "judge" || mode === "run-and-judge";
101463
101852
  const choice = modelChoiceStop([
101464
101853
  ...runsModels && givenModels && models ? givenModels.map((given, i) => ({ given, spec: models[i] })) : [],
101465
101854
  ...runsJudges && givenJudges && judges ? givenJudges.map((given, i) => ({ given, spec: judges[i] })) : []
@@ -101484,9 +101873,6 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
101484
101873
  requirePattern
101485
101874
  });
101486
101875
  setupSession(resolved, models, input);
101487
- const parentClaudeSessionId = await parentSessionForCall({
101488
- hostPid: sessionManager.hostPid
101489
- });
101490
101876
  const monitorRecord = sessionManager.recordTeamRun({
101491
101877
  teamPath: resolved,
101492
101878
  slots: models.length,
@@ -101539,7 +101925,11 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
101539
101925
  };
101540
101926
  }
101541
101927
  case "judge": {
101542
- const verdict = await judgeResponses(resolved, { judges, claudeFlags: childFlags });
101928
+ const verdict = await judgeResponses(resolved, {
101929
+ judges,
101930
+ claudeFlags: childFlags,
101931
+ parentClaudeSessionId
101932
+ });
101543
101933
  const out = providerNote ? { ...verdict, provider_note: providerNote } : verdict;
101544
101934
  return { content: [{ type: "text", text: JSON.stringify(out, null, 2) }] };
101545
101935
  }
@@ -101557,7 +101947,11 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
101557
101947
  const handle = await startModels(resolved, runOpts);
101558
101948
  await handle.done;
101559
101949
  const run = teamRunRow(resolved, handle.runId);
101560
- const verdict = await judgeResponses(resolved, { judges, claudeFlags: childFlags });
101950
+ const verdict = await judgeResponses(resolved, {
101951
+ judges,
101952
+ claudeFlags: childFlags,
101953
+ parentClaudeSessionId
101954
+ });
101561
101955
  return {
101562
101956
  content: [
101563
101957
  {
@@ -101743,7 +102137,7 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
101743
102137
  });
101744
102138
  tools.push({
101745
102139
  name: "allowance_status",
101746
- 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.",
102140
+ 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.',
101747
102141
  inputSchema: {
101748
102142
  type: "object",
101749
102143
  properties: {
@@ -101755,6 +102149,15 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
101755
102149
  horizon_hours: {
101756
102150
  type: "number",
101757
102151
  description: "Forecast horizon in hours, 1\u2013168 (default: 168)"
102152
+ },
102153
+ sections: {
102154
+ type: "array",
102155
+ items: {
102156
+ type: "string",
102157
+ enum: [...ALLOWANCE_REPORT_SECTIONS]
102158
+ },
102159
+ minItems: 1,
102160
+ description: 'Report only these parts beside the header (default: all). Poll with ["accounts","burn","alerts"]; the trajectory and sessions are most of the bytes.'
101758
102161
  }
101759
102162
  }
101760
102163
  },
@@ -101767,7 +102170,8 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
101767
102170
  const hours = args.horizon_hours ?? 168;
101768
102171
  if (typeof hours !== "number" || !Number.isFinite(hours) || hours < 1 || hours > 168)
101769
102172
  throw new ContractErrorException("invalid_args", "'horizon_hours' must be a number from 1 to 168");
101770
- return contractAnswer(await allowanceStatus(hours * 3600000));
102173
+ const sections = allowanceSectionsArg(args.sections);
102174
+ return compactAnswer(allowanceSections(await allowanceStatus(hours * 3600000), sections));
101771
102175
  } catch (e) {
101772
102176
  return contractErrorAnswer(e);
101773
102177
  }
@@ -102241,6 +102645,7 @@ var init_mcp_server = __esm(() => {
102241
102645
  init_types3();
102242
102646
  init_base_api_format();
102243
102647
  init_model_catalog();
102648
+ init_allowance_report_schema();
102244
102649
  init_prehydrate();
102245
102650
  init_prompt_role();
102246
102651
  init_fs_atomic();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudish",
3
- "version": "14.6.0",
3
+ "version": "14.8.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.6.0",
75
- "@claudish/magmux-darwin-x64": "14.6.0",
76
- "@claudish/magmux-linux-arm64": "14.6.0",
77
- "@claudish/magmux-linux-x64": "14.6.0"
74
+ "@claudish/magmux-darwin-arm64": "14.8.0",
75
+ "@claudish/magmux-darwin-x64": "14.8.0",
76
+ "@claudish/magmux-linux-arm64": "14.8.0",
77
+ "@claudish/magmux-linux-x64": "14.8.0"
78
78
  },
79
79
  "author": "Jack Rudenko <i@madappgang.com>",
80
80
  "license": "MIT",