@alook/daemon 0.1.25 → 0.1.27

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 (3) hide show
  1. package/dist/cli/index.js +766 -232
  2. package/dist/index.js +754 -220
  3. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -16646,6 +16646,7 @@ var communityMachine = sqliteTable("community_machine", {
16646
16646
  arch: text("arch").notNull().default(""),
16647
16647
  osRelease: text("os_release").notNull().default(""),
16648
16648
  daemonVersion: text("daemon_version").notNull().default(""),
16649
+ timeZone: text("time_zone"),
16649
16650
  metadata: text("metadata"),
16650
16651
  availableRuntimes: text("available_runtimes", { mode: "json" }).$type().notNull().default([]),
16651
16652
  status: text("status").notNull().default("offline"),
@@ -17536,7 +17537,7 @@ var REASONING_EFFORT_RE = /^[A-Za-z0-9._-]+$/;
17536
17537
  var COMMUNITY_REASONING_EFFORT_MAX = 32;
17537
17538
  var COMMUNITY_REASONING_DESCRIPTION_MAX = 256;
17538
17539
  var COMMUNITY_REASONING_OPTIONS_MAX = 16;
17539
- var COMMUNITY_REASONING_MODELS_MAX = 64;
17540
+ var COMMUNITY_REASONING_MODELS_MAX = 512;
17540
17541
  var ReasoningEffortSchema = exports_external.string().min(1).max(COMMUNITY_REASONING_EFFORT_MAX).regex(REASONING_EFFORT_RE, "invalid reasoning effort charset");
17541
17542
  var RuntimeReasoningOptionSchema = exports_external.object({
17542
17543
  value: ReasoningEffortSchema,
@@ -17544,6 +17545,7 @@ var RuntimeReasoningOptionSchema = exports_external.object({
17544
17545
  });
17545
17546
  var RuntimeReasoningModelSchema = exports_external.object({
17546
17547
  id: exports_external.string().min(1).max(100),
17548
+ displayName: exports_external.string().min(1).max(COMMUNITY_REASONING_DESCRIPTION_MAX).optional().catch(undefined),
17547
17549
  supportedReasoningEfforts: exports_external.array(exports_external.unknown()).max(COMMUNITY_REASONING_OPTIONS_MAX).transform((options) => {
17548
17550
  const seen = new Set;
17549
17551
  return options.flatMap((candidate) => {
@@ -17638,6 +17640,7 @@ var HostReadyMessageSchema = exports_external.object({
17638
17640
  arch: exports_external.string().optional(),
17639
17641
  osRelease: exports_external.string().optional(),
17640
17642
  daemonVersion: exports_external.string().optional(),
17643
+ timeZone: exports_external.string().min(1).max(128).optional(),
17641
17644
  providerQuotas: exports_external.array(ProviderQuotaSnapshotSchema).max(2).optional().default([])
17642
17645
  });
17643
17646
  var CommunityDaemonReadySchema = exports_external.object({
@@ -17660,6 +17663,8 @@ var AgentActivityMessageSchema = exports_external.object({
17660
17663
  type: exports_external.literal("agent_activity"),
17661
17664
  agentId: exports_external.string(),
17662
17665
  state: exports_external.enum(["idle", "starting", "running", "stopping"]),
17666
+ usageTimeZone: exports_external.string().min(1).max(128).optional(),
17667
+ usageDay: exports_external.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
17663
17668
  dailyUsage: exports_external.array(DailyUsageSnapshotSchema).max(7).optional(),
17664
17669
  quota: ProviderQuotaSnapshotSchema.optional()
17665
17670
  });
@@ -17925,6 +17930,42 @@ var BotAuditEventAckFrameSchema = exports_external.strictObject({
17925
17930
  type: exports_external.literal("bot_audit_event_ack"),
17926
17931
  eventId: exports_external.string().min(1).max(128)
17927
17932
  });
17933
+ // ../shared/src/utils/day-key.ts
17934
+ function utcDayKey(now) {
17935
+ const d = now instanceof Date ? now : new Date(now);
17936
+ return d.toISOString().slice(0, 10);
17937
+ }
17938
+ function calendarDayKeyDaysAgo(day, days) {
17939
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(day);
17940
+ if (!match)
17941
+ throw new RangeError("invalid calendar day key");
17942
+ const year = Number(match[1]);
17943
+ const month = Number(match[2]);
17944
+ const date5 = Number(match[3]);
17945
+ const parsed = new Date(Date.UTC(year, month - 1, date5));
17946
+ if (parsed.getUTCFullYear() !== year || parsed.getUTCMonth() !== month - 1 || parsed.getUTCDate() !== date5) {
17947
+ throw new RangeError("invalid calendar day key");
17948
+ }
17949
+ parsed.setUTCDate(parsed.getUTCDate() - days);
17950
+ return utcDayKey(parsed);
17951
+ }
17952
+ function dayKeyInTimeZone(now, timeZone) {
17953
+ const date5 = now instanceof Date ? now : new Date(now);
17954
+ const parts = new Intl.DateTimeFormat("en-US", {
17955
+ timeZone,
17956
+ year: "numeric",
17957
+ month: "2-digit",
17958
+ day: "2-digit"
17959
+ }).formatToParts(date5);
17960
+ const values = new Map(parts.map((part) => [part.type, part.value]));
17961
+ const year = values.get("year");
17962
+ const month = values.get("month");
17963
+ const day = values.get("day");
17964
+ if (!year || !month || !day)
17965
+ throw new RangeError("unable to format calendar day key");
17966
+ return `${year}-${month}-${day}`;
17967
+ }
17968
+
17928
17969
  // ../shared/src/db/community-schema.ts
17929
17970
  var exports_community_schema = {};
17930
17971
  __export(exports_community_schema, {
@@ -20338,8 +20379,8 @@ function resolveLaunchFieldsOrDefault(input) {
20338
20379
  const envVars = Object.fromEntries(Object.entries(normalized.environment ?? {}).filter(([key]) => !controlled.has(key)));
20339
20380
  const providerEnv = {};
20340
20381
  const model = normalized.model.kind === "default" ? undefined : normalized.model.name;
20341
- if (normalized.model.kind === "custom" && normalized.provider?.kind === "custom_endpoint") {
20342
- providerEnv.ANTHROPIC_CUSTOM_MODEL_OPTION = normalized.model.name;
20382
+ if (model && normalized.provider?.kind === "custom_endpoint") {
20383
+ providerEnv.ANTHROPIC_CUSTOM_MODEL_OPTION = model;
20343
20384
  }
20344
20385
  if (normalized.provider?.kind === "custom_endpoint") {
20345
20386
  providerEnv.ANTHROPIC_BASE_URL = normalized.provider.apiUrl;
@@ -20521,6 +20562,66 @@ function buildClaudeArgs(config2) {
20521
20562
  return args;
20522
20563
  }
20523
20564
 
20565
+ // agent-driver/dist/internal/token-usage.js
20566
+ function validIdentityPart(value) {
20567
+ return value.trim().length > 0 && Buffer.byteLength(value, "utf8") <= 512;
20568
+ }
20569
+ function identityKey(identity) {
20570
+ if (!validIdentityPart(identity.runtime) || !validIdentityPart(identity.backendSessionId) || !validIdentityPart(identity.providerRecordId))
20571
+ return null;
20572
+ return JSON.stringify([identity.runtime, identity.backendSessionId, identity.providerRecordId]);
20573
+ }
20574
+ function metric(value) {
20575
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
20576
+ }
20577
+ function cacheMetric(read, write) {
20578
+ const present = [read, write].filter((value) => value !== undefined);
20579
+ if (present.length === 0)
20580
+ return null;
20581
+ const metrics = present.map(metric);
20582
+ if (metrics.some((value) => value === null))
20583
+ return null;
20584
+ const total = metrics.reduce((sum, value) => sum + value, 0);
20585
+ return Number.isSafeInteger(total) ? total : null;
20586
+ }
20587
+
20588
+ class SettledUsageProjector {
20589
+ active = new Set;
20590
+ project(record2) {
20591
+ const key = identityKey(record2);
20592
+ if (!key || this.active.has(key))
20593
+ return null;
20594
+ this.active.add(key);
20595
+ const cache = cacheMetric(record2.cacheRead, record2.cacheWrite);
20596
+ const rawInput = metric(record2.input);
20597
+ const input = record2.inputIncludesCache ? rawInput !== null && cache !== null && cache <= rawInput ? rawInput - cache : null : rawInput;
20598
+ const rawOutput = metric(record2.output);
20599
+ const reasoning = metric(record2.reasoning);
20600
+ const output = record2.outputIncludesReasoning ? rawOutput : rawOutput !== null && reasoning !== null && Number.isSafeInteger(rawOutput + reasoning) ? rawOutput + reasoning : null;
20601
+ if (input === null && output === null && cache === null) {
20602
+ this.active.delete(key);
20603
+ return null;
20604
+ }
20605
+ return {
20606
+ kind: "telemetry",
20607
+ name: "token_usage",
20608
+ source: record2.source,
20609
+ usage: { input, output, cache }
20610
+ };
20611
+ }
20612
+ release(identity) {
20613
+ const key = identityKey(identity);
20614
+ if (key)
20615
+ this.active.delete(key);
20616
+ }
20617
+ reset() {
20618
+ this.active.clear();
20619
+ }
20620
+ get activeCount() {
20621
+ return this.active.size;
20622
+ }
20623
+ }
20624
+
20524
20625
  // agent-driver/dist/internal/utils.js
20525
20626
  import { randomUUID as randomUUID3 } from "crypto";
20526
20627
  function jsonRpcRequest(method, params, id) {
@@ -20540,9 +20641,13 @@ var API_ERROR_RE = /API Error:.*(?:Connection error|\b[45]\d{2}\b)/i;
20540
20641
  class ClaudeEventNormalizer {
20541
20642
  turnProtocol;
20542
20643
  currentSession = null;
20644
+ usageProjector = new SettledUsageProjector;
20543
20645
  constructor(turnProtocol) {
20544
20646
  this.turnProtocol = turnProtocol;
20545
20647
  }
20648
+ beginTurn() {
20649
+ this.usageProjector.reset();
20650
+ }
20546
20651
  get currentSessionId() {
20547
20652
  return this.currentSession;
20548
20653
  }
@@ -20634,7 +20739,7 @@ class ClaudeEventNormalizer {
20634
20739
  const turnOwner = rawOwner ? this.turnProtocol?.claimResult(rawOwner) ?? (this.turnProtocol ? null : `claude:${rawOwner}`) : null;
20635
20740
  if (this.turnProtocol && !turnOwner)
20636
20741
  return;
20637
- const usage = this.buildUsageTelemetry(event);
20742
+ const usage = this.buildUsageTelemetry(event, rawOwner);
20638
20743
  if (usage)
20639
20744
  out.push(usage);
20640
20745
  if (event.is_error || event.subtype === "error_during_execution") {
@@ -20649,23 +20754,26 @@ class ClaudeEventNormalizer {
20649
20754
  acceptsTurnWork() {
20650
20755
  return this.turnProtocol?.acceptsTurnWork() ?? true;
20651
20756
  }
20652
- buildUsageTelemetry(event) {
20757
+ buildUsageTelemetry(event, rootRequestId) {
20653
20758
  const u = event?.usage;
20654
20759
  if (!u)
20655
20760
  return null;
20656
- const metric = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
20657
- const cacheParts = [u.cache_read_input_tokens, u.cache_creation_input_tokens].filter((value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0);
20658
- const cache = cacheParts.length > 0 && Number.isSafeInteger(cacheParts.reduce((sum, value) => sum + value, 0)) ? cacheParts.reduce((sum, value) => sum + value, 0) : null;
20659
- return {
20660
- kind: "telemetry",
20661
- name: "token_usage",
20761
+ const backendSessionId = event.session_id ?? this.currentSession;
20762
+ if (typeof backendSessionId !== "string" || !backendSessionId)
20763
+ return null;
20764
+ const providerRecordId = rootRequestId ?? (typeof event.request_id === "string" ? event.request_id : "invocation-result");
20765
+ return this.usageProjector.project({
20766
+ runtime: "claude",
20767
+ backendSessionId,
20768
+ providerRecordId,
20662
20769
  source: "claude_result_usage",
20663
- usage: {
20664
- input: metric(u.input_tokens),
20665
- output: metric(u.output_tokens),
20666
- cache
20667
- }
20668
- };
20770
+ input: u.input_tokens,
20771
+ output: u.output_tokens,
20772
+ cacheRead: u.cache_read_input_tokens,
20773
+ cacheWrite: u.cache_creation_input_tokens,
20774
+ inputIncludesCache: false,
20775
+ outputIncludesReasoning: true
20776
+ });
20669
20777
  }
20670
20778
  }
20671
20779
 
@@ -20674,6 +20782,7 @@ import { execFileSync as execFileSync2 } from "child_process";
20674
20782
  import * as fs5 from "fs";
20675
20783
  import * as path5 from "path";
20676
20784
  var PROBE_TIMEOUT_MS = 5000;
20785
+ var PROBE_OUTPUT_MAX_BYTES = 1024 * 1024;
20677
20786
  function resolveCommandOnPath(command, deps = {}) {
20678
20787
  if (deps.which)
20679
20788
  return deps.which(command);
@@ -20724,6 +20833,23 @@ function probeCommandVersion(command, args = [], deps = {}, platform = process.p
20724
20833
  return { ok: false, error: String(code) };
20725
20834
  }
20726
20835
  }
20836
+ function probeCommandOutput(command, args, platform = process.platform) {
20837
+ try {
20838
+ const output = execFileSync2(command, args, {
20839
+ encoding: "utf8",
20840
+ timeout: PROBE_TIMEOUT_MS,
20841
+ maxBuffer: PROBE_OUTPUT_MAX_BYTES,
20842
+ shell: needsWindowsShimShell(command, platform),
20843
+ stdio: ["pipe", "pipe", "ignore"],
20844
+ input: "",
20845
+ env: { ...process.env, CI: "1" }
20846
+ });
20847
+ return { ok: true, output };
20848
+ } catch (err) {
20849
+ const code = err?.code ?? "command_probe_failed";
20850
+ return { ok: false, error: String(code) };
20851
+ }
20852
+ }
20727
20853
  function resolveHomePath(relativePath, deps = {}) {
20728
20854
  return path5.join(deps.homeDir || process.env.HOME || ".", relativePath);
20729
20855
  }
@@ -20830,6 +20956,14 @@ class ClaudeTurnProtocol {
20830
20956
  }
20831
20957
 
20832
20958
  // agent-driver/dist/adapters/claude/index.js
20959
+ var CLAUDE_MODEL_CATALOG = {
20960
+ updateMode: "unsupported",
20961
+ models: ["opus", "sonnet", "haiku"].map((id) => ({
20962
+ id,
20963
+ supportedReasoningEfforts: []
20964
+ }))
20965
+ };
20966
+
20833
20967
  class ClaudeDriver {
20834
20968
  id = "claude";
20835
20969
  instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
@@ -20842,14 +20976,17 @@ class ClaudeDriver {
20842
20976
  turnProtocol = new ClaudeTurnProtocol;
20843
20977
  eventNormalizer = new ClaudeEventNormalizer(this.turnProtocol);
20844
20978
  beginTurn() {
20845
- return this.turnProtocol.beginTurn();
20979
+ const receipt = this.turnProtocol.beginTurn();
20980
+ this.eventNormalizer.beginTurn();
20981
+ return receipt;
20846
20982
  }
20847
20983
  probe(command) {
20848
20984
  const explicit = command?.trim();
20849
- if (!explicit)
20850
- return probeClaude();
20851
- const result = probeCommandVersion(explicit);
20852
- return result.ok ? { status: "healthy", version: result.version } : { status: "unhealthy", lastError: result.error };
20985
+ const base = explicit ? (() => {
20986
+ const result = probeCommandVersion(explicit);
20987
+ return result.ok ? { status: "healthy", version: result.version } : { status: "unhealthy", lastError: result.error };
20988
+ })() : probeClaude();
20989
+ return base.status === "healthy" ? { ...base, reasoning: CLAUDE_MODEL_CATALOG } : base;
20853
20990
  }
20854
20991
  async openLane(ctx, options) {
20855
20992
  return createProcessLane(this, ctx, { onRawStdoutLine: options?.onRawStdoutLine });
@@ -20895,14 +21032,6 @@ class ClaudeDriver {
20895
21032
  }
20896
21033
 
20897
21034
  // agent-driver/dist/adapters/codex/telemetry.js
20898
- function metric(value) {
20899
- return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
20900
- }
20901
- function nonCachedInput(input, cached2) {
20902
- if (typeof input !== "number" || !Number.isSafeInteger(input) || input < 0 || typeof cached2 !== "number" || !Number.isSafeInteger(cached2) || cached2 < 0 || cached2 > input)
20903
- return null;
20904
- return input - cached2;
20905
- }
20906
21035
  function canonicalId(value, fallback) {
20907
21036
  return typeof value === "string" && value.length > 0 && new TextEncoder().encode(value).length <= 64 ? value : fallback;
20908
21037
  }
@@ -20996,28 +21125,24 @@ function mapCodexQuotaSnapshots(snapshots, sourceEpoch) {
20996
21125
  }
20997
21126
  };
20998
21127
  }
20999
- function mapCodexTelemetry(method, params, sourceEpoch) {
21000
- if (method === "thread/tokenUsage/updated") {
21001
- const u = params?.tokenUsage?.last ?? params?.token_usage?.last;
21002
- if (!u)
21003
- return [];
21004
- const input = u.inputTokens ?? u.input_tokens;
21005
- const cached2 = u.cachedInputTokens ?? u.cached_input_tokens;
21006
- return [{
21007
- kind: "telemetry",
21008
- name: "token_usage",
21009
- source: "codex_thread_token_usage_updated",
21010
- usage: {
21011
- input: nonCachedInput(input, cached2),
21012
- output: metric(u.outputTokens ?? u.output_tokens),
21013
- cache: metric(cached2)
21014
- }
21015
- }];
21016
- }
21017
- if (method === "account/rateLimits/updated") {
21018
- return [mapCodexQuotaSnapshots([params?.rateLimits ?? params ?? {}], sourceEpoch)];
21019
- }
21020
- return [];
21128
+ function mapCodexSettledUsage(params, projector) {
21129
+ const backendSessionId = params?.threadId ?? params?.thread_id;
21130
+ const providerRecordId = params?.responseId ?? params?.response_id;
21131
+ const usage = params?.usage;
21132
+ if (typeof backendSessionId !== "string" || typeof providerRecordId !== "string" || !usage)
21133
+ return null;
21134
+ return projector.project({
21135
+ runtime: "codex",
21136
+ backendSessionId,
21137
+ providerRecordId,
21138
+ source: "codex_raw_response_completed",
21139
+ input: usage.inputTokens ?? usage.input_tokens,
21140
+ output: usage.outputTokens ?? usage.output_tokens,
21141
+ cacheRead: usage.cachedInputTokens ?? usage.cached_input_tokens,
21142
+ cacheWrite: usage.cacheWriteInputTokens ?? usage.cache_write_input_tokens,
21143
+ inputIncludesCache: true,
21144
+ outputIncludesReasoning: true
21145
+ });
21021
21146
  }
21022
21147
 
21023
21148
  // agent-driver/dist/adapters/codex/normalizer.js
@@ -21069,7 +21194,8 @@ class CodexEventNormalizer {
21069
21194
  rateLimitSnapshots = new Map;
21070
21195
  quotaSnapshotInitialized = false;
21071
21196
  quotaSourceGeneration = codexQuotaSourceGeneration;
21072
- pendingTurnUsage = null;
21197
+ usageProjector = new SettledUsageProjector;
21198
+ usageRecordsBySessionAndTurn = new Map;
21073
21199
  threadId = null;
21074
21200
  turnId = null;
21075
21201
  terminalTurn = null;
@@ -21142,7 +21268,8 @@ class CodexEventNormalizer {
21142
21268
  if (threadId !== this.threadId) {
21143
21269
  this.turnId = null;
21144
21270
  this.terminalTurn = null;
21145
- this.pendingTurnUsage = null;
21271
+ this.usageProjector.reset();
21272
+ this.usageRecordsBySessionAndTurn.clear();
21146
21273
  }
21147
21274
  this.threadId = threadId;
21148
21275
  }
@@ -21191,6 +21318,22 @@ class CodexEventNormalizer {
21191
21318
  }
21192
21319
  handleNotification(method, params) {
21193
21320
  const notificationThreadId = typeof params?.threadId === "string" ? params.threadId : null;
21321
+ if (method === "rawResponse/completed")
21322
+ return this.handleSettledUsage(params);
21323
+ if (method === "turn/completed" && notificationThreadId !== null && notificationThreadId !== this.threadId) {
21324
+ const turnId = this.notificationTurnId(params);
21325
+ if (turnId)
21326
+ this.releaseUsageForTurn(notificationThreadId, turnId);
21327
+ return [];
21328
+ }
21329
+ if (method === "item/completed" && notificationThreadId !== null && notificationThreadId !== this.threadId) {
21330
+ const turnId = this.notificationTurnId(params);
21331
+ const itemType = params?.item?.type ?? params?.type;
21332
+ if (turnId && itemType === "contextCompaction") {
21333
+ this.releaseUsageForTurn(notificationThreadId, turnId);
21334
+ }
21335
+ return [];
21336
+ }
21194
21337
  if (this.threadId !== null && notificationThreadId !== null && notificationThreadId !== this.threadId)
21195
21338
  return [];
21196
21339
  if (this.isRootWorkNotification(method) && !this.acceptRootWork(params))
@@ -21203,7 +21346,6 @@ class CodexEventNormalizer {
21203
21346
  return [];
21204
21347
  this.turnId = params.turn.id;
21205
21348
  this.terminalTurn = null;
21206
- this.pendingTurnUsage = null;
21207
21349
  return [
21208
21350
  {
21209
21351
  kind: "turn_owner",
@@ -21219,7 +21361,7 @@ class CodexEventNormalizer {
21219
21361
  case "item/started":
21220
21362
  return this.handleItemStarted(params);
21221
21363
  case "item/completed":
21222
- return this.handleItemCompleted(params);
21364
+ return this.handleItemCompletedAndReleaseUsage(params);
21223
21365
  case "rawResponseItem/completed":
21224
21366
  return [{ kind: "internal_progress", source: "codex_raw_item", itemType: "rawResponseItem" }];
21225
21367
  case "configWarning":
@@ -21232,30 +21374,24 @@ class CodexEventNormalizer {
21232
21374
  case "turn/completed":
21233
21375
  if (!this.acceptRootTerminal(params))
21234
21376
  return [];
21235
- const usage = this.pendingTurnUsage;
21236
- this.pendingTurnUsage = null;
21377
+ this.releaseUsageForTurn(params.threadId, params.turn.id);
21237
21378
  if (params.turn.status === "failed") {
21238
21379
  return [
21239
- ...usage ? [usage] : [],
21240
21380
  { kind: "error", message: "Codex turn failed" },
21241
21381
  { kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }
21242
21382
  ];
21243
21383
  }
21244
21384
  if (params.turn.status === "interrupted") {
21245
- return [...usage ? [usage] : [], { kind: "error", message: "Codex turn interrupted" }, { kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
21385
+ return [{ kind: "error", message: "Codex turn interrupted" }, { kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
21246
21386
  }
21247
- return [...usage ? [usage] : [], { kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
21387
+ return [{ kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
21248
21388
  case "error":
21249
21389
  if (params?.willRetry === true) {
21250
21390
  return [{ kind: "runtime_recovery", stage: "retrying", source: "codex_stream" }];
21251
21391
  }
21252
21392
  return [{ kind: "error", message: params?.error?.message ?? params?.message ?? "Codex error" }];
21253
- case "thread/tokenUsage/updated": {
21254
- const usage2 = mapCodexTelemetry(method, params, codexQuotaSourceEpoch)[0];
21255
- if (usage2)
21256
- this.pendingTurnUsage = usage2;
21393
+ case "thread/tokenUsage/updated":
21257
21394
  return [];
21258
- }
21259
21395
  case "account/rateLimits/updated":
21260
21396
  return this.mergeQuotaSnapshots(params);
21261
21397
  case "account/updated":
@@ -21266,6 +21402,42 @@ class CodexEventNormalizer {
21266
21402
  return [];
21267
21403
  }
21268
21404
  }
21405
+ handleSettledUsage(params) {
21406
+ const notificationTurnId = this.notificationTurnId(params);
21407
+ if (this.turnId === null && this.terminalTurn?.state === "closed" && notificationTurnId === this.terminalTurn.turnId && params?.threadId === this.terminalTurn.threadId)
21408
+ return [];
21409
+ const backendSessionId = params?.threadId ?? params?.thread_id;
21410
+ const providerRecordId = params?.responseId ?? params?.response_id;
21411
+ if (!notificationTurnId || typeof backendSessionId !== "string" || typeof providerRecordId !== "string")
21412
+ return [];
21413
+ const usage = mapCodexSettledUsage(params, this.usageProjector);
21414
+ if (!usage)
21415
+ return [];
21416
+ const recordsByTurn = this.usageRecordsBySessionAndTurn.get(backendSessionId) ?? new Map;
21417
+ const recordIds = recordsByTurn.get(notificationTurnId) ?? new Set;
21418
+ recordIds.add(providerRecordId);
21419
+ recordsByTurn.set(notificationTurnId, recordIds);
21420
+ this.usageRecordsBySessionAndTurn.set(backendSessionId, recordsByTurn);
21421
+ return [usage];
21422
+ }
21423
+ releaseUsageForTurn(backendSessionId, turnId) {
21424
+ const recordsByTurn = this.usageRecordsBySessionAndTurn.get(backendSessionId);
21425
+ for (const providerRecordId of recordsByTurn?.get(turnId) ?? []) {
21426
+ this.usageProjector.release({ runtime: "codex", backendSessionId, providerRecordId });
21427
+ }
21428
+ recordsByTurn?.delete(turnId);
21429
+ if (recordsByTurn?.size === 0)
21430
+ this.usageRecordsBySessionAndTurn.delete(backendSessionId);
21431
+ }
21432
+ handleItemCompletedAndReleaseUsage(params) {
21433
+ const events = this.handleItemCompleted(params);
21434
+ const turnId = this.notificationTurnId(params);
21435
+ const itemType = params?.item?.type ?? params?.type;
21436
+ if (turnId && itemType === "contextCompaction" && turnId !== this.turnId && typeof params?.threadId === "string") {
21437
+ this.releaseUsageForTurn(params.threadId, turnId);
21438
+ }
21439
+ return events;
21440
+ }
21269
21441
  isRootWorkNotification(method) {
21270
21442
  return method === "item/reasoning/textDelta" || method === "item/reasoning/summaryTextDelta" || method === "item/agentMessage/delta" || method === "item/started" || method === "item/completed" || method === "rawResponseItem/completed";
21271
21443
  }
@@ -21416,10 +21588,59 @@ function stableErrorCode(value, fallback) {
21416
21588
  return /^[A-Za-z0-9_.-]{1,100}$/.test(code) ? code : fallback;
21417
21589
  }
21418
21590
 
21591
+ // agent-driver/dist/internal/modelCatalog.js
21592
+ var RUNTIME_MODEL_CATALOG_MAX = 512;
21593
+ var RUNTIME_MODEL_ID_MAX = 100;
21594
+ function normalizeRuntimeModelId(value) {
21595
+ if (typeof value !== "string")
21596
+ return;
21597
+ const id = value.trim();
21598
+ if (!id || id.length > RUNTIME_MODEL_ID_MAX || /\s/.test(id))
21599
+ return;
21600
+ return id;
21601
+ }
21602
+ function catalogFromIds(ids) {
21603
+ const seen = new Set;
21604
+ const models = [];
21605
+ for (const rawId of ids) {
21606
+ const id = normalizeRuntimeModelId(rawId);
21607
+ if (!id || seen.has(id))
21608
+ continue;
21609
+ if (models.length >= RUNTIME_MODEL_CATALOG_MAX)
21610
+ return;
21611
+ seen.add(id);
21612
+ models.push({ id, supportedReasoningEfforts: [] });
21613
+ }
21614
+ if (models.length === 0)
21615
+ return;
21616
+ return { updateMode: "unsupported", models };
21617
+ }
21618
+ function parseOpenCodeModelCatalog(output) {
21619
+ const ids = output.split(/\r?\n/).flatMap((line) => {
21620
+ const id = normalizeRuntimeModelId(line);
21621
+ return id && /^[^/]+\/.+$/.test(id) ? [id] : [];
21622
+ });
21623
+ return catalogFromIds(ids);
21624
+ }
21625
+ function parsePiModelCatalog(values) {
21626
+ if (!Array.isArray(values))
21627
+ return;
21628
+ const ids = values.flatMap((value) => {
21629
+ if (!value || typeof value !== "object")
21630
+ return [];
21631
+ const model = value;
21632
+ const provider = normalizeRuntimeModelId(model.provider);
21633
+ const id = normalizeRuntimeModelId(model.id);
21634
+ return provider && id && !provider.includes("/") ? [`${provider}/${id}`] : [];
21635
+ });
21636
+ return catalogFromIds(ids);
21637
+ }
21638
+
21419
21639
  // agent-driver/dist/adapters/codex/index.js
21420
21640
  var SETTINGS_UPDATE_TIMEOUT_MS = 5000;
21421
21641
  var MODEL_LIST_TIMEOUT_MS = 5000;
21422
- var MODEL_LIST_MAX = 64;
21642
+ var MODEL_LIST_OUTPUT_MAX_BYTES = 1024 * 1024;
21643
+ var MODEL_LIST_MAX = RUNTIME_MODEL_CATALOG_MAX;
21423
21644
  var MODEL_EFFORT_MAX = 16;
21424
21645
  function isCodexMissingRolloutError(message2) {
21425
21646
  return /\bno\s+rollout\s+found\b/i.test(message2) || /\bmissing\s+rollout\b/i.test(message2) || /\brollout\b.*\b(not found|missing)\b/i.test(message2) || /\b(not found|missing)\b.*\brollout\b/i.test(message2);
@@ -21495,11 +21716,13 @@ class CodexDriver {
21495
21716
  return new Promise((resolve2) => {
21496
21717
  let settled = false;
21497
21718
  let buffer = "";
21719
+ let outputBytes = 0;
21498
21720
  let nextId = 0;
21499
21721
  let initializeId = 0;
21500
21722
  let listId = 0;
21501
21723
  const models = [];
21502
21724
  const seenModels = new Set;
21725
+ let overflow = false;
21503
21726
  let defaultModelId;
21504
21727
  const finish = (catalog) => {
21505
21728
  if (settled)
@@ -21517,12 +21740,16 @@ class CodexDriver {
21517
21740
  `);
21518
21741
  };
21519
21742
  const consumeModel = (value) => {
21520
- if (!value || typeof value !== "object" || models.length >= MODEL_LIST_MAX)
21743
+ if (!value || typeof value !== "object")
21521
21744
  return;
21522
21745
  const model = value;
21523
- const id = typeof model.id === "string" ? model.id.trim() : "";
21524
- if (!id || id.length > 100 || seenModels.has(id))
21746
+ const id = normalizeRuntimeModelId(model.id);
21747
+ if (!id || seenModels.has(id))
21748
+ return;
21749
+ if (models.length >= MODEL_LIST_MAX) {
21750
+ overflow = true;
21525
21751
  return;
21752
+ }
21526
21753
  const rawOptions = Array.isArray(model.supportedReasoningEfforts) ? model.supportedReasoningEfforts : [];
21527
21754
  const seenEfforts = new Set;
21528
21755
  const supportedReasoningEfforts = rawOptions.flatMap((raw) => {
@@ -21566,9 +21793,15 @@ class CodexDriver {
21566
21793
  const result = message2.result;
21567
21794
  for (const model of Array.isArray(result.data) ? result.data : [])
21568
21795
  consumeModel(model);
21796
+ if (overflow)
21797
+ return finish();
21569
21798
  const cursor = typeof result.nextCursor === "string" ? result.nextCursor : undefined;
21570
- if (cursor && models.length < MODEL_LIST_MAX)
21799
+ if (cursor && models.length >= MODEL_LIST_MAX)
21800
+ return finish();
21801
+ if (cursor)
21571
21802
  return requestModelPage(cursor);
21803
+ if (models.length === 0)
21804
+ return finish();
21572
21805
  finish({
21573
21806
  updateMode: "live_next_turn",
21574
21807
  ...defaultModelId ? { defaultModelId } : {},
@@ -21578,7 +21811,11 @@ class CodexDriver {
21578
21811
  const timer = setTimeout(() => finish(), MODEL_LIST_TIMEOUT_MS);
21579
21812
  timer.unref?.();
21580
21813
  proc.stdout?.on("data", (chunk2) => {
21581
- buffer += chunk2.toString();
21814
+ const text2 = chunk2.toString();
21815
+ outputBytes += Buffer.byteLength(text2);
21816
+ if (outputBytes > MODEL_LIST_OUTPUT_MAX_BYTES)
21817
+ return finish();
21818
+ buffer += text2;
21582
21819
  const lines = buffer.split(`
21583
21820
  `);
21584
21821
  buffer = lines.pop() ?? "";
@@ -21781,9 +22018,189 @@ class CodexDriver {
21781
22018
 
21782
22019
  // agent-driver/dist/adapters/cursor/acp-lane.js
21783
22020
  import { EventEmitter as EventEmitter2 } from "node:events";
22021
+
22022
+ // agent-driver/dist/adapters/cursor/catalog-probe.js
21784
22023
  var ACP_PROTOCOL_VERSION = 1;
21785
- var HANDSHAKE_TIMEOUT_MS = 15000;
21786
22024
  var AUTH_METHOD_ID = "cursor_login";
22025
+ var CATALOG_PROBE_TIMEOUT_MS = 15000;
22026
+ var CATALOG_PROBE_OUTPUT_MAX_BYTES = 1024 * 1024;
22027
+ var MODEL_DISPLAY_NAME_MAX = 256;
22028
+ var MODEL_OPTION_NESTING_MAX = 16;
22029
+ function record2(value) {
22030
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
22031
+ }
22032
+ function normalizeDisplayName(value) {
22033
+ if (typeof value !== "string")
22034
+ return;
22035
+ const displayName = value.trim();
22036
+ return displayName && displayName.length <= MODEL_DISPLAY_NAME_MAX ? displayName : undefined;
22037
+ }
22038
+ function flattenCursorAcpSelectOptions(value, depth = 0) {
22039
+ if (!Array.isArray(value) || depth > MODEL_OPTION_NESTING_MAX)
22040
+ return [];
22041
+ const options = [];
22042
+ for (const item of value) {
22043
+ if (Array.isArray(item)) {
22044
+ options.push(...flattenCursorAcpSelectOptions(item, depth + 1));
22045
+ continue;
22046
+ }
22047
+ const candidate = record2(item);
22048
+ if (!candidate)
22049
+ continue;
22050
+ const exactValue = normalizeRuntimeModelId(candidate.value);
22051
+ if (exactValue) {
22052
+ const name = normalizeDisplayName(candidate.name);
22053
+ options.push({ value: exactValue, ...name ? { name } : {} });
22054
+ }
22055
+ if (Array.isArray(candidate.options)) {
22056
+ options.push(...flattenCursorAcpSelectOptions(candidate.options, depth + 1));
22057
+ }
22058
+ }
22059
+ return options;
22060
+ }
22061
+ function parseCursorAcpModelCatalog(session2) {
22062
+ const payload = record2(session2);
22063
+ const configOptions = Array.isArray(payload?.configOptions) ? payload.configOptions : [];
22064
+ const modelConfig = configOptions.map(record2).find((option) => option?.id === "model") ?? null;
22065
+ if (!modelConfig)
22066
+ return;
22067
+ const seen = new Set;
22068
+ const models = [];
22069
+ for (const option of flattenCursorAcpSelectOptions(modelConfig.options)) {
22070
+ if (option.value === "default[]" || seen.has(option.value))
22071
+ continue;
22072
+ if (models.length >= RUNTIME_MODEL_CATALOG_MAX)
22073
+ return;
22074
+ seen.add(option.value);
22075
+ models.push({
22076
+ id: option.value,
22077
+ ...option.name ? { displayName: option.name } : {},
22078
+ supportedReasoningEfforts: []
22079
+ });
22080
+ }
22081
+ return models.length > 0 ? { updateMode: "unsupported", models } : undefined;
22082
+ }
22083
+ async function cleanupProbeProcess(process3) {
22084
+ if (process3.pid) {
22085
+ await killProcessTree(process3.pid, { graceMs: 250 }).catch(() => {});
22086
+ return;
22087
+ }
22088
+ if (process3.exitCode === null && process3.signalCode === null)
22089
+ process3.kill("SIGTERM");
22090
+ }
22091
+ async function probeCursorAcpCatalog(command, options = {}) {
22092
+ const cwd = options.cwd ?? process.cwd();
22093
+ const spec = resolveSpawnSpec("cursor-agent", ["acp"], command);
22094
+ let processHandle;
22095
+ try {
22096
+ processHandle = (options.spawn ?? spawnAgentProcess)(spec.command, spec.args, {
22097
+ cwd,
22098
+ env: { ...process.env, CI: "1" },
22099
+ shell: spec.shell
22100
+ });
22101
+ } catch {
22102
+ return;
22103
+ }
22104
+ return new Promise((resolve2) => {
22105
+ let settled = false;
22106
+ let buffer = "";
22107
+ let outputBytes = 0;
22108
+ let requestId = 0;
22109
+ let expectedId = 0;
22110
+ let expectedMethod = "";
22111
+ const finish = (catalog) => {
22112
+ if (settled)
22113
+ return;
22114
+ settled = true;
22115
+ clearTimeout(timer);
22116
+ const cleanup = options.cleanup ?? cleanupProbeProcess;
22117
+ Promise.resolve().then(() => cleanup(processHandle)).catch(() => {}).finally(() => resolve2(catalog));
22118
+ };
22119
+ const request = (method, params) => {
22120
+ if (settled)
22121
+ return;
22122
+ const stdin = processHandle.stdin;
22123
+ if (!stdin || stdin.destroyed || stdin.writableEnded || stdin.writable === false)
22124
+ return finish();
22125
+ expectedId = ++requestId;
22126
+ expectedMethod = method;
22127
+ try {
22128
+ stdin.write(`${jsonRpcRequest(method, params, expectedId)}
22129
+ `);
22130
+ } catch {
22131
+ finish();
22132
+ }
22133
+ };
22134
+ const onLine = (line) => {
22135
+ const parsed = tryParseJsonLine(line);
22136
+ const message2 = record2(parsed);
22137
+ if (!message2)
22138
+ return finish();
22139
+ if (message2.id !== expectedId)
22140
+ return;
22141
+ if (message2.error !== undefined)
22142
+ return finish();
22143
+ if (!Object.prototype.hasOwnProperty.call(message2, "result"))
22144
+ return finish();
22145
+ if (expectedMethod === "authenticate") {
22146
+ request("session/new", { cwd, mcpServers: [] });
22147
+ return;
22148
+ }
22149
+ const result = record2(message2.result);
22150
+ if (!result)
22151
+ return finish();
22152
+ if (expectedMethod === "initialize") {
22153
+ const authMethods = Array.isArray(result.authMethods) ? result.authMethods : [];
22154
+ if (result.protocolVersion !== ACP_PROTOCOL_VERSION || !authMethods.some((method) => record2(method)?.id === AUTH_METHOD_ID))
22155
+ return finish();
22156
+ request("authenticate", { methodId: AUTH_METHOD_ID });
22157
+ return;
22158
+ }
22159
+ if (expectedMethod !== "session/new" || typeof result.sessionId !== "string" || !result.sessionId.trim())
22160
+ return finish();
22161
+ finish(parseCursorAcpModelCatalog(result));
22162
+ };
22163
+ const timer = setTimeout(() => finish(), options.timeoutMs ?? CATALOG_PROBE_TIMEOUT_MS);
22164
+ timer.unref?.();
22165
+ processHandle.stdout?.on("data", (chunk2) => {
22166
+ if (settled)
22167
+ return;
22168
+ const text2 = chunk2.toString();
22169
+ outputBytes += Buffer.byteLength(text2);
22170
+ if (outputBytes > (options.outputMaxBytes ?? CATALOG_PROBE_OUTPUT_MAX_BYTES))
22171
+ return finish();
22172
+ buffer += text2;
22173
+ const lines = buffer.split(`
22174
+ `);
22175
+ buffer = lines.pop() ?? "";
22176
+ for (const line of lines)
22177
+ if (line.trim())
22178
+ onLine(line);
22179
+ });
22180
+ processHandle.stderr?.on("data", (chunk2) => {
22181
+ if (settled)
22182
+ return;
22183
+ outputBytes += Buffer.byteLength(chunk2.toString());
22184
+ if (outputBytes > (options.outputMaxBytes ?? CATALOG_PROBE_OUTPUT_MAX_BYTES))
22185
+ finish();
22186
+ });
22187
+ processHandle.on("error", () => finish());
22188
+ processHandle.on("exit", () => finish());
22189
+ request("initialize", {
22190
+ protocolVersion: ACP_PROTOCOL_VERSION,
22191
+ clientCapabilities: {
22192
+ fs: { readTextFile: false, writeTextFile: false },
22193
+ terminal: false
22194
+ },
22195
+ clientInfo: { name: "alook-agent-driver-probe", version: "0.1.25" }
22196
+ });
22197
+ });
22198
+ }
22199
+
22200
+ // agent-driver/dist/adapters/cursor/acp-lane.js
22201
+ var ACP_PROTOCOL_VERSION2 = 1;
22202
+ var HANDSHAKE_TIMEOUT_MS = 15000;
22203
+ var AUTH_METHOD_ID2 = "cursor_login";
21787
22204
  var PROMPT_STOP_REASONS = new Set([
21788
22205
  "end_turn",
21789
22206
  "max_tokens",
@@ -21807,16 +22224,16 @@ class CursorAcpRpcError extends Error {
21807
22224
  this.code = code;
21808
22225
  }
21809
22226
  }
21810
- function record2(value) {
22227
+ function record3(value) {
21811
22228
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
21812
22229
  }
21813
22230
  function safeLabel(value) {
21814
22231
  return typeof value === "string" && /^[a-z0-9_-]{1,64}$/i.test(value) ? value : "unknown";
21815
22232
  }
21816
22233
  function rpcErrorMessage(error51) {
21817
- const payload = record2(error51);
22234
+ const payload = record3(error51);
21818
22235
  const message2 = typeof payload?.message === "string" && payload.message.trim() ? payload.message : "Cursor ACP request failed";
21819
- const data = record2(payload?.data);
22236
+ const data = record3(payload?.data);
21820
22237
  const detail = typeof data?.message === "string" && data.message.trim() ? data.message : undefined;
21821
22238
  return detail ? `${message2}: ${detail}` : message2;
21822
22239
  }
@@ -21824,26 +22241,6 @@ function isMissingSessionError(error51) {
21824
22241
  const message2 = error51 instanceof Error ? error51.message : String(error51);
21825
22242
  return /\bsession\b.*\b(not found|missing|unknown|invalid)\b/i.test(message2) || /\b(not found|missing|unknown|invalid)\b.*\bsession\b/i.test(message2);
21826
22243
  }
21827
- function flattenSelectOptions(value) {
21828
- if (!Array.isArray(value))
21829
- return [];
21830
- const out = [];
21831
- for (const item of value) {
21832
- if (Array.isArray(item)) {
21833
- out.push(...flattenSelectOptions(item));
21834
- continue;
21835
- }
21836
- const candidate = record2(item);
21837
- if (!candidate)
21838
- continue;
21839
- if (typeof candidate.value === "string") {
21840
- out.push({ value: candidate.value, ...typeof candidate.name === "string" ? { name: candidate.name } : {} });
21841
- }
21842
- if (Array.isArray(candidate.options))
21843
- out.push(...flattenSelectOptions(candidate.options));
21844
- }
21845
- return out;
21846
- }
21847
22244
 
21848
22245
  class CursorAcpLane {
21849
22246
  factory;
@@ -21967,30 +22364,30 @@ class CursorAcpLane {
21967
22364
  }
21968
22365
  }
21969
22366
  async handshake(ctx) {
21970
- const initialize = record2(await this.call("initialize", {
21971
- protocolVersion: ACP_PROTOCOL_VERSION,
22367
+ const initialize = record3(await this.call("initialize", {
22368
+ protocolVersion: ACP_PROTOCOL_VERSION2,
21972
22369
  clientCapabilities: {
21973
22370
  fs: { readTextFile: false, writeTextFile: false },
21974
22371
  terminal: false
21975
22372
  },
21976
22373
  clientInfo: { name: "alook-agent-driver", version: "0.1.14" }
21977
22374
  }));
21978
- if (initialize?.protocolVersion !== ACP_PROTOCOL_VERSION) {
22375
+ if (initialize?.protocolVersion !== ACP_PROTOCOL_VERSION2) {
21979
22376
  throw new CursorAcpIncompatibleError("Installed Cursor ACP does not support protocol version 1");
21980
22377
  }
21981
- const capabilities = record2(initialize.agentCapabilities);
22378
+ const capabilities = record3(initialize.agentCapabilities);
21982
22379
  if (capabilities?.loadSession !== true) {
21983
22380
  throw new CursorAcpIncompatibleError("Installed Cursor ACP does not support persistent session loading");
21984
22381
  }
21985
22382
  const authMethods = Array.isArray(initialize.authMethods) ? initialize.authMethods : [];
21986
- if (!authMethods.some((method) => record2(method)?.id === AUTH_METHOD_ID)) {
22383
+ if (!authMethods.some((method) => record3(method)?.id === AUTH_METHOD_ID2)) {
21987
22384
  throw new CursorAcpIncompatibleError("Installed Cursor ACP does not expose Cursor login authentication");
21988
22385
  }
21989
- await this.call("authenticate", { methodId: AUTH_METHOD_ID });
22386
+ await this.call("authenticate", { methodId: AUTH_METHOD_ID2 });
21990
22387
  let session2;
21991
22388
  if (ctx.config.sessionId) {
21992
22389
  try {
21993
- session2 = record2(await this.call("session/load", {
22390
+ session2 = record3(await this.call("session/load", {
21994
22391
  sessionId: ctx.config.sessionId,
21995
22392
  cwd: ctx.workingDirectory,
21996
22393
  mcpServers: []
@@ -22002,15 +22399,25 @@ class CursorAcpLane {
22002
22399
  throw error51;
22003
22400
  }
22004
22401
  } else {
22005
- session2 = record2(await this.call("session/new", { cwd: ctx.workingDirectory, mcpServers: [] }));
22402
+ session2 = record3(await this.call("session/new", { cwd: ctx.workingDirectory, mcpServers: [] }));
22006
22403
  }
22007
- if (!session2 || typeof session2.sessionId !== "string" || !session2.sessionId.trim()) {
22404
+ if (!session2)
22405
+ throw new Error("Cursor ACP did not return a valid session response");
22406
+ const returnedSessionId = session2.sessionId;
22407
+ if (returnedSessionId !== undefined && (typeof returnedSessionId !== "string" || !returnedSessionId.trim())) {
22008
22408
  throw new Error("Cursor ACP did not return a valid session id");
22009
22409
  }
22010
- if (ctx.config.sessionId && session2.sessionId !== ctx.config.sessionId) {
22011
- throw new CursorAcpResetRequiredError("Cursor ACP loaded a different session; reset this agent before continuing");
22410
+ if (ctx.config.sessionId) {
22411
+ if (returnedSessionId !== undefined && returnedSessionId !== ctx.config.sessionId) {
22412
+ throw new CursorAcpResetRequiredError("Cursor ACP loaded a different session; reset this agent before continuing");
22413
+ }
22414
+ this.sessionId = ctx.config.sessionId;
22415
+ } else {
22416
+ if (typeof returnedSessionId !== "string") {
22417
+ throw new Error("Cursor ACP did not return a valid session id");
22418
+ }
22419
+ this.sessionId = returnedSessionId;
22012
22420
  }
22013
- this.sessionId = session2.sessionId;
22014
22421
  await this.configureModel(session2, ctx);
22015
22422
  }
22016
22423
  async configureModel(session2, ctx) {
@@ -22018,24 +22425,30 @@ class CursorAcpLane {
22018
22425
  if (!requestedModel)
22019
22426
  return;
22020
22427
  const configOptions = Array.isArray(session2.configOptions) ? session2.configOptions : [];
22021
- const modelConfig = configOptions.map(record2).find((option) => option?.id === "model") ?? null;
22428
+ const modelConfig = configOptions.map(record3).find((option) => option?.id === "model") ?? null;
22022
22429
  if (!modelConfig) {
22023
22430
  throw new CursorAcpIncompatibleError("Installed Cursor ACP does not support model configuration");
22024
22431
  }
22025
- const options = flattenSelectOptions(modelConfig.options);
22026
- const match = options.find((option) => option.value === requestedModel) ?? options.find((option) => option.name === requestedModel);
22432
+ const options = flattenCursorAcpSelectOptions(modelConfig.options);
22433
+ const match = options.find((option) => option.value === requestedModel);
22027
22434
  if (!match) {
22028
22435
  throw new CursorAcpIncompatibleError(`Configured Cursor model is unavailable through ACP: ${requestedModel}`);
22029
22436
  }
22437
+ let response;
22030
22438
  try {
22031
- await this.call("session/set_config_option", {
22439
+ response = record3(await this.call("session/set_config_option", {
22032
22440
  sessionId: this.sessionId,
22033
22441
  configId: "model",
22034
22442
  value: match.value
22035
- });
22443
+ }));
22036
22444
  } catch {
22037
22445
  throw new CursorAcpIncompatibleError("Installed Cursor ACP rejected model configuration");
22038
22446
  }
22447
+ const confirmedOptions = Array.isArray(response?.configOptions) ? response.configOptions : [];
22448
+ const confirmedModel = confirmedOptions.map(record3).find((option) => option?.id === "model") ?? null;
22449
+ if (confirmedModel?.currentValue !== match.value) {
22450
+ throw new CursorAcpIncompatibleError("Cursor ACP did not confirm the exact configured model");
22451
+ }
22039
22452
  }
22040
22453
  admitPrompt(text2) {
22041
22454
  if (!this.sessionId)
@@ -22071,7 +22484,7 @@ class CursorAcpLane {
22071
22484
  completePrompt(active, value) {
22072
22485
  if (this.activePrompt?.requestId !== active.requestId)
22073
22486
  return;
22074
- const result = record2(value);
22487
+ const result = record3(value);
22075
22488
  if (!result || typeof result.stopReason !== "string" || !PROMPT_STOP_REASONS.has(result.stopReason)) {
22076
22489
  this.failPrompt(active, new Error("Cursor ACP prompt response did not contain a supported stopReason"));
22077
22490
  return;
@@ -22213,7 +22626,7 @@ class CursorAcpLane {
22213
22626
  });
22214
22627
  }
22215
22628
  handleMessage(value) {
22216
- const message2 = record2(value);
22629
+ const message2 = record3(value);
22217
22630
  if (!message2 || message2.jsonrpc !== "2.0") {
22218
22631
  this.protocolFailure("Cursor ACP emitted an invalid JSON-RPC message");
22219
22632
  return;
@@ -22241,7 +22654,7 @@ class CursorAcpLane {
22241
22654
  if (pending.kind === "prompt") {
22242
22655
  this.pending.delete(id);
22243
22656
  if (message2.error !== undefined) {
22244
- const payload = record2(message2.error);
22657
+ const payload = record3(message2.error);
22245
22658
  this.failPrompt(pending.active, new CursorAcpRpcError(pending.method, typeof payload?.code === "number" ? payload.code : undefined, rpcErrorMessage(message2.error)));
22246
22659
  } else if (!("result" in message2)) {
22247
22660
  this.failPrompt(pending.active, new Error("Cursor ACP response omitted result"));
@@ -22251,7 +22664,7 @@ class CursorAcpLane {
22251
22664
  return;
22252
22665
  }
22253
22666
  if (message2.error !== undefined) {
22254
- const payload = record2(message2.error);
22667
+ const payload = record3(message2.error);
22255
22668
  this.settleRequest(id, false, new CursorAcpRpcError(pending.method, typeof payload?.code === "number" ? payload.code : undefined, rpcErrorMessage(message2.error)));
22256
22669
  return;
22257
22670
  }
@@ -22283,9 +22696,9 @@ class CursorAcpLane {
22283
22696
  this.diagnostic("warning", `Unsupported Cursor ACP client request: ${safeLabel(method)}`);
22284
22697
  return;
22285
22698
  }
22286
- const payload = record2(params);
22699
+ const payload = record3(params);
22287
22700
  const sameSession = payload?.sessionId === this.sessionId;
22288
- const options = Array.isArray(payload?.options) ? payload.options.map(record2).filter(Boolean) : [];
22701
+ const options = Array.isArray(payload?.options) ? payload.options.map(record3).filter(Boolean) : [];
22289
22702
  const allowOnce = options.find((option) => option.kind === "allow_once" && typeof option.optionId === "string" && option.optionId.trim().length > 0);
22290
22703
  if (!this.ready || !this.activePrompt || !sameSession || !allowOnce) {
22291
22704
  this.write({ jsonrpc: "2.0", id, result: { outcome: { outcome: "cancelled" } } });
@@ -22306,7 +22719,7 @@ class CursorAcpLane {
22306
22719
  this.diagnostic("warning", `Unsupported Cursor ACP notification: ${safeLabel(method)}`);
22307
22720
  }
22308
22721
  handleSessionUpdate(params) {
22309
- const payload = record2(params);
22722
+ const payload = record3(params);
22310
22723
  if (!payload || payload.sessionId !== this.sessionId) {
22311
22724
  this.diagnostic("warning", "Cursor ACP emitted an update for a different session");
22312
22725
  return;
@@ -22315,18 +22728,18 @@ class CursorAcpLane {
22315
22728
  this.diagnostic("warning", "Cursor ACP emitted a session update without an active prompt");
22316
22729
  return;
22317
22730
  }
22318
- const update = record2(payload.update) ?? {};
22731
+ const update = record3(payload.update) ?? {};
22319
22732
  const updateType = update?.sessionUpdate;
22320
22733
  switch (updateType) {
22321
22734
  case "agent_message_chunk": {
22322
- const content = record2(update.content);
22735
+ const content = record3(update.content);
22323
22736
  if (content?.type === "text" && typeof content.text === "string") {
22324
22737
  this.events.emit("runtime_event", { kind: "assistant_message_delta", text: content.text });
22325
22738
  }
22326
22739
  return;
22327
22740
  }
22328
22741
  case "agent_thought_chunk": {
22329
- const content = record2(update.content);
22742
+ const content = record3(update.content);
22330
22743
  if (content?.type === "text" && typeof content.text === "string") {
22331
22744
  this.events.emit("runtime_event", { kind: "assistant_reasoning_delta", text: content.text });
22332
22745
  }
@@ -22398,6 +22811,7 @@ class CursorAcpLane {
22398
22811
 
22399
22812
  // agent-driver/dist/adapters/cursor/index.js
22400
22813
  class CursorDriver {
22814
+ catalogProbe;
22401
22815
  id = "cursor";
22402
22816
  instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
22403
22817
  execution = {
@@ -22406,8 +22820,23 @@ class CursorDriver {
22406
22820
  wakeStart: "immediate",
22407
22821
  terminalOwnership: "transport_request"
22408
22822
  };
22409
- probe(command) {
22410
- return probeCliRuntime("cursor-agent", {}, command);
22823
+ constructor(catalogProbe = probeCursorAcpCatalog) {
22824
+ this.catalogProbe = catalogProbe;
22825
+ }
22826
+ async probe(command) {
22827
+ const result = probeCliRuntime("cursor-agent", {}, command);
22828
+ if (result.status !== "healthy")
22829
+ return result;
22830
+ let reasoning;
22831
+ try {
22832
+ reasoning = await this.catalogProbe(command);
22833
+ } catch {
22834
+ reasoning = undefined;
22835
+ }
22836
+ return {
22837
+ ...result,
22838
+ reasoning
22839
+ };
22411
22840
  }
22412
22841
  async openLane(ctx, options) {
22413
22842
  return new CursorAcpLane(this, ctx, { onRawStdoutLine: options?.onRawStdoutLine });
@@ -22464,7 +22893,7 @@ class OpenCodeHttpError extends Error {
22464
22893
  this.status = status;
22465
22894
  }
22466
22895
  }
22467
- function record3(value) {
22896
+ function record4(value) {
22468
22897
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
22469
22898
  }
22470
22899
  function safeLabel2(value) {
@@ -22511,7 +22940,7 @@ function parseModelRef(model) {
22511
22940
  return { providerID: model.slice(0, slash), id: model.slice(slash + 1) };
22512
22941
  }
22513
22942
  function messageFromError(value) {
22514
- const payload = record3(value);
22943
+ const payload = record4(value);
22515
22944
  const message2 = typeof payload?.message === "string" && payload.message.trim() ? payload.message : undefined;
22516
22945
  return message2 ? "OpenCode turn failed" : "OpenCode reported an inconsistent turn outcome";
22517
22946
  }
@@ -22553,6 +22982,7 @@ class OpenCodeServiceLane {
22553
22982
  lastDurableSeq = 0;
22554
22983
  durableSeqById = new Map;
22555
22984
  durableIdBySeq = new Map;
22985
+ usageProjector = new SettledUsageProjector;
22556
22986
  toolNames = new Map;
22557
22987
  handledPermissions = new Set;
22558
22988
  permissionFlights = new Map;
@@ -22845,7 +23275,7 @@ class OpenCodeServiceLane {
22845
23275
  const healthTimeoutMs = Math.max(1, Math.min(1000, deadline - Date.now(), this.options.requestTimeoutMs ?? REQUEST_TIMEOUT_MS));
22846
23276
  const { response, body } = await this.fetchJsonWithTimeout("/global/health", { method: "GET" }, "health", healthTimeoutMs);
22847
23277
  if (response.ok) {
22848
- const health = record3(body);
23278
+ const health = record4(body);
22849
23279
  if (health?.healthy !== true || health.version !== SUPPORTED_VERSION) {
22850
23280
  throw new OpenCodeIncompatibleError(`Installed OpenCode service must be version ${SUPPORTED_VERSION}`);
22851
23281
  }
@@ -22866,8 +23296,8 @@ class OpenCodeServiceLane {
22866
23296
  const { response, body } = await this.fetchJsonWithTimeout("/doc", { method: "GET" }, "OpenAPI");
22867
23297
  if (!response.ok)
22868
23298
  throw new OpenCodeIncompatibleError("Installed OpenCode service does not expose its OpenAPI document");
22869
- const document = record3(body);
22870
- const paths = record3(document?.paths);
23299
+ const document = record4(body);
23300
+ const paths = record4(document?.paths);
22871
23301
  const required2 = [
22872
23302
  "/api/session",
22873
23303
  "/api/session/active",
@@ -22880,7 +23310,7 @@ class OpenCodeServiceLane {
22880
23310
  "/api/session/{sessionID}/permission/{requestID}/reply",
22881
23311
  "/api/event"
22882
23312
  ];
22883
- if (!paths || required2.some((path7) => !record3(paths[path7]))) {
23313
+ if (!paths || required2.some((path7) => !record4(paths[path7]))) {
22884
23314
  throw new OpenCodeIncompatibleError("Installed OpenCode service is missing required v2 session capabilities");
22885
23315
  }
22886
23316
  }
@@ -22893,7 +23323,7 @@ class OpenCodeServiceLane {
22893
23323
  }
22894
23324
  if (!response2.ok)
22895
23325
  throw new OpenCodeHttpError(response2.status, "session resume");
22896
- const session3 = record3(record3(body2)?.data);
23326
+ const session3 = record4(record4(body2)?.data);
22897
23327
  if (session3?.id !== resumeId) {
22898
23328
  throw new OpenCodeResetRequiredError("OpenCode v2 returned a different resumed session; reset this agent before continuing");
22899
23329
  }
@@ -22914,8 +23344,8 @@ class OpenCodeServiceLane {
22914
23344
  }, "session create");
22915
23345
  if (!response.ok)
22916
23346
  throw new OpenCodeHttpError(response.status, "session create");
22917
- const payload = record3(responseBody);
22918
- const session2 = record3(payload?.data);
23347
+ const payload = record4(responseBody);
23348
+ const session2 = record4(payload?.data);
22919
23349
  if (typeof session2?.id !== "string" || !/^ses/.test(session2.id)) {
22920
23350
  throw new Error("OpenCode v2 did not return a valid session id");
22921
23351
  }
@@ -23072,7 +23502,7 @@ class OpenCodeServiceLane {
23072
23502
  const { response, body: responseBody } = await this.fetchJsonWithTimeout(`/api/session/${encodeURIComponent(this.sessionId)}/history?after=${historyCursor}&limit=${HISTORY_PAGE_LIMIT}`, { method: "GET" }, "session history");
23073
23503
  if (!response.ok)
23074
23504
  throw new OpenCodeHttpError(response.status, "session history");
23075
- const body = record3(responseBody);
23505
+ const body = record4(responseBody);
23076
23506
  if (!Array.isArray(body?.data) || typeof body.hasMore !== "boolean") {
23077
23507
  throw new OpenCodeProtocolError("OpenCode session history returned an invalid page");
23078
23508
  }
@@ -23090,10 +23520,11 @@ class OpenCodeServiceLane {
23090
23520
  return run;
23091
23521
  }
23092
23522
  async handleDurableEvent(value, project) {
23093
- const event = record3(value);
23094
- const durable = record3(event?.durable);
23095
- const data = record3(event?.data);
23096
- if (!event || typeof event.id !== "string" || typeof event.type !== "string" || !durable || durable.aggregateID !== this.sessionId || !Number.isInteger(durable.seq) || Number(durable.seq) < 0 || data?.sessionID !== this.sessionId) {
23523
+ const event = record4(value);
23524
+ const durable = record4(event?.durable);
23525
+ const data = record4(event?.data);
23526
+ const backendSessionId = this.sessionId;
23527
+ if (!event || !backendSessionId || typeof event.id !== "string" || typeof event.type !== "string" || !durable || durable.aggregateID !== this.sessionId || !Number.isInteger(durable.seq) || Number(durable.seq) < 0 || data?.sessionID !== this.sessionId) {
23097
23528
  throw new OpenCodeProtocolError("OpenCode session stream emitted an invalid durable event");
23098
23529
  }
23099
23530
  const seq = Number(durable.seq);
@@ -23182,22 +23613,28 @@ class OpenCodeServiceLane {
23182
23613
  ...!successful ? { message: "OpenCode reported an unsupported final step outcome" } : {}
23183
23614
  });
23184
23615
  }
23185
- const tokens = record3(data.tokens);
23186
- if (tokens && data.finish !== "tool-calls") {
23187
- const cache = record3(tokens.cache);
23188
- const metric2 = (value2) => typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0 ? value2 : null;
23189
- const cacheParts = [cache?.read, cache?.write].filter((value2) => typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0);
23190
- const cacheTotal = cacheParts.reduce((sum, value2) => sum + value2, 0);
23191
- this.events.emit("runtime_event", {
23192
- kind: "telemetry",
23193
- name: "token_usage",
23616
+ const tokens = record4(data.tokens);
23617
+ if (tokens) {
23618
+ const cache = record4(tokens.cache);
23619
+ const identity = {
23620
+ runtime: "opencode",
23621
+ backendSessionId,
23622
+ providerRecordId: event.id
23623
+ };
23624
+ const usage = this.usageProjector.project({
23625
+ ...identity,
23194
23626
  source: "opencode.v2",
23195
- usage: {
23196
- input: metric2(tokens.input),
23197
- output: metric2(tokens.output),
23198
- cache: cacheParts.length > 0 && Number.isSafeInteger(cacheTotal) ? cacheTotal : null
23199
- }
23627
+ input: tokens.input,
23628
+ output: tokens.output,
23629
+ reasoning: tokens.reasoning,
23630
+ cacheRead: cache?.read,
23631
+ cacheWrite: cache?.write,
23632
+ inputIncludesCache: false,
23633
+ outputIncludesReasoning: false
23200
23634
  });
23635
+ if (usage)
23636
+ this.events.emit("runtime_event", usage);
23637
+ this.usageProjector.release(identity);
23201
23638
  }
23202
23639
  break;
23203
23640
  }
@@ -23211,8 +23648,8 @@ class OpenCodeServiceLane {
23211
23648
  return seq;
23212
23649
  }
23213
23650
  async handleLiveEvent(value) {
23214
- const event = record3(value);
23215
- const data = record3(event?.data);
23651
+ const event = record4(value);
23652
+ const data = record4(event?.data);
23216
23653
  if (event?.type !== "permission.v2.asked" || data?.sessionID !== this.sessionId)
23217
23654
  return;
23218
23655
  if (typeof data.id !== "string" || !/^per/.test(data.id)) {
@@ -23226,11 +23663,11 @@ class OpenCodeServiceLane {
23226
23663
  const { response, body: responseBody } = await this.fetchJsonWithTimeout(`/api/session/${encodeURIComponent(this.sessionId)}/permission`, { method: "GET" }, "permission list");
23227
23664
  if (!response.ok)
23228
23665
  throw new OpenCodeHttpError(response.status, "permission list");
23229
- const body = record3(responseBody);
23666
+ const body = record4(responseBody);
23230
23667
  if (!Array.isArray(body?.data))
23231
23668
  throw new OpenCodeProtocolError("OpenCode permission list returned invalid data");
23232
23669
  for (const item of body.data) {
23233
- const permission = record3(item);
23670
+ const permission = record4(item);
23234
23671
  if (permission?.sessionID === this.sessionId && typeof permission.id === "string") {
23235
23672
  await this.replyPermission(permission.id);
23236
23673
  }
@@ -23297,8 +23734,8 @@ class OpenCodeServiceLane {
23297
23734
  }, "prompt admission");
23298
23735
  if (!response.ok)
23299
23736
  throw new OpenCodeHttpError(response.status, "prompt admission");
23300
- const body = record3(responseBody);
23301
- const admitted = record3(body?.data);
23737
+ const body = record4(responseBody);
23738
+ const admitted = record4(body?.data);
23302
23739
  if (admitted?.id !== messageId || admitted.sessionID !== this.sessionId || admitted.delivery !== delivery || !Number.isInteger(admitted.admittedSeq) || Number(admitted.admittedSeq) < 0) {
23303
23740
  throw new OpenCodeProtocolError("OpenCode prompt admission returned an invalid receipt");
23304
23741
  }
@@ -23365,8 +23802,8 @@ class OpenCodeServiceLane {
23365
23802
  const { response, body: responseBody } = await this.fetchJsonWithTimeout("/api/session/active", { method: "GET" }, "active session query");
23366
23803
  if (!response.ok)
23367
23804
  throw new OpenCodeHttpError(response.status, "active session query");
23368
- const body = record3(responseBody);
23369
- const active = record3(body?.data);
23805
+ const body = record4(responseBody);
23806
+ const active = record4(body?.data);
23370
23807
  if (!active)
23371
23808
  throw new OpenCodeProtocolError("OpenCode active session query returned invalid data");
23372
23809
  if (!this.barrierStillCurrent(root, identity, generation))
@@ -23571,6 +24008,7 @@ function createOpenCodeMessageId() {
23571
24008
  }
23572
24009
 
23573
24010
  class OpenCodeDriver {
24011
+ outputProbe;
23574
24012
  id = "opencode";
23575
24013
  instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
23576
24014
  execution = {
@@ -23579,8 +24017,19 @@ class OpenCodeDriver {
23579
24017
  wakeStart: "immediate",
23580
24018
  terminalOwnership: "transport_request"
23581
24019
  };
24020
+ constructor(outputProbe = probeCommandOutput) {
24021
+ this.outputProbe = outputProbe;
24022
+ }
23582
24023
  probe(command) {
23583
- return probeCliRuntime("opencode", {}, command);
24024
+ const result = probeCliRuntime("opencode", {}, command);
24025
+ if (result.status !== "healthy")
24026
+ return result;
24027
+ const spec = resolveSpawnSpec("opencode", ["models", "--pure"], command);
24028
+ const output = this.outputProbe(spec.command, spec.args);
24029
+ return {
24030
+ ...result,
24031
+ reasoning: output.ok ? parseOpenCodeModelCatalog(output.output) : undefined
24032
+ };
23584
24033
  }
23585
24034
  beginTurn() {
23586
24035
  return createOpenCodeMessageId();
@@ -23834,6 +24283,7 @@ function createPiSessionDependencies(ctx, loadSdk = loadPiSdkModule) {
23834
24283
 
23835
24284
  // agent-driver/dist/adapters/pi/index.js
23836
24285
  var PI_SDK_PACKAGE_NAME2 = "@earendil-works/pi-coding-agent";
24286
+ var PI_MODEL_PROBE_TIMEOUT_MS = 5000;
23837
24287
  function isPiSdkPackageJson(pkgJsonPath) {
23838
24288
  if (!existsSync3(pkgJsonPath))
23839
24289
  return false;
@@ -23903,6 +24353,11 @@ function readPiSdkVersion() {
23903
24353
  } catch {}
23904
24354
  return resolvePiSdkVersionFromPath();
23905
24355
  }
24356
+ function piUsageState(state) {
24357
+ state.usageProjector ??= new SettledUsageProjector;
24358
+ state.pendingUsageRecordIds ??= new Set;
24359
+ return { projector: state.usageProjector, pending: state.pendingUsageRecordIds };
24360
+ }
23906
24361
  function mapPiSdkEvent(event, sessionId, state) {
23907
24362
  if (event?.type === "message_update") {
23908
24363
  const d = event.assistantMessageEvent ?? {};
@@ -23923,6 +24378,35 @@ function mapPiSdkEvent(event, sessionId, state) {
23923
24378
  }
23924
24379
  }
23925
24380
  switch (event?.type) {
24381
+ case "message_end": {
24382
+ const message2 = event.message;
24383
+ if (message2?.role !== "assistant" || !message2.usage)
24384
+ return [];
24385
+ state.usageRecordSequence = (state.usageRecordSequence ?? 0) + 1;
24386
+ const providerRecordId = typeof message2.responseId === "string" && message2.responseId ? message2.responseId : `live:${message2.timestamp ?? "unknown"}:${state.usageRecordSequence}`;
24387
+ const { projector, pending } = piUsageState(state);
24388
+ const identity = { runtime: "pi", backendSessionId: sessionId, providerRecordId };
24389
+ const usage = projector.project({
24390
+ ...identity,
24391
+ source: "pi_message_end",
24392
+ input: message2.usage.input,
24393
+ output: message2.usage.output,
24394
+ cacheRead: message2.usage.cacheRead,
24395
+ cacheWrite: message2.usage.cacheWrite,
24396
+ inputIncludesCache: false,
24397
+ outputIncludesReasoning: true
24398
+ });
24399
+ pending.add(providerRecordId);
24400
+ return usage ? [usage] : [];
24401
+ }
24402
+ case "turn_end": {
24403
+ const { projector, pending } = piUsageState(state);
24404
+ for (const providerRecordId of pending) {
24405
+ projector.release({ runtime: "pi", backendSessionId: sessionId, providerRecordId });
24406
+ }
24407
+ pending.clear();
24408
+ return [];
24409
+ }
23926
24410
  case "auto_retry_start":
23927
24411
  return [{ kind: "runtime_recovery", stage: "retrying", source: "pi_auto_retry" }];
23928
24412
  case "auto_retry_end":
@@ -23942,6 +24426,8 @@ function mapPiSdkEvent(event, sessionId, state) {
23942
24426
 
23943
24427
  class PiDriver {
23944
24428
  dependenciesFor;
24429
+ loadSdk;
24430
+ readVersion;
23945
24431
  id = "pi";
23946
24432
  instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
23947
24433
  execution = {
@@ -23952,15 +24438,36 @@ class PiDriver {
23952
24438
  };
23953
24439
  sessionId = null;
23954
24440
  terminalSequence = 0;
23955
- constructor(dependenciesFor = createPiSessionDependencies) {
24441
+ constructor(dependenciesFor = createPiSessionDependencies, loadSdk = loadPiSdkModule, readVersion = readPiSdkVersion) {
23956
24442
  this.dependenciesFor = dependenciesFor;
24443
+ this.loadSdk = loadSdk;
24444
+ this.readVersion = readVersion;
23957
24445
  }
23958
- probe() {
23959
- const version3 = readPiSdkVersion();
24446
+ async probe() {
24447
+ const version3 = this.readVersion();
23960
24448
  if (!version3) {
23961
24449
  return { status: "unhealthy", lastError: "sdk_not_installed" };
23962
24450
  }
23963
- return { status: "healthy", version: version3 };
24451
+ let timer;
24452
+ try {
24453
+ const reasoning = await Promise.race([
24454
+ this.loadSdk().then(async (sdk) => {
24455
+ const authStorage = sdk.AuthStorage.create();
24456
+ const registry2 = sdk.ModelRegistry.create(authStorage);
24457
+ return parsePiModelCatalog(await registry2.getAvailable());
24458
+ }),
24459
+ new Promise((resolve3) => {
24460
+ timer = setTimeout(() => resolve3(undefined), PI_MODEL_PROBE_TIMEOUT_MS);
24461
+ timer.unref?.();
24462
+ })
24463
+ ]);
24464
+ return { status: "healthy", version: version3, reasoning };
24465
+ } catch {
24466
+ return { status: "healthy", version: version3, reasoning: undefined };
24467
+ } finally {
24468
+ if (timer)
24469
+ clearTimeout(timer);
24470
+ }
23964
24471
  }
23965
24472
  async openLane(ctx) {
23966
24473
  const deps = this.dependenciesFor(ctx);
@@ -25868,14 +26375,14 @@ function createLogger2(options = {}) {
25868
26375
  `));
25869
26376
  const err = options.err ?? ((line) => process.stderr.write(line + `
25870
26377
  `));
25871
- const record4 = options.record;
26378
+ const record5 = options.record;
25872
26379
  const emit = (level, message2, data) => {
25873
26380
  if (LEVEL_RANK[level] < minRank)
25874
26381
  return;
25875
26382
  const time3 = now();
25876
26383
  const line = `${time3} ${header} ${level.toUpperCase().padEnd(5)} ${message2}${formatData(data)}`;
25877
26384
  try {
25878
- record4?.({ time: time3, header, level, message: message2, fields: recordFields(data) });
26385
+ record5?.({ time: time3, header, level, message: message2, fields: recordFields(data) });
25879
26386
  } catch {}
25880
26387
  (level === "warn" || level === "error" ? err : out)(line);
25881
26388
  };
@@ -27019,20 +27526,20 @@ function parseLocalMessageReminderBody(body, agentId) {
27019
27526
  }
27020
27527
  if (!value || typeof value !== "object" || Array.isArray(value))
27021
27528
  return null;
27022
- const record4 = value;
27023
- if (Object.keys(record4).sort().join(",") !== "channel,remindAfterMs,sentSeq")
27529
+ const record5 = value;
27530
+ if (Object.keys(record5).sort().join(",") !== "channel,remindAfterMs,sentSeq")
27024
27531
  return null;
27025
- if (typeof record4.channel !== "string" || !isCanonicalChannelScope(record4.channel))
27532
+ if (typeof record5.channel !== "string" || !isCanonicalChannelScope(record5.channel))
27026
27533
  return null;
27027
- if (!Number.isSafeInteger(record4.sentSeq) || record4.sentSeq < 1)
27534
+ if (!Number.isSafeInteger(record5.sentSeq) || record5.sentSeq < 1)
27028
27535
  return null;
27029
- if (!Number.isSafeInteger(record4.remindAfterMs) || record4.remindAfterMs !== 0 && record4.remindAfterMs < LOCAL_MESSAGE_REMINDER_MIN_MS || record4.remindAfterMs > LOCAL_MESSAGE_REMINDER_MAX_MS)
27536
+ if (!Number.isSafeInteger(record5.remindAfterMs) || record5.remindAfterMs !== 0 && record5.remindAfterMs < LOCAL_MESSAGE_REMINDER_MIN_MS || record5.remindAfterMs > LOCAL_MESSAGE_REMINDER_MAX_MS)
27030
27537
  return null;
27031
27538
  return {
27032
27539
  agentId,
27033
- channel: record4.channel,
27034
- sentSeq: record4.sentSeq,
27035
- remindAfterMs: record4.remindAfterMs
27540
+ channel: record5.channel,
27541
+ sentSeq: record5.sentSeq,
27542
+ remindAfterMs: record5.remindAfterMs
27036
27543
  };
27037
27544
  }
27038
27545
  async function handleLocalMessageReminder(req, res, agentId, onArm) {
@@ -27378,13 +27885,13 @@ function reduceManager(state, event) {
27378
27885
  const existing = state.agents[event.agentId];
27379
27886
  if (!existing || existing.execution.sessionInstanceId !== event.sessionInstanceId)
27380
27887
  return { state, effects: [] };
27381
- const record4 = existing.pendingAdmissions.find((entry) => entry.sessionInstanceId === event.sessionInstanceId && entry.commandId === event.commandId);
27382
- if (!record4)
27888
+ const record5 = existing.pendingAdmissions.find((entry) => entry.sessionInstanceId === event.sessionInstanceId && entry.commandId === event.commandId);
27889
+ if (!record5)
27383
27890
  return { state, effects: [] };
27384
27891
  const agent2 = clone2(existing);
27385
27892
  agent2.pendingAdmissions = agent2.pendingAdmissions.filter((entry) => entry.sessionInstanceId !== event.sessionInstanceId || entry.commandId !== event.commandId);
27386
27893
  syncExecutionProjection(agent2);
27387
- return commit(state, agent2, event.outcome === "failed" ? recoveryEffects(agent2, [record4]) : []);
27894
+ return commit(state, agent2, event.outcome === "failed" ? recoveryEffects(agent2, [record5]) : []);
27388
27895
  }
27389
27896
  case "admission_acknowledged": {
27390
27897
  const existing = state.agents[event.agentId];
@@ -27902,11 +28409,11 @@ function syncExecutionProjection(agent2) {
27902
28409
  agent2.lastDeliverAt = agent2.pendingAdmissions.length > 0 ? Math.max(...agent2.pendingAdmissions.map((entry) => entry.admittedAt)) : null;
27903
28410
  }
27904
28411
  function recoveryEffects(agent2, records) {
27905
- return records.filter((record4) => record4.requeueOnFailure).map((record4) => ({
28412
+ return records.filter((record5) => record5.requeueOnFailure).map((record5) => ({
27906
28413
  type: "requeue_delivery",
27907
28414
  agentId: agent2.agentId,
27908
- message: record4.exactAgentMsg,
27909
- mode: record4.mode
28415
+ message: record5.exactAgentMsg,
28416
+ mode: record5.mode
27910
28417
  }));
27911
28418
  }
27912
28419
  function commit(state, agent2, effects) {
@@ -28052,8 +28559,8 @@ async function readClaudeQuota(options) {
28052
28559
  if (!body || typeof body !== "object") {
28053
28560
  return { status: "error", sourceEpoch: claudeSourceEpoch, code: "invalid_response", retryable: true };
28054
28561
  }
28055
- const record4 = body;
28056
- const limits = ["five_hour", "seven_day", "seven_day_sonnet", "seven_day_opus"].map((key) => claudeLimit(key, record4[key])).filter((limit) => limit !== null);
28562
+ const record5 = body;
28563
+ const limits = ["five_hour", "seven_day", "seven_day_sonnet", "seven_day_opus"].map((key) => claudeLimit(key, record5[key])).filter((limit) => limit !== null);
28057
28564
  if (limits.length === 0) {
28058
28565
  return { status: "error", sourceEpoch: claudeSourceEpoch, code: "invalid_response", retryable: true };
28059
28566
  }
@@ -30178,6 +30685,7 @@ class AgentRouter {
30178
30685
  await this.opts.channel.reportReady(this.buildReady());
30179
30686
  }
30180
30687
  buildReady() {
30688
+ const timeZone = typeof this.opts.timeZone === "function" ? this.opts.timeZone() : this.opts.timeZone;
30181
30689
  return {
30182
30690
  runtimeReport: [...this.runtimes.values()],
30183
30691
  capabilities: [CONTROL_HEARTBEAT_CAPABILITY],
@@ -30187,6 +30695,7 @@ class AgentRouter {
30187
30695
  arch: this.opts.arch,
30188
30696
  osRelease: this.opts.osRelease,
30189
30697
  daemonVersion: this.opts.daemonVersion,
30698
+ timeZone,
30190
30699
  ...this.opts.providerQuotas ? { providerQuotas: this.opts.providerQuotas() } : {}
30191
30700
  };
30192
30701
  }
@@ -30228,11 +30737,10 @@ class AgentRouter {
30228
30737
  return;
30229
30738
  if (existing.status === "healthy" && !existing.lastError && !existing.lastErrorAt)
30230
30739
  return;
30231
- this.runtimes.set(id, {
30232
- id: existing.id,
30233
- version: existing.version,
30234
- status: "healthy"
30235
- });
30740
+ const healthy = { ...existing, status: "healthy" };
30741
+ delete healthy.lastError;
30742
+ delete healthy.lastErrorAt;
30743
+ this.runtimes.set(id, healthy);
30236
30744
  this.log.info("runtime marked healthy again", { runtimeId: id });
30237
30745
  this.scheduleReadyFrameResend();
30238
30746
  }
@@ -31813,15 +32321,15 @@ class MessageReminderScheduler {
31813
32321
  const startedAt = this.now();
31814
32322
  const dueAt = startedAt + input.remindAfterMs;
31815
32323
  const sentRef = `${input.channel}#${input.sentSeq}`;
31816
- const record4 = {
32324
+ const record5 = {
31817
32325
  ...input,
31818
32326
  sentRef,
31819
32327
  startedAt,
31820
32328
  dueAt,
31821
32329
  timer: undefined
31822
32330
  };
31823
- record4.timer = this.setTimer(() => {
31824
- if (this.reminders.get(key) !== record4)
32331
+ record5.timer = this.setTimer(() => {
32332
+ if (this.reminders.get(key) !== record5)
31825
32333
  return;
31826
32334
  this.reminders.delete(key);
31827
32335
  try {
@@ -31832,8 +32340,8 @@ class MessageReminderScheduler {
31832
32340
  Promise.resolve(delivery).catch(() => {});
31833
32341
  } catch {}
31834
32342
  }, input.remindAfterMs);
31835
- record4.timer.unref?.();
31836
- this.reminders.set(key, record4);
32343
+ record5.timer.unref?.();
32344
+ this.reminders.set(key, record5);
31837
32345
  return { armed: true, dueAt };
31838
32346
  }
31839
32347
  observe(agentId, channel2, latestSeq) {
@@ -32108,15 +32616,12 @@ class DaemonSelfSleepScheduler {
32108
32616
  import { chmod, mkdir, open, readFile as readFile2, rename, rm } from "node:fs/promises";
32109
32617
  import { dirname as dirname6, join as join14 } from "node:path";
32110
32618
  import { randomUUID as randomUUID7 } from "node:crypto";
32111
- function dayKey(at) {
32112
- return at.toISOString().slice(0, 10);
32619
+ function oldestRetainedDay(at, timeZone) {
32620
+ const today = dayKeyInTimeZone(at, timeZone);
32621
+ return calendarDayKeyDaysAgo(today, 8);
32113
32622
  }
32114
- function retainedDays(at) {
32115
- const days = new Set;
32116
- for (let offset = 0;offset < 7; offset += 1) {
32117
- days.add(dayKey(new Date(at.getTime() - offset * 86400000)));
32118
- }
32119
- return days;
32623
+ function oldestVisibleDay(today) {
32624
+ return calendarDayKeyDaysAgo(today, 6);
32120
32625
  }
32121
32626
  function isMetric(value) {
32122
32627
  return value === null || typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
@@ -32161,16 +32666,25 @@ class DailyTokenUsageStore {
32161
32666
  loaded = false;
32162
32667
  data = { version: 1, bots: {} };
32163
32668
  filePath;
32164
- constructor(workingDirectoryBase, now = () => new Date) {
32669
+ resolveTimeZone;
32670
+ constructor(workingDirectoryBase, now = () => new Date, timeZone = () => Intl.DateTimeFormat().resolvedOptions().timeZone) {
32165
32671
  this.now = now;
32672
+ this.resolveTimeZone = typeof timeZone === "string" ? () => timeZone : timeZone;
32673
+ this.timeZone;
32166
32674
  this.filePath = join14(workingDirectoryBase, ".telemetry", "daily-token-usage.json");
32167
32675
  }
32676
+ get timeZone() {
32677
+ const timeZone = this.resolveTimeZone();
32678
+ dayKeyInTimeZone(0, timeZone);
32679
+ return timeZone;
32680
+ }
32168
32681
  record(botId, delta) {
32169
32682
  return this.enqueue(async () => {
32170
32683
  await this.load();
32171
32684
  const at = this.now();
32172
- this.prune(at);
32173
- const day = dayKey(at);
32685
+ const timeZone = this.timeZone;
32686
+ this.prune(at, timeZone);
32687
+ const day = dayKeyInTimeZone(at, timeZone);
32174
32688
  const snapshots = this.data.bots[botId] ?? [];
32175
32689
  const existing = snapshots.find((snapshot) => snapshot.day === day);
32176
32690
  const next = existing ?? emptySnapshot(botId, day);
@@ -32187,13 +32701,27 @@ class DailyTokenUsageStore {
32187
32701
  });
32188
32702
  }
32189
32703
  snapshots(botId) {
32704
+ return this.usageWindow(botId).then((window2) => window2.snapshots);
32705
+ }
32706
+ usageWindow(botId) {
32190
32707
  let result = [];
32708
+ let usageDay = "";
32709
+ let usageTimeZone = "";
32191
32710
  return this.enqueue(async () => {
32192
32711
  await this.load();
32193
- if (this.prune(this.now()))
32712
+ const at = this.now();
32713
+ const timeZone = this.timeZone;
32714
+ usageTimeZone = timeZone;
32715
+ usageDay = dayKeyInTimeZone(at, timeZone);
32716
+ if (this.prune(at, timeZone))
32194
32717
  await this.persist();
32195
- result = (this.data.bots[botId] ?? []).map((snapshot) => structuredClone(snapshot));
32196
- }).then(() => result);
32718
+ const oldestDay = oldestVisibleDay(usageDay);
32719
+ result = (this.data.bots[botId] ?? []).filter((snapshot) => snapshot.day >= oldestDay && snapshot.day <= usageDay).map((snapshot) => structuredClone(snapshot));
32720
+ }).then(() => ({
32721
+ usageDay,
32722
+ usageTimeZone,
32723
+ snapshots: result
32724
+ }));
32197
32725
  }
32198
32726
  enqueue(operation) {
32199
32727
  const result = this.tail.then(operation, operation);
@@ -32238,11 +32766,11 @@ class DailyTokenUsageStore {
32238
32766
  this.data = { version: 1, bots: valid };
32239
32767
  this.loaded = true;
32240
32768
  }
32241
- prune(at) {
32242
- const keep = retainedDays(at);
32769
+ prune(at, timeZone) {
32770
+ const oldestDay = oldestRetainedDay(at, timeZone);
32243
32771
  let changed = false;
32244
32772
  for (const [botId, snapshots] of Object.entries(this.data.bots)) {
32245
- const retained = snapshots.filter((snapshot) => keep.has(snapshot.day)).sort((a, b) => a.day.localeCompare(b.day)).slice(-7);
32773
+ const retained = snapshots.filter((snapshot) => snapshot.day >= oldestDay).sort((a, b) => a.day.localeCompare(b.day));
32246
32774
  if (retained.length !== snapshots.length || retained.some((snapshot, index2) => snapshot !== snapshots[index2]))
32247
32775
  changed = true;
32248
32776
  if (retained.length === 0)
@@ -32286,6 +32814,7 @@ var WARMUP_CEILING_MS = 30000;
32286
32814
  var RUNTIME_RAW_TRACE_MAX_BYTES = 8 * 1024 * 1024;
32287
32815
  var RUNTIME_RAW_TRACE_AGENT_IDS_ENV = "ALOOK_RUNTIME_RAW_TRACE_AGENT_IDS";
32288
32816
  var STATUS_WRITE_INTERVAL_MS = 5000;
32817
+ var TOKEN_USAGE_BACKENDS = new Set(["claude", "codex", "opencode", "pi"]);
32289
32818
  function parseRuntimeRawTraceAgentIds(value) {
32290
32819
  return new Set((value ?? "").split(",").map((agentId) => agentId.trim()).filter((agentId) => agentId.length > 0 && agentId !== "*"));
32291
32820
  }
@@ -32462,10 +32991,14 @@ async function createDaemon(opts) {
32462
32991
  recordProviderQuota("claude", observed);
32463
32992
  }
32464
32993
  const quota = backendId === "claude" || backendId === "codex" ? providerQuotaByBackend.get(backendId) : undefined;
32465
- const dailyUsage = await dailyTokenUsage2.snapshots(info.agentId);
32994
+ const usageWindow = backendId && TOKEN_USAGE_BACKENDS.has(backendId) ? await dailyTokenUsage2.usageWindow(info.agentId) : null;
32466
32995
  return {
32467
32996
  ...info,
32468
- ...dailyUsage.length > 0 ? { dailyUsage } : {},
32997
+ ...usageWindow ? {
32998
+ usageTimeZone: usageWindow.usageTimeZone,
32999
+ usageDay: usageWindow.usageDay,
33000
+ ...usageWindow.snapshots.length > 0 ? { dailyUsage: usageWindow.snapshots } : {}
33001
+ } : {},
32469
33002
  ...quota ? { quota: structuredClone(quota) } : {}
32470
33003
  };
32471
33004
  };
@@ -32877,6 +33410,7 @@ async function createDaemon(opts) {
32877
33410
  arch: opts.arch,
32878
33411
  osRelease: opts.osRelease,
32879
33412
  daemonVersion: opts.daemonVersion,
33413
+ timeZone: () => dailyTokenUsage2.timeZone,
32880
33414
  providerQuotas: providerQuotaSnapshots,
32881
33415
  resyncActivities: async () => {
32882
33416
  const activities = await Promise.all(manager.liveAgentActivities().map((info) => activityPayload(info)));
@@ -34128,11 +34662,11 @@ async function strictOutcome(response, allowed) {
34128
34662
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
34129
34663
  return { kind: "retryable" };
34130
34664
  }
34131
- const record4 = value;
34132
- if (Object.keys(record4).length !== 2 || record4.kind !== "terminal") {
34665
+ const record5 = value;
34666
+ if (Object.keys(record5).length !== 2 || record5.kind !== "terminal") {
34133
34667
  return { kind: "retryable" };
34134
34668
  }
34135
- const status = record4.status;
34669
+ const status = record5.status;
34136
34670
  if (status !== "uploaded" && status !== "failed" || allowed[status] !== response.status) {
34137
34671
  return { kind: "retryable" };
34138
34672
  }
@@ -34626,7 +35160,7 @@ function createDaemonProcessLogger(daemonDir, foreground) {
34626
35160
  `) : quiet,
34627
35161
  err: foreground ? (line2) => process.stderr.write(line2 + `
34628
35162
  `) : quiet,
34629
- record: (record4) => sink.write(JSON.stringify(record4))
35163
+ record: (record5) => sink.write(JSON.stringify(record5))
34630
35164
  });
34631
35165
  return { logger, logPath, sink };
34632
35166
  }
@@ -35369,18 +35903,18 @@ function readCredentialFile(filePath) {
35369
35903
  } catch {}
35370
35904
  return null;
35371
35905
  }
35372
- function writeCredentialFile(filePath, record4) {
35373
- writePrivateJsonAtomic2(filePath, record4);
35906
+ function writeCredentialFile(filePath, record5) {
35907
+ writePrivateJsonAtomic2(filePath, record5);
35374
35908
  }
35375
35909
  function readDaemonLaunchRecord(baseDir, machineId) {
35376
- const record4 = readCredentialFile(credentialFilePathByMachineId(baseDir, machineId));
35377
- if (!record4 || !("schemaVersion" in record4) || record4.schemaVersion !== 1 || !parseReleaseVersion(record4.daemonVersion)) {
35910
+ const record5 = readCredentialFile(credentialFilePathByMachineId(baseDir, machineId));
35911
+ if (!record5 || !("schemaVersion" in record5) || record5.schemaVersion !== 1 || !parseReleaseVersion(record5.daemonVersion)) {
35378
35912
  throw new Error("daemon launch record is missing or requires a manual start upgrade");
35379
35913
  }
35380
- validateMachineId(record4.machineId);
35381
- if (record4.machineId !== machineId)
35914
+ validateMachineId(record5.machineId);
35915
+ if (record5.machineId !== machineId)
35382
35916
  throw new Error("daemon launch record machine mismatch");
35383
- return record4;
35917
+ return record5;
35384
35918
  }
35385
35919
  function findExistingCredentialForBearer(baseDir, bearer) {
35386
35920
  const dir = daemonsDir(baseDir);
@@ -35545,23 +36079,23 @@ async function daemonResume(opts) {
35545
36079
  if (!/^[A-Za-z0-9_-]{16,128}$/.test(opts.requestId))
35546
36080
  throw new Error("invalid replacement request id");
35547
36081
  const baseDir = opts.baseDir || process.env.ALOOK_DATA_DIR || DEFAULT_BASE_DIR;
35548
- const record4 = readDaemonLaunchRecord(baseDir, opts.id);
36082
+ const record5 = readDaemonLaunchRecord(baseDir, opts.id);
35549
36083
  if (false) {}
35550
36084
  await daemonStart({
35551
- machineKey: record4.credential,
35552
- serverUrl: record4.serverUrl,
35553
- wsUrl: record4.wsUrl,
36085
+ machineKey: record5.credential,
36086
+ serverUrl: record5.serverUrl,
36087
+ wsUrl: record5.wsUrl,
35554
36088
  baseDir,
35555
36089
  resumeRequestId: opts.requestId
35556
36090
  });
35557
36091
  }
35558
36092
  async function daemonStartById(opts) {
35559
36093
  const baseDir = opts.baseDir || process.env.ALOOK_DATA_DIR || DEFAULT_BASE_DIR;
35560
- const record4 = readDaemonLaunchRecord(baseDir, opts.id);
36094
+ const record5 = readDaemonLaunchRecord(baseDir, opts.id);
35561
36095
  await daemonStart({
35562
- machineKey: record4.credential,
35563
- serverUrl: record4.serverUrl,
35564
- wsUrl: record4.wsUrl,
36096
+ machineKey: record5.credential,
36097
+ serverUrl: record5.serverUrl,
36098
+ wsUrl: record5.wsUrl,
35565
36099
  baseDir,
35566
36100
  foreground: opts.foreground
35567
36101
  });