@alook/daemon 0.1.23 → 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 +1623 -201
- package/dist/index.js +1575 -205
- package/package.json +4 -2
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 "));
|
|
@@ -5971,37 +6444,176 @@ class RuntimeNotificationState {
|
|
|
5971
6444
|
return identity.length === 0 || !this.contributedIdentities.has(identity);
|
|
5972
6445
|
});
|
|
5973
6446
|
}
|
|
5974
|
-
add(count = 1) {
|
|
5975
|
-
this.pendingCountValue += count;
|
|
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)
|
|
6580
|
+
});
|
|
6581
|
+
} catch {
|
|
6582
|
+
return { status: "error", sourceEpoch: claudeSourceEpoch, code: "network", retryable: true };
|
|
6583
|
+
}
|
|
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
|
|
@@ -27048,12 +27910,13 @@ function writeStatusFile(path9, snapshot) {
|
|
|
27048
27910
|
} catch {}
|
|
27049
27911
|
}
|
|
27050
27912
|
// ../shared/src/constants/community.ts
|
|
27913
|
+
var MAX_FOLDER_NAME_LENGTH = 100;
|
|
27051
27914
|
var MAX_PROFILE_NAME_LENGTH = 100;
|
|
27052
27915
|
var MAX_PROFILE_ABOUT_LENGTH = 1000;
|
|
27053
27916
|
var MAX_MESSAGE_CONTENT_LENGTH = 4000;
|
|
27054
27917
|
var MAX_ATTACHMENTS_PER_MESSAGE = 10;
|
|
27055
27918
|
var MAX_ATTACHMENT_SIZE_BYTES = 25 * 1024 * 1024;
|
|
27056
|
-
var MAX_ATTACHMENT_THUMBNAIL_SIZE_BYTES =
|
|
27919
|
+
var MAX_ATTACHMENT_THUMBNAIL_SIZE_BYTES = 512 * 1024;
|
|
27057
27920
|
var MAX_SERVER_ICON_SIZE_BYTES = 5 * 1024 * 1024;
|
|
27058
27921
|
var ALLOWED_ICON_MIME_TYPES = [
|
|
27059
27922
|
"image/png",
|
|
@@ -27062,6 +27925,92 @@ var ALLOWED_ICON_MIME_TYPES = [
|
|
|
27062
27925
|
"image/gif"
|
|
27063
27926
|
];
|
|
27064
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();
|
|
27065
28014
|
// ../shared/src/utils/slug.ts
|
|
27066
28015
|
init_nanoid();
|
|
27067
28016
|
var slugId = customAlphabet("0123456789abcdefghijklmnopqrstuvwxyz");
|
|
@@ -27218,10 +28167,13 @@ var ActivateTokenRuntimeSchema = exports_external.object({
|
|
|
27218
28167
|
type: exports_external.string().min(1),
|
|
27219
28168
|
version: exports_external.string().optional().default("")
|
|
27220
28169
|
});
|
|
28170
|
+
var COMMUNITY_RUNTIME_ID_MAX = 64;
|
|
28171
|
+
var COMMUNITY_RUNTIME_VERSION_MAX = 64;
|
|
28172
|
+
var COMMUNITY_RUNTIME_LIST_MAX = 64;
|
|
27221
28173
|
var ActivateTokenRequestSchema = exports_external.object({
|
|
27222
28174
|
token: exports_external.string().min(1),
|
|
27223
28175
|
hostname: exports_external.string().min(1),
|
|
27224
|
-
runtimes: exports_external.array(ActivateTokenRuntimeSchema).min(1)
|
|
28176
|
+
runtimes: exports_external.array(ActivateTokenRuntimeSchema).min(1).max(COMMUNITY_RUNTIME_LIST_MAX)
|
|
27225
28177
|
});
|
|
27226
28178
|
var RegisterDaemonRequestSchema = exports_external.object({
|
|
27227
28179
|
workspace_id: exports_external.string().min(1).optional(),
|
|
@@ -27229,7 +28181,7 @@ var RegisterDaemonRequestSchema = exports_external.object({
|
|
|
27229
28181
|
device_name: exports_external.string().optional().default(""),
|
|
27230
28182
|
cli_version: exports_external.string().optional().default(""),
|
|
27231
28183
|
workspaces_root: exports_external.string().optional().default(""),
|
|
27232
|
-
runtimes: exports_external.array(DaemonRuntimeItemSchema).min(1)
|
|
28184
|
+
runtimes: exports_external.array(DaemonRuntimeItemSchema).min(1).max(COMMUNITY_RUNTIME_LIST_MAX)
|
|
27233
28185
|
});
|
|
27234
28186
|
var DeregisterRequestSchema = exports_external.object({
|
|
27235
28187
|
daemon_id: exports_external.string().min(1)
|
|
@@ -27572,16 +28524,61 @@ var CreateThreadRequestSchema = exports_external.object({
|
|
|
27572
28524
|
content: exports_external.string().optional().default(""),
|
|
27573
28525
|
attachment_ids: exports_external.array(exports_external.string()).optional()
|
|
27574
28526
|
});
|
|
27575
|
-
var COMMUNITY_RUNTIME_ID_MAX = 64;
|
|
27576
|
-
var COMMUNITY_RUNTIME_VERSION_MAX = 64;
|
|
27577
|
-
var COMMUNITY_RUNTIME_LIST_MAX = 64;
|
|
27578
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
|
+
});
|
|
27579
28575
|
var CommunityMachineRuntimeSchema = exports_external.object({
|
|
27580
28576
|
id: exports_external.string().min(1).max(COMMUNITY_RUNTIME_ID_MAX).regex(RUNTIME_ID_RE, "invalid runtime id charset"),
|
|
27581
28577
|
version: exports_external.string().max(COMMUNITY_RUNTIME_VERSION_MAX).optional(),
|
|
27582
28578
|
status: exports_external.enum(["healthy", "unhealthy"]).catch("healthy").default("healthy"),
|
|
27583
28579
|
lastError: exports_external.string().max(128).optional(),
|
|
27584
|
-
lastErrorAt: exports_external.string().optional()
|
|
28580
|
+
lastErrorAt: exports_external.string().optional(),
|
|
28581
|
+
reasoning: RuntimeReasoningCatalogSchema.optional().catch(undefined)
|
|
27585
28582
|
});
|
|
27586
28583
|
var CommunityMachineRuntimeListSchema = exports_external.array(CommunityMachineRuntimeSchema).max(COMMUNITY_RUNTIME_LIST_MAX).transform((list) => {
|
|
27587
28584
|
const seen = new Set;
|
|
@@ -27594,6 +28591,17 @@ var CommunityMachineRuntimeListSchema = exports_external.array(CommunityMachineR
|
|
|
27594
28591
|
}
|
|
27595
28592
|
return out;
|
|
27596
28593
|
});
|
|
28594
|
+
var CommunityRunningAgentListSchema = exports_external.array(exports_external.string()).transform((list) => {
|
|
28595
|
+
const seen = new Set;
|
|
28596
|
+
const out = [];
|
|
28597
|
+
for (const id of list) {
|
|
28598
|
+
if (seen.has(id))
|
|
28599
|
+
continue;
|
|
28600
|
+
seen.add(id);
|
|
28601
|
+
out.push(id);
|
|
28602
|
+
}
|
|
28603
|
+
return out;
|
|
28604
|
+
});
|
|
27597
28605
|
var CommunityMachineSummarySchema = exports_external.object({
|
|
27598
28606
|
id: exports_external.string(),
|
|
27599
28607
|
hostname: exports_external.string(),
|
|
@@ -27617,16 +28625,17 @@ var HostReadyMessageSchema = exports_external.object({
|
|
|
27617
28625
|
type: exports_external.literal("ready"),
|
|
27618
28626
|
runtimeReport: CommunityMachineRuntimeListSchema,
|
|
27619
28627
|
capabilities: exports_external.array(exports_external.string().min(1).max(64)).max(16).optional().default([]),
|
|
27620
|
-
runningAgents:
|
|
28628
|
+
runningAgents: CommunityRunningAgentListSchema.default([]),
|
|
27621
28629
|
hostname: exports_external.string().optional(),
|
|
27622
28630
|
platform: exports_external.string().optional(),
|
|
27623
28631
|
arch: exports_external.string().optional(),
|
|
27624
28632
|
osRelease: exports_external.string().optional(),
|
|
27625
|
-
daemonVersion: exports_external.string().optional()
|
|
28633
|
+
daemonVersion: exports_external.string().optional(),
|
|
28634
|
+
providerQuotas: exports_external.array(ProviderQuotaSnapshotSchema).max(2).optional().default([])
|
|
27626
28635
|
});
|
|
27627
28636
|
var CommunityDaemonReadySchema = exports_external.object({
|
|
27628
28637
|
runtimeReport: CommunityMachineRuntimeListSchema.optional(),
|
|
27629
|
-
runningAgents:
|
|
28638
|
+
runningAgents: CommunityRunningAgentListSchema.default([]),
|
|
27630
28639
|
hostname: exports_external.string().optional(),
|
|
27631
28640
|
os: exports_external.string().optional(),
|
|
27632
28641
|
arch: exports_external.string().optional(),
|
|
@@ -27643,7 +28652,9 @@ var SessionErrorFrameSchema = exports_external.object({
|
|
|
27643
28652
|
var AgentActivityMessageSchema = exports_external.object({
|
|
27644
28653
|
type: exports_external.literal("agent_activity"),
|
|
27645
28654
|
agentId: exports_external.string(),
|
|
27646
|
-
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()
|
|
27647
28658
|
});
|
|
27648
28659
|
var AgentTypingMessageSchema = exports_external.object({
|
|
27649
28660
|
type: exports_external.literal("agent_typing"),
|
|
@@ -27718,15 +28729,17 @@ var CommunityBotCreateRequestSchema = exports_external.object({
|
|
|
27718
28729
|
machineId: exports_external.string().min(1),
|
|
27719
28730
|
runtime: exports_external.string().min(1),
|
|
27720
28731
|
image: BotImageUrlSchema.optional(),
|
|
27721
|
-
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()
|
|
27722
28734
|
});
|
|
27723
28735
|
var CommunityBotPatchRequestSchema = exports_external.object({
|
|
27724
28736
|
name: exports_external.string().trim().min(COMMUNITY_BOT_NAME_MIN).max(COMMUNITY_BOT_NAME_MAX).refine(isMentionSafeName, { message: MENTION_SAFE_NAME_MSG }).optional(),
|
|
27725
28737
|
description: exports_external.string().max(COMMUNITY_BOT_DESCRIPTION_MAX).optional(),
|
|
27726
28738
|
image: BotImageUrlSchema.nullable().optional(),
|
|
27727
28739
|
model: exports_external.string().trim().min(1).max(100).nullable().optional(),
|
|
27728
|
-
runtime: exports_external.string().trim().min(1).max(COMMUNITY_RUNTIME_ID_MAX).optional()
|
|
27729
|
-
|
|
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), {
|
|
27730
28743
|
message: "at least one field must be provided"
|
|
27731
28744
|
});
|
|
27732
28745
|
var CommunityBotAddToServerRequestSchema = exports_external.object({
|
|
@@ -27928,6 +28941,7 @@ __export(exports_community_schema, {
|
|
|
27928
28941
|
communityChannelMember: () => communityChannelMember,
|
|
27929
28942
|
communityChannel: () => communityChannel,
|
|
27930
28943
|
communityCategory: () => communityCategory,
|
|
28944
|
+
communityBotDailyTokenUsage: () => communityBotDailyTokenUsage,
|
|
27931
28945
|
communityBotDailyActivity: () => communityBotDailyActivity,
|
|
27932
28946
|
communityBotApprovalRequest: () => communityBotApprovalRequest,
|
|
27933
28947
|
communityBotActivityEvent: () => communityBotActivityEvent,
|
|
@@ -28180,6 +29194,17 @@ var communityBotDailyActivity = sqliteTable("community_bot_daily_activity", {
|
|
|
28180
29194
|
handledCount: integer2("handled_count").notNull().default(0),
|
|
28181
29195
|
sentCount: integer2("sent_count").notNull().default(0)
|
|
28182
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
|
+
]);
|
|
28183
29208
|
var communityMessageMark = sqliteTable("community_message_mark", {
|
|
28184
29209
|
id: text("id").primaryKey().$defaultFn(() => nanoid3()),
|
|
28185
29210
|
userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
@@ -28304,7 +29329,8 @@ var listedMessageProjection = {
|
|
|
28304
29329
|
clientNonce: communityMessage.clientNonce,
|
|
28305
29330
|
authorName: user.name,
|
|
28306
29331
|
authorEmail: user.email,
|
|
28307
|
-
authorImage: user.image
|
|
29332
|
+
authorImage: user.image,
|
|
29333
|
+
authorAvatarVersion: user.avatarVersion
|
|
28308
29334
|
};
|
|
28309
29335
|
|
|
28310
29336
|
// ../shared/src/db/queries/user.ts
|
|
@@ -28314,6 +29340,7 @@ var publicUserColumns = {
|
|
|
28314
29340
|
email: user.email,
|
|
28315
29341
|
emailVerified: user.emailVerified,
|
|
28316
29342
|
image: user.image,
|
|
29343
|
+
avatarVersion: user.avatarVersion,
|
|
28317
29344
|
createdAt: user.createdAt,
|
|
28318
29345
|
updatedAt: user.updatedAt,
|
|
28319
29346
|
discriminator: user.discriminator
|
|
@@ -28324,6 +29351,12 @@ var internalUserColumns = {
|
|
|
28324
29351
|
ownerUserId: user.ownerUserId,
|
|
28325
29352
|
deletedAt: user.deletedAt
|
|
28326
29353
|
};
|
|
29354
|
+
var avatarPublishColumns = {
|
|
29355
|
+
id: user.id,
|
|
29356
|
+
image: user.image,
|
|
29357
|
+
avatarVersion: user.avatarVersion,
|
|
29358
|
+
avatarObjectKey: user.avatarObjectKey
|
|
29359
|
+
};
|
|
28327
29360
|
|
|
28328
29361
|
// ../shared/src/db/queries/community/channel.ts
|
|
28329
29362
|
var CHANNEL_COLUMNS = {
|
|
@@ -28376,7 +29409,8 @@ var friendApprovalProfileSchema = exports_external.strictObject({
|
|
|
28376
29409
|
id: string4,
|
|
28377
29410
|
name: string4,
|
|
28378
29411
|
discriminator: string4,
|
|
28379
|
-
image: nullableString
|
|
29412
|
+
image: nullableString,
|
|
29413
|
+
avatarVersion: exports_external.number().int().nonnegative()
|
|
28380
29414
|
});
|
|
28381
29415
|
var FriendApprovalPayloadSchema = exports_external.strictObject({
|
|
28382
29416
|
friendshipId: string4,
|
|
@@ -28402,6 +29436,7 @@ var messageSchema = exports_external.strictObject({
|
|
|
28402
29436
|
authorId: string4,
|
|
28403
29437
|
authorName: string4,
|
|
28404
29438
|
authorAvatar: string4.optional(),
|
|
29439
|
+
authorAvatarVersion: exports_external.number().int().nonnegative(),
|
|
28405
29440
|
content: string4,
|
|
28406
29441
|
type: exports_external.enum(["chat", "system"]),
|
|
28407
29442
|
systemKind: exports_external.literal("thread").optional(),
|
|
@@ -28409,6 +29444,7 @@ var messageSchema = exports_external.strictObject({
|
|
|
28409
29444
|
replyToId: nullableString.optional(),
|
|
28410
29445
|
replyTo: exports_external.strictObject({
|
|
28411
29446
|
id: string4,
|
|
29447
|
+
authorId: string4.optional(),
|
|
28412
29448
|
authorName: string4,
|
|
28413
29449
|
text: string4,
|
|
28414
29450
|
deleted: exports_external.boolean().optional()
|
|
@@ -28603,6 +29639,7 @@ var communityMemberJoinSchema = exports_external.strictObject({
|
|
|
28603
29639
|
name: string4,
|
|
28604
29640
|
discriminator: string4,
|
|
28605
29641
|
avatar: string4.optional(),
|
|
29642
|
+
avatarVersion: exports_external.number().int().nonnegative(),
|
|
28606
29643
|
role: string4,
|
|
28607
29644
|
joinedAt: string4
|
|
28608
29645
|
})
|
|
@@ -28705,6 +29742,22 @@ var communityStatusUpdateSchema = exports_external.strictObject({
|
|
|
28705
29742
|
statusEmoji: nullableString,
|
|
28706
29743
|
statusText: nullableString
|
|
28707
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
|
+
});
|
|
28708
29761
|
var machineRuntimeSchema = CommunityMachineRuntimeSchema.strict();
|
|
28709
29762
|
var CommunityMachineSummarySchema2 = exports_external.strictObject({
|
|
28710
29763
|
id: string4,
|
|
@@ -28793,6 +29846,8 @@ var CommunityWsEventDiscriminatedSchema = exports_external.discriminatedUnion("t
|
|
|
28793
29846
|
communityInboxChangedSchema,
|
|
28794
29847
|
communityPresenceUpdateSchema,
|
|
28795
29848
|
communityStatusUpdateSchema,
|
|
29849
|
+
communityIdentityUpdateSchema,
|
|
29850
|
+
communityProfileUpdateSchema,
|
|
28796
29851
|
communityMachineCreatedSchema,
|
|
28797
29852
|
communityMachineStatusSchema,
|
|
28798
29853
|
communityMachineUpdatedSchema,
|
|
@@ -28839,6 +29894,8 @@ var WS_EVENTS = {
|
|
|
28839
29894
|
INBOX_CHANGED: "community:inbox.changed",
|
|
28840
29895
|
PRESENCE_UPDATE: "community:presence.update",
|
|
28841
29896
|
STATUS_UPDATE: "community:status.update",
|
|
29897
|
+
IDENTITY_UPDATE: "community:identity.update",
|
|
29898
|
+
PROFILE_UPDATE: "community:profile.update",
|
|
28842
29899
|
MACHINE_CREATED: "community:machine.created",
|
|
28843
29900
|
MACHINE_STATUS: "community:machine.status",
|
|
28844
29901
|
MACHINE_UPDATED: "community:machine.updated",
|
|
@@ -28915,6 +29972,43 @@ var COMMUNITY_BROWSER_EVENT_BATCH_ENVELOPE_BYTES = 1024;
|
|
|
28915
29972
|
var COMMUNITY_BROWSER_EVENT_BATCH_MAX_BYTES = MESSAGE_DELIVERY_MAX_EVENTS_PER_USER * COMMUNITY_BROWSER_EVENT_MAX_BYTES + COMMUNITY_BROWSER_EVENT_BATCH_ENVELOPE_BYTES;
|
|
28916
29973
|
// ../shared/src/db/index.ts
|
|
28917
29974
|
var allSchema = { ...exports_schema, ...exports_community_schema, ...exports_community_machine_schema };
|
|
29975
|
+
// ../shared/src/community-server-rail.ts
|
|
29976
|
+
var MAX_SERVER_RAIL_COMMANDS = 3;
|
|
29977
|
+
var idSchema = exports_external.string().trim().min(1);
|
|
29978
|
+
var idsSchema = exports_external.array(idSchema);
|
|
29979
|
+
var reorderServersSchema = exports_external.strictObject({
|
|
29980
|
+
kind: exports_external.literal("reorder-servers"),
|
|
29981
|
+
serverIds: idsSchema
|
|
29982
|
+
});
|
|
29983
|
+
var reorderFoldersSchema = exports_external.strictObject({
|
|
29984
|
+
kind: exports_external.literal("reorder-folders"),
|
|
29985
|
+
folderIds: idsSchema
|
|
29986
|
+
});
|
|
29987
|
+
var replaceFolderItemsSchema = exports_external.strictObject({
|
|
29988
|
+
kind: exports_external.literal("replace-folder-items"),
|
|
29989
|
+
folderId: idSchema,
|
|
29990
|
+
serverIds: idsSchema.min(1)
|
|
29991
|
+
});
|
|
29992
|
+
var deleteFolderSchema = exports_external.strictObject({
|
|
29993
|
+
kind: exports_external.literal("delete-folder"),
|
|
29994
|
+
folderId: idSchema
|
|
29995
|
+
});
|
|
29996
|
+
var createFolderSchema = exports_external.strictObject({
|
|
29997
|
+
kind: exports_external.literal("create-folder"),
|
|
29998
|
+
clientId: idSchema,
|
|
29999
|
+
name: exports_external.string().trim().min(1).max(MAX_FOLDER_NAME_LENGTH),
|
|
30000
|
+
serverIds: idsSchema.min(1)
|
|
30001
|
+
});
|
|
30002
|
+
var serverRailCommandSchema = exports_external.discriminatedUnion("kind", [
|
|
30003
|
+
reorderServersSchema,
|
|
30004
|
+
reorderFoldersSchema,
|
|
30005
|
+
replaceFolderItemsSchema,
|
|
30006
|
+
deleteFolderSchema,
|
|
30007
|
+
createFolderSchema
|
|
30008
|
+
]);
|
|
30009
|
+
var serverRailCommitRequestSchema = exports_external.strictObject({
|
|
30010
|
+
commands: exports_external.array(serverRailCommandSchema).min(1).max(MAX_SERVER_RAIL_COMMANDS)
|
|
30011
|
+
});
|
|
28918
30012
|
// ../shared/src/db/queries/task.ts
|
|
28919
30013
|
var DEFAULT_STALE_SECONDS = Number(process.env.ALOOK_STALE_DISPATCH_TIMEOUT_S) || 20;
|
|
28920
30014
|
var DEFAULT_STALE_RUNNING_SECONDS = Number(process.env.ALOOK_STALE_RUNNING_TIMEOUT_S) || 3600;
|
|
@@ -29362,23 +30456,43 @@ class WsControlChannel {
|
|
|
29362
30456
|
this.ws.send(JSON.stringify(frame));
|
|
29363
30457
|
}
|
|
29364
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
|
+
};
|
|
29365
30470
|
if (this.resyncProvider) {
|
|
29366
|
-
const
|
|
29367
|
-
this.
|
|
29368
|
-
|
|
29369
|
-
|
|
29370
|
-
|
|
29371
|
-
|
|
29372
|
-
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
|
+
}
|
|
29373
30477
|
for (const frame of this.pendingBotAuditEvents.values())
|
|
29374
30478
|
this.sendFrame(frame);
|
|
29375
30479
|
this.scheduleAuditRetry();
|
|
29376
|
-
|
|
29377
|
-
|
|
29378
|
-
|
|
29379
|
-
|
|
29380
|
-
|
|
29381
|
-
|
|
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
|
+
}
|
|
29382
30496
|
}
|
|
29383
30497
|
for (const hook of this.resyncHooks) {
|
|
29384
30498
|
try {
|
|
@@ -29596,8 +30710,8 @@ class WsControlChannel {
|
|
|
29596
30710
|
}
|
|
29597
30711
|
// src/timeline/timeline.ts
|
|
29598
30712
|
import * as fs7 from "node:fs";
|
|
29599
|
-
import { createHash as createHash4, randomBytes as
|
|
29600
|
-
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";
|
|
29601
30715
|
|
|
29602
30716
|
// src/timeline/filelock.ts
|
|
29603
30717
|
import * as fs6 from "fs";
|
|
@@ -29940,7 +31054,7 @@ function scanTimelineFile(filePath) {
|
|
|
29940
31054
|
}
|
|
29941
31055
|
}
|
|
29942
31056
|
function atomicReplaceTimeline(filePath, lines) {
|
|
29943
|
-
const tempPath =
|
|
31057
|
+
const tempPath = join11(dirname3(filePath), `.${basename(filePath)}.${process.pid}.${randomBytes6(12).toString("hex")}.tmp`);
|
|
29944
31058
|
let fd = null;
|
|
29945
31059
|
try {
|
|
29946
31060
|
fd = fs7.openSync(tempPath, "wx", 384);
|
|
@@ -30024,14 +31138,14 @@ function readRecentEntries(timelineDir, opts = {}) {
|
|
|
30024
31138
|
const filenames = recentFilenames(maxDays, now).reverse();
|
|
30025
31139
|
const entries = [];
|
|
30026
31140
|
for (const filename of filenames) {
|
|
30027
|
-
entries.push(...readJsonl(
|
|
31141
|
+
entries.push(...readJsonl(join11(timelineDir, filename)));
|
|
30028
31142
|
}
|
|
30029
31143
|
return entries;
|
|
30030
31144
|
}
|
|
30031
31145
|
function readResumeControlState(timelineDir) {
|
|
30032
31146
|
if (timelineDirectoryState(timelineDir) !== "safe")
|
|
30033
31147
|
return { kind: "missing" };
|
|
30034
|
-
const filePath =
|
|
31148
|
+
const filePath = join11(timelineDir, RESUME_CONTROL_FILENAME);
|
|
30035
31149
|
let source;
|
|
30036
31150
|
try {
|
|
30037
31151
|
source = fs7.lstatSync(filePath);
|
|
@@ -30107,8 +31221,8 @@ function updateResumeControlState(timelineDir, update) {
|
|
|
30107
31221
|
`;
|
|
30108
31222
|
if (Buffer.byteLength(body, "utf8") > RESUME_CONTROL_MAX_BYTES)
|
|
30109
31223
|
return false;
|
|
30110
|
-
const filePath =
|
|
30111
|
-
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`);
|
|
30112
31226
|
let fd = null;
|
|
30113
31227
|
try {
|
|
30114
31228
|
fd = fs7.openSync(tempPath, "wx", 384);
|
|
@@ -30138,7 +31252,7 @@ function appendTrackedEntry(timelineDir, entry, now = new Date) {
|
|
|
30138
31252
|
if (timelineDirectoryState(timelineDir) !== "safe")
|
|
30139
31253
|
return { status: "rejected", reason: "unsafe" };
|
|
30140
31254
|
const filename = filenameForDate(now);
|
|
30141
|
-
const filePath =
|
|
31255
|
+
const filePath = join11(timelineDir, filename);
|
|
30142
31256
|
const lockPath = lockPathFor(timelineDir, filename);
|
|
30143
31257
|
try {
|
|
30144
31258
|
if (!acquireLock(lockPath))
|
|
@@ -30166,7 +31280,7 @@ function updateTrackedEntry(timelineDir, handle, update) {
|
|
|
30166
31280
|
if (!DATE_FILENAME_PATTERN.test(handle.filename) || basename(handle.filename) !== handle.filename) {
|
|
30167
31281
|
return { status: "rejected", reason: "unsafe" };
|
|
30168
31282
|
}
|
|
30169
|
-
const filePath =
|
|
31283
|
+
const filePath = join11(timelineDir, handle.filename);
|
|
30170
31284
|
const lockPath = lockPathFor(timelineDir, handle.filename);
|
|
30171
31285
|
try {
|
|
30172
31286
|
if (!acquireLock(lockPath))
|
|
@@ -30225,10 +31339,10 @@ async function sweepTimelineHistory(workingDirectoryBase, opts = {}) {
|
|
|
30225
31339
|
return;
|
|
30226
31340
|
}
|
|
30227
31341
|
for (const agentName of agentNames) {
|
|
30228
|
-
const agentDir =
|
|
31342
|
+
const agentDir = join11(workingDirectoryBase, agentName);
|
|
30229
31343
|
if (!isRealDirectory(agentDir))
|
|
30230
31344
|
continue;
|
|
30231
|
-
const timelineDir =
|
|
31345
|
+
const timelineDir = join11(agentDir, ".context_timeline");
|
|
30232
31346
|
if (!isRealDirectory(timelineDir))
|
|
30233
31347
|
continue;
|
|
30234
31348
|
let filenames;
|
|
@@ -30238,7 +31352,7 @@ async function sweepTimelineHistory(workingDirectoryBase, opts = {}) {
|
|
|
30238
31352
|
continue;
|
|
30239
31353
|
}
|
|
30240
31354
|
for (const filename of filenames) {
|
|
30241
|
-
const filePath =
|
|
31355
|
+
const filePath = join11(timelineDir, filename);
|
|
30242
31356
|
let source;
|
|
30243
31357
|
try {
|
|
30244
31358
|
source = fs7.lstatSync(filePath);
|
|
@@ -30887,7 +32001,12 @@ async function detectRuntimes() {
|
|
|
30887
32001
|
const driver = getDriver(id);
|
|
30888
32002
|
const probe = await driver.probe();
|
|
30889
32003
|
if (probe.status === "healthy") {
|
|
30890
|
-
results.push({
|
|
32004
|
+
results.push({
|
|
32005
|
+
id,
|
|
32006
|
+
status: "healthy",
|
|
32007
|
+
version: probe.version,
|
|
32008
|
+
reasoning: probe.reasoning
|
|
32009
|
+
});
|
|
30891
32010
|
} else {
|
|
30892
32011
|
results.push({
|
|
30893
32012
|
id,
|
|
@@ -31052,8 +32171,8 @@ class MessageReminderScheduler {
|
|
|
31052
32171
|
|
|
31053
32172
|
// src/manager/agentDriverHost.ts
|
|
31054
32173
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
31055
|
-
import { homedir as
|
|
31056
|
-
import { join as
|
|
32174
|
+
import { homedir as homedir4 } from "node:os";
|
|
32175
|
+
import { join as join13 } from "node:path";
|
|
31057
32176
|
|
|
31058
32177
|
// src/drivers/gitIdentityEnv.ts
|
|
31059
32178
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
@@ -31178,7 +32297,7 @@ function createDaemonAgentDriverHost(ctx, onRawLine) {
|
|
|
31178
32297
|
hostUser: readHostGitIdentity() ?? undefined
|
|
31179
32298
|
}),
|
|
31180
32299
|
platformProtected: {
|
|
31181
|
-
ALOOK_HOME: process.env.ALOOK_HOME ??
|
|
32300
|
+
ALOOK_HOME: process.env.ALOOK_HOME ?? join13(homedir4(), ".alook"),
|
|
31182
32301
|
ALOOK_ID: ctx.agentId,
|
|
31183
32302
|
ALOOK_CLI: ctx.agentCliPath,
|
|
31184
32303
|
ALOOK_SERVER_URL: ctx.config.serverUrl,
|
|
@@ -31283,6 +32402,182 @@ class DaemonSelfSleepScheduler {
|
|
|
31283
32402
|
}
|
|
31284
32403
|
}
|
|
31285
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
|
+
}
|
|
31286
32581
|
// src/daemon/createDaemon.ts
|
|
31287
32582
|
var WARMUP_BACKOFF_MS = [250, 500, 1000, 2000, 4000];
|
|
31288
32583
|
var WARMUP_CEILING_MS = 30000;
|
|
@@ -31420,9 +32715,25 @@ function createBuiltinDaemonSessionFactory(onRuntimeRawLine) {
|
|
|
31420
32715
|
}
|
|
31421
32716
|
async function createDaemon(opts) {
|
|
31422
32717
|
const log2 = opts.logger ?? createLogger({ header: "@alook/daemon" });
|
|
31423
|
-
const fallbackBase = (process.env.ALOOK_PROJECT_ROOT || `${
|
|
32718
|
+
const fallbackBase = (process.env.ALOOK_PROJECT_ROOT || `${homedir5()}/.alook`) + "/daemon";
|
|
31424
32719
|
const workingDirectoryBase = opts.workingDirectoryBase ?? fallbackBase;
|
|
31425
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
|
+
};
|
|
31426
32737
|
sweepTimelineHistory(workingDirectoryBase).catch(() => {
|
|
31427
32738
|
log2.warn("timeline startup sweep failed");
|
|
31428
32739
|
});
|
|
@@ -31438,6 +32749,27 @@ async function createDaemon(opts) {
|
|
|
31438
32749
|
});
|
|
31439
32750
|
let channelRef = null;
|
|
31440
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
|
+
};
|
|
31441
32773
|
let reminderSchedulerRef = null;
|
|
31442
32774
|
const selfSleepScheduler = opts.onSelfSleep ? new DaemonSelfSleepScheduler({
|
|
31443
32775
|
onSleep: opts.onSelfSleep,
|
|
@@ -31502,7 +32834,7 @@ async function createDaemon(opts) {
|
|
|
31502
32834
|
function reassertAgentActivity(agentId) {
|
|
31503
32835
|
const state = managerRef?.agentActivity(agentId);
|
|
31504
32836
|
if (state)
|
|
31505
|
-
|
|
32837
|
+
reportAgentActivity({ agentId, state });
|
|
31506
32838
|
}
|
|
31507
32839
|
function startTypingHeartbeat(agentId) {
|
|
31508
32840
|
stopTypingHeartbeat(agentId);
|
|
@@ -31646,6 +32978,20 @@ async function createDaemon(opts) {
|
|
|
31646
32978
|
logger: log2.child("ws")
|
|
31647
32979
|
});
|
|
31648
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
|
+
};
|
|
31649
32995
|
function restorePendingIdleResetEvents(agentId) {
|
|
31650
32996
|
for (const pending of timeline2.pendingIdleResetEvents(agentId)) {
|
|
31651
32997
|
channel2.restorePendingBotAuditEvent({
|
|
@@ -31764,7 +33110,7 @@ async function createDaemon(opts) {
|
|
|
31764
33110
|
onAgentSession: (info) => void channel2.reportAgentSession(info),
|
|
31765
33111
|
onAgentActivity: (info) => {
|
|
31766
33112
|
selfSleepScheduler?.observeAgentActivity(info.agentId, info.state === "running");
|
|
31767
|
-
|
|
33113
|
+
reportAgentActivity(info);
|
|
31768
33114
|
if (info.state === "starting" || info.state === "running") {
|
|
31769
33115
|
if (!typingHeartbeats.has(info.agentId)) {
|
|
31770
33116
|
startTypingHeartbeat(info.agentId);
|
|
@@ -31773,6 +33119,16 @@ async function createDaemon(opts) {
|
|
|
31773
33119
|
emitTypingStopsAndClear(info.agentId);
|
|
31774
33120
|
}
|
|
31775
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
|
+
},
|
|
31776
33132
|
onBotAuditEvent: (agentId, event, context) => emitBotAuditEvent(agentId, event, context),
|
|
31777
33133
|
onAgentLocallyStopped: (info) => router?.markLocallyStopped(info.agentId),
|
|
31778
33134
|
onRuntimeRawLine,
|
|
@@ -31819,6 +33175,11 @@ async function createDaemon(opts) {
|
|
|
31819
33175
|
arch: opts.arch,
|
|
31820
33176
|
osRelease: opts.osRelease,
|
|
31821
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
|
+
},
|
|
31822
33183
|
typingTracker,
|
|
31823
33184
|
logger: log2.child("router"),
|
|
31824
33185
|
onBeforeAgent: async (agentId) => {
|
|
@@ -31834,6 +33195,10 @@ async function createDaemon(opts) {
|
|
|
31834
33195
|
await enrollAgent(agentId);
|
|
31835
33196
|
}
|
|
31836
33197
|
});
|
|
33198
|
+
requestReadyQuotaResend = () => {
|
|
33199
|
+
if (router)
|
|
33200
|
+
channel2.sendReady?.(router.buildReady());
|
|
33201
|
+
};
|
|
31837
33202
|
channel2.onCommand(createSelfUpdateCommandListener(opts.handleSelfUpdate));
|
|
31838
33203
|
channel2.onCommand(createDiagnosticsCommandListener({
|
|
31839
33204
|
handleDiagnosticCommand: opts.handleDiagnosticCommand,
|
|
@@ -31863,6 +33228,11 @@ async function createDaemon(opts) {
|
|
|
31863
33228
|
resyncPendingWakes();
|
|
31864
33229
|
resyncPendingDiagnostics();
|
|
31865
33230
|
});
|
|
33231
|
+
if (opts.runtimeReport.some((runtime) => runtime.id === "claude")) {
|
|
33232
|
+
const observed = await providerQuotaReader("claude");
|
|
33233
|
+
if (observed)
|
|
33234
|
+
recordProviderQuota("claude", observed);
|
|
33235
|
+
}
|
|
31866
33236
|
channel2.connect();
|
|
31867
33237
|
await router.start();
|
|
31868
33238
|
selfSleepScheduler?.start();
|