@slock-ai/daemon 0.53.1-alpha.3 → 0.53.1-alpha.4
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/chat-bridge.js +5 -11
- package/dist/{chunk-QVULOADZ.js → chunk-WJZJFOKB.js} +682 -256
- package/dist/cli/index.js +2 -1
- package/dist/core.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -723,6 +723,7 @@ var SERVER_CAPABILITY_MATRIX = {
|
|
|
723
723
|
var RUNTIMES = [
|
|
724
724
|
{ id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
|
|
725
725
|
{ id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
|
|
726
|
+
{ id: "antigravity", displayName: "Antigravity CLI", binary: "agy", supported: true },
|
|
726
727
|
{ id: "kimi", displayName: "Kimi CLI", binary: "kimi", supported: true },
|
|
727
728
|
{ id: "copilot", displayName: "Copilot CLI", binary: "copilot", supported: true },
|
|
728
729
|
{ id: "cursor", displayName: "Cursor CLI", binary: "cursor-agent", supported: true },
|
|
@@ -832,8 +833,9 @@ function buildPrompt(config, variant, opts) {
|
|
|
832
833
|
"- Use only the provided MCP tools for messaging \u2014 they are already available and ready.",
|
|
833
834
|
`- Always claim a task via ${taskClaimCmd} before starting work on it. If the claim fails, move on to a different task.`
|
|
834
835
|
];
|
|
835
|
-
const
|
|
836
|
-
|
|
836
|
+
const runtimeProfileControl = config.runtimeProfileControl?.kind === "daemon_release_notice" ? config.runtimeProfileControl : null;
|
|
837
|
+
const runtimeProfileControlStartupStep = runtimeProfileControl ? [
|
|
838
|
+
"0. If this system prompt contains a **Runtime Profile Control** section, read that notice first. It is informational only; no runtime control action or chat reply is required. Do not read MEMORY.md, check messages, or respond to inbox messages before reading it."
|
|
837
839
|
] : [];
|
|
838
840
|
const startupSteps = isCli ? [
|
|
839
841
|
...runtimeProfileControlStartupStep,
|
|
@@ -1105,28 +1107,19 @@ ${startupSteps.join("\n")}`;
|
|
|
1105
1107
|
|
|
1106
1108
|
${opts.postStartupNotes.join("\n")}`;
|
|
1107
1109
|
}
|
|
1108
|
-
if (
|
|
1109
|
-
const control =
|
|
1110
|
+
if (runtimeProfileControl) {
|
|
1111
|
+
const control = runtimeProfileControl;
|
|
1110
1112
|
prompt += `
|
|
1111
1113
|
|
|
1112
1114
|
## Runtime Profile Control
|
|
1113
1115
|
|
|
1114
1116
|
`;
|
|
1115
|
-
prompt += `This section is a trusted daemon
|
|
1117
|
+
prompt += `This section is a trusted daemon release notice. Read it before normal startup work; it does not require a runtime control action, chat reply, or migration acknowledgment.
|
|
1116
1118
|
|
|
1117
1119
|
`;
|
|
1118
|
-
|
|
1119
|
-
prompt += `You are currently in Runtime Profile migration. Before handling normal inbox messages, re-ground yourself in the new runtime context and then invoke the runtime control action \`${t("runtime_profile_migration_done")}\` with exactly this migration_key: \`${control.key}\`.
|
|
1120
|
-
|
|
1121
|
-
`;
|
|
1122
|
-
prompt += `Do not use ${sendCmd}, ${checkCmd}, or any chat reply as the migration acknowledgment. Normal inbox delivery is gated until the runtime control action succeeds.
|
|
1120
|
+
prompt += `Read the daemon release notice below before handling normal inbox messages. No chat reply is required for this notice.
|
|
1123
1121
|
|
|
1124
1122
|
`;
|
|
1125
|
-
} else {
|
|
1126
|
-
prompt += `Read the daemon release notice below before handling normal inbox messages. No chat reply is required for this notice.
|
|
1127
|
-
|
|
1128
|
-
`;
|
|
1129
|
-
}
|
|
1130
1123
|
prompt += control.message;
|
|
1131
1124
|
}
|
|
1132
1125
|
prompt += `
|
|
@@ -1367,30 +1360,85 @@ import { randomBytes } from "crypto";
|
|
|
1367
1360
|
import http from "http";
|
|
1368
1361
|
import { URL as URL2 } from "url";
|
|
1369
1362
|
var registrations = /* @__PURE__ */ new Map();
|
|
1370
|
-
var
|
|
1363
|
+
var proxyServerState = null;
|
|
1364
|
+
var proxyServerStartPromise = null;
|
|
1365
|
+
var proxyServerFactory = createProxyServer;
|
|
1371
1366
|
var DECODED_RESPONSE_HEADERS = /* @__PURE__ */ new Set(["content-encoding", "content-length", "transfer-encoding"]);
|
|
1372
1367
|
var LOCAL_HELD_CONTEXT_LIMIT = 3;
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1368
|
+
var AGENT_CREDENTIAL_PROXY_HOST = "127.0.0.1";
|
|
1369
|
+
var AGENT_CREDENTIAL_PROXY_BIND_MAX_ATTEMPTS = 3;
|
|
1370
|
+
function createProxyRequestHandler() {
|
|
1371
|
+
return (req, res) => {
|
|
1372
|
+
void handleProxyRequest(req, res);
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
function createProxyServer(handler) {
|
|
1376
|
+
return http.createServer(handler);
|
|
1377
|
+
}
|
|
1378
|
+
function listenOnLoopback(server) {
|
|
1379
|
+
return new Promise((resolve, reject) => {
|
|
1380
|
+
const onError = (err) => {
|
|
1381
|
+
server.off("listening", onListening);
|
|
1382
|
+
reject(err);
|
|
1383
|
+
};
|
|
1384
|
+
const onListening = () => {
|
|
1385
|
+
server.off("error", onError);
|
|
1386
|
+
const address = server.address();
|
|
1387
|
+
if (!address || typeof address.port !== "number") {
|
|
1388
|
+
reject(new Error("local proxy listen succeeded without a TCP port"));
|
|
1389
|
+
return;
|
|
1390
|
+
}
|
|
1391
|
+
resolve({
|
|
1392
|
+
server,
|
|
1393
|
+
proxyUrl: `http://${AGENT_CREDENTIAL_PROXY_HOST}:${address.port}`
|
|
1385
1394
|
});
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1395
|
+
};
|
|
1396
|
+
server.once("error", onError);
|
|
1397
|
+
server.once("listening", onListening);
|
|
1398
|
+
server.listen(0, AGENT_CREDENTIAL_PROXY_HOST);
|
|
1390
1399
|
});
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1400
|
+
}
|
|
1401
|
+
function closeServerBestEffort(server) {
|
|
1402
|
+
try {
|
|
1403
|
+
server.close();
|
|
1404
|
+
} catch {
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
async function startProxyServer() {
|
|
1408
|
+
let lastError;
|
|
1409
|
+
for (let attempt = 1; attempt <= AGENT_CREDENTIAL_PROXY_BIND_MAX_ATTEMPTS; attempt += 1) {
|
|
1410
|
+
const server = proxyServerFactory(createProxyRequestHandler());
|
|
1411
|
+
try {
|
|
1412
|
+
const state = await listenOnLoopback(server);
|
|
1413
|
+
state.server.on("error", (err) => {
|
|
1414
|
+
logger.warn(`[Agent Credential Proxy] local proxy failed after bind: ${err.message}`);
|
|
1415
|
+
});
|
|
1416
|
+
state.server.unref();
|
|
1417
|
+
return state;
|
|
1418
|
+
} catch (err) {
|
|
1419
|
+
lastError = err;
|
|
1420
|
+
closeServerBestEffort(server);
|
|
1421
|
+
logger.warn(
|
|
1422
|
+
`[Agent Credential Proxy] local proxy bind attempt ${attempt}/${AGENT_CREDENTIAL_PROXY_BIND_MAX_ATTEMPTS} failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1423
|
+
);
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
const detail = lastError instanceof Error ? lastError.message : String(lastError ?? "unknown error");
|
|
1427
|
+
throw new Error(
|
|
1428
|
+
`Agent Credential Proxy local proxy failed to bind ${AGENT_CREDENTIAL_PROXY_HOST} after ${AGENT_CREDENTIAL_PROXY_BIND_MAX_ATTEMPTS} attempts: ${detail}`
|
|
1429
|
+
);
|
|
1430
|
+
}
|
|
1431
|
+
async function ensureServer() {
|
|
1432
|
+
if (proxyServerState) return proxyServerState;
|
|
1433
|
+
if (!proxyServerStartPromise) {
|
|
1434
|
+
proxyServerStartPromise = startProxyServer();
|
|
1435
|
+
}
|
|
1436
|
+
try {
|
|
1437
|
+
proxyServerState = await proxyServerStartPromise;
|
|
1438
|
+
return proxyServerState;
|
|
1439
|
+
} finally {
|
|
1440
|
+
proxyServerStartPromise = null;
|
|
1441
|
+
}
|
|
1394
1442
|
}
|
|
1395
1443
|
function responseHeadersForLocalProxy(upstream) {
|
|
1396
1444
|
const headers = {};
|
|
@@ -1411,7 +1459,6 @@ async function handleProxyRequest(req, res) {
|
|
|
1411
1459
|
}
|
|
1412
1460
|
const method = req.method ?? "GET";
|
|
1413
1461
|
let target;
|
|
1414
|
-
let upstreamStatus = null;
|
|
1415
1462
|
try {
|
|
1416
1463
|
target = new URL2(req.url ?? "/", registration.serverUrl);
|
|
1417
1464
|
const headers = new Headers();
|
|
@@ -1461,7 +1508,6 @@ async function handleProxyRequest(req, res) {
|
|
|
1461
1508
|
// Required by undici when forwarding a Node stream.
|
|
1462
1509
|
duplex: body ? "half" : void 0
|
|
1463
1510
|
});
|
|
1464
|
-
upstreamStatus = upstream.status;
|
|
1465
1511
|
if (shouldBufferJsonResponse(upstream, target.pathname, registration)) {
|
|
1466
1512
|
const responseText = await upstream.text();
|
|
1467
1513
|
consumeVisibleResponse(registration, target, sendTarget, responseText);
|
|
@@ -1485,45 +1531,26 @@ async function handleProxyRequest(req, res) {
|
|
|
1485
1531
|
res.end();
|
|
1486
1532
|
}
|
|
1487
1533
|
} catch (err) {
|
|
1488
|
-
const failure = proxyFailureForError(method, target,
|
|
1534
|
+
const failure = proxyFailureForError(method, target, err);
|
|
1489
1535
|
logger.warn(
|
|
1490
|
-
`[Agent Credential Proxy] request failed (agent=${registration.agentId}, launch=${registration.launchId ?? "none"}, method=${failure.method}, path=${failure.pathname}, query_keys=${failure.queryKeys.join(",") || "none"}): ${failure.
|
|
1536
|
+
`[Agent Credential Proxy] request failed (agent=${registration.agentId}, launch=${registration.launchId ?? "none"}, method=${failure.method}, path=${failure.pathname}, query_keys=${failure.queryKeys.join(",") || "none"}): ${failure.errorName}: ${failure.errorMessage}`
|
|
1491
1537
|
);
|
|
1492
1538
|
registration.inboxCoordinator?.recordProxyFailure?.(failure);
|
|
1493
|
-
|
|
1539
|
+
res.writeHead(502, { "content-type": "application/json" });
|
|
1540
|
+
res.end(JSON.stringify({
|
|
1494
1541
|
error: "failed to proxy local agent request",
|
|
1495
1542
|
code: "agent_proxy_failed",
|
|
1496
1543
|
detail: failure.errorMessage
|
|
1497
|
-
});
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
|
-
function writeProxyFailureResponse(res, body) {
|
|
1501
|
-
if (res.destroyed || res.writableEnded) return;
|
|
1502
|
-
if (proxyResponseStarted(res)) {
|
|
1503
|
-
try {
|
|
1504
|
-
res.end();
|
|
1505
|
-
} catch {
|
|
1506
|
-
}
|
|
1507
|
-
return;
|
|
1508
|
-
}
|
|
1509
|
-
try {
|
|
1510
|
-
res.writeHead(502, { "content-type": "application/json" });
|
|
1511
|
-
res.end(JSON.stringify(body));
|
|
1512
|
-
} catch {
|
|
1544
|
+
}));
|
|
1513
1545
|
}
|
|
1514
1546
|
}
|
|
1515
|
-
function
|
|
1516
|
-
return res.headersSent || res.writableEnded;
|
|
1517
|
-
}
|
|
1518
|
-
function proxyFailureForError(method, target, res, upstreamStatus, err) {
|
|
1547
|
+
function proxyFailureForError(method, target, err) {
|
|
1519
1548
|
const queryKeys = target ? [.../* @__PURE__ */ new Set([...target.searchParams.keys()])].sort() : [];
|
|
1520
1549
|
return {
|
|
1521
1550
|
method,
|
|
1522
1551
|
pathname: target?.pathname ?? "unknown",
|
|
1523
1552
|
queryKeys,
|
|
1524
|
-
|
|
1525
|
-
upstreamStatus,
|
|
1526
|
-
errorClass: err instanceof Error ? err.name : typeof err,
|
|
1553
|
+
errorName: err instanceof Error ? err.name : typeof err,
|
|
1527
1554
|
errorMessage: truncateProxyErrorMessage(err instanceof Error ? err.message : String(err))
|
|
1528
1555
|
};
|
|
1529
1556
|
}
|
|
@@ -1882,9 +1909,8 @@ function consumeVisibleResponse(registration, targetUrl, sendTarget, responseTex
|
|
|
1882
1909
|
coordinator.consumeVisibleMessages({ target, messages, boundarySeq: maxMessageSeq(messages), source: "agent_api_history" });
|
|
1883
1910
|
}
|
|
1884
1911
|
}
|
|
1885
|
-
function registerAgentCredentialProxy(input) {
|
|
1886
|
-
const
|
|
1887
|
-
ensureServer(port);
|
|
1912
|
+
async function registerAgentCredentialProxy(input) {
|
|
1913
|
+
const server = await ensureServer();
|
|
1888
1914
|
const proxyToken = `sap_${randomBytes(32).toString("base64url")}`;
|
|
1889
1915
|
registrations.set(proxyToken, {
|
|
1890
1916
|
serverUrl: input.serverUrl,
|
|
@@ -1895,7 +1921,7 @@ function registerAgentCredentialProxy(input) {
|
|
|
1895
1921
|
inboxCoordinator: input.inboxCoordinator
|
|
1896
1922
|
});
|
|
1897
1923
|
return {
|
|
1898
|
-
proxyUrl:
|
|
1924
|
+
proxyUrl: server.proxyUrl,
|
|
1899
1925
|
proxyToken
|
|
1900
1926
|
};
|
|
1901
1927
|
}
|
|
@@ -1975,7 +2001,7 @@ function runtimeContextEnv(config) {
|
|
|
1975
2001
|
function buildCliTransportSystemPrompt(config, opts) {
|
|
1976
2002
|
return buildCliSystemPrompt(config, opts);
|
|
1977
2003
|
}
|
|
1978
|
-
function prepareCliTransport(ctx, extraEnv = {}, platform = process.platform) {
|
|
2004
|
+
async function prepareCliTransport(ctx, extraEnv = {}, platform = process.platform) {
|
|
1979
2005
|
if (!ctx.slockCliPath) {
|
|
1980
2006
|
throw new Error(`${ctx.config.runtime} driver: slockCliPath is required (daemon must inject it)`);
|
|
1981
2007
|
}
|
|
@@ -1988,7 +2014,7 @@ function prepareCliTransport(ctx, extraEnv = {}, platform = process.platform) {
|
|
|
1988
2014
|
let agentCredentialProxyTokenFile = null;
|
|
1989
2015
|
if (typeof agentCredentialKey === "string" && agentCredentialKey.length > 0) {
|
|
1990
2016
|
rmSync(tokenFile, { force: true });
|
|
1991
|
-
agentCredentialProxy = registerAgentCredentialProxy({
|
|
2017
|
+
agentCredentialProxy = await registerAgentCredentialProxy({
|
|
1992
2018
|
agentId: ctx.agentId,
|
|
1993
2019
|
launchId: ctx.launchId,
|
|
1994
2020
|
serverUrl: ctx.config.serverUrl,
|
|
@@ -2122,12 +2148,6 @@ import path3 from "path";
|
|
|
2122
2148
|
function normalizeExecOutput(raw) {
|
|
2123
2149
|
return Buffer.isBuffer(raw) ? raw.toString("utf8") : String(raw ?? "");
|
|
2124
2150
|
}
|
|
2125
|
-
function preferWindowsCmdShim(resolved, existsSyncFn) {
|
|
2126
|
-
const parsed = path3.win32.parse(resolved);
|
|
2127
|
-
if (parsed.ext.toLowerCase() !== ".ps1") return resolved;
|
|
2128
|
-
const cmdShim = path3.win32.join(parsed.dir, `${parsed.name}.cmd`);
|
|
2129
|
-
return existsSyncFn(cmdShim) ? cmdShim : resolved;
|
|
2130
|
-
}
|
|
2131
2151
|
function resolveCommandOnWindows(command, env, execFileSyncFn, existsSyncFn) {
|
|
2132
2152
|
const script = "& {$cmd = Get-Command -Name $args[0] -ErrorAction Stop | Select-Object -First 1; if ($cmd.Path) { $cmd.Path } elseif ($cmd.Source) { $cmd.Source } elseif ($cmd.Definition) { $cmd.Definition } }";
|
|
2133
2153
|
try {
|
|
@@ -2142,7 +2162,23 @@ function resolveCommandOnWindows(command, env, execFileSyncFn, existsSyncFn) {
|
|
|
2142
2162
|
env
|
|
2143
2163
|
}));
|
|
2144
2164
|
const resolved = output.trim().split(/\r?\n/)[0];
|
|
2145
|
-
|
|
2165
|
+
if (!resolved) return null;
|
|
2166
|
+
const lowerResolved = resolved.toLowerCase();
|
|
2167
|
+
if (lowerResolved.endsWith(".ps1")) {
|
|
2168
|
+
const dir = path3.dirname(resolved);
|
|
2169
|
+
const base = path3.basename(resolved, ".ps1");
|
|
2170
|
+
const alternatives = [
|
|
2171
|
+
path3.join(dir, `${base}.cmd`),
|
|
2172
|
+
path3.join(dir, `${base}.bat`),
|
|
2173
|
+
path3.join(dir, `${base}.exe`),
|
|
2174
|
+
path3.join(dir, base)
|
|
2175
|
+
];
|
|
2176
|
+
for (const alt of alternatives) {
|
|
2177
|
+
if (existsSyncFn(alt)) return alt;
|
|
2178
|
+
}
|
|
2179
|
+
return null;
|
|
2180
|
+
}
|
|
2181
|
+
return resolved;
|
|
2146
2182
|
} catch {
|
|
2147
2183
|
return null;
|
|
2148
2184
|
}
|
|
@@ -2230,7 +2266,8 @@ function isRecord(value) {
|
|
|
2230
2266
|
function expandClaudeMcpConfigVariables(raw, vars) {
|
|
2231
2267
|
let expanded = raw;
|
|
2232
2268
|
for (const [name, value] of Object.entries(vars)) {
|
|
2233
|
-
|
|
2269
|
+
const escapedValue = process.platform === "win32" ? value.replace(/\\/g, "\\\\") : value;
|
|
2270
|
+
expanded = expanded.replaceAll(`\${${name}}`, escapedValue);
|
|
2234
2271
|
}
|
|
2235
2272
|
return expanded;
|
|
2236
2273
|
}
|
|
@@ -2284,7 +2321,7 @@ function collectClaudeMcpConfigFiles(ctx, home) {
|
|
|
2284
2321
|
return files;
|
|
2285
2322
|
}
|
|
2286
2323
|
function buildClaudeUserMcpServers(ctx, home) {
|
|
2287
|
-
const
|
|
2324
|
+
const servers = /* @__PURE__ */ Object.create(null);
|
|
2288
2325
|
for (const configFile of collectClaudeMcpConfigFiles(ctx, home)) {
|
|
2289
2326
|
const mcpServers = readClaudeMcpServers(configFile.path, configFile.vars);
|
|
2290
2327
|
if (!mcpServers) continue;
|
|
@@ -2293,10 +2330,10 @@ function buildClaudeUserMcpServers(ctx, home) {
|
|
|
2293
2330
|
logger.warn(`[Claude] ignoring invalid MCP server "${name}" in ${configFile.path}`);
|
|
2294
2331
|
continue;
|
|
2295
2332
|
}
|
|
2296
|
-
|
|
2333
|
+
servers[name] = server;
|
|
2297
2334
|
}
|
|
2298
2335
|
}
|
|
2299
|
-
return
|
|
2336
|
+
return servers;
|
|
2300
2337
|
}
|
|
2301
2338
|
var ClaudeDriver = class {
|
|
2302
2339
|
id = "claude";
|
|
@@ -2395,8 +2432,8 @@ var ClaudeDriver = class {
|
|
|
2395
2432
|
writeFileSync2(mcpConfigPath, this.buildRuntimeActionsMcpConfig(ctx, home), { mode: 384 });
|
|
2396
2433
|
return { systemPromptPath, mcpConfigPath };
|
|
2397
2434
|
}
|
|
2398
|
-
spawn(ctx) {
|
|
2399
|
-
const { slockDir, tokenFile, spawnEnv } = prepareCliTransport(ctx);
|
|
2435
|
+
async spawn(ctx) {
|
|
2436
|
+
const { slockDir, tokenFile, spawnEnv } = await prepareCliTransport(ctx);
|
|
2400
2437
|
const { systemPromptPath, mcpConfigPath } = this.writeClaudeLaunchFiles(ctx, slockDir);
|
|
2401
2438
|
const args = this.buildClaudeArgs(ctx.config, ctx.standingPrompt, {
|
|
2402
2439
|
standingPromptFilePath: systemPromptPath
|
|
@@ -2406,11 +2443,18 @@ var ClaudeDriver = class {
|
|
|
2406
2443
|
logger.info(
|
|
2407
2444
|
`[Agent ${ctx.agentId}] transport=cli cli=${ctx.slockCliPath} token_file=${tokenFile}`
|
|
2408
2445
|
);
|
|
2409
|
-
const
|
|
2446
|
+
const claudeCommand = resolveClaudeCommand();
|
|
2447
|
+
const isWindows = process.platform === "win32";
|
|
2448
|
+
const lowerClaudeCommand = claudeCommand?.toLowerCase();
|
|
2449
|
+
const isBatchFile = Boolean(
|
|
2450
|
+
isWindows && lowerClaudeCommand && (lowerClaudeCommand.endsWith(".cmd") || lowerClaudeCommand.endsWith(".bat"))
|
|
2451
|
+
);
|
|
2452
|
+
const useShell = isWindows && (!claudeCommand || isBatchFile);
|
|
2453
|
+
const proc = spawn(claudeCommand ?? "claude", args, {
|
|
2410
2454
|
cwd: ctx.workingDirectory,
|
|
2411
2455
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2412
2456
|
env: spawnEnv,
|
|
2413
|
-
shell:
|
|
2457
|
+
shell: useShell
|
|
2414
2458
|
});
|
|
2415
2459
|
const stdinMsg = JSON.stringify({
|
|
2416
2460
|
type: "user",
|
|
@@ -2525,9 +2569,7 @@ var ClaudeDriver = class {
|
|
|
2525
2569
|
buildSystemPrompt(config, _agentId) {
|
|
2526
2570
|
return buildCliTransportSystemPrompt(config, {
|
|
2527
2571
|
toolPrefix: "mcp__chat__",
|
|
2528
|
-
extraCriticalRules: [
|
|
2529
|
-
"- Runtime Profile migration completion is the only exception to CLI-only operation: when a migration notice tells you to acknowledge with `runtime_profile_migration_done`, call the `mcp__chat__runtime_profile_migration_done` tool with the exact `migration_key`; do not use `slock` CLI or reply in chat as the acknowledgment."
|
|
2530
|
-
],
|
|
2572
|
+
extraCriticalRules: [],
|
|
2531
2573
|
postStartupNotes: [
|
|
2532
2574
|
"**Claude runtime note:** While you are busy, Slock batches inbox-count notifications instead of injecting message content. Use `slock message check` at natural breakpoints to pull the pending messages before side-effect actions that depend on current context."
|
|
2533
2575
|
],
|
|
@@ -2779,9 +2821,9 @@ var CodexDriver = class {
|
|
|
2779
2821
|
sessionAnnounced = false;
|
|
2780
2822
|
streamedAgentMessageIds = /* @__PURE__ */ new Set();
|
|
2781
2823
|
streamedReasoningIds = /* @__PURE__ */ new Set();
|
|
2782
|
-
spawn(ctx) {
|
|
2824
|
+
async spawn(ctx) {
|
|
2783
2825
|
ensureGitRepoForCodex(ctx.workingDirectory);
|
|
2784
|
-
const { spawnEnv } = prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
2826
|
+
const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
2785
2827
|
this.process = null;
|
|
2786
2828
|
this.requestId = 0;
|
|
2787
2829
|
this.threadId = ctx.config.sessionId || null;
|
|
@@ -3102,12 +3144,142 @@ function detectCodexModels(home = os3.homedir()) {
|
|
|
3102
3144
|
return { models, default: defaultModel };
|
|
3103
3145
|
}
|
|
3104
3146
|
|
|
3105
|
-
// src/drivers/
|
|
3147
|
+
// src/drivers/antigravity.ts
|
|
3148
|
+
import { randomUUID } from "crypto";
|
|
3106
3149
|
import { spawn as spawn3 } from "child_process";
|
|
3150
|
+
var DEFAULT_PRINT_TIMEOUT = "30m";
|
|
3151
|
+
var ANTIGRAVITY_ENV_OVERRIDES = {
|
|
3152
|
+
// `agy` switches to a separate file-based token store when it detects an SSH
|
|
3153
|
+
// session. Daemon agents commonly run under SSH-managed hosts, while the
|
|
3154
|
+
// human login lives in the normal local Antigravity credential store.
|
|
3155
|
+
SSH_CLIENT: void 0,
|
|
3156
|
+
SSH_CONNECTION: void 0,
|
|
3157
|
+
SSH_TTY: void 0
|
|
3158
|
+
};
|
|
3159
|
+
function resolveAntigravitySpawn(commandArgs, deps = {}) {
|
|
3160
|
+
return {
|
|
3161
|
+
command: resolveCommandOnPath("agy", deps) ?? "agy",
|
|
3162
|
+
args: commandArgs,
|
|
3163
|
+
shell: false
|
|
3164
|
+
};
|
|
3165
|
+
}
|
|
3166
|
+
function buildAntigravityArgs(ctx) {
|
|
3167
|
+
const args = [
|
|
3168
|
+
"--print",
|
|
3169
|
+
"--print-timeout",
|
|
3170
|
+
ctx.config.envVars?.ANTIGRAVITY_PRINT_TIMEOUT || DEFAULT_PRINT_TIMEOUT,
|
|
3171
|
+
"--dangerously-skip-permissions"
|
|
3172
|
+
];
|
|
3173
|
+
if (ctx.config.sessionId) {
|
|
3174
|
+
args.push("--continue");
|
|
3175
|
+
}
|
|
3176
|
+
return args;
|
|
3177
|
+
}
|
|
3178
|
+
var ERROR_LINE_PREFIX_RE = /^(?:(?:error|fatal|exception|failure):\s*|failed(?:\b|:))/i;
|
|
3179
|
+
var ERROR_LINE_PATTERNS = [
|
|
3180
|
+
/authentication timed out/i,
|
|
3181
|
+
/\bpermission denied\b/i,
|
|
3182
|
+
/\bunauthorized\b/i,
|
|
3183
|
+
/\brate limit(?:ed)?\b/i,
|
|
3184
|
+
/\bserver error\b/i,
|
|
3185
|
+
/\bnetwork error\b/i,
|
|
3186
|
+
/\brequest timed out\b/i,
|
|
3187
|
+
/\btimed out waiting\b/i,
|
|
3188
|
+
/\b(?:http|status)\s+(?:401|403|429|500|502|503|504)\b/i
|
|
3189
|
+
];
|
|
3190
|
+
function isErrorLine(line) {
|
|
3191
|
+
return ERROR_LINE_PREFIX_RE.test(line) || ERROR_LINE_PATTERNS.some((pattern) => pattern.test(line));
|
|
3192
|
+
}
|
|
3193
|
+
var AntigravityDriver = class {
|
|
3194
|
+
id = "antigravity";
|
|
3195
|
+
lifecycle = {
|
|
3196
|
+
kind: "per_turn",
|
|
3197
|
+
start: "immediate",
|
|
3198
|
+
exit: "natural",
|
|
3199
|
+
inFlightWake: "spawn_new"
|
|
3200
|
+
};
|
|
3201
|
+
communication = {
|
|
3202
|
+
chat: "slock_cli",
|
|
3203
|
+
runtimeControl: "none"
|
|
3204
|
+
};
|
|
3205
|
+
session = {
|
|
3206
|
+
recovery: "resume_or_fresh"
|
|
3207
|
+
};
|
|
3208
|
+
model = {
|
|
3209
|
+
detectedModelsVerifiedAs: "suggestion_only",
|
|
3210
|
+
toLaunchSpec: (_modelId) => ({ args: [] })
|
|
3211
|
+
};
|
|
3212
|
+
supportsStdinNotification = false;
|
|
3213
|
+
mcpToolPrefix = "";
|
|
3214
|
+
busyDeliveryMode = "none";
|
|
3215
|
+
usesSlockCliForCommunication = true;
|
|
3216
|
+
sessionId = null;
|
|
3217
|
+
sessionAnnounced = false;
|
|
3218
|
+
probe() {
|
|
3219
|
+
const command = resolveCommandOnPath("agy");
|
|
3220
|
+
if (!command) return { available: false };
|
|
3221
|
+
return {
|
|
3222
|
+
available: true,
|
|
3223
|
+
version: readCommandVersion(command) ?? void 0
|
|
3224
|
+
};
|
|
3225
|
+
}
|
|
3226
|
+
async spawn(ctx) {
|
|
3227
|
+
this.sessionId = ctx.config.sessionId || randomUUID();
|
|
3228
|
+
this.sessionAnnounced = false;
|
|
3229
|
+
const { command, args, shell } = resolveAntigravitySpawn(buildAntigravityArgs(ctx));
|
|
3230
|
+
const { spawnEnv } = await prepareCliTransport(ctx, {
|
|
3231
|
+
NO_COLOR: "1",
|
|
3232
|
+
...ANTIGRAVITY_ENV_OVERRIDES
|
|
3233
|
+
});
|
|
3234
|
+
const proc = spawn3(command, args, {
|
|
3235
|
+
cwd: ctx.workingDirectory,
|
|
3236
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
3237
|
+
env: spawnEnv,
|
|
3238
|
+
shell
|
|
3239
|
+
});
|
|
3240
|
+
proc.stdin?.end(ctx.prompt);
|
|
3241
|
+
return { process: proc };
|
|
3242
|
+
}
|
|
3243
|
+
parseLine(line) {
|
|
3244
|
+
const trimmed = line.trim();
|
|
3245
|
+
if (!trimmed) return [];
|
|
3246
|
+
const events = [];
|
|
3247
|
+
if (!this.sessionAnnounced && this.sessionId) {
|
|
3248
|
+
events.push({ kind: "session_init", sessionId: this.sessionId });
|
|
3249
|
+
this.sessionAnnounced = true;
|
|
3250
|
+
}
|
|
3251
|
+
if (isErrorLine(trimmed)) {
|
|
3252
|
+
events.push({ kind: "error", message: trimmed });
|
|
3253
|
+
return events;
|
|
3254
|
+
}
|
|
3255
|
+
events.push({ kind: "text", text: line });
|
|
3256
|
+
return events;
|
|
3257
|
+
}
|
|
3258
|
+
encodeStdinMessage(_text, _sessionId, _opts) {
|
|
3259
|
+
return null;
|
|
3260
|
+
}
|
|
3261
|
+
buildSystemPrompt(config, _agentId) {
|
|
3262
|
+
return buildCliTransportSystemPrompt(config, {
|
|
3263
|
+
toolPrefix: "",
|
|
3264
|
+
extraCriticalRules: [],
|
|
3265
|
+
postStartupNotes: [
|
|
3266
|
+
"**Antigravity runtime note:** Slock launches you as a per-turn process. Complete the current wake using `slock` CLI commands, then stop; the daemon will restart you when new messages arrive."
|
|
3267
|
+
],
|
|
3268
|
+
includeStdinNotificationSection: false,
|
|
3269
|
+
messageNotificationStyle: "poll"
|
|
3270
|
+
});
|
|
3271
|
+
}
|
|
3272
|
+
async detectModels() {
|
|
3273
|
+
return null;
|
|
3274
|
+
}
|
|
3275
|
+
};
|
|
3276
|
+
|
|
3277
|
+
// src/drivers/copilot.ts
|
|
3278
|
+
import { spawn as spawn4 } from "child_process";
|
|
3107
3279
|
import path6 from "path";
|
|
3108
3280
|
import { writeFileSync as writeFileSync3 } from "fs";
|
|
3109
|
-
function buildCopilotSpawnEnv(ctx) {
|
|
3110
|
-
return prepareCliTransport(ctx, { NO_COLOR: "1" }).spawnEnv;
|
|
3281
|
+
async function buildCopilotSpawnEnv(ctx) {
|
|
3282
|
+
return (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
|
|
3111
3283
|
}
|
|
3112
3284
|
var CopilotDriver = class {
|
|
3113
3285
|
id = "copilot";
|
|
@@ -3159,7 +3331,7 @@ var CopilotDriver = class {
|
|
|
3159
3331
|
}
|
|
3160
3332
|
});
|
|
3161
3333
|
}
|
|
3162
|
-
spawn(ctx) {
|
|
3334
|
+
async spawn(ctx) {
|
|
3163
3335
|
this.sessionId = ctx.config.sessionId || null;
|
|
3164
3336
|
this.sessionAnnounced = false;
|
|
3165
3337
|
const mcpConfigPath = path6.join(ctx.workingDirectory, ".slock-copilot-mcp.json");
|
|
@@ -3183,8 +3355,8 @@ var CopilotDriver = class {
|
|
|
3183
3355
|
if (ctx.config.sessionId) {
|
|
3184
3356
|
args.push(`--resume=${ctx.config.sessionId}`);
|
|
3185
3357
|
}
|
|
3186
|
-
const spawnEnv = buildCopilotSpawnEnv(ctx);
|
|
3187
|
-
const proc =
|
|
3358
|
+
const spawnEnv = await buildCopilotSpawnEnv(ctx);
|
|
3359
|
+
const proc = spawn4("copilot", args, {
|
|
3188
3360
|
cwd: ctx.workingDirectory,
|
|
3189
3361
|
stdio: ["pipe", "pipe", "pipe"],
|
|
3190
3362
|
env: spawnEnv,
|
|
@@ -3266,9 +3438,7 @@ var CopilotDriver = class {
|
|
|
3266
3438
|
buildSystemPrompt(config, _agentId) {
|
|
3267
3439
|
return buildCliTransportSystemPrompt(config, {
|
|
3268
3440
|
toolPrefix: "",
|
|
3269
|
-
extraCriticalRules: [
|
|
3270
|
-
"- Runtime Profile migration completion is the only exception to CLI-only operation: when a migration notice tells you to acknowledge with `runtime_profile_migration_done`, call the `runtime_profile_migration_done` tool with the exact `migration_key`; do not use `slock` CLI or reply in chat as the acknowledgment."
|
|
3271
|
-
],
|
|
3441
|
+
extraCriticalRules: [],
|
|
3272
3442
|
postStartupNotes: [
|
|
3273
3443
|
"**Copilot runtime note:** Slock launches you as a per-turn process. Complete the current wake using `slock` CLI commands, then stop; the daemon will restart you when new messages arrive."
|
|
3274
3444
|
],
|
|
@@ -3279,11 +3449,11 @@ var CopilotDriver = class {
|
|
|
3279
3449
|
};
|
|
3280
3450
|
|
|
3281
3451
|
// src/drivers/cursor.ts
|
|
3282
|
-
import { spawn as
|
|
3452
|
+
import { spawn as spawn5, spawnSync } from "child_process";
|
|
3283
3453
|
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync5 } from "fs";
|
|
3284
3454
|
import path7 from "path";
|
|
3285
|
-
function buildCursorSpawnEnv(ctx) {
|
|
3286
|
-
return prepareCliTransport(ctx, { NO_COLOR: "1" }).spawnEnv;
|
|
3455
|
+
async function buildCursorSpawnEnv(ctx) {
|
|
3456
|
+
return (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
|
|
3287
3457
|
}
|
|
3288
3458
|
var CursorDriver = class {
|
|
3289
3459
|
id = "cursor";
|
|
@@ -3333,7 +3503,7 @@ var CursorDriver = class {
|
|
|
3333
3503
|
}
|
|
3334
3504
|
});
|
|
3335
3505
|
}
|
|
3336
|
-
spawn(ctx) {
|
|
3506
|
+
async spawn(ctx) {
|
|
3337
3507
|
const cursorDir = path7.join(ctx.workingDirectory, ".cursor");
|
|
3338
3508
|
if (!existsSync5(cursorDir)) {
|
|
3339
3509
|
mkdirSync2(cursorDir, { recursive: true });
|
|
@@ -3355,8 +3525,8 @@ var CursorDriver = class {
|
|
|
3355
3525
|
args.push("--resume", ctx.config.sessionId);
|
|
3356
3526
|
}
|
|
3357
3527
|
args.push(ctx.prompt);
|
|
3358
|
-
const spawnEnv = buildCursorSpawnEnv(ctx);
|
|
3359
|
-
const proc =
|
|
3528
|
+
const spawnEnv = await buildCursorSpawnEnv(ctx);
|
|
3529
|
+
const proc = spawn5("cursor-agent", args, {
|
|
3360
3530
|
cwd: ctx.workingDirectory,
|
|
3361
3531
|
stdio: ["pipe", "pipe", "pipe"],
|
|
3362
3532
|
env: spawnEnv,
|
|
@@ -3424,9 +3594,7 @@ var CursorDriver = class {
|
|
|
3424
3594
|
buildSystemPrompt(config, _agentId) {
|
|
3425
3595
|
return buildCliTransportSystemPrompt(config, {
|
|
3426
3596
|
toolPrefix: "mcp__chat__",
|
|
3427
|
-
extraCriticalRules: [
|
|
3428
|
-
"- Runtime Profile migration completion is the only exception to CLI-only operation: when a migration notice tells you to acknowledge with `runtime_profile_migration_done`, call the `runtime_profile_migration_done` tool with the exact `migration_key`; do not use `slock` CLI or reply in chat as the acknowledgment."
|
|
3429
|
-
],
|
|
3597
|
+
extraCriticalRules: [],
|
|
3430
3598
|
postStartupNotes: [
|
|
3431
3599
|
"**Cursor runtime note:** Slock launches you as a per-turn process. Complete the current wake using `slock` CLI commands, then stop; the daemon will restart you when new messages arrive."
|
|
3432
3600
|
],
|
|
@@ -3478,17 +3646,14 @@ function runCursorModelsCommand() {
|
|
|
3478
3646
|
}
|
|
3479
3647
|
|
|
3480
3648
|
// src/drivers/gemini.ts
|
|
3481
|
-
import { execFileSync as execFileSync3, spawn as
|
|
3649
|
+
import { execFileSync as execFileSync3, spawn as spawn6 } from "child_process";
|
|
3482
3650
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
3483
3651
|
import path8 from "path";
|
|
3484
|
-
function buildGeminiSpawnEnv(ctx, platform = process.platform) {
|
|
3485
|
-
const { spawnEnv } = prepareCliTransport(ctx, { NO_COLOR: "1" }, platform);
|
|
3652
|
+
async function buildGeminiSpawnEnv(ctx, platform = process.platform) {
|
|
3653
|
+
const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" }, platform);
|
|
3486
3654
|
if (!Object.prototype.hasOwnProperty.call(ctx.config.envVars ?? {}, "GEMINI_CLI_TRUST_WORKSPACE")) {
|
|
3487
3655
|
spawnEnv.GEMINI_CLI_TRUST_WORKSPACE = "true";
|
|
3488
3656
|
}
|
|
3489
|
-
if (platform === "win32" && !Object.prototype.hasOwnProperty.call(ctx.config.envVars ?? {}, "GEMINI_PTY_INFO")) {
|
|
3490
|
-
spawnEnv.GEMINI_PTY_INFO = "child_process";
|
|
3491
|
-
}
|
|
3492
3657
|
return spawnEnv;
|
|
3493
3658
|
}
|
|
3494
3659
|
function normalizeExecOutput2(raw) {
|
|
@@ -3581,13 +3746,13 @@ var GeminiDriver = class {
|
|
|
3581
3746
|
usesSlockCliForCommunication = true;
|
|
3582
3747
|
sessionId = null;
|
|
3583
3748
|
sessionAnnounced = false;
|
|
3584
|
-
spawn(ctx) {
|
|
3749
|
+
async spawn(ctx) {
|
|
3585
3750
|
this.sessionId = ctx.config.sessionId || null;
|
|
3586
3751
|
this.sessionAnnounced = false;
|
|
3587
3752
|
this.writeGeminiSettings(ctx);
|
|
3588
3753
|
const { command, args } = resolveGeminiSpawn(buildGeminiArgs(ctx.config));
|
|
3589
|
-
const spawnEnv = buildGeminiSpawnEnv(ctx);
|
|
3590
|
-
const proc =
|
|
3754
|
+
const spawnEnv = await buildGeminiSpawnEnv(ctx);
|
|
3755
|
+
const proc = spawn6(command, args, {
|
|
3591
3756
|
cwd: ctx.workingDirectory,
|
|
3592
3757
|
stdio: ["pipe", "pipe", "pipe"],
|
|
3593
3758
|
env: spawnEnv,
|
|
@@ -3649,9 +3814,7 @@ var GeminiDriver = class {
|
|
|
3649
3814
|
buildSystemPrompt(config, _agentId) {
|
|
3650
3815
|
return buildCliTransportSystemPrompt(config, {
|
|
3651
3816
|
toolPrefix: "",
|
|
3652
|
-
extraCriticalRules: [
|
|
3653
|
-
"- Runtime Profile migration completion is the only exception to CLI-only operation: when a migration notice tells you to acknowledge with `runtime_profile_migration_done`, call the `runtime_profile_migration_done` tool with the exact `migration_key`; do not use `slock` CLI or reply in chat as the acknowledgment."
|
|
3654
|
-
],
|
|
3817
|
+
extraCriticalRules: [],
|
|
3655
3818
|
postStartupNotes: [
|
|
3656
3819
|
"**Gemini runtime note:** Slock launches you as a per-turn process. Complete the current wake using `slock` CLI commands, then stop; the daemon will restart you when new messages arrive."
|
|
3657
3820
|
],
|
|
@@ -3693,8 +3856,8 @@ var GeminiDriver = class {
|
|
|
3693
3856
|
};
|
|
3694
3857
|
|
|
3695
3858
|
// src/drivers/kimi.ts
|
|
3696
|
-
import { randomUUID } from "crypto";
|
|
3697
|
-
import { spawn as
|
|
3859
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
3860
|
+
import { spawn as spawn7 } from "child_process";
|
|
3698
3861
|
import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
3699
3862
|
import os4 from "os";
|
|
3700
3863
|
import path9 from "path";
|
|
@@ -3758,11 +3921,11 @@ var KimiDriver = class {
|
|
|
3758
3921
|
"--runtime-actions-only"
|
|
3759
3922
|
];
|
|
3760
3923
|
}
|
|
3761
|
-
spawn(ctx) {
|
|
3924
|
+
async spawn(ctx) {
|
|
3762
3925
|
const isResume = !!ctx.config.sessionId;
|
|
3763
|
-
this.sessionId = ctx.config.sessionId ||
|
|
3926
|
+
this.sessionId = ctx.config.sessionId || randomUUID2();
|
|
3764
3927
|
this.sessionAnnounced = false;
|
|
3765
|
-
this.promptRequestId =
|
|
3928
|
+
this.promptRequestId = randomUUID2();
|
|
3766
3929
|
const isTsSource = ctx.chatBridgePath.endsWith(".ts");
|
|
3767
3930
|
const command = isTsSource ? "npx" : "node";
|
|
3768
3931
|
const bridgeArgs = this.buildChatBridgeArgs(ctx);
|
|
@@ -3800,9 +3963,9 @@ var KimiDriver = class {
|
|
|
3800
3963
|
if (ctx.config.model && ctx.config.model !== "default") {
|
|
3801
3964
|
args.push("--model", ctx.config.model);
|
|
3802
3965
|
}
|
|
3803
|
-
const spawnEnv = prepareCliTransport(ctx, { NO_COLOR: "1" }).spawnEnv;
|
|
3966
|
+
const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
|
|
3804
3967
|
const launch = resolveKimiSpawn(args);
|
|
3805
|
-
const proc =
|
|
3968
|
+
const proc = spawn7(launch.command, launch.args, {
|
|
3806
3969
|
cwd: ctx.workingDirectory,
|
|
3807
3970
|
stdio: ["pipe", "pipe", "pipe"],
|
|
3808
3971
|
env: spawnEnv,
|
|
@@ -3814,7 +3977,7 @@ var KimiDriver = class {
|
|
|
3814
3977
|
});
|
|
3815
3978
|
proc.stdin?.write(JSON.stringify({
|
|
3816
3979
|
jsonrpc: "2.0",
|
|
3817
|
-
id:
|
|
3980
|
+
id: randomUUID2(),
|
|
3818
3981
|
method: "initialize",
|
|
3819
3982
|
params: {
|
|
3820
3983
|
protocol_version: KIMI_WIRE_PROTOCOL_VERSION,
|
|
@@ -3895,7 +4058,7 @@ var KimiDriver = class {
|
|
|
3895
4058
|
if (mode === "idle") {
|
|
3896
4059
|
return JSON.stringify({
|
|
3897
4060
|
jsonrpc: "2.0",
|
|
3898
|
-
id:
|
|
4061
|
+
id: randomUUID2(),
|
|
3899
4062
|
method: "prompt",
|
|
3900
4063
|
params: {
|
|
3901
4064
|
user_input: _text
|
|
@@ -3904,7 +4067,7 @@ var KimiDriver = class {
|
|
|
3904
4067
|
}
|
|
3905
4068
|
return JSON.stringify({
|
|
3906
4069
|
jsonrpc: "2.0",
|
|
3907
|
-
id:
|
|
4070
|
+
id: randomUUID2(),
|
|
3908
4071
|
method: "steer",
|
|
3909
4072
|
params: {
|
|
3910
4073
|
user_input: _text
|
|
@@ -3914,9 +4077,7 @@ var KimiDriver = class {
|
|
|
3914
4077
|
buildSystemPrompt(config, _agentId) {
|
|
3915
4078
|
return buildCliTransportSystemPrompt(config, {
|
|
3916
4079
|
toolPrefix: "",
|
|
3917
|
-
extraCriticalRules: [
|
|
3918
|
-
"- Runtime Profile migration completion is the only exception to CLI-only operation: when a migration notice tells you to acknowledge with `runtime_profile_migration_done`, call the `runtime_profile_migration_done` tool with the exact `migration_key`; do not use `slock` CLI or reply in chat as the acknowledgment."
|
|
3919
|
-
],
|
|
4080
|
+
extraCriticalRules: [],
|
|
3920
4081
|
postStartupNotes: [],
|
|
3921
4082
|
includeStdinNotificationSection: true,
|
|
3922
4083
|
messageNotificationStyle: "direct"
|
|
@@ -3953,7 +4114,7 @@ function detectKimiModels(home = os4.homedir()) {
|
|
|
3953
4114
|
}
|
|
3954
4115
|
|
|
3955
4116
|
// src/drivers/opencode.ts
|
|
3956
|
-
import { spawn as
|
|
4117
|
+
import { spawn as spawn8, spawnSync as spawnSync2 } from "child_process";
|
|
3957
4118
|
import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
|
|
3958
4119
|
import os5 from "os";
|
|
3959
4120
|
import path10 from "path";
|
|
@@ -4089,8 +4250,8 @@ function buildOpenCodeConfig(ctx, home = os5.homedir()) {
|
|
|
4089
4250
|
}
|
|
4090
4251
|
};
|
|
4091
4252
|
}
|
|
4092
|
-
function buildOpenCodeLaunchOptions(ctx, home = os5.homedir(), version = null) {
|
|
4093
|
-
const slock = prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
4253
|
+
async function buildOpenCodeLaunchOptions(ctx, home = os5.homedir(), version = null) {
|
|
4254
|
+
const slock = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
4094
4255
|
const config = buildOpenCodeConfig(ctx, home);
|
|
4095
4256
|
const env = {
|
|
4096
4257
|
...slock.spawnEnv,
|
|
@@ -4309,9 +4470,7 @@ function isSystemFirstMessageTask(message) {
|
|
|
4309
4470
|
function buildOpenCodeSystemPrompt(config) {
|
|
4310
4471
|
return buildCliTransportSystemPrompt(config, {
|
|
4311
4472
|
toolPrefix: CHAT_MCP_TOOL_PREFIX,
|
|
4312
|
-
extraCriticalRules: [
|
|
4313
|
-
"- Runtime Profile migration completion is the only exception to CLI-only operation: when a migration notice tells you to acknowledge with `runtime_profile_migration_done`, call the `chat_runtime_profile_migration_done` tool with the exact `migration_key`; do not use `slock` CLI or reply in chat as the acknowledgment."
|
|
4314
|
-
],
|
|
4473
|
+
extraCriticalRules: [],
|
|
4315
4474
|
postStartupNotes: [
|
|
4316
4475
|
"**OpenCode runtime note:** Slock launches you as a per-turn process. Complete the current wake using `slock` CLI commands, then stop; the daemon will restart you when new messages arrive."
|
|
4317
4476
|
],
|
|
@@ -4336,7 +4495,7 @@ var OpenCodeDriver = class {
|
|
|
4336
4495
|
};
|
|
4337
4496
|
model = {
|
|
4338
4497
|
detectedModelsVerifiedAs: "launchable",
|
|
4339
|
-
toLaunchSpec: (modelId, ctx, opts) => {
|
|
4498
|
+
toLaunchSpec: async (modelId, ctx, opts) => {
|
|
4340
4499
|
if (!ctx) return { args: ["--model", modelId] };
|
|
4341
4500
|
const launchCtx = {
|
|
4342
4501
|
...ctx,
|
|
@@ -4346,7 +4505,7 @@ var OpenCodeDriver = class {
|
|
|
4346
4505
|
}
|
|
4347
4506
|
};
|
|
4348
4507
|
const version = readOpenCodeVersion();
|
|
4349
|
-
const launch = buildOpenCodeLaunchOptions(launchCtx, opts?.home, version);
|
|
4508
|
+
const launch = await buildOpenCodeLaunchOptions(launchCtx, opts?.home, version);
|
|
4350
4509
|
return {
|
|
4351
4510
|
args: launch.args,
|
|
4352
4511
|
env: launch.env,
|
|
@@ -4385,7 +4544,7 @@ var OpenCodeDriver = class {
|
|
|
4385
4544
|
async detectModels() {
|
|
4386
4545
|
return detectOpenCodeModels();
|
|
4387
4546
|
}
|
|
4388
|
-
spawn(ctx) {
|
|
4547
|
+
async spawn(ctx) {
|
|
4389
4548
|
this.sessionId = ctx.config.sessionId || null;
|
|
4390
4549
|
this.sessionAnnounced = false;
|
|
4391
4550
|
const version = readOpenCodeVersion();
|
|
@@ -4393,9 +4552,9 @@ var OpenCodeDriver = class {
|
|
|
4393
4552
|
if (unsupportedMessage) {
|
|
4394
4553
|
throw new Error(unsupportedMessage);
|
|
4395
4554
|
}
|
|
4396
|
-
const launch = buildOpenCodeLaunchOptions(ctx, os5.homedir(), version);
|
|
4555
|
+
const launch = await buildOpenCodeLaunchOptions(ctx, os5.homedir(), version);
|
|
4397
4556
|
const spawnSpec = resolveOpenCodeSpawn(launch.args);
|
|
4398
|
-
const proc =
|
|
4557
|
+
const proc = spawn8(spawnSpec.command, spawnSpec.args, {
|
|
4399
4558
|
cwd: ctx.workingDirectory,
|
|
4400
4559
|
stdio: ["pipe", "pipe", "pipe"],
|
|
4401
4560
|
env: launch.env,
|
|
@@ -4461,6 +4620,7 @@ var OpenCodeDriver = class {
|
|
|
4461
4620
|
var driverFactories = {
|
|
4462
4621
|
claude: () => new ClaudeDriver(),
|
|
4463
4622
|
codex: () => new CodexDriver(),
|
|
4623
|
+
antigravity: () => new AntigravityDriver(),
|
|
4464
4624
|
copilot: () => new CopilotDriver(),
|
|
4465
4625
|
cursor: () => new CursorDriver(),
|
|
4466
4626
|
gemini: () => new GeminiDriver(),
|
|
@@ -4588,15 +4748,38 @@ async function deleteWorkspaceDirectory(dataDir, directoryName) {
|
|
|
4588
4748
|
// src/runtimeErrorDiagnostics.ts
|
|
4589
4749
|
import { createHash } from "crypto";
|
|
4590
4750
|
var MAX_RUNTIME_ERROR_MESSAGE_EXCERPT_CHARS = 4096;
|
|
4751
|
+
var RUNTIME_AUTH_ACTION_REQUIRED_PATTERNS = [
|
|
4752
|
+
/access token could not be refreshed/i,
|
|
4753
|
+
/refresh token was already used/i,
|
|
4754
|
+
/authentication token has been invalidated/i,
|
|
4755
|
+
/logged out or signed in to another account/i,
|
|
4756
|
+
/not logged in/i,
|
|
4757
|
+
/not signed in/i,
|
|
4758
|
+
/login required/i,
|
|
4759
|
+
/log in first/i,
|
|
4760
|
+
/please log in/i,
|
|
4761
|
+
/authentication failed/i,
|
|
4762
|
+
/auth(?:entication)? failed/i,
|
|
4763
|
+
/authentication timed out/i,
|
|
4764
|
+
/missing (?:api )?token/i,
|
|
4765
|
+
/no (?:api )?token/i,
|
|
4766
|
+
/missing credentials/i,
|
|
4767
|
+
/credentials? not found/i,
|
|
4768
|
+
/invalid api key/i,
|
|
4769
|
+
/api key (?:is )?not set/i
|
|
4770
|
+
];
|
|
4591
4771
|
function buildRuntimeErrorDiagnosticEnvelope(message) {
|
|
4592
4772
|
const rawMessage = String(message || "");
|
|
4593
4773
|
const scrubbed = scrubRuntimeErrorDiagnosticText(rawMessage);
|
|
4594
4774
|
const { value: excerpt, truncated } = truncateDiagnosticText(scrubbed, MAX_RUNTIME_ERROR_MESSAGE_EXCERPT_CHARS);
|
|
4595
4775
|
const httpStatus = extractHttpStatus(rawMessage);
|
|
4596
4776
|
const runtimeErrorClass = classifyRuntimeError(rawMessage, httpStatus);
|
|
4777
|
+
const runtimeErrorAction = classifyRuntimeErrorAction(rawMessage, runtimeErrorClass);
|
|
4597
4778
|
const fingerprint = fingerprintRuntimeError(scrubbed);
|
|
4598
4779
|
const spanAttrs = {
|
|
4599
4780
|
runtime_error_class: runtimeErrorClass,
|
|
4781
|
+
runtime_error_action: runtimeErrorAction,
|
|
4782
|
+
runtime_error_action_required: runtimeErrorAction !== "none",
|
|
4600
4783
|
runtime_error_fingerprint: fingerprint,
|
|
4601
4784
|
runtime_error_message_present: rawMessage.length > 0,
|
|
4602
4785
|
runtime_error_message_length_bucket: bucketLength(rawMessage.length),
|
|
@@ -4613,6 +4796,14 @@ function buildRuntimeErrorDiagnosticEnvelope(message) {
|
|
|
4613
4796
|
}
|
|
4614
4797
|
};
|
|
4615
4798
|
}
|
|
4799
|
+
function formatRuntimeLoginRequiredMessage(runtimeId) {
|
|
4800
|
+
const runtimeLabel = runtimeDisplayName(runtimeId);
|
|
4801
|
+
return `${runtimeLabel} is not logged in on this machine. Please log in to ${runtimeLabel} locally, then retry starting this agent.`;
|
|
4802
|
+
}
|
|
4803
|
+
function formatRuntimeStartTimeoutMessage(runtimeId) {
|
|
4804
|
+
const runtimeLabel = runtimeDisplayName(runtimeId);
|
|
4805
|
+
return `${runtimeLabel} did not finish starting on this machine. Check that ${runtimeLabel} is installed, logged in, and can run non-interactively, then retry starting this agent.`;
|
|
4806
|
+
}
|
|
4616
4807
|
function scrubRuntimeErrorDiagnosticText(value) {
|
|
4617
4808
|
return value.replace(/\bBearer\s+[A-Za-z0-9._~+/=-]{8,}/gi, "Bearer [REDACTED_TOKEN]").replace(/\b(?:sk|sk-ant|sk-proj|xox[baprs]?)-[A-Za-z0-9_-]{8,}\b/g, "[REDACTED_TOKEN]").replace(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g, "[REDACTED_EMAIL]").replace(/https?:\/\/[^\s"'<>]+/gi, (url) => redactUrlQuery(url)).replace(/\/Users\/[^\s"'<>:]+(?:\/[^\s"'<>:]+)*/g, "[REDACTED_PATH]").replace(/\/home\/[^\s"'<>:]+(?:\/[^\s"'<>:]+)*/g, "[REDACTED_PATH]").replace(/[A-Za-z]:\\Users\\[^\s"'<>:]+(?:\\[^\s"'<>:]+)*/g, "[REDACTED_PATH]");
|
|
4618
4809
|
}
|
|
@@ -4636,12 +4827,44 @@ function classifyRuntimeError(message, httpStatus) {
|
|
|
4636
4827
|
if (httpStatus >= 500) return "ProviderServerError";
|
|
4637
4828
|
return "ProviderApiError";
|
|
4638
4829
|
}
|
|
4830
|
+
if (isRuntimeAuthActionRequiredText(message)) return "AuthError";
|
|
4639
4831
|
if (/\btimeout|timed out\b/i.test(message)) return "TimeoutError";
|
|
4640
4832
|
if (/stream closed before response\.completed|error decoding response body/i.test(message)) return "ProviderStreamError";
|
|
4641
4833
|
if (/\brate.?limit|too many requests\b/i.test(message)) return "RateLimitError";
|
|
4642
4834
|
if (/\bnot found\b/i.test(message)) return "NotFoundError";
|
|
4643
4835
|
return "RuntimeError";
|
|
4644
4836
|
}
|
|
4837
|
+
function classifyRuntimeErrorAction(message, runtimeErrorClass) {
|
|
4838
|
+
if (runtimeErrorClass === "AuthError" && isRuntimeAuthActionRequiredText(message)) {
|
|
4839
|
+
return "user_reauth";
|
|
4840
|
+
}
|
|
4841
|
+
return "none";
|
|
4842
|
+
}
|
|
4843
|
+
function isRuntimeAuthActionRequiredText(text) {
|
|
4844
|
+
return RUNTIME_AUTH_ACTION_REQUIRED_PATTERNS.some((pattern) => pattern.test(text));
|
|
4845
|
+
}
|
|
4846
|
+
function runtimeDisplayName(runtimeId) {
|
|
4847
|
+
switch (runtimeId) {
|
|
4848
|
+
case "antigravity":
|
|
4849
|
+
return "Antigravity CLI";
|
|
4850
|
+
case "claude":
|
|
4851
|
+
return "Claude Code";
|
|
4852
|
+
case "codex":
|
|
4853
|
+
return "Codex";
|
|
4854
|
+
case "copilot":
|
|
4855
|
+
return "GitHub Copilot CLI";
|
|
4856
|
+
case "cursor":
|
|
4857
|
+
return "Cursor CLI";
|
|
4858
|
+
case "gemini":
|
|
4859
|
+
return "Gemini CLI";
|
|
4860
|
+
case "kimi":
|
|
4861
|
+
return "Kimi CLI";
|
|
4862
|
+
case "opencode":
|
|
4863
|
+
return "OpenCode";
|
|
4864
|
+
default:
|
|
4865
|
+
return runtimeId || "This runtime";
|
|
4866
|
+
}
|
|
4867
|
+
}
|
|
4645
4868
|
function fingerprintRuntimeError(value) {
|
|
4646
4869
|
const normalized = value.toLowerCase().replace(/[0-9a-f]{12,}/g, "<hex>").replace(/\b\d+\b/g, "<num>").replace(/\s+/g, " ").trim();
|
|
4647
4870
|
return createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
@@ -4755,6 +4978,12 @@ function stalledRecoverySigtermTimeoutMs() {
|
|
|
4755
4978
|
DEFAULT_STALLED_RECOVERY_SIGTERM_TIMEOUT_MS
|
|
4756
4979
|
);
|
|
4757
4980
|
}
|
|
4981
|
+
function runtimeStartTimeoutMs() {
|
|
4982
|
+
return readNonNegativeIntegerEnv(
|
|
4983
|
+
"SLOCK_DAEMON_RUNTIME_START_TIMEOUT_MS",
|
|
4984
|
+
DEFAULT_RUNTIME_START_TIMEOUT_MS
|
|
4985
|
+
);
|
|
4986
|
+
}
|
|
4758
4987
|
function toLocalTime(iso) {
|
|
4759
4988
|
const d = new Date(iso);
|
|
4760
4989
|
if (isNaN(d.getTime())) return iso;
|
|
@@ -4838,7 +5067,7 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
|
4838
5067
|
runtime,
|
|
4839
5068
|
sessionId,
|
|
4840
5069
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4841
|
-
note: "The native runtime transcript file was not found on this machine; this daemon-created handoff records the
|
|
5070
|
+
note: "The native runtime transcript file was not found on this machine; this daemon-created handoff records the runtime session identity for diagnostics."
|
|
4842
5071
|
}) + "\n", { mode: 384 });
|
|
4843
5072
|
return {
|
|
4844
5073
|
label: sessionId,
|
|
@@ -4947,43 +5176,23 @@ function formatRuntimeProfileControlPrompt(messages) {
|
|
|
4947
5176
|
message,
|
|
4948
5177
|
notification: runtimeProfileNotificationFromMessage(message)
|
|
4949
5178
|
}));
|
|
4950
|
-
if (controls.length === 0 || controls.some(({ notification }) => !notification)) {
|
|
5179
|
+
if (controls.length === 0 || controls.some(({ notification }) => !notification || notification.kind !== "daemon_release_notice")) {
|
|
4951
5180
|
return null;
|
|
4952
5181
|
}
|
|
4953
5182
|
const body = controls.map(({ message }) => message.content).join("\n\n---\n\n");
|
|
4954
|
-
const hasMigration = controls.some(({ notification }) => notification?.kind === "migration");
|
|
4955
|
-
if (!hasMigration) {
|
|
4956
|
-
return [
|
|
4957
|
-
"Runtime Profile daemon release notice.",
|
|
4958
|
-
"",
|
|
4959
|
-
"Read the notice below before continuing. No chat reply or runtime control action is required for this notice \u2014 resume normal inbox processing afterward.",
|
|
4960
|
-
"",
|
|
4961
|
-
body
|
|
4962
|
-
].join("\n");
|
|
4963
|
-
}
|
|
4964
5183
|
return [
|
|
4965
|
-
"Runtime Profile
|
|
4966
|
-
"",
|
|
4967
|
-
"Complete the required runtime control action before reading or responding to normal inbox messages.",
|
|
5184
|
+
"Runtime Profile notice.",
|
|
4968
5185
|
"",
|
|
4969
|
-
|
|
5186
|
+
"Read the daemon release notice below before continuing. No chat reply or runtime control action is required \u2014 resume normal inbox processing afterward.",
|
|
4970
5187
|
"",
|
|
4971
|
-
|
|
5188
|
+
body
|
|
4972
5189
|
].join("\n");
|
|
4973
5190
|
}
|
|
4974
5191
|
function formatRuntimeProfileControlStartupInput(control, driver) {
|
|
4975
|
-
|
|
4976
|
-
return [
|
|
4977
|
-
"Read the Runtime Profile daemon release notice from your system prompt before normal work.",
|
|
4978
|
-
"No chat reply is required for this notice. Stop after reading it; queued inbox messages will be delivered separately."
|
|
4979
|
-
].join("\n");
|
|
4980
|
-
}
|
|
4981
|
-
const actionName = `${driver.mcpToolPrefix}runtime_profile_migration_done`;
|
|
5192
|
+
void driver;
|
|
4982
5193
|
return [
|
|
4983
|
-
"Runtime Profile
|
|
4984
|
-
|
|
4985
|
-
"Do not read MEMORY.md, check messages, or send a chat reply before this tool call.",
|
|
4986
|
-
"After the runtime control action succeeds, stop. Queued inbox messages will be delivered separately.",
|
|
5194
|
+
"Read the Runtime Profile daemon release notice from your system prompt before normal work.",
|
|
5195
|
+
"No chat reply or runtime control action is required for this notice. Stop after reading it; queued inbox messages will be delivered separately.",
|
|
4987
5196
|
"",
|
|
4988
5197
|
control.message
|
|
4989
5198
|
].join("\n");
|
|
@@ -5002,6 +5211,7 @@ var TRAJECTORY_COALESCE_MS = 350;
|
|
|
5002
5211
|
var ACTIVITY_HEARTBEAT_MS = 6e4;
|
|
5003
5212
|
var COMPACTION_STALE_MS = 5 * 6e4;
|
|
5004
5213
|
var RUNTIME_PROGRESS_STALE_MS = 15 * 6e4;
|
|
5214
|
+
var DEFAULT_RUNTIME_START_TIMEOUT_MS = 2 * 6e4;
|
|
5005
5215
|
var DEFAULT_STALLED_RECOVERY_SIGTERM_TIMEOUT_MS = 1e4;
|
|
5006
5216
|
var MAX_STDOUT_LINES = 8;
|
|
5007
5217
|
var MAX_STDOUT_LINE_LENGTH = 240;
|
|
@@ -5444,7 +5654,7 @@ function runtimeProfileNotificationFromMessage(message) {
|
|
|
5444
5654
|
return null;
|
|
5445
5655
|
}
|
|
5446
5656
|
function runtimeProfileNotificationTitle(kind) {
|
|
5447
|
-
return kind === "migration" ? "Runtime Profile
|
|
5657
|
+
return kind === "migration" ? "Runtime Profile reset" : "Runtime Profile notice";
|
|
5448
5658
|
}
|
|
5449
5659
|
function hashRuntimeProfileKey(key) {
|
|
5450
5660
|
if (!key) return void 0;
|
|
@@ -5511,9 +5721,14 @@ function classifyTerminalFailure(ap) {
|
|
|
5511
5721
|
...ap.recentStderr
|
|
5512
5722
|
].filter((value) => !!value);
|
|
5513
5723
|
for (const text of candidates) {
|
|
5724
|
+
const diagnostics = buildRuntimeErrorDiagnosticEnvelope(text);
|
|
5514
5725
|
const lower = text.toLowerCase();
|
|
5515
|
-
if (lower.includes("usage limit") || lower.includes("quota exceeded") || lower.includes("quota limit") || lower.includes("budget limit exceeded") || lower.includes("usage not included in your plan") || lower.includes("modelnotfounderror") || lower.includes("requested entity was not found") || lower.includes("model deprecated") || lower.includes("model not found") || isProviderStreamFailureText(text)) {
|
|
5516
|
-
|
|
5726
|
+
if (lower.includes("usage limit") || lower.includes("quota exceeded") || lower.includes("quota limit") || lower.includes("budget limit exceeded") || lower.includes("usage not included in your plan") || lower.includes("modelnotfounderror") || lower.includes("requested entity was not found") || lower.includes("model deprecated") || lower.includes("model not found") || diagnostics.spanAttrs.runtime_error_action_required === true || isProviderStreamFailureText(text) || isRuntimeStartTimeoutText(text)) {
|
|
5727
|
+
const actionRequired = diagnostics.spanAttrs.runtime_error_action_required === true;
|
|
5728
|
+
return {
|
|
5729
|
+
detail: actionRequired ? formatRuntimeLoginRequiredMessage(ap.driver.id) : text,
|
|
5730
|
+
actionRequired
|
|
5731
|
+
};
|
|
5517
5732
|
}
|
|
5518
5733
|
}
|
|
5519
5734
|
return null;
|
|
@@ -5521,6 +5736,9 @@ function classifyTerminalFailure(ap) {
|
|
|
5521
5736
|
function isProviderStreamFailureText(text) {
|
|
5522
5737
|
return /stream closed before response\.completed|error decoding response body/i.test(text);
|
|
5523
5738
|
}
|
|
5739
|
+
function isRuntimeStartTimeoutText(text) {
|
|
5740
|
+
return /did not finish starting on this machine/i.test(text);
|
|
5741
|
+
}
|
|
5524
5742
|
function isCodexProviderReconnectLog(text) {
|
|
5525
5743
|
return /Reconnecting\.\.\.\s*\d+\s*\/\s*\d+/i.test(text);
|
|
5526
5744
|
}
|
|
@@ -5607,6 +5825,8 @@ function buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes) {
|
|
|
5607
5825
|
lastActivityDetailKind: classifyActivityDetailForTrace(ap.lastActivityDetail),
|
|
5608
5826
|
runtime: ap.config.runtime,
|
|
5609
5827
|
model: ap.config.model,
|
|
5828
|
+
platform: process.platform,
|
|
5829
|
+
arch: process.arch,
|
|
5610
5830
|
launchId: ap.launchId || void 0,
|
|
5611
5831
|
sessionIdPresent: Boolean(ap.sessionId),
|
|
5612
5832
|
inboxCount: ap.inbox.length,
|
|
@@ -5623,6 +5843,12 @@ function buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes) {
|
|
|
5623
5843
|
}
|
|
5624
5844
|
};
|
|
5625
5845
|
}
|
|
5846
|
+
function classifyRuntimeStallReason(ap) {
|
|
5847
|
+
if (ap.lastRuntimeEventKind === "tool_output" && ap.gatedSteering.outstandingToolUses === 0) {
|
|
5848
|
+
return "harness_post_tool_silent_wedge";
|
|
5849
|
+
}
|
|
5850
|
+
return "no_runtime_events";
|
|
5851
|
+
}
|
|
5626
5852
|
function bucketBytes(value) {
|
|
5627
5853
|
const bytes = typeof value === "string" ? Buffer.byteLength(value, "utf8") : Math.max(0, Math.floor(value ?? 0));
|
|
5628
5854
|
if (bytes === 0) return "0";
|
|
@@ -5927,9 +6153,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
5927
6153
|
method: input.method,
|
|
5928
6154
|
path: input.pathname,
|
|
5929
6155
|
query_keys: input.queryKeys,
|
|
5930
|
-
|
|
5931
|
-
upstream_status: input.upstreamStatus,
|
|
5932
|
-
error_class: input.errorClass,
|
|
6156
|
+
error_name: input.errorName,
|
|
5933
6157
|
error_message: input.errorMessage
|
|
5934
6158
|
}, "error");
|
|
5935
6159
|
}
|
|
@@ -6182,9 +6406,31 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6182
6406
|
this.recordDaemonTrace("daemon.agent.spawn.started", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId));
|
|
6183
6407
|
try {
|
|
6184
6408
|
const driver = this.driverResolver(config.runtime || "claude");
|
|
6409
|
+
const legacyWakeRuntimeProfile = wakeMessage ? runtimeProfileNotificationFromMessage(wakeMessage) : null;
|
|
6410
|
+
if (legacyWakeRuntimeProfile?.kind === "migration") {
|
|
6411
|
+
this.completeDeprecatedRuntimeProfileMigration(
|
|
6412
|
+
agentId,
|
|
6413
|
+
legacyWakeRuntimeProfile.key,
|
|
6414
|
+
launchId || null,
|
|
6415
|
+
wakeMessage?.traceparent,
|
|
6416
|
+
"wake_message"
|
|
6417
|
+
);
|
|
6418
|
+
wakeMessage = void 0;
|
|
6419
|
+
}
|
|
6185
6420
|
const agentDataDir = path12.join(this.dataDir, agentId);
|
|
6186
6421
|
await mkdir(agentDataDir, { recursive: true });
|
|
6187
|
-
|
|
6422
|
+
let runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
|
|
6423
|
+
const legacyRuntimeProfileControl = runtimeConfig.runtimeProfileControl?.kind === "migration" ? runtimeConfig.runtimeProfileControl : null;
|
|
6424
|
+
if (legacyRuntimeProfileControl) {
|
|
6425
|
+
this.completeDeprecatedRuntimeProfileMigration(
|
|
6426
|
+
agentId,
|
|
6427
|
+
legacyRuntimeProfileControl.key,
|
|
6428
|
+
launchId || null,
|
|
6429
|
+
void 0,
|
|
6430
|
+
"agent_config"
|
|
6431
|
+
);
|
|
6432
|
+
runtimeConfig = { ...runtimeConfig, runtimeProfileControl: null };
|
|
6433
|
+
}
|
|
6188
6434
|
const memoryMdPath = path12.join(agentDataDir, "MEMORY.md");
|
|
6189
6435
|
try {
|
|
6190
6436
|
await access(memoryMdPath);
|
|
@@ -6300,7 +6546,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6300
6546
|
}
|
|
6301
6547
|
return;
|
|
6302
6548
|
}
|
|
6303
|
-
const { process: proc } = driver.spawn({
|
|
6549
|
+
const { process: proc } = await driver.spawn({
|
|
6304
6550
|
agentId,
|
|
6305
6551
|
config: effectiveConfig,
|
|
6306
6552
|
standingPrompt,
|
|
@@ -6332,9 +6578,12 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6332
6578
|
notificationTimer: null,
|
|
6333
6579
|
pendingNotificationCount: 0,
|
|
6334
6580
|
activityHeartbeat: null,
|
|
6581
|
+
startupTimeoutTimer: null,
|
|
6582
|
+
startupTimedOut: false,
|
|
6335
6583
|
compactionWatchdog: null,
|
|
6336
6584
|
compactionStartedAt: null,
|
|
6337
6585
|
lastRuntimeEventAt: Date.now(),
|
|
6586
|
+
lastRuntimeEventKind: null,
|
|
6338
6587
|
runtimeProgressStaleSince: null,
|
|
6339
6588
|
runtimeTraceSpan: null,
|
|
6340
6589
|
runtimeTraceCounters: createRuntimeTraceCounters(),
|
|
@@ -6362,8 +6611,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6362
6611
|
});
|
|
6363
6612
|
this.startRuntimeTrace(agentId, agentProcess, "spawn", wakeMessage ? [wakeMessage] : void 0, runtimeInputTraceAttrs);
|
|
6364
6613
|
this.agentsStarting.delete(agentId);
|
|
6365
|
-
if (
|
|
6366
|
-
this.ackInjectedRuntimeProfileControl(agentId,
|
|
6614
|
+
if (runtimeConfig.runtimeProfileControl) {
|
|
6615
|
+
this.ackInjectedRuntimeProfileControl(agentId, runtimeConfig.runtimeProfileControl, agentProcess.launchId);
|
|
6367
6616
|
}
|
|
6368
6617
|
if (wakeMessage) {
|
|
6369
6618
|
this.consumeVisibleMessages(agentId, { messages: [wakeMessage], source: "spawn_wake_message" });
|
|
@@ -6415,7 +6664,10 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6415
6664
|
});
|
|
6416
6665
|
proc.on("error", (err) => {
|
|
6417
6666
|
const current = this.agents.get(agentId);
|
|
6418
|
-
if (current)
|
|
6667
|
+
if (current) {
|
|
6668
|
+
current.spawnError = err.message;
|
|
6669
|
+
this.clearRuntimeStartupTimeout(current);
|
|
6670
|
+
}
|
|
6419
6671
|
this.recordDaemonTrace("daemon.agent.process.error", {
|
|
6420
6672
|
agentId,
|
|
6421
6673
|
launchId: current?.launchId || void 0,
|
|
@@ -6459,10 +6711,11 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6459
6711
|
if (ap.activityHeartbeat) {
|
|
6460
6712
|
clearInterval(ap.activityHeartbeat);
|
|
6461
6713
|
}
|
|
6714
|
+
this.clearRuntimeStartupTimeout(ap);
|
|
6462
6715
|
this.clearStalledRecoverySigtermWatchdog(ap);
|
|
6463
6716
|
const finalCode = ap.exitCode ?? code;
|
|
6464
6717
|
const finalSignal = ap.exitSignal ?? signal;
|
|
6465
|
-
const expectedTermination = Boolean(ap.expectedTerminationReason);
|
|
6718
|
+
const expectedTermination = Boolean(ap.expectedTerminationReason) || ap.startupTimedOut;
|
|
6466
6719
|
const processEndedCleanly = finalCode === 0 || expectedTermination && !ap.lastRuntimeError;
|
|
6467
6720
|
const terminalFailureDetail = processEndedCleanly ? null : classifyTerminalFailure(ap);
|
|
6468
6721
|
const missingResumeSession = isMissingResumeSession(ap);
|
|
@@ -6555,7 +6808,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6555
6808
|
this.broadcastActivity(agentId, "online", "Process idle");
|
|
6556
6809
|
} else {
|
|
6557
6810
|
const reason = formatCrashReason(finalCode, finalSignal, ap);
|
|
6558
|
-
if (terminalFailureDetail && isProviderStreamFailureText(terminalFailureDetail)) {
|
|
6811
|
+
if (terminalFailureDetail && isProviderStreamFailureText(terminalFailureDetail.detail)) {
|
|
6559
6812
|
this.idleAgentConfigs.set(agentId, {
|
|
6560
6813
|
config: { ...ap.config, sessionId: ap.sessionId },
|
|
6561
6814
|
sessionId: ap.sessionId,
|
|
@@ -6563,19 +6816,23 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6563
6816
|
});
|
|
6564
6817
|
logger.warn(`[Agent ${agentId}] Recoverable provider stream failure (${reason}) \u2014 keeping agent wakeable`);
|
|
6565
6818
|
this.sendAgentStatus(agentId, "active", ap.launchId);
|
|
6819
|
+
} else if (ap.startupTimedOut) {
|
|
6820
|
+
logger.warn(`[Agent ${agentId}] Startup timeout cleanup completed (${reason})`);
|
|
6566
6821
|
} else {
|
|
6567
6822
|
this.idleAgentConfigs.delete(agentId);
|
|
6568
6823
|
logger.error(`[Agent ${agentId}] Process crashed (${reason}) \u2014 marking inactive`);
|
|
6569
6824
|
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
6570
6825
|
}
|
|
6571
6826
|
if (terminalFailureDetail) {
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6827
|
+
if (!ap.startupTimedOut) {
|
|
6828
|
+
this.broadcastActivity(
|
|
6829
|
+
agentId,
|
|
6830
|
+
"error",
|
|
6831
|
+
terminalFailureDetail.detail,
|
|
6832
|
+
[{ kind: "text", text: `Error: ${terminalFailureDetail.detail}` }],
|
|
6833
|
+
ap.launchId
|
|
6834
|
+
);
|
|
6835
|
+
}
|
|
6579
6836
|
} else {
|
|
6580
6837
|
this.broadcastActivity(agentId, "offline", `Crashed (${summary})`, [], ap.launchId);
|
|
6581
6838
|
}
|
|
@@ -6584,6 +6841,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6584
6841
|
});
|
|
6585
6842
|
this.sendAgentStatus(agentId, "active", launchId || null);
|
|
6586
6843
|
this.broadcastActivity(agentId, "working", "Starting\u2026");
|
|
6844
|
+
this.startRuntimeStartupTimeout(agentId, agentProcess);
|
|
6587
6845
|
} catch (err) {
|
|
6588
6846
|
this.agentsStarting.delete(agentId);
|
|
6589
6847
|
throw err;
|
|
@@ -7103,6 +7361,30 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7103
7361
|
logger.info(`[Agent ${agentId}] Deferred non-concrete wake message for ${driver.id}`);
|
|
7104
7362
|
return true;
|
|
7105
7363
|
}
|
|
7364
|
+
completeDeprecatedRuntimeProfileMigration(agentId, migrationKey, launchId, traceparent, source) {
|
|
7365
|
+
this.sendToServer({
|
|
7366
|
+
type: "agent:runtime_profile:migration:ack",
|
|
7367
|
+
agentId,
|
|
7368
|
+
migrationKey,
|
|
7369
|
+
launchId: launchId || void 0,
|
|
7370
|
+
traceparent
|
|
7371
|
+
});
|
|
7372
|
+
this.sendToServer({
|
|
7373
|
+
type: "agent:runtime_profile:migration_done",
|
|
7374
|
+
agentId,
|
|
7375
|
+
migrationKey,
|
|
7376
|
+
launchId: launchId || void 0,
|
|
7377
|
+
traceparent
|
|
7378
|
+
});
|
|
7379
|
+
this.recordDaemonTrace("daemon.runtime_profile.migration.deprecated_noop", {
|
|
7380
|
+
agentId,
|
|
7381
|
+
key_present: Boolean(migrationKey),
|
|
7382
|
+
key_hash: hashRuntimeProfileKey(migrationKey),
|
|
7383
|
+
launchId: launchId || void 0,
|
|
7384
|
+
source
|
|
7385
|
+
});
|
|
7386
|
+
logger.info(`[Agent ${agentId}] Completed deprecated Runtime Profile migration ${migrationKey} as reset-session no-op`);
|
|
7387
|
+
}
|
|
7106
7388
|
getAgentSessionId(agentId) {
|
|
7107
7389
|
return this.agents.get(agentId)?.sessionId ?? null;
|
|
7108
7390
|
}
|
|
@@ -7164,7 +7446,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7164
7446
|
}
|
|
7165
7447
|
return reports;
|
|
7166
7448
|
}
|
|
7167
|
-
deliverRuntimeProfileNotification(agentId, key, kind, content, traceparent) {
|
|
7449
|
+
deliverRuntimeProfileNotification(agentId, key, kind, content, traceparent, launchId) {
|
|
7168
7450
|
const span = this.tracer.startSpan("daemon.runtime_profile.control.inject", {
|
|
7169
7451
|
parent: parseTraceparent(traceparent),
|
|
7170
7452
|
surface: "daemon",
|
|
@@ -7176,6 +7458,17 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7176
7458
|
key_hash: hashRuntimeProfileKey(key)
|
|
7177
7459
|
}
|
|
7178
7460
|
});
|
|
7461
|
+
if (kind === "migration") {
|
|
7462
|
+
this.completeDeprecatedRuntimeProfileMigration(
|
|
7463
|
+
agentId,
|
|
7464
|
+
key,
|
|
7465
|
+
launchId || null,
|
|
7466
|
+
formatTraceparent(span.context),
|
|
7467
|
+
"runtime_profile_message"
|
|
7468
|
+
);
|
|
7469
|
+
span.end("ok", { attrs: { outcome: "deprecated_noop_completed", launchId: launchId || void 0 } });
|
|
7470
|
+
return true;
|
|
7471
|
+
}
|
|
7179
7472
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7180
7473
|
const message = {
|
|
7181
7474
|
channel_id: "system",
|
|
@@ -7186,7 +7479,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7186
7479
|
sender_type: "system",
|
|
7187
7480
|
content,
|
|
7188
7481
|
timestamp: now,
|
|
7189
|
-
message_id: `${
|
|
7482
|
+
message_id: `${RUNTIME_PROFILE_DAEMON_NOTICE_MESSAGE_PREFIX}${key}`,
|
|
7190
7483
|
traceparent: formatTraceparent(span.context)
|
|
7191
7484
|
};
|
|
7192
7485
|
const ap = this.agents.get(agentId);
|
|
@@ -7281,25 +7574,19 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7281
7574
|
for (const message of messages) {
|
|
7282
7575
|
const notification = runtimeProfileNotificationFromMessage(message);
|
|
7283
7576
|
if (!notification) continue;
|
|
7284
|
-
const title = runtimeProfileNotificationTitle(notification.kind);
|
|
7285
|
-
this.broadcastActivity(agentId, "working", title, [{ kind: "system", title, text: message.content }], launchId);
|
|
7286
7577
|
if (notification.kind === "migration") {
|
|
7287
|
-
this.
|
|
7288
|
-
|
|
7289
|
-
agentId,
|
|
7290
|
-
migrationKey: notification.key,
|
|
7291
|
-
launchId: launchId || void 0,
|
|
7292
|
-
traceparent: message.traceparent
|
|
7293
|
-
});
|
|
7294
|
-
} else {
|
|
7295
|
-
this.sendToServer({
|
|
7296
|
-
type: "agent:runtime_profile:daemon_release_notice:ack",
|
|
7297
|
-
agentId,
|
|
7298
|
-
noticeKey: notification.key,
|
|
7299
|
-
launchId: launchId || void 0,
|
|
7300
|
-
traceparent: message.traceparent
|
|
7301
|
-
});
|
|
7578
|
+
this.completeDeprecatedRuntimeProfileMigration(agentId, notification.key, launchId, message.traceparent, "runtime_profile_message");
|
|
7579
|
+
continue;
|
|
7302
7580
|
}
|
|
7581
|
+
const title = runtimeProfileNotificationTitle(notification.kind);
|
|
7582
|
+
this.broadcastActivity(agentId, "working", title, [{ kind: "system", title, text: message.content }], launchId);
|
|
7583
|
+
this.sendToServer({
|
|
7584
|
+
type: "agent:runtime_profile:daemon_release_notice:ack",
|
|
7585
|
+
agentId,
|
|
7586
|
+
noticeKey: notification.key,
|
|
7587
|
+
launchId: launchId || void 0,
|
|
7588
|
+
traceparent: message.traceparent
|
|
7589
|
+
});
|
|
7303
7590
|
}
|
|
7304
7591
|
}
|
|
7305
7592
|
ackInjectedRuntimeProfileControl(agentId, control, launchId) {
|
|
@@ -7316,24 +7603,19 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7316
7603
|
}
|
|
7317
7604
|
});
|
|
7318
7605
|
const title = runtimeProfileNotificationTitle(control.kind);
|
|
7319
|
-
this.broadcastActivity(agentId, "working", title, [{ kind: "system", title, text: control.message }], launchId);
|
|
7320
7606
|
if (control.kind === "migration") {
|
|
7321
|
-
this.
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
migrationKey: control.key,
|
|
7325
|
-
launchId: launchId || void 0,
|
|
7326
|
-
traceparent: formatTraceparent(span.context)
|
|
7327
|
-
});
|
|
7328
|
-
} else {
|
|
7329
|
-
this.sendToServer({
|
|
7330
|
-
type: "agent:runtime_profile:daemon_release_notice:ack",
|
|
7331
|
-
agentId,
|
|
7332
|
-
noticeKey: control.key,
|
|
7333
|
-
launchId: launchId || void 0,
|
|
7334
|
-
traceparent: formatTraceparent(span.context)
|
|
7335
|
-
});
|
|
7607
|
+
this.completeDeprecatedRuntimeProfileMigration(agentId, control.key, launchId, formatTraceparent(span.context), "agent_config");
|
|
7608
|
+
span.end("ok", { attrs: { outcome: "deprecated_noop_completed" } });
|
|
7609
|
+
return;
|
|
7336
7610
|
}
|
|
7611
|
+
this.broadcastActivity(agentId, "working", title, [{ kind: "system", title, text: control.message }], launchId);
|
|
7612
|
+
this.sendToServer({
|
|
7613
|
+
type: "agent:runtime_profile:daemon_release_notice:ack",
|
|
7614
|
+
agentId,
|
|
7615
|
+
noticeKey: control.key,
|
|
7616
|
+
launchId: launchId || void 0,
|
|
7617
|
+
traceparent: formatTraceparent(span.context)
|
|
7618
|
+
});
|
|
7337
7619
|
span.end("ok", { attrs: { outcome: "agent_config_ack_sent" } });
|
|
7338
7620
|
}
|
|
7339
7621
|
sendRuntimeProfileWireReport(report) {
|
|
@@ -7774,7 +8056,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7774
8056
|
runtime_profile_key_hash: control.keyHash || void 0,
|
|
7775
8057
|
runtime_profile_key_present: control.keyPresent,
|
|
7776
8058
|
runtime_profile_pending_age_ms: pendingAgeMs,
|
|
7777
|
-
runtime_profile_requires_ack:
|
|
8059
|
+
runtime_profile_requires_ack: false,
|
|
7778
8060
|
runtime_profile_migration_done_observed: control.kind === "migration" ? control.migrationDoneToolObserved : void 0
|
|
7779
8061
|
};
|
|
7780
8062
|
}
|
|
@@ -7801,19 +8083,11 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7801
8083
|
const control = ap.runtimeProfileTurnControl;
|
|
7802
8084
|
if (!control) return {};
|
|
7803
8085
|
const attrs = this.runtimeProfileTurnControlTraceAttrs(control);
|
|
7804
|
-
if (control.kind === "migration" && !control.migrationDoneToolObserved) {
|
|
7805
|
-
this.recordRuntimeTraceEvent(agentId, ap, "runtime_profile.migration.turn_without_ack", {
|
|
7806
|
-
...attrs,
|
|
7807
|
-
terminal,
|
|
7808
|
-
inbox_count: ap.inbox.length,
|
|
7809
|
-
pending_notification_count: ap.pendingNotificationCount
|
|
7810
|
-
});
|
|
7811
|
-
}
|
|
7812
8086
|
ap.runtimeProfileTurnControl = null;
|
|
7813
8087
|
return {
|
|
7814
8088
|
...attrs,
|
|
7815
8089
|
runtime_profile_turn_terminal: terminal,
|
|
7816
|
-
runtime_profile_turn_outcome: control.kind === "migration" ? control.migrationDoneToolObserved ? "
|
|
8090
|
+
runtime_profile_turn_outcome: control.kind === "migration" ? control.migrationDoneToolObserved ? "deprecated_migration_done_observed" : "reset_session_notice" : "notice_only"
|
|
7817
8091
|
};
|
|
7818
8092
|
}
|
|
7819
8093
|
startRuntimeTrace(agentId, ap, reason, messages, inputTraceAttrs = {}) {
|
|
@@ -7854,10 +8128,42 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7854
8128
|
recordRuntimeTraceEvent(agentId, ap, name, attrs) {
|
|
7855
8129
|
this.startRuntimeTrace(agentId, ap, "runtime-progress").addEvent(name, attrs);
|
|
7856
8130
|
}
|
|
7857
|
-
noteRuntimeProgress(ap) {
|
|
8131
|
+
noteRuntimeProgress(ap, eventKind) {
|
|
7858
8132
|
ap.lastRuntimeEventAt = Date.now();
|
|
8133
|
+
ap.lastRuntimeEventKind = eventKind ?? null;
|
|
7859
8134
|
ap.runtimeProgressStaleSince = null;
|
|
7860
8135
|
}
|
|
8136
|
+
observeRuntimeTranscriptProgress(agentId, ap, staleForMs, source) {
|
|
8137
|
+
if (ap.config.runtime !== "codex" || !ap.sessionId) return false;
|
|
8138
|
+
const ref = resolveRuntimeSessionRef(ap.config.runtime, ap.sessionId, this.runtimeSessionHomeDir);
|
|
8139
|
+
if (!ref.reachable || !ref.path) return false;
|
|
8140
|
+
let mtimeMs;
|
|
8141
|
+
try {
|
|
8142
|
+
const stats = statSync2(ref.path);
|
|
8143
|
+
if (!stats.isFile()) return false;
|
|
8144
|
+
mtimeMs = stats.mtimeMs;
|
|
8145
|
+
} catch {
|
|
8146
|
+
return false;
|
|
8147
|
+
}
|
|
8148
|
+
if (mtimeMs <= ap.lastRuntimeEventAt) return false;
|
|
8149
|
+
const now = Date.now();
|
|
8150
|
+
const transcriptAgeMs = Math.max(0, now - mtimeMs);
|
|
8151
|
+
if (transcriptAgeMs >= RUNTIME_PROGRESS_STALE_MS) return false;
|
|
8152
|
+
ap.lastRuntimeEventAt = mtimeMs;
|
|
8153
|
+
ap.runtimeProgressStaleSince = null;
|
|
8154
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.internal_observed", {
|
|
8155
|
+
turn_outcome: "held",
|
|
8156
|
+
turn_subtype: "runtime_progress",
|
|
8157
|
+
turn_reason: "internal_activity_observed",
|
|
8158
|
+
signal: "runtime_transcript_mtime",
|
|
8159
|
+
source,
|
|
8160
|
+
runtime: ap.config.runtime,
|
|
8161
|
+
sessionRefReachable: true,
|
|
8162
|
+
transcriptAgeMs,
|
|
8163
|
+
previousStaleForMs: staleForMs
|
|
8164
|
+
});
|
|
8165
|
+
return true;
|
|
8166
|
+
}
|
|
7861
8167
|
recordGatedSteeringEvent(agentId, ap, event, attrs = {}) {
|
|
7862
8168
|
if (ap.driver.busyDeliveryMode !== "gated") return;
|
|
7863
8169
|
const summary = `${event}:${ap.gatedSteering.phase}:tools=${ap.gatedSteering.outstandingToolUses}:compact=${ap.gatedSteering.compacting}`;
|
|
@@ -7937,6 +8243,66 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7937
8243
|
ap.gatedSteering.inFlightBatch = null;
|
|
7938
8244
|
this.recordGatedSteeringEvent(agentId, ap, "ack", { reason, messageCount });
|
|
7939
8245
|
}
|
|
8246
|
+
startRuntimeStartupTimeout(agentId, ap) {
|
|
8247
|
+
const timeoutMs = runtimeStartTimeoutMs();
|
|
8248
|
+
if (timeoutMs <= 0) return;
|
|
8249
|
+
ap.startupTimeoutTimer = setTimeout(() => {
|
|
8250
|
+
this.handleRuntimeStartupTimeout(agentId, ap, timeoutMs);
|
|
8251
|
+
}, timeoutMs);
|
|
8252
|
+
ap.startupTimeoutTimer.unref?.();
|
|
8253
|
+
}
|
|
8254
|
+
clearRuntimeStartupTimeout(ap) {
|
|
8255
|
+
if (!ap.startupTimeoutTimer) return;
|
|
8256
|
+
clearTimeout(ap.startupTimeoutTimer);
|
|
8257
|
+
ap.startupTimeoutTimer = null;
|
|
8258
|
+
}
|
|
8259
|
+
handleRuntimeStartupTimeout(agentId, ap, timeoutMs) {
|
|
8260
|
+
const current = this.agents.get(agentId);
|
|
8261
|
+
if (current !== ap) return;
|
|
8262
|
+
if (ap.lastRuntimeEventKind) {
|
|
8263
|
+
this.clearRuntimeStartupTimeout(ap);
|
|
8264
|
+
return;
|
|
8265
|
+
}
|
|
8266
|
+
this.clearRuntimeStartupTimeout(ap);
|
|
8267
|
+
const terminalFailureDetail = classifyTerminalFailure(ap);
|
|
8268
|
+
const detail = terminalFailureDetail?.detail ?? formatRuntimeStartTimeoutMessage(ap.driver.id);
|
|
8269
|
+
ap.startupTimedOut = true;
|
|
8270
|
+
ap.lastRuntimeError = detail;
|
|
8271
|
+
ap.runtimeProgressStaleSince = Date.now();
|
|
8272
|
+
const staleForMs = Math.max(timeoutMs, Date.now() - ap.lastRuntimeEventAt);
|
|
8273
|
+
const diagnostic = buildRuntimeStallDiagnostic(ap, staleForMs, Math.max(1, Math.floor(staleForMs / 6e4)));
|
|
8274
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.start.timeout", {
|
|
8275
|
+
turn_outcome: "failed",
|
|
8276
|
+
turn_subtype: "runtime_stalled",
|
|
8277
|
+
turn_reason: "no_runtime_events",
|
|
8278
|
+
runtime_start_failure_kind: "runtime_start_timeout",
|
|
8279
|
+
timeout_ms: timeoutMs,
|
|
8280
|
+
...diagnostic.traceAttrs
|
|
8281
|
+
});
|
|
8282
|
+
this.endRuntimeTrace(ap, "error", {
|
|
8283
|
+
turn_outcome: "failed",
|
|
8284
|
+
turn_subtype: "runtime_stalled",
|
|
8285
|
+
turn_reason: "no_runtime_events",
|
|
8286
|
+
runtime_start_failure_kind: "runtime_start_timeout",
|
|
8287
|
+
timeout_ms: timeoutMs,
|
|
8288
|
+
...runtimeTraceCounterAttrs(ap),
|
|
8289
|
+
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_stalled")
|
|
8290
|
+
});
|
|
8291
|
+
logger.warn(`[Agent ${agentId}] ${ap.driver.id} did not emit a startup runtime event within ${timeoutMs}ms; terminating process`);
|
|
8292
|
+
this.broadcastActivity(agentId, "error", detail, [{ kind: "text", text: `Error: ${detail}` }], ap.launchId);
|
|
8293
|
+
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
8294
|
+
this.idleAgentConfigs.delete(agentId);
|
|
8295
|
+
try {
|
|
8296
|
+
this.processExitTraceAttrs.set(ap.process, {
|
|
8297
|
+
stop_source: "startup_timeout",
|
|
8298
|
+
timeout_ms: timeoutMs
|
|
8299
|
+
});
|
|
8300
|
+
ap.process.kill("SIGTERM");
|
|
8301
|
+
} catch (err) {
|
|
8302
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
8303
|
+
logger.warn(`[Agent ${agentId}] Failed to terminate startup-timed-out ${ap.driver.id} process: ${reason}`);
|
|
8304
|
+
}
|
|
8305
|
+
}
|
|
7940
8306
|
requeueGatedInFlightBatch(agentId, ap, reason) {
|
|
7941
8307
|
if (ap.driver.busyDeliveryMode !== "gated" || !ap.gatedSteering.inFlightBatch) return;
|
|
7942
8308
|
const batch = ap.gatedSteering.inFlightBatch;
|
|
@@ -7957,12 +8323,21 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7957
8323
|
if (ap.runtimeProgressStaleSince) return true;
|
|
7958
8324
|
const staleForMs = Date.now() - ap.lastRuntimeEventAt;
|
|
7959
8325
|
if (staleForMs < RUNTIME_PROGRESS_STALE_MS) return false;
|
|
8326
|
+
if (this.observeRuntimeTranscriptProgress(agentId, ap, staleForMs, "activity_heartbeat")) return false;
|
|
7960
8327
|
ap.runtimeProgressStaleSince = Date.now();
|
|
7961
8328
|
const staleForMinutes = Math.max(1, Math.floor(staleForMs / 6e4));
|
|
7962
8329
|
const diagnostic = buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes);
|
|
7963
|
-
|
|
8330
|
+
const turnReason = classifyRuntimeStallReason(ap);
|
|
8331
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.stalled", {
|
|
8332
|
+
turn_outcome: "failed",
|
|
8333
|
+
turn_subtype: "runtime_stalled",
|
|
8334
|
+
turn_reason: turnReason,
|
|
8335
|
+
...diagnostic.traceAttrs
|
|
8336
|
+
});
|
|
7964
8337
|
this.endRuntimeTrace(ap, "error", {
|
|
7965
|
-
|
|
8338
|
+
turn_outcome: "failed",
|
|
8339
|
+
turn_subtype: "runtime_stalled",
|
|
8340
|
+
turn_reason: turnReason,
|
|
7966
8341
|
ageMs: staleForMs,
|
|
7967
8342
|
lastActivity: ap.lastActivity,
|
|
7968
8343
|
lastActivityDetailPresent: Boolean(ap.lastActivityDetail),
|
|
@@ -7984,16 +8359,23 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7984
8359
|
if (!canRestartStalledProcess) return false;
|
|
7985
8360
|
const staleForMs = Date.now() - ap.lastRuntimeEventAt;
|
|
7986
8361
|
if (staleForMs < RUNTIME_PROGRESS_STALE_MS && !ap.runtimeProgressStaleSince) return false;
|
|
8362
|
+
if (this.observeRuntimeTranscriptProgress(agentId, ap, staleForMs, "queued_recovery")) return false;
|
|
7987
8363
|
const staleForMinutes = Math.max(1, Math.floor(staleForMs / 6e4));
|
|
7988
8364
|
ap.runtimeProgressStaleSince ??= Date.now();
|
|
7989
8365
|
const diagnostic = buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes);
|
|
8366
|
+
const turnReason = classifyRuntimeStallReason(ap);
|
|
7990
8367
|
this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.stalled", {
|
|
8368
|
+
turn_outcome: "failed",
|
|
8369
|
+
turn_subtype: "runtime_stalled",
|
|
8370
|
+
turn_reason: turnReason,
|
|
7991
8371
|
...diagnostic.traceAttrs,
|
|
7992
8372
|
pendingMessages: ap.inbox.length,
|
|
7993
8373
|
recovery: "terminate_for_queued_message"
|
|
7994
8374
|
});
|
|
7995
8375
|
this.endRuntimeTrace(ap, "error", {
|
|
7996
|
-
|
|
8376
|
+
turn_outcome: "failed",
|
|
8377
|
+
turn_subtype: "runtime_stalled",
|
|
8378
|
+
turn_reason: turnReason,
|
|
7997
8379
|
ageMs: staleForMs,
|
|
7998
8380
|
lastActivity: ap.lastActivity,
|
|
7999
8381
|
lastActivityDetailPresent: Boolean(ap.lastActivityDetail),
|
|
@@ -8030,12 +8412,13 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8030
8412
|
const ap = this.agents.get(agentId);
|
|
8031
8413
|
if (ap) {
|
|
8032
8414
|
const wasStalled = Boolean(ap.runtimeProgressStaleSince);
|
|
8415
|
+
this.clearRuntimeStartupTimeout(ap);
|
|
8033
8416
|
this.noteRuntimeTraceCounter(ap, event);
|
|
8034
8417
|
this.recordRuntimeTraceEvent(agentId, ap, "runtime.event.received", { kind: event.kind });
|
|
8035
8418
|
if (wasStalled) {
|
|
8036
8419
|
this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.observed", { afterStall: true });
|
|
8037
8420
|
}
|
|
8038
|
-
this.noteRuntimeProgress(ap);
|
|
8421
|
+
this.noteRuntimeProgress(ap, event.kind);
|
|
8039
8422
|
}
|
|
8040
8423
|
switch (event.kind) {
|
|
8041
8424
|
case "session_init":
|
|
@@ -8192,8 +8575,12 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8192
8575
|
this.interruptCompactionIfActive(agentId);
|
|
8193
8576
|
this.flushPendingTrajectory(agentId);
|
|
8194
8577
|
if (ap) ap.lastRuntimeError = event.message;
|
|
8578
|
+
let visibleErrorMessage = event.message;
|
|
8195
8579
|
if (ap) {
|
|
8196
8580
|
const runtimeErrorDiagnostics = buildRuntimeErrorDiagnosticEnvelope(event.message);
|
|
8581
|
+
if (runtimeErrorDiagnostics.spanAttrs.runtime_error_action_required === true) {
|
|
8582
|
+
visibleErrorMessage = formatRuntimeLoginRequiredMessage(ap.driver.id);
|
|
8583
|
+
}
|
|
8197
8584
|
this.setGatedSteeringPhase(agentId, ap, "error", { event: "error" });
|
|
8198
8585
|
if (ap.driver.busyDeliveryMode === "gated" && this.isThinkingBlockMutationError(event.message)) {
|
|
8199
8586
|
this.requeueGatedInFlightBatch(agentId, ap, "thinking_block_mutation_error");
|
|
@@ -8217,18 +8604,33 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8217
8604
|
...runtimeTraceCounterAttrs(ap),
|
|
8218
8605
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_error")
|
|
8219
8606
|
});
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8607
|
+
const terminalFailure = classifyTerminalFailure(ap);
|
|
8608
|
+
if (ap.driver.supportsStdinNotification && terminalFailure) {
|
|
8609
|
+
if (terminalFailure.actionRequired) {
|
|
8610
|
+
logger.warn(`[Agent ${agentId}] ${ap.driver.id} auth requires user action; terminating runtime process`);
|
|
8611
|
+
try {
|
|
8612
|
+
this.processExitTraceAttrs.set(ap.process, {
|
|
8613
|
+
stop_source: "runtime_auth_error",
|
|
8614
|
+
runtime_error_class: "AuthError"
|
|
8615
|
+
});
|
|
8616
|
+
ap.process.kill("SIGTERM");
|
|
8617
|
+
} catch (err) {
|
|
8618
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
8619
|
+
logger.warn(`[Agent ${agentId}] Failed to terminate ${ap.driver.id} after auth error: ${reason}`);
|
|
8620
|
+
}
|
|
8621
|
+
} else {
|
|
8622
|
+
ap.isIdle = true;
|
|
8623
|
+
ap.pendingNotificationCount = 0;
|
|
8624
|
+
if (ap.notificationTimer) {
|
|
8625
|
+
clearTimeout(ap.notificationTimer);
|
|
8626
|
+
ap.notificationTimer = null;
|
|
8627
|
+
}
|
|
8628
|
+
logger.info(`[Agent ${agentId}] Marked ${ap.driver.id} wakeable after terminal runtime error`);
|
|
8226
8629
|
}
|
|
8227
|
-
logger.info(`[Agent ${agentId}] Marked ${ap.driver.id} wakeable after terminal runtime error`);
|
|
8228
8630
|
}
|
|
8229
8631
|
}
|
|
8230
|
-
this.broadcastActivity(agentId, "error",
|
|
8231
|
-
{ kind: "text", text: `Error: ${
|
|
8632
|
+
this.broadcastActivity(agentId, "error", visibleErrorMessage, [
|
|
8633
|
+
{ kind: "text", text: `Error: ${visibleErrorMessage}` }
|
|
8232
8634
|
]);
|
|
8233
8635
|
break;
|
|
8234
8636
|
}
|
|
@@ -8346,6 +8748,30 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8346
8748
|
/** Deliver a message to an agent via stdin, formatting it the same way as the MCP bridge */
|
|
8347
8749
|
deliverMessagesViaStdin(agentId, ap, messages, mode) {
|
|
8348
8750
|
if (messages.length === 0) return true;
|
|
8751
|
+
const runtimeProfileMigrationMessages = messages.filter((message) => runtimeProfileNotificationFromMessage(message)?.kind === "migration");
|
|
8752
|
+
if (runtimeProfileMigrationMessages.length > 0) {
|
|
8753
|
+
for (const message of runtimeProfileMigrationMessages) {
|
|
8754
|
+
const notification = runtimeProfileNotificationFromMessage(message);
|
|
8755
|
+
if (notification?.kind === "migration") {
|
|
8756
|
+
this.completeDeprecatedRuntimeProfileMigration(agentId, notification.key, ap.launchId, message.traceparent, "runtime_profile_message");
|
|
8757
|
+
}
|
|
8758
|
+
}
|
|
8759
|
+
messages = messages.filter((message) => runtimeProfileNotificationFromMessage(message)?.kind !== "migration");
|
|
8760
|
+
this.recordDaemonTrace("daemon.agent.runtime_profile.deprecated_migration_filtered", {
|
|
8761
|
+
agentId,
|
|
8762
|
+
launchId: ap.launchId || void 0,
|
|
8763
|
+
runtime: ap.config.runtime,
|
|
8764
|
+
mode,
|
|
8765
|
+
filtered_messages_count: runtimeProfileMigrationMessages.length,
|
|
8766
|
+
remaining_messages_count: messages.length
|
|
8767
|
+
});
|
|
8768
|
+
if (messages.length === 0) {
|
|
8769
|
+
if (mode === "idle") {
|
|
8770
|
+
ap.isIdle = true;
|
|
8771
|
+
}
|
|
8772
|
+
return true;
|
|
8773
|
+
}
|
|
8774
|
+
}
|
|
8349
8775
|
const split = this.splitRuntimeProfileControlBatch(messages);
|
|
8350
8776
|
if (split.deferredMessages.length > 0) {
|
|
8351
8777
|
ap.inbox.unshift(...split.deferredMessages);
|
|
@@ -8751,7 +9177,7 @@ var ReminderCache = class {
|
|
|
8751
9177
|
};
|
|
8752
9178
|
|
|
8753
9179
|
// src/machineLock.ts
|
|
8754
|
-
import { createHash as createHash3, randomUUID as
|
|
9180
|
+
import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
|
|
8755
9181
|
import { mkdirSync as mkdirSync5, readFileSync as readFileSync6, rmSync as rmSync3, statSync as statSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
8756
9182
|
import os7 from "os";
|
|
8757
9183
|
import path13 from "path";
|
|
@@ -8808,7 +9234,7 @@ function acquireDaemonMachineLock(options) {
|
|
|
8808
9234
|
const lockId = getDaemonMachineLockId(options.apiKey);
|
|
8809
9235
|
const machineDir = path13.join(rootDir, lockId);
|
|
8810
9236
|
const lockDir = path13.join(machineDir, "daemon.lock");
|
|
8811
|
-
const token =
|
|
9237
|
+
const token = randomUUID3();
|
|
8812
9238
|
mkdirSync5(machineDir, { recursive: true });
|
|
8813
9239
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
8814
9240
|
try {
|
|
@@ -9025,7 +9451,7 @@ function isDiagnosticErrorAttr(key) {
|
|
|
9025
9451
|
}
|
|
9026
9452
|
|
|
9027
9453
|
// src/traceBundleUpload.ts
|
|
9028
|
-
import { createHash as createHash5, randomUUID as
|
|
9454
|
+
import { createHash as createHash5, randomUUID as randomUUID4 } from "crypto";
|
|
9029
9455
|
import { gzipSync } from "zlib";
|
|
9030
9456
|
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
9031
9457
|
import path15 from "path";
|
|
@@ -9296,7 +9722,7 @@ var DaemonTraceBundleUploader = class {
|
|
|
9296
9722
|
}
|
|
9297
9723
|
const gzipped = gzipSync(raw);
|
|
9298
9724
|
const bundleSha256 = sha256Hex(gzipped);
|
|
9299
|
-
const bundleId =
|
|
9725
|
+
const bundleId = randomUUID4();
|
|
9300
9726
|
await uploadWithSignedCapability({
|
|
9301
9727
|
serverUrl: this.options.serverUrl,
|
|
9302
9728
|
apiKey: this.options.apiKey,
|
|
@@ -9924,7 +10350,7 @@ var DaemonCore = class {
|
|
|
9924
10350
|
});
|
|
9925
10351
|
logger.info(`[Agent ${msg.agentId}] Runtime profile migration received (${msg.migrationKey})`);
|
|
9926
10352
|
Promise.resolve(
|
|
9927
|
-
this.agentManager.deliverRuntimeProfileNotification(msg.agentId, msg.migrationKey, "migration", msg.message, formatTraceparent(span.context))
|
|
10353
|
+
this.agentManager.deliverRuntimeProfileNotification(msg.agentId, msg.migrationKey, "migration", msg.message, formatTraceparent(span.context), msg.launchId || null)
|
|
9928
10354
|
).then((accepted) => {
|
|
9929
10355
|
span.end("ok", { attrs: { outcome: accepted ? "accepted" : "no_injection_path" } });
|
|
9930
10356
|
}, (err) => {
|
|
@@ -9947,7 +10373,7 @@ var DaemonCore = class {
|
|
|
9947
10373
|
});
|
|
9948
10374
|
logger.info(`[Agent ${msg.agentId}] Runtime profile daemon release notice received (${msg.noticeKey})`);
|
|
9949
10375
|
Promise.resolve(
|
|
9950
|
-
this.agentManager.deliverRuntimeProfileNotification(msg.agentId, msg.noticeKey, "daemon_release_notice", msg.message, formatTraceparent(span.context))
|
|
10376
|
+
this.agentManager.deliverRuntimeProfileNotification(msg.agentId, msg.noticeKey, "daemon_release_notice", msg.message, formatTraceparent(span.context), msg.launchId || null)
|
|
9951
10377
|
).then((accepted) => {
|
|
9952
10378
|
span.end("ok", { attrs: { outcome: accepted ? "accepted" : "no_injection_path" } });
|
|
9953
10379
|
}, (err) => {
|