@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.
- package/dist/cli/index.js +1567 -194
- package/dist/index.js +1525 -204
- 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
|
|
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
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
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
|
|
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?.
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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 ??
|
|
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
|
-
|
|
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_${
|
|
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_${
|
|
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:
|
|
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,
|
|
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 {
|
|
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
|
-
|
|
5975
|
-
|
|
6584
|
+
if (response.status === 401 || response.status === 403) {
|
|
6585
|
+
return { status: "error", sourceEpoch: claudeSourceEpoch, code: "unauthorized", retryable: false };
|
|
5976
6586
|
}
|
|
5977
|
-
|
|
5978
|
-
|
|
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
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
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
|
-
|
|
5997
|
-
|
|
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
|
-
|
|
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, {
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
29416
|
-
this.
|
|
29417
|
-
|
|
29418
|
-
|
|
29419
|
-
|
|
29420
|
-
|
|
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
|
-
|
|
29426
|
-
|
|
29427
|
-
|
|
29428
|
-
|
|
29429
|
-
|
|
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
|
|
29649
|
-
import { basename, dirname as dirname3, join as
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
30160
|
-
const tempPath =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
31342
|
+
const agentDir = join11(workingDirectoryBase, agentName);
|
|
30278
31343
|
if (!isRealDirectory(agentDir))
|
|
30279
31344
|
continue;
|
|
30280
|
-
const timelineDir =
|
|
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 =
|
|
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({
|
|
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
|
|
31105
|
-
import { join as
|
|
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 ??
|
|
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 || `${
|
|
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
|
-
|
|
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
|
-
|
|
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();
|