@alook/daemon 0.1.24 → 0.1.25

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 +1567 -194
  2. package/dist/index.js +1525 -204
  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();
@@ -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
  }
@@ -1502,49 +1499,153 @@ class ClaudeDriver {
1502
1499
  }
1503
1500
 
1504
1501
  // agent-driver/dist/adapters/codex/telemetry.js
1505
- function mapCodexTelemetry(method, params) {
1502
+ function metric(value) {
1503
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
1504
+ }
1505
+ function nonCachedInput(input, cached) {
1506
+ if (typeof input !== "number" || !Number.isSafeInteger(input) || input < 0 || typeof cached !== "number" || !Number.isSafeInteger(cached) || cached < 0 || cached > input)
1507
+ return null;
1508
+ return input - cached;
1509
+ }
1510
+ function canonicalId(value, fallback) {
1511
+ return typeof value === "string" && value.length > 0 && new TextEncoder().encode(value).length <= 64 ? value : fallback;
1512
+ }
1513
+ function mappedPlanName(value) {
1514
+ switch (value) {
1515
+ case "free":
1516
+ return "Free";
1517
+ case "plus":
1518
+ return "Plus";
1519
+ case "pro":
1520
+ return "Pro";
1521
+ case "team":
1522
+ return "Team";
1523
+ case "business":
1524
+ return "Business";
1525
+ case "enterprise":
1526
+ return "Enterprise";
1527
+ case "edu":
1528
+ return "Education";
1529
+ default:
1530
+ return;
1531
+ }
1532
+ }
1533
+ function quotaWindow(minutes, slot) {
1534
+ if (typeof minutes !== "number" || !Number.isSafeInteger(minutes) || minutes <= 0)
1535
+ return null;
1536
+ if (minutes === 1440)
1537
+ return { kind: "calendar", period: "day", displayName: "Daily usage limit" };
1538
+ if (minutes === 10080)
1539
+ return { kind: "calendar", period: "week", displayName: "Weekly usage limit" };
1540
+ if (minutes === 43200)
1541
+ return { kind: "calendar", period: "month", displayName: "Monthly usage limit" };
1542
+ return {
1543
+ kind: "rolling",
1544
+ durationSeconds: minutes * 60,
1545
+ displayName: slot === "primary" && minutes === 300 ? "5 hour usage limit" : `${minutes} minute usage limit`
1546
+ };
1547
+ }
1548
+ function resetIso(value) {
1549
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
1550
+ const date = new Date(value < 10000000000 ? value * 1000 : value);
1551
+ return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
1552
+ }
1553
+ if (typeof value === "string") {
1554
+ const date = new Date(value);
1555
+ return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
1556
+ }
1557
+ return;
1558
+ }
1559
+ function mapCodexQuotaSnapshots(snapshots, sourceEpoch) {
1560
+ const limits = [];
1561
+ let planName;
1562
+ for (const snapshot of snapshots) {
1563
+ const limitId = canonicalId(snapshot?.limitId ?? snapshot?.limit_id, "codex");
1564
+ const spark = /spark/i.test(limitId) || /spark/i.test(String(snapshot?.limitName ?? snapshot?.limit_name ?? ""));
1565
+ const product = spark ? { kind: "reported", id: "codex-spark", displayName: "Spark" } : { kind: "reported", id: "codex", displayName: "Codex" };
1566
+ const model = spark ? { kind: "reported", id: "gpt-5.3-codex-spark" } : { kind: "not_applicable" };
1567
+ planName ??= mappedPlanName(snapshot?.planType ?? snapshot?.plan_type);
1568
+ for (const slot of ["primary", "secondary"]) {
1569
+ const value = snapshot?.[slot];
1570
+ const window2 = quotaWindow(value?.windowDurationMins ?? value?.window_duration_mins, slot);
1571
+ const usedPercent = value?.usedPercent ?? value?.used_percent;
1572
+ if (!window2 || typeof usedPercent !== "number" || !Number.isFinite(usedPercent) || usedPercent < 0 || usedPercent > 100)
1573
+ continue;
1574
+ const resetsAt = resetIso(value?.resetsAt ?? value?.resets_at);
1575
+ limits.push({
1576
+ bucket: { limitId, product, model, window: window2 },
1577
+ usedPercent,
1578
+ ...resetsAt ? { resetsAt } : {}
1579
+ });
1580
+ }
1581
+ }
1582
+ if (limits.length === 0) {
1583
+ return {
1584
+ kind: "telemetry",
1585
+ name: "rate_limits",
1586
+ source: "codex_account_rate_limits_updated",
1587
+ quota: { status: "error", sourceEpoch, code: "invalid_response", retryable: true }
1588
+ };
1589
+ }
1590
+ return {
1591
+ kind: "telemetry",
1592
+ name: "rate_limits",
1593
+ source: "codex_account_rate_limits_updated",
1594
+ quota: {
1595
+ status: "available",
1596
+ sourceEpoch,
1597
+ ...planName ? { planName } : {},
1598
+ freshForSeconds: 300,
1599
+ limits
1600
+ }
1601
+ };
1602
+ }
1603
+ function mapCodexTelemetry(method, params, sourceEpoch) {
1506
1604
  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
- }
1605
+ const u = params?.tokenUsage?.last ?? params?.token_usage?.last;
1606
+ if (!u)
1607
+ return [];
1608
+ const input = u.inputTokens ?? u.input_tokens;
1609
+ const cached = u.cachedInputTokens ?? u.cached_input_tokens;
1610
+ return [{
1611
+ kind: "telemetry",
1612
+ name: "token_usage",
1613
+ source: "codex_thread_token_usage_updated",
1614
+ usage: {
1615
+ input: nonCachedInput(input, cached),
1616
+ output: metric(u.outputTokens ?? u.output_tokens),
1617
+ cache: metric(cached)
1524
1618
  }
1525
- ];
1619
+ }];
1526
1620
  }
1527
1621
  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
- ];
1622
+ return [mapCodexQuotaSnapshots([params?.rateLimits ?? params ?? {}], sourceEpoch)];
1543
1623
  }
1544
1624
  return [];
1545
1625
  }
1546
1626
 
1547
1627
  // agent-driver/dist/adapters/codex/normalizer.js
1628
+ import { randomBytes } from "node:crypto";
1629
+ var codexQuotaSourceEpoch = randomBytes(16).toString("base64url");
1630
+ var codexQuotaSourceGeneration = 0;
1631
+ var codexAccountFingerprint = null;
1632
+ function rotateCodexQuotaSource() {
1633
+ codexQuotaSourceEpoch = randomBytes(16).toString("base64url");
1634
+ codexQuotaSourceGeneration += 1;
1635
+ codexAccountFingerprint = null;
1636
+ }
1637
+ function observeCodexAccount(result) {
1638
+ const account = result?.account;
1639
+ const fingerprint = account && typeof account === "object" ? JSON.stringify([
1640
+ account.type ?? "unknown",
1641
+ account.email ?? null,
1642
+ account.planType ?? account.plan_type ?? null
1643
+ ]) : "none";
1644
+ if (codexAccountFingerprint !== null && codexAccountFingerprint !== fingerprint) {
1645
+ rotateCodexQuotaSource();
1646
+ }
1647
+ codexAccountFingerprint = fingerprint;
1648
+ }
1548
1649
  function normalizeFileChangeInput(item) {
1549
1650
  const paths = [];
1550
1651
  const seen = new Set;
@@ -1567,6 +1668,12 @@ function normalizeFileChangeInput(item) {
1567
1668
  }
1568
1669
 
1569
1670
  class CodexEventNormalizer {
1671
+ quotaReadRequestIds = new Set;
1672
+ accountReadRequestIds = new Set;
1673
+ rateLimitSnapshots = new Map;
1674
+ quotaSnapshotInitialized = false;
1675
+ quotaSourceGeneration = codexQuotaSourceGeneration;
1676
+ pendingTurnUsage = null;
1570
1677
  threadId = null;
1571
1678
  turnId = null;
1572
1679
  terminalTurn = null;
@@ -1577,10 +1684,69 @@ class CodexEventNormalizer {
1577
1684
  get currentTurnId() {
1578
1685
  return this.turnId;
1579
1686
  }
1687
+ registerQuotaReadRequest(requestId) {
1688
+ this.quotaReadRequestIds.add(requestId);
1689
+ }
1690
+ registerAccountReadRequest(requestId) {
1691
+ this.accountReadRequestIds.add(requestId);
1692
+ }
1693
+ syncQuotaSourceGeneration() {
1694
+ if (this.quotaSourceGeneration === codexQuotaSourceGeneration)
1695
+ return;
1696
+ this.quotaSourceGeneration = codexQuotaSourceGeneration;
1697
+ this.rateLimitSnapshots.clear();
1698
+ this.quotaSnapshotInitialized = false;
1699
+ }
1700
+ quotaSnapshots(value) {
1701
+ const byLimitId = value?.rateLimitsByLimitId ?? value?.rate_limits_by_limit_id;
1702
+ if (byLimitId && typeof byLimitId === "object" && !Array.isArray(byLimitId)) {
1703
+ return Object.entries(byLimitId).flatMap(([key2, snapshot2]) => {
1704
+ if (!snapshot2 || typeof snapshot2 !== "object" || Array.isArray(snapshot2))
1705
+ return [];
1706
+ return [[key2, {
1707
+ ...snapshot2,
1708
+ limitId: snapshot2.limitId ?? snapshot2.limit_id ?? key2
1709
+ }]];
1710
+ });
1711
+ }
1712
+ const snapshot = value?.rateLimits ?? value?.rate_limits ?? value;
1713
+ if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot))
1714
+ return [];
1715
+ const record = snapshot;
1716
+ const key = typeof record.limitId === "string" ? record.limitId : typeof record.limit_id === "string" ? record.limit_id : "codex";
1717
+ return [[key, { ...record, limitId: key }]];
1718
+ }
1719
+ replaceQuotaSnapshots(value) {
1720
+ this.syncQuotaSourceGeneration();
1721
+ this.rateLimitSnapshots.clear();
1722
+ for (const [key, snapshot] of this.quotaSnapshots(value)) {
1723
+ this.rateLimitSnapshots.set(key, snapshot);
1724
+ }
1725
+ this.quotaSnapshotInitialized = true;
1726
+ return [mapCodexQuotaSnapshots([...this.rateLimitSnapshots.values()], codexQuotaSourceEpoch)];
1727
+ }
1728
+ mergeQuotaSnapshots(value) {
1729
+ this.syncQuotaSourceGeneration();
1730
+ for (const [key, update] of this.quotaSnapshots(value)) {
1731
+ const merged = {
1732
+ ...this.rateLimitSnapshots.get(key) ?? {},
1733
+ limitId: key
1734
+ };
1735
+ for (const [field, fieldValue] of Object.entries(update)) {
1736
+ if (fieldValue !== undefined && fieldValue !== null)
1737
+ merged[field] = fieldValue;
1738
+ }
1739
+ this.rateLimitSnapshots.set(key, merged);
1740
+ }
1741
+ if (!this.quotaSnapshotInitialized)
1742
+ return [];
1743
+ return [mapCodexQuotaSnapshots([...this.rateLimitSnapshots.values()], codexQuotaSourceEpoch)];
1744
+ }
1580
1745
  adoptThreadId(threadId) {
1581
1746
  if (threadId !== this.threadId) {
1582
1747
  this.turnId = null;
1583
1748
  this.terminalTurn = null;
1749
+ this.pendingTurnUsage = null;
1584
1750
  }
1585
1751
  this.threadId = threadId;
1586
1752
  }
@@ -1598,6 +1764,25 @@ class CodexEventNormalizer {
1598
1764
  const msg = tryParseJsonLine(line);
1599
1765
  if (!msg)
1600
1766
  return [];
1767
+ if (msg?.id !== undefined && this.accountReadRequestIds.delete(msg.id)) {
1768
+ if (!msg.error) {
1769
+ observeCodexAccount(msg.result);
1770
+ this.syncQuotaSourceGeneration();
1771
+ }
1772
+ return [];
1773
+ }
1774
+ if (msg?.id !== undefined && this.quotaReadRequestIds.delete(msg.id)) {
1775
+ this.syncQuotaSourceGeneration();
1776
+ if (msg.error) {
1777
+ return [{
1778
+ kind: "telemetry",
1779
+ name: "rate_limits",
1780
+ source: "codex_account_rate_limits_read",
1781
+ quota: { status: "error", sourceEpoch: codexQuotaSourceEpoch, code: "provider_error", retryable: true }
1782
+ }];
1783
+ }
1784
+ return this.replaceQuotaSnapshots(msg.result ?? {});
1785
+ }
1601
1786
  if (msg?.error && msg.id !== undefined) {
1602
1787
  return [{ kind: "error", message: msg.error?.message ?? "Codex RPC error" }];
1603
1788
  }
@@ -1622,6 +1807,7 @@ class CodexEventNormalizer {
1622
1807
  return [];
1623
1808
  this.turnId = params.turn.id;
1624
1809
  this.terminalTurn = null;
1810
+ this.pendingTurnUsage = null;
1625
1811
  return [
1626
1812
  {
1627
1813
  kind: "turn_owner",
@@ -1650,24 +1836,36 @@ class CodexEventNormalizer {
1650
1836
  case "turn/completed":
1651
1837
  if (!this.acceptRootTerminal(params))
1652
1838
  return [];
1839
+ const usage = this.pendingTurnUsage;
1840
+ this.pendingTurnUsage = null;
1653
1841
  if (params.turn.status === "failed") {
1654
1842
  return [
1843
+ ...usage ? [usage] : [],
1655
1844
  { kind: "error", message: "Codex turn failed" },
1656
1845
  { kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }
1657
1846
  ];
1658
1847
  }
1659
1848
  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) }];
1849
+ return [...usage ? [usage] : [], { kind: "error", message: "Codex turn interrupted" }, { kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
1661
1850
  }
1662
- return [{ kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
1851
+ return [...usage ? [usage] : [], { kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
1663
1852
  case "error":
1664
1853
  if (params?.willRetry === true) {
1665
1854
  return [{ kind: "runtime_recovery", stage: "retrying", source: "codex_stream" }];
1666
1855
  }
1667
1856
  return [{ kind: "error", message: params?.error?.message ?? params?.message ?? "Codex error" }];
1668
- case "thread/tokenUsage/updated":
1857
+ case "thread/tokenUsage/updated": {
1858
+ const usage2 = mapCodexTelemetry(method, params, codexQuotaSourceEpoch)[0];
1859
+ if (usage2)
1860
+ this.pendingTurnUsage = usage2;
1861
+ return [];
1862
+ }
1669
1863
  case "account/rateLimits/updated":
1670
- return mapCodexTelemetry(method, params);
1864
+ return this.mergeQuotaSnapshots(params);
1865
+ case "account/updated":
1866
+ rotateCodexQuotaSource();
1867
+ this.syncQuotaSourceGeneration();
1868
+ return [];
1671
1869
  default:
1672
1870
  return [];
1673
1871
  }
@@ -1780,7 +1978,53 @@ function resolveCodexHomeRootFromEnv(env = process.env, opts = {}) {
1780
1978
  return path5.join(opts.defaultHomeDir ?? os.homedir(), ".codex");
1781
1979
  }
1782
1980
 
1981
+ // agent-driver/dist/internal/errors.js
1982
+ var MAX_PUBLIC_ERROR_MESSAGE = 1000;
1983
+ 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}`;
1984
+ var QUOTED_CREDENTIAL_ASSIGNMENT = new RegExp(String.raw`(["'])(${CREDENTIAL_NAME})\1\s*[:=]\s*(["'])[^"'\r\n]*\3`, "gi");
1985
+ var CREDENTIAL_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9_-])((?:${CREDENTIAL_NAME})\s*[:=]\s*)(?!\[redacted\])[^\s,;}\]]+`, "gi");
1986
+ function scrubDriverErrorMessage(value, fallback = "Runtime operation failed") {
1987
+ const text = value instanceof Error ? value.message : String(value ?? "");
1988
+ 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();
1989
+ return (scrubbed || fallback).slice(0, MAX_PUBLIC_ERROR_MESSAGE);
1990
+ }
1991
+ function scrubDriverError(error) {
1992
+ return {
1993
+ ...error,
1994
+ code: stableErrorCode(error.code, "runtime_error"),
1995
+ message: scrubDriverErrorMessage(error.message),
1996
+ ...error.details ? { details: scrubDetails(error.details) } : {}
1997
+ };
1998
+ }
1999
+ function scrubDetails(details) {
2000
+ const scrubValue = (value, key) => {
2001
+ if (key && /api[_-]?key|access[_-]?token|auth(?:orization)?|password|passwd|secret|token/i.test(key)) {
2002
+ return "[redacted]";
2003
+ }
2004
+ if (typeof value === "string")
2005
+ return scrubDriverErrorMessage(value, "[redacted]");
2006
+ if (Array.isArray(value))
2007
+ return value.map((item) => scrubValue(item));
2008
+ if (value && typeof value === "object") {
2009
+ return Object.fromEntries(Object.entries(value).map(([childKey, child]) => [
2010
+ childKey,
2011
+ scrubValue(child, childKey)
2012
+ ]));
2013
+ }
2014
+ return value;
2015
+ };
2016
+ return scrubValue(details);
2017
+ }
2018
+ function stableErrorCode(value, fallback) {
2019
+ const code = String(value ?? "");
2020
+ return /^[A-Za-z0-9_.-]{1,100}$/.test(code) ? code : fallback;
2021
+ }
2022
+
1783
2023
  // agent-driver/dist/adapters/codex/index.js
2024
+ var SETTINGS_UPDATE_TIMEOUT_MS = 5000;
2025
+ var MODEL_LIST_TIMEOUT_MS = 5000;
2026
+ var MODEL_LIST_MAX = 64;
2027
+ var MODEL_EFFORT_MAX = 16;
1784
2028
  function isCodexMissingRolloutError(message) {
1785
2029
  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
2030
  }
@@ -1801,19 +2045,157 @@ class CodexDriver {
1801
2045
  }
1802
2046
  };
1803
2047
  eventNormalizer = new CodexEventNormalizer;
2048
+ pendingAccountReadRequestIds = new Set;
1804
2049
  requestId = 0;
1805
2050
  codexHomeRoot = null;
1806
2051
  proc = null;
1807
2052
  pendingInitialPrompt = null;
1808
2053
  pendingResumeFallbackParams = null;
2054
+ pendingSettingsUpdates = new Map;
1809
2055
  nextRequestId() {
1810
2056
  return ++this.requestId;
1811
2057
  }
2058
+ requestAccountQuotaSnapshot() {
2059
+ if (!this.proc?.stdin || this.proc.stdin.destroyed)
2060
+ return;
2061
+ const accountReadRequestId = this.nextRequestId();
2062
+ this.pendingAccountReadRequestIds.add(accountReadRequestId);
2063
+ this.eventNormalizer.registerAccountReadRequest(accountReadRequestId);
2064
+ this.proc.stdin.write(jsonRpcRequest("account/read", { refreshToken: false }, accountReadRequestId) + `
2065
+ `);
2066
+ }
2067
+ requestQuotaSnapshot() {
2068
+ if (!this.proc?.stdin || this.proc.stdin.destroyed)
2069
+ return;
2070
+ const quotaReadRequestId = this.nextRequestId();
2071
+ this.eventNormalizer.registerQuotaReadRequest(quotaReadRequestId);
2072
+ this.proc.stdin.write(jsonRpcRequest("account/rateLimits/read", {}, quotaReadRequestId) + `
2073
+ `);
2074
+ }
1812
2075
  get codexHome() {
1813
2076
  return this.codexHomeRoot;
1814
2077
  }
1815
- probe(command) {
1816
- return probeCliRuntime("codex", {}, command);
2078
+ async probe(command) {
2079
+ const result = await probeCliRuntime("codex", {}, command);
2080
+ if (result.status !== "healthy")
2081
+ return result;
2082
+ return {
2083
+ ...result,
2084
+ reasoning: await this.probeReasoningCatalog(command)
2085
+ };
2086
+ }
2087
+ async probeReasoningCatalog(command) {
2088
+ const spec = resolveSpawnSpec("codex", ["app-server", "--listen", "stdio://"], command);
2089
+ let proc;
2090
+ try {
2091
+ proc = spawnAgentProcess(spec.command, spec.args, {
2092
+ cwd: process.cwd(),
2093
+ env: { ...process.env, CI: "1" },
2094
+ shell: spec.shell
2095
+ });
2096
+ } catch {
2097
+ return;
2098
+ }
2099
+ return new Promise((resolve2) => {
2100
+ let settled = false;
2101
+ let buffer = "";
2102
+ let nextId = 0;
2103
+ let initializeId = 0;
2104
+ let listId = 0;
2105
+ const models = [];
2106
+ const seenModels = new Set;
2107
+ let defaultModelId;
2108
+ const finish = (catalog) => {
2109
+ if (settled)
2110
+ return;
2111
+ settled = true;
2112
+ clearTimeout(timer);
2113
+ const done = proc.pid ? killProcessTree(proc.pid, { graceMs: 250 }).catch(() => {}) : Promise.resolve().then(() => {
2114
+ proc.kill("SIGTERM");
2115
+ });
2116
+ done.finally(() => resolve2(catalog));
2117
+ };
2118
+ const requestModelPage = (cursor) => {
2119
+ listId = ++nextId;
2120
+ proc.stdin?.write(jsonRpcRequest("model/list", { limit: Math.min(MODEL_LIST_MAX - models.length, MODEL_LIST_MAX), includeHidden: false, ...cursor ? { cursor } : {} }, listId) + `
2121
+ `);
2122
+ };
2123
+ const consumeModel = (value) => {
2124
+ if (!value || typeof value !== "object" || models.length >= MODEL_LIST_MAX)
2125
+ return;
2126
+ const model = value;
2127
+ const id = typeof model.id === "string" ? model.id.trim() : "";
2128
+ if (!id || id.length > 100 || seenModels.has(id))
2129
+ return;
2130
+ const rawOptions = Array.isArray(model.supportedReasoningEfforts) ? model.supportedReasoningEfforts : [];
2131
+ const seenEfforts = new Set;
2132
+ const supportedReasoningEfforts = rawOptions.flatMap((raw) => {
2133
+ if (!raw || typeof raw !== "object")
2134
+ return [];
2135
+ const option = raw;
2136
+ const value2 = typeof option.reasoningEffort === "string" ? option.reasoningEffort.trim() : "";
2137
+ if (!value2 || value2.length > 32 || !/^[A-Za-z0-9._-]+$/.test(value2) || seenEfforts.has(value2))
2138
+ return [];
2139
+ seenEfforts.add(value2);
2140
+ const description = typeof option.description === "string" ? option.description.slice(0, 256) : undefined;
2141
+ return [{ value: value2, ...description ? { description } : {} }];
2142
+ }).slice(0, MODEL_EFFORT_MAX);
2143
+ const candidateDefault = typeof model.defaultReasoningEffort === "string" ? model.defaultReasoningEffort : undefined;
2144
+ seenModels.add(id);
2145
+ if (model.isDefault === true)
2146
+ defaultModelId = id;
2147
+ models.push({
2148
+ id,
2149
+ supportedReasoningEfforts,
2150
+ ...candidateDefault && supportedReasoningEfforts.some((item) => item.value === candidateDefault) ? { defaultReasoningEffort: candidateDefault } : {}
2151
+ });
2152
+ };
2153
+ const onLine = (line) => {
2154
+ let message;
2155
+ try {
2156
+ message = JSON.parse(line);
2157
+ } catch {
2158
+ return;
2159
+ }
2160
+ if (message.id === initializeId) {
2161
+ if (message.error)
2162
+ return finish();
2163
+ requestModelPage();
2164
+ return;
2165
+ }
2166
+ if (message.id !== listId)
2167
+ return;
2168
+ if (message.error || !message.result || typeof message.result !== "object")
2169
+ return finish();
2170
+ const result = message.result;
2171
+ for (const model of Array.isArray(result.data) ? result.data : [])
2172
+ consumeModel(model);
2173
+ const cursor = typeof result.nextCursor === "string" ? result.nextCursor : undefined;
2174
+ if (cursor && models.length < MODEL_LIST_MAX)
2175
+ return requestModelPage(cursor);
2176
+ finish({
2177
+ updateMode: "live_next_turn",
2178
+ ...defaultModelId ? { defaultModelId } : {},
2179
+ models
2180
+ });
2181
+ };
2182
+ const timer = setTimeout(() => finish(), MODEL_LIST_TIMEOUT_MS);
2183
+ timer.unref?.();
2184
+ proc.stdout?.on("data", (chunk) => {
2185
+ buffer += chunk.toString();
2186
+ const lines = buffer.split(`
2187
+ `);
2188
+ buffer = lines.pop() ?? "";
2189
+ for (const line of lines)
2190
+ if (line.trim())
2191
+ onLine(line);
2192
+ });
2193
+ proc.on("error", () => finish());
2194
+ proc.on("exit", () => finish());
2195
+ initializeId = ++nextId;
2196
+ proc.stdin?.write(jsonRpcRequest("initialize", { clientInfo: { name: "alook-agent-driver-probe", version: "0.1.24" }, capabilities: { experimentalApi: true } }, initializeId) + `
2197
+ `);
2198
+ });
1817
2199
  }
1818
2200
  async openLane(ctx, options) {
1819
2201
  return createProcessLane(this, ctx, { onRawStdoutLine: options?.onRawStdoutLine });
@@ -1829,6 +2211,9 @@ class CodexDriver {
1829
2211
  shell: spec.shell
1830
2212
  });
1831
2213
  this.proc = proc;
2214
+ proc.once("exit", () => {
2215
+ this.failPendingSettingsUpdates("settings_process_exited", "Codex exited before acknowledging the settings update");
2216
+ });
1832
2217
  const initialPrompt = ctx.prompt?.trim() ? ctx.prompt : null;
1833
2218
  this.pendingInitialPrompt = initialPrompt;
1834
2219
  queueMicrotask(() => {
@@ -1857,11 +2242,21 @@ class CodexDriver {
1857
2242
  proc.stdin?.write(jsonRpcRequest("thread/start", freshParams, this.nextRequestId()) + `
1858
2243
  `);
1859
2244
  }
2245
+ this.requestAccountQuotaSnapshot();
1860
2246
  });
1861
2247
  return { process: proc };
1862
2248
  }
1863
2249
  normalizeLine(line) {
2250
+ const settingsResponse = this.consumeSettingsUpdateResponse(line);
2251
+ if (settingsResponse)
2252
+ return [];
2253
+ const parsed = tryParseJsonLine(line);
1864
2254
  const events = this.eventNormalizer.normalizeLine(line);
2255
+ if (typeof parsed?.id === "number" && this.pendingAccountReadRequestIds.delete(parsed.id)) {
2256
+ this.requestQuotaSnapshot();
2257
+ }
2258
+ if (parsed?.method === "account/updated")
2259
+ this.requestAccountQuotaSnapshot();
1865
2260
  if (this.pendingResumeFallbackParams && this.proc?.stdin && !this.proc.stdin.destroyed) {
1866
2261
  const rolloutErr = events.find((e) => e.kind === "error" && isCodexMissingRolloutError(e.message));
1867
2262
  if (rolloutErr) {
@@ -1885,6 +2280,90 @@ class CodexDriver {
1885
2280
  }
1886
2281
  return events;
1887
2282
  }
2283
+ updateSettings(input) {
2284
+ const threadId = this.eventNormalizer.currentSessionId;
2285
+ const stdin = this.proc?.stdin;
2286
+ if (!threadId || !stdin || stdin.destroyed || stdin.writableEnded || stdin.writable === false) {
2287
+ return Promise.resolve({
2288
+ status: "failed",
2289
+ error: this.settingsError("process", "settings_thread_unavailable", "Codex thread is not available for a settings update", true)
2290
+ });
2291
+ }
2292
+ const id = this.nextRequestId();
2293
+ return new Promise((resolve2) => {
2294
+ const timer = setTimeout(() => {
2295
+ if (!this.pendingSettingsUpdates.delete(id))
2296
+ return;
2297
+ resolve2({
2298
+ status: "failed",
2299
+ error: this.settingsError("timeout", "settings_update_timeout", "Codex did not acknowledge the settings update before the deadline", true)
2300
+ });
2301
+ }, SETTINGS_UPDATE_TIMEOUT_MS);
2302
+ timer.unref?.();
2303
+ this.pendingSettingsUpdates.set(id, { resolve: resolve2, timer });
2304
+ try {
2305
+ stdin.write(jsonRpcRequest("thread/settings/update", { threadId, effort: input.reasoningEffort }, id) + `
2306
+ `);
2307
+ } catch (error) {
2308
+ clearTimeout(timer);
2309
+ this.pendingSettingsUpdates.delete(id);
2310
+ resolve2({
2311
+ status: "failed",
2312
+ error: this.settingsError("process", "settings_update_write_failed", String(error), true)
2313
+ });
2314
+ }
2315
+ });
2316
+ }
2317
+ consumeSettingsUpdateResponse(line) {
2318
+ let value;
2319
+ try {
2320
+ value = JSON.parse(line);
2321
+ } catch {
2322
+ return false;
2323
+ }
2324
+ if (!value || typeof value !== "object")
2325
+ return false;
2326
+ const record = value;
2327
+ if (typeof record.id !== "number")
2328
+ return false;
2329
+ const pending = this.pendingSettingsUpdates.get(record.id);
2330
+ if (!pending)
2331
+ return false;
2332
+ clearTimeout(pending.timer);
2333
+ this.pendingSettingsUpdates.delete(record.id);
2334
+ const error = record.error;
2335
+ if (!error || typeof error !== "object") {
2336
+ pending.resolve({ status: "applied" });
2337
+ return true;
2338
+ }
2339
+ const rpcError = error;
2340
+ const message = typeof rpcError.message === "string" ? rpcError.message : "Codex rejected the settings update";
2341
+ if (rpcError.code === -32601 || /method\s+not\s+found/i.test(message)) {
2342
+ pending.resolve({
2343
+ status: "unsupported",
2344
+ error: this.settingsError("protocol", "settings_update_unsupported", "Codex does not support live reasoning settings updates", false)
2345
+ });
2346
+ } else {
2347
+ pending.resolve({
2348
+ status: "failed",
2349
+ error: this.settingsError("protocol", "settings_update_rejected", message, true)
2350
+ });
2351
+ }
2352
+ return true;
2353
+ }
2354
+ settingsError(category, code, message, retryable) {
2355
+ return { category, code, message: scrubDriverErrorMessage(message), retryable };
2356
+ }
2357
+ failPendingSettingsUpdates(code, message) {
2358
+ for (const [id, pending] of this.pendingSettingsUpdates) {
2359
+ clearTimeout(pending.timer);
2360
+ this.pendingSettingsUpdates.delete(id);
2361
+ pending.resolve({
2362
+ status: "failed",
2363
+ error: this.settingsError("process", code, message, true)
2364
+ });
2365
+ }
2366
+ }
1888
2367
  get currentSessionId() {
1889
2368
  return this.eventNormalizer.currentSessionId;
1890
2369
  }
@@ -2203,15 +2682,6 @@ class CursorAcpLane {
2203
2682
  }
2204
2683
  this.activePrompt = null;
2205
2684
  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
2685
  this.events.emit("runtime_event", {
2216
2686
  kind: "turn_end",
2217
2687
  sessionId: this.sessionId ?? undefined,
@@ -2561,10 +3031,10 @@ class CursorDriver {
2561
3031
  }
2562
3032
 
2563
3033
  // agent-driver/dist/adapters/opencode/index.js
2564
- import { randomBytes as randomBytes2 } from "node:crypto";
3034
+ import { randomBytes as randomBytes3 } from "node:crypto";
2565
3035
 
2566
3036
  // agent-driver/dist/adapters/opencode/service-lane.js
2567
- import { randomBytes } from "node:crypto";
3037
+ import { randomBytes as randomBytes2 } from "node:crypto";
2568
3038
  import { EventEmitter as EventEmitter3 } from "node:events";
2569
3039
  import { createServer as createServer2 } from "node:net";
2570
3040
  var SUPPORTED_VERSION = "1.17.20";
@@ -2703,7 +3173,7 @@ class OpenCodeServiceLane {
2703
3173
  this.ctx = ctx;
2704
3174
  this.options = options;
2705
3175
  this.fetchFn = options.fetch ?? fetch;
2706
- this.password = options.password ?? randomBytes(32).toString("base64url");
3176
+ this.password = options.password ?? randomBytes2(32).toString("base64url");
2707
3177
  }
2708
3178
  get currentSessionId() {
2709
3179
  return this.sessionId;
@@ -3317,12 +3787,20 @@ class OpenCodeServiceLane {
3317
3787
  });
3318
3788
  }
3319
3789
  const tokens = record2(data.tokens);
3320
- if (tokens) {
3790
+ if (tokens && data.finish !== "tool-calls") {
3791
+ const cache = record2(tokens.cache);
3792
+ const metric2 = (value2) => typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0 ? value2 : null;
3793
+ const cacheParts = [cache?.read, cache?.write].filter((value2) => typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0);
3794
+ const cacheTotal = cacheParts.reduce((sum, value2) => sum + value2, 0);
3321
3795
  this.events.emit("runtime_event", {
3322
3796
  kind: "telemetry",
3323
3797
  name: "token_usage",
3324
3798
  source: "opencode.v2",
3325
- attrs: tokens
3799
+ usage: {
3800
+ input: metric2(tokens.input),
3801
+ output: metric2(tokens.output),
3802
+ cache: cacheParts.length > 0 && Number.isSafeInteger(cacheTotal) ? cacheTotal : null
3803
+ }
3326
3804
  });
3327
3805
  }
3328
3806
  break;
@@ -3628,7 +4106,7 @@ class OpenCodeServiceLane {
3628
4106
  return headers;
3629
4107
  }
3630
4108
  newMessageId() {
3631
- return `msg_${randomBytes(16).toString("hex")}`;
4109
+ return `msg_${randomBytes2(16).toString("hex")}`;
3632
4110
  }
3633
4111
  diagnostic(severity, message) {
3634
4112
  this.events.emit("runtime_event", {
@@ -3693,7 +4171,7 @@ class OpenCodeServiceLane {
3693
4171
 
3694
4172
  // agent-driver/dist/adapters/opencode/index.js
3695
4173
  function createOpenCodeMessageId() {
3696
- return `msg_${randomBytes2(16).toString("hex")}`;
4174
+ return `msg_${randomBytes3(16).toString("hex")}`;
3697
4175
  }
3698
4176
 
3699
4177
  class OpenCodeDriver {
@@ -4471,48 +4949,6 @@ function assertInstructionFileName(name) {
4471
4949
  }
4472
4950
  }
4473
4951
 
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
4952
  // agent-driver/dist/controller/logical-session.js
4517
4953
  import { mkdirSync as mkdirSync3 } from "node:fs";
4518
4954
  var SEMANTIC_ASSEMBLER_MAX_BYTES = 1048576;
@@ -4630,6 +5066,8 @@ class LogicalAgentSession {
4630
5066
  toolBoundaryFlushDisabled = false;
4631
5067
  safeBoundaryFlush;
4632
5068
  safeBoundaryDelivery;
5069
+ settingsUpdateTail = Promise.resolve();
5070
+ settingsUpdatePending = false;
4633
5071
  turnAdmission;
4634
5072
  instructionsMaterialized = false;
4635
5073
  lifecycleGeneration = 0;
@@ -4685,6 +5123,35 @@ class LogicalAgentSession {
4685
5123
  send(message) {
4686
5124
  return this.admit("send", message);
4687
5125
  }
5126
+ updateSettings(input) {
5127
+ if (this.state === "closed" || this.state === "stopping" || this.finishing) {
5128
+ return Promise.resolve({
5129
+ status: "failed",
5130
+ error: driverError("process", "settings_session_closed", "Runtime session is closed", true)
5131
+ });
5132
+ }
5133
+ this.settingsUpdatePending = true;
5134
+ const operation = this.settingsUpdateTail.then(async () => {
5135
+ if (!this.lane?.updateSettings)
5136
+ return { status: "unsupported" };
5137
+ try {
5138
+ return await this.lane.updateSettings(input);
5139
+ } catch (error) {
5140
+ return {
5141
+ status: "failed",
5142
+ error: driverError("process", "settings_update_failed", String(error), true)
5143
+ };
5144
+ }
5145
+ });
5146
+ this.settingsUpdateTail = operation.then((result) => {
5147
+ if (result.status === "applied") {
5148
+ this.settingsUpdatePending = false;
5149
+ return;
5150
+ }
5151
+ return new Promise(() => {});
5152
+ });
5153
+ return operation;
5154
+ }
4688
5155
  async interrupt(input) {
4689
5156
  if (this.state === "closed" || this.state === "stopping" || this.finishing)
4690
5157
  return { status: "closed" };
@@ -4846,7 +5313,7 @@ class LogicalAgentSession {
4846
5313
  }
4847
5314
  return receipt;
4848
5315
  }
4849
- if (this.state === "idle" && (this.queued.length > 0 || this.safeBoundaryFlush !== undefined || this.safeBoundaryDelivery !== undefined)) {
5316
+ if (this.state === "idle" && (this.queued.length > 0 || this.safeBoundaryFlush !== undefined || this.safeBoundaryDelivery !== undefined || this.settingsUpdatePending)) {
4850
5317
  return this.queue(message, "runtime_busy");
4851
5318
  }
4852
5319
  return this.startTurn([message], "prompt");
@@ -5154,11 +5621,10 @@ class LogicalAgentSession {
5154
5621
  }
5155
5622
  return;
5156
5623
  case "telemetry": {
5157
- const details = jsonValue(event.attrs);
5158
5624
  if (event.name === "token_usage") {
5159
- this.emit({ type: "token_usage", turnId, source: event.source, usage: {}, details });
5625
+ this.emit({ type: "token_usage", turnId, source: event.source, usage: event.usage });
5160
5626
  } else {
5161
- this.emit({ type: "rate_limits", turnId, source: event.source, details });
5627
+ this.emit({ type: "rate_limits", turnId, source: event.source, quota: event.quota });
5162
5628
  }
5163
5629
  return;
5164
5630
  }
@@ -5259,7 +5725,7 @@ class LogicalAgentSession {
5259
5725
  if (this.adapter.execution.lifetime === "turn") {
5260
5726
  this.processTurnEnded = true;
5261
5727
  } else {
5262
- Promise.resolve().then(() => this.safeBoundaryFlush).then(() => this.startNextQueued());
5728
+ Promise.resolve().then(() => this.safeBoundaryFlush).then(() => this.settingsUpdateTail).then(() => this.startNextQueued());
5263
5729
  }
5264
5730
  }
5265
5731
  flushSafeBoundaryQueue() {
@@ -5759,8 +6225,14 @@ function createAgentDriverSdkWithRegistry(options) {
5759
6225
  assertAdapterCompatibility(String(registration.id), registration.capabilities, adapter);
5760
6226
  const command = capabilities2.commandOverride ? input.command : undefined;
5761
6227
  const result = await adapter.probe(command);
5762
- if (result.status === "healthy")
5763
- return { status: "healthy", version: result.version, capabilities: capabilities2 };
6228
+ if (result.status === "healthy") {
6229
+ return {
6230
+ status: "healthy",
6231
+ version: result.version,
6232
+ capabilities: capabilities2,
6233
+ reasoning: result.reasoning
6234
+ };
6235
+ }
5764
6236
  return {
5765
6237
  status: "unhealthy",
5766
6238
  error: {
@@ -5769,7 +6241,8 @@ function createAgentDriverSdkWithRegistry(options) {
5769
6241
  message: `Backend ${input.backend} is unavailable`,
5770
6242
  retryable: true
5771
6243
  },
5772
- capabilities: capabilities2
6244
+ capabilities: capabilities2,
6245
+ reasoning: result.reasoning
5773
6246
  };
5774
6247
  } catch (error) {
5775
6248
  const contractInvalid = error instanceof Error && (error.message.startsWith("Adapter ") || error.message.startsWith("Agent backend registration "));
@@ -5963,45 +6436,184 @@ class RuntimeNotificationState {
5963
6436
  return false;
5964
6437
  return this.lastEncodeFailedFingerprint === fingerprint && this.lastEncodeFailedSessionId === sessionId;
5965
6438
  }
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);
6439
+ filterUncontributedMessages(messages, sessionId) {
6440
+ if (this.contributionSessionId !== sessionId)
6441
+ return messages;
6442
+ return messages.filter((m) => {
6443
+ const identity = inboxNoticeMessageIdentity(m);
6444
+ return identity.length === 0 || !this.contributedIdentities.has(identity);
6445
+ });
6446
+ }
6447
+ add(count = 1) {
6448
+ this.pendingCountValue += count;
6449
+ }
6450
+ schedule(callback, delayMs) {
6451
+ if (this.timerValue)
6452
+ return false;
6453
+ this.timerValue = setTimeout(() => {
6454
+ this.timerValue = null;
6455
+ callback();
6456
+ }, delayMs);
6457
+ this.timerValue.unref?.();
6458
+ return true;
6459
+ }
6460
+ takePendingAndClearTimer() {
6461
+ const count = this.pendingCountValue;
6462
+ this.pendingCountValue = 0;
6463
+ if (this.timerValue) {
6464
+ clearTimeout(this.timerValue);
6465
+ this.timerValue = null;
6466
+ }
6467
+ return count;
6468
+ }
6469
+ ensureContributionSession(sessionId) {
6470
+ if (this.contributionSessionId !== sessionId) {
6471
+ this.contributionSessionId = sessionId;
6472
+ this.contributedIdentities = new Set;
6473
+ }
6474
+ }
6475
+ }
6476
+ // src/runtime/errorDiagnostics.ts
6477
+ import { createHash as createHash2 } from "crypto";
6478
+ // agent-driver/dist/provider-quota.js
6479
+ import { execFile } from "node:child_process";
6480
+ import { readFile } from "node:fs/promises";
6481
+ import { homedir as homedir3 } from "node:os";
6482
+ import { join as join9 } from "node:path";
6483
+ import { promisify } from "node:util";
6484
+ import { randomBytes as randomBytes4 } from "node:crypto";
6485
+ var execFileAsync = promisify(execFile);
6486
+ var claudeAccessToken = null;
6487
+ var claudeSourceEpoch = randomBytes4(16).toString("base64url");
6488
+ function parseCredentials(value) {
6489
+ try {
6490
+ const parsed = JSON.parse(value);
6491
+ return parsed && typeof parsed === "object" ? parsed : null;
6492
+ } catch {
6493
+ return null;
6494
+ }
6495
+ }
6496
+ async function claudeCredentials(options) {
6497
+ const platform = options.platform ?? process.platform;
6498
+ if (platform === "darwin") {
6499
+ try {
6500
+ const value = options.readKeychain ? await options.readKeychain() : (await execFileAsync("security", ["find-generic-password", "-s", "Claude Code-credentials", "-w"], {
6501
+ timeout: 3000,
6502
+ maxBuffer: 256 * 1024
6503
+ })).stdout;
6504
+ const parsed = parseCredentials(value.trim());
6505
+ if (parsed)
6506
+ return parsed;
6507
+ } catch {}
6508
+ }
6509
+ const env = options.env ?? process.env;
6510
+ const root = env.CLAUDE_CONFIG_DIR || join9(options.home ?? homedir3(), ".claude");
6511
+ try {
6512
+ const value = options.readCredentialsFile ? await options.readCredentialsFile(join9(root, ".credentials.json")) : await readFile(join9(root, ".credentials.json"), "utf8");
6513
+ return parseCredentials(value);
6514
+ } catch {
6515
+ return null;
6516
+ }
6517
+ }
6518
+ function mappedPlanName2(value) {
6519
+ switch (value) {
6520
+ case "free":
6521
+ return "Free";
6522
+ case "pro":
6523
+ return "Pro";
6524
+ case "max":
6525
+ return "Max";
6526
+ case "team":
6527
+ return "Team";
6528
+ case "enterprise":
6529
+ return "Enterprise";
6530
+ default:
6531
+ return;
6532
+ }
6533
+ }
6534
+ function resetIso2(value) {
6535
+ if (typeof value !== "string")
6536
+ return;
6537
+ const date = new Date(value);
6538
+ return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
6539
+ }
6540
+ function claudeLimit(key, value) {
6541
+ if (!value || typeof value !== "object")
6542
+ return null;
6543
+ const row = value;
6544
+ if (typeof row.utilization !== "number" || !Number.isFinite(row.utilization) || row.utilization < 0 || row.utilization > 100)
6545
+ return null;
6546
+ const model = key.includes("sonnet") ? { kind: "reported", id: "claude-sonnet" } : key.includes("opus") ? { kind: "reported", id: "claude-opus" } : { kind: "not_applicable" };
6547
+ const window2 = key === "five_hour" ? { kind: "rolling", durationSeconds: 18000, displayName: "5 hour usage limit" } : { kind: "rolling", durationSeconds: 604800, displayName: "7 day usage limit" };
6548
+ const resetsAt = resetIso2(row.resets_at ?? row.resetsAt);
6549
+ return {
6550
+ bucket: {
6551
+ limitId: key,
6552
+ product: { kind: "reported", id: "claude", displayName: "Claude" },
6553
+ model,
6554
+ window: window2
6555
+ },
6556
+ usedPercent: row.utilization,
6557
+ ...resetsAt ? { resetsAt } : {}
6558
+ };
6559
+ }
6560
+ async function readClaudeQuota(options) {
6561
+ const env = options.env ?? process.env;
6562
+ if (env.ANTHROPIC_API_KEY || env.ANTHROPIC_BASE_URL)
6563
+ return null;
6564
+ const credentials = await claudeCredentials(options);
6565
+ const token = credentials?.claudeAiOauth?.accessToken;
6566
+ if (typeof token !== "string" || token.length === 0)
6567
+ return null;
6568
+ if (claudeAccessToken !== token) {
6569
+ claudeAccessToken = token;
6570
+ claudeSourceEpoch = randomBytes4(16).toString("base64url");
6571
+ }
6572
+ let response;
6573
+ try {
6574
+ response = await (options.fetchUsage ?? fetch)("https://api.anthropic.com/api/oauth/usage", {
6575
+ headers: {
6576
+ authorization: `Bearer ${token}`,
6577
+ "anthropic-beta": "oauth-2025-04-20"
6578
+ },
6579
+ signal: AbortSignal.timeout(5000)
5972
6580
  });
6581
+ } catch {
6582
+ return { status: "error", sourceEpoch: claudeSourceEpoch, code: "network", retryable: true };
5973
6583
  }
5974
- add(count = 1) {
5975
- this.pendingCountValue += count;
6584
+ if (response.status === 401 || response.status === 403) {
6585
+ return { status: "error", sourceEpoch: claudeSourceEpoch, code: "unauthorized", retryable: false };
5976
6586
  }
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;
6587
+ if (!response.ok) {
6588
+ return { status: "error", sourceEpoch: claudeSourceEpoch, code: "provider_error", retryable: response.status === 429 || response.status >= 500 };
5986
6589
  }
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;
6590
+ let body;
6591
+ try {
6592
+ body = await response.json();
6593
+ } catch {
6594
+ return { status: "error", sourceEpoch: claudeSourceEpoch, code: "invalid_response", retryable: true };
5995
6595
  }
5996
- ensureContributionSession(sessionId) {
5997
- if (this.contributionSessionId !== sessionId) {
5998
- this.contributionSessionId = sessionId;
5999
- this.contributedIdentities = new Set;
6000
- }
6596
+ if (!body || typeof body !== "object") {
6597
+ return { status: "error", sourceEpoch: claudeSourceEpoch, code: "invalid_response", retryable: true };
6001
6598
  }
6599
+ const record3 = body;
6600
+ const limits = ["five_hour", "seven_day", "seven_day_sonnet", "seven_day_opus"].map((key) => claudeLimit(key, record3[key])).filter((limit) => limit !== null);
6601
+ if (limits.length === 0) {
6602
+ return { status: "error", sourceEpoch: claudeSourceEpoch, code: "invalid_response", retryable: true };
6603
+ }
6604
+ const planName = mappedPlanName2(credentials?.claudeAiOauth?.subscriptionType);
6605
+ return {
6606
+ status: "available",
6607
+ sourceEpoch: claudeSourceEpoch,
6608
+ ...planName ? { planName } : {},
6609
+ freshForSeconds: 300,
6610
+ limits
6611
+ };
6612
+ }
6613
+ async function readBuiltinProviderQuota(backend, options = {}) {
6614
+ return backend === "claude" ? readClaudeQuota(options) : null;
6002
6615
  }
6003
6616
  // src/runtime/errorDiagnostics.ts
6004
- import { createHash as createHash2 } from "crypto";
6005
6617
  var ERROR_EXCERPT_MAX_BYTES = 4000;
6006
6618
  var ERROR_FINGERPRINT_LEN = 16;
6007
6619
  var ERROR_LEN_BUCKETS = [
@@ -6824,6 +7436,29 @@ function reduceManager(state, event) {
6824
7436
  a.inbox = [...a.inbox, event.message];
6825
7437
  a.idleSince = null;
6826
7438
  });
7439
+ case "runtime_config_queued":
7440
+ return mutate(state, event.agentId, (a) => {
7441
+ if (!a.inbox.some((message) => message.id === event.message.id)) {
7442
+ a.inbox = [...a.inbox, event.message];
7443
+ }
7444
+ syncExecutionProjection(a);
7445
+ a.idleSince = null;
7446
+ });
7447
+ case "runtime_config_applied": {
7448
+ const existing = state.agents[event.agentId];
7449
+ if (!existing)
7450
+ return { state, effects: [] };
7451
+ const agent = clone(existing);
7452
+ if (agent.status !== "running" || leaseIsWorking(agent.execution.lease) || agent.pendingAdmissions.length > 0 || agent.inbox.length === 0)
7453
+ return { state, effects: [] };
7454
+ const messages = drainInbox(agent);
7455
+ return commit(state, agent, messages.map((message) => ({
7456
+ type: "send",
7457
+ agentId: event.agentId,
7458
+ message,
7459
+ mode: "idle"
7460
+ })));
7461
+ }
6827
7462
  case "turn_started": {
6828
7463
  const existing = state.agents[event.agentId];
6829
7464
  if (!existing || existing.execution.sessionInstanceId !== event.sessionInstanceId)
@@ -7031,7 +7666,7 @@ function onTurnCompleted(state, agentId, sessionInstanceId, nowMs, turnId, endRe
7031
7666
  agent.stalledSessionId = null;
7032
7667
  syncExecutionProjection(agent);
7033
7668
  const clearEffects = clearedStallSessionId === null ? [] : [{ type: "clear_stall_recovery", agentId, sessionId: clearedStallSessionId }];
7034
- if (agent.inbox.length > 0) {
7669
+ if (agent.inbox.length > 0 && !agent.resetting) {
7035
7670
  const messages = drainInbox(agent);
7036
7671
  return commit(state, agent, [
7037
7672
  ...clearEffects,
@@ -7946,9 +8581,13 @@ class AgentProcessManager {
7946
8581
  state;
7947
8582
  sessions = new Map;
7948
8583
  runtimeConfigs = new Map;
8584
+ appliedRuntimeConfigs = new Map;
8585
+ pendingRuntimeConfigUpdates = new Map;
8586
+ runtimeConfigApplyRunning = new Set;
7949
8587
  resumeSessions = new Map;
7950
8588
  launchIds = new Map;
7951
8589
  liveSessions = new Map;
8590
+ liveBackendIds = new Map;
7952
8591
  activeSpawnState = new Map;
7953
8592
  publishedAgentActivity = new Map;
7954
8593
  traceProcessNonce = randomUUID5();
@@ -7977,19 +8616,156 @@ class AgentProcessManager {
7977
8616
  this.state = createInitialManagerState(this.opts.staleThresholdMs, this.opts.idleTimeoutMs, this.opts.resetStuckThresholdMs, this.opts.stoppingStuckThresholdMs, this.opts.idleResetTimeoutMs);
7978
8617
  }
7979
8618
  register(agentId, launch) {
7980
- if (launch?.runtimeConfig)
7981
- this.runtimeConfigs.set(agentId, launch.runtimeConfig);
8619
+ const runtimeConfigAcceptance = launch?.runtimeConfig ? this.acceptRuntimeConfig(agentId, launch.runtimeConfig) : undefined;
7982
8620
  if (launch?.sessionId)
7983
8621
  this.resumeSessions.set(agentId, launch.sessionId);
7984
8622
  if (launch?.launchId)
7985
8623
  this.launchIds.set(agentId, launch.launchId);
7986
8624
  this.dispatch({ type: "register", agentId });
8625
+ const registered = this.state.agents[agentId];
8626
+ if (launch?.runtimeConfig && launch.applyRuntimeConfig !== false && (runtimeConfigAcceptance === "accepted" || this.pendingRuntimeConfigUpdates.has(agentId)) && this.sessions.has(agentId) && registered && !isActivelyWorking(registered)) {
8627
+ this.convergeRuntimeConfig(agentId);
8628
+ }
8629
+ }
8630
+ async updateRuntimeConfig(agentId, config) {
8631
+ const accepted = this.acceptRuntimeConfig(agentId, config);
8632
+ if (accepted === "stale" || accepted === "idempotent")
8633
+ return accepted;
8634
+ const session = this.sessions.get(agentId);
8635
+ if (!session) {
8636
+ this.pendingRuntimeConfigUpdates.delete(agentId);
8637
+ return "saved_for_start";
8638
+ }
8639
+ const agent = this.state.agents[agentId];
8640
+ if (agent && (agent.turnActive || isActivelyWorking(agent)))
8641
+ return "deferred";
8642
+ return this.convergeRuntimeConfig(agentId);
8643
+ }
8644
+ acceptRuntimeConfig(agentId, config) {
8645
+ const desired = this.runtimeConfigs.get(agentId);
8646
+ const revision = config.runtimeConfigRevision ?? 0;
8647
+ const desiredRevision = desired?.runtimeConfigRevision ?? 0;
8648
+ if (desired && revision < desiredRevision)
8649
+ return "stale";
8650
+ if (desired && revision === desiredRevision) {
8651
+ if (this.runtimeConfigTuple(desired) !== this.runtimeConfigTuple(config)) {
8652
+ throw new Error(`Conflicting runtime config for ${agentId} at revision ${revision}`);
8653
+ }
8654
+ this.runtimeConfigs.set(agentId, config);
8655
+ return "idempotent";
8656
+ }
8657
+ this.runtimeConfigs.set(agentId, config);
8658
+ this.pendingRuntimeConfigUpdates.set(agentId, config);
8659
+ return "accepted";
8660
+ }
8661
+ runtimeConfigTuple(config) {
8662
+ return JSON.stringify({
8663
+ version: config.version,
8664
+ runtime: config.runtime,
8665
+ model: config.model,
8666
+ mode: config.mode,
8667
+ reasoningEffort: config.reasoningEffort ?? null,
8668
+ provider: config.provider ?? null,
8669
+ command: config.command ?? null,
8670
+ disallowedTools: config.disallowedTools ?? null,
8671
+ envVars: config.envVars ?? null
8672
+ });
8673
+ }
8674
+ runtimeLaunchTuple(config) {
8675
+ return JSON.stringify({
8676
+ version: config.version,
8677
+ runtime: config.runtime,
8678
+ model: config.model,
8679
+ mode: config.mode,
8680
+ provider: config.provider ?? null,
8681
+ command: config.command ?? null,
8682
+ disallowedTools: config.disallowedTools ?? null,
8683
+ envVars: config.envVars ?? null
8684
+ });
8685
+ }
8686
+ async convergeRuntimeConfig(agentId, restartOnFailure = true) {
8687
+ if (this.runtimeConfigApplyRunning.has(agentId))
8688
+ return "deferred";
8689
+ const session = this.sessions.get(agentId);
8690
+ if (!session)
8691
+ return "saved_for_start";
8692
+ this.runtimeConfigApplyRunning.add(agentId);
8693
+ try {
8694
+ while (this.sessions.get(agentId) === session) {
8695
+ const desired = this.pendingRuntimeConfigUpdates.get(agentId) ?? this.runtimeConfigs.get(agentId);
8696
+ if (!desired)
8697
+ return "idempotent";
8698
+ const desiredRevision = desired.runtimeConfigRevision ?? 0;
8699
+ const applied = this.appliedRuntimeConfigs.get(agentId);
8700
+ const appliedRevision = applied?.runtimeConfigRevision ?? -1;
8701
+ if (applied && desiredRevision <= appliedRevision) {
8702
+ this.pendingRuntimeConfigUpdates.delete(agentId);
8703
+ return desiredRevision === appliedRevision ? "idempotent" : "stale";
8704
+ }
8705
+ const canApplyNatively = applied && this.runtimeLaunchTuple(applied) === this.runtimeLaunchTuple(desired) && typeof session.updateSettings === "function";
8706
+ let result = { status: "unsupported" };
8707
+ if (canApplyNatively) {
8708
+ try {
8709
+ result = await session.updateSettings({
8710
+ reasoningEffort: desired.reasoningEffort ?? null
8711
+ });
8712
+ } catch (error) {
8713
+ this.log.warn("runtime config live apply threw; restarting at safe boundary", {
8714
+ agentId,
8715
+ revision: desiredRevision,
8716
+ error: String(error)
8717
+ });
8718
+ if (restartOnFailure)
8719
+ await this.restartForRuntimeConfig(agentId, session);
8720
+ return "saved_for_start";
8721
+ }
8722
+ }
8723
+ if (result.status !== "applied") {
8724
+ this.log.warn("runtime config live apply unavailable; restarting at safe boundary", {
8725
+ agentId,
8726
+ revision: desiredRevision,
8727
+ status: result.status,
8728
+ code: result.error?.code
8729
+ });
8730
+ if (restartOnFailure)
8731
+ await this.restartForRuntimeConfig(agentId, session);
8732
+ return "saved_for_start";
8733
+ }
8734
+ this.appliedRuntimeConfigs.set(agentId, desired);
8735
+ if ((this.pendingRuntimeConfigUpdates.get(agentId)?.runtimeConfigRevision ?? -1) === desiredRevision) {
8736
+ this.pendingRuntimeConfigUpdates.delete(agentId);
8737
+ }
8738
+ const latestRevision = this.runtimeConfigs.get(agentId)?.runtimeConfigRevision ?? 0;
8739
+ if (latestRevision <= desiredRevision) {
8740
+ this.dispatch({ type: "runtime_config_applied", agentId });
8741
+ return "applied";
8742
+ }
8743
+ }
8744
+ return "saved_for_start";
8745
+ } finally {
8746
+ this.runtimeConfigApplyRunning.delete(agentId);
8747
+ }
8748
+ }
8749
+ async restartForRuntimeConfig(agentId, session) {
8750
+ if (this.sessions.get(agentId) !== session)
8751
+ return;
8752
+ this.opts.timeline?.fenceSession(agentId);
8753
+ this.markResetting(agentId);
8754
+ await this.stop(agentId);
7987
8755
  }
7988
8756
  deliver(agentId, message) {
7989
8757
  const normalized = message.id ? message : {
7990
8758
  ...message,
7991
8759
  id: message.seq !== undefined ? `${agentId}:source:${message.seq}` : `${agentId}:synthetic:${this.nextDeliveryOrdinal++}`
7992
8760
  };
8761
+ if (this.sessions.has(agentId) && this.pendingRuntimeConfigUpdates.has(agentId)) {
8762
+ this.dispatch({
8763
+ type: "runtime_config_queued",
8764
+ agentId,
8765
+ message: normalized
8766
+ });
8767
+ return this.state.agents[agentId] !== undefined;
8768
+ }
7993
8769
  const effects = this.dispatch({ type: "wake", agentId, message: normalized, nowMs: this.now() });
7994
8770
  return effects.length > 0;
7995
8771
  }
@@ -8033,7 +8809,11 @@ class AgentProcessManager {
8033
8809
  this.emitErrorAudit(agentId, "reset", "resume_control_update_failed", "Reset aborted because resume control could not be persisted");
8034
8810
  throw new Error("Reset aborted because resume control could not be persisted");
8035
8811
  }
8036
- this.register(agentId, { runtimeConfig: opts.runtimeConfig, launchId: opts.launchId });
8812
+ this.register(agentId, {
8813
+ runtimeConfig: opts.runtimeConfig,
8814
+ launchId: opts.launchId,
8815
+ applyRuntimeConfig: false
8816
+ });
8037
8817
  if (!opts.forgetSession)
8038
8818
  this.opts.timeline?.fenceSession(agentId);
8039
8819
  this.abortCurrentTurn(agentId, opts.abortCause);
@@ -8134,6 +8914,9 @@ class AgentProcessManager {
8134
8914
  const agent = this.state.agents[agentId];
8135
8915
  return agent ? this.deriveActivity(agent) : null;
8136
8916
  }
8917
+ agentBackendId(agentId) {
8918
+ return this.liveBackendIds.get(agentId) ?? null;
8919
+ }
8137
8920
  statusProjection(nowMs) {
8138
8921
  return Object.values(this.state.agents).map((a) => ({
8139
8922
  agentId: a.agentId,
@@ -8659,6 +9442,7 @@ ${this.opts.wakePromptFooter}` : text;
8659
9442
  throw new Error(`AgentProcessManager: spawn for ${agentId} has no command`);
8660
9443
  const prompt = this.withFooter(first.text);
8661
9444
  const driver = this.opts.driverFor(agentId, this.runtimeConfigs.get(agentId));
9445
+ this.liveBackendIds.set(agentId, driver.id);
8662
9446
  const base = this.opts.baseContextFor(agentId);
8663
9447
  const configuredRuntime = this.runtimeConfigs.get(agentId) ?? base.config?.runtimeConfig;
8664
9448
  this.log.info("spawning agent", {
@@ -8779,7 +9563,10 @@ ${this.opts.wakePromptFooter}` : text;
8779
9563
  if (state.session && this.sessions.get(agentId) === state.session)
8780
9564
  this.sessions.delete(agentId);
8781
9565
  this.liveSessions.delete(agentId);
9566
+ if (this.activeSpawnState.get(agentId) === state)
9567
+ this.liveBackendIds.delete(agentId);
8782
9568
  if (this.activeSpawnState.get(agentId) === state) {
9569
+ this.appliedRuntimeConfigs.delete(agentId);
8783
9570
  this.activeSpawnState.delete(agentId);
8784
9571
  this.nonCleanEndMarker.delete(agentId);
8785
9572
  }
@@ -8827,6 +9614,10 @@ ${this.opts.wakePromptFooter}` : text;
8827
9614
  state.session = session;
8828
9615
  state.sessionInstanceId = session.sessionInstanceId;
8829
9616
  this.sessions.set(agentId, session);
9617
+ this.appliedRuntimeConfigs.set(agentId, runtimeConfig);
9618
+ if ((this.pendingRuntimeConfigUpdates.get(agentId)?.runtimeConfigRevision ?? -1) <= (runtimeConfig.runtimeConfigRevision ?? 0)) {
9619
+ this.pendingRuntimeConfigUpdates.delete(agentId);
9620
+ }
8830
9621
  this.dispatch({
8831
9622
  type: "attach_session",
8832
9623
  agentId,
@@ -9060,6 +9851,12 @@ ${this.opts.wakePromptFooter}` : text;
9060
9851
  runtime: runtimeId
9061
9852
  });
9062
9853
  }
9854
+ if (event.type === "token_usage") {
9855
+ this.opts.onTokenUsage?.({ agentId, backendId: runtimeId, usage: event.usage });
9856
+ }
9857
+ if (event.type === "rate_limits") {
9858
+ this.opts.onProviderQuota?.({ agentId, backendId: runtimeId, quota: event.quota });
9859
+ }
9063
9860
  if (event.type === "turn_started") {
9064
9861
  const timelineTurnOwner = {
9065
9862
  sessionInstanceId: event.sessionInstanceId,
@@ -9174,7 +9971,7 @@ ${this.opts.wakePromptFooter}` : text;
9174
9971
  this.logSessionEnded(agentId, "turn_end");
9175
9972
  const marker = this.nonCleanEndMarker.get(agentId);
9176
9973
  this.nonCleanEndMarker.delete(agentId);
9177
- this.dispatch(marker !== undefined ? {
9974
+ const completionEvent = marker !== undefined ? {
9178
9975
  type: "turn_completed",
9179
9976
  agentId,
9180
9977
  sessionInstanceId: event.sessionInstanceId,
@@ -9189,7 +9986,26 @@ ${this.opts.wakePromptFooter}` : text;
9189
9986
  sessionInstanceId: event.sessionInstanceId,
9190
9987
  nowMs: this.now(),
9191
9988
  turnId: event.turnId
9192
- }, owner);
9989
+ };
9990
+ if (this.pendingRuntimeConfigUpdates.has(agentId) && this.sessions.get(agentId) === owner.session) {
9991
+ this.convergeRuntimeConfig(agentId, false).then((result) => {
9992
+ if (result === "saved_for_start" && owner.session)
9993
+ this.markResetting(agentId);
9994
+ this.dispatch(completionEvent, owner);
9995
+ if (result === "saved_for_start" && owner.session) {
9996
+ this.restartForRuntimeConfig(agentId, owner.session);
9997
+ }
9998
+ }).catch((error) => {
9999
+ this.log.error("runtime config convergence failed", { agentId, error: String(error) });
10000
+ if (owner.session)
10001
+ this.markResetting(agentId);
10002
+ this.dispatch(completionEvent, owner);
10003
+ if (owner.session)
10004
+ this.restartForRuntimeConfig(agentId, owner.session);
10005
+ });
10006
+ return;
10007
+ }
10008
+ this.dispatch(completionEvent, owner);
9193
10009
  }
9194
10010
  }
9195
10011
  }
@@ -23489,6 +24305,7 @@ var exports_community_machine_schema = {};
23489
24305
  __export(exports_community_machine_schema, {
23490
24306
  communityMachineToken: () => communityMachineToken,
23491
24307
  communityMachineCredential: () => communityMachineCredential,
24308
+ communityMachineBackendQuota: () => communityMachineBackendQuota,
23492
24309
  communityMachine: () => communityMachine,
23493
24310
  communityDiagnosticReport: () => communityDiagnosticReport,
23494
24311
  communityBotBinding: () => communityBotBinding,
@@ -24085,7 +24902,7 @@ function sql(strings, ...params) {
24085
24902
  return new SQL([new StringChunk(str)]);
24086
24903
  }
24087
24904
  sql2.raw = raw;
24088
- function join9(chunks, separator) {
24905
+ function join10(chunks, separator) {
24089
24906
  const result = [];
24090
24907
  for (const [i, chunk] of chunks.entries()) {
24091
24908
  if (i > 0 && separator !== undefined) {
@@ -24095,7 +24912,7 @@ function sql(strings, ...params) {
24095
24912
  }
24096
24913
  return new SQL(result);
24097
24914
  }
24098
- sql2.join = join9;
24915
+ sql2.join = join10;
24099
24916
  function identifier(value) {
24100
24917
  return new Name(value);
24101
24918
  }
@@ -25092,6 +25909,8 @@ var user = sqliteTable("user", {
25092
25909
  email: text("email").unique().notNull(),
25093
25910
  emailVerified: integer2("emailVerified", { mode: "boolean" }),
25094
25911
  image: text("image"),
25912
+ avatarVersion: integer2("avatarVersion").notNull().default(0),
25913
+ avatarObjectKey: text("avatarObjectKey"),
25095
25914
  createdAt: text("createdAt").notNull().$defaultFn(() => new Date().toISOString()),
25096
25915
  updatedAt: text("updatedAt").notNull().$defaultFn(() => new Date().toISOString()),
25097
25916
  isBot: integer2("isBot", { mode: "boolean" }).notNull().default(false),
@@ -25765,8 +26584,23 @@ var communityBotBinding = sqliteTable("community_bot_binding", {
25765
26584
  runtime: text("runtime").notNull(),
25766
26585
  instruction: text("instruction").notNull().default(""),
25767
26586
  modelName: text("model_name"),
26587
+ reasoningEffort: text("reasoning_effort"),
26588
+ runtimeConfigRevision: integer2("runtime_config_revision").notNull().default(0),
25768
26589
  createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
25769
26590
  }, (t) => [index("idx_community_bot_binding_machine").on(t.machineId)]);
26591
+ var communityMachineBackendQuota = sqliteTable("community_machine_backend_quota", {
26592
+ machineId: text("machine_id").notNull().references(() => communityMachine.id, { onDelete: "cascade" }),
26593
+ agentBackendId: text("agent_backend_id").$type().notNull(),
26594
+ sourceEpoch: text("source_epoch").notNull(),
26595
+ status: text("status").$type().notNull(),
26596
+ planName: text("plan_name"),
26597
+ freshForSeconds: integer2("fresh_for_seconds"),
26598
+ limits: text("limits", { mode: "json" }).$type(),
26599
+ errorCode: text("error_code"),
26600
+ retryable: integer2("retryable", { mode: "boolean" }),
26601
+ observedAt: text("observed_at").notNull(),
26602
+ updatedAt: text("updated_at").notNull()
26603
+ }, (t) => [primaryKey({ columns: [t.machineId, t.agentBackendId] })]);
25770
26604
  var communityAgentRunnerKey = sqliteTable("community_agent_runner_key", {
25771
26605
  id: text("id").primaryKey().$defaultFn(() => "crkid_" + nanoid3()),
25772
26606
  userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
@@ -25945,6 +26779,11 @@ var HostCommandSchema = exports_external.discriminatedUnion("type", [
25945
26779
  config: exports_external.unknown(),
25946
26780
  launchId: exports_external.string().min(1)
25947
26781
  }),
26782
+ exports_external.object({
26783
+ type: exports_external.literal("agent:runtime_config_update"),
26784
+ agentId: exports_external.string().min(1),
26785
+ config: exports_external.unknown()
26786
+ }),
25948
26787
  exports_external.object({
25949
26788
  type: exports_external.literal("machine:reset_all"),
25950
26789
  resets: exports_external.array(exports_external.object({
@@ -26051,7 +26890,8 @@ class AgentRouter {
26051
26890
  version: r.version,
26052
26891
  status: r.status ?? "healthy",
26053
26892
  lastError: r.lastError,
26054
- lastErrorAt: r.lastErrorAt
26893
+ lastErrorAt: r.lastErrorAt,
26894
+ reasoning: r.reasoning
26055
26895
  });
26056
26896
  }
26057
26897
  }
@@ -26060,7 +26900,7 @@ class AgentRouter {
26060
26900
  this.opts.channel.onResync?.(() => ({
26061
26901
  ready: this.buildReady(),
26062
26902
  sessions: this.opts.manager.liveSessionReports(),
26063
- activities: this.opts.manager.liveAgentActivities()
26903
+ activities: this.opts.resyncActivities ? this.opts.resyncActivities() : this.opts.manager.liveAgentActivities()
26064
26904
  }));
26065
26905
  await this.opts.channel.reportReady(this.buildReady());
26066
26906
  }
@@ -26073,7 +26913,8 @@ class AgentRouter {
26073
26913
  platform: this.opts.platform,
26074
26914
  arch: this.opts.arch,
26075
26915
  osRelease: this.opts.osRelease,
26076
- daemonVersion: this.opts.daemonVersion
26916
+ daemonVersion: this.opts.daemonVersion,
26917
+ ...this.opts.providerQuotas ? { providerQuotas: this.opts.providerQuotas() } : {}
26077
26918
  };
26078
26919
  }
26079
26920
  healthyRuntimeIds() {
@@ -26295,6 +27136,27 @@ class AgentRouter {
26295
27136
  rewakePrompt: MODEL_SWITCH_REWAKE_PROMPT
26296
27137
  }));
26297
27138
  break;
27139
+ case "agent:runtime_config_update": {
27140
+ this.log.info("agent:runtime_config_update received", {
27141
+ agentId: cmd.agentId,
27142
+ revision: cmd.config.runtimeConfigRevision ?? 0
27143
+ });
27144
+ try {
27145
+ const result = await this.opts.manager.updateRuntimeConfig(cmd.agentId, cmd.config);
27146
+ this.log.info("agent:runtime_config_update accepted", {
27147
+ agentId: cmd.agentId,
27148
+ revision: cmd.config.runtimeConfigRevision ?? 0,
27149
+ result
27150
+ });
27151
+ } catch (err) {
27152
+ this.log.warn("agent:runtime_config_update rejected", {
27153
+ agentId: cmd.agentId,
27154
+ revision: cmd.config.runtimeConfigRevision ?? 0,
27155
+ error: err instanceof Error ? err.message : String(err)
27156
+ });
27157
+ }
27158
+ break;
27159
+ }
26298
27160
  case "agent:stop":
26299
27161
  this.log.info("agent:stop received", { agentId: cmd.agentId });
26300
27162
  try {
@@ -26779,7 +27641,7 @@ function joinPath(basePath, reqUrl) {
26779
27641
  return base + reqPath || "/";
26780
27642
  }
26781
27643
  // src/daemon/createDaemon.ts
26782
- import { homedir as homedir4 } from "os";
27644
+ import { homedir as homedir5 } from "os";
26783
27645
  import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync7 } from "node:fs";
26784
27646
 
26785
27647
  // src/util/rotatingFileSink.ts
@@ -27054,7 +27916,7 @@ var MAX_PROFILE_ABOUT_LENGTH = 1000;
27054
27916
  var MAX_MESSAGE_CONTENT_LENGTH = 4000;
27055
27917
  var MAX_ATTACHMENTS_PER_MESSAGE = 10;
27056
27918
  var MAX_ATTACHMENT_SIZE_BYTES = 25 * 1024 * 1024;
27057
- var MAX_ATTACHMENT_THUMBNAIL_SIZE_BYTES = 50 * 1024;
27919
+ var MAX_ATTACHMENT_THUMBNAIL_SIZE_BYTES = 512 * 1024;
27058
27920
  var MAX_SERVER_ICON_SIZE_BYTES = 5 * 1024 * 1024;
27059
27921
  var ALLOWED_ICON_MIME_TYPES = [
27060
27922
  "image/png",
@@ -27063,6 +27925,92 @@ var ALLOWED_ICON_MIME_TYPES = [
27063
27925
  "image/gif"
27064
27926
  ];
27065
27927
  var MAX_ICON_SOURCE_FILE_SIZE_BYTES = 15 * 1024 * 1024;
27928
+ // ../shared/src/provider-telemetry.ts
27929
+ var safeToken = exports_external.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
27930
+ var boundedText = exports_external.string().min(1).refine((value) => new TextEncoder().encode(value).length <= 64, { message: "must be at most 64 UTF-8 bytes" });
27931
+ var DailyUsageMetricSchema = safeToken.nullable();
27932
+ var DailyUsageSnapshotSchema = exports_external.object({
27933
+ botId: exports_external.string().min(1),
27934
+ day: exports_external.string().regex(/^\d{4}-\d{2}-\d{2}$/),
27935
+ metrics: exports_external.object({
27936
+ input: DailyUsageMetricSchema,
27937
+ output: DailyUsageMetricSchema,
27938
+ cache: DailyUsageMetricSchema
27939
+ }).strict()
27940
+ }).strict();
27941
+ var QuotaProductIdentitySchema = exports_external.discriminatedUnion("kind", [
27942
+ exports_external.object({ kind: exports_external.literal("reported"), id: boundedText, displayName: boundedText }).strict(),
27943
+ exports_external.object({ kind: exports_external.literal("unknown"), displayName: boundedText }).strict()
27944
+ ]);
27945
+ var QuotaModelIdentitySchema = exports_external.discriminatedUnion("kind", [
27946
+ exports_external.object({ kind: exports_external.literal("reported"), id: boundedText }).strict(),
27947
+ exports_external.object({ kind: exports_external.literal("not_applicable") }).strict(),
27948
+ exports_external.object({ kind: exports_external.literal("unknown") }).strict()
27949
+ ]);
27950
+ var QuotaWindowIdentitySchema = exports_external.discriminatedUnion("kind", [
27951
+ exports_external.object({
27952
+ kind: exports_external.literal("rolling"),
27953
+ durationSeconds: exports_external.number().int().positive().max(Number.MAX_SAFE_INTEGER),
27954
+ displayName: boundedText
27955
+ }).strict(),
27956
+ exports_external.object({
27957
+ kind: exports_external.literal("calendar"),
27958
+ period: exports_external.enum(["day", "week", "month"]),
27959
+ displayName: boundedText
27960
+ }).strict(),
27961
+ exports_external.object({
27962
+ kind: exports_external.literal("provider_defined"),
27963
+ id: boundedText,
27964
+ durationSeconds: exports_external.number().int().positive().max(Number.MAX_SAFE_INTEGER).optional(),
27965
+ displayName: boundedText
27966
+ }).strict()
27967
+ ]);
27968
+ var QuotaLimitSchema = exports_external.object({
27969
+ bucket: exports_external.object({
27970
+ limitId: boundedText,
27971
+ product: QuotaProductIdentitySchema,
27972
+ model: QuotaModelIdentitySchema,
27973
+ window: QuotaWindowIdentitySchema
27974
+ }).strict(),
27975
+ usedPercent: exports_external.number().finite().min(0).max(100),
27976
+ resetsAt: exports_external.string().datetime({ offset: true }).optional()
27977
+ }).strict();
27978
+ function quotaIdentity(limit) {
27979
+ const { product, model, window: window2, limitId } = limit.bucket;
27980
+ const productKey = product.kind === "reported" ? `reported:${product.id}` : "unknown";
27981
+ const modelKey = model.kind === "reported" ? `reported:${model.id}` : model.kind;
27982
+ const windowKey = window2.kind === "rolling" ? `rolling:${window2.durationSeconds}` : window2.kind === "calendar" ? `calendar:${window2.period}` : `provider_defined:${window2.id}:${window2.durationSeconds === undefined ? "absent" : window2.durationSeconds}`;
27983
+ return `${productKey}\x00${modelKey}\x00${windowKey}\x00${limitId}`;
27984
+ }
27985
+ var AvailableQuotaObservationSchema = exports_external.object({
27986
+ status: exports_external.literal("available"),
27987
+ sourceEpoch: exports_external.string().regex(/^[A-Za-z0-9_-]{22}$/),
27988
+ planName: boundedText.optional(),
27989
+ freshForSeconds: exports_external.number().int().positive().max(86400),
27990
+ limits: exports_external.array(QuotaLimitSchema).min(1).max(8)
27991
+ }).strict().superRefine((value, ctx) => {
27992
+ const identities = new Set;
27993
+ for (const [index2, limit] of value.limits.entries()) {
27994
+ const identity = quotaIdentity(limit);
27995
+ if (identities.has(identity)) {
27996
+ ctx.addIssue({ code: "custom", message: "duplicate quota bucket identity", path: ["limits", index2] });
27997
+ }
27998
+ identities.add(identity);
27999
+ }
28000
+ });
28001
+ var ProviderQuotaObservationSchema = exports_external.union([
28002
+ AvailableQuotaObservationSchema,
28003
+ exports_external.object({
28004
+ status: exports_external.literal("error"),
28005
+ sourceEpoch: exports_external.string().regex(/^[A-Za-z0-9_-]{22}$/),
28006
+ code: exports_external.enum(["unavailable", "unauthorized", "network", "provider_error", "invalid_response"]),
28007
+ retryable: exports_external.boolean()
28008
+ }).strict()
28009
+ ]);
28010
+ var ProviderQuotaSnapshotSchema = exports_external.object({
28011
+ agentBackendId: exports_external.enum(["claude", "codex"]),
28012
+ observation: ProviderQuotaObservationSchema
28013
+ }).strict();
27066
28014
  // ../shared/src/utils/slug.ts
27067
28015
  init_nanoid();
27068
28016
  var slugId = customAlphabet("0123456789abcdefghijklmnopqrstuvwxyz");
@@ -27577,12 +28525,60 @@ var CreateThreadRequestSchema = exports_external.object({
27577
28525
  attachment_ids: exports_external.array(exports_external.string()).optional()
27578
28526
  });
27579
28527
  var RUNTIME_ID_RE = /^[A-Za-z0-9._@/-]+$/;
28528
+ var REASONING_EFFORT_RE = /^[A-Za-z0-9._-]+$/;
28529
+ var COMMUNITY_REASONING_EFFORT_MAX = 32;
28530
+ var COMMUNITY_REASONING_DESCRIPTION_MAX = 256;
28531
+ var COMMUNITY_REASONING_OPTIONS_MAX = 16;
28532
+ var COMMUNITY_REASONING_MODELS_MAX = 64;
28533
+ var ReasoningEffortSchema = exports_external.string().min(1).max(COMMUNITY_REASONING_EFFORT_MAX).regex(REASONING_EFFORT_RE, "invalid reasoning effort charset");
28534
+ var RuntimeReasoningOptionSchema = exports_external.object({
28535
+ value: ReasoningEffortSchema,
28536
+ description: exports_external.string().max(COMMUNITY_REASONING_DESCRIPTION_MAX).optional()
28537
+ });
28538
+ var RuntimeReasoningModelSchema = exports_external.object({
28539
+ id: exports_external.string().min(1).max(100),
28540
+ supportedReasoningEfforts: exports_external.array(exports_external.unknown()).max(COMMUNITY_REASONING_OPTIONS_MAX).transform((options) => {
28541
+ const seen = new Set;
28542
+ return options.flatMap((candidate) => {
28543
+ const parsed = RuntimeReasoningOptionSchema.safeParse(candidate);
28544
+ if (!parsed.success)
28545
+ return [];
28546
+ const option = parsed.data;
28547
+ if (seen.has(option.value))
28548
+ return [];
28549
+ seen.add(option.value);
28550
+ return [option];
28551
+ });
28552
+ }),
28553
+ defaultReasoningEffort: ReasoningEffortSchema.optional().catch(undefined)
28554
+ }).transform((model) => {
28555
+ const { defaultReasoningEffort, ...rest } = model;
28556
+ return defaultReasoningEffort !== undefined && model.supportedReasoningEfforts.some((option) => option.value === defaultReasoningEffort) ? { ...rest, defaultReasoningEffort } : rest;
28557
+ });
28558
+ var RuntimeReasoningCatalogSchema = exports_external.object({
28559
+ updateMode: exports_external.enum(["live_next_turn", "context_preserving_restart", "unsupported"]),
28560
+ defaultModelId: exports_external.string().min(1).max(100).optional().catch(undefined),
28561
+ models: exports_external.array(exports_external.unknown()).max(COMMUNITY_REASONING_MODELS_MAX).transform((models) => {
28562
+ const seen = new Set;
28563
+ return models.flatMap((candidate) => {
28564
+ const parsed = RuntimeReasoningModelSchema.safeParse(candidate);
28565
+ if (!parsed.success)
28566
+ return [];
28567
+ const model = parsed.data;
28568
+ if (seen.has(model.id))
28569
+ return [];
28570
+ seen.add(model.id);
28571
+ return [model];
28572
+ });
28573
+ })
28574
+ });
27580
28575
  var CommunityMachineRuntimeSchema = exports_external.object({
27581
28576
  id: exports_external.string().min(1).max(COMMUNITY_RUNTIME_ID_MAX).regex(RUNTIME_ID_RE, "invalid runtime id charset"),
27582
28577
  version: exports_external.string().max(COMMUNITY_RUNTIME_VERSION_MAX).optional(),
27583
28578
  status: exports_external.enum(["healthy", "unhealthy"]).catch("healthy").default("healthy"),
27584
28579
  lastError: exports_external.string().max(128).optional(),
27585
- lastErrorAt: exports_external.string().optional()
28580
+ lastErrorAt: exports_external.string().optional(),
28581
+ reasoning: RuntimeReasoningCatalogSchema.optional().catch(undefined)
27586
28582
  });
27587
28583
  var CommunityMachineRuntimeListSchema = exports_external.array(CommunityMachineRuntimeSchema).max(COMMUNITY_RUNTIME_LIST_MAX).transform((list) => {
27588
28584
  const seen = new Set;
@@ -27634,7 +28630,8 @@ var HostReadyMessageSchema = exports_external.object({
27634
28630
  platform: exports_external.string().optional(),
27635
28631
  arch: exports_external.string().optional(),
27636
28632
  osRelease: exports_external.string().optional(),
27637
- daemonVersion: exports_external.string().optional()
28633
+ daemonVersion: exports_external.string().optional(),
28634
+ providerQuotas: exports_external.array(ProviderQuotaSnapshotSchema).max(2).optional().default([])
27638
28635
  });
27639
28636
  var CommunityDaemonReadySchema = exports_external.object({
27640
28637
  runtimeReport: CommunityMachineRuntimeListSchema.optional(),
@@ -27655,7 +28652,9 @@ var SessionErrorFrameSchema = exports_external.object({
27655
28652
  var AgentActivityMessageSchema = exports_external.object({
27656
28653
  type: exports_external.literal("agent_activity"),
27657
28654
  agentId: exports_external.string(),
27658
- state: exports_external.enum(["idle", "starting", "running", "stopping"])
28655
+ state: exports_external.enum(["idle", "starting", "running", "stopping"]),
28656
+ dailyUsage: exports_external.array(DailyUsageSnapshotSchema).max(7).optional(),
28657
+ quota: ProviderQuotaSnapshotSchema.optional()
27659
28658
  });
27660
28659
  var AgentTypingMessageSchema = exports_external.object({
27661
28660
  type: exports_external.literal("agent_typing"),
@@ -27730,15 +28729,17 @@ var CommunityBotCreateRequestSchema = exports_external.object({
27730
28729
  machineId: exports_external.string().min(1),
27731
28730
  runtime: exports_external.string().min(1),
27732
28731
  image: BotImageUrlSchema.optional(),
27733
- model: exports_external.string().trim().min(1).max(100).nullable().optional()
28732
+ model: exports_external.string().trim().min(1).max(100).nullable().optional(),
28733
+ reasoningEffort: ReasoningEffortSchema.nullable().optional()
27734
28734
  });
27735
28735
  var CommunityBotPatchRequestSchema = exports_external.object({
27736
28736
  name: exports_external.string().trim().min(COMMUNITY_BOT_NAME_MIN).max(COMMUNITY_BOT_NAME_MAX).refine(isMentionSafeName, { message: MENTION_SAFE_NAME_MSG }).optional(),
27737
28737
  description: exports_external.string().max(COMMUNITY_BOT_DESCRIPTION_MAX).optional(),
27738
28738
  image: BotImageUrlSchema.nullable().optional(),
27739
28739
  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), {
28740
+ runtime: exports_external.string().trim().min(1).max(COMMUNITY_RUNTIME_ID_MAX).optional(),
28741
+ reasoningEffort: ReasoningEffortSchema.nullable().optional()
28742
+ }).refine((v) => v.name !== undefined || v.description !== undefined || v.image !== undefined || v.runtime !== undefined || ("reasoningEffort" in v) || ("model" in v), {
27742
28743
  message: "at least one field must be provided"
27743
28744
  });
27744
28745
  var CommunityBotAddToServerRequestSchema = exports_external.object({
@@ -27940,6 +28941,7 @@ __export(exports_community_schema, {
27940
28941
  communityChannelMember: () => communityChannelMember,
27941
28942
  communityChannel: () => communityChannel,
27942
28943
  communityCategory: () => communityCategory,
28944
+ communityBotDailyTokenUsage: () => communityBotDailyTokenUsage,
27943
28945
  communityBotDailyActivity: () => communityBotDailyActivity,
27944
28946
  communityBotApprovalRequest: () => communityBotApprovalRequest,
27945
28947
  communityBotActivityEvent: () => communityBotActivityEvent,
@@ -28192,6 +29194,17 @@ var communityBotDailyActivity = sqliteTable("community_bot_daily_activity", {
28192
29194
  handledCount: integer2("handled_count").notNull().default(0),
28193
29195
  sentCount: integer2("sent_count").notNull().default(0)
28194
29196
  }, (t) => [primaryKey({ columns: [t.botId, t.day] })]);
29197
+ var communityBotDailyTokenUsage = sqliteTable("community_bot_daily_token_usage", {
29198
+ botId: text("bot_id").notNull().references(() => user.id, { onDelete: "cascade" }),
29199
+ day: text("day").notNull(),
29200
+ inputTokens: integer2("input_tokens"),
29201
+ outputTokens: integer2("output_tokens"),
29202
+ cacheTokens: integer2("cache_tokens"),
29203
+ updatedAt: text("updated_at").notNull()
29204
+ }, (t) => [
29205
+ primaryKey({ columns: [t.botId, t.day] }),
29206
+ index("idx_community_bot_daily_token_usage_day").on(t.day)
29207
+ ]);
28195
29208
  var communityMessageMark = sqliteTable("community_message_mark", {
28196
29209
  id: text("id").primaryKey().$defaultFn(() => nanoid3()),
28197
29210
  userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
@@ -28316,7 +29329,8 @@ var listedMessageProjection = {
28316
29329
  clientNonce: communityMessage.clientNonce,
28317
29330
  authorName: user.name,
28318
29331
  authorEmail: user.email,
28319
- authorImage: user.image
29332
+ authorImage: user.image,
29333
+ authorAvatarVersion: user.avatarVersion
28320
29334
  };
28321
29335
 
28322
29336
  // ../shared/src/db/queries/user.ts
@@ -28326,6 +29340,7 @@ var publicUserColumns = {
28326
29340
  email: user.email,
28327
29341
  emailVerified: user.emailVerified,
28328
29342
  image: user.image,
29343
+ avatarVersion: user.avatarVersion,
28329
29344
  createdAt: user.createdAt,
28330
29345
  updatedAt: user.updatedAt,
28331
29346
  discriminator: user.discriminator
@@ -28336,6 +29351,12 @@ var internalUserColumns = {
28336
29351
  ownerUserId: user.ownerUserId,
28337
29352
  deletedAt: user.deletedAt
28338
29353
  };
29354
+ var avatarPublishColumns = {
29355
+ id: user.id,
29356
+ image: user.image,
29357
+ avatarVersion: user.avatarVersion,
29358
+ avatarObjectKey: user.avatarObjectKey
29359
+ };
28339
29360
 
28340
29361
  // ../shared/src/db/queries/community/channel.ts
28341
29362
  var CHANNEL_COLUMNS = {
@@ -28388,7 +29409,8 @@ var friendApprovalProfileSchema = exports_external.strictObject({
28388
29409
  id: string4,
28389
29410
  name: string4,
28390
29411
  discriminator: string4,
28391
- image: nullableString
29412
+ image: nullableString,
29413
+ avatarVersion: exports_external.number().int().nonnegative()
28392
29414
  });
28393
29415
  var FriendApprovalPayloadSchema = exports_external.strictObject({
28394
29416
  friendshipId: string4,
@@ -28414,6 +29436,7 @@ var messageSchema = exports_external.strictObject({
28414
29436
  authorId: string4,
28415
29437
  authorName: string4,
28416
29438
  authorAvatar: string4.optional(),
29439
+ authorAvatarVersion: exports_external.number().int().nonnegative(),
28417
29440
  content: string4,
28418
29441
  type: exports_external.enum(["chat", "system"]),
28419
29442
  systemKind: exports_external.literal("thread").optional(),
@@ -28421,6 +29444,7 @@ var messageSchema = exports_external.strictObject({
28421
29444
  replyToId: nullableString.optional(),
28422
29445
  replyTo: exports_external.strictObject({
28423
29446
  id: string4,
29447
+ authorId: string4.optional(),
28424
29448
  authorName: string4,
28425
29449
  text: string4,
28426
29450
  deleted: exports_external.boolean().optional()
@@ -28615,6 +29639,7 @@ var communityMemberJoinSchema = exports_external.strictObject({
28615
29639
  name: string4,
28616
29640
  discriminator: string4,
28617
29641
  avatar: string4.optional(),
29642
+ avatarVersion: exports_external.number().int().nonnegative(),
28618
29643
  role: string4,
28619
29644
  joinedAt: string4
28620
29645
  })
@@ -28717,6 +29742,22 @@ var communityStatusUpdateSchema = exports_external.strictObject({
28717
29742
  statusEmoji: nullableString,
28718
29743
  statusText: nullableString
28719
29744
  });
29745
+ var communityIdentityUpdateSchema = exports_external.strictObject({
29746
+ type: exports_external.literal("community:identity.update"),
29747
+ userId: string4,
29748
+ avatar: string4,
29749
+ avatarVersion: exports_external.number().int().positive()
29750
+ });
29751
+ var communityProfileUpdateSchema = exports_external.strictObject({
29752
+ type: exports_external.literal("community:profile.update"),
29753
+ userId: string4,
29754
+ name: string4,
29755
+ discriminator: string4,
29756
+ aboutMe: string4,
29757
+ bannerColor: nullableString,
29758
+ kind: exports_external.enum(["human", "bot"]),
29759
+ ownerUserId: nullableString
29760
+ });
28720
29761
  var machineRuntimeSchema = CommunityMachineRuntimeSchema.strict();
28721
29762
  var CommunityMachineSummarySchema2 = exports_external.strictObject({
28722
29763
  id: string4,
@@ -28805,6 +29846,8 @@ var CommunityWsEventDiscriminatedSchema = exports_external.discriminatedUnion("t
28805
29846
  communityInboxChangedSchema,
28806
29847
  communityPresenceUpdateSchema,
28807
29848
  communityStatusUpdateSchema,
29849
+ communityIdentityUpdateSchema,
29850
+ communityProfileUpdateSchema,
28808
29851
  communityMachineCreatedSchema,
28809
29852
  communityMachineStatusSchema,
28810
29853
  communityMachineUpdatedSchema,
@@ -28851,6 +29894,8 @@ var WS_EVENTS = {
28851
29894
  INBOX_CHANGED: "community:inbox.changed",
28852
29895
  PRESENCE_UPDATE: "community:presence.update",
28853
29896
  STATUS_UPDATE: "community:status.update",
29897
+ IDENTITY_UPDATE: "community:identity.update",
29898
+ PROFILE_UPDATE: "community:profile.update",
28854
29899
  MACHINE_CREATED: "community:machine.created",
28855
29900
  MACHINE_STATUS: "community:machine.status",
28856
29901
  MACHINE_UPDATED: "community:machine.updated",
@@ -29411,23 +30456,43 @@ class WsControlChannel {
29411
30456
  this.ws.send(JSON.stringify(frame));
29412
30457
  }
29413
30458
  resyncOnConnect() {
30459
+ const sendActivities = (activities, counts) => {
30460
+ for (const activity of activities) {
30461
+ this.sendFrame({ type: "agent_activity", ...activity });
30462
+ }
30463
+ this.log.info("resync sent", {
30464
+ ready: counts.ready,
30465
+ sessions: counts.sessions,
30466
+ activities: activities.length,
30467
+ pendingAuditEvents: this.pendingBotAuditEvents.size
30468
+ });
30469
+ };
29414
30470
  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 });
30471
+ const socketAtStart = this.ws;
30472
+ const snapshot = this.resyncProvider();
30473
+ this.sendFrame({ type: "ready", ...snapshot.ready });
30474
+ for (const session2 of snapshot.sessions) {
30475
+ this.sendFrame({ type: "agent_session", ...session2 });
30476
+ }
29422
30477
  for (const frame of this.pendingBotAuditEvents.values())
29423
30478
  this.sendFrame(frame);
29424
30479
  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
- });
30480
+ const activities = snapshot.activities ?? [];
30481
+ const counts = {
30482
+ ready: snapshot.ready.runtimeReport.length,
30483
+ sessions: snapshot.sessions.length
30484
+ };
30485
+ if (activities instanceof Promise) {
30486
+ activities.then((resolved) => {
30487
+ if (this.ws === socketAtStart && this.statusValue === "open") {
30488
+ sendActivities(resolved, counts);
30489
+ }
30490
+ }).catch((err) => {
30491
+ this.log.warn("resync provider failed", { err: describeErr(err) });
30492
+ });
30493
+ } else {
30494
+ sendActivities(activities, counts);
30495
+ }
29431
30496
  }
29432
30497
  for (const hook of this.resyncHooks) {
29433
30498
  try {
@@ -29645,8 +30710,8 @@ class WsControlChannel {
29645
30710
  }
29646
30711
  // src/timeline/timeline.ts
29647
30712
  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";
30713
+ import { createHash as createHash4, randomBytes as randomBytes6 } from "node:crypto";
30714
+ import { basename, dirname as dirname3, join as join11 } from "node:path";
29650
30715
 
29651
30716
  // src/timeline/filelock.ts
29652
30717
  import * as fs6 from "fs";
@@ -29989,7 +31054,7 @@ function scanTimelineFile(filePath) {
29989
31054
  }
29990
31055
  }
29991
31056
  function atomicReplaceTimeline(filePath, lines) {
29992
- const tempPath = join10(dirname3(filePath), `.${basename(filePath)}.${process.pid}.${randomBytes4(12).toString("hex")}.tmp`);
31057
+ const tempPath = join11(dirname3(filePath), `.${basename(filePath)}.${process.pid}.${randomBytes6(12).toString("hex")}.tmp`);
29993
31058
  let fd = null;
29994
31059
  try {
29995
31060
  fd = fs7.openSync(tempPath, "wx", 384);
@@ -30073,14 +31138,14 @@ function readRecentEntries(timelineDir, opts = {}) {
30073
31138
  const filenames = recentFilenames(maxDays, now).reverse();
30074
31139
  const entries = [];
30075
31140
  for (const filename of filenames) {
30076
- entries.push(...readJsonl(join10(timelineDir, filename)));
31141
+ entries.push(...readJsonl(join11(timelineDir, filename)));
30077
31142
  }
30078
31143
  return entries;
30079
31144
  }
30080
31145
  function readResumeControlState(timelineDir) {
30081
31146
  if (timelineDirectoryState(timelineDir) !== "safe")
30082
31147
  return { kind: "missing" };
30083
- const filePath = join10(timelineDir, RESUME_CONTROL_FILENAME);
31148
+ const filePath = join11(timelineDir, RESUME_CONTROL_FILENAME);
30084
31149
  let source;
30085
31150
  try {
30086
31151
  source = fs7.lstatSync(filePath);
@@ -30156,8 +31221,8 @@ function updateResumeControlState(timelineDir, update) {
30156
31221
  `;
30157
31222
  if (Buffer.byteLength(body, "utf8") > RESUME_CONTROL_MAX_BYTES)
30158
31223
  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`);
31224
+ const filePath = join11(timelineDir, RESUME_CONTROL_FILENAME);
31225
+ const tempPath = join11(timelineDir, `.${RESUME_CONTROL_FILENAME}.${process.pid}.${randomBytes6(12).toString("hex")}.tmp`);
30161
31226
  let fd = null;
30162
31227
  try {
30163
31228
  fd = fs7.openSync(tempPath, "wx", 384);
@@ -30187,7 +31252,7 @@ function appendTrackedEntry(timelineDir, entry, now = new Date) {
30187
31252
  if (timelineDirectoryState(timelineDir) !== "safe")
30188
31253
  return { status: "rejected", reason: "unsafe" };
30189
31254
  const filename = filenameForDate(now);
30190
- const filePath = join10(timelineDir, filename);
31255
+ const filePath = join11(timelineDir, filename);
30191
31256
  const lockPath = lockPathFor(timelineDir, filename);
30192
31257
  try {
30193
31258
  if (!acquireLock(lockPath))
@@ -30215,7 +31280,7 @@ function updateTrackedEntry(timelineDir, handle, update) {
30215
31280
  if (!DATE_FILENAME_PATTERN.test(handle.filename) || basename(handle.filename) !== handle.filename) {
30216
31281
  return { status: "rejected", reason: "unsafe" };
30217
31282
  }
30218
- const filePath = join10(timelineDir, handle.filename);
31283
+ const filePath = join11(timelineDir, handle.filename);
30219
31284
  const lockPath = lockPathFor(timelineDir, handle.filename);
30220
31285
  try {
30221
31286
  if (!acquireLock(lockPath))
@@ -30274,10 +31339,10 @@ async function sweepTimelineHistory(workingDirectoryBase, opts = {}) {
30274
31339
  return;
30275
31340
  }
30276
31341
  for (const agentName of agentNames) {
30277
- const agentDir = join10(workingDirectoryBase, agentName);
31342
+ const agentDir = join11(workingDirectoryBase, agentName);
30278
31343
  if (!isRealDirectory(agentDir))
30279
31344
  continue;
30280
- const timelineDir = join10(agentDir, ".context_timeline");
31345
+ const timelineDir = join11(agentDir, ".context_timeline");
30281
31346
  if (!isRealDirectory(timelineDir))
30282
31347
  continue;
30283
31348
  let filenames;
@@ -30287,7 +31352,7 @@ async function sweepTimelineHistory(workingDirectoryBase, opts = {}) {
30287
31352
  continue;
30288
31353
  }
30289
31354
  for (const filename of filenames) {
30290
- const filePath = join10(timelineDir, filename);
31355
+ const filePath = join11(timelineDir, filename);
30291
31356
  let source;
30292
31357
  try {
30293
31358
  source = fs7.lstatSync(filePath);
@@ -30936,7 +32001,12 @@ async function detectRuntimes() {
30936
32001
  const driver = getDriver(id);
30937
32002
  const probe = await driver.probe();
30938
32003
  if (probe.status === "healthy") {
30939
- results.push({ id, status: "healthy", version: probe.version });
32004
+ results.push({
32005
+ id,
32006
+ status: "healthy",
32007
+ version: probe.version,
32008
+ reasoning: probe.reasoning
32009
+ });
30940
32010
  } else {
30941
32011
  results.push({
30942
32012
  id,
@@ -31101,8 +32171,8 @@ class MessageReminderScheduler {
31101
32171
 
31102
32172
  // src/manager/agentDriverHost.ts
31103
32173
  import { randomUUID as randomUUID6 } from "node:crypto";
31104
- import { homedir as homedir3 } from "node:os";
31105
- import { join as join12 } from "node:path";
32174
+ import { homedir as homedir4 } from "node:os";
32175
+ import { join as join13 } from "node:path";
31106
32176
 
31107
32177
  // src/drivers/gitIdentityEnv.ts
31108
32178
  import { execFileSync as execFileSync3 } from "child_process";
@@ -31227,7 +32297,7 @@ function createDaemonAgentDriverHost(ctx, onRawLine) {
31227
32297
  hostUser: readHostGitIdentity() ?? undefined
31228
32298
  }),
31229
32299
  platformProtected: {
31230
- ALOOK_HOME: process.env.ALOOK_HOME ?? join12(homedir3(), ".alook"),
32300
+ ALOOK_HOME: process.env.ALOOK_HOME ?? join13(homedir4(), ".alook"),
31231
32301
  ALOOK_ID: ctx.agentId,
31232
32302
  ALOOK_CLI: ctx.agentCliPath,
31233
32303
  ALOOK_SERVER_URL: ctx.config.serverUrl,
@@ -31332,6 +32402,182 @@ class DaemonSelfSleepScheduler {
31332
32402
  }
31333
32403
  }
31334
32404
 
32405
+ // src/telemetry/dailyTokenUsage.ts
32406
+ import { chmod, mkdir, open, readFile as readFile2, rename, rm } from "node:fs/promises";
32407
+ import { dirname as dirname5, join as join14 } from "node:path";
32408
+ import { randomUUID as randomUUID7 } from "node:crypto";
32409
+ function dayKey(at) {
32410
+ return at.toISOString().slice(0, 10);
32411
+ }
32412
+ function retainedDays(at) {
32413
+ const days = new Set;
32414
+ for (let offset = 0;offset < 7; offset += 1) {
32415
+ days.add(dayKey(new Date(at.getTime() - offset * 86400000)));
32416
+ }
32417
+ return days;
32418
+ }
32419
+ function isMetric(value) {
32420
+ return value === null || typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
32421
+ }
32422
+ function isSnapshot(value) {
32423
+ if (!value || typeof value !== "object")
32424
+ return false;
32425
+ const snapshot = value;
32426
+ const metrics = snapshot.metrics;
32427
+ 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);
32428
+ }
32429
+ function mergeMetric(existing, delta, hasExistingSnapshot) {
32430
+ if (delta === null)
32431
+ return null;
32432
+ if (!Number.isSafeInteger(delta) || delta < 0) {
32433
+ throw new RangeError("token usage delta must be a non-negative safe integer");
32434
+ }
32435
+ if (!hasExistingSnapshot)
32436
+ return delta;
32437
+ if (existing === null)
32438
+ return null;
32439
+ const sum = existing + delta;
32440
+ if (!Number.isSafeInteger(sum))
32441
+ throw new RangeError("daily token usage exceeds safe integer range");
32442
+ return sum;
32443
+ }
32444
+ function emptySnapshot(botId, day) {
32445
+ return {
32446
+ botId,
32447
+ day,
32448
+ metrics: {
32449
+ input: null,
32450
+ output: null,
32451
+ cache: null
32452
+ }
32453
+ };
32454
+ }
32455
+
32456
+ class DailyTokenUsageStore {
32457
+ now;
32458
+ tail = Promise.resolve();
32459
+ loaded = false;
32460
+ data = { version: 1, bots: {} };
32461
+ filePath;
32462
+ constructor(workingDirectoryBase, now = () => new Date) {
32463
+ this.now = now;
32464
+ this.filePath = join14(workingDirectoryBase, ".telemetry", "daily-token-usage.json");
32465
+ }
32466
+ record(botId, delta) {
32467
+ return this.enqueue(async () => {
32468
+ await this.load();
32469
+ const at = this.now();
32470
+ this.prune(at);
32471
+ const day = dayKey(at);
32472
+ const snapshots = this.data.bots[botId] ?? [];
32473
+ const existing = snapshots.find((snapshot) => snapshot.day === day);
32474
+ const next = existing ?? emptySnapshot(botId, day);
32475
+ next.metrics = {
32476
+ input: mergeMetric(next.metrics.input, delta.input, existing !== undefined),
32477
+ output: mergeMetric(next.metrics.output, delta.output, existing !== undefined),
32478
+ cache: mergeMetric(next.metrics.cache, delta.cache, existing !== undefined)
32479
+ };
32480
+ if (!existing)
32481
+ snapshots.push(next);
32482
+ snapshots.sort((a, b) => a.day.localeCompare(b.day));
32483
+ this.data.bots[botId] = snapshots;
32484
+ await this.persist();
32485
+ });
32486
+ }
32487
+ snapshots(botId) {
32488
+ let result = [];
32489
+ return this.enqueue(async () => {
32490
+ await this.load();
32491
+ if (this.prune(this.now()))
32492
+ await this.persist();
32493
+ result = (this.data.bots[botId] ?? []).map((snapshot) => structuredClone(snapshot));
32494
+ }).then(() => result);
32495
+ }
32496
+ enqueue(operation) {
32497
+ const result = this.tail.then(operation, operation);
32498
+ this.tail = result.then(() => {
32499
+ return;
32500
+ }, () => {
32501
+ return;
32502
+ });
32503
+ return result;
32504
+ }
32505
+ async load() {
32506
+ if (this.loaded)
32507
+ return;
32508
+ let source;
32509
+ try {
32510
+ source = await readFile2(this.filePath, "utf8");
32511
+ } catch (error51) {
32512
+ if (!error51 || typeof error51 !== "object" || !("code" in error51) || error51.code !== "ENOENT") {
32513
+ throw error51;
32514
+ }
32515
+ this.data = { version: 1, bots: {} };
32516
+ this.loaded = true;
32517
+ return;
32518
+ }
32519
+ const parsed = JSON.parse(source);
32520
+ if (!parsed || typeof parsed !== "object" || parsed.version !== 1) {
32521
+ throw new Error("invalid daily token usage file version");
32522
+ }
32523
+ const bots = parsed.bots;
32524
+ if (!bots || typeof bots !== "object" || Array.isArray(bots)) {
32525
+ throw new Error("invalid daily token usage bots map");
32526
+ }
32527
+ const valid = {};
32528
+ for (const [botId, value] of Object.entries(bots)) {
32529
+ if (!Array.isArray(value) || !value.every(isSnapshot) || value.some((snapshot) => snapshot.botId !== botId)) {
32530
+ throw new Error(`invalid daily token usage snapshots for bot ${botId}`);
32531
+ }
32532
+ if (value.length > 0) {
32533
+ valid[botId] = value;
32534
+ }
32535
+ }
32536
+ this.data = { version: 1, bots: valid };
32537
+ this.loaded = true;
32538
+ }
32539
+ prune(at) {
32540
+ const keep = retainedDays(at);
32541
+ let changed = false;
32542
+ for (const [botId, snapshots] of Object.entries(this.data.bots)) {
32543
+ const retained = snapshots.filter((snapshot) => keep.has(snapshot.day)).sort((a, b) => a.day.localeCompare(b.day)).slice(-7);
32544
+ if (retained.length !== snapshots.length || retained.some((snapshot, index2) => snapshot !== snapshots[index2]))
32545
+ changed = true;
32546
+ if (retained.length === 0)
32547
+ delete this.data.bots[botId];
32548
+ else
32549
+ this.data.bots[botId] = retained;
32550
+ }
32551
+ return changed;
32552
+ }
32553
+ async persist() {
32554
+ const directory = dirname5(this.filePath);
32555
+ await mkdir(directory, { recursive: true, mode: 448 });
32556
+ const temporary = `${this.filePath}.${randomUUID7()}.tmp`;
32557
+ try {
32558
+ const file2 = await open(temporary, "wx", 384);
32559
+ try {
32560
+ await file2.writeFile(JSON.stringify(this.data), { encoding: "utf8" });
32561
+ await file2.sync();
32562
+ } finally {
32563
+ await file2.close();
32564
+ }
32565
+ await rename(temporary, this.filePath);
32566
+ await chmod(this.filePath, 384);
32567
+ try {
32568
+ const directoryHandle = await open(directory, "r");
32569
+ try {
32570
+ await directoryHandle.sync();
32571
+ } finally {
32572
+ await directoryHandle.close();
32573
+ }
32574
+ } catch {}
32575
+ } catch (error51) {
32576
+ await rm(temporary, { force: true }).catch(() => {});
32577
+ throw error51;
32578
+ }
32579
+ }
32580
+ }
31335
32581
  // src/daemon/createDaemon.ts
31336
32582
  var WARMUP_BACKOFF_MS = [250, 500, 1000, 2000, 4000];
31337
32583
  var WARMUP_CEILING_MS = 30000;
@@ -31469,9 +32715,25 @@ function createBuiltinDaemonSessionFactory(onRuntimeRawLine) {
31469
32715
  }
31470
32716
  async function createDaemon(opts) {
31471
32717
  const log2 = opts.logger ?? createLogger({ header: "@alook/daemon" });
31472
- const fallbackBase = (process.env.ALOOK_PROJECT_ROOT || `${homedir4()}/.alook`) + "/daemon";
32718
+ const fallbackBase = (process.env.ALOOK_PROJECT_ROOT || `${homedir5()}/.alook`) + "/daemon";
31473
32719
  const workingDirectoryBase = opts.workingDirectoryBase ?? fallbackBase;
31474
32720
  const workdirFor = (agentId) => `${workingDirectoryBase}/${agentId}`;
32721
+ const dailyTokenUsage2 = new DailyTokenUsageStore(workingDirectoryBase);
32722
+ const providerQuotaReader = opts.providerQuotaReader ?? (opts.sessionFactory ? async () => null : readBuiltinProviderQuota);
32723
+ const providerQuotaByBackend = new Map;
32724
+ let requestReadyQuotaResend = () => {};
32725
+ const recordProviderQuota = (backendId, quota) => {
32726
+ const previous = providerQuotaByBackend.get(backendId);
32727
+ if (previous?.observation.status === "available" && quota.status === "error" && previous.observation.sourceEpoch === quota.sourceEpoch)
32728
+ return;
32729
+ providerQuotaByBackend.set(backendId, {
32730
+ agentBackendId: backendId,
32731
+ observation: structuredClone(quota)
32732
+ });
32733
+ if (previous && previous.observation.sourceEpoch !== quota.sourceEpoch) {
32734
+ requestReadyQuotaResend();
32735
+ }
32736
+ };
31475
32737
  sweepTimelineHistory(workingDirectoryBase).catch(() => {
31476
32738
  log2.warn("timeline startup sweep failed");
31477
32739
  });
@@ -31487,6 +32749,27 @@ async function createDaemon(opts) {
31487
32749
  });
31488
32750
  let channelRef = null;
31489
32751
  let managerRef = null;
32752
+ const providerQuotaSnapshots = () => [...providerQuotaByBackend.values()].map((snapshot) => structuredClone(snapshot));
32753
+ const activityPayload = async (info) => {
32754
+ if (info.state !== "idle")
32755
+ return info;
32756
+ const backendId = managerRef?.agentBackendId(info.agentId);
32757
+ if (backendId === "claude") {
32758
+ const observed = await providerQuotaReader("claude");
32759
+ if (observed)
32760
+ recordProviderQuota("claude", observed);
32761
+ }
32762
+ const quota = backendId === "claude" || backendId === "codex" ? providerQuotaByBackend.get(backendId) : undefined;
32763
+ const dailyUsage = await dailyTokenUsage2.snapshots(info.agentId);
32764
+ return {
32765
+ ...info,
32766
+ ...dailyUsage.length > 0 ? { dailyUsage } : {},
32767
+ ...quota ? { quota: structuredClone(quota) } : {}
32768
+ };
32769
+ };
32770
+ let reportAgentActivity = (info) => {
32771
+ channelRef?.reportAgentActivity?.(info);
32772
+ };
31490
32773
  let reminderSchedulerRef = null;
31491
32774
  const selfSleepScheduler = opts.onSelfSleep ? new DaemonSelfSleepScheduler({
31492
32775
  onSleep: opts.onSelfSleep,
@@ -31551,7 +32834,7 @@ async function createDaemon(opts) {
31551
32834
  function reassertAgentActivity(agentId) {
31552
32835
  const state = managerRef?.agentActivity(agentId);
31553
32836
  if (state)
31554
- channel2.reportAgentActivity?.({ agentId, state });
32837
+ reportAgentActivity({ agentId, state });
31555
32838
  }
31556
32839
  function startTypingHeartbeat(agentId) {
31557
32840
  stopTypingHeartbeat(agentId);
@@ -31695,6 +32978,20 @@ async function createDaemon(opts) {
31695
32978
  logger: log2.child("ws")
31696
32979
  });
31697
32980
  channelRef = channel2;
32981
+ const activityReportTails = new Map;
32982
+ reportAgentActivity = (info) => {
32983
+ const prior = activityReportTails.get(info.agentId) ?? Promise.resolve();
32984
+ const next = prior.then(async () => {
32985
+ await channel2.reportAgentActivity(await activityPayload(info));
32986
+ }).catch(() => {
32987
+ log2.warn("agent activity telemetry report failed", { agentId: info.agentId, state: info.state });
32988
+ });
32989
+ activityReportTails.set(info.agentId, next);
32990
+ next.finally(() => {
32991
+ if (activityReportTails.get(info.agentId) === next)
32992
+ activityReportTails.delete(info.agentId);
32993
+ });
32994
+ };
31698
32995
  function restorePendingIdleResetEvents(agentId) {
31699
32996
  for (const pending of timeline2.pendingIdleResetEvents(agentId)) {
31700
32997
  channel2.restorePendingBotAuditEvent({
@@ -31813,7 +33110,7 @@ async function createDaemon(opts) {
31813
33110
  onAgentSession: (info) => void channel2.reportAgentSession(info),
31814
33111
  onAgentActivity: (info) => {
31815
33112
  selfSleepScheduler?.observeAgentActivity(info.agentId, info.state === "running");
31816
- channel2.reportAgentActivity?.(info);
33113
+ reportAgentActivity(info);
31817
33114
  if (info.state === "starting" || info.state === "running") {
31818
33115
  if (!typingHeartbeats.has(info.agentId)) {
31819
33116
  startTypingHeartbeat(info.agentId);
@@ -31822,6 +33119,16 @@ async function createDaemon(opts) {
31822
33119
  emitTypingStopsAndClear(info.agentId);
31823
33120
  }
31824
33121
  },
33122
+ onTokenUsage: ({ agentId, usage }) => {
33123
+ dailyTokenUsage2.record(agentId, usage).catch(() => {
33124
+ log2.warn("daily token usage persistence failed", { agentId });
33125
+ });
33126
+ },
33127
+ onProviderQuota: ({ backendId, quota }) => {
33128
+ if (backendId !== "claude" && backendId !== "codex")
33129
+ return;
33130
+ recordProviderQuota(backendId, quota);
33131
+ },
31825
33132
  onBotAuditEvent: (agentId, event, context) => emitBotAuditEvent(agentId, event, context),
31826
33133
  onAgentLocallyStopped: (info) => router?.markLocallyStopped(info.agentId),
31827
33134
  onRuntimeRawLine,
@@ -31868,6 +33175,11 @@ async function createDaemon(opts) {
31868
33175
  arch: opts.arch,
31869
33176
  osRelease: opts.osRelease,
31870
33177
  daemonVersion: opts.daemonVersion,
33178
+ providerQuotas: providerQuotaSnapshots,
33179
+ resyncActivities: async () => {
33180
+ const activities = await Promise.all(manager.liveAgentActivities().map((info) => activityPayload(info)));
33181
+ return activities.filter((activity) => manager.agentActivity(activity.agentId) === activity.state);
33182
+ },
31871
33183
  typingTracker,
31872
33184
  logger: log2.child("router"),
31873
33185
  onBeforeAgent: async (agentId) => {
@@ -31883,6 +33195,10 @@ async function createDaemon(opts) {
31883
33195
  await enrollAgent(agentId);
31884
33196
  }
31885
33197
  });
33198
+ requestReadyQuotaResend = () => {
33199
+ if (router)
33200
+ channel2.sendReady?.(router.buildReady());
33201
+ };
31886
33202
  channel2.onCommand(createSelfUpdateCommandListener(opts.handleSelfUpdate));
31887
33203
  channel2.onCommand(createDiagnosticsCommandListener({
31888
33204
  handleDiagnosticCommand: opts.handleDiagnosticCommand,
@@ -31912,6 +33228,11 @@ async function createDaemon(opts) {
31912
33228
  resyncPendingWakes();
31913
33229
  resyncPendingDiagnostics();
31914
33230
  });
33231
+ if (opts.runtimeReport.some((runtime) => runtime.id === "claude")) {
33232
+ const observed = await providerQuotaReader("claude");
33233
+ if (observed)
33234
+ recordProviderQuota("claude", observed);
33235
+ }
31915
33236
  channel2.connect();
31916
33237
  await router.start();
31917
33238
  selfSleepScheduler?.start();