@alook/daemon 0.1.25 → 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +461 -141
- package/dist/index.js +449 -129
- 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;
|
|
@@ -1278,6 +1278,7 @@ import { execFileSync as execFileSync2 } from "child_process";
|
|
|
1278
1278
|
import * as fs4 from "fs";
|
|
1279
1279
|
import * as path4 from "path";
|
|
1280
1280
|
var PROBE_TIMEOUT_MS = 5000;
|
|
1281
|
+
var PROBE_OUTPUT_MAX_BYTES = 1024 * 1024;
|
|
1281
1282
|
function resolveCommandOnPath(command, deps = {}) {
|
|
1282
1283
|
if (deps.which)
|
|
1283
1284
|
return deps.which(command);
|
|
@@ -1328,6 +1329,23 @@ function probeCommandVersion(command, args = [], deps = {}, platform = process.p
|
|
|
1328
1329
|
return { ok: false, error: String(code) };
|
|
1329
1330
|
}
|
|
1330
1331
|
}
|
|
1332
|
+
function probeCommandOutput(command, args, platform = process.platform) {
|
|
1333
|
+
try {
|
|
1334
|
+
const output = execFileSync2(command, args, {
|
|
1335
|
+
encoding: "utf8",
|
|
1336
|
+
timeout: PROBE_TIMEOUT_MS,
|
|
1337
|
+
maxBuffer: PROBE_OUTPUT_MAX_BYTES,
|
|
1338
|
+
shell: needsWindowsShimShell(command, platform),
|
|
1339
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
1340
|
+
input: "",
|
|
1341
|
+
env: { ...process.env, CI: "1" }
|
|
1342
|
+
});
|
|
1343
|
+
return { ok: true, output };
|
|
1344
|
+
} catch (err) {
|
|
1345
|
+
const code = err?.code ?? "command_probe_failed";
|
|
1346
|
+
return { ok: false, error: String(code) };
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1331
1349
|
function resolveHomePath(relativePath, deps = {}) {
|
|
1332
1350
|
return path4.join(deps.homeDir || process.env.HOME || ".", relativePath);
|
|
1333
1351
|
}
|
|
@@ -1434,6 +1452,14 @@ class ClaudeTurnProtocol {
|
|
|
1434
1452
|
}
|
|
1435
1453
|
|
|
1436
1454
|
// agent-driver/dist/adapters/claude/index.js
|
|
1455
|
+
var CLAUDE_MODEL_CATALOG = {
|
|
1456
|
+
updateMode: "unsupported",
|
|
1457
|
+
models: ["opus", "sonnet", "haiku"].map((id) => ({
|
|
1458
|
+
id,
|
|
1459
|
+
supportedReasoningEfforts: []
|
|
1460
|
+
}))
|
|
1461
|
+
};
|
|
1462
|
+
|
|
1437
1463
|
class ClaudeDriver {
|
|
1438
1464
|
id = "claude";
|
|
1439
1465
|
instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
|
|
@@ -1450,10 +1476,11 @@ class ClaudeDriver {
|
|
|
1450
1476
|
}
|
|
1451
1477
|
probe(command) {
|
|
1452
1478
|
const explicit = command?.trim();
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1479
|
+
const base = explicit ? (() => {
|
|
1480
|
+
const result = probeCommandVersion(explicit);
|
|
1481
|
+
return result.ok ? { status: "healthy", version: result.version } : { status: "unhealthy", lastError: result.error };
|
|
1482
|
+
})() : probeClaude();
|
|
1483
|
+
return base.status === "healthy" ? { ...base, reasoning: CLAUDE_MODEL_CATALOG } : base;
|
|
1457
1484
|
}
|
|
1458
1485
|
async openLane(ctx, options) {
|
|
1459
1486
|
return createProcessLane(this, ctx, { onRawStdoutLine: options?.onRawStdoutLine });
|
|
@@ -2020,10 +2047,59 @@ function stableErrorCode(value, fallback) {
|
|
|
2020
2047
|
return /^[A-Za-z0-9_.-]{1,100}$/.test(code) ? code : fallback;
|
|
2021
2048
|
}
|
|
2022
2049
|
|
|
2050
|
+
// agent-driver/dist/internal/modelCatalog.js
|
|
2051
|
+
var RUNTIME_MODEL_CATALOG_MAX = 512;
|
|
2052
|
+
var RUNTIME_MODEL_ID_MAX = 100;
|
|
2053
|
+
function normalizeRuntimeModelId(value) {
|
|
2054
|
+
if (typeof value !== "string")
|
|
2055
|
+
return;
|
|
2056
|
+
const id = value.trim();
|
|
2057
|
+
if (!id || id.length > RUNTIME_MODEL_ID_MAX || /\s/.test(id))
|
|
2058
|
+
return;
|
|
2059
|
+
return id;
|
|
2060
|
+
}
|
|
2061
|
+
function catalogFromIds(ids) {
|
|
2062
|
+
const seen = new Set;
|
|
2063
|
+
const models = [];
|
|
2064
|
+
for (const rawId of ids) {
|
|
2065
|
+
const id = normalizeRuntimeModelId(rawId);
|
|
2066
|
+
if (!id || seen.has(id))
|
|
2067
|
+
continue;
|
|
2068
|
+
if (models.length >= RUNTIME_MODEL_CATALOG_MAX)
|
|
2069
|
+
return;
|
|
2070
|
+
seen.add(id);
|
|
2071
|
+
models.push({ id, supportedReasoningEfforts: [] });
|
|
2072
|
+
}
|
|
2073
|
+
if (models.length === 0)
|
|
2074
|
+
return;
|
|
2075
|
+
return { updateMode: "unsupported", models };
|
|
2076
|
+
}
|
|
2077
|
+
function parseOpenCodeModelCatalog(output) {
|
|
2078
|
+
const ids = output.split(/\r?\n/).flatMap((line) => {
|
|
2079
|
+
const id = normalizeRuntimeModelId(line);
|
|
2080
|
+
return id && /^[^/]+\/.+$/.test(id) ? [id] : [];
|
|
2081
|
+
});
|
|
2082
|
+
return catalogFromIds(ids);
|
|
2083
|
+
}
|
|
2084
|
+
function parsePiModelCatalog(values) {
|
|
2085
|
+
if (!Array.isArray(values))
|
|
2086
|
+
return;
|
|
2087
|
+
const ids = values.flatMap((value) => {
|
|
2088
|
+
if (!value || typeof value !== "object")
|
|
2089
|
+
return [];
|
|
2090
|
+
const model = value;
|
|
2091
|
+
const provider = normalizeRuntimeModelId(model.provider);
|
|
2092
|
+
const id = normalizeRuntimeModelId(model.id);
|
|
2093
|
+
return provider && id && !provider.includes("/") ? [`${provider}/${id}`] : [];
|
|
2094
|
+
});
|
|
2095
|
+
return catalogFromIds(ids);
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2023
2098
|
// agent-driver/dist/adapters/codex/index.js
|
|
2024
2099
|
var SETTINGS_UPDATE_TIMEOUT_MS = 5000;
|
|
2025
2100
|
var MODEL_LIST_TIMEOUT_MS = 5000;
|
|
2026
|
-
var
|
|
2101
|
+
var MODEL_LIST_OUTPUT_MAX_BYTES = 1024 * 1024;
|
|
2102
|
+
var MODEL_LIST_MAX = RUNTIME_MODEL_CATALOG_MAX;
|
|
2027
2103
|
var MODEL_EFFORT_MAX = 16;
|
|
2028
2104
|
function isCodexMissingRolloutError(message) {
|
|
2029
2105
|
return /\bno\s+rollout\s+found\b/i.test(message) || /\bmissing\s+rollout\b/i.test(message) || /\brollout\b.*\b(not found|missing)\b/i.test(message) || /\b(not found|missing)\b.*\brollout\b/i.test(message);
|
|
@@ -2099,11 +2175,13 @@ class CodexDriver {
|
|
|
2099
2175
|
return new Promise((resolve2) => {
|
|
2100
2176
|
let settled = false;
|
|
2101
2177
|
let buffer = "";
|
|
2178
|
+
let outputBytes = 0;
|
|
2102
2179
|
let nextId = 0;
|
|
2103
2180
|
let initializeId = 0;
|
|
2104
2181
|
let listId = 0;
|
|
2105
2182
|
const models = [];
|
|
2106
2183
|
const seenModels = new Set;
|
|
2184
|
+
let overflow = false;
|
|
2107
2185
|
let defaultModelId;
|
|
2108
2186
|
const finish = (catalog) => {
|
|
2109
2187
|
if (settled)
|
|
@@ -2121,12 +2199,16 @@ class CodexDriver {
|
|
|
2121
2199
|
`);
|
|
2122
2200
|
};
|
|
2123
2201
|
const consumeModel = (value) => {
|
|
2124
|
-
if (!value || typeof value !== "object"
|
|
2202
|
+
if (!value || typeof value !== "object")
|
|
2125
2203
|
return;
|
|
2126
2204
|
const model = value;
|
|
2127
|
-
const id =
|
|
2128
|
-
if (!id ||
|
|
2205
|
+
const id = normalizeRuntimeModelId(model.id);
|
|
2206
|
+
if (!id || seenModels.has(id))
|
|
2129
2207
|
return;
|
|
2208
|
+
if (models.length >= MODEL_LIST_MAX) {
|
|
2209
|
+
overflow = true;
|
|
2210
|
+
return;
|
|
2211
|
+
}
|
|
2130
2212
|
const rawOptions = Array.isArray(model.supportedReasoningEfforts) ? model.supportedReasoningEfforts : [];
|
|
2131
2213
|
const seenEfforts = new Set;
|
|
2132
2214
|
const supportedReasoningEfforts = rawOptions.flatMap((raw) => {
|
|
@@ -2170,9 +2252,15 @@ class CodexDriver {
|
|
|
2170
2252
|
const result = message.result;
|
|
2171
2253
|
for (const model of Array.isArray(result.data) ? result.data : [])
|
|
2172
2254
|
consumeModel(model);
|
|
2255
|
+
if (overflow)
|
|
2256
|
+
return finish();
|
|
2173
2257
|
const cursor = typeof result.nextCursor === "string" ? result.nextCursor : undefined;
|
|
2174
|
-
if (cursor && models.length
|
|
2258
|
+
if (cursor && models.length >= MODEL_LIST_MAX)
|
|
2259
|
+
return finish();
|
|
2260
|
+
if (cursor)
|
|
2175
2261
|
return requestModelPage(cursor);
|
|
2262
|
+
if (models.length === 0)
|
|
2263
|
+
return finish();
|
|
2176
2264
|
finish({
|
|
2177
2265
|
updateMode: "live_next_turn",
|
|
2178
2266
|
...defaultModelId ? { defaultModelId } : {},
|
|
@@ -2182,7 +2270,11 @@ class CodexDriver {
|
|
|
2182
2270
|
const timer = setTimeout(() => finish(), MODEL_LIST_TIMEOUT_MS);
|
|
2183
2271
|
timer.unref?.();
|
|
2184
2272
|
proc.stdout?.on("data", (chunk) => {
|
|
2185
|
-
|
|
2273
|
+
const text = chunk.toString();
|
|
2274
|
+
outputBytes += Buffer.byteLength(text);
|
|
2275
|
+
if (outputBytes > MODEL_LIST_OUTPUT_MAX_BYTES)
|
|
2276
|
+
return finish();
|
|
2277
|
+
buffer += text;
|
|
2186
2278
|
const lines = buffer.split(`
|
|
2187
2279
|
`);
|
|
2188
2280
|
buffer = lines.pop() ?? "";
|
|
@@ -2385,9 +2477,189 @@ class CodexDriver {
|
|
|
2385
2477
|
|
|
2386
2478
|
// agent-driver/dist/adapters/cursor/acp-lane.js
|
|
2387
2479
|
import { EventEmitter as EventEmitter2 } from "node:events";
|
|
2480
|
+
|
|
2481
|
+
// agent-driver/dist/adapters/cursor/catalog-probe.js
|
|
2388
2482
|
var ACP_PROTOCOL_VERSION = 1;
|
|
2389
|
-
var HANDSHAKE_TIMEOUT_MS = 15000;
|
|
2390
2483
|
var AUTH_METHOD_ID = "cursor_login";
|
|
2484
|
+
var CATALOG_PROBE_TIMEOUT_MS = 15000;
|
|
2485
|
+
var CATALOG_PROBE_OUTPUT_MAX_BYTES = 1024 * 1024;
|
|
2486
|
+
var MODEL_DISPLAY_NAME_MAX = 256;
|
|
2487
|
+
var MODEL_OPTION_NESTING_MAX = 16;
|
|
2488
|
+
function record(value) {
|
|
2489
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
2490
|
+
}
|
|
2491
|
+
function normalizeDisplayName(value) {
|
|
2492
|
+
if (typeof value !== "string")
|
|
2493
|
+
return;
|
|
2494
|
+
const displayName = value.trim();
|
|
2495
|
+
return displayName && displayName.length <= MODEL_DISPLAY_NAME_MAX ? displayName : undefined;
|
|
2496
|
+
}
|
|
2497
|
+
function flattenCursorAcpSelectOptions(value, depth = 0) {
|
|
2498
|
+
if (!Array.isArray(value) || depth > MODEL_OPTION_NESTING_MAX)
|
|
2499
|
+
return [];
|
|
2500
|
+
const options = [];
|
|
2501
|
+
for (const item of value) {
|
|
2502
|
+
if (Array.isArray(item)) {
|
|
2503
|
+
options.push(...flattenCursorAcpSelectOptions(item, depth + 1));
|
|
2504
|
+
continue;
|
|
2505
|
+
}
|
|
2506
|
+
const candidate = record(item);
|
|
2507
|
+
if (!candidate)
|
|
2508
|
+
continue;
|
|
2509
|
+
const exactValue = normalizeRuntimeModelId(candidate.value);
|
|
2510
|
+
if (exactValue) {
|
|
2511
|
+
const name = normalizeDisplayName(candidate.name);
|
|
2512
|
+
options.push({ value: exactValue, ...name ? { name } : {} });
|
|
2513
|
+
}
|
|
2514
|
+
if (Array.isArray(candidate.options)) {
|
|
2515
|
+
options.push(...flattenCursorAcpSelectOptions(candidate.options, depth + 1));
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
return options;
|
|
2519
|
+
}
|
|
2520
|
+
function parseCursorAcpModelCatalog(session) {
|
|
2521
|
+
const payload = record(session);
|
|
2522
|
+
const configOptions = Array.isArray(payload?.configOptions) ? payload.configOptions : [];
|
|
2523
|
+
const modelConfig = configOptions.map(record).find((option) => option?.id === "model") ?? null;
|
|
2524
|
+
if (!modelConfig)
|
|
2525
|
+
return;
|
|
2526
|
+
const seen = new Set;
|
|
2527
|
+
const models = [];
|
|
2528
|
+
for (const option of flattenCursorAcpSelectOptions(modelConfig.options)) {
|
|
2529
|
+
if (option.value === "default[]" || seen.has(option.value))
|
|
2530
|
+
continue;
|
|
2531
|
+
if (models.length >= RUNTIME_MODEL_CATALOG_MAX)
|
|
2532
|
+
return;
|
|
2533
|
+
seen.add(option.value);
|
|
2534
|
+
models.push({
|
|
2535
|
+
id: option.value,
|
|
2536
|
+
...option.name ? { displayName: option.name } : {},
|
|
2537
|
+
supportedReasoningEfforts: []
|
|
2538
|
+
});
|
|
2539
|
+
}
|
|
2540
|
+
return models.length > 0 ? { updateMode: "unsupported", models } : undefined;
|
|
2541
|
+
}
|
|
2542
|
+
async function cleanupProbeProcess(process2) {
|
|
2543
|
+
if (process2.pid) {
|
|
2544
|
+
await killProcessTree(process2.pid, { graceMs: 250 }).catch(() => {});
|
|
2545
|
+
return;
|
|
2546
|
+
}
|
|
2547
|
+
if (process2.exitCode === null && process2.signalCode === null)
|
|
2548
|
+
process2.kill("SIGTERM");
|
|
2549
|
+
}
|
|
2550
|
+
async function probeCursorAcpCatalog(command, options = {}) {
|
|
2551
|
+
const cwd = options.cwd ?? process.cwd();
|
|
2552
|
+
const spec = resolveSpawnSpec("cursor-agent", ["acp"], command);
|
|
2553
|
+
let processHandle;
|
|
2554
|
+
try {
|
|
2555
|
+
processHandle = (options.spawn ?? spawnAgentProcess)(spec.command, spec.args, {
|
|
2556
|
+
cwd,
|
|
2557
|
+
env: { ...process.env, CI: "1" },
|
|
2558
|
+
shell: spec.shell
|
|
2559
|
+
});
|
|
2560
|
+
} catch {
|
|
2561
|
+
return;
|
|
2562
|
+
}
|
|
2563
|
+
return new Promise((resolve2) => {
|
|
2564
|
+
let settled = false;
|
|
2565
|
+
let buffer = "";
|
|
2566
|
+
let outputBytes = 0;
|
|
2567
|
+
let requestId = 0;
|
|
2568
|
+
let expectedId = 0;
|
|
2569
|
+
let expectedMethod = "";
|
|
2570
|
+
const finish = (catalog) => {
|
|
2571
|
+
if (settled)
|
|
2572
|
+
return;
|
|
2573
|
+
settled = true;
|
|
2574
|
+
clearTimeout(timer);
|
|
2575
|
+
const cleanup = options.cleanup ?? cleanupProbeProcess;
|
|
2576
|
+
Promise.resolve().then(() => cleanup(processHandle)).catch(() => {}).finally(() => resolve2(catalog));
|
|
2577
|
+
};
|
|
2578
|
+
const request = (method, params) => {
|
|
2579
|
+
if (settled)
|
|
2580
|
+
return;
|
|
2581
|
+
const stdin = processHandle.stdin;
|
|
2582
|
+
if (!stdin || stdin.destroyed || stdin.writableEnded || stdin.writable === false)
|
|
2583
|
+
return finish();
|
|
2584
|
+
expectedId = ++requestId;
|
|
2585
|
+
expectedMethod = method;
|
|
2586
|
+
try {
|
|
2587
|
+
stdin.write(`${jsonRpcRequest(method, params, expectedId)}
|
|
2588
|
+
`);
|
|
2589
|
+
} catch {
|
|
2590
|
+
finish();
|
|
2591
|
+
}
|
|
2592
|
+
};
|
|
2593
|
+
const onLine = (line) => {
|
|
2594
|
+
const parsed = tryParseJsonLine(line);
|
|
2595
|
+
const message = record(parsed);
|
|
2596
|
+
if (!message)
|
|
2597
|
+
return finish();
|
|
2598
|
+
if (message.id !== expectedId)
|
|
2599
|
+
return;
|
|
2600
|
+
if (message.error !== undefined)
|
|
2601
|
+
return finish();
|
|
2602
|
+
if (!Object.prototype.hasOwnProperty.call(message, "result"))
|
|
2603
|
+
return finish();
|
|
2604
|
+
if (expectedMethod === "authenticate") {
|
|
2605
|
+
request("session/new", { cwd, mcpServers: [] });
|
|
2606
|
+
return;
|
|
2607
|
+
}
|
|
2608
|
+
const result = record(message.result);
|
|
2609
|
+
if (!result)
|
|
2610
|
+
return finish();
|
|
2611
|
+
if (expectedMethod === "initialize") {
|
|
2612
|
+
const authMethods = Array.isArray(result.authMethods) ? result.authMethods : [];
|
|
2613
|
+
if (result.protocolVersion !== ACP_PROTOCOL_VERSION || !authMethods.some((method) => record(method)?.id === AUTH_METHOD_ID))
|
|
2614
|
+
return finish();
|
|
2615
|
+
request("authenticate", { methodId: AUTH_METHOD_ID });
|
|
2616
|
+
return;
|
|
2617
|
+
}
|
|
2618
|
+
if (expectedMethod !== "session/new" || typeof result.sessionId !== "string" || !result.sessionId.trim())
|
|
2619
|
+
return finish();
|
|
2620
|
+
finish(parseCursorAcpModelCatalog(result));
|
|
2621
|
+
};
|
|
2622
|
+
const timer = setTimeout(() => finish(), options.timeoutMs ?? CATALOG_PROBE_TIMEOUT_MS);
|
|
2623
|
+
timer.unref?.();
|
|
2624
|
+
processHandle.stdout?.on("data", (chunk) => {
|
|
2625
|
+
if (settled)
|
|
2626
|
+
return;
|
|
2627
|
+
const text = chunk.toString();
|
|
2628
|
+
outputBytes += Buffer.byteLength(text);
|
|
2629
|
+
if (outputBytes > (options.outputMaxBytes ?? CATALOG_PROBE_OUTPUT_MAX_BYTES))
|
|
2630
|
+
return finish();
|
|
2631
|
+
buffer += text;
|
|
2632
|
+
const lines = buffer.split(`
|
|
2633
|
+
`);
|
|
2634
|
+
buffer = lines.pop() ?? "";
|
|
2635
|
+
for (const line of lines)
|
|
2636
|
+
if (line.trim())
|
|
2637
|
+
onLine(line);
|
|
2638
|
+
});
|
|
2639
|
+
processHandle.stderr?.on("data", (chunk) => {
|
|
2640
|
+
if (settled)
|
|
2641
|
+
return;
|
|
2642
|
+
outputBytes += Buffer.byteLength(chunk.toString());
|
|
2643
|
+
if (outputBytes > (options.outputMaxBytes ?? CATALOG_PROBE_OUTPUT_MAX_BYTES))
|
|
2644
|
+
finish();
|
|
2645
|
+
});
|
|
2646
|
+
processHandle.on("error", () => finish());
|
|
2647
|
+
processHandle.on("exit", () => finish());
|
|
2648
|
+
request("initialize", {
|
|
2649
|
+
protocolVersion: ACP_PROTOCOL_VERSION,
|
|
2650
|
+
clientCapabilities: {
|
|
2651
|
+
fs: { readTextFile: false, writeTextFile: false },
|
|
2652
|
+
terminal: false
|
|
2653
|
+
},
|
|
2654
|
+
clientInfo: { name: "alook-agent-driver-probe", version: "0.1.25" }
|
|
2655
|
+
});
|
|
2656
|
+
});
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
// agent-driver/dist/adapters/cursor/acp-lane.js
|
|
2660
|
+
var ACP_PROTOCOL_VERSION2 = 1;
|
|
2661
|
+
var HANDSHAKE_TIMEOUT_MS = 15000;
|
|
2662
|
+
var AUTH_METHOD_ID2 = "cursor_login";
|
|
2391
2663
|
var PROMPT_STOP_REASONS = new Set([
|
|
2392
2664
|
"end_turn",
|
|
2393
2665
|
"max_tokens",
|
|
@@ -2411,16 +2683,16 @@ class CursorAcpRpcError extends Error {
|
|
|
2411
2683
|
this.code = code;
|
|
2412
2684
|
}
|
|
2413
2685
|
}
|
|
2414
|
-
function
|
|
2686
|
+
function record2(value) {
|
|
2415
2687
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
2416
2688
|
}
|
|
2417
2689
|
function safeLabel(value) {
|
|
2418
2690
|
return typeof value === "string" && /^[a-z0-9_-]{1,64}$/i.test(value) ? value : "unknown";
|
|
2419
2691
|
}
|
|
2420
2692
|
function rpcErrorMessage(error) {
|
|
2421
|
-
const payload =
|
|
2693
|
+
const payload = record2(error);
|
|
2422
2694
|
const message = typeof payload?.message === "string" && payload.message.trim() ? payload.message : "Cursor ACP request failed";
|
|
2423
|
-
const data =
|
|
2695
|
+
const data = record2(payload?.data);
|
|
2424
2696
|
const detail = typeof data?.message === "string" && data.message.trim() ? data.message : undefined;
|
|
2425
2697
|
return detail ? `${message}: ${detail}` : message;
|
|
2426
2698
|
}
|
|
@@ -2428,26 +2700,6 @@ function isMissingSessionError(error) {
|
|
|
2428
2700
|
const message = error instanceof Error ? error.message : String(error);
|
|
2429
2701
|
return /\bsession\b.*\b(not found|missing|unknown|invalid)\b/i.test(message) || /\b(not found|missing|unknown|invalid)\b.*\bsession\b/i.test(message);
|
|
2430
2702
|
}
|
|
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
2703
|
|
|
2452
2704
|
class CursorAcpLane {
|
|
2453
2705
|
factory;
|
|
@@ -2571,30 +2823,30 @@ class CursorAcpLane {
|
|
|
2571
2823
|
}
|
|
2572
2824
|
}
|
|
2573
2825
|
async handshake(ctx) {
|
|
2574
|
-
const initialize =
|
|
2575
|
-
protocolVersion:
|
|
2826
|
+
const initialize = record2(await this.call("initialize", {
|
|
2827
|
+
protocolVersion: ACP_PROTOCOL_VERSION2,
|
|
2576
2828
|
clientCapabilities: {
|
|
2577
2829
|
fs: { readTextFile: false, writeTextFile: false },
|
|
2578
2830
|
terminal: false
|
|
2579
2831
|
},
|
|
2580
2832
|
clientInfo: { name: "alook-agent-driver", version: "0.1.14" }
|
|
2581
2833
|
}));
|
|
2582
|
-
if (initialize?.protocolVersion !==
|
|
2834
|
+
if (initialize?.protocolVersion !== ACP_PROTOCOL_VERSION2) {
|
|
2583
2835
|
throw new CursorAcpIncompatibleError("Installed Cursor ACP does not support protocol version 1");
|
|
2584
2836
|
}
|
|
2585
|
-
const capabilities =
|
|
2837
|
+
const capabilities = record2(initialize.agentCapabilities);
|
|
2586
2838
|
if (capabilities?.loadSession !== true) {
|
|
2587
2839
|
throw new CursorAcpIncompatibleError("Installed Cursor ACP does not support persistent session loading");
|
|
2588
2840
|
}
|
|
2589
2841
|
const authMethods = Array.isArray(initialize.authMethods) ? initialize.authMethods : [];
|
|
2590
|
-
if (!authMethods.some((method) =>
|
|
2842
|
+
if (!authMethods.some((method) => record2(method)?.id === AUTH_METHOD_ID2)) {
|
|
2591
2843
|
throw new CursorAcpIncompatibleError("Installed Cursor ACP does not expose Cursor login authentication");
|
|
2592
2844
|
}
|
|
2593
|
-
await this.call("authenticate", { methodId:
|
|
2845
|
+
await this.call("authenticate", { methodId: AUTH_METHOD_ID2 });
|
|
2594
2846
|
let session;
|
|
2595
2847
|
if (ctx.config.sessionId) {
|
|
2596
2848
|
try {
|
|
2597
|
-
session =
|
|
2849
|
+
session = record2(await this.call("session/load", {
|
|
2598
2850
|
sessionId: ctx.config.sessionId,
|
|
2599
2851
|
cwd: ctx.workingDirectory,
|
|
2600
2852
|
mcpServers: []
|
|
@@ -2606,15 +2858,25 @@ class CursorAcpLane {
|
|
|
2606
2858
|
throw error;
|
|
2607
2859
|
}
|
|
2608
2860
|
} else {
|
|
2609
|
-
session =
|
|
2861
|
+
session = record2(await this.call("session/new", { cwd: ctx.workingDirectory, mcpServers: [] }));
|
|
2610
2862
|
}
|
|
2611
|
-
if (!session
|
|
2863
|
+
if (!session)
|
|
2864
|
+
throw new Error("Cursor ACP did not return a valid session response");
|
|
2865
|
+
const returnedSessionId = session.sessionId;
|
|
2866
|
+
if (returnedSessionId !== undefined && (typeof returnedSessionId !== "string" || !returnedSessionId.trim())) {
|
|
2612
2867
|
throw new Error("Cursor ACP did not return a valid session id");
|
|
2613
2868
|
}
|
|
2614
|
-
if (ctx.config.sessionId
|
|
2615
|
-
|
|
2869
|
+
if (ctx.config.sessionId) {
|
|
2870
|
+
if (returnedSessionId !== undefined && returnedSessionId !== ctx.config.sessionId) {
|
|
2871
|
+
throw new CursorAcpResetRequiredError("Cursor ACP loaded a different session; reset this agent before continuing");
|
|
2872
|
+
}
|
|
2873
|
+
this.sessionId = ctx.config.sessionId;
|
|
2874
|
+
} else {
|
|
2875
|
+
if (typeof returnedSessionId !== "string") {
|
|
2876
|
+
throw new Error("Cursor ACP did not return a valid session id");
|
|
2877
|
+
}
|
|
2878
|
+
this.sessionId = returnedSessionId;
|
|
2616
2879
|
}
|
|
2617
|
-
this.sessionId = session.sessionId;
|
|
2618
2880
|
await this.configureModel(session, ctx);
|
|
2619
2881
|
}
|
|
2620
2882
|
async configureModel(session, ctx) {
|
|
@@ -2622,24 +2884,30 @@ class CursorAcpLane {
|
|
|
2622
2884
|
if (!requestedModel)
|
|
2623
2885
|
return;
|
|
2624
2886
|
const configOptions = Array.isArray(session.configOptions) ? session.configOptions : [];
|
|
2625
|
-
const modelConfig = configOptions.map(
|
|
2887
|
+
const modelConfig = configOptions.map(record2).find((option) => option?.id === "model") ?? null;
|
|
2626
2888
|
if (!modelConfig) {
|
|
2627
2889
|
throw new CursorAcpIncompatibleError("Installed Cursor ACP does not support model configuration");
|
|
2628
2890
|
}
|
|
2629
|
-
const options =
|
|
2630
|
-
const match = options.find((option) => option.value === requestedModel)
|
|
2891
|
+
const options = flattenCursorAcpSelectOptions(modelConfig.options);
|
|
2892
|
+
const match = options.find((option) => option.value === requestedModel);
|
|
2631
2893
|
if (!match) {
|
|
2632
2894
|
throw new CursorAcpIncompatibleError(`Configured Cursor model is unavailable through ACP: ${requestedModel}`);
|
|
2633
2895
|
}
|
|
2896
|
+
let response;
|
|
2634
2897
|
try {
|
|
2635
|
-
await this.call("session/set_config_option", {
|
|
2898
|
+
response = record2(await this.call("session/set_config_option", {
|
|
2636
2899
|
sessionId: this.sessionId,
|
|
2637
2900
|
configId: "model",
|
|
2638
2901
|
value: match.value
|
|
2639
|
-
});
|
|
2902
|
+
}));
|
|
2640
2903
|
} catch {
|
|
2641
2904
|
throw new CursorAcpIncompatibleError("Installed Cursor ACP rejected model configuration");
|
|
2642
2905
|
}
|
|
2906
|
+
const confirmedOptions = Array.isArray(response?.configOptions) ? response.configOptions : [];
|
|
2907
|
+
const confirmedModel = confirmedOptions.map(record2).find((option) => option?.id === "model") ?? null;
|
|
2908
|
+
if (confirmedModel?.currentValue !== match.value) {
|
|
2909
|
+
throw new CursorAcpIncompatibleError("Cursor ACP did not confirm the exact configured model");
|
|
2910
|
+
}
|
|
2643
2911
|
}
|
|
2644
2912
|
admitPrompt(text) {
|
|
2645
2913
|
if (!this.sessionId)
|
|
@@ -2675,7 +2943,7 @@ class CursorAcpLane {
|
|
|
2675
2943
|
completePrompt(active, value) {
|
|
2676
2944
|
if (this.activePrompt?.requestId !== active.requestId)
|
|
2677
2945
|
return;
|
|
2678
|
-
const result =
|
|
2946
|
+
const result = record2(value);
|
|
2679
2947
|
if (!result || typeof result.stopReason !== "string" || !PROMPT_STOP_REASONS.has(result.stopReason)) {
|
|
2680
2948
|
this.failPrompt(active, new Error("Cursor ACP prompt response did not contain a supported stopReason"));
|
|
2681
2949
|
return;
|
|
@@ -2817,7 +3085,7 @@ class CursorAcpLane {
|
|
|
2817
3085
|
});
|
|
2818
3086
|
}
|
|
2819
3087
|
handleMessage(value) {
|
|
2820
|
-
const message =
|
|
3088
|
+
const message = record2(value);
|
|
2821
3089
|
if (!message || message.jsonrpc !== "2.0") {
|
|
2822
3090
|
this.protocolFailure("Cursor ACP emitted an invalid JSON-RPC message");
|
|
2823
3091
|
return;
|
|
@@ -2845,7 +3113,7 @@ class CursorAcpLane {
|
|
|
2845
3113
|
if (pending.kind === "prompt") {
|
|
2846
3114
|
this.pending.delete(id);
|
|
2847
3115
|
if (message.error !== undefined) {
|
|
2848
|
-
const payload =
|
|
3116
|
+
const payload = record2(message.error);
|
|
2849
3117
|
this.failPrompt(pending.active, new CursorAcpRpcError(pending.method, typeof payload?.code === "number" ? payload.code : undefined, rpcErrorMessage(message.error)));
|
|
2850
3118
|
} else if (!("result" in message)) {
|
|
2851
3119
|
this.failPrompt(pending.active, new Error("Cursor ACP response omitted result"));
|
|
@@ -2855,7 +3123,7 @@ class CursorAcpLane {
|
|
|
2855
3123
|
return;
|
|
2856
3124
|
}
|
|
2857
3125
|
if (message.error !== undefined) {
|
|
2858
|
-
const payload =
|
|
3126
|
+
const payload = record2(message.error);
|
|
2859
3127
|
this.settleRequest(id, false, new CursorAcpRpcError(pending.method, typeof payload?.code === "number" ? payload.code : undefined, rpcErrorMessage(message.error)));
|
|
2860
3128
|
return;
|
|
2861
3129
|
}
|
|
@@ -2887,9 +3155,9 @@ class CursorAcpLane {
|
|
|
2887
3155
|
this.diagnostic("warning", `Unsupported Cursor ACP client request: ${safeLabel(method)}`);
|
|
2888
3156
|
return;
|
|
2889
3157
|
}
|
|
2890
|
-
const payload =
|
|
3158
|
+
const payload = record2(params);
|
|
2891
3159
|
const sameSession = payload?.sessionId === this.sessionId;
|
|
2892
|
-
const options = Array.isArray(payload?.options) ? payload.options.map(
|
|
3160
|
+
const options = Array.isArray(payload?.options) ? payload.options.map(record2).filter(Boolean) : [];
|
|
2893
3161
|
const allowOnce = options.find((option) => option.kind === "allow_once" && typeof option.optionId === "string" && option.optionId.trim().length > 0);
|
|
2894
3162
|
if (!this.ready || !this.activePrompt || !sameSession || !allowOnce) {
|
|
2895
3163
|
this.write({ jsonrpc: "2.0", id, result: { outcome: { outcome: "cancelled" } } });
|
|
@@ -2910,7 +3178,7 @@ class CursorAcpLane {
|
|
|
2910
3178
|
this.diagnostic("warning", `Unsupported Cursor ACP notification: ${safeLabel(method)}`);
|
|
2911
3179
|
}
|
|
2912
3180
|
handleSessionUpdate(params) {
|
|
2913
|
-
const payload =
|
|
3181
|
+
const payload = record2(params);
|
|
2914
3182
|
if (!payload || payload.sessionId !== this.sessionId) {
|
|
2915
3183
|
this.diagnostic("warning", "Cursor ACP emitted an update for a different session");
|
|
2916
3184
|
return;
|
|
@@ -2919,18 +3187,18 @@ class CursorAcpLane {
|
|
|
2919
3187
|
this.diagnostic("warning", "Cursor ACP emitted a session update without an active prompt");
|
|
2920
3188
|
return;
|
|
2921
3189
|
}
|
|
2922
|
-
const update =
|
|
3190
|
+
const update = record2(payload.update) ?? {};
|
|
2923
3191
|
const updateType = update?.sessionUpdate;
|
|
2924
3192
|
switch (updateType) {
|
|
2925
3193
|
case "agent_message_chunk": {
|
|
2926
|
-
const content =
|
|
3194
|
+
const content = record2(update.content);
|
|
2927
3195
|
if (content?.type === "text" && typeof content.text === "string") {
|
|
2928
3196
|
this.events.emit("runtime_event", { kind: "assistant_message_delta", text: content.text });
|
|
2929
3197
|
}
|
|
2930
3198
|
return;
|
|
2931
3199
|
}
|
|
2932
3200
|
case "agent_thought_chunk": {
|
|
2933
|
-
const content =
|
|
3201
|
+
const content = record2(update.content);
|
|
2934
3202
|
if (content?.type === "text" && typeof content.text === "string") {
|
|
2935
3203
|
this.events.emit("runtime_event", { kind: "assistant_reasoning_delta", text: content.text });
|
|
2936
3204
|
}
|
|
@@ -3002,6 +3270,7 @@ class CursorAcpLane {
|
|
|
3002
3270
|
|
|
3003
3271
|
// agent-driver/dist/adapters/cursor/index.js
|
|
3004
3272
|
class CursorDriver {
|
|
3273
|
+
catalogProbe;
|
|
3005
3274
|
id = "cursor";
|
|
3006
3275
|
instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
|
|
3007
3276
|
execution = {
|
|
@@ -3010,8 +3279,23 @@ class CursorDriver {
|
|
|
3010
3279
|
wakeStart: "immediate",
|
|
3011
3280
|
terminalOwnership: "transport_request"
|
|
3012
3281
|
};
|
|
3013
|
-
|
|
3014
|
-
|
|
3282
|
+
constructor(catalogProbe = probeCursorAcpCatalog) {
|
|
3283
|
+
this.catalogProbe = catalogProbe;
|
|
3284
|
+
}
|
|
3285
|
+
async probe(command) {
|
|
3286
|
+
const result = probeCliRuntime("cursor-agent", {}, command);
|
|
3287
|
+
if (result.status !== "healthy")
|
|
3288
|
+
return result;
|
|
3289
|
+
let reasoning;
|
|
3290
|
+
try {
|
|
3291
|
+
reasoning = await this.catalogProbe(command);
|
|
3292
|
+
} catch {
|
|
3293
|
+
reasoning = undefined;
|
|
3294
|
+
}
|
|
3295
|
+
return {
|
|
3296
|
+
...result,
|
|
3297
|
+
reasoning
|
|
3298
|
+
};
|
|
3015
3299
|
}
|
|
3016
3300
|
async openLane(ctx, options) {
|
|
3017
3301
|
return new CursorAcpLane(this, ctx, { onRawStdoutLine: options?.onRawStdoutLine });
|
|
@@ -3068,7 +3352,7 @@ class OpenCodeHttpError extends Error {
|
|
|
3068
3352
|
this.status = status;
|
|
3069
3353
|
}
|
|
3070
3354
|
}
|
|
3071
|
-
function
|
|
3355
|
+
function record3(value) {
|
|
3072
3356
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
3073
3357
|
}
|
|
3074
3358
|
function safeLabel2(value) {
|
|
@@ -3115,7 +3399,7 @@ function parseModelRef(model) {
|
|
|
3115
3399
|
return { providerID: model.slice(0, slash), id: model.slice(slash + 1) };
|
|
3116
3400
|
}
|
|
3117
3401
|
function messageFromError(value) {
|
|
3118
|
-
const payload =
|
|
3402
|
+
const payload = record3(value);
|
|
3119
3403
|
const message = typeof payload?.message === "string" && payload.message.trim() ? payload.message : undefined;
|
|
3120
3404
|
return message ? "OpenCode turn failed" : "OpenCode reported an inconsistent turn outcome";
|
|
3121
3405
|
}
|
|
@@ -3449,7 +3733,7 @@ class OpenCodeServiceLane {
|
|
|
3449
3733
|
const healthTimeoutMs = Math.max(1, Math.min(1000, deadline - Date.now(), this.options.requestTimeoutMs ?? REQUEST_TIMEOUT_MS));
|
|
3450
3734
|
const { response, body } = await this.fetchJsonWithTimeout("/global/health", { method: "GET" }, "health", healthTimeoutMs);
|
|
3451
3735
|
if (response.ok) {
|
|
3452
|
-
const health =
|
|
3736
|
+
const health = record3(body);
|
|
3453
3737
|
if (health?.healthy !== true || health.version !== SUPPORTED_VERSION) {
|
|
3454
3738
|
throw new OpenCodeIncompatibleError(`Installed OpenCode service must be version ${SUPPORTED_VERSION}`);
|
|
3455
3739
|
}
|
|
@@ -3470,8 +3754,8 @@ class OpenCodeServiceLane {
|
|
|
3470
3754
|
const { response, body } = await this.fetchJsonWithTimeout("/doc", { method: "GET" }, "OpenAPI");
|
|
3471
3755
|
if (!response.ok)
|
|
3472
3756
|
throw new OpenCodeIncompatibleError("Installed OpenCode service does not expose its OpenAPI document");
|
|
3473
|
-
const document =
|
|
3474
|
-
const paths =
|
|
3757
|
+
const document = record3(body);
|
|
3758
|
+
const paths = record3(document?.paths);
|
|
3475
3759
|
const required = [
|
|
3476
3760
|
"/api/session",
|
|
3477
3761
|
"/api/session/active",
|
|
@@ -3484,7 +3768,7 @@ class OpenCodeServiceLane {
|
|
|
3484
3768
|
"/api/session/{sessionID}/permission/{requestID}/reply",
|
|
3485
3769
|
"/api/event"
|
|
3486
3770
|
];
|
|
3487
|
-
if (!paths || required.some((path6) => !
|
|
3771
|
+
if (!paths || required.some((path6) => !record3(paths[path6]))) {
|
|
3488
3772
|
throw new OpenCodeIncompatibleError("Installed OpenCode service is missing required v2 session capabilities");
|
|
3489
3773
|
}
|
|
3490
3774
|
}
|
|
@@ -3497,7 +3781,7 @@ class OpenCodeServiceLane {
|
|
|
3497
3781
|
}
|
|
3498
3782
|
if (!response2.ok)
|
|
3499
3783
|
throw new OpenCodeHttpError(response2.status, "session resume");
|
|
3500
|
-
const session2 =
|
|
3784
|
+
const session2 = record3(record3(body2)?.data);
|
|
3501
3785
|
if (session2?.id !== resumeId) {
|
|
3502
3786
|
throw new OpenCodeResetRequiredError("OpenCode v2 returned a different resumed session; reset this agent before continuing");
|
|
3503
3787
|
}
|
|
@@ -3518,8 +3802,8 @@ class OpenCodeServiceLane {
|
|
|
3518
3802
|
}, "session create");
|
|
3519
3803
|
if (!response.ok)
|
|
3520
3804
|
throw new OpenCodeHttpError(response.status, "session create");
|
|
3521
|
-
const payload =
|
|
3522
|
-
const session =
|
|
3805
|
+
const payload = record3(responseBody);
|
|
3806
|
+
const session = record3(payload?.data);
|
|
3523
3807
|
if (typeof session?.id !== "string" || !/^ses/.test(session.id)) {
|
|
3524
3808
|
throw new Error("OpenCode v2 did not return a valid session id");
|
|
3525
3809
|
}
|
|
@@ -3676,7 +3960,7 @@ class OpenCodeServiceLane {
|
|
|
3676
3960
|
const { response, body: responseBody } = await this.fetchJsonWithTimeout(`/api/session/${encodeURIComponent(this.sessionId)}/history?after=${historyCursor}&limit=${HISTORY_PAGE_LIMIT}`, { method: "GET" }, "session history");
|
|
3677
3961
|
if (!response.ok)
|
|
3678
3962
|
throw new OpenCodeHttpError(response.status, "session history");
|
|
3679
|
-
const body =
|
|
3963
|
+
const body = record3(responseBody);
|
|
3680
3964
|
if (!Array.isArray(body?.data) || typeof body.hasMore !== "boolean") {
|
|
3681
3965
|
throw new OpenCodeProtocolError("OpenCode session history returned an invalid page");
|
|
3682
3966
|
}
|
|
@@ -3694,9 +3978,9 @@ class OpenCodeServiceLane {
|
|
|
3694
3978
|
return run;
|
|
3695
3979
|
}
|
|
3696
3980
|
async handleDurableEvent(value, project) {
|
|
3697
|
-
const event =
|
|
3698
|
-
const durable =
|
|
3699
|
-
const data =
|
|
3981
|
+
const event = record3(value);
|
|
3982
|
+
const durable = record3(event?.durable);
|
|
3983
|
+
const data = record3(event?.data);
|
|
3700
3984
|
if (!event || typeof event.id !== "string" || typeof event.type !== "string" || !durable || durable.aggregateID !== this.sessionId || !Number.isInteger(durable.seq) || Number(durable.seq) < 0 || data?.sessionID !== this.sessionId) {
|
|
3701
3985
|
throw new OpenCodeProtocolError("OpenCode session stream emitted an invalid durable event");
|
|
3702
3986
|
}
|
|
@@ -3786,9 +4070,9 @@ class OpenCodeServiceLane {
|
|
|
3786
4070
|
...!successful ? { message: "OpenCode reported an unsupported final step outcome" } : {}
|
|
3787
4071
|
});
|
|
3788
4072
|
}
|
|
3789
|
-
const tokens =
|
|
4073
|
+
const tokens = record3(data.tokens);
|
|
3790
4074
|
if (tokens && data.finish !== "tool-calls") {
|
|
3791
|
-
const cache =
|
|
4075
|
+
const cache = record3(tokens.cache);
|
|
3792
4076
|
const metric2 = (value2) => typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0 ? value2 : null;
|
|
3793
4077
|
const cacheParts = [cache?.read, cache?.write].filter((value2) => typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0);
|
|
3794
4078
|
const cacheTotal = cacheParts.reduce((sum, value2) => sum + value2, 0);
|
|
@@ -3815,8 +4099,8 @@ class OpenCodeServiceLane {
|
|
|
3815
4099
|
return seq;
|
|
3816
4100
|
}
|
|
3817
4101
|
async handleLiveEvent(value) {
|
|
3818
|
-
const event =
|
|
3819
|
-
const data =
|
|
4102
|
+
const event = record3(value);
|
|
4103
|
+
const data = record3(event?.data);
|
|
3820
4104
|
if (event?.type !== "permission.v2.asked" || data?.sessionID !== this.sessionId)
|
|
3821
4105
|
return;
|
|
3822
4106
|
if (typeof data.id !== "string" || !/^per/.test(data.id)) {
|
|
@@ -3830,11 +4114,11 @@ class OpenCodeServiceLane {
|
|
|
3830
4114
|
const { response, body: responseBody } = await this.fetchJsonWithTimeout(`/api/session/${encodeURIComponent(this.sessionId)}/permission`, { method: "GET" }, "permission list");
|
|
3831
4115
|
if (!response.ok)
|
|
3832
4116
|
throw new OpenCodeHttpError(response.status, "permission list");
|
|
3833
|
-
const body =
|
|
4117
|
+
const body = record3(responseBody);
|
|
3834
4118
|
if (!Array.isArray(body?.data))
|
|
3835
4119
|
throw new OpenCodeProtocolError("OpenCode permission list returned invalid data");
|
|
3836
4120
|
for (const item of body.data) {
|
|
3837
|
-
const permission =
|
|
4121
|
+
const permission = record3(item);
|
|
3838
4122
|
if (permission?.sessionID === this.sessionId && typeof permission.id === "string") {
|
|
3839
4123
|
await this.replyPermission(permission.id);
|
|
3840
4124
|
}
|
|
@@ -3901,8 +4185,8 @@ class OpenCodeServiceLane {
|
|
|
3901
4185
|
}, "prompt admission");
|
|
3902
4186
|
if (!response.ok)
|
|
3903
4187
|
throw new OpenCodeHttpError(response.status, "prompt admission");
|
|
3904
|
-
const body =
|
|
3905
|
-
const admitted =
|
|
4188
|
+
const body = record3(responseBody);
|
|
4189
|
+
const admitted = record3(body?.data);
|
|
3906
4190
|
if (admitted?.id !== messageId || admitted.sessionID !== this.sessionId || admitted.delivery !== delivery || !Number.isInteger(admitted.admittedSeq) || Number(admitted.admittedSeq) < 0) {
|
|
3907
4191
|
throw new OpenCodeProtocolError("OpenCode prompt admission returned an invalid receipt");
|
|
3908
4192
|
}
|
|
@@ -3969,8 +4253,8 @@ class OpenCodeServiceLane {
|
|
|
3969
4253
|
const { response, body: responseBody } = await this.fetchJsonWithTimeout("/api/session/active", { method: "GET" }, "active session query");
|
|
3970
4254
|
if (!response.ok)
|
|
3971
4255
|
throw new OpenCodeHttpError(response.status, "active session query");
|
|
3972
|
-
const body =
|
|
3973
|
-
const active =
|
|
4256
|
+
const body = record3(responseBody);
|
|
4257
|
+
const active = record3(body?.data);
|
|
3974
4258
|
if (!active)
|
|
3975
4259
|
throw new OpenCodeProtocolError("OpenCode active session query returned invalid data");
|
|
3976
4260
|
if (!this.barrierStillCurrent(root, identity, generation))
|
|
@@ -4175,6 +4459,7 @@ function createOpenCodeMessageId() {
|
|
|
4175
4459
|
}
|
|
4176
4460
|
|
|
4177
4461
|
class OpenCodeDriver {
|
|
4462
|
+
outputProbe;
|
|
4178
4463
|
id = "opencode";
|
|
4179
4464
|
instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
|
|
4180
4465
|
execution = {
|
|
@@ -4183,8 +4468,19 @@ class OpenCodeDriver {
|
|
|
4183
4468
|
wakeStart: "immediate",
|
|
4184
4469
|
terminalOwnership: "transport_request"
|
|
4185
4470
|
};
|
|
4471
|
+
constructor(outputProbe = probeCommandOutput) {
|
|
4472
|
+
this.outputProbe = outputProbe;
|
|
4473
|
+
}
|
|
4186
4474
|
probe(command) {
|
|
4187
|
-
|
|
4475
|
+
const result = probeCliRuntime("opencode", {}, command);
|
|
4476
|
+
if (result.status !== "healthy")
|
|
4477
|
+
return result;
|
|
4478
|
+
const spec = resolveSpawnSpec("opencode", ["models", "--pure"], command);
|
|
4479
|
+
const output = this.outputProbe(spec.command, spec.args);
|
|
4480
|
+
return {
|
|
4481
|
+
...result,
|
|
4482
|
+
reasoning: output.ok ? parseOpenCodeModelCatalog(output.output) : undefined
|
|
4483
|
+
};
|
|
4188
4484
|
}
|
|
4189
4485
|
beginTurn() {
|
|
4190
4486
|
return createOpenCodeMessageId();
|
|
@@ -4438,6 +4734,7 @@ function createPiSessionDependencies(ctx, loadSdk = loadPiSdkModule) {
|
|
|
4438
4734
|
|
|
4439
4735
|
// agent-driver/dist/adapters/pi/index.js
|
|
4440
4736
|
var PI_SDK_PACKAGE_NAME2 = "@earendil-works/pi-coding-agent";
|
|
4737
|
+
var PI_MODEL_PROBE_TIMEOUT_MS = 5000;
|
|
4441
4738
|
function isPiSdkPackageJson(pkgJsonPath) {
|
|
4442
4739
|
if (!existsSync3(pkgJsonPath))
|
|
4443
4740
|
return false;
|
|
@@ -4546,6 +4843,8 @@ function mapPiSdkEvent(event, sessionId, state) {
|
|
|
4546
4843
|
|
|
4547
4844
|
class PiDriver {
|
|
4548
4845
|
dependenciesFor;
|
|
4846
|
+
loadSdk;
|
|
4847
|
+
readVersion;
|
|
4549
4848
|
id = "pi";
|
|
4550
4849
|
instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
|
|
4551
4850
|
execution = {
|
|
@@ -4556,15 +4855,36 @@ class PiDriver {
|
|
|
4556
4855
|
};
|
|
4557
4856
|
sessionId = null;
|
|
4558
4857
|
terminalSequence = 0;
|
|
4559
|
-
constructor(dependenciesFor = createPiSessionDependencies) {
|
|
4858
|
+
constructor(dependenciesFor = createPiSessionDependencies, loadSdk = loadPiSdkModule, readVersion = readPiSdkVersion) {
|
|
4560
4859
|
this.dependenciesFor = dependenciesFor;
|
|
4860
|
+
this.loadSdk = loadSdk;
|
|
4861
|
+
this.readVersion = readVersion;
|
|
4561
4862
|
}
|
|
4562
|
-
probe() {
|
|
4563
|
-
const version =
|
|
4863
|
+
async probe() {
|
|
4864
|
+
const version = this.readVersion();
|
|
4564
4865
|
if (!version) {
|
|
4565
4866
|
return { status: "unhealthy", lastError: "sdk_not_installed" };
|
|
4566
4867
|
}
|
|
4567
|
-
|
|
4868
|
+
let timer;
|
|
4869
|
+
try {
|
|
4870
|
+
const reasoning = await Promise.race([
|
|
4871
|
+
this.loadSdk().then(async (sdk) => {
|
|
4872
|
+
const authStorage = sdk.AuthStorage.create();
|
|
4873
|
+
const registry = sdk.ModelRegistry.create(authStorage);
|
|
4874
|
+
return parsePiModelCatalog(await registry.getAvailable());
|
|
4875
|
+
}),
|
|
4876
|
+
new Promise((resolve3) => {
|
|
4877
|
+
timer = setTimeout(() => resolve3(undefined), PI_MODEL_PROBE_TIMEOUT_MS);
|
|
4878
|
+
timer.unref?.();
|
|
4879
|
+
})
|
|
4880
|
+
]);
|
|
4881
|
+
return { status: "healthy", version, reasoning };
|
|
4882
|
+
} catch {
|
|
4883
|
+
return { status: "healthy", version, reasoning: undefined };
|
|
4884
|
+
} finally {
|
|
4885
|
+
if (timer)
|
|
4886
|
+
clearTimeout(timer);
|
|
4887
|
+
}
|
|
4568
4888
|
}
|
|
4569
4889
|
async openLane(ctx) {
|
|
4570
4890
|
const deps = this.dependenciesFor(ctx);
|
|
@@ -6596,8 +6916,8 @@ async function readClaudeQuota(options) {
|
|
|
6596
6916
|
if (!body || typeof body !== "object") {
|
|
6597
6917
|
return { status: "error", sourceEpoch: claudeSourceEpoch, code: "invalid_response", retryable: true };
|
|
6598
6918
|
}
|
|
6599
|
-
const
|
|
6600
|
-
const limits = ["five_hour", "seven_day", "seven_day_sonnet", "seven_day_opus"].map((key) => claudeLimit(key,
|
|
6919
|
+
const record4 = body;
|
|
6920
|
+
const limits = ["five_hour", "seven_day", "seven_day_sonnet", "seven_day_opus"].map((key) => claudeLimit(key, record4[key])).filter((limit) => limit !== null);
|
|
6601
6921
|
if (limits.length === 0) {
|
|
6602
6922
|
return { status: "error", sourceEpoch: claudeSourceEpoch, code: "invalid_response", retryable: true };
|
|
6603
6923
|
}
|
|
@@ -7254,10 +7574,10 @@ function stableNormalizeApmHeldFreshness(value) {
|
|
|
7254
7574
|
return value.map((item) => stableNormalizeApmHeldFreshness(item));
|
|
7255
7575
|
if (!value || typeof value !== "object")
|
|
7256
7576
|
return value;
|
|
7257
|
-
const
|
|
7577
|
+
const record4 = value;
|
|
7258
7578
|
const normalized = {};
|
|
7259
|
-
for (const key of Object.keys(
|
|
7260
|
-
normalized[key] = stableNormalizeApmHeldFreshness(
|
|
7579
|
+
for (const key of Object.keys(record4).sort()) {
|
|
7580
|
+
normalized[key] = stableNormalizeApmHeldFreshness(record4[key]);
|
|
7261
7581
|
}
|
|
7262
7582
|
return normalized;
|
|
7263
7583
|
}
|
|
@@ -7373,13 +7693,13 @@ function reduceManager(state, event) {
|
|
|
7373
7693
|
const existing = state.agents[event.agentId];
|
|
7374
7694
|
if (!existing || existing.execution.sessionInstanceId !== event.sessionInstanceId)
|
|
7375
7695
|
return { state, effects: [] };
|
|
7376
|
-
const
|
|
7377
|
-
if (!
|
|
7696
|
+
const record4 = existing.pendingAdmissions.find((entry) => entry.sessionInstanceId === event.sessionInstanceId && entry.commandId === event.commandId);
|
|
7697
|
+
if (!record4)
|
|
7378
7698
|
return { state, effects: [] };
|
|
7379
7699
|
const agent = clone(existing);
|
|
7380
7700
|
agent.pendingAdmissions = agent.pendingAdmissions.filter((entry) => entry.sessionInstanceId !== event.sessionInstanceId || entry.commandId !== event.commandId);
|
|
7381
7701
|
syncExecutionProjection(agent);
|
|
7382
|
-
return commit(state, agent, event.outcome === "failed" ? recoveryEffects(agent, [
|
|
7702
|
+
return commit(state, agent, event.outcome === "failed" ? recoveryEffects(agent, [record4]) : []);
|
|
7383
7703
|
}
|
|
7384
7704
|
case "admission_acknowledged": {
|
|
7385
7705
|
const existing = state.agents[event.agentId];
|
|
@@ -7897,11 +8217,11 @@ function syncExecutionProjection(agent) {
|
|
|
7897
8217
|
agent.lastDeliverAt = agent.pendingAdmissions.length > 0 ? Math.max(...agent.pendingAdmissions.map((entry) => entry.admittedAt)) : null;
|
|
7898
8218
|
}
|
|
7899
8219
|
function recoveryEffects(agent, records) {
|
|
7900
|
-
return records.filter((
|
|
8220
|
+
return records.filter((record4) => record4.requeueOnFailure).map((record4) => ({
|
|
7901
8221
|
type: "requeue_delivery",
|
|
7902
8222
|
agentId: agent.agentId,
|
|
7903
|
-
message:
|
|
7904
|
-
mode:
|
|
8223
|
+
message: record4.exactAgentMsg,
|
|
8224
|
+
mode: record4.mode
|
|
7905
8225
|
}));
|
|
7906
8226
|
}
|
|
7907
8227
|
function commit(state, agent, effects) {
|
|
@@ -8312,14 +8632,14 @@ function createLogger(options = {}) {
|
|
|
8312
8632
|
`));
|
|
8313
8633
|
const err = options.err ?? ((line) => process.stderr.write(line + `
|
|
8314
8634
|
`));
|
|
8315
|
-
const
|
|
8635
|
+
const record4 = options.record;
|
|
8316
8636
|
const emit = (level, message, data) => {
|
|
8317
8637
|
if (LEVEL_RANK[level] < minRank)
|
|
8318
8638
|
return;
|
|
8319
8639
|
const time = now();
|
|
8320
8640
|
const line = `${time} ${header} ${level.toUpperCase().padEnd(5)} ${message}${formatData(data)}`;
|
|
8321
8641
|
try {
|
|
8322
|
-
|
|
8642
|
+
record4?.({ time, header, level, message, fields: recordFields(data) });
|
|
8323
8643
|
} catch {}
|
|
8324
8644
|
(level === "warn" || level === "error" ? err : out)(line);
|
|
8325
8645
|
};
|
|
@@ -10058,7 +10378,7 @@ __export(exports_external, {
|
|
|
10058
10378
|
regexes: () => exports_regexes,
|
|
10059
10379
|
regex: () => _regex,
|
|
10060
10380
|
refine: () => refine,
|
|
10061
|
-
record: () =>
|
|
10381
|
+
record: () => record4,
|
|
10062
10382
|
readonly: () => readonly,
|
|
10063
10383
|
property: () => _property,
|
|
10064
10384
|
promise: () => promise,
|
|
@@ -22214,7 +22534,7 @@ __export(exports_schemas2, {
|
|
|
22214
22534
|
strictObject: () => strictObject,
|
|
22215
22535
|
set: () => set,
|
|
22216
22536
|
refine: () => refine,
|
|
22217
|
-
record: () =>
|
|
22537
|
+
record: () => record4,
|
|
22218
22538
|
readonly: () => readonly,
|
|
22219
22539
|
promise: () => promise,
|
|
22220
22540
|
preprocess: () => preprocess,
|
|
@@ -23291,7 +23611,7 @@ var ZodRecord = /* @__PURE__ */ $constructor("ZodRecord", (inst, def) => {
|
|
|
23291
23611
|
inst.keyType = def.keyType;
|
|
23292
23612
|
inst.valueType = def.valueType;
|
|
23293
23613
|
});
|
|
23294
|
-
function
|
|
23614
|
+
function record4(keyType, valueType, params) {
|
|
23295
23615
|
if (!valueType || !valueType._zod) {
|
|
23296
23616
|
return new ZodRecord({
|
|
23297
23617
|
type: "record",
|
|
@@ -23756,7 +24076,7 @@ var stringbool = (...args) => _stringbool({
|
|
|
23756
24076
|
}, ...args);
|
|
23757
24077
|
function json(params) {
|
|
23758
24078
|
const jsonSchema = lazy(() => {
|
|
23759
|
-
return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema),
|
|
24079
|
+
return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema), record4(string2(), jsonSchema)]);
|
|
23760
24080
|
});
|
|
23761
24081
|
return jsonSchema;
|
|
23762
24082
|
}
|
|
@@ -26955,11 +27275,10 @@ class AgentRouter {
|
|
|
26955
27275
|
return;
|
|
26956
27276
|
if (existing.status === "healthy" && !existing.lastError && !existing.lastErrorAt)
|
|
26957
27277
|
return;
|
|
26958
|
-
|
|
26959
|
-
|
|
26960
|
-
|
|
26961
|
-
|
|
26962
|
-
});
|
|
27278
|
+
const healthy = { ...existing, status: "healthy" };
|
|
27279
|
+
delete healthy.lastError;
|
|
27280
|
+
delete healthy.lastErrorAt;
|
|
27281
|
+
this.runtimes.set(id, healthy);
|
|
26963
27282
|
this.log.info("runtime marked healthy again", { runtimeId: id });
|
|
26964
27283
|
this.scheduleReadyFrameResend();
|
|
26965
27284
|
}
|
|
@@ -27379,20 +27698,20 @@ function parseLocalMessageReminderBody(body, agentId) {
|
|
|
27379
27698
|
}
|
|
27380
27699
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
27381
27700
|
return null;
|
|
27382
|
-
const
|
|
27383
|
-
if (Object.keys(
|
|
27701
|
+
const record5 = value;
|
|
27702
|
+
if (Object.keys(record5).sort().join(",") !== "channel,remindAfterMs,sentSeq")
|
|
27384
27703
|
return null;
|
|
27385
|
-
if (typeof
|
|
27704
|
+
if (typeof record5.channel !== "string" || !isCanonicalChannelScope(record5.channel))
|
|
27386
27705
|
return null;
|
|
27387
|
-
if (!Number.isSafeInteger(
|
|
27706
|
+
if (!Number.isSafeInteger(record5.sentSeq) || record5.sentSeq < 1)
|
|
27388
27707
|
return null;
|
|
27389
|
-
if (!Number.isSafeInteger(
|
|
27708
|
+
if (!Number.isSafeInteger(record5.remindAfterMs) || record5.remindAfterMs !== 0 && record5.remindAfterMs < LOCAL_MESSAGE_REMINDER_MIN_MS || record5.remindAfterMs > LOCAL_MESSAGE_REMINDER_MAX_MS)
|
|
27390
27709
|
return null;
|
|
27391
27710
|
return {
|
|
27392
27711
|
agentId,
|
|
27393
|
-
channel:
|
|
27394
|
-
sentSeq:
|
|
27395
|
-
remindAfterMs:
|
|
27712
|
+
channel: record5.channel,
|
|
27713
|
+
sentSeq: record5.sentSeq,
|
|
27714
|
+
remindAfterMs: record5.remindAfterMs
|
|
27396
27715
|
};
|
|
27397
27716
|
}
|
|
27398
27717
|
async function handleLocalMessageReminder(req, res, agentId, onArm) {
|
|
@@ -28529,7 +28848,7 @@ var REASONING_EFFORT_RE = /^[A-Za-z0-9._-]+$/;
|
|
|
28529
28848
|
var COMMUNITY_REASONING_EFFORT_MAX = 32;
|
|
28530
28849
|
var COMMUNITY_REASONING_DESCRIPTION_MAX = 256;
|
|
28531
28850
|
var COMMUNITY_REASONING_OPTIONS_MAX = 16;
|
|
28532
|
-
var COMMUNITY_REASONING_MODELS_MAX =
|
|
28851
|
+
var COMMUNITY_REASONING_MODELS_MAX = 512;
|
|
28533
28852
|
var ReasoningEffortSchema = exports_external.string().min(1).max(COMMUNITY_REASONING_EFFORT_MAX).regex(REASONING_EFFORT_RE, "invalid reasoning effort charset");
|
|
28534
28853
|
var RuntimeReasoningOptionSchema = exports_external.object({
|
|
28535
28854
|
value: ReasoningEffortSchema,
|
|
@@ -28537,6 +28856,7 @@ var RuntimeReasoningOptionSchema = exports_external.object({
|
|
|
28537
28856
|
});
|
|
28538
28857
|
var RuntimeReasoningModelSchema = exports_external.object({
|
|
28539
28858
|
id: exports_external.string().min(1).max(100),
|
|
28859
|
+
displayName: exports_external.string().min(1).max(COMMUNITY_REASONING_DESCRIPTION_MAX).optional().catch(undefined),
|
|
28540
28860
|
supportedReasoningEfforts: exports_external.array(exports_external.unknown()).max(COMMUNITY_REASONING_OPTIONS_MAX).transform((options) => {
|
|
28541
28861
|
const seen = new Set;
|
|
28542
28862
|
return options.flatMap((candidate) => {
|
|
@@ -32111,15 +32431,15 @@ class MessageReminderScheduler {
|
|
|
32111
32431
|
const startedAt = this.now();
|
|
32112
32432
|
const dueAt = startedAt + input.remindAfterMs;
|
|
32113
32433
|
const sentRef = `${input.channel}#${input.sentSeq}`;
|
|
32114
|
-
const
|
|
32434
|
+
const record5 = {
|
|
32115
32435
|
...input,
|
|
32116
32436
|
sentRef,
|
|
32117
32437
|
startedAt,
|
|
32118
32438
|
dueAt,
|
|
32119
32439
|
timer: undefined
|
|
32120
32440
|
};
|
|
32121
|
-
|
|
32122
|
-
if (this.reminders.get(key) !==
|
|
32441
|
+
record5.timer = this.setTimer(() => {
|
|
32442
|
+
if (this.reminders.get(key) !== record5)
|
|
32123
32443
|
return;
|
|
32124
32444
|
this.reminders.delete(key);
|
|
32125
32445
|
try {
|
|
@@ -32130,8 +32450,8 @@ class MessageReminderScheduler {
|
|
|
32130
32450
|
Promise.resolve(delivery).catch(() => {});
|
|
32131
32451
|
} catch {}
|
|
32132
32452
|
}, input.remindAfterMs);
|
|
32133
|
-
|
|
32134
|
-
this.reminders.set(key,
|
|
32453
|
+
record5.timer.unref?.();
|
|
32454
|
+
this.reminders.set(key, record5);
|
|
32135
32455
|
return { armed: true, dueAt };
|
|
32136
32456
|
}
|
|
32137
32457
|
observe(agentId, channel2, latestSeq) {
|