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