@alook/daemon 0.1.24 → 0.1.26

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 +2018 -325
  2. package/dist/index.js +1964 -323
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -865,6 +865,9 @@ class ProcessLane {
865
865
  return false;
866
866
  return proc.kill("SIGINT");
867
867
  }
868
+ updateSettings(input) {
869
+ return this.driver.updateSettings?.(input) ?? Promise.resolve({ status: "unsupported" });
870
+ }
868
871
  attachProcess(proc) {
869
872
  proc.stdout?.on("data", (chunk) => {
870
873
  const chunkText = chunk.toString();
@@ -939,8 +942,8 @@ function resolveLaunchFieldsOrDefault(input) {
939
942
  const envVars = Object.fromEntries(Object.entries(normalized.environment ?? {}).filter(([key]) => !controlled.has(key)));
940
943
  const providerEnv = {};
941
944
  const model = normalized.model.kind === "default" ? undefined : normalized.model.name;
942
- if (normalized.model.kind === "custom" && normalized.provider?.kind === "custom_endpoint") {
943
- providerEnv.ANTHROPIC_CUSTOM_MODEL_OPTION = normalized.model.name;
945
+ if (model && normalized.provider?.kind === "custom_endpoint") {
946
+ providerEnv.ANTHROPIC_CUSTOM_MODEL_OPTION = model;
944
947
  }
945
948
  if (normalized.provider?.kind === "custom_endpoint") {
946
949
  providerEnv.ANTHROPIC_BASE_URL = normalized.provider.apiUrl;
@@ -1252,25 +1255,19 @@ class ClaudeEventNormalizer {
1252
1255
  }
1253
1256
  buildUsageTelemetry(event) {
1254
1257
  const u = event?.usage;
1255
- if (!u && event?.total_cost_usd == null)
1258
+ if (!u)
1256
1259
  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;
1257
1263
  return {
1258
1264
  kind: "telemetry",
1259
1265
  name: "token_usage",
1260
1266
  source: "claude_result_usage",
1261
- usageKind: "per_turn",
1262
- attrs: {
1263
- inputTokens: u?.input_tokens,
1264
- outputTokens: u?.output_tokens,
1265
- cachedInputTokens: u?.cache_read_input_tokens,
1266
- cacheCreationInputTokens: u?.cache_creation_input_tokens,
1267
- totalCostUsd: event?.total_cost_usd,
1268
- durationMs: event?.duration_ms,
1269
- durationApiMs: event?.duration_api_ms,
1270
- numTurns: event?.num_turns,
1271
- resultSubtype: event?.subtype,
1272
- resultIsError: event?.is_error,
1273
- serviceTier: u?.service_tier
1267
+ usage: {
1268
+ input: metric(u.input_tokens),
1269
+ output: metric(u.output_tokens),
1270
+ cache
1274
1271
  }
1275
1272
  };
1276
1273
  }
@@ -1281,6 +1278,7 @@ import { execFileSync as execFileSync2 } from "child_process";
1281
1278
  import * as fs4 from "fs";
1282
1279
  import * as path4 from "path";
1283
1280
  var PROBE_TIMEOUT_MS = 5000;
1281
+ var PROBE_OUTPUT_MAX_BYTES = 1024 * 1024;
1284
1282
  function resolveCommandOnPath(command, deps = {}) {
1285
1283
  if (deps.which)
1286
1284
  return deps.which(command);
@@ -1331,6 +1329,23 @@ function probeCommandVersion(command, args = [], deps = {}, platform = process.p
1331
1329
  return { ok: false, error: String(code) };
1332
1330
  }
1333
1331
  }
1332
+ function probeCommandOutput(command, args, platform = process.platform) {
1333
+ try {
1334
+ const output = execFileSync2(command, args, {
1335
+ encoding: "utf8",
1336
+ timeout: PROBE_TIMEOUT_MS,
1337
+ maxBuffer: PROBE_OUTPUT_MAX_BYTES,
1338
+ shell: needsWindowsShimShell(command, platform),
1339
+ stdio: ["pipe", "pipe", "ignore"],
1340
+ input: "",
1341
+ env: { ...process.env, CI: "1" }
1342
+ });
1343
+ return { ok: true, output };
1344
+ } catch (err) {
1345
+ const code = err?.code ?? "command_probe_failed";
1346
+ return { ok: false, error: String(code) };
1347
+ }
1348
+ }
1334
1349
  function resolveHomePath(relativePath, deps = {}) {
1335
1350
  return path4.join(deps.homeDir || process.env.HOME || ".", relativePath);
1336
1351
  }
@@ -1437,6 +1452,14 @@ class ClaudeTurnProtocol {
1437
1452
  }
1438
1453
 
1439
1454
  // agent-driver/dist/adapters/claude/index.js
1455
+ var CLAUDE_MODEL_CATALOG = {
1456
+ updateMode: "unsupported",
1457
+ models: ["opus", "sonnet", "haiku"].map((id) => ({
1458
+ id,
1459
+ supportedReasoningEfforts: []
1460
+ }))
1461
+ };
1462
+
1440
1463
  class ClaudeDriver {
1441
1464
  id = "claude";
1442
1465
  instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
@@ -1453,10 +1476,11 @@ class ClaudeDriver {
1453
1476
  }
1454
1477
  probe(command) {
1455
1478
  const explicit = command?.trim();
1456
- if (!explicit)
1457
- return probeClaude();
1458
- const result = probeCommandVersion(explicit);
1459
- return result.ok ? { status: "healthy", version: result.version } : { status: "unhealthy", lastError: result.error };
1479
+ const base = explicit ? (() => {
1480
+ const result = probeCommandVersion(explicit);
1481
+ return result.ok ? { status: "healthy", version: result.version } : { status: "unhealthy", lastError: result.error };
1482
+ })() : probeClaude();
1483
+ return base.status === "healthy" ? { ...base, reasoning: CLAUDE_MODEL_CATALOG } : base;
1460
1484
  }
1461
1485
  async openLane(ctx, options) {
1462
1486
  return createProcessLane(this, ctx, { onRawStdoutLine: options?.onRawStdoutLine });
@@ -1502,49 +1526,153 @@ class ClaudeDriver {
1502
1526
  }
1503
1527
 
1504
1528
  // agent-driver/dist/adapters/codex/telemetry.js
1505
- function mapCodexTelemetry(method, params) {
1529
+ function metric(value) {
1530
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
1531
+ }
1532
+ function nonCachedInput(input, cached) {
1533
+ if (typeof input !== "number" || !Number.isSafeInteger(input) || input < 0 || typeof cached !== "number" || !Number.isSafeInteger(cached) || cached < 0 || cached > input)
1534
+ return null;
1535
+ return input - cached;
1536
+ }
1537
+ function canonicalId(value, fallback) {
1538
+ return typeof value === "string" && value.length > 0 && new TextEncoder().encode(value).length <= 64 ? value : fallback;
1539
+ }
1540
+ function mappedPlanName(value) {
1541
+ switch (value) {
1542
+ case "free":
1543
+ return "Free";
1544
+ case "plus":
1545
+ return "Plus";
1546
+ case "pro":
1547
+ return "Pro";
1548
+ case "team":
1549
+ return "Team";
1550
+ case "business":
1551
+ return "Business";
1552
+ case "enterprise":
1553
+ return "Enterprise";
1554
+ case "edu":
1555
+ return "Education";
1556
+ default:
1557
+ return;
1558
+ }
1559
+ }
1560
+ function quotaWindow(minutes, slot) {
1561
+ if (typeof minutes !== "number" || !Number.isSafeInteger(minutes) || minutes <= 0)
1562
+ return null;
1563
+ if (minutes === 1440)
1564
+ return { kind: "calendar", period: "day", displayName: "Daily usage limit" };
1565
+ if (minutes === 10080)
1566
+ return { kind: "calendar", period: "week", displayName: "Weekly usage limit" };
1567
+ if (minutes === 43200)
1568
+ return { kind: "calendar", period: "month", displayName: "Monthly usage limit" };
1569
+ return {
1570
+ kind: "rolling",
1571
+ durationSeconds: minutes * 60,
1572
+ displayName: slot === "primary" && minutes === 300 ? "5 hour usage limit" : `${minutes} minute usage limit`
1573
+ };
1574
+ }
1575
+ function resetIso(value) {
1576
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
1577
+ const date = new Date(value < 10000000000 ? value * 1000 : value);
1578
+ return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
1579
+ }
1580
+ if (typeof value === "string") {
1581
+ const date = new Date(value);
1582
+ return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
1583
+ }
1584
+ return;
1585
+ }
1586
+ function mapCodexQuotaSnapshots(snapshots, sourceEpoch) {
1587
+ const limits = [];
1588
+ let planName;
1589
+ for (const snapshot of snapshots) {
1590
+ const limitId = canonicalId(snapshot?.limitId ?? snapshot?.limit_id, "codex");
1591
+ const spark = /spark/i.test(limitId) || /spark/i.test(String(snapshot?.limitName ?? snapshot?.limit_name ?? ""));
1592
+ const product = spark ? { kind: "reported", id: "codex-spark", displayName: "Spark" } : { kind: "reported", id: "codex", displayName: "Codex" };
1593
+ const model = spark ? { kind: "reported", id: "gpt-5.3-codex-spark" } : { kind: "not_applicable" };
1594
+ planName ??= mappedPlanName(snapshot?.planType ?? snapshot?.plan_type);
1595
+ for (const slot of ["primary", "secondary"]) {
1596
+ const value = snapshot?.[slot];
1597
+ const window2 = quotaWindow(value?.windowDurationMins ?? value?.window_duration_mins, slot);
1598
+ const usedPercent = value?.usedPercent ?? value?.used_percent;
1599
+ if (!window2 || typeof usedPercent !== "number" || !Number.isFinite(usedPercent) || usedPercent < 0 || usedPercent > 100)
1600
+ continue;
1601
+ const resetsAt = resetIso(value?.resetsAt ?? value?.resets_at);
1602
+ limits.push({
1603
+ bucket: { limitId, product, model, window: window2 },
1604
+ usedPercent,
1605
+ ...resetsAt ? { resetsAt } : {}
1606
+ });
1607
+ }
1608
+ }
1609
+ if (limits.length === 0) {
1610
+ return {
1611
+ kind: "telemetry",
1612
+ name: "rate_limits",
1613
+ source: "codex_account_rate_limits_updated",
1614
+ quota: { status: "error", sourceEpoch, code: "invalid_response", retryable: true }
1615
+ };
1616
+ }
1617
+ return {
1618
+ kind: "telemetry",
1619
+ name: "rate_limits",
1620
+ source: "codex_account_rate_limits_updated",
1621
+ quota: {
1622
+ status: "available",
1623
+ sourceEpoch,
1624
+ ...planName ? { planName } : {},
1625
+ freshForSeconds: 300,
1626
+ limits
1627
+ }
1628
+ };
1629
+ }
1630
+ function mapCodexTelemetry(method, params, sourceEpoch) {
1506
1631
  if (method === "thread/tokenUsage/updated") {
1507
- const u = params?.usage ?? params ?? {};
1508
- return [
1509
- {
1510
- kind: "telemetry",
1511
- name: "token_usage",
1512
- source: "codex_thread_token_usage_updated",
1513
- usageKind: "cumulative_session",
1514
- attrs: {
1515
- totalTokens: u.totalTokens ?? u.total_tokens,
1516
- inputTokens: u.inputTokens ?? u.input_tokens,
1517
- cachedInputTokens: u.cachedInputTokens ?? u.cached_input_tokens,
1518
- outputTokens: u.outputTokens ?? u.output_tokens,
1519
- reasoningOutputTokens: u.reasoningOutputTokens ?? u.reasoning_output_tokens,
1520
- modelContextWindow: u.modelContextWindow ?? u.model_context_window,
1521
- cachedInputRatio: u.cachedInputRatio,
1522
- contextUtilization: u.contextUtilization
1523
- }
1632
+ const u = params?.tokenUsage?.last ?? params?.token_usage?.last;
1633
+ if (!u)
1634
+ return [];
1635
+ const input = u.inputTokens ?? u.input_tokens;
1636
+ const cached = u.cachedInputTokens ?? u.cached_input_tokens;
1637
+ return [{
1638
+ kind: "telemetry",
1639
+ name: "token_usage",
1640
+ source: "codex_thread_token_usage_updated",
1641
+ usage: {
1642
+ input: nonCachedInput(input, cached),
1643
+ output: metric(u.outputTokens ?? u.output_tokens),
1644
+ cache: metric(cached)
1524
1645
  }
1525
- ];
1646
+ }];
1526
1647
  }
1527
1648
  if (method === "account/rateLimits/updated") {
1528
- const r = params ?? {};
1529
- return [
1530
- {
1531
- kind: "telemetry",
1532
- name: "rate_limits",
1533
- source: "codex_account_rate_limits_updated",
1534
- attrs: {
1535
- limitId: r.limitId,
1536
- planType: r.planType,
1537
- usedPercent: r.usedPercent,
1538
- windowDurationMins: r.windowDurationMins,
1539
- resetsAt: r.resetsAt
1540
- }
1541
- }
1542
- ];
1649
+ return [mapCodexQuotaSnapshots([params?.rateLimits ?? params ?? {}], sourceEpoch)];
1543
1650
  }
1544
1651
  return [];
1545
1652
  }
1546
1653
 
1547
1654
  // agent-driver/dist/adapters/codex/normalizer.js
1655
+ import { randomBytes } from "node:crypto";
1656
+ var codexQuotaSourceEpoch = randomBytes(16).toString("base64url");
1657
+ var codexQuotaSourceGeneration = 0;
1658
+ var codexAccountFingerprint = null;
1659
+ function rotateCodexQuotaSource() {
1660
+ codexQuotaSourceEpoch = randomBytes(16).toString("base64url");
1661
+ codexQuotaSourceGeneration += 1;
1662
+ codexAccountFingerprint = null;
1663
+ }
1664
+ function observeCodexAccount(result) {
1665
+ const account = result?.account;
1666
+ const fingerprint = account && typeof account === "object" ? JSON.stringify([
1667
+ account.type ?? "unknown",
1668
+ account.email ?? null,
1669
+ account.planType ?? account.plan_type ?? null
1670
+ ]) : "none";
1671
+ if (codexAccountFingerprint !== null && codexAccountFingerprint !== fingerprint) {
1672
+ rotateCodexQuotaSource();
1673
+ }
1674
+ codexAccountFingerprint = fingerprint;
1675
+ }
1548
1676
  function normalizeFileChangeInput(item) {
1549
1677
  const paths = [];
1550
1678
  const seen = new Set;
@@ -1567,6 +1695,12 @@ function normalizeFileChangeInput(item) {
1567
1695
  }
1568
1696
 
1569
1697
  class CodexEventNormalizer {
1698
+ quotaReadRequestIds = new Set;
1699
+ accountReadRequestIds = new Set;
1700
+ rateLimitSnapshots = new Map;
1701
+ quotaSnapshotInitialized = false;
1702
+ quotaSourceGeneration = codexQuotaSourceGeneration;
1703
+ pendingTurnUsage = null;
1570
1704
  threadId = null;
1571
1705
  turnId = null;
1572
1706
  terminalTurn = null;
@@ -1577,10 +1711,69 @@ class CodexEventNormalizer {
1577
1711
  get currentTurnId() {
1578
1712
  return this.turnId;
1579
1713
  }
1714
+ registerQuotaReadRequest(requestId) {
1715
+ this.quotaReadRequestIds.add(requestId);
1716
+ }
1717
+ registerAccountReadRequest(requestId) {
1718
+ this.accountReadRequestIds.add(requestId);
1719
+ }
1720
+ syncQuotaSourceGeneration() {
1721
+ if (this.quotaSourceGeneration === codexQuotaSourceGeneration)
1722
+ return;
1723
+ this.quotaSourceGeneration = codexQuotaSourceGeneration;
1724
+ this.rateLimitSnapshots.clear();
1725
+ this.quotaSnapshotInitialized = false;
1726
+ }
1727
+ quotaSnapshots(value) {
1728
+ const byLimitId = value?.rateLimitsByLimitId ?? value?.rate_limits_by_limit_id;
1729
+ if (byLimitId && typeof byLimitId === "object" && !Array.isArray(byLimitId)) {
1730
+ return Object.entries(byLimitId).flatMap(([key2, snapshot2]) => {
1731
+ if (!snapshot2 || typeof snapshot2 !== "object" || Array.isArray(snapshot2))
1732
+ return [];
1733
+ return [[key2, {
1734
+ ...snapshot2,
1735
+ limitId: snapshot2.limitId ?? snapshot2.limit_id ?? key2
1736
+ }]];
1737
+ });
1738
+ }
1739
+ const snapshot = value?.rateLimits ?? value?.rate_limits ?? value;
1740
+ if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot))
1741
+ return [];
1742
+ const record = snapshot;
1743
+ const key = typeof record.limitId === "string" ? record.limitId : typeof record.limit_id === "string" ? record.limit_id : "codex";
1744
+ return [[key, { ...record, limitId: key }]];
1745
+ }
1746
+ replaceQuotaSnapshots(value) {
1747
+ this.syncQuotaSourceGeneration();
1748
+ this.rateLimitSnapshots.clear();
1749
+ for (const [key, snapshot] of this.quotaSnapshots(value)) {
1750
+ this.rateLimitSnapshots.set(key, snapshot);
1751
+ }
1752
+ this.quotaSnapshotInitialized = true;
1753
+ return [mapCodexQuotaSnapshots([...this.rateLimitSnapshots.values()], codexQuotaSourceEpoch)];
1754
+ }
1755
+ mergeQuotaSnapshots(value) {
1756
+ this.syncQuotaSourceGeneration();
1757
+ for (const [key, update] of this.quotaSnapshots(value)) {
1758
+ const merged = {
1759
+ ...this.rateLimitSnapshots.get(key) ?? {},
1760
+ limitId: key
1761
+ };
1762
+ for (const [field, fieldValue] of Object.entries(update)) {
1763
+ if (fieldValue !== undefined && fieldValue !== null)
1764
+ merged[field] = fieldValue;
1765
+ }
1766
+ this.rateLimitSnapshots.set(key, merged);
1767
+ }
1768
+ if (!this.quotaSnapshotInitialized)
1769
+ return [];
1770
+ return [mapCodexQuotaSnapshots([...this.rateLimitSnapshots.values()], codexQuotaSourceEpoch)];
1771
+ }
1580
1772
  adoptThreadId(threadId) {
1581
1773
  if (threadId !== this.threadId) {
1582
1774
  this.turnId = null;
1583
1775
  this.terminalTurn = null;
1776
+ this.pendingTurnUsage = null;
1584
1777
  }
1585
1778
  this.threadId = threadId;
1586
1779
  }
@@ -1598,6 +1791,25 @@ class CodexEventNormalizer {
1598
1791
  const msg = tryParseJsonLine(line);
1599
1792
  if (!msg)
1600
1793
  return [];
1794
+ if (msg?.id !== undefined && this.accountReadRequestIds.delete(msg.id)) {
1795
+ if (!msg.error) {
1796
+ observeCodexAccount(msg.result);
1797
+ this.syncQuotaSourceGeneration();
1798
+ }
1799
+ return [];
1800
+ }
1801
+ if (msg?.id !== undefined && this.quotaReadRequestIds.delete(msg.id)) {
1802
+ this.syncQuotaSourceGeneration();
1803
+ if (msg.error) {
1804
+ return [{
1805
+ kind: "telemetry",
1806
+ name: "rate_limits",
1807
+ source: "codex_account_rate_limits_read",
1808
+ quota: { status: "error", sourceEpoch: codexQuotaSourceEpoch, code: "provider_error", retryable: true }
1809
+ }];
1810
+ }
1811
+ return this.replaceQuotaSnapshots(msg.result ?? {});
1812
+ }
1601
1813
  if (msg?.error && msg.id !== undefined) {
1602
1814
  return [{ kind: "error", message: msg.error?.message ?? "Codex RPC error" }];
1603
1815
  }
@@ -1622,6 +1834,7 @@ class CodexEventNormalizer {
1622
1834
  return [];
1623
1835
  this.turnId = params.turn.id;
1624
1836
  this.terminalTurn = null;
1837
+ this.pendingTurnUsage = null;
1625
1838
  return [
1626
1839
  {
1627
1840
  kind: "turn_owner",
@@ -1650,24 +1863,36 @@ class CodexEventNormalizer {
1650
1863
  case "turn/completed":
1651
1864
  if (!this.acceptRootTerminal(params))
1652
1865
  return [];
1866
+ const usage = this.pendingTurnUsage;
1867
+ this.pendingTurnUsage = null;
1653
1868
  if (params.turn.status === "failed") {
1654
1869
  return [
1870
+ ...usage ? [usage] : [],
1655
1871
  { kind: "error", message: "Codex turn failed" },
1656
1872
  { kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }
1657
1873
  ];
1658
1874
  }
1659
1875
  if (params.turn.status === "interrupted") {
1660
- return [{ kind: "error", message: "Codex turn interrupted" }, { kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
1876
+ return [...usage ? [usage] : [], { kind: "error", message: "Codex turn interrupted" }, { kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
1661
1877
  }
1662
- return [{ kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
1878
+ return [...usage ? [usage] : [], { kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
1663
1879
  case "error":
1664
1880
  if (params?.willRetry === true) {
1665
1881
  return [{ kind: "runtime_recovery", stage: "retrying", source: "codex_stream" }];
1666
1882
  }
1667
1883
  return [{ kind: "error", message: params?.error?.message ?? params?.message ?? "Codex error" }];
1668
- case "thread/tokenUsage/updated":
1884
+ case "thread/tokenUsage/updated": {
1885
+ const usage2 = mapCodexTelemetry(method, params, codexQuotaSourceEpoch)[0];
1886
+ if (usage2)
1887
+ this.pendingTurnUsage = usage2;
1888
+ return [];
1889
+ }
1669
1890
  case "account/rateLimits/updated":
1670
- return mapCodexTelemetry(method, params);
1891
+ return this.mergeQuotaSnapshots(params);
1892
+ case "account/updated":
1893
+ rotateCodexQuotaSource();
1894
+ this.syncQuotaSourceGeneration();
1895
+ return [];
1671
1896
  default:
1672
1897
  return [];
1673
1898
  }
@@ -1780,7 +2005,102 @@ function resolveCodexHomeRootFromEnv(env = process.env, opts = {}) {
1780
2005
  return path5.join(opts.defaultHomeDir ?? os.homedir(), ".codex");
1781
2006
  }
1782
2007
 
2008
+ // agent-driver/dist/internal/errors.js
2009
+ var MAX_PUBLIC_ERROR_MESSAGE = 1000;
2010
+ var CREDENTIAL_NAME = String.raw`(?:[A-Za-z0-9]{1,32}[_-]){0,4}(?:api[_-]?key|access[_-]?key|secret(?:[_-]?access[_-]?key)?|client[_-]?secret|access[_-]?token|auth(?:orization)?|password|passwd|token|voucher)(?:[_-][A-Za-z0-9]{1,32}){0,4}`;
2011
+ var QUOTED_CREDENTIAL_ASSIGNMENT = new RegExp(String.raw`(["'])(${CREDENTIAL_NAME})\1\s*[:=]\s*(["'])[^"'\r\n]*\3`, "gi");
2012
+ var CREDENTIAL_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9_-])((?:${CREDENTIAL_NAME})\s*[:=]\s*)(?!\[redacted\])[^\s,;}\]]+`, "gi");
2013
+ function scrubDriverErrorMessage(value, fallback = "Runtime operation failed") {
2014
+ const text = value instanceof Error ? value.message : String(value ?? "");
2015
+ const scrubbed = text.replace(/\b(?:cmk|cmt|crk)_[A-Za-z0-9_-]+\b/g, "[redacted-token]").replace(/(Authorization\s*:\s*)(?:Bearer|Basic)\s+[^\s,;]+/gi, "$1[redacted]").replace(/Bearer\s+[A-Za-z0-9._\-]+/gi, "Bearer [redacted]").replace(/\b(?:sk|sk-ant|sk-proj|xox[abprs])-[A-Za-z0-9._\-]+/gi, "[redacted-token]").replace(QUOTED_CREDENTIAL_ASSIGNMENT, "$1$2$1:$3[redacted]$3").replace(CREDENTIAL_ASSIGNMENT, "$1[redacted]").replace(/(?<![A-Za-z0-9._%+\-])[A-Za-z0-9._%+\-]{1,320}@[A-Za-z0-9.\-]{1,255}\.[A-Za-z]{2,63}/g, "[redacted-email]").replace(/([?&])([^=\s]+)=([^&\s]+)/g, "$1$2=[redacted]").replace(/\/(?:Users|home)\/[^\r\n,;]+/g, "[redacted-path]").replace(/[A-Za-z]:\\Users\\[^\r\n,;]+/gi, "[redacted-path]").replace(/\\\\[^\\\s]+\\[^\r\n,;]+/g, "[redacted-path]").replace(/(?:[A-Za-z]:\\|\/)(?:[^\s/:]+[\\/]){1,}[^\s:]*/g, "[redacted-path]").trim();
2016
+ return (scrubbed || fallback).slice(0, MAX_PUBLIC_ERROR_MESSAGE);
2017
+ }
2018
+ function scrubDriverError(error) {
2019
+ return {
2020
+ ...error,
2021
+ code: stableErrorCode(error.code, "runtime_error"),
2022
+ message: scrubDriverErrorMessage(error.message),
2023
+ ...error.details ? { details: scrubDetails(error.details) } : {}
2024
+ };
2025
+ }
2026
+ function scrubDetails(details) {
2027
+ const scrubValue = (value, key) => {
2028
+ if (key && /api[_-]?key|access[_-]?token|auth(?:orization)?|password|passwd|secret|token/i.test(key)) {
2029
+ return "[redacted]";
2030
+ }
2031
+ if (typeof value === "string")
2032
+ return scrubDriverErrorMessage(value, "[redacted]");
2033
+ if (Array.isArray(value))
2034
+ return value.map((item) => scrubValue(item));
2035
+ if (value && typeof value === "object") {
2036
+ return Object.fromEntries(Object.entries(value).map(([childKey, child]) => [
2037
+ childKey,
2038
+ scrubValue(child, childKey)
2039
+ ]));
2040
+ }
2041
+ return value;
2042
+ };
2043
+ return scrubValue(details);
2044
+ }
2045
+ function stableErrorCode(value, fallback) {
2046
+ const code = String(value ?? "");
2047
+ return /^[A-Za-z0-9_.-]{1,100}$/.test(code) ? code : fallback;
2048
+ }
2049
+
2050
+ // agent-driver/dist/internal/modelCatalog.js
2051
+ var RUNTIME_MODEL_CATALOG_MAX = 512;
2052
+ var RUNTIME_MODEL_ID_MAX = 100;
2053
+ function normalizeRuntimeModelId(value) {
2054
+ if (typeof value !== "string")
2055
+ return;
2056
+ const id = value.trim();
2057
+ if (!id || id.length > RUNTIME_MODEL_ID_MAX || /\s/.test(id))
2058
+ return;
2059
+ return id;
2060
+ }
2061
+ function catalogFromIds(ids) {
2062
+ const seen = new Set;
2063
+ const models = [];
2064
+ for (const rawId of ids) {
2065
+ const id = normalizeRuntimeModelId(rawId);
2066
+ if (!id || seen.has(id))
2067
+ continue;
2068
+ if (models.length >= RUNTIME_MODEL_CATALOG_MAX)
2069
+ return;
2070
+ seen.add(id);
2071
+ models.push({ id, supportedReasoningEfforts: [] });
2072
+ }
2073
+ if (models.length === 0)
2074
+ return;
2075
+ return { updateMode: "unsupported", models };
2076
+ }
2077
+ function parseOpenCodeModelCatalog(output) {
2078
+ const ids = output.split(/\r?\n/).flatMap((line) => {
2079
+ const id = normalizeRuntimeModelId(line);
2080
+ return id && /^[^/]+\/.+$/.test(id) ? [id] : [];
2081
+ });
2082
+ return catalogFromIds(ids);
2083
+ }
2084
+ function parsePiModelCatalog(values) {
2085
+ if (!Array.isArray(values))
2086
+ return;
2087
+ const ids = values.flatMap((value) => {
2088
+ if (!value || typeof value !== "object")
2089
+ return [];
2090
+ const model = value;
2091
+ const provider = normalizeRuntimeModelId(model.provider);
2092
+ const id = normalizeRuntimeModelId(model.id);
2093
+ return provider && id && !provider.includes("/") ? [`${provider}/${id}`] : [];
2094
+ });
2095
+ return catalogFromIds(ids);
2096
+ }
2097
+
1783
2098
  // agent-driver/dist/adapters/codex/index.js
2099
+ var SETTINGS_UPDATE_TIMEOUT_MS = 5000;
2100
+ var MODEL_LIST_TIMEOUT_MS = 5000;
2101
+ var MODEL_LIST_OUTPUT_MAX_BYTES = 1024 * 1024;
2102
+ var MODEL_LIST_MAX = RUNTIME_MODEL_CATALOG_MAX;
2103
+ var MODEL_EFFORT_MAX = 16;
1784
2104
  function isCodexMissingRolloutError(message) {
1785
2105
  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);
1786
2106
  }
@@ -1801,19 +2121,173 @@ class CodexDriver {
1801
2121
  }
1802
2122
  };
1803
2123
  eventNormalizer = new CodexEventNormalizer;
2124
+ pendingAccountReadRequestIds = new Set;
1804
2125
  requestId = 0;
1805
2126
  codexHomeRoot = null;
1806
2127
  proc = null;
1807
2128
  pendingInitialPrompt = null;
1808
2129
  pendingResumeFallbackParams = null;
2130
+ pendingSettingsUpdates = new Map;
1809
2131
  nextRequestId() {
1810
2132
  return ++this.requestId;
1811
2133
  }
2134
+ requestAccountQuotaSnapshot() {
2135
+ if (!this.proc?.stdin || this.proc.stdin.destroyed)
2136
+ return;
2137
+ const accountReadRequestId = this.nextRequestId();
2138
+ this.pendingAccountReadRequestIds.add(accountReadRequestId);
2139
+ this.eventNormalizer.registerAccountReadRequest(accountReadRequestId);
2140
+ this.proc.stdin.write(jsonRpcRequest("account/read", { refreshToken: false }, accountReadRequestId) + `
2141
+ `);
2142
+ }
2143
+ requestQuotaSnapshot() {
2144
+ if (!this.proc?.stdin || this.proc.stdin.destroyed)
2145
+ return;
2146
+ const quotaReadRequestId = this.nextRequestId();
2147
+ this.eventNormalizer.registerQuotaReadRequest(quotaReadRequestId);
2148
+ this.proc.stdin.write(jsonRpcRequest("account/rateLimits/read", {}, quotaReadRequestId) + `
2149
+ `);
2150
+ }
1812
2151
  get codexHome() {
1813
2152
  return this.codexHomeRoot;
1814
2153
  }
1815
- probe(command) {
1816
- return probeCliRuntime("codex", {}, command);
2154
+ async probe(command) {
2155
+ const result = await probeCliRuntime("codex", {}, command);
2156
+ if (result.status !== "healthy")
2157
+ return result;
2158
+ return {
2159
+ ...result,
2160
+ reasoning: await this.probeReasoningCatalog(command)
2161
+ };
2162
+ }
2163
+ async probeReasoningCatalog(command) {
2164
+ const spec = resolveSpawnSpec("codex", ["app-server", "--listen", "stdio://"], command);
2165
+ let proc;
2166
+ try {
2167
+ proc = spawnAgentProcess(spec.command, spec.args, {
2168
+ cwd: process.cwd(),
2169
+ env: { ...process.env, CI: "1" },
2170
+ shell: spec.shell
2171
+ });
2172
+ } catch {
2173
+ return;
2174
+ }
2175
+ return new Promise((resolve2) => {
2176
+ let settled = false;
2177
+ let buffer = "";
2178
+ let outputBytes = 0;
2179
+ let nextId = 0;
2180
+ let initializeId = 0;
2181
+ let listId = 0;
2182
+ const models = [];
2183
+ const seenModels = new Set;
2184
+ let overflow = false;
2185
+ let defaultModelId;
2186
+ const finish = (catalog) => {
2187
+ if (settled)
2188
+ return;
2189
+ settled = true;
2190
+ clearTimeout(timer);
2191
+ const done = proc.pid ? killProcessTree(proc.pid, { graceMs: 250 }).catch(() => {}) : Promise.resolve().then(() => {
2192
+ proc.kill("SIGTERM");
2193
+ });
2194
+ done.finally(() => resolve2(catalog));
2195
+ };
2196
+ const requestModelPage = (cursor) => {
2197
+ listId = ++nextId;
2198
+ proc.stdin?.write(jsonRpcRequest("model/list", { limit: Math.min(MODEL_LIST_MAX - models.length, MODEL_LIST_MAX), includeHidden: false, ...cursor ? { cursor } : {} }, listId) + `
2199
+ `);
2200
+ };
2201
+ const consumeModel = (value) => {
2202
+ if (!value || typeof value !== "object")
2203
+ return;
2204
+ const model = value;
2205
+ const id = normalizeRuntimeModelId(model.id);
2206
+ if (!id || seenModels.has(id))
2207
+ return;
2208
+ if (models.length >= MODEL_LIST_MAX) {
2209
+ overflow = true;
2210
+ return;
2211
+ }
2212
+ const rawOptions = Array.isArray(model.supportedReasoningEfforts) ? model.supportedReasoningEfforts : [];
2213
+ const seenEfforts = new Set;
2214
+ const supportedReasoningEfforts = rawOptions.flatMap((raw) => {
2215
+ if (!raw || typeof raw !== "object")
2216
+ return [];
2217
+ const option = raw;
2218
+ const value2 = typeof option.reasoningEffort === "string" ? option.reasoningEffort.trim() : "";
2219
+ if (!value2 || value2.length > 32 || !/^[A-Za-z0-9._-]+$/.test(value2) || seenEfforts.has(value2))
2220
+ return [];
2221
+ seenEfforts.add(value2);
2222
+ const description = typeof option.description === "string" ? option.description.slice(0, 256) : undefined;
2223
+ return [{ value: value2, ...description ? { description } : {} }];
2224
+ }).slice(0, MODEL_EFFORT_MAX);
2225
+ const candidateDefault = typeof model.defaultReasoningEffort === "string" ? model.defaultReasoningEffort : undefined;
2226
+ seenModels.add(id);
2227
+ if (model.isDefault === true)
2228
+ defaultModelId = id;
2229
+ models.push({
2230
+ id,
2231
+ supportedReasoningEfforts,
2232
+ ...candidateDefault && supportedReasoningEfforts.some((item) => item.value === candidateDefault) ? { defaultReasoningEffort: candidateDefault } : {}
2233
+ });
2234
+ };
2235
+ const onLine = (line) => {
2236
+ let message;
2237
+ try {
2238
+ message = JSON.parse(line);
2239
+ } catch {
2240
+ return;
2241
+ }
2242
+ if (message.id === initializeId) {
2243
+ if (message.error)
2244
+ return finish();
2245
+ requestModelPage();
2246
+ return;
2247
+ }
2248
+ if (message.id !== listId)
2249
+ return;
2250
+ if (message.error || !message.result || typeof message.result !== "object")
2251
+ return finish();
2252
+ const result = message.result;
2253
+ for (const model of Array.isArray(result.data) ? result.data : [])
2254
+ consumeModel(model);
2255
+ if (overflow)
2256
+ return finish();
2257
+ const cursor = typeof result.nextCursor === "string" ? result.nextCursor : undefined;
2258
+ if (cursor && models.length >= MODEL_LIST_MAX)
2259
+ return finish();
2260
+ if (cursor)
2261
+ return requestModelPage(cursor);
2262
+ if (models.length === 0)
2263
+ return finish();
2264
+ finish({
2265
+ updateMode: "live_next_turn",
2266
+ ...defaultModelId ? { defaultModelId } : {},
2267
+ models
2268
+ });
2269
+ };
2270
+ const timer = setTimeout(() => finish(), MODEL_LIST_TIMEOUT_MS);
2271
+ timer.unref?.();
2272
+ proc.stdout?.on("data", (chunk) => {
2273
+ const text = chunk.toString();
2274
+ outputBytes += Buffer.byteLength(text);
2275
+ if (outputBytes > MODEL_LIST_OUTPUT_MAX_BYTES)
2276
+ return finish();
2277
+ buffer += text;
2278
+ const lines = buffer.split(`
2279
+ `);
2280
+ buffer = lines.pop() ?? "";
2281
+ for (const line of lines)
2282
+ if (line.trim())
2283
+ onLine(line);
2284
+ });
2285
+ proc.on("error", () => finish());
2286
+ proc.on("exit", () => finish());
2287
+ initializeId = ++nextId;
2288
+ proc.stdin?.write(jsonRpcRequest("initialize", { clientInfo: { name: "alook-agent-driver-probe", version: "0.1.24" }, capabilities: { experimentalApi: true } }, initializeId) + `
2289
+ `);
2290
+ });
1817
2291
  }
1818
2292
  async openLane(ctx, options) {
1819
2293
  return createProcessLane(this, ctx, { onRawStdoutLine: options?.onRawStdoutLine });
@@ -1829,6 +2303,9 @@ class CodexDriver {
1829
2303
  shell: spec.shell
1830
2304
  });
1831
2305
  this.proc = proc;
2306
+ proc.once("exit", () => {
2307
+ this.failPendingSettingsUpdates("settings_process_exited", "Codex exited before acknowledging the settings update");
2308
+ });
1832
2309
  const initialPrompt = ctx.prompt?.trim() ? ctx.prompt : null;
1833
2310
  this.pendingInitialPrompt = initialPrompt;
1834
2311
  queueMicrotask(() => {
@@ -1857,11 +2334,21 @@ class CodexDriver {
1857
2334
  proc.stdin?.write(jsonRpcRequest("thread/start", freshParams, this.nextRequestId()) + `
1858
2335
  `);
1859
2336
  }
2337
+ this.requestAccountQuotaSnapshot();
1860
2338
  });
1861
2339
  return { process: proc };
1862
2340
  }
1863
2341
  normalizeLine(line) {
2342
+ const settingsResponse = this.consumeSettingsUpdateResponse(line);
2343
+ if (settingsResponse)
2344
+ return [];
2345
+ const parsed = tryParseJsonLine(line);
1864
2346
  const events = this.eventNormalizer.normalizeLine(line);
2347
+ if (typeof parsed?.id === "number" && this.pendingAccountReadRequestIds.delete(parsed.id)) {
2348
+ this.requestQuotaSnapshot();
2349
+ }
2350
+ if (parsed?.method === "account/updated")
2351
+ this.requestAccountQuotaSnapshot();
1865
2352
  if (this.pendingResumeFallbackParams && this.proc?.stdin && !this.proc.stdin.destroyed) {
1866
2353
  const rolloutErr = events.find((e) => e.kind === "error" && isCodexMissingRolloutError(e.message));
1867
2354
  if (rolloutErr) {
@@ -1885,6 +2372,90 @@ class CodexDriver {
1885
2372
  }
1886
2373
  return events;
1887
2374
  }
2375
+ updateSettings(input) {
2376
+ const threadId = this.eventNormalizer.currentSessionId;
2377
+ const stdin = this.proc?.stdin;
2378
+ if (!threadId || !stdin || stdin.destroyed || stdin.writableEnded || stdin.writable === false) {
2379
+ return Promise.resolve({
2380
+ status: "failed",
2381
+ error: this.settingsError("process", "settings_thread_unavailable", "Codex thread is not available for a settings update", true)
2382
+ });
2383
+ }
2384
+ const id = this.nextRequestId();
2385
+ return new Promise((resolve2) => {
2386
+ const timer = setTimeout(() => {
2387
+ if (!this.pendingSettingsUpdates.delete(id))
2388
+ return;
2389
+ resolve2({
2390
+ status: "failed",
2391
+ error: this.settingsError("timeout", "settings_update_timeout", "Codex did not acknowledge the settings update before the deadline", true)
2392
+ });
2393
+ }, SETTINGS_UPDATE_TIMEOUT_MS);
2394
+ timer.unref?.();
2395
+ this.pendingSettingsUpdates.set(id, { resolve: resolve2, timer });
2396
+ try {
2397
+ stdin.write(jsonRpcRequest("thread/settings/update", { threadId, effort: input.reasoningEffort }, id) + `
2398
+ `);
2399
+ } catch (error) {
2400
+ clearTimeout(timer);
2401
+ this.pendingSettingsUpdates.delete(id);
2402
+ resolve2({
2403
+ status: "failed",
2404
+ error: this.settingsError("process", "settings_update_write_failed", String(error), true)
2405
+ });
2406
+ }
2407
+ });
2408
+ }
2409
+ consumeSettingsUpdateResponse(line) {
2410
+ let value;
2411
+ try {
2412
+ value = JSON.parse(line);
2413
+ } catch {
2414
+ return false;
2415
+ }
2416
+ if (!value || typeof value !== "object")
2417
+ return false;
2418
+ const record = value;
2419
+ if (typeof record.id !== "number")
2420
+ return false;
2421
+ const pending = this.pendingSettingsUpdates.get(record.id);
2422
+ if (!pending)
2423
+ return false;
2424
+ clearTimeout(pending.timer);
2425
+ this.pendingSettingsUpdates.delete(record.id);
2426
+ const error = record.error;
2427
+ if (!error || typeof error !== "object") {
2428
+ pending.resolve({ status: "applied" });
2429
+ return true;
2430
+ }
2431
+ const rpcError = error;
2432
+ const message = typeof rpcError.message === "string" ? rpcError.message : "Codex rejected the settings update";
2433
+ if (rpcError.code === -32601 || /method\s+not\s+found/i.test(message)) {
2434
+ pending.resolve({
2435
+ status: "unsupported",
2436
+ error: this.settingsError("protocol", "settings_update_unsupported", "Codex does not support live reasoning settings updates", false)
2437
+ });
2438
+ } else {
2439
+ pending.resolve({
2440
+ status: "failed",
2441
+ error: this.settingsError("protocol", "settings_update_rejected", message, true)
2442
+ });
2443
+ }
2444
+ return true;
2445
+ }
2446
+ settingsError(category, code, message, retryable) {
2447
+ return { category, code, message: scrubDriverErrorMessage(message), retryable };
2448
+ }
2449
+ failPendingSettingsUpdates(code, message) {
2450
+ for (const [id, pending] of this.pendingSettingsUpdates) {
2451
+ clearTimeout(pending.timer);
2452
+ this.pendingSettingsUpdates.delete(id);
2453
+ pending.resolve({
2454
+ status: "failed",
2455
+ error: this.settingsError("process", code, message, true)
2456
+ });
2457
+ }
2458
+ }
1888
2459
  get currentSessionId() {
1889
2460
  return this.eventNormalizer.currentSessionId;
1890
2461
  }
@@ -1906,9 +2477,189 @@ class CodexDriver {
1906
2477
 
1907
2478
  // agent-driver/dist/adapters/cursor/acp-lane.js
1908
2479
  import { EventEmitter as EventEmitter2 } from "node:events";
2480
+
2481
+ // agent-driver/dist/adapters/cursor/catalog-probe.js
1909
2482
  var ACP_PROTOCOL_VERSION = 1;
1910
- var HANDSHAKE_TIMEOUT_MS = 15000;
1911
2483
  var AUTH_METHOD_ID = "cursor_login";
2484
+ var CATALOG_PROBE_TIMEOUT_MS = 15000;
2485
+ var CATALOG_PROBE_OUTPUT_MAX_BYTES = 1024 * 1024;
2486
+ var MODEL_DISPLAY_NAME_MAX = 256;
2487
+ var MODEL_OPTION_NESTING_MAX = 16;
2488
+ function record(value) {
2489
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
2490
+ }
2491
+ function normalizeDisplayName(value) {
2492
+ if (typeof value !== "string")
2493
+ return;
2494
+ const displayName = value.trim();
2495
+ return displayName && displayName.length <= MODEL_DISPLAY_NAME_MAX ? displayName : undefined;
2496
+ }
2497
+ function flattenCursorAcpSelectOptions(value, depth = 0) {
2498
+ if (!Array.isArray(value) || depth > MODEL_OPTION_NESTING_MAX)
2499
+ return [];
2500
+ const options = [];
2501
+ for (const item of value) {
2502
+ if (Array.isArray(item)) {
2503
+ options.push(...flattenCursorAcpSelectOptions(item, depth + 1));
2504
+ continue;
2505
+ }
2506
+ const candidate = record(item);
2507
+ if (!candidate)
2508
+ continue;
2509
+ const exactValue = normalizeRuntimeModelId(candidate.value);
2510
+ if (exactValue) {
2511
+ const name = normalizeDisplayName(candidate.name);
2512
+ options.push({ value: exactValue, ...name ? { name } : {} });
2513
+ }
2514
+ if (Array.isArray(candidate.options)) {
2515
+ options.push(...flattenCursorAcpSelectOptions(candidate.options, depth + 1));
2516
+ }
2517
+ }
2518
+ return options;
2519
+ }
2520
+ function parseCursorAcpModelCatalog(session) {
2521
+ const payload = record(session);
2522
+ const configOptions = Array.isArray(payload?.configOptions) ? payload.configOptions : [];
2523
+ const modelConfig = configOptions.map(record).find((option) => option?.id === "model") ?? null;
2524
+ if (!modelConfig)
2525
+ return;
2526
+ const seen = new Set;
2527
+ const models = [];
2528
+ for (const option of flattenCursorAcpSelectOptions(modelConfig.options)) {
2529
+ if (option.value === "default[]" || seen.has(option.value))
2530
+ continue;
2531
+ if (models.length >= RUNTIME_MODEL_CATALOG_MAX)
2532
+ return;
2533
+ seen.add(option.value);
2534
+ models.push({
2535
+ id: option.value,
2536
+ ...option.name ? { displayName: option.name } : {},
2537
+ supportedReasoningEfforts: []
2538
+ });
2539
+ }
2540
+ return models.length > 0 ? { updateMode: "unsupported", models } : undefined;
2541
+ }
2542
+ async function cleanupProbeProcess(process2) {
2543
+ if (process2.pid) {
2544
+ await killProcessTree(process2.pid, { graceMs: 250 }).catch(() => {});
2545
+ return;
2546
+ }
2547
+ if (process2.exitCode === null && process2.signalCode === null)
2548
+ process2.kill("SIGTERM");
2549
+ }
2550
+ async function probeCursorAcpCatalog(command, options = {}) {
2551
+ const cwd = options.cwd ?? process.cwd();
2552
+ const spec = resolveSpawnSpec("cursor-agent", ["acp"], command);
2553
+ let processHandle;
2554
+ try {
2555
+ processHandle = (options.spawn ?? spawnAgentProcess)(spec.command, spec.args, {
2556
+ cwd,
2557
+ env: { ...process.env, CI: "1" },
2558
+ shell: spec.shell
2559
+ });
2560
+ } catch {
2561
+ return;
2562
+ }
2563
+ return new Promise((resolve2) => {
2564
+ let settled = false;
2565
+ let buffer = "";
2566
+ let outputBytes = 0;
2567
+ let requestId = 0;
2568
+ let expectedId = 0;
2569
+ let expectedMethod = "";
2570
+ const finish = (catalog) => {
2571
+ if (settled)
2572
+ return;
2573
+ settled = true;
2574
+ clearTimeout(timer);
2575
+ const cleanup = options.cleanup ?? cleanupProbeProcess;
2576
+ Promise.resolve().then(() => cleanup(processHandle)).catch(() => {}).finally(() => resolve2(catalog));
2577
+ };
2578
+ const request = (method, params) => {
2579
+ if (settled)
2580
+ return;
2581
+ const stdin = processHandle.stdin;
2582
+ if (!stdin || stdin.destroyed || stdin.writableEnded || stdin.writable === false)
2583
+ return finish();
2584
+ expectedId = ++requestId;
2585
+ expectedMethod = method;
2586
+ try {
2587
+ stdin.write(`${jsonRpcRequest(method, params, expectedId)}
2588
+ `);
2589
+ } catch {
2590
+ finish();
2591
+ }
2592
+ };
2593
+ const onLine = (line) => {
2594
+ const parsed = tryParseJsonLine(line);
2595
+ const message = record(parsed);
2596
+ if (!message)
2597
+ return finish();
2598
+ if (message.id !== expectedId)
2599
+ return;
2600
+ if (message.error !== undefined)
2601
+ return finish();
2602
+ if (!Object.prototype.hasOwnProperty.call(message, "result"))
2603
+ return finish();
2604
+ if (expectedMethod === "authenticate") {
2605
+ request("session/new", { cwd, mcpServers: [] });
2606
+ return;
2607
+ }
2608
+ const result = record(message.result);
2609
+ if (!result)
2610
+ return finish();
2611
+ if (expectedMethod === "initialize") {
2612
+ const authMethods = Array.isArray(result.authMethods) ? result.authMethods : [];
2613
+ if (result.protocolVersion !== ACP_PROTOCOL_VERSION || !authMethods.some((method) => record(method)?.id === AUTH_METHOD_ID))
2614
+ return finish();
2615
+ request("authenticate", { methodId: AUTH_METHOD_ID });
2616
+ return;
2617
+ }
2618
+ if (expectedMethod !== "session/new" || typeof result.sessionId !== "string" || !result.sessionId.trim())
2619
+ return finish();
2620
+ finish(parseCursorAcpModelCatalog(result));
2621
+ };
2622
+ const timer = setTimeout(() => finish(), options.timeoutMs ?? CATALOG_PROBE_TIMEOUT_MS);
2623
+ timer.unref?.();
2624
+ processHandle.stdout?.on("data", (chunk) => {
2625
+ if (settled)
2626
+ return;
2627
+ const text = chunk.toString();
2628
+ outputBytes += Buffer.byteLength(text);
2629
+ if (outputBytes > (options.outputMaxBytes ?? CATALOG_PROBE_OUTPUT_MAX_BYTES))
2630
+ return finish();
2631
+ buffer += text;
2632
+ const lines = buffer.split(`
2633
+ `);
2634
+ buffer = lines.pop() ?? "";
2635
+ for (const line of lines)
2636
+ if (line.trim())
2637
+ onLine(line);
2638
+ });
2639
+ processHandle.stderr?.on("data", (chunk) => {
2640
+ if (settled)
2641
+ return;
2642
+ outputBytes += Buffer.byteLength(chunk.toString());
2643
+ if (outputBytes > (options.outputMaxBytes ?? CATALOG_PROBE_OUTPUT_MAX_BYTES))
2644
+ finish();
2645
+ });
2646
+ processHandle.on("error", () => finish());
2647
+ processHandle.on("exit", () => finish());
2648
+ request("initialize", {
2649
+ protocolVersion: ACP_PROTOCOL_VERSION,
2650
+ clientCapabilities: {
2651
+ fs: { readTextFile: false, writeTextFile: false },
2652
+ terminal: false
2653
+ },
2654
+ clientInfo: { name: "alook-agent-driver-probe", version: "0.1.25" }
2655
+ });
2656
+ });
2657
+ }
2658
+
2659
+ // agent-driver/dist/adapters/cursor/acp-lane.js
2660
+ var ACP_PROTOCOL_VERSION2 = 1;
2661
+ var HANDSHAKE_TIMEOUT_MS = 15000;
2662
+ var AUTH_METHOD_ID2 = "cursor_login";
1912
2663
  var PROMPT_STOP_REASONS = new Set([
1913
2664
  "end_turn",
1914
2665
  "max_tokens",
@@ -1932,16 +2683,16 @@ class CursorAcpRpcError extends Error {
1932
2683
  this.code = code;
1933
2684
  }
1934
2685
  }
1935
- function record(value) {
2686
+ function record2(value) {
1936
2687
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
1937
2688
  }
1938
2689
  function safeLabel(value) {
1939
2690
  return typeof value === "string" && /^[a-z0-9_-]{1,64}$/i.test(value) ? value : "unknown";
1940
2691
  }
1941
2692
  function rpcErrorMessage(error) {
1942
- const payload = record(error);
2693
+ const payload = record2(error);
1943
2694
  const message = typeof payload?.message === "string" && payload.message.trim() ? payload.message : "Cursor ACP request failed";
1944
- const data = record(payload?.data);
2695
+ const data = record2(payload?.data);
1945
2696
  const detail = typeof data?.message === "string" && data.message.trim() ? data.message : undefined;
1946
2697
  return detail ? `${message}: ${detail}` : message;
1947
2698
  }
@@ -1949,26 +2700,6 @@ function isMissingSessionError(error) {
1949
2700
  const message = error instanceof Error ? error.message : String(error);
1950
2701
  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);
1951
2702
  }
1952
- function flattenSelectOptions(value) {
1953
- if (!Array.isArray(value))
1954
- return [];
1955
- const out = [];
1956
- for (const item of value) {
1957
- if (Array.isArray(item)) {
1958
- out.push(...flattenSelectOptions(item));
1959
- continue;
1960
- }
1961
- const candidate = record(item);
1962
- if (!candidate)
1963
- continue;
1964
- if (typeof candidate.value === "string") {
1965
- out.push({ value: candidate.value, ...typeof candidate.name === "string" ? { name: candidate.name } : {} });
1966
- }
1967
- if (Array.isArray(candidate.options))
1968
- out.push(...flattenSelectOptions(candidate.options));
1969
- }
1970
- return out;
1971
- }
1972
2703
 
1973
2704
  class CursorAcpLane {
1974
2705
  factory;
@@ -2092,30 +2823,30 @@ class CursorAcpLane {
2092
2823
  }
2093
2824
  }
2094
2825
  async handshake(ctx) {
2095
- const initialize = record(await this.call("initialize", {
2096
- protocolVersion: ACP_PROTOCOL_VERSION,
2826
+ const initialize = record2(await this.call("initialize", {
2827
+ protocolVersion: ACP_PROTOCOL_VERSION2,
2097
2828
  clientCapabilities: {
2098
2829
  fs: { readTextFile: false, writeTextFile: false },
2099
2830
  terminal: false
2100
2831
  },
2101
2832
  clientInfo: { name: "alook-agent-driver", version: "0.1.14" }
2102
2833
  }));
2103
- if (initialize?.protocolVersion !== ACP_PROTOCOL_VERSION) {
2834
+ if (initialize?.protocolVersion !== ACP_PROTOCOL_VERSION2) {
2104
2835
  throw new CursorAcpIncompatibleError("Installed Cursor ACP does not support protocol version 1");
2105
2836
  }
2106
- const capabilities = record(initialize.agentCapabilities);
2837
+ const capabilities = record2(initialize.agentCapabilities);
2107
2838
  if (capabilities?.loadSession !== true) {
2108
2839
  throw new CursorAcpIncompatibleError("Installed Cursor ACP does not support persistent session loading");
2109
2840
  }
2110
2841
  const authMethods = Array.isArray(initialize.authMethods) ? initialize.authMethods : [];
2111
- if (!authMethods.some((method) => record(method)?.id === AUTH_METHOD_ID)) {
2842
+ if (!authMethods.some((method) => record2(method)?.id === AUTH_METHOD_ID2)) {
2112
2843
  throw new CursorAcpIncompatibleError("Installed Cursor ACP does not expose Cursor login authentication");
2113
2844
  }
2114
- await this.call("authenticate", { methodId: AUTH_METHOD_ID });
2845
+ await this.call("authenticate", { methodId: AUTH_METHOD_ID2 });
2115
2846
  let session;
2116
2847
  if (ctx.config.sessionId) {
2117
2848
  try {
2118
- session = record(await this.call("session/load", {
2849
+ session = record2(await this.call("session/load", {
2119
2850
  sessionId: ctx.config.sessionId,
2120
2851
  cwd: ctx.workingDirectory,
2121
2852
  mcpServers: []
@@ -2127,15 +2858,25 @@ class CursorAcpLane {
2127
2858
  throw error;
2128
2859
  }
2129
2860
  } else {
2130
- session = record(await this.call("session/new", { cwd: ctx.workingDirectory, mcpServers: [] }));
2861
+ session = record2(await this.call("session/new", { cwd: ctx.workingDirectory, mcpServers: [] }));
2131
2862
  }
2132
- if (!session || typeof session.sessionId !== "string" || !session.sessionId.trim()) {
2863
+ if (!session)
2864
+ throw new Error("Cursor ACP did not return a valid session response");
2865
+ const returnedSessionId = session.sessionId;
2866
+ if (returnedSessionId !== undefined && (typeof returnedSessionId !== "string" || !returnedSessionId.trim())) {
2133
2867
  throw new Error("Cursor ACP did not return a valid session id");
2134
2868
  }
2135
- if (ctx.config.sessionId && session.sessionId !== ctx.config.sessionId) {
2136
- throw new CursorAcpResetRequiredError("Cursor ACP loaded a different session; reset this agent before continuing");
2869
+ if (ctx.config.sessionId) {
2870
+ if (returnedSessionId !== undefined && returnedSessionId !== ctx.config.sessionId) {
2871
+ throw new CursorAcpResetRequiredError("Cursor ACP loaded a different session; reset this agent before continuing");
2872
+ }
2873
+ this.sessionId = ctx.config.sessionId;
2874
+ } else {
2875
+ if (typeof returnedSessionId !== "string") {
2876
+ throw new Error("Cursor ACP did not return a valid session id");
2877
+ }
2878
+ this.sessionId = returnedSessionId;
2137
2879
  }
2138
- this.sessionId = session.sessionId;
2139
2880
  await this.configureModel(session, ctx);
2140
2881
  }
2141
2882
  async configureModel(session, ctx) {
@@ -2143,24 +2884,30 @@ class CursorAcpLane {
2143
2884
  if (!requestedModel)
2144
2885
  return;
2145
2886
  const configOptions = Array.isArray(session.configOptions) ? session.configOptions : [];
2146
- const modelConfig = configOptions.map(record).find((option) => option?.id === "model") ?? null;
2887
+ const modelConfig = configOptions.map(record2).find((option) => option?.id === "model") ?? null;
2147
2888
  if (!modelConfig) {
2148
2889
  throw new CursorAcpIncompatibleError("Installed Cursor ACP does not support model configuration");
2149
2890
  }
2150
- const options = flattenSelectOptions(modelConfig.options);
2151
- const match = options.find((option) => option.value === requestedModel) ?? options.find((option) => option.name === requestedModel);
2891
+ const options = flattenCursorAcpSelectOptions(modelConfig.options);
2892
+ const match = options.find((option) => option.value === requestedModel);
2152
2893
  if (!match) {
2153
2894
  throw new CursorAcpIncompatibleError(`Configured Cursor model is unavailable through ACP: ${requestedModel}`);
2154
2895
  }
2896
+ let response;
2155
2897
  try {
2156
- await this.call("session/set_config_option", {
2898
+ response = record2(await this.call("session/set_config_option", {
2157
2899
  sessionId: this.sessionId,
2158
2900
  configId: "model",
2159
2901
  value: match.value
2160
- });
2902
+ }));
2161
2903
  } catch {
2162
2904
  throw new CursorAcpIncompatibleError("Installed Cursor ACP rejected model configuration");
2163
2905
  }
2906
+ const confirmedOptions = Array.isArray(response?.configOptions) ? response.configOptions : [];
2907
+ const confirmedModel = confirmedOptions.map(record2).find((option) => option?.id === "model") ?? null;
2908
+ if (confirmedModel?.currentValue !== match.value) {
2909
+ throw new CursorAcpIncompatibleError("Cursor ACP did not confirm the exact configured model");
2910
+ }
2164
2911
  }
2165
2912
  admitPrompt(text) {
2166
2913
  if (!this.sessionId)
@@ -2196,22 +2943,13 @@ class CursorAcpLane {
2196
2943
  completePrompt(active, value) {
2197
2944
  if (this.activePrompt?.requestId !== active.requestId)
2198
2945
  return;
2199
- const result = record(value);
2946
+ const result = record2(value);
2200
2947
  if (!result || typeof result.stopReason !== "string" || !PROMPT_STOP_REASONS.has(result.stopReason)) {
2201
2948
  this.failPrompt(active, new Error("Cursor ACP prompt response did not contain a supported stopReason"));
2202
2949
  return;
2203
2950
  }
2204
2951
  this.activePrompt = null;
2205
2952
  this.openToolCalls.clear();
2206
- const usage = record(result.usage);
2207
- if (usage) {
2208
- this.events.emit("runtime_event", {
2209
- kind: "telemetry",
2210
- name: "token_usage",
2211
- source: "cursor.acp",
2212
- attrs: usage
2213
- });
2214
- }
2215
2953
  this.events.emit("runtime_event", {
2216
2954
  kind: "turn_end",
2217
2955
  sessionId: this.sessionId ?? undefined,
@@ -2347,7 +3085,7 @@ class CursorAcpLane {
2347
3085
  });
2348
3086
  }
2349
3087
  handleMessage(value) {
2350
- const message = record(value);
3088
+ const message = record2(value);
2351
3089
  if (!message || message.jsonrpc !== "2.0") {
2352
3090
  this.protocolFailure("Cursor ACP emitted an invalid JSON-RPC message");
2353
3091
  return;
@@ -2375,7 +3113,7 @@ class CursorAcpLane {
2375
3113
  if (pending.kind === "prompt") {
2376
3114
  this.pending.delete(id);
2377
3115
  if (message.error !== undefined) {
2378
- const payload = record(message.error);
3116
+ const payload = record2(message.error);
2379
3117
  this.failPrompt(pending.active, new CursorAcpRpcError(pending.method, typeof payload?.code === "number" ? payload.code : undefined, rpcErrorMessage(message.error)));
2380
3118
  } else if (!("result" in message)) {
2381
3119
  this.failPrompt(pending.active, new Error("Cursor ACP response omitted result"));
@@ -2385,7 +3123,7 @@ class CursorAcpLane {
2385
3123
  return;
2386
3124
  }
2387
3125
  if (message.error !== undefined) {
2388
- const payload = record(message.error);
3126
+ const payload = record2(message.error);
2389
3127
  this.settleRequest(id, false, new CursorAcpRpcError(pending.method, typeof payload?.code === "number" ? payload.code : undefined, rpcErrorMessage(message.error)));
2390
3128
  return;
2391
3129
  }
@@ -2417,9 +3155,9 @@ class CursorAcpLane {
2417
3155
  this.diagnostic("warning", `Unsupported Cursor ACP client request: ${safeLabel(method)}`);
2418
3156
  return;
2419
3157
  }
2420
- const payload = record(params);
3158
+ const payload = record2(params);
2421
3159
  const sameSession = payload?.sessionId === this.sessionId;
2422
- const options = Array.isArray(payload?.options) ? payload.options.map(record).filter(Boolean) : [];
3160
+ const options = Array.isArray(payload?.options) ? payload.options.map(record2).filter(Boolean) : [];
2423
3161
  const allowOnce = options.find((option) => option.kind === "allow_once" && typeof option.optionId === "string" && option.optionId.trim().length > 0);
2424
3162
  if (!this.ready || !this.activePrompt || !sameSession || !allowOnce) {
2425
3163
  this.write({ jsonrpc: "2.0", id, result: { outcome: { outcome: "cancelled" } } });
@@ -2440,7 +3178,7 @@ class CursorAcpLane {
2440
3178
  this.diagnostic("warning", `Unsupported Cursor ACP notification: ${safeLabel(method)}`);
2441
3179
  }
2442
3180
  handleSessionUpdate(params) {
2443
- const payload = record(params);
3181
+ const payload = record2(params);
2444
3182
  if (!payload || payload.sessionId !== this.sessionId) {
2445
3183
  this.diagnostic("warning", "Cursor ACP emitted an update for a different session");
2446
3184
  return;
@@ -2449,18 +3187,18 @@ class CursorAcpLane {
2449
3187
  this.diagnostic("warning", "Cursor ACP emitted a session update without an active prompt");
2450
3188
  return;
2451
3189
  }
2452
- const update = record(payload.update) ?? {};
3190
+ const update = record2(payload.update) ?? {};
2453
3191
  const updateType = update?.sessionUpdate;
2454
3192
  switch (updateType) {
2455
3193
  case "agent_message_chunk": {
2456
- const content = record(update.content);
3194
+ const content = record2(update.content);
2457
3195
  if (content?.type === "text" && typeof content.text === "string") {
2458
3196
  this.events.emit("runtime_event", { kind: "assistant_message_delta", text: content.text });
2459
3197
  }
2460
3198
  return;
2461
3199
  }
2462
3200
  case "agent_thought_chunk": {
2463
- const content = record(update.content);
3201
+ const content = record2(update.content);
2464
3202
  if (content?.type === "text" && typeof content.text === "string") {
2465
3203
  this.events.emit("runtime_event", { kind: "assistant_reasoning_delta", text: content.text });
2466
3204
  }
@@ -2532,6 +3270,7 @@ class CursorAcpLane {
2532
3270
 
2533
3271
  // agent-driver/dist/adapters/cursor/index.js
2534
3272
  class CursorDriver {
3273
+ catalogProbe;
2535
3274
  id = "cursor";
2536
3275
  instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
2537
3276
  execution = {
@@ -2540,8 +3279,23 @@ class CursorDriver {
2540
3279
  wakeStart: "immediate",
2541
3280
  terminalOwnership: "transport_request"
2542
3281
  };
2543
- probe(command) {
2544
- return probeCliRuntime("cursor-agent", {}, command);
3282
+ constructor(catalogProbe = probeCursorAcpCatalog) {
3283
+ this.catalogProbe = catalogProbe;
3284
+ }
3285
+ async probe(command) {
3286
+ const result = probeCliRuntime("cursor-agent", {}, command);
3287
+ if (result.status !== "healthy")
3288
+ return result;
3289
+ let reasoning;
3290
+ try {
3291
+ reasoning = await this.catalogProbe(command);
3292
+ } catch {
3293
+ reasoning = undefined;
3294
+ }
3295
+ return {
3296
+ ...result,
3297
+ reasoning
3298
+ };
2545
3299
  }
2546
3300
  async openLane(ctx, options) {
2547
3301
  return new CursorAcpLane(this, ctx, { onRawStdoutLine: options?.onRawStdoutLine });
@@ -2561,10 +3315,10 @@ class CursorDriver {
2561
3315
  }
2562
3316
 
2563
3317
  // agent-driver/dist/adapters/opencode/index.js
2564
- import { randomBytes as randomBytes2 } from "node:crypto";
3318
+ import { randomBytes as randomBytes3 } from "node:crypto";
2565
3319
 
2566
3320
  // agent-driver/dist/adapters/opencode/service-lane.js
2567
- import { randomBytes } from "node:crypto";
3321
+ import { randomBytes as randomBytes2 } from "node:crypto";
2568
3322
  import { EventEmitter as EventEmitter3 } from "node:events";
2569
3323
  import { createServer as createServer2 } from "node:net";
2570
3324
  var SUPPORTED_VERSION = "1.17.20";
@@ -2598,7 +3352,7 @@ class OpenCodeHttpError extends Error {
2598
3352
  this.status = status;
2599
3353
  }
2600
3354
  }
2601
- function record2(value) {
3355
+ function record3(value) {
2602
3356
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
2603
3357
  }
2604
3358
  function safeLabel2(value) {
@@ -2645,7 +3399,7 @@ function parseModelRef(model) {
2645
3399
  return { providerID: model.slice(0, slash), id: model.slice(slash + 1) };
2646
3400
  }
2647
3401
  function messageFromError(value) {
2648
- const payload = record2(value);
3402
+ const payload = record3(value);
2649
3403
  const message = typeof payload?.message === "string" && payload.message.trim() ? payload.message : undefined;
2650
3404
  return message ? "OpenCode turn failed" : "OpenCode reported an inconsistent turn outcome";
2651
3405
  }
@@ -2703,7 +3457,7 @@ class OpenCodeServiceLane {
2703
3457
  this.ctx = ctx;
2704
3458
  this.options = options;
2705
3459
  this.fetchFn = options.fetch ?? fetch;
2706
- this.password = options.password ?? randomBytes(32).toString("base64url");
3460
+ this.password = options.password ?? randomBytes2(32).toString("base64url");
2707
3461
  }
2708
3462
  get currentSessionId() {
2709
3463
  return this.sessionId;
@@ -2979,7 +3733,7 @@ class OpenCodeServiceLane {
2979
3733
  const healthTimeoutMs = Math.max(1, Math.min(1000, deadline - Date.now(), this.options.requestTimeoutMs ?? REQUEST_TIMEOUT_MS));
2980
3734
  const { response, body } = await this.fetchJsonWithTimeout("/global/health", { method: "GET" }, "health", healthTimeoutMs);
2981
3735
  if (response.ok) {
2982
- const health = record2(body);
3736
+ const health = record3(body);
2983
3737
  if (health?.healthy !== true || health.version !== SUPPORTED_VERSION) {
2984
3738
  throw new OpenCodeIncompatibleError(`Installed OpenCode service must be version ${SUPPORTED_VERSION}`);
2985
3739
  }
@@ -3000,8 +3754,8 @@ class OpenCodeServiceLane {
3000
3754
  const { response, body } = await this.fetchJsonWithTimeout("/doc", { method: "GET" }, "OpenAPI");
3001
3755
  if (!response.ok)
3002
3756
  throw new OpenCodeIncompatibleError("Installed OpenCode service does not expose its OpenAPI document");
3003
- const document = record2(body);
3004
- const paths = record2(document?.paths);
3757
+ const document = record3(body);
3758
+ const paths = record3(document?.paths);
3005
3759
  const required = [
3006
3760
  "/api/session",
3007
3761
  "/api/session/active",
@@ -3014,7 +3768,7 @@ class OpenCodeServiceLane {
3014
3768
  "/api/session/{sessionID}/permission/{requestID}/reply",
3015
3769
  "/api/event"
3016
3770
  ];
3017
- if (!paths || required.some((path6) => !record2(paths[path6]))) {
3771
+ if (!paths || required.some((path6) => !record3(paths[path6]))) {
3018
3772
  throw new OpenCodeIncompatibleError("Installed OpenCode service is missing required v2 session capabilities");
3019
3773
  }
3020
3774
  }
@@ -3027,7 +3781,7 @@ class OpenCodeServiceLane {
3027
3781
  }
3028
3782
  if (!response2.ok)
3029
3783
  throw new OpenCodeHttpError(response2.status, "session resume");
3030
- const session2 = record2(record2(body2)?.data);
3784
+ const session2 = record3(record3(body2)?.data);
3031
3785
  if (session2?.id !== resumeId) {
3032
3786
  throw new OpenCodeResetRequiredError("OpenCode v2 returned a different resumed session; reset this agent before continuing");
3033
3787
  }
@@ -3048,8 +3802,8 @@ class OpenCodeServiceLane {
3048
3802
  }, "session create");
3049
3803
  if (!response.ok)
3050
3804
  throw new OpenCodeHttpError(response.status, "session create");
3051
- const payload = record2(responseBody);
3052
- const session = record2(payload?.data);
3805
+ const payload = record3(responseBody);
3806
+ const session = record3(payload?.data);
3053
3807
  if (typeof session?.id !== "string" || !/^ses/.test(session.id)) {
3054
3808
  throw new Error("OpenCode v2 did not return a valid session id");
3055
3809
  }
@@ -3206,7 +3960,7 @@ class OpenCodeServiceLane {
3206
3960
  const { response, body: responseBody } = await this.fetchJsonWithTimeout(`/api/session/${encodeURIComponent(this.sessionId)}/history?after=${historyCursor}&limit=${HISTORY_PAGE_LIMIT}`, { method: "GET" }, "session history");
3207
3961
  if (!response.ok)
3208
3962
  throw new OpenCodeHttpError(response.status, "session history");
3209
- const body = record2(responseBody);
3963
+ const body = record3(responseBody);
3210
3964
  if (!Array.isArray(body?.data) || typeof body.hasMore !== "boolean") {
3211
3965
  throw new OpenCodeProtocolError("OpenCode session history returned an invalid page");
3212
3966
  }
@@ -3224,9 +3978,9 @@ class OpenCodeServiceLane {
3224
3978
  return run;
3225
3979
  }
3226
3980
  async handleDurableEvent(value, project) {
3227
- const event = record2(value);
3228
- const durable = record2(event?.durable);
3229
- const data = record2(event?.data);
3981
+ const event = record3(value);
3982
+ const durable = record3(event?.durable);
3983
+ const data = record3(event?.data);
3230
3984
  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) {
3231
3985
  throw new OpenCodeProtocolError("OpenCode session stream emitted an invalid durable event");
3232
3986
  }
@@ -3316,13 +4070,21 @@ class OpenCodeServiceLane {
3316
4070
  ...!successful ? { message: "OpenCode reported an unsupported final step outcome" } : {}
3317
4071
  });
3318
4072
  }
3319
- const tokens = record2(data.tokens);
3320
- if (tokens) {
4073
+ const tokens = record3(data.tokens);
4074
+ if (tokens && data.finish !== "tool-calls") {
4075
+ const cache = record3(tokens.cache);
4076
+ const metric2 = (value2) => typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0 ? value2 : null;
4077
+ const cacheParts = [cache?.read, cache?.write].filter((value2) => typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0);
4078
+ const cacheTotal = cacheParts.reduce((sum, value2) => sum + value2, 0);
3321
4079
  this.events.emit("runtime_event", {
3322
4080
  kind: "telemetry",
3323
4081
  name: "token_usage",
3324
4082
  source: "opencode.v2",
3325
- attrs: tokens
4083
+ usage: {
4084
+ input: metric2(tokens.input),
4085
+ output: metric2(tokens.output),
4086
+ cache: cacheParts.length > 0 && Number.isSafeInteger(cacheTotal) ? cacheTotal : null
4087
+ }
3326
4088
  });
3327
4089
  }
3328
4090
  break;
@@ -3337,8 +4099,8 @@ class OpenCodeServiceLane {
3337
4099
  return seq;
3338
4100
  }
3339
4101
  async handleLiveEvent(value) {
3340
- const event = record2(value);
3341
- const data = record2(event?.data);
4102
+ const event = record3(value);
4103
+ const data = record3(event?.data);
3342
4104
  if (event?.type !== "permission.v2.asked" || data?.sessionID !== this.sessionId)
3343
4105
  return;
3344
4106
  if (typeof data.id !== "string" || !/^per/.test(data.id)) {
@@ -3352,11 +4114,11 @@ class OpenCodeServiceLane {
3352
4114
  const { response, body: responseBody } = await this.fetchJsonWithTimeout(`/api/session/${encodeURIComponent(this.sessionId)}/permission`, { method: "GET" }, "permission list");
3353
4115
  if (!response.ok)
3354
4116
  throw new OpenCodeHttpError(response.status, "permission list");
3355
- const body = record2(responseBody);
4117
+ const body = record3(responseBody);
3356
4118
  if (!Array.isArray(body?.data))
3357
4119
  throw new OpenCodeProtocolError("OpenCode permission list returned invalid data");
3358
4120
  for (const item of body.data) {
3359
- const permission = record2(item);
4121
+ const permission = record3(item);
3360
4122
  if (permission?.sessionID === this.sessionId && typeof permission.id === "string") {
3361
4123
  await this.replyPermission(permission.id);
3362
4124
  }
@@ -3423,8 +4185,8 @@ class OpenCodeServiceLane {
3423
4185
  }, "prompt admission");
3424
4186
  if (!response.ok)
3425
4187
  throw new OpenCodeHttpError(response.status, "prompt admission");
3426
- const body = record2(responseBody);
3427
- const admitted = record2(body?.data);
4188
+ const body = record3(responseBody);
4189
+ const admitted = record3(body?.data);
3428
4190
  if (admitted?.id !== messageId || admitted.sessionID !== this.sessionId || admitted.delivery !== delivery || !Number.isInteger(admitted.admittedSeq) || Number(admitted.admittedSeq) < 0) {
3429
4191
  throw new OpenCodeProtocolError("OpenCode prompt admission returned an invalid receipt");
3430
4192
  }
@@ -3491,8 +4253,8 @@ class OpenCodeServiceLane {
3491
4253
  const { response, body: responseBody } = await this.fetchJsonWithTimeout("/api/session/active", { method: "GET" }, "active session query");
3492
4254
  if (!response.ok)
3493
4255
  throw new OpenCodeHttpError(response.status, "active session query");
3494
- const body = record2(responseBody);
3495
- const active = record2(body?.data);
4256
+ const body = record3(responseBody);
4257
+ const active = record3(body?.data);
3496
4258
  if (!active)
3497
4259
  throw new OpenCodeProtocolError("OpenCode active session query returned invalid data");
3498
4260
  if (!this.barrierStillCurrent(root, identity, generation))
@@ -3628,7 +4390,7 @@ class OpenCodeServiceLane {
3628
4390
  return headers;
3629
4391
  }
3630
4392
  newMessageId() {
3631
- return `msg_${randomBytes(16).toString("hex")}`;
4393
+ return `msg_${randomBytes2(16).toString("hex")}`;
3632
4394
  }
3633
4395
  diagnostic(severity, message) {
3634
4396
  this.events.emit("runtime_event", {
@@ -3693,10 +4455,11 @@ class OpenCodeServiceLane {
3693
4455
 
3694
4456
  // agent-driver/dist/adapters/opencode/index.js
3695
4457
  function createOpenCodeMessageId() {
3696
- return `msg_${randomBytes2(16).toString("hex")}`;
4458
+ return `msg_${randomBytes3(16).toString("hex")}`;
3697
4459
  }
3698
4460
 
3699
4461
  class OpenCodeDriver {
4462
+ outputProbe;
3700
4463
  id = "opencode";
3701
4464
  instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
3702
4465
  execution = {
@@ -3705,8 +4468,19 @@ class OpenCodeDriver {
3705
4468
  wakeStart: "immediate",
3706
4469
  terminalOwnership: "transport_request"
3707
4470
  };
4471
+ constructor(outputProbe = probeCommandOutput) {
4472
+ this.outputProbe = outputProbe;
4473
+ }
3708
4474
  probe(command) {
3709
- return probeCliRuntime("opencode", {}, command);
4475
+ const result = probeCliRuntime("opencode", {}, command);
4476
+ if (result.status !== "healthy")
4477
+ return result;
4478
+ const spec = resolveSpawnSpec("opencode", ["models", "--pure"], command);
4479
+ const output = this.outputProbe(spec.command, spec.args);
4480
+ return {
4481
+ ...result,
4482
+ reasoning: output.ok ? parseOpenCodeModelCatalog(output.output) : undefined
4483
+ };
3710
4484
  }
3711
4485
  beginTurn() {
3712
4486
  return createOpenCodeMessageId();
@@ -3960,6 +4734,7 @@ function createPiSessionDependencies(ctx, loadSdk = loadPiSdkModule) {
3960
4734
 
3961
4735
  // agent-driver/dist/adapters/pi/index.js
3962
4736
  var PI_SDK_PACKAGE_NAME2 = "@earendil-works/pi-coding-agent";
4737
+ var PI_MODEL_PROBE_TIMEOUT_MS = 5000;
3963
4738
  function isPiSdkPackageJson(pkgJsonPath) {
3964
4739
  if (!existsSync3(pkgJsonPath))
3965
4740
  return false;
@@ -4068,6 +4843,8 @@ function mapPiSdkEvent(event, sessionId, state) {
4068
4843
 
4069
4844
  class PiDriver {
4070
4845
  dependenciesFor;
4846
+ loadSdk;
4847
+ readVersion;
4071
4848
  id = "pi";
4072
4849
  instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
4073
4850
  execution = {
@@ -4078,15 +4855,36 @@ class PiDriver {
4078
4855
  };
4079
4856
  sessionId = null;
4080
4857
  terminalSequence = 0;
4081
- constructor(dependenciesFor = createPiSessionDependencies) {
4858
+ constructor(dependenciesFor = createPiSessionDependencies, loadSdk = loadPiSdkModule, readVersion = readPiSdkVersion) {
4082
4859
  this.dependenciesFor = dependenciesFor;
4860
+ this.loadSdk = loadSdk;
4861
+ this.readVersion = readVersion;
4083
4862
  }
4084
- probe() {
4085
- const version = readPiSdkVersion();
4863
+ async probe() {
4864
+ const version = this.readVersion();
4086
4865
  if (!version) {
4087
4866
  return { status: "unhealthy", lastError: "sdk_not_installed" };
4088
4867
  }
4089
- return { status: "healthy", version };
4868
+ let timer;
4869
+ try {
4870
+ const reasoning = await Promise.race([
4871
+ this.loadSdk().then(async (sdk) => {
4872
+ const authStorage = sdk.AuthStorage.create();
4873
+ const registry = sdk.ModelRegistry.create(authStorage);
4874
+ return parsePiModelCatalog(await registry.getAvailable());
4875
+ }),
4876
+ new Promise((resolve3) => {
4877
+ timer = setTimeout(() => resolve3(undefined), PI_MODEL_PROBE_TIMEOUT_MS);
4878
+ timer.unref?.();
4879
+ })
4880
+ ]);
4881
+ return { status: "healthy", version, reasoning };
4882
+ } catch {
4883
+ return { status: "healthy", version, reasoning: undefined };
4884
+ } finally {
4885
+ if (timer)
4886
+ clearTimeout(timer);
4887
+ }
4090
4888
  }
4091
4889
  async openLane(ctx) {
4092
4890
  const deps = this.dependenciesFor(ctx);
@@ -4471,48 +5269,6 @@ function assertInstructionFileName(name) {
4471
5269
  }
4472
5270
  }
4473
5271
 
4474
- // agent-driver/dist/internal/errors.js
4475
- var MAX_PUBLIC_ERROR_MESSAGE = 1000;
4476
- var CREDENTIAL_NAME = String.raw`(?:[A-Za-z0-9]{1,32}[_-]){0,4}(?:api[_-]?key|access[_-]?key|secret(?:[_-]?access[_-]?key)?|client[_-]?secret|access[_-]?token|auth(?:orization)?|password|passwd|token|voucher)(?:[_-][A-Za-z0-9]{1,32}){0,4}`;
4477
- var QUOTED_CREDENTIAL_ASSIGNMENT = new RegExp(String.raw`(["'])(${CREDENTIAL_NAME})\1\s*[:=]\s*(["'])[^"'\r\n]*\3`, "gi");
4478
- var CREDENTIAL_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9_-])((?:${CREDENTIAL_NAME})\s*[:=]\s*)(?!\[redacted\])[^\s,;}\]]+`, "gi");
4479
- function scrubDriverErrorMessage(value, fallback = "Runtime operation failed") {
4480
- const text = value instanceof Error ? value.message : String(value ?? "");
4481
- const scrubbed = text.replace(/\b(?:cmk|cmt|crk)_[A-Za-z0-9_-]+\b/g, "[redacted-token]").replace(/(Authorization\s*:\s*)(?:Bearer|Basic)\s+[^\s,;]+/gi, "$1[redacted]").replace(/Bearer\s+[A-Za-z0-9._\-]+/gi, "Bearer [redacted]").replace(/\b(?:sk|sk-ant|sk-proj|xox[abprs])-[A-Za-z0-9._\-]+/gi, "[redacted-token]").replace(QUOTED_CREDENTIAL_ASSIGNMENT, "$1$2$1:$3[redacted]$3").replace(CREDENTIAL_ASSIGNMENT, "$1[redacted]").replace(/(?<![A-Za-z0-9._%+\-])[A-Za-z0-9._%+\-]{1,320}@[A-Za-z0-9.\-]{1,255}\.[A-Za-z]{2,63}/g, "[redacted-email]").replace(/([?&])([^=\s]+)=([^&\s]+)/g, "$1$2=[redacted]").replace(/\/(?:Users|home)\/[^\r\n,;]+/g, "[redacted-path]").replace(/[A-Za-z]:\\Users\\[^\r\n,;]+/gi, "[redacted-path]").replace(/\\\\[^\\\s]+\\[^\r\n,;]+/g, "[redacted-path]").replace(/(?:[A-Za-z]:\\|\/)(?:[^\s/:]+[\\/]){1,}[^\s:]*/g, "[redacted-path]").trim();
4482
- return (scrubbed || fallback).slice(0, MAX_PUBLIC_ERROR_MESSAGE);
4483
- }
4484
- function scrubDriverError(error) {
4485
- return {
4486
- ...error,
4487
- code: stableErrorCode(error.code, "runtime_error"),
4488
- message: scrubDriverErrorMessage(error.message),
4489
- ...error.details ? { details: scrubDetails(error.details) } : {}
4490
- };
4491
- }
4492
- function scrubDetails(details) {
4493
- const scrubValue = (value, key) => {
4494
- if (key && /api[_-]?key|access[_-]?token|auth(?:orization)?|password|passwd|secret|token/i.test(key)) {
4495
- return "[redacted]";
4496
- }
4497
- if (typeof value === "string")
4498
- return scrubDriverErrorMessage(value, "[redacted]");
4499
- if (Array.isArray(value))
4500
- return value.map((item) => scrubValue(item));
4501
- if (value && typeof value === "object") {
4502
- return Object.fromEntries(Object.entries(value).map(([childKey, child]) => [
4503
- childKey,
4504
- scrubValue(child, childKey)
4505
- ]));
4506
- }
4507
- return value;
4508
- };
4509
- return scrubValue(details);
4510
- }
4511
- function stableErrorCode(value, fallback) {
4512
- const code = String(value ?? "");
4513
- return /^[A-Za-z0-9_.-]{1,100}$/.test(code) ? code : fallback;
4514
- }
4515
-
4516
5272
  // agent-driver/dist/controller/logical-session.js
4517
5273
  import { mkdirSync as mkdirSync3 } from "node:fs";
4518
5274
  var SEMANTIC_ASSEMBLER_MAX_BYTES = 1048576;
@@ -4630,6 +5386,8 @@ class LogicalAgentSession {
4630
5386
  toolBoundaryFlushDisabled = false;
4631
5387
  safeBoundaryFlush;
4632
5388
  safeBoundaryDelivery;
5389
+ settingsUpdateTail = Promise.resolve();
5390
+ settingsUpdatePending = false;
4633
5391
  turnAdmission;
4634
5392
  instructionsMaterialized = false;
4635
5393
  lifecycleGeneration = 0;
@@ -4685,6 +5443,35 @@ class LogicalAgentSession {
4685
5443
  send(message) {
4686
5444
  return this.admit("send", message);
4687
5445
  }
5446
+ updateSettings(input) {
5447
+ if (this.state === "closed" || this.state === "stopping" || this.finishing) {
5448
+ return Promise.resolve({
5449
+ status: "failed",
5450
+ error: driverError("process", "settings_session_closed", "Runtime session is closed", true)
5451
+ });
5452
+ }
5453
+ this.settingsUpdatePending = true;
5454
+ const operation = this.settingsUpdateTail.then(async () => {
5455
+ if (!this.lane?.updateSettings)
5456
+ return { status: "unsupported" };
5457
+ try {
5458
+ return await this.lane.updateSettings(input);
5459
+ } catch (error) {
5460
+ return {
5461
+ status: "failed",
5462
+ error: driverError("process", "settings_update_failed", String(error), true)
5463
+ };
5464
+ }
5465
+ });
5466
+ this.settingsUpdateTail = operation.then((result) => {
5467
+ if (result.status === "applied") {
5468
+ this.settingsUpdatePending = false;
5469
+ return;
5470
+ }
5471
+ return new Promise(() => {});
5472
+ });
5473
+ return operation;
5474
+ }
4688
5475
  async interrupt(input) {
4689
5476
  if (this.state === "closed" || this.state === "stopping" || this.finishing)
4690
5477
  return { status: "closed" };
@@ -4846,7 +5633,7 @@ class LogicalAgentSession {
4846
5633
  }
4847
5634
  return receipt;
4848
5635
  }
4849
- if (this.state === "idle" && (this.queued.length > 0 || this.safeBoundaryFlush !== undefined || this.safeBoundaryDelivery !== undefined)) {
5636
+ if (this.state === "idle" && (this.queued.length > 0 || this.safeBoundaryFlush !== undefined || this.safeBoundaryDelivery !== undefined || this.settingsUpdatePending)) {
4850
5637
  return this.queue(message, "runtime_busy");
4851
5638
  }
4852
5639
  return this.startTurn([message], "prompt");
@@ -5154,11 +5941,10 @@ class LogicalAgentSession {
5154
5941
  }
5155
5942
  return;
5156
5943
  case "telemetry": {
5157
- const details = jsonValue(event.attrs);
5158
5944
  if (event.name === "token_usage") {
5159
- this.emit({ type: "token_usage", turnId, source: event.source, usage: {}, details });
5945
+ this.emit({ type: "token_usage", turnId, source: event.source, usage: event.usage });
5160
5946
  } else {
5161
- this.emit({ type: "rate_limits", turnId, source: event.source, details });
5947
+ this.emit({ type: "rate_limits", turnId, source: event.source, quota: event.quota });
5162
5948
  }
5163
5949
  return;
5164
5950
  }
@@ -5259,7 +6045,7 @@ class LogicalAgentSession {
5259
6045
  if (this.adapter.execution.lifetime === "turn") {
5260
6046
  this.processTurnEnded = true;
5261
6047
  } else {
5262
- Promise.resolve().then(() => this.safeBoundaryFlush).then(() => this.startNextQueued());
6048
+ Promise.resolve().then(() => this.safeBoundaryFlush).then(() => this.settingsUpdateTail).then(() => this.startNextQueued());
5263
6049
  }
5264
6050
  }
5265
6051
  flushSafeBoundaryQueue() {
@@ -5759,8 +6545,14 @@ function createAgentDriverSdkWithRegistry(options) {
5759
6545
  assertAdapterCompatibility(String(registration.id), registration.capabilities, adapter);
5760
6546
  const command = capabilities2.commandOverride ? input.command : undefined;
5761
6547
  const result = await adapter.probe(command);
5762
- if (result.status === "healthy")
5763
- return { status: "healthy", version: result.version, capabilities: capabilities2 };
6548
+ if (result.status === "healthy") {
6549
+ return {
6550
+ status: "healthy",
6551
+ version: result.version,
6552
+ capabilities: capabilities2,
6553
+ reasoning: result.reasoning
6554
+ };
6555
+ }
5764
6556
  return {
5765
6557
  status: "unhealthy",
5766
6558
  error: {
@@ -5769,7 +6561,8 @@ function createAgentDriverSdkWithRegistry(options) {
5769
6561
  message: `Backend ${input.backend} is unavailable`,
5770
6562
  retryable: true
5771
6563
  },
5772
- capabilities: capabilities2
6564
+ capabilities: capabilities2,
6565
+ reasoning: result.reasoning
5773
6566
  };
5774
6567
  } catch (error) {
5775
6568
  const contractInvalid = error instanceof Error && (error.message.startsWith("Adapter ") || error.message.startsWith("Agent backend registration "));
@@ -5963,45 +6756,184 @@ class RuntimeNotificationState {
5963
6756
  return false;
5964
6757
  return this.lastEncodeFailedFingerprint === fingerprint && this.lastEncodeFailedSessionId === sessionId;
5965
6758
  }
5966
- filterUncontributedMessages(messages, sessionId) {
5967
- if (this.contributionSessionId !== sessionId)
5968
- return messages;
5969
- return messages.filter((m) => {
5970
- const identity = inboxNoticeMessageIdentity(m);
5971
- return identity.length === 0 || !this.contributedIdentities.has(identity);
6759
+ filterUncontributedMessages(messages, sessionId) {
6760
+ if (this.contributionSessionId !== sessionId)
6761
+ return messages;
6762
+ return messages.filter((m) => {
6763
+ const identity = inboxNoticeMessageIdentity(m);
6764
+ return identity.length === 0 || !this.contributedIdentities.has(identity);
6765
+ });
6766
+ }
6767
+ add(count = 1) {
6768
+ this.pendingCountValue += count;
6769
+ }
6770
+ schedule(callback, delayMs) {
6771
+ if (this.timerValue)
6772
+ return false;
6773
+ this.timerValue = setTimeout(() => {
6774
+ this.timerValue = null;
6775
+ callback();
6776
+ }, delayMs);
6777
+ this.timerValue.unref?.();
6778
+ return true;
6779
+ }
6780
+ takePendingAndClearTimer() {
6781
+ const count = this.pendingCountValue;
6782
+ this.pendingCountValue = 0;
6783
+ if (this.timerValue) {
6784
+ clearTimeout(this.timerValue);
6785
+ this.timerValue = null;
6786
+ }
6787
+ return count;
6788
+ }
6789
+ ensureContributionSession(sessionId) {
6790
+ if (this.contributionSessionId !== sessionId) {
6791
+ this.contributionSessionId = sessionId;
6792
+ this.contributedIdentities = new Set;
6793
+ }
6794
+ }
6795
+ }
6796
+ // src/runtime/errorDiagnostics.ts
6797
+ import { createHash as createHash2 } from "crypto";
6798
+ // agent-driver/dist/provider-quota.js
6799
+ import { execFile } from "node:child_process";
6800
+ import { readFile } from "node:fs/promises";
6801
+ import { homedir as homedir3 } from "node:os";
6802
+ import { join as join9 } from "node:path";
6803
+ import { promisify } from "node:util";
6804
+ import { randomBytes as randomBytes4 } from "node:crypto";
6805
+ var execFileAsync = promisify(execFile);
6806
+ var claudeAccessToken = null;
6807
+ var claudeSourceEpoch = randomBytes4(16).toString("base64url");
6808
+ function parseCredentials(value) {
6809
+ try {
6810
+ const parsed = JSON.parse(value);
6811
+ return parsed && typeof parsed === "object" ? parsed : null;
6812
+ } catch {
6813
+ return null;
6814
+ }
6815
+ }
6816
+ async function claudeCredentials(options) {
6817
+ const platform = options.platform ?? process.platform;
6818
+ if (platform === "darwin") {
6819
+ try {
6820
+ const value = options.readKeychain ? await options.readKeychain() : (await execFileAsync("security", ["find-generic-password", "-s", "Claude Code-credentials", "-w"], {
6821
+ timeout: 3000,
6822
+ maxBuffer: 256 * 1024
6823
+ })).stdout;
6824
+ const parsed = parseCredentials(value.trim());
6825
+ if (parsed)
6826
+ return parsed;
6827
+ } catch {}
6828
+ }
6829
+ const env = options.env ?? process.env;
6830
+ const root = env.CLAUDE_CONFIG_DIR || join9(options.home ?? homedir3(), ".claude");
6831
+ try {
6832
+ const value = options.readCredentialsFile ? await options.readCredentialsFile(join9(root, ".credentials.json")) : await readFile(join9(root, ".credentials.json"), "utf8");
6833
+ return parseCredentials(value);
6834
+ } catch {
6835
+ return null;
6836
+ }
6837
+ }
6838
+ function mappedPlanName2(value) {
6839
+ switch (value) {
6840
+ case "free":
6841
+ return "Free";
6842
+ case "pro":
6843
+ return "Pro";
6844
+ case "max":
6845
+ return "Max";
6846
+ case "team":
6847
+ return "Team";
6848
+ case "enterprise":
6849
+ return "Enterprise";
6850
+ default:
6851
+ return;
6852
+ }
6853
+ }
6854
+ function resetIso2(value) {
6855
+ if (typeof value !== "string")
6856
+ return;
6857
+ const date = new Date(value);
6858
+ return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
6859
+ }
6860
+ function claudeLimit(key, value) {
6861
+ if (!value || typeof value !== "object")
6862
+ return null;
6863
+ const row = value;
6864
+ if (typeof row.utilization !== "number" || !Number.isFinite(row.utilization) || row.utilization < 0 || row.utilization > 100)
6865
+ return null;
6866
+ const model = key.includes("sonnet") ? { kind: "reported", id: "claude-sonnet" } : key.includes("opus") ? { kind: "reported", id: "claude-opus" } : { kind: "not_applicable" };
6867
+ const window2 = key === "five_hour" ? { kind: "rolling", durationSeconds: 18000, displayName: "5 hour usage limit" } : { kind: "rolling", durationSeconds: 604800, displayName: "7 day usage limit" };
6868
+ const resetsAt = resetIso2(row.resets_at ?? row.resetsAt);
6869
+ return {
6870
+ bucket: {
6871
+ limitId: key,
6872
+ product: { kind: "reported", id: "claude", displayName: "Claude" },
6873
+ model,
6874
+ window: window2
6875
+ },
6876
+ usedPercent: row.utilization,
6877
+ ...resetsAt ? { resetsAt } : {}
6878
+ };
6879
+ }
6880
+ async function readClaudeQuota(options) {
6881
+ const env = options.env ?? process.env;
6882
+ if (env.ANTHROPIC_API_KEY || env.ANTHROPIC_BASE_URL)
6883
+ return null;
6884
+ const credentials = await claudeCredentials(options);
6885
+ const token = credentials?.claudeAiOauth?.accessToken;
6886
+ if (typeof token !== "string" || token.length === 0)
6887
+ return null;
6888
+ if (claudeAccessToken !== token) {
6889
+ claudeAccessToken = token;
6890
+ claudeSourceEpoch = randomBytes4(16).toString("base64url");
6891
+ }
6892
+ let response;
6893
+ try {
6894
+ response = await (options.fetchUsage ?? fetch)("https://api.anthropic.com/api/oauth/usage", {
6895
+ headers: {
6896
+ authorization: `Bearer ${token}`,
6897
+ "anthropic-beta": "oauth-2025-04-20"
6898
+ },
6899
+ signal: AbortSignal.timeout(5000)
5972
6900
  });
6901
+ } catch {
6902
+ return { status: "error", sourceEpoch: claudeSourceEpoch, code: "network", retryable: true };
5973
6903
  }
5974
- add(count = 1) {
5975
- this.pendingCountValue += count;
6904
+ if (response.status === 401 || response.status === 403) {
6905
+ return { status: "error", sourceEpoch: claudeSourceEpoch, code: "unauthorized", retryable: false };
5976
6906
  }
5977
- schedule(callback, delayMs) {
5978
- if (this.timerValue)
5979
- return false;
5980
- this.timerValue = setTimeout(() => {
5981
- this.timerValue = null;
5982
- callback();
5983
- }, delayMs);
5984
- this.timerValue.unref?.();
5985
- return true;
6907
+ if (!response.ok) {
6908
+ return { status: "error", sourceEpoch: claudeSourceEpoch, code: "provider_error", retryable: response.status === 429 || response.status >= 500 };
5986
6909
  }
5987
- takePendingAndClearTimer() {
5988
- const count = this.pendingCountValue;
5989
- this.pendingCountValue = 0;
5990
- if (this.timerValue) {
5991
- clearTimeout(this.timerValue);
5992
- this.timerValue = null;
5993
- }
5994
- return count;
6910
+ let body;
6911
+ try {
6912
+ body = await response.json();
6913
+ } catch {
6914
+ return { status: "error", sourceEpoch: claudeSourceEpoch, code: "invalid_response", retryable: true };
5995
6915
  }
5996
- ensureContributionSession(sessionId) {
5997
- if (this.contributionSessionId !== sessionId) {
5998
- this.contributionSessionId = sessionId;
5999
- this.contributedIdentities = new Set;
6000
- }
6916
+ if (!body || typeof body !== "object") {
6917
+ return { status: "error", sourceEpoch: claudeSourceEpoch, code: "invalid_response", retryable: true };
6001
6918
  }
6919
+ const record4 = body;
6920
+ const limits = ["five_hour", "seven_day", "seven_day_sonnet", "seven_day_opus"].map((key) => claudeLimit(key, record4[key])).filter((limit) => limit !== null);
6921
+ if (limits.length === 0) {
6922
+ return { status: "error", sourceEpoch: claudeSourceEpoch, code: "invalid_response", retryable: true };
6923
+ }
6924
+ const planName = mappedPlanName2(credentials?.claudeAiOauth?.subscriptionType);
6925
+ return {
6926
+ status: "available",
6927
+ sourceEpoch: claudeSourceEpoch,
6928
+ ...planName ? { planName } : {},
6929
+ freshForSeconds: 300,
6930
+ limits
6931
+ };
6932
+ }
6933
+ async function readBuiltinProviderQuota(backend, options = {}) {
6934
+ return backend === "claude" ? readClaudeQuota(options) : null;
6002
6935
  }
6003
6936
  // src/runtime/errorDiagnostics.ts
6004
- import { createHash as createHash2 } from "crypto";
6005
6937
  var ERROR_EXCERPT_MAX_BYTES = 4000;
6006
6938
  var ERROR_FINGERPRINT_LEN = 16;
6007
6939
  var ERROR_LEN_BUCKETS = [
@@ -6642,10 +7574,10 @@ function stableNormalizeApmHeldFreshness(value) {
6642
7574
  return value.map((item) => stableNormalizeApmHeldFreshness(item));
6643
7575
  if (!value || typeof value !== "object")
6644
7576
  return value;
6645
- const record3 = value;
7577
+ const record4 = value;
6646
7578
  const normalized = {};
6647
- for (const key of Object.keys(record3).sort()) {
6648
- normalized[key] = stableNormalizeApmHeldFreshness(record3[key]);
7579
+ for (const key of Object.keys(record4).sort()) {
7580
+ normalized[key] = stableNormalizeApmHeldFreshness(record4[key]);
6649
7581
  }
6650
7582
  return normalized;
6651
7583
  }
@@ -6761,13 +7693,13 @@ function reduceManager(state, event) {
6761
7693
  const existing = state.agents[event.agentId];
6762
7694
  if (!existing || existing.execution.sessionInstanceId !== event.sessionInstanceId)
6763
7695
  return { state, effects: [] };
6764
- const record3 = existing.pendingAdmissions.find((entry) => entry.sessionInstanceId === event.sessionInstanceId && entry.commandId === event.commandId);
6765
- if (!record3)
7696
+ const record4 = existing.pendingAdmissions.find((entry) => entry.sessionInstanceId === event.sessionInstanceId && entry.commandId === event.commandId);
7697
+ if (!record4)
6766
7698
  return { state, effects: [] };
6767
7699
  const agent = clone(existing);
6768
7700
  agent.pendingAdmissions = agent.pendingAdmissions.filter((entry) => entry.sessionInstanceId !== event.sessionInstanceId || entry.commandId !== event.commandId);
6769
7701
  syncExecutionProjection(agent);
6770
- return commit(state, agent, event.outcome === "failed" ? recoveryEffects(agent, [record3]) : []);
7702
+ return commit(state, agent, event.outcome === "failed" ? recoveryEffects(agent, [record4]) : []);
6771
7703
  }
6772
7704
  case "admission_acknowledged": {
6773
7705
  const existing = state.agents[event.agentId];
@@ -6824,6 +7756,29 @@ function reduceManager(state, event) {
6824
7756
  a.inbox = [...a.inbox, event.message];
6825
7757
  a.idleSince = null;
6826
7758
  });
7759
+ case "runtime_config_queued":
7760
+ return mutate(state, event.agentId, (a) => {
7761
+ if (!a.inbox.some((message) => message.id === event.message.id)) {
7762
+ a.inbox = [...a.inbox, event.message];
7763
+ }
7764
+ syncExecutionProjection(a);
7765
+ a.idleSince = null;
7766
+ });
7767
+ case "runtime_config_applied": {
7768
+ const existing = state.agents[event.agentId];
7769
+ if (!existing)
7770
+ return { state, effects: [] };
7771
+ const agent = clone(existing);
7772
+ if (agent.status !== "running" || leaseIsWorking(agent.execution.lease) || agent.pendingAdmissions.length > 0 || agent.inbox.length === 0)
7773
+ return { state, effects: [] };
7774
+ const messages = drainInbox(agent);
7775
+ return commit(state, agent, messages.map((message) => ({
7776
+ type: "send",
7777
+ agentId: event.agentId,
7778
+ message,
7779
+ mode: "idle"
7780
+ })));
7781
+ }
6827
7782
  case "turn_started": {
6828
7783
  const existing = state.agents[event.agentId];
6829
7784
  if (!existing || existing.execution.sessionInstanceId !== event.sessionInstanceId)
@@ -7031,7 +7986,7 @@ function onTurnCompleted(state, agentId, sessionInstanceId, nowMs, turnId, endRe
7031
7986
  agent.stalledSessionId = null;
7032
7987
  syncExecutionProjection(agent);
7033
7988
  const clearEffects = clearedStallSessionId === null ? [] : [{ type: "clear_stall_recovery", agentId, sessionId: clearedStallSessionId }];
7034
- if (agent.inbox.length > 0) {
7989
+ if (agent.inbox.length > 0 && !agent.resetting) {
7035
7990
  const messages = drainInbox(agent);
7036
7991
  return commit(state, agent, [
7037
7992
  ...clearEffects,
@@ -7262,11 +8217,11 @@ function syncExecutionProjection(agent) {
7262
8217
  agent.lastDeliverAt = agent.pendingAdmissions.length > 0 ? Math.max(...agent.pendingAdmissions.map((entry) => entry.admittedAt)) : null;
7263
8218
  }
7264
8219
  function recoveryEffects(agent, records) {
7265
- return records.filter((record3) => record3.requeueOnFailure).map((record3) => ({
8220
+ return records.filter((record4) => record4.requeueOnFailure).map((record4) => ({
7266
8221
  type: "requeue_delivery",
7267
8222
  agentId: agent.agentId,
7268
- message: record3.exactAgentMsg,
7269
- mode: record3.mode
8223
+ message: record4.exactAgentMsg,
8224
+ mode: record4.mode
7270
8225
  }));
7271
8226
  }
7272
8227
  function commit(state, agent, effects) {
@@ -7677,14 +8632,14 @@ function createLogger(options = {}) {
7677
8632
  `));
7678
8633
  const err = options.err ?? ((line) => process.stderr.write(line + `
7679
8634
  `));
7680
- const record3 = options.record;
8635
+ const record4 = options.record;
7681
8636
  const emit = (level, message, data) => {
7682
8637
  if (LEVEL_RANK[level] < minRank)
7683
8638
  return;
7684
8639
  const time = now();
7685
8640
  const line = `${time} ${header} ${level.toUpperCase().padEnd(5)} ${message}${formatData(data)}`;
7686
8641
  try {
7687
- record3?.({ time, header, level, message, fields: recordFields(data) });
8642
+ record4?.({ time, header, level, message, fields: recordFields(data) });
7688
8643
  } catch {}
7689
8644
  (level === "warn" || level === "error" ? err : out)(line);
7690
8645
  };
@@ -7946,9 +8901,13 @@ class AgentProcessManager {
7946
8901
  state;
7947
8902
  sessions = new Map;
7948
8903
  runtimeConfigs = new Map;
8904
+ appliedRuntimeConfigs = new Map;
8905
+ pendingRuntimeConfigUpdates = new Map;
8906
+ runtimeConfigApplyRunning = new Set;
7949
8907
  resumeSessions = new Map;
7950
8908
  launchIds = new Map;
7951
8909
  liveSessions = new Map;
8910
+ liveBackendIds = new Map;
7952
8911
  activeSpawnState = new Map;
7953
8912
  publishedAgentActivity = new Map;
7954
8913
  traceProcessNonce = randomUUID5();
@@ -7977,19 +8936,156 @@ class AgentProcessManager {
7977
8936
  this.state = createInitialManagerState(this.opts.staleThresholdMs, this.opts.idleTimeoutMs, this.opts.resetStuckThresholdMs, this.opts.stoppingStuckThresholdMs, this.opts.idleResetTimeoutMs);
7978
8937
  }
7979
8938
  register(agentId, launch) {
7980
- if (launch?.runtimeConfig)
7981
- this.runtimeConfigs.set(agentId, launch.runtimeConfig);
8939
+ const runtimeConfigAcceptance = launch?.runtimeConfig ? this.acceptRuntimeConfig(agentId, launch.runtimeConfig) : undefined;
7982
8940
  if (launch?.sessionId)
7983
8941
  this.resumeSessions.set(agentId, launch.sessionId);
7984
8942
  if (launch?.launchId)
7985
8943
  this.launchIds.set(agentId, launch.launchId);
7986
8944
  this.dispatch({ type: "register", agentId });
8945
+ const registered = this.state.agents[agentId];
8946
+ if (launch?.runtimeConfig && launch.applyRuntimeConfig !== false && (runtimeConfigAcceptance === "accepted" || this.pendingRuntimeConfigUpdates.has(agentId)) && this.sessions.has(agentId) && registered && !isActivelyWorking(registered)) {
8947
+ this.convergeRuntimeConfig(agentId);
8948
+ }
8949
+ }
8950
+ async updateRuntimeConfig(agentId, config) {
8951
+ const accepted = this.acceptRuntimeConfig(agentId, config);
8952
+ if (accepted === "stale" || accepted === "idempotent")
8953
+ return accepted;
8954
+ const session = this.sessions.get(agentId);
8955
+ if (!session) {
8956
+ this.pendingRuntimeConfigUpdates.delete(agentId);
8957
+ return "saved_for_start";
8958
+ }
8959
+ const agent = this.state.agents[agentId];
8960
+ if (agent && (agent.turnActive || isActivelyWorking(agent)))
8961
+ return "deferred";
8962
+ return this.convergeRuntimeConfig(agentId);
8963
+ }
8964
+ acceptRuntimeConfig(agentId, config) {
8965
+ const desired = this.runtimeConfigs.get(agentId);
8966
+ const revision = config.runtimeConfigRevision ?? 0;
8967
+ const desiredRevision = desired?.runtimeConfigRevision ?? 0;
8968
+ if (desired && revision < desiredRevision)
8969
+ return "stale";
8970
+ if (desired && revision === desiredRevision) {
8971
+ if (this.runtimeConfigTuple(desired) !== this.runtimeConfigTuple(config)) {
8972
+ throw new Error(`Conflicting runtime config for ${agentId} at revision ${revision}`);
8973
+ }
8974
+ this.runtimeConfigs.set(agentId, config);
8975
+ return "idempotent";
8976
+ }
8977
+ this.runtimeConfigs.set(agentId, config);
8978
+ this.pendingRuntimeConfigUpdates.set(agentId, config);
8979
+ return "accepted";
8980
+ }
8981
+ runtimeConfigTuple(config) {
8982
+ return JSON.stringify({
8983
+ version: config.version,
8984
+ runtime: config.runtime,
8985
+ model: config.model,
8986
+ mode: config.mode,
8987
+ reasoningEffort: config.reasoningEffort ?? null,
8988
+ provider: config.provider ?? null,
8989
+ command: config.command ?? null,
8990
+ disallowedTools: config.disallowedTools ?? null,
8991
+ envVars: config.envVars ?? null
8992
+ });
8993
+ }
8994
+ runtimeLaunchTuple(config) {
8995
+ return JSON.stringify({
8996
+ version: config.version,
8997
+ runtime: config.runtime,
8998
+ model: config.model,
8999
+ mode: config.mode,
9000
+ provider: config.provider ?? null,
9001
+ command: config.command ?? null,
9002
+ disallowedTools: config.disallowedTools ?? null,
9003
+ envVars: config.envVars ?? null
9004
+ });
9005
+ }
9006
+ async convergeRuntimeConfig(agentId, restartOnFailure = true) {
9007
+ if (this.runtimeConfigApplyRunning.has(agentId))
9008
+ return "deferred";
9009
+ const session = this.sessions.get(agentId);
9010
+ if (!session)
9011
+ return "saved_for_start";
9012
+ this.runtimeConfigApplyRunning.add(agentId);
9013
+ try {
9014
+ while (this.sessions.get(agentId) === session) {
9015
+ const desired = this.pendingRuntimeConfigUpdates.get(agentId) ?? this.runtimeConfigs.get(agentId);
9016
+ if (!desired)
9017
+ return "idempotent";
9018
+ const desiredRevision = desired.runtimeConfigRevision ?? 0;
9019
+ const applied = this.appliedRuntimeConfigs.get(agentId);
9020
+ const appliedRevision = applied?.runtimeConfigRevision ?? -1;
9021
+ if (applied && desiredRevision <= appliedRevision) {
9022
+ this.pendingRuntimeConfigUpdates.delete(agentId);
9023
+ return desiredRevision === appliedRevision ? "idempotent" : "stale";
9024
+ }
9025
+ const canApplyNatively = applied && this.runtimeLaunchTuple(applied) === this.runtimeLaunchTuple(desired) && typeof session.updateSettings === "function";
9026
+ let result = { status: "unsupported" };
9027
+ if (canApplyNatively) {
9028
+ try {
9029
+ result = await session.updateSettings({
9030
+ reasoningEffort: desired.reasoningEffort ?? null
9031
+ });
9032
+ } catch (error) {
9033
+ this.log.warn("runtime config live apply threw; restarting at safe boundary", {
9034
+ agentId,
9035
+ revision: desiredRevision,
9036
+ error: String(error)
9037
+ });
9038
+ if (restartOnFailure)
9039
+ await this.restartForRuntimeConfig(agentId, session);
9040
+ return "saved_for_start";
9041
+ }
9042
+ }
9043
+ if (result.status !== "applied") {
9044
+ this.log.warn("runtime config live apply unavailable; restarting at safe boundary", {
9045
+ agentId,
9046
+ revision: desiredRevision,
9047
+ status: result.status,
9048
+ code: result.error?.code
9049
+ });
9050
+ if (restartOnFailure)
9051
+ await this.restartForRuntimeConfig(agentId, session);
9052
+ return "saved_for_start";
9053
+ }
9054
+ this.appliedRuntimeConfigs.set(agentId, desired);
9055
+ if ((this.pendingRuntimeConfigUpdates.get(agentId)?.runtimeConfigRevision ?? -1) === desiredRevision) {
9056
+ this.pendingRuntimeConfigUpdates.delete(agentId);
9057
+ }
9058
+ const latestRevision = this.runtimeConfigs.get(agentId)?.runtimeConfigRevision ?? 0;
9059
+ if (latestRevision <= desiredRevision) {
9060
+ this.dispatch({ type: "runtime_config_applied", agentId });
9061
+ return "applied";
9062
+ }
9063
+ }
9064
+ return "saved_for_start";
9065
+ } finally {
9066
+ this.runtimeConfigApplyRunning.delete(agentId);
9067
+ }
9068
+ }
9069
+ async restartForRuntimeConfig(agentId, session) {
9070
+ if (this.sessions.get(agentId) !== session)
9071
+ return;
9072
+ this.opts.timeline?.fenceSession(agentId);
9073
+ this.markResetting(agentId);
9074
+ await this.stop(agentId);
7987
9075
  }
7988
9076
  deliver(agentId, message) {
7989
9077
  const normalized = message.id ? message : {
7990
9078
  ...message,
7991
9079
  id: message.seq !== undefined ? `${agentId}:source:${message.seq}` : `${agentId}:synthetic:${this.nextDeliveryOrdinal++}`
7992
9080
  };
9081
+ if (this.sessions.has(agentId) && this.pendingRuntimeConfigUpdates.has(agentId)) {
9082
+ this.dispatch({
9083
+ type: "runtime_config_queued",
9084
+ agentId,
9085
+ message: normalized
9086
+ });
9087
+ return this.state.agents[agentId] !== undefined;
9088
+ }
7993
9089
  const effects = this.dispatch({ type: "wake", agentId, message: normalized, nowMs: this.now() });
7994
9090
  return effects.length > 0;
7995
9091
  }
@@ -8033,7 +9129,11 @@ class AgentProcessManager {
8033
9129
  this.emitErrorAudit(agentId, "reset", "resume_control_update_failed", "Reset aborted because resume control could not be persisted");
8034
9130
  throw new Error("Reset aborted because resume control could not be persisted");
8035
9131
  }
8036
- this.register(agentId, { runtimeConfig: opts.runtimeConfig, launchId: opts.launchId });
9132
+ this.register(agentId, {
9133
+ runtimeConfig: opts.runtimeConfig,
9134
+ launchId: opts.launchId,
9135
+ applyRuntimeConfig: false
9136
+ });
8037
9137
  if (!opts.forgetSession)
8038
9138
  this.opts.timeline?.fenceSession(agentId);
8039
9139
  this.abortCurrentTurn(agentId, opts.abortCause);
@@ -8134,6 +9234,9 @@ class AgentProcessManager {
8134
9234
  const agent = this.state.agents[agentId];
8135
9235
  return agent ? this.deriveActivity(agent) : null;
8136
9236
  }
9237
+ agentBackendId(agentId) {
9238
+ return this.liveBackendIds.get(agentId) ?? null;
9239
+ }
8137
9240
  statusProjection(nowMs) {
8138
9241
  return Object.values(this.state.agents).map((a) => ({
8139
9242
  agentId: a.agentId,
@@ -8659,6 +9762,7 @@ ${this.opts.wakePromptFooter}` : text;
8659
9762
  throw new Error(`AgentProcessManager: spawn for ${agentId} has no command`);
8660
9763
  const prompt = this.withFooter(first.text);
8661
9764
  const driver = this.opts.driverFor(agentId, this.runtimeConfigs.get(agentId));
9765
+ this.liveBackendIds.set(agentId, driver.id);
8662
9766
  const base = this.opts.baseContextFor(agentId);
8663
9767
  const configuredRuntime = this.runtimeConfigs.get(agentId) ?? base.config?.runtimeConfig;
8664
9768
  this.log.info("spawning agent", {
@@ -8779,7 +9883,10 @@ ${this.opts.wakePromptFooter}` : text;
8779
9883
  if (state.session && this.sessions.get(agentId) === state.session)
8780
9884
  this.sessions.delete(agentId);
8781
9885
  this.liveSessions.delete(agentId);
9886
+ if (this.activeSpawnState.get(agentId) === state)
9887
+ this.liveBackendIds.delete(agentId);
8782
9888
  if (this.activeSpawnState.get(agentId) === state) {
9889
+ this.appliedRuntimeConfigs.delete(agentId);
8783
9890
  this.activeSpawnState.delete(agentId);
8784
9891
  this.nonCleanEndMarker.delete(agentId);
8785
9892
  }
@@ -8827,6 +9934,10 @@ ${this.opts.wakePromptFooter}` : text;
8827
9934
  state.session = session;
8828
9935
  state.sessionInstanceId = session.sessionInstanceId;
8829
9936
  this.sessions.set(agentId, session);
9937
+ this.appliedRuntimeConfigs.set(agentId, runtimeConfig);
9938
+ if ((this.pendingRuntimeConfigUpdates.get(agentId)?.runtimeConfigRevision ?? -1) <= (runtimeConfig.runtimeConfigRevision ?? 0)) {
9939
+ this.pendingRuntimeConfigUpdates.delete(agentId);
9940
+ }
8830
9941
  this.dispatch({
8831
9942
  type: "attach_session",
8832
9943
  agentId,
@@ -9060,6 +10171,12 @@ ${this.opts.wakePromptFooter}` : text;
9060
10171
  runtime: runtimeId
9061
10172
  });
9062
10173
  }
10174
+ if (event.type === "token_usage") {
10175
+ this.opts.onTokenUsage?.({ agentId, backendId: runtimeId, usage: event.usage });
10176
+ }
10177
+ if (event.type === "rate_limits") {
10178
+ this.opts.onProviderQuota?.({ agentId, backendId: runtimeId, quota: event.quota });
10179
+ }
9063
10180
  if (event.type === "turn_started") {
9064
10181
  const timelineTurnOwner = {
9065
10182
  sessionInstanceId: event.sessionInstanceId,
@@ -9174,7 +10291,7 @@ ${this.opts.wakePromptFooter}` : text;
9174
10291
  this.logSessionEnded(agentId, "turn_end");
9175
10292
  const marker = this.nonCleanEndMarker.get(agentId);
9176
10293
  this.nonCleanEndMarker.delete(agentId);
9177
- this.dispatch(marker !== undefined ? {
10294
+ const completionEvent = marker !== undefined ? {
9178
10295
  type: "turn_completed",
9179
10296
  agentId,
9180
10297
  sessionInstanceId: event.sessionInstanceId,
@@ -9189,7 +10306,26 @@ ${this.opts.wakePromptFooter}` : text;
9189
10306
  sessionInstanceId: event.sessionInstanceId,
9190
10307
  nowMs: this.now(),
9191
10308
  turnId: event.turnId
9192
- }, owner);
10309
+ };
10310
+ if (this.pendingRuntimeConfigUpdates.has(agentId) && this.sessions.get(agentId) === owner.session) {
10311
+ this.convergeRuntimeConfig(agentId, false).then((result) => {
10312
+ if (result === "saved_for_start" && owner.session)
10313
+ this.markResetting(agentId);
10314
+ this.dispatch(completionEvent, owner);
10315
+ if (result === "saved_for_start" && owner.session) {
10316
+ this.restartForRuntimeConfig(agentId, owner.session);
10317
+ }
10318
+ }).catch((error) => {
10319
+ this.log.error("runtime config convergence failed", { agentId, error: String(error) });
10320
+ if (owner.session)
10321
+ this.markResetting(agentId);
10322
+ this.dispatch(completionEvent, owner);
10323
+ if (owner.session)
10324
+ this.restartForRuntimeConfig(agentId, owner.session);
10325
+ });
10326
+ return;
10327
+ }
10328
+ this.dispatch(completionEvent, owner);
9193
10329
  }
9194
10330
  }
9195
10331
  }
@@ -9242,7 +10378,7 @@ __export(exports_external, {
9242
10378
  regexes: () => exports_regexes,
9243
10379
  regex: () => _regex,
9244
10380
  refine: () => refine,
9245
- record: () => record3,
10381
+ record: () => record4,
9246
10382
  readonly: () => readonly,
9247
10383
  property: () => _property,
9248
10384
  promise: () => promise,
@@ -21398,7 +22534,7 @@ __export(exports_schemas2, {
21398
22534
  strictObject: () => strictObject,
21399
22535
  set: () => set,
21400
22536
  refine: () => refine,
21401
- record: () => record3,
22537
+ record: () => record4,
21402
22538
  readonly: () => readonly,
21403
22539
  promise: () => promise,
21404
22540
  preprocess: () => preprocess,
@@ -22475,7 +23611,7 @@ var ZodRecord = /* @__PURE__ */ $constructor("ZodRecord", (inst, def) => {
22475
23611
  inst.keyType = def.keyType;
22476
23612
  inst.valueType = def.valueType;
22477
23613
  });
22478
- function record3(keyType, valueType, params) {
23614
+ function record4(keyType, valueType, params) {
22479
23615
  if (!valueType || !valueType._zod) {
22480
23616
  return new ZodRecord({
22481
23617
  type: "record",
@@ -22940,7 +24076,7 @@ var stringbool = (...args) => _stringbool({
22940
24076
  }, ...args);
22941
24077
  function json(params) {
22942
24078
  const jsonSchema = lazy(() => {
22943
- return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema), record3(string2(), jsonSchema)]);
24079
+ return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema), record4(string2(), jsonSchema)]);
22944
24080
  });
22945
24081
  return jsonSchema;
22946
24082
  }
@@ -23489,6 +24625,7 @@ var exports_community_machine_schema = {};
23489
24625
  __export(exports_community_machine_schema, {
23490
24626
  communityMachineToken: () => communityMachineToken,
23491
24627
  communityMachineCredential: () => communityMachineCredential,
24628
+ communityMachineBackendQuota: () => communityMachineBackendQuota,
23492
24629
  communityMachine: () => communityMachine,
23493
24630
  communityDiagnosticReport: () => communityDiagnosticReport,
23494
24631
  communityBotBinding: () => communityBotBinding,
@@ -24085,7 +25222,7 @@ function sql(strings, ...params) {
24085
25222
  return new SQL([new StringChunk(str)]);
24086
25223
  }
24087
25224
  sql2.raw = raw;
24088
- function join9(chunks, separator) {
25225
+ function join10(chunks, separator) {
24089
25226
  const result = [];
24090
25227
  for (const [i, chunk] of chunks.entries()) {
24091
25228
  if (i > 0 && separator !== undefined) {
@@ -24095,7 +25232,7 @@ function sql(strings, ...params) {
24095
25232
  }
24096
25233
  return new SQL(result);
24097
25234
  }
24098
- sql2.join = join9;
25235
+ sql2.join = join10;
24099
25236
  function identifier(value) {
24100
25237
  return new Name(value);
24101
25238
  }
@@ -25092,6 +26229,8 @@ var user = sqliteTable("user", {
25092
26229
  email: text("email").unique().notNull(),
25093
26230
  emailVerified: integer2("emailVerified", { mode: "boolean" }),
25094
26231
  image: text("image"),
26232
+ avatarVersion: integer2("avatarVersion").notNull().default(0),
26233
+ avatarObjectKey: text("avatarObjectKey"),
25095
26234
  createdAt: text("createdAt").notNull().$defaultFn(() => new Date().toISOString()),
25096
26235
  updatedAt: text("updatedAt").notNull().$defaultFn(() => new Date().toISOString()),
25097
26236
  isBot: integer2("isBot", { mode: "boolean" }).notNull().default(false),
@@ -25765,8 +26904,23 @@ var communityBotBinding = sqliteTable("community_bot_binding", {
25765
26904
  runtime: text("runtime").notNull(),
25766
26905
  instruction: text("instruction").notNull().default(""),
25767
26906
  modelName: text("model_name"),
26907
+ reasoningEffort: text("reasoning_effort"),
26908
+ runtimeConfigRevision: integer2("runtime_config_revision").notNull().default(0),
25768
26909
  createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
25769
26910
  }, (t) => [index("idx_community_bot_binding_machine").on(t.machineId)]);
26911
+ var communityMachineBackendQuota = sqliteTable("community_machine_backend_quota", {
26912
+ machineId: text("machine_id").notNull().references(() => communityMachine.id, { onDelete: "cascade" }),
26913
+ agentBackendId: text("agent_backend_id").$type().notNull(),
26914
+ sourceEpoch: text("source_epoch").notNull(),
26915
+ status: text("status").$type().notNull(),
26916
+ planName: text("plan_name"),
26917
+ freshForSeconds: integer2("fresh_for_seconds"),
26918
+ limits: text("limits", { mode: "json" }).$type(),
26919
+ errorCode: text("error_code"),
26920
+ retryable: integer2("retryable", { mode: "boolean" }),
26921
+ observedAt: text("observed_at").notNull(),
26922
+ updatedAt: text("updated_at").notNull()
26923
+ }, (t) => [primaryKey({ columns: [t.machineId, t.agentBackendId] })]);
25770
26924
  var communityAgentRunnerKey = sqliteTable("community_agent_runner_key", {
25771
26925
  id: text("id").primaryKey().$defaultFn(() => "crkid_" + nanoid3()),
25772
26926
  userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
@@ -25945,6 +27099,11 @@ var HostCommandSchema = exports_external.discriminatedUnion("type", [
25945
27099
  config: exports_external.unknown(),
25946
27100
  launchId: exports_external.string().min(1)
25947
27101
  }),
27102
+ exports_external.object({
27103
+ type: exports_external.literal("agent:runtime_config_update"),
27104
+ agentId: exports_external.string().min(1),
27105
+ config: exports_external.unknown()
27106
+ }),
25948
27107
  exports_external.object({
25949
27108
  type: exports_external.literal("machine:reset_all"),
25950
27109
  resets: exports_external.array(exports_external.object({
@@ -26051,7 +27210,8 @@ class AgentRouter {
26051
27210
  version: r.version,
26052
27211
  status: r.status ?? "healthy",
26053
27212
  lastError: r.lastError,
26054
- lastErrorAt: r.lastErrorAt
27213
+ lastErrorAt: r.lastErrorAt,
27214
+ reasoning: r.reasoning
26055
27215
  });
26056
27216
  }
26057
27217
  }
@@ -26060,7 +27220,7 @@ class AgentRouter {
26060
27220
  this.opts.channel.onResync?.(() => ({
26061
27221
  ready: this.buildReady(),
26062
27222
  sessions: this.opts.manager.liveSessionReports(),
26063
- activities: this.opts.manager.liveAgentActivities()
27223
+ activities: this.opts.resyncActivities ? this.opts.resyncActivities() : this.opts.manager.liveAgentActivities()
26064
27224
  }));
26065
27225
  await this.opts.channel.reportReady(this.buildReady());
26066
27226
  }
@@ -26073,7 +27233,8 @@ class AgentRouter {
26073
27233
  platform: this.opts.platform,
26074
27234
  arch: this.opts.arch,
26075
27235
  osRelease: this.opts.osRelease,
26076
- daemonVersion: this.opts.daemonVersion
27236
+ daemonVersion: this.opts.daemonVersion,
27237
+ ...this.opts.providerQuotas ? { providerQuotas: this.opts.providerQuotas() } : {}
26077
27238
  };
26078
27239
  }
26079
27240
  healthyRuntimeIds() {
@@ -26114,11 +27275,10 @@ class AgentRouter {
26114
27275
  return;
26115
27276
  if (existing.status === "healthy" && !existing.lastError && !existing.lastErrorAt)
26116
27277
  return;
26117
- this.runtimes.set(id, {
26118
- id: existing.id,
26119
- version: existing.version,
26120
- status: "healthy"
26121
- });
27278
+ const healthy = { ...existing, status: "healthy" };
27279
+ delete healthy.lastError;
27280
+ delete healthy.lastErrorAt;
27281
+ this.runtimes.set(id, healthy);
26122
27282
  this.log.info("runtime marked healthy again", { runtimeId: id });
26123
27283
  this.scheduleReadyFrameResend();
26124
27284
  }
@@ -26295,6 +27455,27 @@ class AgentRouter {
26295
27455
  rewakePrompt: MODEL_SWITCH_REWAKE_PROMPT
26296
27456
  }));
26297
27457
  break;
27458
+ case "agent:runtime_config_update": {
27459
+ this.log.info("agent:runtime_config_update received", {
27460
+ agentId: cmd.agentId,
27461
+ revision: cmd.config.runtimeConfigRevision ?? 0
27462
+ });
27463
+ try {
27464
+ const result = await this.opts.manager.updateRuntimeConfig(cmd.agentId, cmd.config);
27465
+ this.log.info("agent:runtime_config_update accepted", {
27466
+ agentId: cmd.agentId,
27467
+ revision: cmd.config.runtimeConfigRevision ?? 0,
27468
+ result
27469
+ });
27470
+ } catch (err) {
27471
+ this.log.warn("agent:runtime_config_update rejected", {
27472
+ agentId: cmd.agentId,
27473
+ revision: cmd.config.runtimeConfigRevision ?? 0,
27474
+ error: err instanceof Error ? err.message : String(err)
27475
+ });
27476
+ }
27477
+ break;
27478
+ }
26298
27479
  case "agent:stop":
26299
27480
  this.log.info("agent:stop received", { agentId: cmd.agentId });
26300
27481
  try {
@@ -26517,20 +27698,20 @@ function parseLocalMessageReminderBody(body, agentId) {
26517
27698
  }
26518
27699
  if (!value || typeof value !== "object" || Array.isArray(value))
26519
27700
  return null;
26520
- const record4 = value;
26521
- if (Object.keys(record4).sort().join(",") !== "channel,remindAfterMs,sentSeq")
27701
+ const record5 = value;
27702
+ if (Object.keys(record5).sort().join(",") !== "channel,remindAfterMs,sentSeq")
26522
27703
  return null;
26523
- if (typeof record4.channel !== "string" || !isCanonicalChannelScope(record4.channel))
27704
+ if (typeof record5.channel !== "string" || !isCanonicalChannelScope(record5.channel))
26524
27705
  return null;
26525
- if (!Number.isSafeInteger(record4.sentSeq) || record4.sentSeq < 1)
27706
+ if (!Number.isSafeInteger(record5.sentSeq) || record5.sentSeq < 1)
26526
27707
  return null;
26527
- if (!Number.isSafeInteger(record4.remindAfterMs) || record4.remindAfterMs !== 0 && record4.remindAfterMs < LOCAL_MESSAGE_REMINDER_MIN_MS || record4.remindAfterMs > LOCAL_MESSAGE_REMINDER_MAX_MS)
27708
+ if (!Number.isSafeInteger(record5.remindAfterMs) || record5.remindAfterMs !== 0 && record5.remindAfterMs < LOCAL_MESSAGE_REMINDER_MIN_MS || record5.remindAfterMs > LOCAL_MESSAGE_REMINDER_MAX_MS)
26528
27709
  return null;
26529
27710
  return {
26530
27711
  agentId,
26531
- channel: record4.channel,
26532
- sentSeq: record4.sentSeq,
26533
- remindAfterMs: record4.remindAfterMs
27712
+ channel: record5.channel,
27713
+ sentSeq: record5.sentSeq,
27714
+ remindAfterMs: record5.remindAfterMs
26534
27715
  };
26535
27716
  }
26536
27717
  async function handleLocalMessageReminder(req, res, agentId, onArm) {
@@ -26779,7 +27960,7 @@ function joinPath(basePath, reqUrl) {
26779
27960
  return base + reqPath || "/";
26780
27961
  }
26781
27962
  // src/daemon/createDaemon.ts
26782
- import { homedir as homedir4 } from "os";
27963
+ import { homedir as homedir5 } from "os";
26783
27964
  import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync7 } from "node:fs";
26784
27965
 
26785
27966
  // src/util/rotatingFileSink.ts
@@ -27054,7 +28235,7 @@ var MAX_PROFILE_ABOUT_LENGTH = 1000;
27054
28235
  var MAX_MESSAGE_CONTENT_LENGTH = 4000;
27055
28236
  var MAX_ATTACHMENTS_PER_MESSAGE = 10;
27056
28237
  var MAX_ATTACHMENT_SIZE_BYTES = 25 * 1024 * 1024;
27057
- var MAX_ATTACHMENT_THUMBNAIL_SIZE_BYTES = 50 * 1024;
28238
+ var MAX_ATTACHMENT_THUMBNAIL_SIZE_BYTES = 512 * 1024;
27058
28239
  var MAX_SERVER_ICON_SIZE_BYTES = 5 * 1024 * 1024;
27059
28240
  var ALLOWED_ICON_MIME_TYPES = [
27060
28241
  "image/png",
@@ -27063,6 +28244,92 @@ var ALLOWED_ICON_MIME_TYPES = [
27063
28244
  "image/gif"
27064
28245
  ];
27065
28246
  var MAX_ICON_SOURCE_FILE_SIZE_BYTES = 15 * 1024 * 1024;
28247
+ // ../shared/src/provider-telemetry.ts
28248
+ var safeToken = exports_external.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
28249
+ var boundedText = exports_external.string().min(1).refine((value) => new TextEncoder().encode(value).length <= 64, { message: "must be at most 64 UTF-8 bytes" });
28250
+ var DailyUsageMetricSchema = safeToken.nullable();
28251
+ var DailyUsageSnapshotSchema = exports_external.object({
28252
+ botId: exports_external.string().min(1),
28253
+ day: exports_external.string().regex(/^\d{4}-\d{2}-\d{2}$/),
28254
+ metrics: exports_external.object({
28255
+ input: DailyUsageMetricSchema,
28256
+ output: DailyUsageMetricSchema,
28257
+ cache: DailyUsageMetricSchema
28258
+ }).strict()
28259
+ }).strict();
28260
+ var QuotaProductIdentitySchema = exports_external.discriminatedUnion("kind", [
28261
+ exports_external.object({ kind: exports_external.literal("reported"), id: boundedText, displayName: boundedText }).strict(),
28262
+ exports_external.object({ kind: exports_external.literal("unknown"), displayName: boundedText }).strict()
28263
+ ]);
28264
+ var QuotaModelIdentitySchema = exports_external.discriminatedUnion("kind", [
28265
+ exports_external.object({ kind: exports_external.literal("reported"), id: boundedText }).strict(),
28266
+ exports_external.object({ kind: exports_external.literal("not_applicable") }).strict(),
28267
+ exports_external.object({ kind: exports_external.literal("unknown") }).strict()
28268
+ ]);
28269
+ var QuotaWindowIdentitySchema = exports_external.discriminatedUnion("kind", [
28270
+ exports_external.object({
28271
+ kind: exports_external.literal("rolling"),
28272
+ durationSeconds: exports_external.number().int().positive().max(Number.MAX_SAFE_INTEGER),
28273
+ displayName: boundedText
28274
+ }).strict(),
28275
+ exports_external.object({
28276
+ kind: exports_external.literal("calendar"),
28277
+ period: exports_external.enum(["day", "week", "month"]),
28278
+ displayName: boundedText
28279
+ }).strict(),
28280
+ exports_external.object({
28281
+ kind: exports_external.literal("provider_defined"),
28282
+ id: boundedText,
28283
+ durationSeconds: exports_external.number().int().positive().max(Number.MAX_SAFE_INTEGER).optional(),
28284
+ displayName: boundedText
28285
+ }).strict()
28286
+ ]);
28287
+ var QuotaLimitSchema = exports_external.object({
28288
+ bucket: exports_external.object({
28289
+ limitId: boundedText,
28290
+ product: QuotaProductIdentitySchema,
28291
+ model: QuotaModelIdentitySchema,
28292
+ window: QuotaWindowIdentitySchema
28293
+ }).strict(),
28294
+ usedPercent: exports_external.number().finite().min(0).max(100),
28295
+ resetsAt: exports_external.string().datetime({ offset: true }).optional()
28296
+ }).strict();
28297
+ function quotaIdentity(limit) {
28298
+ const { product, model, window: window2, limitId } = limit.bucket;
28299
+ const productKey = product.kind === "reported" ? `reported:${product.id}` : "unknown";
28300
+ const modelKey = model.kind === "reported" ? `reported:${model.id}` : model.kind;
28301
+ const windowKey = window2.kind === "rolling" ? `rolling:${window2.durationSeconds}` : window2.kind === "calendar" ? `calendar:${window2.period}` : `provider_defined:${window2.id}:${window2.durationSeconds === undefined ? "absent" : window2.durationSeconds}`;
28302
+ return `${productKey}\x00${modelKey}\x00${windowKey}\x00${limitId}`;
28303
+ }
28304
+ var AvailableQuotaObservationSchema = exports_external.object({
28305
+ status: exports_external.literal("available"),
28306
+ sourceEpoch: exports_external.string().regex(/^[A-Za-z0-9_-]{22}$/),
28307
+ planName: boundedText.optional(),
28308
+ freshForSeconds: exports_external.number().int().positive().max(86400),
28309
+ limits: exports_external.array(QuotaLimitSchema).min(1).max(8)
28310
+ }).strict().superRefine((value, ctx) => {
28311
+ const identities = new Set;
28312
+ for (const [index2, limit] of value.limits.entries()) {
28313
+ const identity = quotaIdentity(limit);
28314
+ if (identities.has(identity)) {
28315
+ ctx.addIssue({ code: "custom", message: "duplicate quota bucket identity", path: ["limits", index2] });
28316
+ }
28317
+ identities.add(identity);
28318
+ }
28319
+ });
28320
+ var ProviderQuotaObservationSchema = exports_external.union([
28321
+ AvailableQuotaObservationSchema,
28322
+ exports_external.object({
28323
+ status: exports_external.literal("error"),
28324
+ sourceEpoch: exports_external.string().regex(/^[A-Za-z0-9_-]{22}$/),
28325
+ code: exports_external.enum(["unavailable", "unauthorized", "network", "provider_error", "invalid_response"]),
28326
+ retryable: exports_external.boolean()
28327
+ }).strict()
28328
+ ]);
28329
+ var ProviderQuotaSnapshotSchema = exports_external.object({
28330
+ agentBackendId: exports_external.enum(["claude", "codex"]),
28331
+ observation: ProviderQuotaObservationSchema
28332
+ }).strict();
27066
28333
  // ../shared/src/utils/slug.ts
27067
28334
  init_nanoid();
27068
28335
  var slugId = customAlphabet("0123456789abcdefghijklmnopqrstuvwxyz");
@@ -27577,12 +28844,61 @@ var CreateThreadRequestSchema = exports_external.object({
27577
28844
  attachment_ids: exports_external.array(exports_external.string()).optional()
27578
28845
  });
27579
28846
  var RUNTIME_ID_RE = /^[A-Za-z0-9._@/-]+$/;
28847
+ var REASONING_EFFORT_RE = /^[A-Za-z0-9._-]+$/;
28848
+ var COMMUNITY_REASONING_EFFORT_MAX = 32;
28849
+ var COMMUNITY_REASONING_DESCRIPTION_MAX = 256;
28850
+ var COMMUNITY_REASONING_OPTIONS_MAX = 16;
28851
+ var COMMUNITY_REASONING_MODELS_MAX = 512;
28852
+ var ReasoningEffortSchema = exports_external.string().min(1).max(COMMUNITY_REASONING_EFFORT_MAX).regex(REASONING_EFFORT_RE, "invalid reasoning effort charset");
28853
+ var RuntimeReasoningOptionSchema = exports_external.object({
28854
+ value: ReasoningEffortSchema,
28855
+ description: exports_external.string().max(COMMUNITY_REASONING_DESCRIPTION_MAX).optional()
28856
+ });
28857
+ var RuntimeReasoningModelSchema = exports_external.object({
28858
+ id: exports_external.string().min(1).max(100),
28859
+ displayName: exports_external.string().min(1).max(COMMUNITY_REASONING_DESCRIPTION_MAX).optional().catch(undefined),
28860
+ supportedReasoningEfforts: exports_external.array(exports_external.unknown()).max(COMMUNITY_REASONING_OPTIONS_MAX).transform((options) => {
28861
+ const seen = new Set;
28862
+ return options.flatMap((candidate) => {
28863
+ const parsed = RuntimeReasoningOptionSchema.safeParse(candidate);
28864
+ if (!parsed.success)
28865
+ return [];
28866
+ const option = parsed.data;
28867
+ if (seen.has(option.value))
28868
+ return [];
28869
+ seen.add(option.value);
28870
+ return [option];
28871
+ });
28872
+ }),
28873
+ defaultReasoningEffort: ReasoningEffortSchema.optional().catch(undefined)
28874
+ }).transform((model) => {
28875
+ const { defaultReasoningEffort, ...rest } = model;
28876
+ return defaultReasoningEffort !== undefined && model.supportedReasoningEfforts.some((option) => option.value === defaultReasoningEffort) ? { ...rest, defaultReasoningEffort } : rest;
28877
+ });
28878
+ var RuntimeReasoningCatalogSchema = exports_external.object({
28879
+ updateMode: exports_external.enum(["live_next_turn", "context_preserving_restart", "unsupported"]),
28880
+ defaultModelId: exports_external.string().min(1).max(100).optional().catch(undefined),
28881
+ models: exports_external.array(exports_external.unknown()).max(COMMUNITY_REASONING_MODELS_MAX).transform((models) => {
28882
+ const seen = new Set;
28883
+ return models.flatMap((candidate) => {
28884
+ const parsed = RuntimeReasoningModelSchema.safeParse(candidate);
28885
+ if (!parsed.success)
28886
+ return [];
28887
+ const model = parsed.data;
28888
+ if (seen.has(model.id))
28889
+ return [];
28890
+ seen.add(model.id);
28891
+ return [model];
28892
+ });
28893
+ })
28894
+ });
27580
28895
  var CommunityMachineRuntimeSchema = exports_external.object({
27581
28896
  id: exports_external.string().min(1).max(COMMUNITY_RUNTIME_ID_MAX).regex(RUNTIME_ID_RE, "invalid runtime id charset"),
27582
28897
  version: exports_external.string().max(COMMUNITY_RUNTIME_VERSION_MAX).optional(),
27583
28898
  status: exports_external.enum(["healthy", "unhealthy"]).catch("healthy").default("healthy"),
27584
28899
  lastError: exports_external.string().max(128).optional(),
27585
- lastErrorAt: exports_external.string().optional()
28900
+ lastErrorAt: exports_external.string().optional(),
28901
+ reasoning: RuntimeReasoningCatalogSchema.optional().catch(undefined)
27586
28902
  });
27587
28903
  var CommunityMachineRuntimeListSchema = exports_external.array(CommunityMachineRuntimeSchema).max(COMMUNITY_RUNTIME_LIST_MAX).transform((list) => {
27588
28904
  const seen = new Set;
@@ -27634,7 +28950,8 @@ var HostReadyMessageSchema = exports_external.object({
27634
28950
  platform: exports_external.string().optional(),
27635
28951
  arch: exports_external.string().optional(),
27636
28952
  osRelease: exports_external.string().optional(),
27637
- daemonVersion: exports_external.string().optional()
28953
+ daemonVersion: exports_external.string().optional(),
28954
+ providerQuotas: exports_external.array(ProviderQuotaSnapshotSchema).max(2).optional().default([])
27638
28955
  });
27639
28956
  var CommunityDaemonReadySchema = exports_external.object({
27640
28957
  runtimeReport: CommunityMachineRuntimeListSchema.optional(),
@@ -27655,7 +28972,9 @@ var SessionErrorFrameSchema = exports_external.object({
27655
28972
  var AgentActivityMessageSchema = exports_external.object({
27656
28973
  type: exports_external.literal("agent_activity"),
27657
28974
  agentId: exports_external.string(),
27658
- state: exports_external.enum(["idle", "starting", "running", "stopping"])
28975
+ state: exports_external.enum(["idle", "starting", "running", "stopping"]),
28976
+ dailyUsage: exports_external.array(DailyUsageSnapshotSchema).max(7).optional(),
28977
+ quota: ProviderQuotaSnapshotSchema.optional()
27659
28978
  });
27660
28979
  var AgentTypingMessageSchema = exports_external.object({
27661
28980
  type: exports_external.literal("agent_typing"),
@@ -27730,15 +29049,17 @@ var CommunityBotCreateRequestSchema = exports_external.object({
27730
29049
  machineId: exports_external.string().min(1),
27731
29050
  runtime: exports_external.string().min(1),
27732
29051
  image: BotImageUrlSchema.optional(),
27733
- model: exports_external.string().trim().min(1).max(100).nullable().optional()
29052
+ model: exports_external.string().trim().min(1).max(100).nullable().optional(),
29053
+ reasoningEffort: ReasoningEffortSchema.nullable().optional()
27734
29054
  });
27735
29055
  var CommunityBotPatchRequestSchema = exports_external.object({
27736
29056
  name: exports_external.string().trim().min(COMMUNITY_BOT_NAME_MIN).max(COMMUNITY_BOT_NAME_MAX).refine(isMentionSafeName, { message: MENTION_SAFE_NAME_MSG }).optional(),
27737
29057
  description: exports_external.string().max(COMMUNITY_BOT_DESCRIPTION_MAX).optional(),
27738
29058
  image: BotImageUrlSchema.nullable().optional(),
27739
29059
  model: exports_external.string().trim().min(1).max(100).nullable().optional(),
27740
- runtime: exports_external.string().trim().min(1).max(COMMUNITY_RUNTIME_ID_MAX).optional()
27741
- }).refine((v) => v.name !== undefined || v.description !== undefined || v.image !== undefined || v.runtime !== undefined || ("model" in v), {
29060
+ runtime: exports_external.string().trim().min(1).max(COMMUNITY_RUNTIME_ID_MAX).optional(),
29061
+ reasoningEffort: ReasoningEffortSchema.nullable().optional()
29062
+ }).refine((v) => v.name !== undefined || v.description !== undefined || v.image !== undefined || v.runtime !== undefined || ("reasoningEffort" in v) || ("model" in v), {
27742
29063
  message: "at least one field must be provided"
27743
29064
  });
27744
29065
  var CommunityBotAddToServerRequestSchema = exports_external.object({
@@ -27940,6 +29261,7 @@ __export(exports_community_schema, {
27940
29261
  communityChannelMember: () => communityChannelMember,
27941
29262
  communityChannel: () => communityChannel,
27942
29263
  communityCategory: () => communityCategory,
29264
+ communityBotDailyTokenUsage: () => communityBotDailyTokenUsage,
27943
29265
  communityBotDailyActivity: () => communityBotDailyActivity,
27944
29266
  communityBotApprovalRequest: () => communityBotApprovalRequest,
27945
29267
  communityBotActivityEvent: () => communityBotActivityEvent,
@@ -28192,6 +29514,17 @@ var communityBotDailyActivity = sqliteTable("community_bot_daily_activity", {
28192
29514
  handledCount: integer2("handled_count").notNull().default(0),
28193
29515
  sentCount: integer2("sent_count").notNull().default(0)
28194
29516
  }, (t) => [primaryKey({ columns: [t.botId, t.day] })]);
29517
+ var communityBotDailyTokenUsage = sqliteTable("community_bot_daily_token_usage", {
29518
+ botId: text("bot_id").notNull().references(() => user.id, { onDelete: "cascade" }),
29519
+ day: text("day").notNull(),
29520
+ inputTokens: integer2("input_tokens"),
29521
+ outputTokens: integer2("output_tokens"),
29522
+ cacheTokens: integer2("cache_tokens"),
29523
+ updatedAt: text("updated_at").notNull()
29524
+ }, (t) => [
29525
+ primaryKey({ columns: [t.botId, t.day] }),
29526
+ index("idx_community_bot_daily_token_usage_day").on(t.day)
29527
+ ]);
28195
29528
  var communityMessageMark = sqliteTable("community_message_mark", {
28196
29529
  id: text("id").primaryKey().$defaultFn(() => nanoid3()),
28197
29530
  userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
@@ -28316,7 +29649,8 @@ var listedMessageProjection = {
28316
29649
  clientNonce: communityMessage.clientNonce,
28317
29650
  authorName: user.name,
28318
29651
  authorEmail: user.email,
28319
- authorImage: user.image
29652
+ authorImage: user.image,
29653
+ authorAvatarVersion: user.avatarVersion
28320
29654
  };
28321
29655
 
28322
29656
  // ../shared/src/db/queries/user.ts
@@ -28326,6 +29660,7 @@ var publicUserColumns = {
28326
29660
  email: user.email,
28327
29661
  emailVerified: user.emailVerified,
28328
29662
  image: user.image,
29663
+ avatarVersion: user.avatarVersion,
28329
29664
  createdAt: user.createdAt,
28330
29665
  updatedAt: user.updatedAt,
28331
29666
  discriminator: user.discriminator
@@ -28336,6 +29671,12 @@ var internalUserColumns = {
28336
29671
  ownerUserId: user.ownerUserId,
28337
29672
  deletedAt: user.deletedAt
28338
29673
  };
29674
+ var avatarPublishColumns = {
29675
+ id: user.id,
29676
+ image: user.image,
29677
+ avatarVersion: user.avatarVersion,
29678
+ avatarObjectKey: user.avatarObjectKey
29679
+ };
28339
29680
 
28340
29681
  // ../shared/src/db/queries/community/channel.ts
28341
29682
  var CHANNEL_COLUMNS = {
@@ -28388,7 +29729,8 @@ var friendApprovalProfileSchema = exports_external.strictObject({
28388
29729
  id: string4,
28389
29730
  name: string4,
28390
29731
  discriminator: string4,
28391
- image: nullableString
29732
+ image: nullableString,
29733
+ avatarVersion: exports_external.number().int().nonnegative()
28392
29734
  });
28393
29735
  var FriendApprovalPayloadSchema = exports_external.strictObject({
28394
29736
  friendshipId: string4,
@@ -28414,6 +29756,7 @@ var messageSchema = exports_external.strictObject({
28414
29756
  authorId: string4,
28415
29757
  authorName: string4,
28416
29758
  authorAvatar: string4.optional(),
29759
+ authorAvatarVersion: exports_external.number().int().nonnegative(),
28417
29760
  content: string4,
28418
29761
  type: exports_external.enum(["chat", "system"]),
28419
29762
  systemKind: exports_external.literal("thread").optional(),
@@ -28421,6 +29764,7 @@ var messageSchema = exports_external.strictObject({
28421
29764
  replyToId: nullableString.optional(),
28422
29765
  replyTo: exports_external.strictObject({
28423
29766
  id: string4,
29767
+ authorId: string4.optional(),
28424
29768
  authorName: string4,
28425
29769
  text: string4,
28426
29770
  deleted: exports_external.boolean().optional()
@@ -28615,6 +29959,7 @@ var communityMemberJoinSchema = exports_external.strictObject({
28615
29959
  name: string4,
28616
29960
  discriminator: string4,
28617
29961
  avatar: string4.optional(),
29962
+ avatarVersion: exports_external.number().int().nonnegative(),
28618
29963
  role: string4,
28619
29964
  joinedAt: string4
28620
29965
  })
@@ -28717,6 +30062,22 @@ var communityStatusUpdateSchema = exports_external.strictObject({
28717
30062
  statusEmoji: nullableString,
28718
30063
  statusText: nullableString
28719
30064
  });
30065
+ var communityIdentityUpdateSchema = exports_external.strictObject({
30066
+ type: exports_external.literal("community:identity.update"),
30067
+ userId: string4,
30068
+ avatar: string4,
30069
+ avatarVersion: exports_external.number().int().positive()
30070
+ });
30071
+ var communityProfileUpdateSchema = exports_external.strictObject({
30072
+ type: exports_external.literal("community:profile.update"),
30073
+ userId: string4,
30074
+ name: string4,
30075
+ discriminator: string4,
30076
+ aboutMe: string4,
30077
+ bannerColor: nullableString,
30078
+ kind: exports_external.enum(["human", "bot"]),
30079
+ ownerUserId: nullableString
30080
+ });
28720
30081
  var machineRuntimeSchema = CommunityMachineRuntimeSchema.strict();
28721
30082
  var CommunityMachineSummarySchema2 = exports_external.strictObject({
28722
30083
  id: string4,
@@ -28805,6 +30166,8 @@ var CommunityWsEventDiscriminatedSchema = exports_external.discriminatedUnion("t
28805
30166
  communityInboxChangedSchema,
28806
30167
  communityPresenceUpdateSchema,
28807
30168
  communityStatusUpdateSchema,
30169
+ communityIdentityUpdateSchema,
30170
+ communityProfileUpdateSchema,
28808
30171
  communityMachineCreatedSchema,
28809
30172
  communityMachineStatusSchema,
28810
30173
  communityMachineUpdatedSchema,
@@ -28851,6 +30214,8 @@ var WS_EVENTS = {
28851
30214
  INBOX_CHANGED: "community:inbox.changed",
28852
30215
  PRESENCE_UPDATE: "community:presence.update",
28853
30216
  STATUS_UPDATE: "community:status.update",
30217
+ IDENTITY_UPDATE: "community:identity.update",
30218
+ PROFILE_UPDATE: "community:profile.update",
28854
30219
  MACHINE_CREATED: "community:machine.created",
28855
30220
  MACHINE_STATUS: "community:machine.status",
28856
30221
  MACHINE_UPDATED: "community:machine.updated",
@@ -29411,23 +30776,43 @@ class WsControlChannel {
29411
30776
  this.ws.send(JSON.stringify(frame));
29412
30777
  }
29413
30778
  resyncOnConnect() {
30779
+ const sendActivities = (activities, counts) => {
30780
+ for (const activity of activities) {
30781
+ this.sendFrame({ type: "agent_activity", ...activity });
30782
+ }
30783
+ this.log.info("resync sent", {
30784
+ ready: counts.ready,
30785
+ sessions: counts.sessions,
30786
+ activities: activities.length,
30787
+ pendingAuditEvents: this.pendingBotAuditEvents.size
30788
+ });
30789
+ };
29414
30790
  if (this.resyncProvider) {
29415
- const { ready, sessions, activities } = this.resyncProvider();
29416
- this.sendFrame({ type: "ready", ...ready });
29417
- for (const s of sessions)
29418
- this.sendFrame({ type: "agent_session", ...s });
29419
- const liveActivities = activities ?? [];
29420
- for (const a of liveActivities)
29421
- this.sendFrame({ type: "agent_activity", ...a });
30791
+ const socketAtStart = this.ws;
30792
+ const snapshot = this.resyncProvider();
30793
+ this.sendFrame({ type: "ready", ...snapshot.ready });
30794
+ for (const session2 of snapshot.sessions) {
30795
+ this.sendFrame({ type: "agent_session", ...session2 });
30796
+ }
29422
30797
  for (const frame of this.pendingBotAuditEvents.values())
29423
30798
  this.sendFrame(frame);
29424
30799
  this.scheduleAuditRetry();
29425
- this.log.info("resync sent", {
29426
- ready: ready.runtimeReport.length,
29427
- sessions: sessions.length,
29428
- activities: liveActivities.length,
29429
- pendingAuditEvents: this.pendingBotAuditEvents.size
29430
- });
30800
+ const activities = snapshot.activities ?? [];
30801
+ const counts = {
30802
+ ready: snapshot.ready.runtimeReport.length,
30803
+ sessions: snapshot.sessions.length
30804
+ };
30805
+ if (activities instanceof Promise) {
30806
+ activities.then((resolved) => {
30807
+ if (this.ws === socketAtStart && this.statusValue === "open") {
30808
+ sendActivities(resolved, counts);
30809
+ }
30810
+ }).catch((err) => {
30811
+ this.log.warn("resync provider failed", { err: describeErr(err) });
30812
+ });
30813
+ } else {
30814
+ sendActivities(activities, counts);
30815
+ }
29431
30816
  }
29432
30817
  for (const hook of this.resyncHooks) {
29433
30818
  try {
@@ -29645,8 +31030,8 @@ class WsControlChannel {
29645
31030
  }
29646
31031
  // src/timeline/timeline.ts
29647
31032
  import * as fs7 from "node:fs";
29648
- import { createHash as createHash4, randomBytes as randomBytes4 } from "node:crypto";
29649
- import { basename, dirname as dirname3, join as join10 } from "node:path";
31033
+ import { createHash as createHash4, randomBytes as randomBytes6 } from "node:crypto";
31034
+ import { basename, dirname as dirname3, join as join11 } from "node:path";
29650
31035
 
29651
31036
  // src/timeline/filelock.ts
29652
31037
  import * as fs6 from "fs";
@@ -29989,7 +31374,7 @@ function scanTimelineFile(filePath) {
29989
31374
  }
29990
31375
  }
29991
31376
  function atomicReplaceTimeline(filePath, lines) {
29992
- const tempPath = join10(dirname3(filePath), `.${basename(filePath)}.${process.pid}.${randomBytes4(12).toString("hex")}.tmp`);
31377
+ const tempPath = join11(dirname3(filePath), `.${basename(filePath)}.${process.pid}.${randomBytes6(12).toString("hex")}.tmp`);
29993
31378
  let fd = null;
29994
31379
  try {
29995
31380
  fd = fs7.openSync(tempPath, "wx", 384);
@@ -30073,14 +31458,14 @@ function readRecentEntries(timelineDir, opts = {}) {
30073
31458
  const filenames = recentFilenames(maxDays, now).reverse();
30074
31459
  const entries = [];
30075
31460
  for (const filename of filenames) {
30076
- entries.push(...readJsonl(join10(timelineDir, filename)));
31461
+ entries.push(...readJsonl(join11(timelineDir, filename)));
30077
31462
  }
30078
31463
  return entries;
30079
31464
  }
30080
31465
  function readResumeControlState(timelineDir) {
30081
31466
  if (timelineDirectoryState(timelineDir) !== "safe")
30082
31467
  return { kind: "missing" };
30083
- const filePath = join10(timelineDir, RESUME_CONTROL_FILENAME);
31468
+ const filePath = join11(timelineDir, RESUME_CONTROL_FILENAME);
30084
31469
  let source;
30085
31470
  try {
30086
31471
  source = fs7.lstatSync(filePath);
@@ -30156,8 +31541,8 @@ function updateResumeControlState(timelineDir, update) {
30156
31541
  `;
30157
31542
  if (Buffer.byteLength(body, "utf8") > RESUME_CONTROL_MAX_BYTES)
30158
31543
  return false;
30159
- const filePath = join10(timelineDir, RESUME_CONTROL_FILENAME);
30160
- const tempPath = join10(timelineDir, `.${RESUME_CONTROL_FILENAME}.${process.pid}.${randomBytes4(12).toString("hex")}.tmp`);
31544
+ const filePath = join11(timelineDir, RESUME_CONTROL_FILENAME);
31545
+ const tempPath = join11(timelineDir, `.${RESUME_CONTROL_FILENAME}.${process.pid}.${randomBytes6(12).toString("hex")}.tmp`);
30161
31546
  let fd = null;
30162
31547
  try {
30163
31548
  fd = fs7.openSync(tempPath, "wx", 384);
@@ -30187,7 +31572,7 @@ function appendTrackedEntry(timelineDir, entry, now = new Date) {
30187
31572
  if (timelineDirectoryState(timelineDir) !== "safe")
30188
31573
  return { status: "rejected", reason: "unsafe" };
30189
31574
  const filename = filenameForDate(now);
30190
- const filePath = join10(timelineDir, filename);
31575
+ const filePath = join11(timelineDir, filename);
30191
31576
  const lockPath = lockPathFor(timelineDir, filename);
30192
31577
  try {
30193
31578
  if (!acquireLock(lockPath))
@@ -30215,7 +31600,7 @@ function updateTrackedEntry(timelineDir, handle, update) {
30215
31600
  if (!DATE_FILENAME_PATTERN.test(handle.filename) || basename(handle.filename) !== handle.filename) {
30216
31601
  return { status: "rejected", reason: "unsafe" };
30217
31602
  }
30218
- const filePath = join10(timelineDir, handle.filename);
31603
+ const filePath = join11(timelineDir, handle.filename);
30219
31604
  const lockPath = lockPathFor(timelineDir, handle.filename);
30220
31605
  try {
30221
31606
  if (!acquireLock(lockPath))
@@ -30274,10 +31659,10 @@ async function sweepTimelineHistory(workingDirectoryBase, opts = {}) {
30274
31659
  return;
30275
31660
  }
30276
31661
  for (const agentName of agentNames) {
30277
- const agentDir = join10(workingDirectoryBase, agentName);
31662
+ const agentDir = join11(workingDirectoryBase, agentName);
30278
31663
  if (!isRealDirectory(agentDir))
30279
31664
  continue;
30280
- const timelineDir = join10(agentDir, ".context_timeline");
31665
+ const timelineDir = join11(agentDir, ".context_timeline");
30281
31666
  if (!isRealDirectory(timelineDir))
30282
31667
  continue;
30283
31668
  let filenames;
@@ -30287,7 +31672,7 @@ async function sweepTimelineHistory(workingDirectoryBase, opts = {}) {
30287
31672
  continue;
30288
31673
  }
30289
31674
  for (const filename of filenames) {
30290
- const filePath = join10(timelineDir, filename);
31675
+ const filePath = join11(timelineDir, filename);
30291
31676
  let source;
30292
31677
  try {
30293
31678
  source = fs7.lstatSync(filePath);
@@ -30936,7 +32321,12 @@ async function detectRuntimes() {
30936
32321
  const driver = getDriver(id);
30937
32322
  const probe = await driver.probe();
30938
32323
  if (probe.status === "healthy") {
30939
- results.push({ id, status: "healthy", version: probe.version });
32324
+ results.push({
32325
+ id,
32326
+ status: "healthy",
32327
+ version: probe.version,
32328
+ reasoning: probe.reasoning
32329
+ });
30940
32330
  } else {
30941
32331
  results.push({
30942
32332
  id,
@@ -31041,15 +32431,15 @@ class MessageReminderScheduler {
31041
32431
  const startedAt = this.now();
31042
32432
  const dueAt = startedAt + input.remindAfterMs;
31043
32433
  const sentRef = `${input.channel}#${input.sentSeq}`;
31044
- const record4 = {
32434
+ const record5 = {
31045
32435
  ...input,
31046
32436
  sentRef,
31047
32437
  startedAt,
31048
32438
  dueAt,
31049
32439
  timer: undefined
31050
32440
  };
31051
- record4.timer = this.setTimer(() => {
31052
- if (this.reminders.get(key) !== record4)
32441
+ record5.timer = this.setTimer(() => {
32442
+ if (this.reminders.get(key) !== record5)
31053
32443
  return;
31054
32444
  this.reminders.delete(key);
31055
32445
  try {
@@ -31060,8 +32450,8 @@ class MessageReminderScheduler {
31060
32450
  Promise.resolve(delivery).catch(() => {});
31061
32451
  } catch {}
31062
32452
  }, input.remindAfterMs);
31063
- record4.timer.unref?.();
31064
- this.reminders.set(key, record4);
32453
+ record5.timer.unref?.();
32454
+ this.reminders.set(key, record5);
31065
32455
  return { armed: true, dueAt };
31066
32456
  }
31067
32457
  observe(agentId, channel2, latestSeq) {
@@ -31101,8 +32491,8 @@ class MessageReminderScheduler {
31101
32491
 
31102
32492
  // src/manager/agentDriverHost.ts
31103
32493
  import { randomUUID as randomUUID6 } from "node:crypto";
31104
- import { homedir as homedir3 } from "node:os";
31105
- import { join as join12 } from "node:path";
32494
+ import { homedir as homedir4 } from "node:os";
32495
+ import { join as join13 } from "node:path";
31106
32496
 
31107
32497
  // src/drivers/gitIdentityEnv.ts
31108
32498
  import { execFileSync as execFileSync3 } from "child_process";
@@ -31227,7 +32617,7 @@ function createDaemonAgentDriverHost(ctx, onRawLine) {
31227
32617
  hostUser: readHostGitIdentity() ?? undefined
31228
32618
  }),
31229
32619
  platformProtected: {
31230
- ALOOK_HOME: process.env.ALOOK_HOME ?? join12(homedir3(), ".alook"),
32620
+ ALOOK_HOME: process.env.ALOOK_HOME ?? join13(homedir4(), ".alook"),
31231
32621
  ALOOK_ID: ctx.agentId,
31232
32622
  ALOOK_CLI: ctx.agentCliPath,
31233
32623
  ALOOK_SERVER_URL: ctx.config.serverUrl,
@@ -31332,6 +32722,182 @@ class DaemonSelfSleepScheduler {
31332
32722
  }
31333
32723
  }
31334
32724
 
32725
+ // src/telemetry/dailyTokenUsage.ts
32726
+ import { chmod, mkdir, open, readFile as readFile2, rename, rm } from "node:fs/promises";
32727
+ import { dirname as dirname5, join as join14 } from "node:path";
32728
+ import { randomUUID as randomUUID7 } from "node:crypto";
32729
+ function dayKey(at) {
32730
+ return at.toISOString().slice(0, 10);
32731
+ }
32732
+ function retainedDays(at) {
32733
+ const days = new Set;
32734
+ for (let offset = 0;offset < 7; offset += 1) {
32735
+ days.add(dayKey(new Date(at.getTime() - offset * 86400000)));
32736
+ }
32737
+ return days;
32738
+ }
32739
+ function isMetric(value) {
32740
+ return value === null || typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
32741
+ }
32742
+ function isSnapshot(value) {
32743
+ if (!value || typeof value !== "object")
32744
+ return false;
32745
+ const snapshot = value;
32746
+ const metrics = snapshot.metrics;
32747
+ return typeof snapshot.botId === "string" && snapshot.botId.length > 0 && typeof snapshot.day === "string" && /^\d{4}-\d{2}-\d{2}$/.test(snapshot.day) && !!metrics && isMetric(metrics.input) && isMetric(metrics.output) && isMetric(metrics.cache);
32748
+ }
32749
+ function mergeMetric(existing, delta, hasExistingSnapshot) {
32750
+ if (delta === null)
32751
+ return null;
32752
+ if (!Number.isSafeInteger(delta) || delta < 0) {
32753
+ throw new RangeError("token usage delta must be a non-negative safe integer");
32754
+ }
32755
+ if (!hasExistingSnapshot)
32756
+ return delta;
32757
+ if (existing === null)
32758
+ return null;
32759
+ const sum = existing + delta;
32760
+ if (!Number.isSafeInteger(sum))
32761
+ throw new RangeError("daily token usage exceeds safe integer range");
32762
+ return sum;
32763
+ }
32764
+ function emptySnapshot(botId, day) {
32765
+ return {
32766
+ botId,
32767
+ day,
32768
+ metrics: {
32769
+ input: null,
32770
+ output: null,
32771
+ cache: null
32772
+ }
32773
+ };
32774
+ }
32775
+
32776
+ class DailyTokenUsageStore {
32777
+ now;
32778
+ tail = Promise.resolve();
32779
+ loaded = false;
32780
+ data = { version: 1, bots: {} };
32781
+ filePath;
32782
+ constructor(workingDirectoryBase, now = () => new Date) {
32783
+ this.now = now;
32784
+ this.filePath = join14(workingDirectoryBase, ".telemetry", "daily-token-usage.json");
32785
+ }
32786
+ record(botId, delta) {
32787
+ return this.enqueue(async () => {
32788
+ await this.load();
32789
+ const at = this.now();
32790
+ this.prune(at);
32791
+ const day = dayKey(at);
32792
+ const snapshots = this.data.bots[botId] ?? [];
32793
+ const existing = snapshots.find((snapshot) => snapshot.day === day);
32794
+ const next = existing ?? emptySnapshot(botId, day);
32795
+ next.metrics = {
32796
+ input: mergeMetric(next.metrics.input, delta.input, existing !== undefined),
32797
+ output: mergeMetric(next.metrics.output, delta.output, existing !== undefined),
32798
+ cache: mergeMetric(next.metrics.cache, delta.cache, existing !== undefined)
32799
+ };
32800
+ if (!existing)
32801
+ snapshots.push(next);
32802
+ snapshots.sort((a, b) => a.day.localeCompare(b.day));
32803
+ this.data.bots[botId] = snapshots;
32804
+ await this.persist();
32805
+ });
32806
+ }
32807
+ snapshots(botId) {
32808
+ let result = [];
32809
+ return this.enqueue(async () => {
32810
+ await this.load();
32811
+ if (this.prune(this.now()))
32812
+ await this.persist();
32813
+ result = (this.data.bots[botId] ?? []).map((snapshot) => structuredClone(snapshot));
32814
+ }).then(() => result);
32815
+ }
32816
+ enqueue(operation) {
32817
+ const result = this.tail.then(operation, operation);
32818
+ this.tail = result.then(() => {
32819
+ return;
32820
+ }, () => {
32821
+ return;
32822
+ });
32823
+ return result;
32824
+ }
32825
+ async load() {
32826
+ if (this.loaded)
32827
+ return;
32828
+ let source;
32829
+ try {
32830
+ source = await readFile2(this.filePath, "utf8");
32831
+ } catch (error51) {
32832
+ if (!error51 || typeof error51 !== "object" || !("code" in error51) || error51.code !== "ENOENT") {
32833
+ throw error51;
32834
+ }
32835
+ this.data = { version: 1, bots: {} };
32836
+ this.loaded = true;
32837
+ return;
32838
+ }
32839
+ const parsed = JSON.parse(source);
32840
+ if (!parsed || typeof parsed !== "object" || parsed.version !== 1) {
32841
+ throw new Error("invalid daily token usage file version");
32842
+ }
32843
+ const bots = parsed.bots;
32844
+ if (!bots || typeof bots !== "object" || Array.isArray(bots)) {
32845
+ throw new Error("invalid daily token usage bots map");
32846
+ }
32847
+ const valid = {};
32848
+ for (const [botId, value] of Object.entries(bots)) {
32849
+ if (!Array.isArray(value) || !value.every(isSnapshot) || value.some((snapshot) => snapshot.botId !== botId)) {
32850
+ throw new Error(`invalid daily token usage snapshots for bot ${botId}`);
32851
+ }
32852
+ if (value.length > 0) {
32853
+ valid[botId] = value;
32854
+ }
32855
+ }
32856
+ this.data = { version: 1, bots: valid };
32857
+ this.loaded = true;
32858
+ }
32859
+ prune(at) {
32860
+ const keep = retainedDays(at);
32861
+ let changed = false;
32862
+ for (const [botId, snapshots] of Object.entries(this.data.bots)) {
32863
+ const retained = snapshots.filter((snapshot) => keep.has(snapshot.day)).sort((a, b) => a.day.localeCompare(b.day)).slice(-7);
32864
+ if (retained.length !== snapshots.length || retained.some((snapshot, index2) => snapshot !== snapshots[index2]))
32865
+ changed = true;
32866
+ if (retained.length === 0)
32867
+ delete this.data.bots[botId];
32868
+ else
32869
+ this.data.bots[botId] = retained;
32870
+ }
32871
+ return changed;
32872
+ }
32873
+ async persist() {
32874
+ const directory = dirname5(this.filePath);
32875
+ await mkdir(directory, { recursive: true, mode: 448 });
32876
+ const temporary = `${this.filePath}.${randomUUID7()}.tmp`;
32877
+ try {
32878
+ const file2 = await open(temporary, "wx", 384);
32879
+ try {
32880
+ await file2.writeFile(JSON.stringify(this.data), { encoding: "utf8" });
32881
+ await file2.sync();
32882
+ } finally {
32883
+ await file2.close();
32884
+ }
32885
+ await rename(temporary, this.filePath);
32886
+ await chmod(this.filePath, 384);
32887
+ try {
32888
+ const directoryHandle = await open(directory, "r");
32889
+ try {
32890
+ await directoryHandle.sync();
32891
+ } finally {
32892
+ await directoryHandle.close();
32893
+ }
32894
+ } catch {}
32895
+ } catch (error51) {
32896
+ await rm(temporary, { force: true }).catch(() => {});
32897
+ throw error51;
32898
+ }
32899
+ }
32900
+ }
31335
32901
  // src/daemon/createDaemon.ts
31336
32902
  var WARMUP_BACKOFF_MS = [250, 500, 1000, 2000, 4000];
31337
32903
  var WARMUP_CEILING_MS = 30000;
@@ -31469,9 +33035,25 @@ function createBuiltinDaemonSessionFactory(onRuntimeRawLine) {
31469
33035
  }
31470
33036
  async function createDaemon(opts) {
31471
33037
  const log2 = opts.logger ?? createLogger({ header: "@alook/daemon" });
31472
- const fallbackBase = (process.env.ALOOK_PROJECT_ROOT || `${homedir4()}/.alook`) + "/daemon";
33038
+ const fallbackBase = (process.env.ALOOK_PROJECT_ROOT || `${homedir5()}/.alook`) + "/daemon";
31473
33039
  const workingDirectoryBase = opts.workingDirectoryBase ?? fallbackBase;
31474
33040
  const workdirFor = (agentId) => `${workingDirectoryBase}/${agentId}`;
33041
+ const dailyTokenUsage2 = new DailyTokenUsageStore(workingDirectoryBase);
33042
+ const providerQuotaReader = opts.providerQuotaReader ?? (opts.sessionFactory ? async () => null : readBuiltinProviderQuota);
33043
+ const providerQuotaByBackend = new Map;
33044
+ let requestReadyQuotaResend = () => {};
33045
+ const recordProviderQuota = (backendId, quota) => {
33046
+ const previous = providerQuotaByBackend.get(backendId);
33047
+ if (previous?.observation.status === "available" && quota.status === "error" && previous.observation.sourceEpoch === quota.sourceEpoch)
33048
+ return;
33049
+ providerQuotaByBackend.set(backendId, {
33050
+ agentBackendId: backendId,
33051
+ observation: structuredClone(quota)
33052
+ });
33053
+ if (previous && previous.observation.sourceEpoch !== quota.sourceEpoch) {
33054
+ requestReadyQuotaResend();
33055
+ }
33056
+ };
31475
33057
  sweepTimelineHistory(workingDirectoryBase).catch(() => {
31476
33058
  log2.warn("timeline startup sweep failed");
31477
33059
  });
@@ -31487,6 +33069,27 @@ async function createDaemon(opts) {
31487
33069
  });
31488
33070
  let channelRef = null;
31489
33071
  let managerRef = null;
33072
+ const providerQuotaSnapshots = () => [...providerQuotaByBackend.values()].map((snapshot) => structuredClone(snapshot));
33073
+ const activityPayload = async (info) => {
33074
+ if (info.state !== "idle")
33075
+ return info;
33076
+ const backendId = managerRef?.agentBackendId(info.agentId);
33077
+ if (backendId === "claude") {
33078
+ const observed = await providerQuotaReader("claude");
33079
+ if (observed)
33080
+ recordProviderQuota("claude", observed);
33081
+ }
33082
+ const quota = backendId === "claude" || backendId === "codex" ? providerQuotaByBackend.get(backendId) : undefined;
33083
+ const dailyUsage = await dailyTokenUsage2.snapshots(info.agentId);
33084
+ return {
33085
+ ...info,
33086
+ ...dailyUsage.length > 0 ? { dailyUsage } : {},
33087
+ ...quota ? { quota: structuredClone(quota) } : {}
33088
+ };
33089
+ };
33090
+ let reportAgentActivity = (info) => {
33091
+ channelRef?.reportAgentActivity?.(info);
33092
+ };
31490
33093
  let reminderSchedulerRef = null;
31491
33094
  const selfSleepScheduler = opts.onSelfSleep ? new DaemonSelfSleepScheduler({
31492
33095
  onSleep: opts.onSelfSleep,
@@ -31551,7 +33154,7 @@ async function createDaemon(opts) {
31551
33154
  function reassertAgentActivity(agentId) {
31552
33155
  const state = managerRef?.agentActivity(agentId);
31553
33156
  if (state)
31554
- channel2.reportAgentActivity?.({ agentId, state });
33157
+ reportAgentActivity({ agentId, state });
31555
33158
  }
31556
33159
  function startTypingHeartbeat(agentId) {
31557
33160
  stopTypingHeartbeat(agentId);
@@ -31695,6 +33298,20 @@ async function createDaemon(opts) {
31695
33298
  logger: log2.child("ws")
31696
33299
  });
31697
33300
  channelRef = channel2;
33301
+ const activityReportTails = new Map;
33302
+ reportAgentActivity = (info) => {
33303
+ const prior = activityReportTails.get(info.agentId) ?? Promise.resolve();
33304
+ const next = prior.then(async () => {
33305
+ await channel2.reportAgentActivity(await activityPayload(info));
33306
+ }).catch(() => {
33307
+ log2.warn("agent activity telemetry report failed", { agentId: info.agentId, state: info.state });
33308
+ });
33309
+ activityReportTails.set(info.agentId, next);
33310
+ next.finally(() => {
33311
+ if (activityReportTails.get(info.agentId) === next)
33312
+ activityReportTails.delete(info.agentId);
33313
+ });
33314
+ };
31698
33315
  function restorePendingIdleResetEvents(agentId) {
31699
33316
  for (const pending of timeline2.pendingIdleResetEvents(agentId)) {
31700
33317
  channel2.restorePendingBotAuditEvent({
@@ -31813,7 +33430,7 @@ async function createDaemon(opts) {
31813
33430
  onAgentSession: (info) => void channel2.reportAgentSession(info),
31814
33431
  onAgentActivity: (info) => {
31815
33432
  selfSleepScheduler?.observeAgentActivity(info.agentId, info.state === "running");
31816
- channel2.reportAgentActivity?.(info);
33433
+ reportAgentActivity(info);
31817
33434
  if (info.state === "starting" || info.state === "running") {
31818
33435
  if (!typingHeartbeats.has(info.agentId)) {
31819
33436
  startTypingHeartbeat(info.agentId);
@@ -31822,6 +33439,16 @@ async function createDaemon(opts) {
31822
33439
  emitTypingStopsAndClear(info.agentId);
31823
33440
  }
31824
33441
  },
33442
+ onTokenUsage: ({ agentId, usage }) => {
33443
+ dailyTokenUsage2.record(agentId, usage).catch(() => {
33444
+ log2.warn("daily token usage persistence failed", { agentId });
33445
+ });
33446
+ },
33447
+ onProviderQuota: ({ backendId, quota }) => {
33448
+ if (backendId !== "claude" && backendId !== "codex")
33449
+ return;
33450
+ recordProviderQuota(backendId, quota);
33451
+ },
31825
33452
  onBotAuditEvent: (agentId, event, context) => emitBotAuditEvent(agentId, event, context),
31826
33453
  onAgentLocallyStopped: (info) => router?.markLocallyStopped(info.agentId),
31827
33454
  onRuntimeRawLine,
@@ -31868,6 +33495,11 @@ async function createDaemon(opts) {
31868
33495
  arch: opts.arch,
31869
33496
  osRelease: opts.osRelease,
31870
33497
  daemonVersion: opts.daemonVersion,
33498
+ providerQuotas: providerQuotaSnapshots,
33499
+ resyncActivities: async () => {
33500
+ const activities = await Promise.all(manager.liveAgentActivities().map((info) => activityPayload(info)));
33501
+ return activities.filter((activity) => manager.agentActivity(activity.agentId) === activity.state);
33502
+ },
31871
33503
  typingTracker,
31872
33504
  logger: log2.child("router"),
31873
33505
  onBeforeAgent: async (agentId) => {
@@ -31883,6 +33515,10 @@ async function createDaemon(opts) {
31883
33515
  await enrollAgent(agentId);
31884
33516
  }
31885
33517
  });
33518
+ requestReadyQuotaResend = () => {
33519
+ if (router)
33520
+ channel2.sendReady?.(router.buildReady());
33521
+ };
31886
33522
  channel2.onCommand(createSelfUpdateCommandListener(opts.handleSelfUpdate));
31887
33523
  channel2.onCommand(createDiagnosticsCommandListener({
31888
33524
  handleDiagnosticCommand: opts.handleDiagnosticCommand,
@@ -31912,6 +33548,11 @@ async function createDaemon(opts) {
31912
33548
  resyncPendingWakes();
31913
33549
  resyncPendingDiagnostics();
31914
33550
  });
33551
+ if (opts.runtimeReport.some((runtime) => runtime.id === "claude")) {
33552
+ const observed = await providerQuotaReader("claude");
33553
+ if (observed)
33554
+ recordProviderQuota("claude", observed);
33555
+ }
31915
33556
  channel2.connect();
31916
33557
  await router.start();
31917
33558
  selfSleepScheduler?.start();