@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/index.js CHANGED
@@ -942,8 +942,8 @@ function resolveLaunchFieldsOrDefault(input) {
942
942
  const envVars = Object.fromEntries(Object.entries(normalized.environment ?? {}).filter(([key]) => !controlled.has(key)));
943
943
  const providerEnv = {};
944
944
  const model = normalized.model.kind === "default" ? undefined : normalized.model.name;
945
- if (normalized.model.kind === "custom" && normalized.provider?.kind === "custom_endpoint") {
946
- providerEnv.ANTHROPIC_CUSTOM_MODEL_OPTION = normalized.model.name;
945
+ if (model && normalized.provider?.kind === "custom_endpoint") {
946
+ providerEnv.ANTHROPIC_CUSTOM_MODEL_OPTION = model;
947
947
  }
948
948
  if (normalized.provider?.kind === "custom_endpoint") {
949
949
  providerEnv.ANTHROPIC_BASE_URL = normalized.provider.apiUrl;
@@ -1125,6 +1125,66 @@ function buildClaudeArgs(config) {
1125
1125
  return args;
1126
1126
  }
1127
1127
 
1128
+ // agent-driver/dist/internal/token-usage.js
1129
+ function validIdentityPart(value) {
1130
+ return value.trim().length > 0 && Buffer.byteLength(value, "utf8") <= 512;
1131
+ }
1132
+ function identityKey(identity) {
1133
+ if (!validIdentityPart(identity.runtime) || !validIdentityPart(identity.backendSessionId) || !validIdentityPart(identity.providerRecordId))
1134
+ return null;
1135
+ return JSON.stringify([identity.runtime, identity.backendSessionId, identity.providerRecordId]);
1136
+ }
1137
+ function metric(value) {
1138
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
1139
+ }
1140
+ function cacheMetric(read, write) {
1141
+ const present = [read, write].filter((value) => value !== undefined);
1142
+ if (present.length === 0)
1143
+ return null;
1144
+ const metrics = present.map(metric);
1145
+ if (metrics.some((value) => value === null))
1146
+ return null;
1147
+ const total = metrics.reduce((sum, value) => sum + value, 0);
1148
+ return Number.isSafeInteger(total) ? total : null;
1149
+ }
1150
+
1151
+ class SettledUsageProjector {
1152
+ active = new Set;
1153
+ project(record) {
1154
+ const key = identityKey(record);
1155
+ if (!key || this.active.has(key))
1156
+ return null;
1157
+ this.active.add(key);
1158
+ const cache = cacheMetric(record.cacheRead, record.cacheWrite);
1159
+ const rawInput = metric(record.input);
1160
+ const input = record.inputIncludesCache ? rawInput !== null && cache !== null && cache <= rawInput ? rawInput - cache : null : rawInput;
1161
+ const rawOutput = metric(record.output);
1162
+ const reasoning = metric(record.reasoning);
1163
+ const output = record.outputIncludesReasoning ? rawOutput : rawOutput !== null && reasoning !== null && Number.isSafeInteger(rawOutput + reasoning) ? rawOutput + reasoning : null;
1164
+ if (input === null && output === null && cache === null) {
1165
+ this.active.delete(key);
1166
+ return null;
1167
+ }
1168
+ return {
1169
+ kind: "telemetry",
1170
+ name: "token_usage",
1171
+ source: record.source,
1172
+ usage: { input, output, cache }
1173
+ };
1174
+ }
1175
+ release(identity) {
1176
+ const key = identityKey(identity);
1177
+ if (key)
1178
+ this.active.delete(key);
1179
+ }
1180
+ reset() {
1181
+ this.active.clear();
1182
+ }
1183
+ get activeCount() {
1184
+ return this.active.size;
1185
+ }
1186
+ }
1187
+
1128
1188
  // agent-driver/dist/internal/utils.js
1129
1189
  import { randomUUID as randomUUID3 } from "crypto";
1130
1190
  function jsonRpcRequest(method, params, id) {
@@ -1144,9 +1204,13 @@ var API_ERROR_RE = /API Error:.*(?:Connection error|\b[45]\d{2}\b)/i;
1144
1204
  class ClaudeEventNormalizer {
1145
1205
  turnProtocol;
1146
1206
  currentSession = null;
1207
+ usageProjector = new SettledUsageProjector;
1147
1208
  constructor(turnProtocol) {
1148
1209
  this.turnProtocol = turnProtocol;
1149
1210
  }
1211
+ beginTurn() {
1212
+ this.usageProjector.reset();
1213
+ }
1150
1214
  get currentSessionId() {
1151
1215
  return this.currentSession;
1152
1216
  }
@@ -1238,7 +1302,7 @@ class ClaudeEventNormalizer {
1238
1302
  const turnOwner = rawOwner ? this.turnProtocol?.claimResult(rawOwner) ?? (this.turnProtocol ? null : `claude:${rawOwner}`) : null;
1239
1303
  if (this.turnProtocol && !turnOwner)
1240
1304
  return;
1241
- const usage = this.buildUsageTelemetry(event);
1305
+ const usage = this.buildUsageTelemetry(event, rawOwner);
1242
1306
  if (usage)
1243
1307
  out.push(usage);
1244
1308
  if (event.is_error || event.subtype === "error_during_execution") {
@@ -1253,23 +1317,26 @@ class ClaudeEventNormalizer {
1253
1317
  acceptsTurnWork() {
1254
1318
  return this.turnProtocol?.acceptsTurnWork() ?? true;
1255
1319
  }
1256
- buildUsageTelemetry(event) {
1320
+ buildUsageTelemetry(event, rootRequestId) {
1257
1321
  const u = event?.usage;
1258
1322
  if (!u)
1259
1323
  return null;
1260
- const metric = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
1261
- const cacheParts = [u.cache_read_input_tokens, u.cache_creation_input_tokens].filter((value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0);
1262
- const cache = cacheParts.length > 0 && Number.isSafeInteger(cacheParts.reduce((sum, value) => sum + value, 0)) ? cacheParts.reduce((sum, value) => sum + value, 0) : null;
1263
- return {
1264
- kind: "telemetry",
1265
- name: "token_usage",
1324
+ const backendSessionId = event.session_id ?? this.currentSession;
1325
+ if (typeof backendSessionId !== "string" || !backendSessionId)
1326
+ return null;
1327
+ const providerRecordId = rootRequestId ?? (typeof event.request_id === "string" ? event.request_id : "invocation-result");
1328
+ return this.usageProjector.project({
1329
+ runtime: "claude",
1330
+ backendSessionId,
1331
+ providerRecordId,
1266
1332
  source: "claude_result_usage",
1267
- usage: {
1268
- input: metric(u.input_tokens),
1269
- output: metric(u.output_tokens),
1270
- cache
1271
- }
1272
- };
1333
+ input: u.input_tokens,
1334
+ output: u.output_tokens,
1335
+ cacheRead: u.cache_read_input_tokens,
1336
+ cacheWrite: u.cache_creation_input_tokens,
1337
+ inputIncludesCache: false,
1338
+ outputIncludesReasoning: true
1339
+ });
1273
1340
  }
1274
1341
  }
1275
1342
 
@@ -1278,6 +1345,7 @@ import { execFileSync as execFileSync2 } from "child_process";
1278
1345
  import * as fs4 from "fs";
1279
1346
  import * as path4 from "path";
1280
1347
  var PROBE_TIMEOUT_MS = 5000;
1348
+ var PROBE_OUTPUT_MAX_BYTES = 1024 * 1024;
1281
1349
  function resolveCommandOnPath(command, deps = {}) {
1282
1350
  if (deps.which)
1283
1351
  return deps.which(command);
@@ -1328,6 +1396,23 @@ function probeCommandVersion(command, args = [], deps = {}, platform = process.p
1328
1396
  return { ok: false, error: String(code) };
1329
1397
  }
1330
1398
  }
1399
+ function probeCommandOutput(command, args, platform = process.platform) {
1400
+ try {
1401
+ const output = execFileSync2(command, args, {
1402
+ encoding: "utf8",
1403
+ timeout: PROBE_TIMEOUT_MS,
1404
+ maxBuffer: PROBE_OUTPUT_MAX_BYTES,
1405
+ shell: needsWindowsShimShell(command, platform),
1406
+ stdio: ["pipe", "pipe", "ignore"],
1407
+ input: "",
1408
+ env: { ...process.env, CI: "1" }
1409
+ });
1410
+ return { ok: true, output };
1411
+ } catch (err) {
1412
+ const code = err?.code ?? "command_probe_failed";
1413
+ return { ok: false, error: String(code) };
1414
+ }
1415
+ }
1331
1416
  function resolveHomePath(relativePath, deps = {}) {
1332
1417
  return path4.join(deps.homeDir || process.env.HOME || ".", relativePath);
1333
1418
  }
@@ -1434,6 +1519,14 @@ class ClaudeTurnProtocol {
1434
1519
  }
1435
1520
 
1436
1521
  // agent-driver/dist/adapters/claude/index.js
1522
+ var CLAUDE_MODEL_CATALOG = {
1523
+ updateMode: "unsupported",
1524
+ models: ["opus", "sonnet", "haiku"].map((id) => ({
1525
+ id,
1526
+ supportedReasoningEfforts: []
1527
+ }))
1528
+ };
1529
+
1437
1530
  class ClaudeDriver {
1438
1531
  id = "claude";
1439
1532
  instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
@@ -1446,14 +1539,17 @@ class ClaudeDriver {
1446
1539
  turnProtocol = new ClaudeTurnProtocol;
1447
1540
  eventNormalizer = new ClaudeEventNormalizer(this.turnProtocol);
1448
1541
  beginTurn() {
1449
- return this.turnProtocol.beginTurn();
1542
+ const receipt = this.turnProtocol.beginTurn();
1543
+ this.eventNormalizer.beginTurn();
1544
+ return receipt;
1450
1545
  }
1451
1546
  probe(command) {
1452
1547
  const explicit = command?.trim();
1453
- if (!explicit)
1454
- return probeClaude();
1455
- const result = probeCommandVersion(explicit);
1456
- return result.ok ? { status: "healthy", version: result.version } : { status: "unhealthy", lastError: result.error };
1548
+ const base = explicit ? (() => {
1549
+ const result = probeCommandVersion(explicit);
1550
+ return result.ok ? { status: "healthy", version: result.version } : { status: "unhealthy", lastError: result.error };
1551
+ })() : probeClaude();
1552
+ return base.status === "healthy" ? { ...base, reasoning: CLAUDE_MODEL_CATALOG } : base;
1457
1553
  }
1458
1554
  async openLane(ctx, options) {
1459
1555
  return createProcessLane(this, ctx, { onRawStdoutLine: options?.onRawStdoutLine });
@@ -1499,14 +1595,6 @@ class ClaudeDriver {
1499
1595
  }
1500
1596
 
1501
1597
  // agent-driver/dist/adapters/codex/telemetry.js
1502
- function metric(value) {
1503
- return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
1504
- }
1505
- function nonCachedInput(input, cached) {
1506
- if (typeof input !== "number" || !Number.isSafeInteger(input) || input < 0 || typeof cached !== "number" || !Number.isSafeInteger(cached) || cached < 0 || cached > input)
1507
- return null;
1508
- return input - cached;
1509
- }
1510
1598
  function canonicalId(value, fallback) {
1511
1599
  return typeof value === "string" && value.length > 0 && new TextEncoder().encode(value).length <= 64 ? value : fallback;
1512
1600
  }
@@ -1600,28 +1688,24 @@ function mapCodexQuotaSnapshots(snapshots, sourceEpoch) {
1600
1688
  }
1601
1689
  };
1602
1690
  }
1603
- function mapCodexTelemetry(method, params, sourceEpoch) {
1604
- if (method === "thread/tokenUsage/updated") {
1605
- const u = params?.tokenUsage?.last ?? params?.token_usage?.last;
1606
- if (!u)
1607
- return [];
1608
- const input = u.inputTokens ?? u.input_tokens;
1609
- const cached = u.cachedInputTokens ?? u.cached_input_tokens;
1610
- return [{
1611
- kind: "telemetry",
1612
- name: "token_usage",
1613
- source: "codex_thread_token_usage_updated",
1614
- usage: {
1615
- input: nonCachedInput(input, cached),
1616
- output: metric(u.outputTokens ?? u.output_tokens),
1617
- cache: metric(cached)
1618
- }
1619
- }];
1620
- }
1621
- if (method === "account/rateLimits/updated") {
1622
- return [mapCodexQuotaSnapshots([params?.rateLimits ?? params ?? {}], sourceEpoch)];
1623
- }
1624
- return [];
1691
+ function mapCodexSettledUsage(params, projector) {
1692
+ const backendSessionId = params?.threadId ?? params?.thread_id;
1693
+ const providerRecordId = params?.responseId ?? params?.response_id;
1694
+ const usage = params?.usage;
1695
+ if (typeof backendSessionId !== "string" || typeof providerRecordId !== "string" || !usage)
1696
+ return null;
1697
+ return projector.project({
1698
+ runtime: "codex",
1699
+ backendSessionId,
1700
+ providerRecordId,
1701
+ source: "codex_raw_response_completed",
1702
+ input: usage.inputTokens ?? usage.input_tokens,
1703
+ output: usage.outputTokens ?? usage.output_tokens,
1704
+ cacheRead: usage.cachedInputTokens ?? usage.cached_input_tokens,
1705
+ cacheWrite: usage.cacheWriteInputTokens ?? usage.cache_write_input_tokens,
1706
+ inputIncludesCache: true,
1707
+ outputIncludesReasoning: true
1708
+ });
1625
1709
  }
1626
1710
 
1627
1711
  // agent-driver/dist/adapters/codex/normalizer.js
@@ -1673,7 +1757,8 @@ class CodexEventNormalizer {
1673
1757
  rateLimitSnapshots = new Map;
1674
1758
  quotaSnapshotInitialized = false;
1675
1759
  quotaSourceGeneration = codexQuotaSourceGeneration;
1676
- pendingTurnUsage = null;
1760
+ usageProjector = new SettledUsageProjector;
1761
+ usageRecordsBySessionAndTurn = new Map;
1677
1762
  threadId = null;
1678
1763
  turnId = null;
1679
1764
  terminalTurn = null;
@@ -1746,7 +1831,8 @@ class CodexEventNormalizer {
1746
1831
  if (threadId !== this.threadId) {
1747
1832
  this.turnId = null;
1748
1833
  this.terminalTurn = null;
1749
- this.pendingTurnUsage = null;
1834
+ this.usageProjector.reset();
1835
+ this.usageRecordsBySessionAndTurn.clear();
1750
1836
  }
1751
1837
  this.threadId = threadId;
1752
1838
  }
@@ -1795,6 +1881,22 @@ class CodexEventNormalizer {
1795
1881
  }
1796
1882
  handleNotification(method, params) {
1797
1883
  const notificationThreadId = typeof params?.threadId === "string" ? params.threadId : null;
1884
+ if (method === "rawResponse/completed")
1885
+ return this.handleSettledUsage(params);
1886
+ if (method === "turn/completed" && notificationThreadId !== null && notificationThreadId !== this.threadId) {
1887
+ const turnId = this.notificationTurnId(params);
1888
+ if (turnId)
1889
+ this.releaseUsageForTurn(notificationThreadId, turnId);
1890
+ return [];
1891
+ }
1892
+ if (method === "item/completed" && notificationThreadId !== null && notificationThreadId !== this.threadId) {
1893
+ const turnId = this.notificationTurnId(params);
1894
+ const itemType = params?.item?.type ?? params?.type;
1895
+ if (turnId && itemType === "contextCompaction") {
1896
+ this.releaseUsageForTurn(notificationThreadId, turnId);
1897
+ }
1898
+ return [];
1899
+ }
1798
1900
  if (this.threadId !== null && notificationThreadId !== null && notificationThreadId !== this.threadId)
1799
1901
  return [];
1800
1902
  if (this.isRootWorkNotification(method) && !this.acceptRootWork(params))
@@ -1807,7 +1909,6 @@ class CodexEventNormalizer {
1807
1909
  return [];
1808
1910
  this.turnId = params.turn.id;
1809
1911
  this.terminalTurn = null;
1810
- this.pendingTurnUsage = null;
1811
1912
  return [
1812
1913
  {
1813
1914
  kind: "turn_owner",
@@ -1823,7 +1924,7 @@ class CodexEventNormalizer {
1823
1924
  case "item/started":
1824
1925
  return this.handleItemStarted(params);
1825
1926
  case "item/completed":
1826
- return this.handleItemCompleted(params);
1927
+ return this.handleItemCompletedAndReleaseUsage(params);
1827
1928
  case "rawResponseItem/completed":
1828
1929
  return [{ kind: "internal_progress", source: "codex_raw_item", itemType: "rawResponseItem" }];
1829
1930
  case "configWarning":
@@ -1836,30 +1937,24 @@ class CodexEventNormalizer {
1836
1937
  case "turn/completed":
1837
1938
  if (!this.acceptRootTerminal(params))
1838
1939
  return [];
1839
- const usage = this.pendingTurnUsage;
1840
- this.pendingTurnUsage = null;
1940
+ this.releaseUsageForTurn(params.threadId, params.turn.id);
1841
1941
  if (params.turn.status === "failed") {
1842
1942
  return [
1843
- ...usage ? [usage] : [],
1844
1943
  { kind: "error", message: "Codex turn failed" },
1845
1944
  { kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }
1846
1945
  ];
1847
1946
  }
1848
1947
  if (params.turn.status === "interrupted") {
1849
- return [...usage ? [usage] : [], { kind: "error", message: "Codex turn interrupted" }, { kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
1948
+ return [{ kind: "error", message: "Codex turn interrupted" }, { kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
1850
1949
  }
1851
- return [...usage ? [usage] : [], { kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
1950
+ return [{ kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
1852
1951
  case "error":
1853
1952
  if (params?.willRetry === true) {
1854
1953
  return [{ kind: "runtime_recovery", stage: "retrying", source: "codex_stream" }];
1855
1954
  }
1856
1955
  return [{ kind: "error", message: params?.error?.message ?? params?.message ?? "Codex error" }];
1857
- case "thread/tokenUsage/updated": {
1858
- const usage2 = mapCodexTelemetry(method, params, codexQuotaSourceEpoch)[0];
1859
- if (usage2)
1860
- this.pendingTurnUsage = usage2;
1956
+ case "thread/tokenUsage/updated":
1861
1957
  return [];
1862
- }
1863
1958
  case "account/rateLimits/updated":
1864
1959
  return this.mergeQuotaSnapshots(params);
1865
1960
  case "account/updated":
@@ -1870,6 +1965,42 @@ class CodexEventNormalizer {
1870
1965
  return [];
1871
1966
  }
1872
1967
  }
1968
+ handleSettledUsage(params) {
1969
+ const notificationTurnId = this.notificationTurnId(params);
1970
+ if (this.turnId === null && this.terminalTurn?.state === "closed" && notificationTurnId === this.terminalTurn.turnId && params?.threadId === this.terminalTurn.threadId)
1971
+ return [];
1972
+ const backendSessionId = params?.threadId ?? params?.thread_id;
1973
+ const providerRecordId = params?.responseId ?? params?.response_id;
1974
+ if (!notificationTurnId || typeof backendSessionId !== "string" || typeof providerRecordId !== "string")
1975
+ return [];
1976
+ const usage = mapCodexSettledUsage(params, this.usageProjector);
1977
+ if (!usage)
1978
+ return [];
1979
+ const recordsByTurn = this.usageRecordsBySessionAndTurn.get(backendSessionId) ?? new Map;
1980
+ const recordIds = recordsByTurn.get(notificationTurnId) ?? new Set;
1981
+ recordIds.add(providerRecordId);
1982
+ recordsByTurn.set(notificationTurnId, recordIds);
1983
+ this.usageRecordsBySessionAndTurn.set(backendSessionId, recordsByTurn);
1984
+ return [usage];
1985
+ }
1986
+ releaseUsageForTurn(backendSessionId, turnId) {
1987
+ const recordsByTurn = this.usageRecordsBySessionAndTurn.get(backendSessionId);
1988
+ for (const providerRecordId of recordsByTurn?.get(turnId) ?? []) {
1989
+ this.usageProjector.release({ runtime: "codex", backendSessionId, providerRecordId });
1990
+ }
1991
+ recordsByTurn?.delete(turnId);
1992
+ if (recordsByTurn?.size === 0)
1993
+ this.usageRecordsBySessionAndTurn.delete(backendSessionId);
1994
+ }
1995
+ handleItemCompletedAndReleaseUsage(params) {
1996
+ const events = this.handleItemCompleted(params);
1997
+ const turnId = this.notificationTurnId(params);
1998
+ const itemType = params?.item?.type ?? params?.type;
1999
+ if (turnId && itemType === "contextCompaction" && turnId !== this.turnId && typeof params?.threadId === "string") {
2000
+ this.releaseUsageForTurn(params.threadId, turnId);
2001
+ }
2002
+ return events;
2003
+ }
1873
2004
  isRootWorkNotification(method) {
1874
2005
  return method === "item/reasoning/textDelta" || method === "item/reasoning/summaryTextDelta" || method === "item/agentMessage/delta" || method === "item/started" || method === "item/completed" || method === "rawResponseItem/completed";
1875
2006
  }
@@ -2020,10 +2151,59 @@ function stableErrorCode(value, fallback) {
2020
2151
  return /^[A-Za-z0-9_.-]{1,100}$/.test(code) ? code : fallback;
2021
2152
  }
2022
2153
 
2154
+ // agent-driver/dist/internal/modelCatalog.js
2155
+ var RUNTIME_MODEL_CATALOG_MAX = 512;
2156
+ var RUNTIME_MODEL_ID_MAX = 100;
2157
+ function normalizeRuntimeModelId(value) {
2158
+ if (typeof value !== "string")
2159
+ return;
2160
+ const id = value.trim();
2161
+ if (!id || id.length > RUNTIME_MODEL_ID_MAX || /\s/.test(id))
2162
+ return;
2163
+ return id;
2164
+ }
2165
+ function catalogFromIds(ids) {
2166
+ const seen = new Set;
2167
+ const models = [];
2168
+ for (const rawId of ids) {
2169
+ const id = normalizeRuntimeModelId(rawId);
2170
+ if (!id || seen.has(id))
2171
+ continue;
2172
+ if (models.length >= RUNTIME_MODEL_CATALOG_MAX)
2173
+ return;
2174
+ seen.add(id);
2175
+ models.push({ id, supportedReasoningEfforts: [] });
2176
+ }
2177
+ if (models.length === 0)
2178
+ return;
2179
+ return { updateMode: "unsupported", models };
2180
+ }
2181
+ function parseOpenCodeModelCatalog(output) {
2182
+ const ids = output.split(/\r?\n/).flatMap((line) => {
2183
+ const id = normalizeRuntimeModelId(line);
2184
+ return id && /^[^/]+\/.+$/.test(id) ? [id] : [];
2185
+ });
2186
+ return catalogFromIds(ids);
2187
+ }
2188
+ function parsePiModelCatalog(values) {
2189
+ if (!Array.isArray(values))
2190
+ return;
2191
+ const ids = values.flatMap((value) => {
2192
+ if (!value || typeof value !== "object")
2193
+ return [];
2194
+ const model = value;
2195
+ const provider = normalizeRuntimeModelId(model.provider);
2196
+ const id = normalizeRuntimeModelId(model.id);
2197
+ return provider && id && !provider.includes("/") ? [`${provider}/${id}`] : [];
2198
+ });
2199
+ return catalogFromIds(ids);
2200
+ }
2201
+
2023
2202
  // agent-driver/dist/adapters/codex/index.js
2024
2203
  var SETTINGS_UPDATE_TIMEOUT_MS = 5000;
2025
2204
  var MODEL_LIST_TIMEOUT_MS = 5000;
2026
- var MODEL_LIST_MAX = 64;
2205
+ var MODEL_LIST_OUTPUT_MAX_BYTES = 1024 * 1024;
2206
+ var MODEL_LIST_MAX = RUNTIME_MODEL_CATALOG_MAX;
2027
2207
  var MODEL_EFFORT_MAX = 16;
2028
2208
  function isCodexMissingRolloutError(message) {
2029
2209
  return /\bno\s+rollout\s+found\b/i.test(message) || /\bmissing\s+rollout\b/i.test(message) || /\brollout\b.*\b(not found|missing)\b/i.test(message) || /\b(not found|missing)\b.*\brollout\b/i.test(message);
@@ -2099,11 +2279,13 @@ class CodexDriver {
2099
2279
  return new Promise((resolve2) => {
2100
2280
  let settled = false;
2101
2281
  let buffer = "";
2282
+ let outputBytes = 0;
2102
2283
  let nextId = 0;
2103
2284
  let initializeId = 0;
2104
2285
  let listId = 0;
2105
2286
  const models = [];
2106
2287
  const seenModels = new Set;
2288
+ let overflow = false;
2107
2289
  let defaultModelId;
2108
2290
  const finish = (catalog) => {
2109
2291
  if (settled)
@@ -2121,12 +2303,16 @@ class CodexDriver {
2121
2303
  `);
2122
2304
  };
2123
2305
  const consumeModel = (value) => {
2124
- if (!value || typeof value !== "object" || models.length >= MODEL_LIST_MAX)
2306
+ if (!value || typeof value !== "object")
2125
2307
  return;
2126
2308
  const model = value;
2127
- const id = typeof model.id === "string" ? model.id.trim() : "";
2128
- if (!id || id.length > 100 || seenModels.has(id))
2309
+ const id = normalizeRuntimeModelId(model.id);
2310
+ if (!id || seenModels.has(id))
2311
+ return;
2312
+ if (models.length >= MODEL_LIST_MAX) {
2313
+ overflow = true;
2129
2314
  return;
2315
+ }
2130
2316
  const rawOptions = Array.isArray(model.supportedReasoningEfforts) ? model.supportedReasoningEfforts : [];
2131
2317
  const seenEfforts = new Set;
2132
2318
  const supportedReasoningEfforts = rawOptions.flatMap((raw) => {
@@ -2170,9 +2356,15 @@ class CodexDriver {
2170
2356
  const result = message.result;
2171
2357
  for (const model of Array.isArray(result.data) ? result.data : [])
2172
2358
  consumeModel(model);
2359
+ if (overflow)
2360
+ return finish();
2173
2361
  const cursor = typeof result.nextCursor === "string" ? result.nextCursor : undefined;
2174
- if (cursor && models.length < MODEL_LIST_MAX)
2362
+ if (cursor && models.length >= MODEL_LIST_MAX)
2363
+ return finish();
2364
+ if (cursor)
2175
2365
  return requestModelPage(cursor);
2366
+ if (models.length === 0)
2367
+ return finish();
2176
2368
  finish({
2177
2369
  updateMode: "live_next_turn",
2178
2370
  ...defaultModelId ? { defaultModelId } : {},
@@ -2182,7 +2374,11 @@ class CodexDriver {
2182
2374
  const timer = setTimeout(() => finish(), MODEL_LIST_TIMEOUT_MS);
2183
2375
  timer.unref?.();
2184
2376
  proc.stdout?.on("data", (chunk) => {
2185
- buffer += chunk.toString();
2377
+ const text = chunk.toString();
2378
+ outputBytes += Buffer.byteLength(text);
2379
+ if (outputBytes > MODEL_LIST_OUTPUT_MAX_BYTES)
2380
+ return finish();
2381
+ buffer += text;
2186
2382
  const lines = buffer.split(`
2187
2383
  `);
2188
2384
  buffer = lines.pop() ?? "";
@@ -2385,9 +2581,189 @@ class CodexDriver {
2385
2581
 
2386
2582
  // agent-driver/dist/adapters/cursor/acp-lane.js
2387
2583
  import { EventEmitter as EventEmitter2 } from "node:events";
2584
+
2585
+ // agent-driver/dist/adapters/cursor/catalog-probe.js
2388
2586
  var ACP_PROTOCOL_VERSION = 1;
2389
- var HANDSHAKE_TIMEOUT_MS = 15000;
2390
2587
  var AUTH_METHOD_ID = "cursor_login";
2588
+ var CATALOG_PROBE_TIMEOUT_MS = 15000;
2589
+ var CATALOG_PROBE_OUTPUT_MAX_BYTES = 1024 * 1024;
2590
+ var MODEL_DISPLAY_NAME_MAX = 256;
2591
+ var MODEL_OPTION_NESTING_MAX = 16;
2592
+ function record(value) {
2593
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
2594
+ }
2595
+ function normalizeDisplayName(value) {
2596
+ if (typeof value !== "string")
2597
+ return;
2598
+ const displayName = value.trim();
2599
+ return displayName && displayName.length <= MODEL_DISPLAY_NAME_MAX ? displayName : undefined;
2600
+ }
2601
+ function flattenCursorAcpSelectOptions(value, depth = 0) {
2602
+ if (!Array.isArray(value) || depth > MODEL_OPTION_NESTING_MAX)
2603
+ return [];
2604
+ const options = [];
2605
+ for (const item of value) {
2606
+ if (Array.isArray(item)) {
2607
+ options.push(...flattenCursorAcpSelectOptions(item, depth + 1));
2608
+ continue;
2609
+ }
2610
+ const candidate = record(item);
2611
+ if (!candidate)
2612
+ continue;
2613
+ const exactValue = normalizeRuntimeModelId(candidate.value);
2614
+ if (exactValue) {
2615
+ const name = normalizeDisplayName(candidate.name);
2616
+ options.push({ value: exactValue, ...name ? { name } : {} });
2617
+ }
2618
+ if (Array.isArray(candidate.options)) {
2619
+ options.push(...flattenCursorAcpSelectOptions(candidate.options, depth + 1));
2620
+ }
2621
+ }
2622
+ return options;
2623
+ }
2624
+ function parseCursorAcpModelCatalog(session) {
2625
+ const payload = record(session);
2626
+ const configOptions = Array.isArray(payload?.configOptions) ? payload.configOptions : [];
2627
+ const modelConfig = configOptions.map(record).find((option) => option?.id === "model") ?? null;
2628
+ if (!modelConfig)
2629
+ return;
2630
+ const seen = new Set;
2631
+ const models = [];
2632
+ for (const option of flattenCursorAcpSelectOptions(modelConfig.options)) {
2633
+ if (option.value === "default[]" || seen.has(option.value))
2634
+ continue;
2635
+ if (models.length >= RUNTIME_MODEL_CATALOG_MAX)
2636
+ return;
2637
+ seen.add(option.value);
2638
+ models.push({
2639
+ id: option.value,
2640
+ ...option.name ? { displayName: option.name } : {},
2641
+ supportedReasoningEfforts: []
2642
+ });
2643
+ }
2644
+ return models.length > 0 ? { updateMode: "unsupported", models } : undefined;
2645
+ }
2646
+ async function cleanupProbeProcess(process2) {
2647
+ if (process2.pid) {
2648
+ await killProcessTree(process2.pid, { graceMs: 250 }).catch(() => {});
2649
+ return;
2650
+ }
2651
+ if (process2.exitCode === null && process2.signalCode === null)
2652
+ process2.kill("SIGTERM");
2653
+ }
2654
+ async function probeCursorAcpCatalog(command, options = {}) {
2655
+ const cwd = options.cwd ?? process.cwd();
2656
+ const spec = resolveSpawnSpec("cursor-agent", ["acp"], command);
2657
+ let processHandle;
2658
+ try {
2659
+ processHandle = (options.spawn ?? spawnAgentProcess)(spec.command, spec.args, {
2660
+ cwd,
2661
+ env: { ...process.env, CI: "1" },
2662
+ shell: spec.shell
2663
+ });
2664
+ } catch {
2665
+ return;
2666
+ }
2667
+ return new Promise((resolve2) => {
2668
+ let settled = false;
2669
+ let buffer = "";
2670
+ let outputBytes = 0;
2671
+ let requestId = 0;
2672
+ let expectedId = 0;
2673
+ let expectedMethod = "";
2674
+ const finish = (catalog) => {
2675
+ if (settled)
2676
+ return;
2677
+ settled = true;
2678
+ clearTimeout(timer);
2679
+ const cleanup = options.cleanup ?? cleanupProbeProcess;
2680
+ Promise.resolve().then(() => cleanup(processHandle)).catch(() => {}).finally(() => resolve2(catalog));
2681
+ };
2682
+ const request = (method, params) => {
2683
+ if (settled)
2684
+ return;
2685
+ const stdin = processHandle.stdin;
2686
+ if (!stdin || stdin.destroyed || stdin.writableEnded || stdin.writable === false)
2687
+ return finish();
2688
+ expectedId = ++requestId;
2689
+ expectedMethod = method;
2690
+ try {
2691
+ stdin.write(`${jsonRpcRequest(method, params, expectedId)}
2692
+ `);
2693
+ } catch {
2694
+ finish();
2695
+ }
2696
+ };
2697
+ const onLine = (line) => {
2698
+ const parsed = tryParseJsonLine(line);
2699
+ const message = record(parsed);
2700
+ if (!message)
2701
+ return finish();
2702
+ if (message.id !== expectedId)
2703
+ return;
2704
+ if (message.error !== undefined)
2705
+ return finish();
2706
+ if (!Object.prototype.hasOwnProperty.call(message, "result"))
2707
+ return finish();
2708
+ if (expectedMethod === "authenticate") {
2709
+ request("session/new", { cwd, mcpServers: [] });
2710
+ return;
2711
+ }
2712
+ const result = record(message.result);
2713
+ if (!result)
2714
+ return finish();
2715
+ if (expectedMethod === "initialize") {
2716
+ const authMethods = Array.isArray(result.authMethods) ? result.authMethods : [];
2717
+ if (result.protocolVersion !== ACP_PROTOCOL_VERSION || !authMethods.some((method) => record(method)?.id === AUTH_METHOD_ID))
2718
+ return finish();
2719
+ request("authenticate", { methodId: AUTH_METHOD_ID });
2720
+ return;
2721
+ }
2722
+ if (expectedMethod !== "session/new" || typeof result.sessionId !== "string" || !result.sessionId.trim())
2723
+ return finish();
2724
+ finish(parseCursorAcpModelCatalog(result));
2725
+ };
2726
+ const timer = setTimeout(() => finish(), options.timeoutMs ?? CATALOG_PROBE_TIMEOUT_MS);
2727
+ timer.unref?.();
2728
+ processHandle.stdout?.on("data", (chunk) => {
2729
+ if (settled)
2730
+ return;
2731
+ const text = chunk.toString();
2732
+ outputBytes += Buffer.byteLength(text);
2733
+ if (outputBytes > (options.outputMaxBytes ?? CATALOG_PROBE_OUTPUT_MAX_BYTES))
2734
+ return finish();
2735
+ buffer += text;
2736
+ const lines = buffer.split(`
2737
+ `);
2738
+ buffer = lines.pop() ?? "";
2739
+ for (const line of lines)
2740
+ if (line.trim())
2741
+ onLine(line);
2742
+ });
2743
+ processHandle.stderr?.on("data", (chunk) => {
2744
+ if (settled)
2745
+ return;
2746
+ outputBytes += Buffer.byteLength(chunk.toString());
2747
+ if (outputBytes > (options.outputMaxBytes ?? CATALOG_PROBE_OUTPUT_MAX_BYTES))
2748
+ finish();
2749
+ });
2750
+ processHandle.on("error", () => finish());
2751
+ processHandle.on("exit", () => finish());
2752
+ request("initialize", {
2753
+ protocolVersion: ACP_PROTOCOL_VERSION,
2754
+ clientCapabilities: {
2755
+ fs: { readTextFile: false, writeTextFile: false },
2756
+ terminal: false
2757
+ },
2758
+ clientInfo: { name: "alook-agent-driver-probe", version: "0.1.25" }
2759
+ });
2760
+ });
2761
+ }
2762
+
2763
+ // agent-driver/dist/adapters/cursor/acp-lane.js
2764
+ var ACP_PROTOCOL_VERSION2 = 1;
2765
+ var HANDSHAKE_TIMEOUT_MS = 15000;
2766
+ var AUTH_METHOD_ID2 = "cursor_login";
2391
2767
  var PROMPT_STOP_REASONS = new Set([
2392
2768
  "end_turn",
2393
2769
  "max_tokens",
@@ -2411,16 +2787,16 @@ class CursorAcpRpcError extends Error {
2411
2787
  this.code = code;
2412
2788
  }
2413
2789
  }
2414
- function record(value) {
2790
+ function record2(value) {
2415
2791
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
2416
2792
  }
2417
2793
  function safeLabel(value) {
2418
2794
  return typeof value === "string" && /^[a-z0-9_-]{1,64}$/i.test(value) ? value : "unknown";
2419
2795
  }
2420
2796
  function rpcErrorMessage(error) {
2421
- const payload = record(error);
2797
+ const payload = record2(error);
2422
2798
  const message = typeof payload?.message === "string" && payload.message.trim() ? payload.message : "Cursor ACP request failed";
2423
- const data = record(payload?.data);
2799
+ const data = record2(payload?.data);
2424
2800
  const detail = typeof data?.message === "string" && data.message.trim() ? data.message : undefined;
2425
2801
  return detail ? `${message}: ${detail}` : message;
2426
2802
  }
@@ -2428,26 +2804,6 @@ function isMissingSessionError(error) {
2428
2804
  const message = error instanceof Error ? error.message : String(error);
2429
2805
  return /\bsession\b.*\b(not found|missing|unknown|invalid)\b/i.test(message) || /\b(not found|missing|unknown|invalid)\b.*\bsession\b/i.test(message);
2430
2806
  }
2431
- function flattenSelectOptions(value) {
2432
- if (!Array.isArray(value))
2433
- return [];
2434
- const out = [];
2435
- for (const item of value) {
2436
- if (Array.isArray(item)) {
2437
- out.push(...flattenSelectOptions(item));
2438
- continue;
2439
- }
2440
- const candidate = record(item);
2441
- if (!candidate)
2442
- continue;
2443
- if (typeof candidate.value === "string") {
2444
- out.push({ value: candidate.value, ...typeof candidate.name === "string" ? { name: candidate.name } : {} });
2445
- }
2446
- if (Array.isArray(candidate.options))
2447
- out.push(...flattenSelectOptions(candidate.options));
2448
- }
2449
- return out;
2450
- }
2451
2807
 
2452
2808
  class CursorAcpLane {
2453
2809
  factory;
@@ -2571,30 +2927,30 @@ class CursorAcpLane {
2571
2927
  }
2572
2928
  }
2573
2929
  async handshake(ctx) {
2574
- const initialize = record(await this.call("initialize", {
2575
- protocolVersion: ACP_PROTOCOL_VERSION,
2930
+ const initialize = record2(await this.call("initialize", {
2931
+ protocolVersion: ACP_PROTOCOL_VERSION2,
2576
2932
  clientCapabilities: {
2577
2933
  fs: { readTextFile: false, writeTextFile: false },
2578
2934
  terminal: false
2579
2935
  },
2580
2936
  clientInfo: { name: "alook-agent-driver", version: "0.1.14" }
2581
2937
  }));
2582
- if (initialize?.protocolVersion !== ACP_PROTOCOL_VERSION) {
2938
+ if (initialize?.protocolVersion !== ACP_PROTOCOL_VERSION2) {
2583
2939
  throw new CursorAcpIncompatibleError("Installed Cursor ACP does not support protocol version 1");
2584
2940
  }
2585
- const capabilities = record(initialize.agentCapabilities);
2941
+ const capabilities = record2(initialize.agentCapabilities);
2586
2942
  if (capabilities?.loadSession !== true) {
2587
2943
  throw new CursorAcpIncompatibleError("Installed Cursor ACP does not support persistent session loading");
2588
2944
  }
2589
2945
  const authMethods = Array.isArray(initialize.authMethods) ? initialize.authMethods : [];
2590
- if (!authMethods.some((method) => record(method)?.id === AUTH_METHOD_ID)) {
2946
+ if (!authMethods.some((method) => record2(method)?.id === AUTH_METHOD_ID2)) {
2591
2947
  throw new CursorAcpIncompatibleError("Installed Cursor ACP does not expose Cursor login authentication");
2592
2948
  }
2593
- await this.call("authenticate", { methodId: AUTH_METHOD_ID });
2949
+ await this.call("authenticate", { methodId: AUTH_METHOD_ID2 });
2594
2950
  let session;
2595
2951
  if (ctx.config.sessionId) {
2596
2952
  try {
2597
- session = record(await this.call("session/load", {
2953
+ session = record2(await this.call("session/load", {
2598
2954
  sessionId: ctx.config.sessionId,
2599
2955
  cwd: ctx.workingDirectory,
2600
2956
  mcpServers: []
@@ -2606,15 +2962,25 @@ class CursorAcpLane {
2606
2962
  throw error;
2607
2963
  }
2608
2964
  } else {
2609
- session = record(await this.call("session/new", { cwd: ctx.workingDirectory, mcpServers: [] }));
2965
+ session = record2(await this.call("session/new", { cwd: ctx.workingDirectory, mcpServers: [] }));
2610
2966
  }
2611
- if (!session || typeof session.sessionId !== "string" || !session.sessionId.trim()) {
2967
+ if (!session)
2968
+ throw new Error("Cursor ACP did not return a valid session response");
2969
+ const returnedSessionId = session.sessionId;
2970
+ if (returnedSessionId !== undefined && (typeof returnedSessionId !== "string" || !returnedSessionId.trim())) {
2612
2971
  throw new Error("Cursor ACP did not return a valid session id");
2613
2972
  }
2614
- if (ctx.config.sessionId && session.sessionId !== ctx.config.sessionId) {
2615
- throw new CursorAcpResetRequiredError("Cursor ACP loaded a different session; reset this agent before continuing");
2973
+ if (ctx.config.sessionId) {
2974
+ if (returnedSessionId !== undefined && returnedSessionId !== ctx.config.sessionId) {
2975
+ throw new CursorAcpResetRequiredError("Cursor ACP loaded a different session; reset this agent before continuing");
2976
+ }
2977
+ this.sessionId = ctx.config.sessionId;
2978
+ } else {
2979
+ if (typeof returnedSessionId !== "string") {
2980
+ throw new Error("Cursor ACP did not return a valid session id");
2981
+ }
2982
+ this.sessionId = returnedSessionId;
2616
2983
  }
2617
- this.sessionId = session.sessionId;
2618
2984
  await this.configureModel(session, ctx);
2619
2985
  }
2620
2986
  async configureModel(session, ctx) {
@@ -2622,24 +2988,30 @@ class CursorAcpLane {
2622
2988
  if (!requestedModel)
2623
2989
  return;
2624
2990
  const configOptions = Array.isArray(session.configOptions) ? session.configOptions : [];
2625
- const modelConfig = configOptions.map(record).find((option) => option?.id === "model") ?? null;
2991
+ const modelConfig = configOptions.map(record2).find((option) => option?.id === "model") ?? null;
2626
2992
  if (!modelConfig) {
2627
2993
  throw new CursorAcpIncompatibleError("Installed Cursor ACP does not support model configuration");
2628
2994
  }
2629
- const options = flattenSelectOptions(modelConfig.options);
2630
- const match = options.find((option) => option.value === requestedModel) ?? options.find((option) => option.name === requestedModel);
2995
+ const options = flattenCursorAcpSelectOptions(modelConfig.options);
2996
+ const match = options.find((option) => option.value === requestedModel);
2631
2997
  if (!match) {
2632
2998
  throw new CursorAcpIncompatibleError(`Configured Cursor model is unavailable through ACP: ${requestedModel}`);
2633
2999
  }
3000
+ let response;
2634
3001
  try {
2635
- await this.call("session/set_config_option", {
3002
+ response = record2(await this.call("session/set_config_option", {
2636
3003
  sessionId: this.sessionId,
2637
3004
  configId: "model",
2638
3005
  value: match.value
2639
- });
3006
+ }));
2640
3007
  } catch {
2641
3008
  throw new CursorAcpIncompatibleError("Installed Cursor ACP rejected model configuration");
2642
3009
  }
3010
+ const confirmedOptions = Array.isArray(response?.configOptions) ? response.configOptions : [];
3011
+ const confirmedModel = confirmedOptions.map(record2).find((option) => option?.id === "model") ?? null;
3012
+ if (confirmedModel?.currentValue !== match.value) {
3013
+ throw new CursorAcpIncompatibleError("Cursor ACP did not confirm the exact configured model");
3014
+ }
2643
3015
  }
2644
3016
  admitPrompt(text) {
2645
3017
  if (!this.sessionId)
@@ -2675,7 +3047,7 @@ class CursorAcpLane {
2675
3047
  completePrompt(active, value) {
2676
3048
  if (this.activePrompt?.requestId !== active.requestId)
2677
3049
  return;
2678
- const result = record(value);
3050
+ const result = record2(value);
2679
3051
  if (!result || typeof result.stopReason !== "string" || !PROMPT_STOP_REASONS.has(result.stopReason)) {
2680
3052
  this.failPrompt(active, new Error("Cursor ACP prompt response did not contain a supported stopReason"));
2681
3053
  return;
@@ -2817,7 +3189,7 @@ class CursorAcpLane {
2817
3189
  });
2818
3190
  }
2819
3191
  handleMessage(value) {
2820
- const message = record(value);
3192
+ const message = record2(value);
2821
3193
  if (!message || message.jsonrpc !== "2.0") {
2822
3194
  this.protocolFailure("Cursor ACP emitted an invalid JSON-RPC message");
2823
3195
  return;
@@ -2845,7 +3217,7 @@ class CursorAcpLane {
2845
3217
  if (pending.kind === "prompt") {
2846
3218
  this.pending.delete(id);
2847
3219
  if (message.error !== undefined) {
2848
- const payload = record(message.error);
3220
+ const payload = record2(message.error);
2849
3221
  this.failPrompt(pending.active, new CursorAcpRpcError(pending.method, typeof payload?.code === "number" ? payload.code : undefined, rpcErrorMessage(message.error)));
2850
3222
  } else if (!("result" in message)) {
2851
3223
  this.failPrompt(pending.active, new Error("Cursor ACP response omitted result"));
@@ -2855,7 +3227,7 @@ class CursorAcpLane {
2855
3227
  return;
2856
3228
  }
2857
3229
  if (message.error !== undefined) {
2858
- const payload = record(message.error);
3230
+ const payload = record2(message.error);
2859
3231
  this.settleRequest(id, false, new CursorAcpRpcError(pending.method, typeof payload?.code === "number" ? payload.code : undefined, rpcErrorMessage(message.error)));
2860
3232
  return;
2861
3233
  }
@@ -2887,9 +3259,9 @@ class CursorAcpLane {
2887
3259
  this.diagnostic("warning", `Unsupported Cursor ACP client request: ${safeLabel(method)}`);
2888
3260
  return;
2889
3261
  }
2890
- const payload = record(params);
3262
+ const payload = record2(params);
2891
3263
  const sameSession = payload?.sessionId === this.sessionId;
2892
- const options = Array.isArray(payload?.options) ? payload.options.map(record).filter(Boolean) : [];
3264
+ const options = Array.isArray(payload?.options) ? payload.options.map(record2).filter(Boolean) : [];
2893
3265
  const allowOnce = options.find((option) => option.kind === "allow_once" && typeof option.optionId === "string" && option.optionId.trim().length > 0);
2894
3266
  if (!this.ready || !this.activePrompt || !sameSession || !allowOnce) {
2895
3267
  this.write({ jsonrpc: "2.0", id, result: { outcome: { outcome: "cancelled" } } });
@@ -2910,7 +3282,7 @@ class CursorAcpLane {
2910
3282
  this.diagnostic("warning", `Unsupported Cursor ACP notification: ${safeLabel(method)}`);
2911
3283
  }
2912
3284
  handleSessionUpdate(params) {
2913
- const payload = record(params);
3285
+ const payload = record2(params);
2914
3286
  if (!payload || payload.sessionId !== this.sessionId) {
2915
3287
  this.diagnostic("warning", "Cursor ACP emitted an update for a different session");
2916
3288
  return;
@@ -2919,18 +3291,18 @@ class CursorAcpLane {
2919
3291
  this.diagnostic("warning", "Cursor ACP emitted a session update without an active prompt");
2920
3292
  return;
2921
3293
  }
2922
- const update = record(payload.update) ?? {};
3294
+ const update = record2(payload.update) ?? {};
2923
3295
  const updateType = update?.sessionUpdate;
2924
3296
  switch (updateType) {
2925
3297
  case "agent_message_chunk": {
2926
- const content = record(update.content);
3298
+ const content = record2(update.content);
2927
3299
  if (content?.type === "text" && typeof content.text === "string") {
2928
3300
  this.events.emit("runtime_event", { kind: "assistant_message_delta", text: content.text });
2929
3301
  }
2930
3302
  return;
2931
3303
  }
2932
3304
  case "agent_thought_chunk": {
2933
- const content = record(update.content);
3305
+ const content = record2(update.content);
2934
3306
  if (content?.type === "text" && typeof content.text === "string") {
2935
3307
  this.events.emit("runtime_event", { kind: "assistant_reasoning_delta", text: content.text });
2936
3308
  }
@@ -3002,6 +3374,7 @@ class CursorAcpLane {
3002
3374
 
3003
3375
  // agent-driver/dist/adapters/cursor/index.js
3004
3376
  class CursorDriver {
3377
+ catalogProbe;
3005
3378
  id = "cursor";
3006
3379
  instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
3007
3380
  execution = {
@@ -3010,8 +3383,23 @@ class CursorDriver {
3010
3383
  wakeStart: "immediate",
3011
3384
  terminalOwnership: "transport_request"
3012
3385
  };
3013
- probe(command) {
3014
- return probeCliRuntime("cursor-agent", {}, command);
3386
+ constructor(catalogProbe = probeCursorAcpCatalog) {
3387
+ this.catalogProbe = catalogProbe;
3388
+ }
3389
+ async probe(command) {
3390
+ const result = probeCliRuntime("cursor-agent", {}, command);
3391
+ if (result.status !== "healthy")
3392
+ return result;
3393
+ let reasoning;
3394
+ try {
3395
+ reasoning = await this.catalogProbe(command);
3396
+ } catch {
3397
+ reasoning = undefined;
3398
+ }
3399
+ return {
3400
+ ...result,
3401
+ reasoning
3402
+ };
3015
3403
  }
3016
3404
  async openLane(ctx, options) {
3017
3405
  return new CursorAcpLane(this, ctx, { onRawStdoutLine: options?.onRawStdoutLine });
@@ -3068,7 +3456,7 @@ class OpenCodeHttpError extends Error {
3068
3456
  this.status = status;
3069
3457
  }
3070
3458
  }
3071
- function record2(value) {
3459
+ function record3(value) {
3072
3460
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
3073
3461
  }
3074
3462
  function safeLabel2(value) {
@@ -3115,7 +3503,7 @@ function parseModelRef(model) {
3115
3503
  return { providerID: model.slice(0, slash), id: model.slice(slash + 1) };
3116
3504
  }
3117
3505
  function messageFromError(value) {
3118
- const payload = record2(value);
3506
+ const payload = record3(value);
3119
3507
  const message = typeof payload?.message === "string" && payload.message.trim() ? payload.message : undefined;
3120
3508
  return message ? "OpenCode turn failed" : "OpenCode reported an inconsistent turn outcome";
3121
3509
  }
@@ -3157,6 +3545,7 @@ class OpenCodeServiceLane {
3157
3545
  lastDurableSeq = 0;
3158
3546
  durableSeqById = new Map;
3159
3547
  durableIdBySeq = new Map;
3548
+ usageProjector = new SettledUsageProjector;
3160
3549
  toolNames = new Map;
3161
3550
  handledPermissions = new Set;
3162
3551
  permissionFlights = new Map;
@@ -3449,7 +3838,7 @@ class OpenCodeServiceLane {
3449
3838
  const healthTimeoutMs = Math.max(1, Math.min(1000, deadline - Date.now(), this.options.requestTimeoutMs ?? REQUEST_TIMEOUT_MS));
3450
3839
  const { response, body } = await this.fetchJsonWithTimeout("/global/health", { method: "GET" }, "health", healthTimeoutMs);
3451
3840
  if (response.ok) {
3452
- const health = record2(body);
3841
+ const health = record3(body);
3453
3842
  if (health?.healthy !== true || health.version !== SUPPORTED_VERSION) {
3454
3843
  throw new OpenCodeIncompatibleError(`Installed OpenCode service must be version ${SUPPORTED_VERSION}`);
3455
3844
  }
@@ -3470,8 +3859,8 @@ class OpenCodeServiceLane {
3470
3859
  const { response, body } = await this.fetchJsonWithTimeout("/doc", { method: "GET" }, "OpenAPI");
3471
3860
  if (!response.ok)
3472
3861
  throw new OpenCodeIncompatibleError("Installed OpenCode service does not expose its OpenAPI document");
3473
- const document = record2(body);
3474
- const paths = record2(document?.paths);
3862
+ const document = record3(body);
3863
+ const paths = record3(document?.paths);
3475
3864
  const required = [
3476
3865
  "/api/session",
3477
3866
  "/api/session/active",
@@ -3484,7 +3873,7 @@ class OpenCodeServiceLane {
3484
3873
  "/api/session/{sessionID}/permission/{requestID}/reply",
3485
3874
  "/api/event"
3486
3875
  ];
3487
- if (!paths || required.some((path6) => !record2(paths[path6]))) {
3876
+ if (!paths || required.some((path6) => !record3(paths[path6]))) {
3488
3877
  throw new OpenCodeIncompatibleError("Installed OpenCode service is missing required v2 session capabilities");
3489
3878
  }
3490
3879
  }
@@ -3497,7 +3886,7 @@ class OpenCodeServiceLane {
3497
3886
  }
3498
3887
  if (!response2.ok)
3499
3888
  throw new OpenCodeHttpError(response2.status, "session resume");
3500
- const session2 = record2(record2(body2)?.data);
3889
+ const session2 = record3(record3(body2)?.data);
3501
3890
  if (session2?.id !== resumeId) {
3502
3891
  throw new OpenCodeResetRequiredError("OpenCode v2 returned a different resumed session; reset this agent before continuing");
3503
3892
  }
@@ -3518,8 +3907,8 @@ class OpenCodeServiceLane {
3518
3907
  }, "session create");
3519
3908
  if (!response.ok)
3520
3909
  throw new OpenCodeHttpError(response.status, "session create");
3521
- const payload = record2(responseBody);
3522
- const session = record2(payload?.data);
3910
+ const payload = record3(responseBody);
3911
+ const session = record3(payload?.data);
3523
3912
  if (typeof session?.id !== "string" || !/^ses/.test(session.id)) {
3524
3913
  throw new Error("OpenCode v2 did not return a valid session id");
3525
3914
  }
@@ -3676,7 +4065,7 @@ class OpenCodeServiceLane {
3676
4065
  const { response, body: responseBody } = await this.fetchJsonWithTimeout(`/api/session/${encodeURIComponent(this.sessionId)}/history?after=${historyCursor}&limit=${HISTORY_PAGE_LIMIT}`, { method: "GET" }, "session history");
3677
4066
  if (!response.ok)
3678
4067
  throw new OpenCodeHttpError(response.status, "session history");
3679
- const body = record2(responseBody);
4068
+ const body = record3(responseBody);
3680
4069
  if (!Array.isArray(body?.data) || typeof body.hasMore !== "boolean") {
3681
4070
  throw new OpenCodeProtocolError("OpenCode session history returned an invalid page");
3682
4071
  }
@@ -3694,10 +4083,11 @@ class OpenCodeServiceLane {
3694
4083
  return run;
3695
4084
  }
3696
4085
  async handleDurableEvent(value, project) {
3697
- const event = record2(value);
3698
- const durable = record2(event?.durable);
3699
- const data = record2(event?.data);
3700
- 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) {
4086
+ const event = record3(value);
4087
+ const durable = record3(event?.durable);
4088
+ const data = record3(event?.data);
4089
+ const backendSessionId = this.sessionId;
4090
+ 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) {
3701
4091
  throw new OpenCodeProtocolError("OpenCode session stream emitted an invalid durable event");
3702
4092
  }
3703
4093
  const seq = Number(durable.seq);
@@ -3786,22 +4176,28 @@ class OpenCodeServiceLane {
3786
4176
  ...!successful ? { message: "OpenCode reported an unsupported final step outcome" } : {}
3787
4177
  });
3788
4178
  }
3789
- const tokens = record2(data.tokens);
3790
- if (tokens && data.finish !== "tool-calls") {
3791
- const cache = record2(tokens.cache);
3792
- const metric2 = (value2) => typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0 ? value2 : null;
3793
- const cacheParts = [cache?.read, cache?.write].filter((value2) => typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0);
3794
- const cacheTotal = cacheParts.reduce((sum, value2) => sum + value2, 0);
3795
- this.events.emit("runtime_event", {
3796
- kind: "telemetry",
3797
- name: "token_usage",
4179
+ const tokens = record3(data.tokens);
4180
+ if (tokens) {
4181
+ const cache = record3(tokens.cache);
4182
+ const identity = {
4183
+ runtime: "opencode",
4184
+ backendSessionId,
4185
+ providerRecordId: event.id
4186
+ };
4187
+ const usage = this.usageProjector.project({
4188
+ ...identity,
3798
4189
  source: "opencode.v2",
3799
- usage: {
3800
- input: metric2(tokens.input),
3801
- output: metric2(tokens.output),
3802
- cache: cacheParts.length > 0 && Number.isSafeInteger(cacheTotal) ? cacheTotal : null
3803
- }
4190
+ input: tokens.input,
4191
+ output: tokens.output,
4192
+ reasoning: tokens.reasoning,
4193
+ cacheRead: cache?.read,
4194
+ cacheWrite: cache?.write,
4195
+ inputIncludesCache: false,
4196
+ outputIncludesReasoning: false
3804
4197
  });
4198
+ if (usage)
4199
+ this.events.emit("runtime_event", usage);
4200
+ this.usageProjector.release(identity);
3805
4201
  }
3806
4202
  break;
3807
4203
  }
@@ -3815,8 +4211,8 @@ class OpenCodeServiceLane {
3815
4211
  return seq;
3816
4212
  }
3817
4213
  async handleLiveEvent(value) {
3818
- const event = record2(value);
3819
- const data = record2(event?.data);
4214
+ const event = record3(value);
4215
+ const data = record3(event?.data);
3820
4216
  if (event?.type !== "permission.v2.asked" || data?.sessionID !== this.sessionId)
3821
4217
  return;
3822
4218
  if (typeof data.id !== "string" || !/^per/.test(data.id)) {
@@ -3830,11 +4226,11 @@ class OpenCodeServiceLane {
3830
4226
  const { response, body: responseBody } = await this.fetchJsonWithTimeout(`/api/session/${encodeURIComponent(this.sessionId)}/permission`, { method: "GET" }, "permission list");
3831
4227
  if (!response.ok)
3832
4228
  throw new OpenCodeHttpError(response.status, "permission list");
3833
- const body = record2(responseBody);
4229
+ const body = record3(responseBody);
3834
4230
  if (!Array.isArray(body?.data))
3835
4231
  throw new OpenCodeProtocolError("OpenCode permission list returned invalid data");
3836
4232
  for (const item of body.data) {
3837
- const permission = record2(item);
4233
+ const permission = record3(item);
3838
4234
  if (permission?.sessionID === this.sessionId && typeof permission.id === "string") {
3839
4235
  await this.replyPermission(permission.id);
3840
4236
  }
@@ -3901,8 +4297,8 @@ class OpenCodeServiceLane {
3901
4297
  }, "prompt admission");
3902
4298
  if (!response.ok)
3903
4299
  throw new OpenCodeHttpError(response.status, "prompt admission");
3904
- const body = record2(responseBody);
3905
- const admitted = record2(body?.data);
4300
+ const body = record3(responseBody);
4301
+ const admitted = record3(body?.data);
3906
4302
  if (admitted?.id !== messageId || admitted.sessionID !== this.sessionId || admitted.delivery !== delivery || !Number.isInteger(admitted.admittedSeq) || Number(admitted.admittedSeq) < 0) {
3907
4303
  throw new OpenCodeProtocolError("OpenCode prompt admission returned an invalid receipt");
3908
4304
  }
@@ -3969,8 +4365,8 @@ class OpenCodeServiceLane {
3969
4365
  const { response, body: responseBody } = await this.fetchJsonWithTimeout("/api/session/active", { method: "GET" }, "active session query");
3970
4366
  if (!response.ok)
3971
4367
  throw new OpenCodeHttpError(response.status, "active session query");
3972
- const body = record2(responseBody);
3973
- const active = record2(body?.data);
4368
+ const body = record3(responseBody);
4369
+ const active = record3(body?.data);
3974
4370
  if (!active)
3975
4371
  throw new OpenCodeProtocolError("OpenCode active session query returned invalid data");
3976
4372
  if (!this.barrierStillCurrent(root, identity, generation))
@@ -4175,6 +4571,7 @@ function createOpenCodeMessageId() {
4175
4571
  }
4176
4572
 
4177
4573
  class OpenCodeDriver {
4574
+ outputProbe;
4178
4575
  id = "opencode";
4179
4576
  instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
4180
4577
  execution = {
@@ -4183,8 +4580,19 @@ class OpenCodeDriver {
4183
4580
  wakeStart: "immediate",
4184
4581
  terminalOwnership: "transport_request"
4185
4582
  };
4583
+ constructor(outputProbe = probeCommandOutput) {
4584
+ this.outputProbe = outputProbe;
4585
+ }
4186
4586
  probe(command) {
4187
- return probeCliRuntime("opencode", {}, command);
4587
+ const result = probeCliRuntime("opencode", {}, command);
4588
+ if (result.status !== "healthy")
4589
+ return result;
4590
+ const spec = resolveSpawnSpec("opencode", ["models", "--pure"], command);
4591
+ const output = this.outputProbe(spec.command, spec.args);
4592
+ return {
4593
+ ...result,
4594
+ reasoning: output.ok ? parseOpenCodeModelCatalog(output.output) : undefined
4595
+ };
4188
4596
  }
4189
4597
  beginTurn() {
4190
4598
  return createOpenCodeMessageId();
@@ -4438,6 +4846,7 @@ function createPiSessionDependencies(ctx, loadSdk = loadPiSdkModule) {
4438
4846
 
4439
4847
  // agent-driver/dist/adapters/pi/index.js
4440
4848
  var PI_SDK_PACKAGE_NAME2 = "@earendil-works/pi-coding-agent";
4849
+ var PI_MODEL_PROBE_TIMEOUT_MS = 5000;
4441
4850
  function isPiSdkPackageJson(pkgJsonPath) {
4442
4851
  if (!existsSync3(pkgJsonPath))
4443
4852
  return false;
@@ -4507,6 +4916,11 @@ function readPiSdkVersion() {
4507
4916
  } catch {}
4508
4917
  return resolvePiSdkVersionFromPath();
4509
4918
  }
4919
+ function piUsageState(state) {
4920
+ state.usageProjector ??= new SettledUsageProjector;
4921
+ state.pendingUsageRecordIds ??= new Set;
4922
+ return { projector: state.usageProjector, pending: state.pendingUsageRecordIds };
4923
+ }
4510
4924
  function mapPiSdkEvent(event, sessionId, state) {
4511
4925
  if (event?.type === "message_update") {
4512
4926
  const d = event.assistantMessageEvent ?? {};
@@ -4527,6 +4941,35 @@ function mapPiSdkEvent(event, sessionId, state) {
4527
4941
  }
4528
4942
  }
4529
4943
  switch (event?.type) {
4944
+ case "message_end": {
4945
+ const message = event.message;
4946
+ if (message?.role !== "assistant" || !message.usage)
4947
+ return [];
4948
+ state.usageRecordSequence = (state.usageRecordSequence ?? 0) + 1;
4949
+ const providerRecordId = typeof message.responseId === "string" && message.responseId ? message.responseId : `live:${message.timestamp ?? "unknown"}:${state.usageRecordSequence}`;
4950
+ const { projector, pending } = piUsageState(state);
4951
+ const identity = { runtime: "pi", backendSessionId: sessionId, providerRecordId };
4952
+ const usage = projector.project({
4953
+ ...identity,
4954
+ source: "pi_message_end",
4955
+ input: message.usage.input,
4956
+ output: message.usage.output,
4957
+ cacheRead: message.usage.cacheRead,
4958
+ cacheWrite: message.usage.cacheWrite,
4959
+ inputIncludesCache: false,
4960
+ outputIncludesReasoning: true
4961
+ });
4962
+ pending.add(providerRecordId);
4963
+ return usage ? [usage] : [];
4964
+ }
4965
+ case "turn_end": {
4966
+ const { projector, pending } = piUsageState(state);
4967
+ for (const providerRecordId of pending) {
4968
+ projector.release({ runtime: "pi", backendSessionId: sessionId, providerRecordId });
4969
+ }
4970
+ pending.clear();
4971
+ return [];
4972
+ }
4530
4973
  case "auto_retry_start":
4531
4974
  return [{ kind: "runtime_recovery", stage: "retrying", source: "pi_auto_retry" }];
4532
4975
  case "auto_retry_end":
@@ -4546,6 +4989,8 @@ function mapPiSdkEvent(event, sessionId, state) {
4546
4989
 
4547
4990
  class PiDriver {
4548
4991
  dependenciesFor;
4992
+ loadSdk;
4993
+ readVersion;
4549
4994
  id = "pi";
4550
4995
  instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
4551
4996
  execution = {
@@ -4556,15 +5001,36 @@ class PiDriver {
4556
5001
  };
4557
5002
  sessionId = null;
4558
5003
  terminalSequence = 0;
4559
- constructor(dependenciesFor = createPiSessionDependencies) {
5004
+ constructor(dependenciesFor = createPiSessionDependencies, loadSdk = loadPiSdkModule, readVersion = readPiSdkVersion) {
4560
5005
  this.dependenciesFor = dependenciesFor;
5006
+ this.loadSdk = loadSdk;
5007
+ this.readVersion = readVersion;
4561
5008
  }
4562
- probe() {
4563
- const version = readPiSdkVersion();
5009
+ async probe() {
5010
+ const version = this.readVersion();
4564
5011
  if (!version) {
4565
5012
  return { status: "unhealthy", lastError: "sdk_not_installed" };
4566
5013
  }
4567
- return { status: "healthy", version };
5014
+ let timer;
5015
+ try {
5016
+ const reasoning = await Promise.race([
5017
+ this.loadSdk().then(async (sdk) => {
5018
+ const authStorage = sdk.AuthStorage.create();
5019
+ const registry = sdk.ModelRegistry.create(authStorage);
5020
+ return parsePiModelCatalog(await registry.getAvailable());
5021
+ }),
5022
+ new Promise((resolve3) => {
5023
+ timer = setTimeout(() => resolve3(undefined), PI_MODEL_PROBE_TIMEOUT_MS);
5024
+ timer.unref?.();
5025
+ })
5026
+ ]);
5027
+ return { status: "healthy", version, reasoning };
5028
+ } catch {
5029
+ return { status: "healthy", version, reasoning: undefined };
5030
+ } finally {
5031
+ if (timer)
5032
+ clearTimeout(timer);
5033
+ }
4568
5034
  }
4569
5035
  async openLane(ctx) {
4570
5036
  const deps = this.dependenciesFor(ctx);
@@ -6596,8 +7062,8 @@ async function readClaudeQuota(options) {
6596
7062
  if (!body || typeof body !== "object") {
6597
7063
  return { status: "error", sourceEpoch: claudeSourceEpoch, code: "invalid_response", retryable: true };
6598
7064
  }
6599
- const record3 = body;
6600
- const limits = ["five_hour", "seven_day", "seven_day_sonnet", "seven_day_opus"].map((key) => claudeLimit(key, record3[key])).filter((limit) => limit !== null);
7065
+ const record4 = body;
7066
+ const limits = ["five_hour", "seven_day", "seven_day_sonnet", "seven_day_opus"].map((key) => claudeLimit(key, record4[key])).filter((limit) => limit !== null);
6601
7067
  if (limits.length === 0) {
6602
7068
  return { status: "error", sourceEpoch: claudeSourceEpoch, code: "invalid_response", retryable: true };
6603
7069
  }
@@ -7254,10 +7720,10 @@ function stableNormalizeApmHeldFreshness(value) {
7254
7720
  return value.map((item) => stableNormalizeApmHeldFreshness(item));
7255
7721
  if (!value || typeof value !== "object")
7256
7722
  return value;
7257
- const record3 = value;
7723
+ const record4 = value;
7258
7724
  const normalized = {};
7259
- for (const key of Object.keys(record3).sort()) {
7260
- normalized[key] = stableNormalizeApmHeldFreshness(record3[key]);
7725
+ for (const key of Object.keys(record4).sort()) {
7726
+ normalized[key] = stableNormalizeApmHeldFreshness(record4[key]);
7261
7727
  }
7262
7728
  return normalized;
7263
7729
  }
@@ -7373,13 +7839,13 @@ function reduceManager(state, event) {
7373
7839
  const existing = state.agents[event.agentId];
7374
7840
  if (!existing || existing.execution.sessionInstanceId !== event.sessionInstanceId)
7375
7841
  return { state, effects: [] };
7376
- const record3 = existing.pendingAdmissions.find((entry) => entry.sessionInstanceId === event.sessionInstanceId && entry.commandId === event.commandId);
7377
- if (!record3)
7842
+ const record4 = existing.pendingAdmissions.find((entry) => entry.sessionInstanceId === event.sessionInstanceId && entry.commandId === event.commandId);
7843
+ if (!record4)
7378
7844
  return { state, effects: [] };
7379
7845
  const agent = clone(existing);
7380
7846
  agent.pendingAdmissions = agent.pendingAdmissions.filter((entry) => entry.sessionInstanceId !== event.sessionInstanceId || entry.commandId !== event.commandId);
7381
7847
  syncExecutionProjection(agent);
7382
- return commit(state, agent, event.outcome === "failed" ? recoveryEffects(agent, [record3]) : []);
7848
+ return commit(state, agent, event.outcome === "failed" ? recoveryEffects(agent, [record4]) : []);
7383
7849
  }
7384
7850
  case "admission_acknowledged": {
7385
7851
  const existing = state.agents[event.agentId];
@@ -7897,11 +8363,11 @@ function syncExecutionProjection(agent) {
7897
8363
  agent.lastDeliverAt = agent.pendingAdmissions.length > 0 ? Math.max(...agent.pendingAdmissions.map((entry) => entry.admittedAt)) : null;
7898
8364
  }
7899
8365
  function recoveryEffects(agent, records) {
7900
- return records.filter((record3) => record3.requeueOnFailure).map((record3) => ({
8366
+ return records.filter((record4) => record4.requeueOnFailure).map((record4) => ({
7901
8367
  type: "requeue_delivery",
7902
8368
  agentId: agent.agentId,
7903
- message: record3.exactAgentMsg,
7904
- mode: record3.mode
8369
+ message: record4.exactAgentMsg,
8370
+ mode: record4.mode
7905
8371
  }));
7906
8372
  }
7907
8373
  function commit(state, agent, effects) {
@@ -8312,14 +8778,14 @@ function createLogger(options = {}) {
8312
8778
  `));
8313
8779
  const err = options.err ?? ((line) => process.stderr.write(line + `
8314
8780
  `));
8315
- const record3 = options.record;
8781
+ const record4 = options.record;
8316
8782
  const emit = (level, message, data) => {
8317
8783
  if (LEVEL_RANK[level] < minRank)
8318
8784
  return;
8319
8785
  const time = now();
8320
8786
  const line = `${time} ${header} ${level.toUpperCase().padEnd(5)} ${message}${formatData(data)}`;
8321
8787
  try {
8322
- record3?.({ time, header, level, message, fields: recordFields(data) });
8788
+ record4?.({ time, header, level, message, fields: recordFields(data) });
8323
8789
  } catch {}
8324
8790
  (level === "warn" || level === "error" ? err : out)(line);
8325
8791
  };
@@ -10058,7 +10524,7 @@ __export(exports_external, {
10058
10524
  regexes: () => exports_regexes,
10059
10525
  regex: () => _regex,
10060
10526
  refine: () => refine,
10061
- record: () => record3,
10527
+ record: () => record4,
10062
10528
  readonly: () => readonly,
10063
10529
  property: () => _property,
10064
10530
  promise: () => promise,
@@ -22214,7 +22680,7 @@ __export(exports_schemas2, {
22214
22680
  strictObject: () => strictObject,
22215
22681
  set: () => set,
22216
22682
  refine: () => refine,
22217
- record: () => record3,
22683
+ record: () => record4,
22218
22684
  readonly: () => readonly,
22219
22685
  promise: () => promise,
22220
22686
  preprocess: () => preprocess,
@@ -23291,7 +23757,7 @@ var ZodRecord = /* @__PURE__ */ $constructor("ZodRecord", (inst, def) => {
23291
23757
  inst.keyType = def.keyType;
23292
23758
  inst.valueType = def.valueType;
23293
23759
  });
23294
- function record3(keyType, valueType, params) {
23760
+ function record4(keyType, valueType, params) {
23295
23761
  if (!valueType || !valueType._zod) {
23296
23762
  return new ZodRecord({
23297
23763
  type: "record",
@@ -23756,7 +24222,7 @@ var stringbool = (...args) => _stringbool({
23756
24222
  }, ...args);
23757
24223
  function json(params) {
23758
24224
  const jsonSchema = lazy(() => {
23759
- return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema), record3(string2(), jsonSchema)]);
24225
+ return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema), record4(string2(), jsonSchema)]);
23760
24226
  });
23761
24227
  return jsonSchema;
23762
24228
  }
@@ -26554,6 +27020,7 @@ var communityMachine = sqliteTable("community_machine", {
26554
27020
  arch: text("arch").notNull().default(""),
26555
27021
  osRelease: text("os_release").notNull().default(""),
26556
27022
  daemonVersion: text("daemon_version").notNull().default(""),
27023
+ timeZone: text("time_zone"),
26557
27024
  metadata: text("metadata"),
26558
27025
  availableRuntimes: text("available_runtimes", { mode: "json" }).$type().notNull().default([]),
26559
27026
  status: text("status").notNull().default("offline"),
@@ -26905,6 +27372,7 @@ class AgentRouter {
26905
27372
  await this.opts.channel.reportReady(this.buildReady());
26906
27373
  }
26907
27374
  buildReady() {
27375
+ const timeZone = typeof this.opts.timeZone === "function" ? this.opts.timeZone() : this.opts.timeZone;
26908
27376
  return {
26909
27377
  runtimeReport: [...this.runtimes.values()],
26910
27378
  capabilities: [CONTROL_HEARTBEAT_CAPABILITY],
@@ -26914,6 +27382,7 @@ class AgentRouter {
26914
27382
  arch: this.opts.arch,
26915
27383
  osRelease: this.opts.osRelease,
26916
27384
  daemonVersion: this.opts.daemonVersion,
27385
+ timeZone,
26917
27386
  ...this.opts.providerQuotas ? { providerQuotas: this.opts.providerQuotas() } : {}
26918
27387
  };
26919
27388
  }
@@ -26955,11 +27424,10 @@ class AgentRouter {
26955
27424
  return;
26956
27425
  if (existing.status === "healthy" && !existing.lastError && !existing.lastErrorAt)
26957
27426
  return;
26958
- this.runtimes.set(id, {
26959
- id: existing.id,
26960
- version: existing.version,
26961
- status: "healthy"
26962
- });
27427
+ const healthy = { ...existing, status: "healthy" };
27428
+ delete healthy.lastError;
27429
+ delete healthy.lastErrorAt;
27430
+ this.runtimes.set(id, healthy);
26963
27431
  this.log.info("runtime marked healthy again", { runtimeId: id });
26964
27432
  this.scheduleReadyFrameResend();
26965
27433
  }
@@ -27379,20 +27847,20 @@ function parseLocalMessageReminderBody(body, agentId) {
27379
27847
  }
27380
27848
  if (!value || typeof value !== "object" || Array.isArray(value))
27381
27849
  return null;
27382
- const record4 = value;
27383
- if (Object.keys(record4).sort().join(",") !== "channel,remindAfterMs,sentSeq")
27850
+ const record5 = value;
27851
+ if (Object.keys(record5).sort().join(",") !== "channel,remindAfterMs,sentSeq")
27384
27852
  return null;
27385
- if (typeof record4.channel !== "string" || !isCanonicalChannelScope(record4.channel))
27853
+ if (typeof record5.channel !== "string" || !isCanonicalChannelScope(record5.channel))
27386
27854
  return null;
27387
- if (!Number.isSafeInteger(record4.sentSeq) || record4.sentSeq < 1)
27855
+ if (!Number.isSafeInteger(record5.sentSeq) || record5.sentSeq < 1)
27388
27856
  return null;
27389
- if (!Number.isSafeInteger(record4.remindAfterMs) || record4.remindAfterMs !== 0 && record4.remindAfterMs < LOCAL_MESSAGE_REMINDER_MIN_MS || record4.remindAfterMs > LOCAL_MESSAGE_REMINDER_MAX_MS)
27857
+ if (!Number.isSafeInteger(record5.remindAfterMs) || record5.remindAfterMs !== 0 && record5.remindAfterMs < LOCAL_MESSAGE_REMINDER_MIN_MS || record5.remindAfterMs > LOCAL_MESSAGE_REMINDER_MAX_MS)
27390
27858
  return null;
27391
27859
  return {
27392
27860
  agentId,
27393
- channel: record4.channel,
27394
- sentSeq: record4.sentSeq,
27395
- remindAfterMs: record4.remindAfterMs
27861
+ channel: record5.channel,
27862
+ sentSeq: record5.sentSeq,
27863
+ remindAfterMs: record5.remindAfterMs
27396
27864
  };
27397
27865
  }
27398
27866
  async function handleLocalMessageReminder(req, res, agentId, onArm) {
@@ -28529,7 +28997,7 @@ var REASONING_EFFORT_RE = /^[A-Za-z0-9._-]+$/;
28529
28997
  var COMMUNITY_REASONING_EFFORT_MAX = 32;
28530
28998
  var COMMUNITY_REASONING_DESCRIPTION_MAX = 256;
28531
28999
  var COMMUNITY_REASONING_OPTIONS_MAX = 16;
28532
- var COMMUNITY_REASONING_MODELS_MAX = 64;
29000
+ var COMMUNITY_REASONING_MODELS_MAX = 512;
28533
29001
  var ReasoningEffortSchema = exports_external.string().min(1).max(COMMUNITY_REASONING_EFFORT_MAX).regex(REASONING_EFFORT_RE, "invalid reasoning effort charset");
28534
29002
  var RuntimeReasoningOptionSchema = exports_external.object({
28535
29003
  value: ReasoningEffortSchema,
@@ -28537,6 +29005,7 @@ var RuntimeReasoningOptionSchema = exports_external.object({
28537
29005
  });
28538
29006
  var RuntimeReasoningModelSchema = exports_external.object({
28539
29007
  id: exports_external.string().min(1).max(100),
29008
+ displayName: exports_external.string().min(1).max(COMMUNITY_REASONING_DESCRIPTION_MAX).optional().catch(undefined),
28540
29009
  supportedReasoningEfforts: exports_external.array(exports_external.unknown()).max(COMMUNITY_REASONING_OPTIONS_MAX).transform((options) => {
28541
29010
  const seen = new Set;
28542
29011
  return options.flatMap((candidate) => {
@@ -28631,6 +29100,7 @@ var HostReadyMessageSchema = exports_external.object({
28631
29100
  arch: exports_external.string().optional(),
28632
29101
  osRelease: exports_external.string().optional(),
28633
29102
  daemonVersion: exports_external.string().optional(),
29103
+ timeZone: exports_external.string().min(1).max(128).optional(),
28634
29104
  providerQuotas: exports_external.array(ProviderQuotaSnapshotSchema).max(2).optional().default([])
28635
29105
  });
28636
29106
  var CommunityDaemonReadySchema = exports_external.object({
@@ -28653,6 +29123,8 @@ var AgentActivityMessageSchema = exports_external.object({
28653
29123
  type: exports_external.literal("agent_activity"),
28654
29124
  agentId: exports_external.string(),
28655
29125
  state: exports_external.enum(["idle", "starting", "running", "stopping"]),
29126
+ usageTimeZone: exports_external.string().min(1).max(128).optional(),
29127
+ usageDay: exports_external.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
28656
29128
  dailyUsage: exports_external.array(DailyUsageSnapshotSchema).max(7).optional(),
28657
29129
  quota: ProviderQuotaSnapshotSchema.optional()
28658
29130
  });
@@ -28918,6 +29390,42 @@ var BotAuditEventAckFrameSchema = exports_external.strictObject({
28918
29390
  type: exports_external.literal("bot_audit_event_ack"),
28919
29391
  eventId: exports_external.string().min(1).max(128)
28920
29392
  });
29393
+ // ../shared/src/utils/day-key.ts
29394
+ function utcDayKey(now) {
29395
+ const d = now instanceof Date ? now : new Date(now);
29396
+ return d.toISOString().slice(0, 10);
29397
+ }
29398
+ function calendarDayKeyDaysAgo(day, days) {
29399
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(day);
29400
+ if (!match)
29401
+ throw new RangeError("invalid calendar day key");
29402
+ const year = Number(match[1]);
29403
+ const month = Number(match[2]);
29404
+ const date5 = Number(match[3]);
29405
+ const parsed = new Date(Date.UTC(year, month - 1, date5));
29406
+ if (parsed.getUTCFullYear() !== year || parsed.getUTCMonth() !== month - 1 || parsed.getUTCDate() !== date5) {
29407
+ throw new RangeError("invalid calendar day key");
29408
+ }
29409
+ parsed.setUTCDate(parsed.getUTCDate() - days);
29410
+ return utcDayKey(parsed);
29411
+ }
29412
+ function dayKeyInTimeZone(now, timeZone) {
29413
+ const date5 = now instanceof Date ? now : new Date(now);
29414
+ const parts = new Intl.DateTimeFormat("en-US", {
29415
+ timeZone,
29416
+ year: "numeric",
29417
+ month: "2-digit",
29418
+ day: "2-digit"
29419
+ }).formatToParts(date5);
29420
+ const values = new Map(parts.map((part) => [part.type, part.value]));
29421
+ const year = values.get("year");
29422
+ const month = values.get("month");
29423
+ const day = values.get("day");
29424
+ if (!year || !month || !day)
29425
+ throw new RangeError("unable to format calendar day key");
29426
+ return `${year}-${month}-${day}`;
29427
+ }
29428
+
28921
29429
  // ../shared/src/db/community-schema.ts
28922
29430
  var exports_community_schema = {};
28923
29431
  __export(exports_community_schema, {
@@ -32111,15 +32619,15 @@ class MessageReminderScheduler {
32111
32619
  const startedAt = this.now();
32112
32620
  const dueAt = startedAt + input.remindAfterMs;
32113
32621
  const sentRef = `${input.channel}#${input.sentSeq}`;
32114
- const record4 = {
32622
+ const record5 = {
32115
32623
  ...input,
32116
32624
  sentRef,
32117
32625
  startedAt,
32118
32626
  dueAt,
32119
32627
  timer: undefined
32120
32628
  };
32121
- record4.timer = this.setTimer(() => {
32122
- if (this.reminders.get(key) !== record4)
32629
+ record5.timer = this.setTimer(() => {
32630
+ if (this.reminders.get(key) !== record5)
32123
32631
  return;
32124
32632
  this.reminders.delete(key);
32125
32633
  try {
@@ -32130,8 +32638,8 @@ class MessageReminderScheduler {
32130
32638
  Promise.resolve(delivery).catch(() => {});
32131
32639
  } catch {}
32132
32640
  }, input.remindAfterMs);
32133
- record4.timer.unref?.();
32134
- this.reminders.set(key, record4);
32641
+ record5.timer.unref?.();
32642
+ this.reminders.set(key, record5);
32135
32643
  return { armed: true, dueAt };
32136
32644
  }
32137
32645
  observe(agentId, channel2, latestSeq) {
@@ -32406,15 +32914,12 @@ class DaemonSelfSleepScheduler {
32406
32914
  import { chmod, mkdir, open, readFile as readFile2, rename, rm } from "node:fs/promises";
32407
32915
  import { dirname as dirname5, join as join14 } from "node:path";
32408
32916
  import { randomUUID as randomUUID7 } from "node:crypto";
32409
- function dayKey(at) {
32410
- return at.toISOString().slice(0, 10);
32917
+ function oldestRetainedDay(at, timeZone) {
32918
+ const today = dayKeyInTimeZone(at, timeZone);
32919
+ return calendarDayKeyDaysAgo(today, 8);
32411
32920
  }
32412
- function retainedDays(at) {
32413
- const days = new Set;
32414
- for (let offset = 0;offset < 7; offset += 1) {
32415
- days.add(dayKey(new Date(at.getTime() - offset * 86400000)));
32416
- }
32417
- return days;
32921
+ function oldestVisibleDay(today) {
32922
+ return calendarDayKeyDaysAgo(today, 6);
32418
32923
  }
32419
32924
  function isMetric(value) {
32420
32925
  return value === null || typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
@@ -32459,16 +32964,25 @@ class DailyTokenUsageStore {
32459
32964
  loaded = false;
32460
32965
  data = { version: 1, bots: {} };
32461
32966
  filePath;
32462
- constructor(workingDirectoryBase, now = () => new Date) {
32967
+ resolveTimeZone;
32968
+ constructor(workingDirectoryBase, now = () => new Date, timeZone = () => Intl.DateTimeFormat().resolvedOptions().timeZone) {
32463
32969
  this.now = now;
32970
+ this.resolveTimeZone = typeof timeZone === "string" ? () => timeZone : timeZone;
32971
+ this.timeZone;
32464
32972
  this.filePath = join14(workingDirectoryBase, ".telemetry", "daily-token-usage.json");
32465
32973
  }
32974
+ get timeZone() {
32975
+ const timeZone = this.resolveTimeZone();
32976
+ dayKeyInTimeZone(0, timeZone);
32977
+ return timeZone;
32978
+ }
32466
32979
  record(botId, delta) {
32467
32980
  return this.enqueue(async () => {
32468
32981
  await this.load();
32469
32982
  const at = this.now();
32470
- this.prune(at);
32471
- const day = dayKey(at);
32983
+ const timeZone = this.timeZone;
32984
+ this.prune(at, timeZone);
32985
+ const day = dayKeyInTimeZone(at, timeZone);
32472
32986
  const snapshots = this.data.bots[botId] ?? [];
32473
32987
  const existing = snapshots.find((snapshot) => snapshot.day === day);
32474
32988
  const next = existing ?? emptySnapshot(botId, day);
@@ -32485,13 +32999,27 @@ class DailyTokenUsageStore {
32485
32999
  });
32486
33000
  }
32487
33001
  snapshots(botId) {
33002
+ return this.usageWindow(botId).then((window2) => window2.snapshots);
33003
+ }
33004
+ usageWindow(botId) {
32488
33005
  let result = [];
33006
+ let usageDay = "";
33007
+ let usageTimeZone = "";
32489
33008
  return this.enqueue(async () => {
32490
33009
  await this.load();
32491
- if (this.prune(this.now()))
33010
+ const at = this.now();
33011
+ const timeZone = this.timeZone;
33012
+ usageTimeZone = timeZone;
33013
+ usageDay = dayKeyInTimeZone(at, timeZone);
33014
+ if (this.prune(at, timeZone))
32492
33015
  await this.persist();
32493
- result = (this.data.bots[botId] ?? []).map((snapshot) => structuredClone(snapshot));
32494
- }).then(() => result);
33016
+ const oldestDay = oldestVisibleDay(usageDay);
33017
+ result = (this.data.bots[botId] ?? []).filter((snapshot) => snapshot.day >= oldestDay && snapshot.day <= usageDay).map((snapshot) => structuredClone(snapshot));
33018
+ }).then(() => ({
33019
+ usageDay,
33020
+ usageTimeZone,
33021
+ snapshots: result
33022
+ }));
32495
33023
  }
32496
33024
  enqueue(operation) {
32497
33025
  const result = this.tail.then(operation, operation);
@@ -32536,11 +33064,11 @@ class DailyTokenUsageStore {
32536
33064
  this.data = { version: 1, bots: valid };
32537
33065
  this.loaded = true;
32538
33066
  }
32539
- prune(at) {
32540
- const keep = retainedDays(at);
33067
+ prune(at, timeZone) {
33068
+ const oldestDay = oldestRetainedDay(at, timeZone);
32541
33069
  let changed = false;
32542
33070
  for (const [botId, snapshots] of Object.entries(this.data.bots)) {
32543
- const retained = snapshots.filter((snapshot) => keep.has(snapshot.day)).sort((a, b) => a.day.localeCompare(b.day)).slice(-7);
33071
+ const retained = snapshots.filter((snapshot) => snapshot.day >= oldestDay).sort((a, b) => a.day.localeCompare(b.day));
32544
33072
  if (retained.length !== snapshots.length || retained.some((snapshot, index2) => snapshot !== snapshots[index2]))
32545
33073
  changed = true;
32546
33074
  if (retained.length === 0)
@@ -32584,6 +33112,7 @@ var WARMUP_CEILING_MS = 30000;
32584
33112
  var RUNTIME_RAW_TRACE_MAX_BYTES = 8 * 1024 * 1024;
32585
33113
  var RUNTIME_RAW_TRACE_AGENT_IDS_ENV = "ALOOK_RUNTIME_RAW_TRACE_AGENT_IDS";
32586
33114
  var STATUS_WRITE_INTERVAL_MS = 5000;
33115
+ var TOKEN_USAGE_BACKENDS = new Set(["claude", "codex", "opencode", "pi"]);
32587
33116
  function parseRuntimeRawTraceAgentIds(value) {
32588
33117
  return new Set((value ?? "").split(",").map((agentId) => agentId.trim()).filter((agentId) => agentId.length > 0 && agentId !== "*"));
32589
33118
  }
@@ -32760,10 +33289,14 @@ async function createDaemon(opts) {
32760
33289
  recordProviderQuota("claude", observed);
32761
33290
  }
32762
33291
  const quota = backendId === "claude" || backendId === "codex" ? providerQuotaByBackend.get(backendId) : undefined;
32763
- const dailyUsage = await dailyTokenUsage2.snapshots(info.agentId);
33292
+ const usageWindow = backendId && TOKEN_USAGE_BACKENDS.has(backendId) ? await dailyTokenUsage2.usageWindow(info.agentId) : null;
32764
33293
  return {
32765
33294
  ...info,
32766
- ...dailyUsage.length > 0 ? { dailyUsage } : {},
33295
+ ...usageWindow ? {
33296
+ usageTimeZone: usageWindow.usageTimeZone,
33297
+ usageDay: usageWindow.usageDay,
33298
+ ...usageWindow.snapshots.length > 0 ? { dailyUsage: usageWindow.snapshots } : {}
33299
+ } : {},
32767
33300
  ...quota ? { quota: structuredClone(quota) } : {}
32768
33301
  };
32769
33302
  };
@@ -33175,6 +33708,7 @@ async function createDaemon(opts) {
33175
33708
  arch: opts.arch,
33176
33709
  osRelease: opts.osRelease,
33177
33710
  daemonVersion: opts.daemonVersion,
33711
+ timeZone: () => dailyTokenUsage2.timeZone,
33178
33712
  providerQuotas: providerQuotaSnapshots,
33179
33713
  resyncActivities: async () => {
33180
33714
  const activities = await Promise.all(manager.liveAgentActivities().map((info) => activityPayload(info)));