@slock-ai/daemon 0.50.0 → 0.51.0
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/{chunk-EXJF5JKE.js → chunk-NAMH7BB6.js} +972 -84
- package/dist/cli/index.js +192 -34
- package/dist/core.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -777,7 +777,7 @@ import os2 from "os";
|
|
|
777
777
|
import path4 from "path";
|
|
778
778
|
|
|
779
779
|
// src/drivers/cliTransport.ts
|
|
780
|
-
import { mkdirSync, writeFileSync } from "fs";
|
|
780
|
+
import { mkdirSync, rmSync, writeFileSync } from "fs";
|
|
781
781
|
import path2 from "path";
|
|
782
782
|
|
|
783
783
|
// src/drivers/systemPrompt.ts
|
|
@@ -870,7 +870,7 @@ Use the \`slock\` CLI for chat / task / attachment operations. The daemon inject
|
|
|
870
870
|
16. **\`slock attachment upload\`** \u2014 Upload a file to attach to a message. Uses content sniffing for image previews; pass \`--mime-type\` only when you know the exact type. Returns an attachment ID to pass to \`slock message send\`.
|
|
871
871
|
17. **\`slock attachment view\`** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
|
|
872
872
|
18. **\`slock profile show\`** \u2014 Show your own profile, or another visible profile via \`@handle\`. Mirrors the canonical Slock profile view.
|
|
873
|
-
19. **\`slock profile update\`** \u2014 Update your own profile. Supports \`--avatar-file <path>\`, \`--display-name <name>\`, and \`--description <text>\`. Values must be non-empty. Provide at least one flag per call; multiple flags can be combined.
|
|
873
|
+
19. **\`slock profile update\`** \u2014 Update your own profile. Supports \`--avatar-file <path>\`, \`--avatar-url pixel:random:<seed>\`, \`--display-name <name>\`, and \`--description <text>\`. Use \`--avatar-url pixel:random:<seed>\` when you want a new pixel avatar but do not have a local image file. Values must be non-empty. Provide at least one flag per call; multiple flags can be combined.
|
|
874
874
|
20. **\`slock reminder schedule\`** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
|
|
875
875
|
21. **\`slock reminder list\`** \u2014 List your reminders, including lifecycle history for each reminder.
|
|
876
876
|
22. **\`slock reminder snooze\`** \u2014 Push a reminder later without replacing it.
|
|
@@ -1349,8 +1349,304 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
|
|
|
1349
1349
|
return candidates.filter((candidate) => existsSync(candidate.path));
|
|
1350
1350
|
}
|
|
1351
1351
|
|
|
1352
|
+
// src/agentCredentialProxy.ts
|
|
1353
|
+
import { randomBytes } from "crypto";
|
|
1354
|
+
import http from "http";
|
|
1355
|
+
import { URL as URL2 } from "url";
|
|
1356
|
+
var registrations = /* @__PURE__ */ new Map();
|
|
1357
|
+
var servers = /* @__PURE__ */ new Map();
|
|
1358
|
+
function allocatePort() {
|
|
1359
|
+
return 43e3 + randomBytes(2).readUInt16BE(0) % 1e4;
|
|
1360
|
+
}
|
|
1361
|
+
function ensureServer(port) {
|
|
1362
|
+
if (servers.has(port)) return;
|
|
1363
|
+
const server = http.createServer((req, res) => {
|
|
1364
|
+
void handleProxyRequest(req, res);
|
|
1365
|
+
});
|
|
1366
|
+
server.on("error", (err) => {
|
|
1367
|
+
logger.warn(`[Agent Credential Proxy] local proxy on port ${port} failed: ${err.message}`);
|
|
1368
|
+
});
|
|
1369
|
+
server.listen(port, "127.0.0.1");
|
|
1370
|
+
server.unref();
|
|
1371
|
+
servers.set(port, server);
|
|
1372
|
+
}
|
|
1373
|
+
async function handleProxyRequest(req, res) {
|
|
1374
|
+
const auth = req.headers.authorization;
|
|
1375
|
+
const token = typeof auth === "string" && auth.startsWith("Bearer ") ? auth.slice("Bearer ".length) : "";
|
|
1376
|
+
const registration = registrations.get(token);
|
|
1377
|
+
if (!registration) {
|
|
1378
|
+
res.writeHead(401, { "content-type": "application/json" });
|
|
1379
|
+
res.end(JSON.stringify({ error: "invalid local agent proxy token", code: "invalid_agent_proxy_token" }));
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1382
|
+
try {
|
|
1383
|
+
const target = new URL2(req.url ?? "/", registration.serverUrl);
|
|
1384
|
+
const headers = new Headers();
|
|
1385
|
+
for (const [name, value] of Object.entries(req.headers)) {
|
|
1386
|
+
if (value === void 0) continue;
|
|
1387
|
+
if (name.toLowerCase() === "host") continue;
|
|
1388
|
+
if (name.toLowerCase() === "authorization") continue;
|
|
1389
|
+
if (Array.isArray(value)) {
|
|
1390
|
+
for (const item of value) headers.append(name, item);
|
|
1391
|
+
} else {
|
|
1392
|
+
headers.set(name, value);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
headers.set("Authorization", `Bearer ${registration.apiKey}`);
|
|
1396
|
+
headers.set("X-Agent-Id", registration.agentId);
|
|
1397
|
+
headers.set("X-Slock-Client", "cli");
|
|
1398
|
+
headers.set("X-Slock-Agent-Active-Capabilities", registration.activeCapabilities);
|
|
1399
|
+
const method = req.method ?? "GET";
|
|
1400
|
+
let body = method === "GET" || method === "HEAD" ? void 0 : req;
|
|
1401
|
+
let sendTarget;
|
|
1402
|
+
if (method === "POST" && target.pathname === "/internal/agent-api/send") {
|
|
1403
|
+
const rawBody = await readRequestBody(req);
|
|
1404
|
+
const prepared = await prepareAgentApiSendForward(registration, headers, rawBody);
|
|
1405
|
+
if (prepared.localResponse) {
|
|
1406
|
+
const responseText = JSON.stringify(prepared.localResponse);
|
|
1407
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
1408
|
+
res.end(responseText);
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
body = prepared.bodyText;
|
|
1412
|
+
sendTarget = prepared.target;
|
|
1413
|
+
headers.set("content-type", "application/json");
|
|
1414
|
+
headers.set("content-length", String(Buffer.byteLength(prepared.bodyText)));
|
|
1415
|
+
}
|
|
1416
|
+
const upstream = await fetch(target, {
|
|
1417
|
+
method,
|
|
1418
|
+
headers,
|
|
1419
|
+
body,
|
|
1420
|
+
// Required by undici when forwarding a Node stream.
|
|
1421
|
+
duplex: body ? "half" : void 0
|
|
1422
|
+
});
|
|
1423
|
+
if (shouldBufferJsonResponse(upstream, target.pathname, registration)) {
|
|
1424
|
+
const responseText = await upstream.text();
|
|
1425
|
+
consumeVisibleResponse(registration, target, sendTarget, responseText);
|
|
1426
|
+
res.writeHead(upstream.status, Object.fromEntries(upstream.headers.entries()));
|
|
1427
|
+
res.end(responseText);
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
res.writeHead(upstream.status, Object.fromEntries(upstream.headers.entries()));
|
|
1431
|
+
if (upstream.body) {
|
|
1432
|
+
const reader = upstream.body.getReader();
|
|
1433
|
+
try {
|
|
1434
|
+
while (true) {
|
|
1435
|
+
const { done, value } = await reader.read();
|
|
1436
|
+
if (done) break;
|
|
1437
|
+
res.write(Buffer.from(value));
|
|
1438
|
+
}
|
|
1439
|
+
} finally {
|
|
1440
|
+
res.end();
|
|
1441
|
+
}
|
|
1442
|
+
} else {
|
|
1443
|
+
res.end();
|
|
1444
|
+
}
|
|
1445
|
+
} catch (err) {
|
|
1446
|
+
res.writeHead(502, { "content-type": "application/json" });
|
|
1447
|
+
res.end(JSON.stringify({
|
|
1448
|
+
error: "failed to proxy local agent request",
|
|
1449
|
+
code: "agent_proxy_failed",
|
|
1450
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
1451
|
+
}));
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
async function readRequestBody(req) {
|
|
1455
|
+
let body = "";
|
|
1456
|
+
req.setEncoding("utf8");
|
|
1457
|
+
for await (const chunk of req) {
|
|
1458
|
+
body += String(chunk);
|
|
1459
|
+
}
|
|
1460
|
+
return body;
|
|
1461
|
+
}
|
|
1462
|
+
function messageSeq(message) {
|
|
1463
|
+
return Number(message.seq ?? 0);
|
|
1464
|
+
}
|
|
1465
|
+
function boundaryBefore(messages) {
|
|
1466
|
+
let minSeq = Number.POSITIVE_INFINITY;
|
|
1467
|
+
for (const message of messages) {
|
|
1468
|
+
const seq = Math.floor(messageSeq(message));
|
|
1469
|
+
if (Number.isFinite(seq) && seq > 0) minSeq = Math.min(minSeq, seq);
|
|
1470
|
+
}
|
|
1471
|
+
return Number.isFinite(minSeq) ? Math.max(0, minSeq - 1) : void 0;
|
|
1472
|
+
}
|
|
1473
|
+
function maxMessageSeq(messages) {
|
|
1474
|
+
let maxSeq = 0;
|
|
1475
|
+
for (const message of messages) {
|
|
1476
|
+
const seq = Math.floor(messageSeq(message));
|
|
1477
|
+
if (Number.isFinite(seq) && seq > 0) maxSeq = Math.max(maxSeq, seq);
|
|
1478
|
+
}
|
|
1479
|
+
return maxSeq > 0 ? maxSeq : void 0;
|
|
1480
|
+
}
|
|
1481
|
+
function parseTargetFields(target) {
|
|
1482
|
+
if (target.startsWith("dm:@")) {
|
|
1483
|
+
const rest = target.slice("dm:@".length);
|
|
1484
|
+
const [peer, threadId] = rest.split(":", 2);
|
|
1485
|
+
if (threadId) {
|
|
1486
|
+
return {
|
|
1487
|
+
channel_type: "thread",
|
|
1488
|
+
channel_name: threadId,
|
|
1489
|
+
parent_channel_type: "dm",
|
|
1490
|
+
parent_channel_name: peer
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
return { channel_type: "dm", channel_name: peer };
|
|
1494
|
+
}
|
|
1495
|
+
if (target.startsWith("#")) {
|
|
1496
|
+
const rest = target.slice(1);
|
|
1497
|
+
const [channel, threadId] = rest.split(":", 2);
|
|
1498
|
+
if (threadId) {
|
|
1499
|
+
return {
|
|
1500
|
+
channel_type: "thread",
|
|
1501
|
+
channel_name: threadId,
|
|
1502
|
+
parent_channel_type: "channel",
|
|
1503
|
+
parent_channel_name: channel
|
|
1504
|
+
};
|
|
1505
|
+
}
|
|
1506
|
+
return { channel_type: "channel", channel_name: channel };
|
|
1507
|
+
}
|
|
1508
|
+
return {};
|
|
1509
|
+
}
|
|
1510
|
+
function normalizeVisibleMessage(message, target) {
|
|
1511
|
+
const targetFields = target ? parseTargetFields(target) : {};
|
|
1512
|
+
return {
|
|
1513
|
+
...targetFields,
|
|
1514
|
+
...message,
|
|
1515
|
+
message_id: message.message_id ?? message.id,
|
|
1516
|
+
timestamp: message.timestamp ?? message.createdAt,
|
|
1517
|
+
sender_type: message.sender_type ?? message.senderType,
|
|
1518
|
+
sender_name: message.sender_name ?? message.senderName,
|
|
1519
|
+
sender_description: message.sender_description ?? message.senderDescription ?? null
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
function normalizeVisibleMessages(messages, target) {
|
|
1523
|
+
return messages.map((message) => normalizeVisibleMessage(message, target));
|
|
1524
|
+
}
|
|
1525
|
+
async function loadRecentTargetMessages(registration, headers, target) {
|
|
1526
|
+
const historyUrl = new URL2("/internal/agent-api/history", registration.serverUrl);
|
|
1527
|
+
historyUrl.searchParams.set("channel", target);
|
|
1528
|
+
historyUrl.searchParams.set("limit", "3");
|
|
1529
|
+
const historyHeaders = new Headers(headers);
|
|
1530
|
+
historyHeaders.delete("content-length");
|
|
1531
|
+
historyHeaders.delete("content-type");
|
|
1532
|
+
const res = await fetch(historyUrl, { method: "GET", headers: historyHeaders });
|
|
1533
|
+
if (!res.ok) return [];
|
|
1534
|
+
const parsed = await res.json().catch(() => null);
|
|
1535
|
+
return Array.isArray(parsed?.messages) ? normalizeVisibleMessages(parsed.messages, target) : [];
|
|
1536
|
+
}
|
|
1537
|
+
async function prepareAgentApiSendForward(registration, headers, rawBody) {
|
|
1538
|
+
let body;
|
|
1539
|
+
try {
|
|
1540
|
+
body = rawBody ? JSON.parse(rawBody) : {};
|
|
1541
|
+
} catch {
|
|
1542
|
+
return { bodyText: rawBody };
|
|
1543
|
+
}
|
|
1544
|
+
const target = typeof body.target === "string" ? body.target : void 0;
|
|
1545
|
+
const coordinator = registration.inboxCoordinator;
|
|
1546
|
+
if (!target || !coordinator || body.continueAnyway === true) {
|
|
1547
|
+
return { bodyText: JSON.stringify(body), target };
|
|
1548
|
+
}
|
|
1549
|
+
const pending = coordinator.getPendingMessages(target);
|
|
1550
|
+
if (pending.length > 0) {
|
|
1551
|
+
const staleBoundary = boundaryBefore(pending);
|
|
1552
|
+
if (staleBoundary !== void 0) {
|
|
1553
|
+
body.seenUpToSeq = staleBoundary;
|
|
1554
|
+
}
|
|
1555
|
+
return { bodyText: JSON.stringify(body), target };
|
|
1556
|
+
}
|
|
1557
|
+
const existingBoundary = typeof body.seenUpToSeq === "number" && Number.isFinite(body.seenUpToSeq) ? Math.max(0, Math.floor(body.seenUpToSeq)) : void 0;
|
|
1558
|
+
const loadedBoundary = coordinator.getBoundary(target);
|
|
1559
|
+
const boundary = Math.max(existingBoundary ?? 0, loadedBoundary ?? 0);
|
|
1560
|
+
if (boundary > 0) {
|
|
1561
|
+
body.seenUpToSeq = boundary;
|
|
1562
|
+
return { bodyText: JSON.stringify(body), target };
|
|
1563
|
+
}
|
|
1564
|
+
const recent = await loadRecentTargetMessages(registration, headers, target);
|
|
1565
|
+
if (recent.length > 0) {
|
|
1566
|
+
const seenUpToSeq = maxMessageSeq(recent);
|
|
1567
|
+
coordinator.consumeVisibleMessages({ target, messages: recent, boundarySeq: seenUpToSeq, source: "send_preflight_context" });
|
|
1568
|
+
return {
|
|
1569
|
+
bodyText: JSON.stringify(body),
|
|
1570
|
+
target,
|
|
1571
|
+
localResponse: {
|
|
1572
|
+
state: "held",
|
|
1573
|
+
seenUpToSeq,
|
|
1574
|
+
heldMessages: recent,
|
|
1575
|
+
newMessageCount: recent.length,
|
|
1576
|
+
shownMessageCount: recent.length,
|
|
1577
|
+
omittedMessageCount: 0
|
|
1578
|
+
}
|
|
1579
|
+
};
|
|
1580
|
+
}
|
|
1581
|
+
return { bodyText: JSON.stringify(body), target };
|
|
1582
|
+
}
|
|
1583
|
+
function shouldBufferJsonResponse(upstream, pathname, registration) {
|
|
1584
|
+
if (!registration.inboxCoordinator) return false;
|
|
1585
|
+
const contentType = upstream.headers.get("content-type") ?? "";
|
|
1586
|
+
if (!contentType.includes("application/json")) return false;
|
|
1587
|
+
return pathname === "/internal/agent-api/send" || pathname === "/internal/agent-api/events" || pathname === "/internal/agent-api/history";
|
|
1588
|
+
}
|
|
1589
|
+
function consumeVisibleResponse(registration, targetUrl, sendTarget, responseText) {
|
|
1590
|
+
const coordinator = registration.inboxCoordinator;
|
|
1591
|
+
if (!coordinator) return;
|
|
1592
|
+
let parsed;
|
|
1593
|
+
try {
|
|
1594
|
+
parsed = JSON.parse(responseText);
|
|
1595
|
+
} catch {
|
|
1596
|
+
return;
|
|
1597
|
+
}
|
|
1598
|
+
if (targetUrl.pathname === "/internal/agent-api/send" && parsed.state === "held" && Array.isArray(parsed.heldMessages)) {
|
|
1599
|
+
coordinator.consumeVisibleMessages({
|
|
1600
|
+
target: sendTarget,
|
|
1601
|
+
messages: normalizeVisibleMessages(parsed.heldMessages, sendTarget),
|
|
1602
|
+
boundarySeq: typeof parsed.seenUpToSeq === "number" ? parsed.seenUpToSeq : void 0,
|
|
1603
|
+
source: "server_held_context"
|
|
1604
|
+
});
|
|
1605
|
+
return;
|
|
1606
|
+
}
|
|
1607
|
+
if (targetUrl.pathname === "/internal/agent-api/events" && Array.isArray(parsed.events)) {
|
|
1608
|
+
coordinator.consumeVisibleMessages({ messages: normalizeVisibleMessages(parsed.events), source: "agent_api_events" });
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
if (targetUrl.pathname === "/internal/agent-api/history" && Array.isArray(parsed.messages)) {
|
|
1612
|
+
const target = targetUrl.searchParams.get("channel") ?? void 0;
|
|
1613
|
+
const messages = normalizeVisibleMessages(parsed.messages, target);
|
|
1614
|
+
coordinator.consumeVisibleMessages({ target, messages, boundarySeq: maxMessageSeq(messages), source: "agent_api_history" });
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
function registerAgentCredentialProxy(input) {
|
|
1618
|
+
const port = allocatePort();
|
|
1619
|
+
ensureServer(port);
|
|
1620
|
+
const proxyToken = `sap_${randomBytes(32).toString("base64url")}`;
|
|
1621
|
+
registrations.set(proxyToken, {
|
|
1622
|
+
serverUrl: input.serverUrl,
|
|
1623
|
+
apiKey: input.apiKey,
|
|
1624
|
+
agentId: input.agentId,
|
|
1625
|
+
launchId: input.launchId ?? null,
|
|
1626
|
+
activeCapabilities: input.activeCapabilities,
|
|
1627
|
+
inboxCoordinator: input.inboxCoordinator
|
|
1628
|
+
});
|
|
1629
|
+
return {
|
|
1630
|
+
proxyUrl: `http://127.0.0.1:${port}`,
|
|
1631
|
+
proxyToken
|
|
1632
|
+
};
|
|
1633
|
+
}
|
|
1634
|
+
function unregisterAgentCredentialProxyForLaunch(input) {
|
|
1635
|
+
let removed = 0;
|
|
1636
|
+
const launchId = input.launchId ?? null;
|
|
1637
|
+
for (const [token, registration] of registrations) {
|
|
1638
|
+
if (registration.agentId === input.agentId && registration.launchId === launchId) {
|
|
1639
|
+
registrations.delete(token);
|
|
1640
|
+
removed += 1;
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
return removed;
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1352
1646
|
// src/drivers/cliTransport.ts
|
|
1353
1647
|
var shellSingleQuote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1648
|
+
var DEFAULT_ACTIVE_CAPABILITIES = "send,read,mentions,tasks,reactions,server,channels";
|
|
1649
|
+
var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
1354
1650
|
function runtimeContextEnv(config) {
|
|
1355
1651
|
const ctx = config.runtimeContext;
|
|
1356
1652
|
if (!ctx) return {};
|
|
@@ -1374,17 +1670,43 @@ function prepareCliTransport(ctx, extraEnv = {}, platform = process.platform) {
|
|
|
1374
1670
|
}
|
|
1375
1671
|
const slockDir = path2.join(ctx.workingDirectory, ".slock");
|
|
1376
1672
|
mkdirSync(slockDir, { recursive: true });
|
|
1673
|
+
const slockHome = resolveSlockHome();
|
|
1377
1674
|
const tokenFile = path2.join(slockDir, "agent-token");
|
|
1378
|
-
|
|
1675
|
+
const agentCredentialKey = ctx.config.agentCredentialKey;
|
|
1676
|
+
let agentCredentialProxy = null;
|
|
1677
|
+
let agentCredentialProxyTokenFile = null;
|
|
1678
|
+
if (typeof agentCredentialKey === "string" && agentCredentialKey.length > 0) {
|
|
1679
|
+
rmSync(tokenFile, { force: true });
|
|
1680
|
+
agentCredentialProxy = registerAgentCredentialProxy({
|
|
1681
|
+
agentId: ctx.agentId,
|
|
1682
|
+
launchId: ctx.launchId,
|
|
1683
|
+
serverUrl: ctx.config.serverUrl,
|
|
1684
|
+
apiKey: agentCredentialKey,
|
|
1685
|
+
activeCapabilities: DEFAULT_ACTIVE_CAPABILITIES,
|
|
1686
|
+
inboxCoordinator: ctx.agentCredentialProxyInboxCoordinator
|
|
1687
|
+
});
|
|
1688
|
+
const launchPart = safePathPart(ctx.launchId || `pid-${process.pid}`);
|
|
1689
|
+
const proxyTokenDir = path2.join(slockHome, "agent-proxy-tokens", safePathPart(ctx.agentId));
|
|
1690
|
+
mkdirSync(proxyTokenDir, { recursive: true, mode: 448 });
|
|
1691
|
+
agentCredentialProxyTokenFile = path2.join(proxyTokenDir, `${launchPart}.token`);
|
|
1692
|
+
writeFileSync(agentCredentialProxyTokenFile, agentCredentialProxy.proxyToken, { mode: 384 });
|
|
1693
|
+
} else {
|
|
1694
|
+
writeFileSync(tokenFile, ctx.config.authToken || ctx.daemonApiKey, { mode: 384 });
|
|
1695
|
+
}
|
|
1379
1696
|
const posixWrapper = path2.join(slockDir, "slock");
|
|
1697
|
+
const posixCredentialPrefix = agentCredentialProxy ? `SLOCK_AGENT_PROXY_URL=${shellSingleQuote(agentCredentialProxy.proxyUrl)} SLOCK_AGENT_PROXY_TOKEN_FILE=${shellSingleQuote(agentCredentialProxyTokenFile)} SLOCK_AGENT_ACTIVE_CAPABILITIES=${shellSingleQuote(DEFAULT_ACTIVE_CAPABILITIES)} ` : "";
|
|
1380
1698
|
const posixBody = `#!/usr/bin/env bash
|
|
1381
|
-
exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(ctx.slockCliPath)} "$@"
|
|
1699
|
+
${posixCredentialPrefix}exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(ctx.slockCliPath)} "$@"
|
|
1382
1700
|
`;
|
|
1383
1701
|
writeFileSync(posixWrapper, posixBody, { mode: 493 });
|
|
1384
1702
|
if (platform === "win32") {
|
|
1385
1703
|
const cmdWrapper = path2.join(slockDir, "slock.cmd");
|
|
1704
|
+
const cmdCredentialLine = agentCredentialProxy ? `set "SLOCK_AGENT_PROXY_URL=${agentCredentialProxy.proxyUrl}"\r
|
|
1705
|
+
set "SLOCK_AGENT_PROXY_TOKEN_FILE=${agentCredentialProxyTokenFile}"\r
|
|
1706
|
+
set "SLOCK_AGENT_ACTIVE_CAPABILITIES=${DEFAULT_ACTIVE_CAPABILITIES}"\r
|
|
1707
|
+
` : "";
|
|
1386
1708
|
const cmdBody = `@echo off\r
|
|
1387
|
-
"${process.execPath}" "${ctx.slockCliPath}" %*\r
|
|
1709
|
+
${cmdCredentialLine}"${process.execPath}" "${ctx.slockCliPath}" %*\r
|
|
1388
1710
|
`;
|
|
1389
1711
|
writeFileSync(cmdWrapper, cmdBody);
|
|
1390
1712
|
}
|
|
@@ -1395,17 +1717,27 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(ctx.slockCliPath)}
|
|
|
1395
1717
|
...ctx.config.envVars || {},
|
|
1396
1718
|
...extraEnv,
|
|
1397
1719
|
...runtimeContextEnv(ctx.config),
|
|
1398
|
-
[SLOCK_HOME_ENV]:
|
|
1720
|
+
[SLOCK_HOME_ENV]: slockHome,
|
|
1399
1721
|
SLOCK_AGENT_ID: ctx.agentId,
|
|
1400
1722
|
...ctx.launchId ? { SLOCK_AGENT_LAUNCH_ID: ctx.launchId } : {},
|
|
1401
1723
|
SLOCK_SERVER_URL: ctx.config.serverUrl,
|
|
1402
|
-
SLOCK_AGENT_TOKEN_FILE: tokenFile,
|
|
1724
|
+
...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
|
|
1403
1725
|
PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
|
|
1404
1726
|
};
|
|
1405
1727
|
delete spawnEnv.SLOCK_AGENT_TOKEN;
|
|
1728
|
+
delete spawnEnv.SLOCK_AGENT_CREDENTIAL_KEY;
|
|
1729
|
+
delete spawnEnv.SLOCK_AGENT_CREDENTIAL_KEY_FILE;
|
|
1730
|
+
delete spawnEnv.SLOCK_AGENT_PROXY_URL;
|
|
1731
|
+
delete spawnEnv.SLOCK_AGENT_PROXY_TOKEN;
|
|
1732
|
+
delete spawnEnv.SLOCK_AGENT_PROXY_TOKEN_FILE;
|
|
1733
|
+
delete spawnEnv.SLOCK_AGENT_ACTIVE_CAPABILITIES;
|
|
1734
|
+
if (agentCredentialProxy) {
|
|
1735
|
+
delete spawnEnv.SLOCK_AGENT_TOKEN_FILE;
|
|
1736
|
+
}
|
|
1406
1737
|
return {
|
|
1407
1738
|
slockDir,
|
|
1408
1739
|
tokenFile,
|
|
1740
|
+
agentCredentialProxyUrl: agentCredentialProxy?.proxyUrl ?? null,
|
|
1409
1741
|
wrapperPath,
|
|
1410
1742
|
spawnEnv
|
|
1411
1743
|
};
|
|
@@ -1573,7 +1905,7 @@ function collectClaudeMcpConfigFiles(ctx, home) {
|
|
|
1573
1905
|
return files;
|
|
1574
1906
|
}
|
|
1575
1907
|
function buildClaudeUserMcpServers(ctx, home) {
|
|
1576
|
-
const
|
|
1908
|
+
const servers2 = /* @__PURE__ */ Object.create(null);
|
|
1577
1909
|
for (const configFile of collectClaudeMcpConfigFiles(ctx, home)) {
|
|
1578
1910
|
const mcpServers = readClaudeMcpServers(configFile.path, configFile.vars);
|
|
1579
1911
|
if (!mcpServers) continue;
|
|
@@ -1582,10 +1914,10 @@ function buildClaudeUserMcpServers(ctx, home) {
|
|
|
1582
1914
|
logger.warn(`[Claude] ignoring invalid MCP server "${name}" in ${configFile.path}`);
|
|
1583
1915
|
continue;
|
|
1584
1916
|
}
|
|
1585
|
-
|
|
1917
|
+
servers2[name] = server;
|
|
1586
1918
|
}
|
|
1587
1919
|
}
|
|
1588
|
-
return
|
|
1920
|
+
return servers2;
|
|
1589
1921
|
}
|
|
1590
1922
|
var ClaudeDriver = class {
|
|
1591
1923
|
id = "claude";
|
|
@@ -1633,9 +1965,9 @@ var ClaudeDriver = class {
|
|
|
1633
1965
|
CLAUDE_DISALLOWED_TOOLS
|
|
1634
1966
|
];
|
|
1635
1967
|
if (opts.standingPromptFilePath) {
|
|
1636
|
-
args.push("--system-prompt-file", opts.standingPromptFilePath);
|
|
1968
|
+
args.push("--append-system-prompt-file", opts.standingPromptFilePath);
|
|
1637
1969
|
} else {
|
|
1638
|
-
args.push("--system-prompt", standingPrompt);
|
|
1970
|
+
args.push("--append-system-prompt", standingPrompt);
|
|
1639
1971
|
}
|
|
1640
1972
|
if (config.sessionId) {
|
|
1641
1973
|
args.push("--resume", config.sessionId);
|
|
@@ -1883,13 +2215,29 @@ function resolveWindowsNpmCodexEntry(deps = {}) {
|
|
|
1883
2215
|
}
|
|
1884
2216
|
return null;
|
|
1885
2217
|
}
|
|
2218
|
+
function resolveWindowsCodexDesktopEntry(deps = {}) {
|
|
2219
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync4;
|
|
2220
|
+
const env = deps.env ?? process.env;
|
|
2221
|
+
const homeDir = deps.homeDir;
|
|
2222
|
+
const winPath = path5.win32;
|
|
2223
|
+
const candidates = [
|
|
2224
|
+
env.LOCALAPPDATA ? winPath.join(env.LOCALAPPDATA, "OpenAI", "Codex", "bin", "codex.exe") : null,
|
|
2225
|
+
env.USERPROFILE ? winPath.join(env.USERPROFILE, "AppData", "Local", "OpenAI", "Codex", "bin", "codex.exe") : null,
|
|
2226
|
+
homeDir ? winPath.join(homeDir, "AppData", "Local", "OpenAI", "Codex", "bin", "codex.exe") : null
|
|
2227
|
+
].filter((candidate) => Boolean(candidate));
|
|
2228
|
+
for (const candidate of candidates) {
|
|
2229
|
+
if (existsSyncFn(candidate)) return candidate;
|
|
2230
|
+
}
|
|
2231
|
+
return null;
|
|
2232
|
+
}
|
|
1886
2233
|
function resolveCodexCommand(deps = {}) {
|
|
1887
2234
|
const platform = deps.platform ?? process.platform;
|
|
1888
2235
|
if (platform === "win32") {
|
|
1889
2236
|
const npmEntry = resolveWindowsNpmCodexEntry(deps);
|
|
1890
2237
|
if (npmEntry) return npmEntry;
|
|
1891
2238
|
const command = resolveCommandOnPath("codex", deps);
|
|
1892
|
-
|
|
2239
|
+
if (command && !isWindowsSandboxRunner(command)) return command;
|
|
2240
|
+
return resolveWindowsCodexDesktopEntry(deps);
|
|
1893
2241
|
}
|
|
1894
2242
|
const pathCommand = resolveCommandOnPath("codex", deps);
|
|
1895
2243
|
if (pathCommand) return pathCommand;
|
|
@@ -1932,6 +2280,10 @@ function resolveCodexSpawn(commandArgs, deps = {}) {
|
|
|
1932
2280
|
if (command && !isWindowsSandboxRunner(command)) {
|
|
1933
2281
|
return { command, args: commandArgs };
|
|
1934
2282
|
}
|
|
2283
|
+
const desktopEntry = resolveWindowsCodexDesktopEntry(deps);
|
|
2284
|
+
if (desktopEntry) {
|
|
2285
|
+
return { command: desktopEntry, args: commandArgs };
|
|
2286
|
+
}
|
|
1935
2287
|
throw new Error(
|
|
1936
2288
|
"Cannot resolve Codex CLI entry point on Windows. Install Codex Desktop or install @openai/codex globally via npm (npm i -g @openai/codex). Ignoring .codex/.sandbox-bin/codex-command-runner because it is a sandbox helper, not the Codex CLI."
|
|
1937
2289
|
);
|
|
@@ -3297,6 +3649,18 @@ function isSupportedOpenCodeVersion(version) {
|
|
|
3297
3649
|
}
|
|
3298
3650
|
return true;
|
|
3299
3651
|
}
|
|
3652
|
+
var AGENT_FLAG_CUTOFF_VERSION = "1.15.0";
|
|
3653
|
+
function requiresAgentCliFlag(version) {
|
|
3654
|
+
if (!version) return true;
|
|
3655
|
+
const actual = parseSemver(version);
|
|
3656
|
+
const cutoff = parseSemver(AGENT_FLAG_CUTOFF_VERSION);
|
|
3657
|
+
if (!actual || !cutoff) return true;
|
|
3658
|
+
for (let i = 0; i < 3; i += 1) {
|
|
3659
|
+
if (actual[i] > cutoff[i]) return false;
|
|
3660
|
+
if (actual[i] < cutoff[i]) return true;
|
|
3661
|
+
}
|
|
3662
|
+
return false;
|
|
3663
|
+
}
|
|
3300
3664
|
function unsupportedOpenCodeVersionMessage(version) {
|
|
3301
3665
|
if (!version || isSupportedOpenCodeVersion(version)) return null;
|
|
3302
3666
|
return `OpenCode CLI ${version} is unsupported; requires OpenCode >= ${MIN_SUPPORTED_OPENCODE_VERSION}. Upgrade opencode before starting this runtime.`;
|
|
@@ -3344,7 +3708,7 @@ function buildOpenCodeConfig(ctx, home = os5.homedir()) {
|
|
|
3344
3708
|
}
|
|
3345
3709
|
};
|
|
3346
3710
|
}
|
|
3347
|
-
function buildOpenCodeLaunchOptions(ctx, home = os5.homedir()) {
|
|
3711
|
+
function buildOpenCodeLaunchOptions(ctx, home = os5.homedir(), version = null) {
|
|
3348
3712
|
const slock = prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
3349
3713
|
const config = buildOpenCodeConfig(ctx, home);
|
|
3350
3714
|
const env = {
|
|
@@ -3363,7 +3727,9 @@ function buildOpenCodeLaunchOptions(ctx, home = os5.homedir()) {
|
|
|
3363
3727
|
if (ctx.config.model && ctx.config.model !== "default") {
|
|
3364
3728
|
args.push("--model", ctx.config.model);
|
|
3365
3729
|
}
|
|
3366
|
-
|
|
3730
|
+
if (requiresAgentCliFlag(version)) {
|
|
3731
|
+
args.push("--agent", SLOCK_AGENT_NAME);
|
|
3732
|
+
}
|
|
3367
3733
|
if (ctx.config.sessionId) {
|
|
3368
3734
|
args.push("--session", ctx.config.sessionId);
|
|
3369
3735
|
}
|
|
@@ -3499,7 +3865,8 @@ var OpenCodeDriver = class {
|
|
|
3499
3865
|
model: modelId
|
|
3500
3866
|
}
|
|
3501
3867
|
};
|
|
3502
|
-
const
|
|
3868
|
+
const version = readCommandVersion("opencode");
|
|
3869
|
+
const launch = buildOpenCodeLaunchOptions(launchCtx, opts?.home, version);
|
|
3503
3870
|
return {
|
|
3504
3871
|
args: launch.args,
|
|
3505
3872
|
env: launch.env,
|
|
@@ -3536,11 +3903,12 @@ var OpenCodeDriver = class {
|
|
|
3536
3903
|
spawn(ctx) {
|
|
3537
3904
|
this.sessionId = ctx.config.sessionId || null;
|
|
3538
3905
|
this.sessionAnnounced = false;
|
|
3539
|
-
const
|
|
3906
|
+
const version = readCommandVersion("opencode");
|
|
3907
|
+
const unsupportedMessage = unsupportedOpenCodeVersionMessage(version);
|
|
3540
3908
|
if (unsupportedMessage) {
|
|
3541
3909
|
throw new Error(unsupportedMessage);
|
|
3542
3910
|
}
|
|
3543
|
-
const launch = buildOpenCodeLaunchOptions(ctx);
|
|
3911
|
+
const launch = buildOpenCodeLaunchOptions(ctx, os5.homedir(), version);
|
|
3544
3912
|
const proc = spawn7("opencode", launch.args, {
|
|
3545
3913
|
cwd: ctx.workingDirectory,
|
|
3546
3914
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -3813,6 +4181,8 @@ function redactUrlQuery(value) {
|
|
|
3813
4181
|
// src/agentProcessManager.ts
|
|
3814
4182
|
var DEFAULT_MAX_CONCURRENT_AGENT_STARTS = 5;
|
|
3815
4183
|
var DEFAULT_AGENT_START_INTERVAL_MS = 500;
|
|
4184
|
+
var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS = 3;
|
|
4185
|
+
var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS = 250;
|
|
3816
4186
|
var WORKSPACE_TEXT_FILE_MAX_BYTES = 1048576;
|
|
3817
4187
|
var WORKSPACE_IMAGE_PREVIEW_MAX_BYTES = 5 * 1024 * 1024;
|
|
3818
4188
|
var WORKSPACE_TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
@@ -3857,6 +4227,41 @@ function readNonNegativeIntegerEnv(name, fallback) {
|
|
|
3857
4227
|
if (!Number.isFinite(parsed) || parsed < 0) return fallback;
|
|
3858
4228
|
return Math.floor(parsed);
|
|
3859
4229
|
}
|
|
4230
|
+
var RunnerCredentialMintError = class extends Error {
|
|
4231
|
+
code;
|
|
4232
|
+
retryable;
|
|
4233
|
+
status;
|
|
4234
|
+
constructor(message, opts) {
|
|
4235
|
+
super(message);
|
|
4236
|
+
this.name = "RunnerCredentialMintError";
|
|
4237
|
+
this.code = opts.code;
|
|
4238
|
+
this.retryable = opts.retryable ?? false;
|
|
4239
|
+
this.status = opts.status;
|
|
4240
|
+
}
|
|
4241
|
+
};
|
|
4242
|
+
function isRetryableMintHttpFailure(status, code) {
|
|
4243
|
+
if (code === "experimental_surface_disabled") return false;
|
|
4244
|
+
return status === 408 || status === 425 || status === 429 || status >= 500;
|
|
4245
|
+
}
|
|
4246
|
+
function runnerCredentialErrorDetail(error) {
|
|
4247
|
+
if (error instanceof RunnerCredentialMintError) {
|
|
4248
|
+
return {
|
|
4249
|
+
message: error.message,
|
|
4250
|
+
code: error.code,
|
|
4251
|
+
retryable: error.retryable,
|
|
4252
|
+
status: error.status
|
|
4253
|
+
};
|
|
4254
|
+
}
|
|
4255
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4256
|
+
return {
|
|
4257
|
+
message,
|
|
4258
|
+
code: "runner_credential_mint_network_error",
|
|
4259
|
+
retryable: true
|
|
4260
|
+
};
|
|
4261
|
+
}
|
|
4262
|
+
async function waitForRunnerCredentialRetry() {
|
|
4263
|
+
await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS));
|
|
4264
|
+
}
|
|
3860
4265
|
function stalledRecoverySigtermTimeoutMs() {
|
|
3861
4266
|
return readNonNegativeIntegerEnv(
|
|
3862
4267
|
"SLOCK_DAEMON_STALLED_RECOVERY_SIGTERM_TIMEOUT_MS",
|
|
@@ -3885,6 +4290,22 @@ function formatMessageTarget(message) {
|
|
|
3885
4290
|
}
|
|
3886
4291
|
return `#${message.channel_name}`;
|
|
3887
4292
|
}
|
|
4293
|
+
function formatVisibleMessageTarget(message) {
|
|
4294
|
+
if (message.channel_type === "thread" && message.parent_channel_name && message.channel_name) {
|
|
4295
|
+
const shortId = getMessageShortId(String(message.channel_name));
|
|
4296
|
+
if (message.parent_channel_type === "dm") {
|
|
4297
|
+
return `dm:@${message.parent_channel_name}:${shortId}`;
|
|
4298
|
+
}
|
|
4299
|
+
return `#${message.parent_channel_name}:${shortId}`;
|
|
4300
|
+
}
|
|
4301
|
+
if (message.channel_type === "dm" && message.channel_name) {
|
|
4302
|
+
return `dm:@${message.channel_name}`;
|
|
4303
|
+
}
|
|
4304
|
+
if (message.channel_name) {
|
|
4305
|
+
return `#${message.channel_name}`;
|
|
4306
|
+
}
|
|
4307
|
+
return null;
|
|
4308
|
+
}
|
|
3888
4309
|
function getMessageShortId(messageId) {
|
|
3889
4310
|
return messageId.startsWith("thread-") ? messageId.slice(7) : messageId.slice(0, 8);
|
|
3890
4311
|
}
|
|
@@ -4505,6 +4926,14 @@ function createRuntimeTraceCounters() {
|
|
|
4505
4926
|
thinkingEvents: 0
|
|
4506
4927
|
};
|
|
4507
4928
|
}
|
|
4929
|
+
function cleanupAgentCredentialProxy(agentId, launchId) {
|
|
4930
|
+
unregisterAgentCredentialProxyForLaunch({ agentId, launchId });
|
|
4931
|
+
}
|
|
4932
|
+
function stripManagedRunnerCredential(config) {
|
|
4933
|
+
if (!config.agentCredentialKey && !config.agentCredentialId) return config;
|
|
4934
|
+
const { agentCredentialKey: _agentCredentialKey, agentCredentialId: _agentCredentialId, ...rest } = config;
|
|
4935
|
+
return rest;
|
|
4936
|
+
}
|
|
4508
4937
|
function createGatedSteeringState() {
|
|
4509
4938
|
return {
|
|
4510
4939
|
phase: "idle",
|
|
@@ -4836,6 +5265,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
4836
5265
|
tracer;
|
|
4837
5266
|
deliveryTraceContexts = /* @__PURE__ */ new WeakMap();
|
|
4838
5267
|
processExitTraceAttrs = /* @__PURE__ */ new WeakMap();
|
|
5268
|
+
agentVisibleBoundaries = /* @__PURE__ */ new Map();
|
|
4839
5269
|
constructor(chatBridgePath, sendToServer, daemonApiKey, opts) {
|
|
4840
5270
|
this.chatBridgePath = chatBridgePath;
|
|
4841
5271
|
this.slockCliPath = opts.slockCliPath ?? "";
|
|
@@ -4863,6 +5293,97 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
4863
5293
|
setTracer(tracer) {
|
|
4864
5294
|
this.tracer = tracer;
|
|
4865
5295
|
}
|
|
5296
|
+
visibleBoundaryMap(agentId) {
|
|
5297
|
+
let map = this.agentVisibleBoundaries.get(agentId);
|
|
5298
|
+
if (!map) {
|
|
5299
|
+
map = /* @__PURE__ */ new Map();
|
|
5300
|
+
this.agentVisibleBoundaries.set(agentId, map);
|
|
5301
|
+
}
|
|
5302
|
+
return map;
|
|
5303
|
+
}
|
|
5304
|
+
getVisibleBoundary(agentId, target) {
|
|
5305
|
+
return this.agentVisibleBoundaries.get(agentId)?.get(target);
|
|
5306
|
+
}
|
|
5307
|
+
pendingVisibleMessages(agentId, target) {
|
|
5308
|
+
const collect = (messages) => (messages ?? []).filter((message) => formatMessageTarget(message) === target && typeof message.seq === "number" && message.seq > 0);
|
|
5309
|
+
return [
|
|
5310
|
+
...collect(this.agents.get(agentId)?.inbox),
|
|
5311
|
+
...collect(this.startingInboxes.get(agentId))
|
|
5312
|
+
];
|
|
5313
|
+
}
|
|
5314
|
+
/**
|
|
5315
|
+
* Single-inbox consume point for messages that have been rendered into the
|
|
5316
|
+
* agent-visible input surface. Daemon pending inbox is only a runner cache:
|
|
5317
|
+
* once a message is visible through wake/stdin/check/read/held/preflight, the
|
|
5318
|
+
* same seq must not be injected later through the local pending queue.
|
|
5319
|
+
*/
|
|
5320
|
+
consumeVisibleMessages(agentId, input) {
|
|
5321
|
+
if (input.messages.length === 0 && (!input.target || typeof input.boundarySeq !== "number")) return;
|
|
5322
|
+
const byTarget = /* @__PURE__ */ new Map();
|
|
5323
|
+
const ensureBucket = (target) => {
|
|
5324
|
+
let bucket = byTarget.get(target);
|
|
5325
|
+
if (!bucket) {
|
|
5326
|
+
bucket = { maxSeq: 0, boundarySeq: 0, seqs: /* @__PURE__ */ new Set(), ids: /* @__PURE__ */ new Set() };
|
|
5327
|
+
byTarget.set(target, bucket);
|
|
5328
|
+
}
|
|
5329
|
+
return bucket;
|
|
5330
|
+
};
|
|
5331
|
+
for (const message of input.messages) {
|
|
5332
|
+
const seq = Number(message.seq ?? 0);
|
|
5333
|
+
if (!Number.isFinite(seq) || seq <= 0) continue;
|
|
5334
|
+
const target = input.target ?? formatVisibleMessageTarget(message);
|
|
5335
|
+
if (!target) continue;
|
|
5336
|
+
const bucket = ensureBucket(target);
|
|
5337
|
+
bucket.maxSeq = Math.max(bucket.maxSeq, Math.floor(seq));
|
|
5338
|
+
bucket.seqs.add(Math.floor(seq));
|
|
5339
|
+
const id = typeof message.id === "string" ? message.id : typeof message.message_id === "string" ? message.message_id : null;
|
|
5340
|
+
if (id) bucket.ids.add(id);
|
|
5341
|
+
}
|
|
5342
|
+
if (input.target && typeof input.boundarySeq === "number" && Number.isFinite(input.boundarySeq) && input.boundarySeq > 0) {
|
|
5343
|
+
const bucket = ensureBucket(input.target);
|
|
5344
|
+
bucket.boundarySeq = Math.max(bucket.boundarySeq, Math.floor(input.boundarySeq));
|
|
5345
|
+
}
|
|
5346
|
+
if (byTarget.size === 0) return;
|
|
5347
|
+
const boundaryMap = this.visibleBoundaryMap(agentId);
|
|
5348
|
+
for (const [target, bucket] of byTarget) {
|
|
5349
|
+
const highWaterSeq = Math.max(bucket.maxSeq, bucket.boundarySeq);
|
|
5350
|
+
const previous = boundaryMap.get(target) ?? 0;
|
|
5351
|
+
boundaryMap.set(target, Math.max(previous, highWaterSeq));
|
|
5352
|
+
}
|
|
5353
|
+
const suppress = (messages) => {
|
|
5354
|
+
if (!messages || messages.length === 0) return 0;
|
|
5355
|
+
let removed = 0;
|
|
5356
|
+
const retained = messages.filter((message) => {
|
|
5357
|
+
const target = formatMessageTarget(message);
|
|
5358
|
+
const bucket = byTarget.get(target);
|
|
5359
|
+
if (!bucket) return true;
|
|
5360
|
+
const seq = typeof message.seq === "number" ? Math.floor(message.seq) : 0;
|
|
5361
|
+
const id = message.message_id ?? "";
|
|
5362
|
+
const matched = seq > 0 && bucket.boundarySeq > 0 && seq <= bucket.boundarySeq || seq > 0 && bucket.seqs.has(seq) || id.length > 0 && bucket.ids.has(id);
|
|
5363
|
+
if (matched) removed += 1;
|
|
5364
|
+
return !matched;
|
|
5365
|
+
});
|
|
5366
|
+
messages.splice(0, messages.length, ...retained);
|
|
5367
|
+
return removed;
|
|
5368
|
+
};
|
|
5369
|
+
const active = this.agents.get(agentId);
|
|
5370
|
+
const removedActive = suppress(active?.inbox);
|
|
5371
|
+
const removedStarting = suppress(this.startingInboxes.get(agentId));
|
|
5372
|
+
this.recordDaemonTrace("daemon.agent.inbox.visible_consumed", {
|
|
5373
|
+
agentId,
|
|
5374
|
+
source: input.source,
|
|
5375
|
+
targets: [...byTarget.keys()],
|
|
5376
|
+
messages_count: input.messages.length,
|
|
5377
|
+
suppressed_pending_count: removedActive + removedStarting
|
|
5378
|
+
});
|
|
5379
|
+
}
|
|
5380
|
+
createAgentProxyInboxCoordinator(agentId) {
|
|
5381
|
+
return {
|
|
5382
|
+
getBoundary: (target) => this.getVisibleBoundary(agentId, target),
|
|
5383
|
+
getPendingMessages: (target) => this.pendingVisibleMessages(agentId, target),
|
|
5384
|
+
consumeVisibleMessages: (input) => this.consumeVisibleMessages(agentId, input)
|
|
5385
|
+
};
|
|
5386
|
+
}
|
|
4866
5387
|
recordDaemonTrace(name, attrs, status = "ok", parentTraceparent) {
|
|
4867
5388
|
const span = this.tracer.startSpan(name, {
|
|
4868
5389
|
parent: parseTraceparent(parentTraceparent),
|
|
@@ -5214,7 +5735,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5214
5735
|
chatBridgePath: this.chatBridgePath,
|
|
5215
5736
|
slockCliPath: this.slockCliPath,
|
|
5216
5737
|
daemonApiKey: this.daemonApiKey,
|
|
5217
|
-
launchId: launchId || null
|
|
5738
|
+
launchId: launchId || null,
|
|
5739
|
+
agentCredentialProxyInboxCoordinator: this.createAgentProxyInboxCoordinator(agentId)
|
|
5218
5740
|
});
|
|
5219
5741
|
this.recordDaemonTrace("daemon.agent.spawn.created", {
|
|
5220
5742
|
...this.startQueueTraceAttrs(agentId, effectiveConfig, wakeMessage, unreadSummary, resumePrompt, launchId),
|
|
@@ -5270,6 +5792,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5270
5792
|
this.ackInjectedRuntimeProfileControl(agentId, config.runtimeProfileControl, agentProcess.launchId);
|
|
5271
5793
|
}
|
|
5272
5794
|
if (wakeMessage) {
|
|
5795
|
+
this.consumeVisibleMessages(agentId, { messages: [wakeMessage], source: "spawn_wake_message" });
|
|
5273
5796
|
this.ackInjectedRuntimeProfileMessages(agentId, [wakeMessage], agentProcess.launchId);
|
|
5274
5797
|
}
|
|
5275
5798
|
let buffer = "";
|
|
@@ -5362,16 +5885,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5362
5885
|
...runtimeTraceCounterAttrs(ap),
|
|
5363
5886
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "process_exit")
|
|
5364
5887
|
});
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
this.clearCompactionWatchdog(ap);
|
|
5369
|
-
}
|
|
5888
|
+
this.interruptCompactionIfActive(agentId);
|
|
5889
|
+
cleanupAgentCredentialProxy(agentId, ap.launchId);
|
|
5890
|
+
this.revokeManagedRunnerCredential(agentId, ap.config, ap.launchId);
|
|
5370
5891
|
this.agents.delete(agentId);
|
|
5371
5892
|
if (missingResumeSession) {
|
|
5372
5893
|
const staleSessionId = ap.sessionId;
|
|
5373
5894
|
const runtimeLabel = ap.driver.id === "opencode" ? "OpenCode" : "Claude";
|
|
5374
|
-
const restartConfig = { ...ap.config, sessionId: null };
|
|
5895
|
+
const restartConfig = { ...stripManagedRunnerCredential(ap.config), sessionId: null };
|
|
5375
5896
|
logger.warn(
|
|
5376
5897
|
`[Agent ${agentId}] Stored ${runtimeLabel} session ${staleSessionId} is unavailable locally; falling back to cold start`
|
|
5377
5898
|
);
|
|
@@ -5408,7 +5929,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5408
5929
|
const unreadSummary2 = queuedWakeMessage ? buildUnreadSummary(ap.inbox, formatChannelLabel(queuedWakeMessage)) : void 0;
|
|
5409
5930
|
if (queuedWakeMessage) {
|
|
5410
5931
|
logger.info(`[Agent ${agentId}] Turn completed; restarting immediately for queued message`);
|
|
5411
|
-
const nextConfig = { ...ap.config, sessionId: ap.sessionId };
|
|
5932
|
+
const nextConfig = { ...stripManagedRunnerCredential(ap.config), sessionId: ap.sessionId };
|
|
5412
5933
|
this.idleAgentConfigs.set(agentId, {
|
|
5413
5934
|
config: nextConfig,
|
|
5414
5935
|
sessionId: ap.sessionId,
|
|
@@ -5418,6 +5939,9 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5418
5939
|
this.idleAgentConfigs.delete(agentId);
|
|
5419
5940
|
this.startAgent(agentId, nextConfig, queuedWakeMessage, unreadSummary2, void 0, ap.launchId || void 0).catch((err) => {
|
|
5420
5941
|
logger.error(`[Agent ${agentId}] Failed to continue with queued message`, err);
|
|
5942
|
+
if (this.reportRunnerCredentialMintFailure(agentId, err, ap.launchId, "queued_continuation")) {
|
|
5943
|
+
return;
|
|
5944
|
+
}
|
|
5421
5945
|
this.idleAgentConfigs.set(agentId, {
|
|
5422
5946
|
config: nextConfig,
|
|
5423
5947
|
sessionId: ap.sessionId,
|
|
@@ -5428,7 +5952,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5428
5952
|
return;
|
|
5429
5953
|
}
|
|
5430
5954
|
this.idleAgentConfigs.set(agentId, {
|
|
5431
|
-
config: { ...ap.config, sessionId: ap.sessionId },
|
|
5955
|
+
config: { ...stripManagedRunnerCredential(ap.config), sessionId: ap.sessionId },
|
|
5432
5956
|
sessionId: ap.sessionId,
|
|
5433
5957
|
launchId: ap.launchId
|
|
5434
5958
|
});
|
|
@@ -5464,27 +5988,28 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5464
5988
|
}
|
|
5465
5989
|
async buildSpawnConfig(agentId, config) {
|
|
5466
5990
|
const baseConfig = config.serverUrl === this.serverUrl ? config : { ...config, serverUrl: this.serverUrl };
|
|
5991
|
+
const runnerConfig = await this.ensureManagedRunnerCredential(agentId, baseConfig);
|
|
5467
5992
|
if (!this.defaultAgentEnvVarsProvider) {
|
|
5468
|
-
return
|
|
5993
|
+
return runnerConfig;
|
|
5469
5994
|
}
|
|
5470
5995
|
try {
|
|
5471
5996
|
const defaultEnvVars = await this.defaultAgentEnvVarsProvider({
|
|
5472
|
-
runtime:
|
|
5473
|
-
model:
|
|
5474
|
-
envVars:
|
|
5997
|
+
runtime: runnerConfig.runtime,
|
|
5998
|
+
model: runnerConfig.model,
|
|
5999
|
+
envVars: runnerConfig.envVars
|
|
5475
6000
|
});
|
|
5476
6001
|
if (!defaultEnvVars || Object.keys(defaultEnvVars).length === 0) {
|
|
5477
|
-
return
|
|
6002
|
+
return runnerConfig;
|
|
5478
6003
|
}
|
|
5479
6004
|
const mergedEnvVars = {
|
|
5480
6005
|
...defaultEnvVars,
|
|
5481
|
-
...
|
|
6006
|
+
...runnerConfig.envVars ?? {}
|
|
5482
6007
|
};
|
|
5483
|
-
if (this.sameEnvVars(mergedEnvVars,
|
|
5484
|
-
return
|
|
6008
|
+
if (this.sameEnvVars(mergedEnvVars, runnerConfig.envVars)) {
|
|
6009
|
+
return runnerConfig;
|
|
5485
6010
|
}
|
|
5486
6011
|
return {
|
|
5487
|
-
...
|
|
6012
|
+
...runnerConfig,
|
|
5488
6013
|
envVars: mergedEnvVars
|
|
5489
6014
|
};
|
|
5490
6015
|
} catch (error) {
|
|
@@ -5492,9 +6017,131 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5492
6017
|
logger.warn(
|
|
5493
6018
|
`[Agent ${agentId}] Failed to resolve default runtime env vars \u2014 continuing without machine-level defaults (${reason})`
|
|
5494
6019
|
);
|
|
5495
|
-
return
|
|
6020
|
+
return runnerConfig;
|
|
5496
6021
|
}
|
|
5497
6022
|
}
|
|
6023
|
+
async requestManagedRunnerCredentialOnce(agentId, config) {
|
|
6024
|
+
const url = new URL(`/internal/computer/runners/${encodeURIComponent(agentId)}/credentials`, this.serverUrl);
|
|
6025
|
+
const res = await fetch(url, {
|
|
6026
|
+
method: "POST",
|
|
6027
|
+
headers: {
|
|
6028
|
+
Authorization: `Bearer ${this.daemonApiKey}`,
|
|
6029
|
+
"Content-Type": "application/json",
|
|
6030
|
+
"X-Slock-Client": "daemon-server-session-worker"
|
|
6031
|
+
},
|
|
6032
|
+
body: JSON.stringify({
|
|
6033
|
+
scopes: ["send", "read", "mentions", "tasks", "reactions", "server", "channels"],
|
|
6034
|
+
name: `runner:${config.runtime}:${agentId.slice(0, 8)}`
|
|
6035
|
+
})
|
|
6036
|
+
});
|
|
6037
|
+
if (!res.ok) {
|
|
6038
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
6039
|
+
let detail = `HTTP ${res.status}`;
|
|
6040
|
+
let code = null;
|
|
6041
|
+
if (contentType.includes("application/json")) {
|
|
6042
|
+
const body2 = await res.json().catch(() => null);
|
|
6043
|
+
const error = typeof body2?.error === "string" ? body2.error : null;
|
|
6044
|
+
code = typeof body2?.code === "string" ? body2.code : null;
|
|
6045
|
+
detail = [detail, code, error].filter(Boolean).join(" ");
|
|
6046
|
+
}
|
|
6047
|
+
throw new RunnerCredentialMintError(detail, {
|
|
6048
|
+
code: code ?? "runner_credential_mint_http_error",
|
|
6049
|
+
retryable: isRetryableMintHttpFailure(res.status, code),
|
|
6050
|
+
status: res.status
|
|
6051
|
+
});
|
|
6052
|
+
}
|
|
6053
|
+
const body = await res.json().catch(() => null);
|
|
6054
|
+
if (typeof body?.apiKey !== "string" || !body.apiKey.startsWith("sk_agent_")) {
|
|
6055
|
+
throw new RunnerCredentialMintError("invalid_agent_credential_payload", {
|
|
6056
|
+
code: "invalid_agent_credential_payload"
|
|
6057
|
+
});
|
|
6058
|
+
}
|
|
6059
|
+
return {
|
|
6060
|
+
apiKey: body.apiKey,
|
|
6061
|
+
credentialId: typeof body.credentialId === "string" ? body.credentialId : null
|
|
6062
|
+
};
|
|
6063
|
+
}
|
|
6064
|
+
async ensureManagedRunnerCredential(agentId, config) {
|
|
6065
|
+
if (config.agentCredentialKey) return config;
|
|
6066
|
+
if (process.env.SLOCK_AGENT_RUNNER_CREDENTIALS_DISABLED === "1") {
|
|
6067
|
+
throw new RunnerCredentialMintError("runner credential mint is disabled by SLOCK_AGENT_RUNNER_CREDENTIALS_DISABLED", {
|
|
6068
|
+
code: "runner_credentials_disabled"
|
|
6069
|
+
});
|
|
6070
|
+
}
|
|
6071
|
+
let lastError = null;
|
|
6072
|
+
for (let attempt = 1; attempt <= RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS; attempt += 1) {
|
|
6073
|
+
try {
|
|
6074
|
+
const credential = await this.requestManagedRunnerCredentialOnce(agentId, config);
|
|
6075
|
+
return {
|
|
6076
|
+
...config,
|
|
6077
|
+
agentCredentialKey: credential.apiKey,
|
|
6078
|
+
agentCredentialId: credential.credentialId
|
|
6079
|
+
};
|
|
6080
|
+
} catch (err) {
|
|
6081
|
+
lastError = err;
|
|
6082
|
+
const detail2 = runnerCredentialErrorDetail(err);
|
|
6083
|
+
this.recordDaemonTrace("daemon.runner_credential_mint.retry", {
|
|
6084
|
+
agentId,
|
|
6085
|
+
runtime: config.runtime,
|
|
6086
|
+
attempt,
|
|
6087
|
+
max_attempts: RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS,
|
|
6088
|
+
status: detail2.status,
|
|
6089
|
+
code: detail2.code,
|
|
6090
|
+
reason: detail2.message,
|
|
6091
|
+
retryable: detail2.retryable
|
|
6092
|
+
}, detail2.retryable && attempt < RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS ? "ok" : "error");
|
|
6093
|
+
if (!detail2.retryable || attempt >= RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS) break;
|
|
6094
|
+
await waitForRunnerCredentialRetry();
|
|
6095
|
+
}
|
|
6096
|
+
}
|
|
6097
|
+
const detail = runnerCredentialErrorDetail(lastError);
|
|
6098
|
+
this.recordDaemonTrace("daemon.runner_credential_mint.failed", {
|
|
6099
|
+
agentId,
|
|
6100
|
+
runtime: config.runtime,
|
|
6101
|
+
status: detail.status,
|
|
6102
|
+
code: detail.code,
|
|
6103
|
+
reason: detail.message,
|
|
6104
|
+
retryable: detail.retryable,
|
|
6105
|
+
max_attempts: RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS
|
|
6106
|
+
}, "error");
|
|
6107
|
+
throw new RunnerCredentialMintError(
|
|
6108
|
+
`runner_credential_mint_failed: ${detail.message}. Managed runner startup requires /internal/computer credential mint; deploy server first or roll back the daemon binary.`,
|
|
6109
|
+
{
|
|
6110
|
+
code: detail.code,
|
|
6111
|
+
retryable: detail.retryable,
|
|
6112
|
+
status: detail.status
|
|
6113
|
+
}
|
|
6114
|
+
);
|
|
6115
|
+
}
|
|
6116
|
+
revokeManagedRunnerCredential(agentId, config, launchId) {
|
|
6117
|
+
const credentialId = config.agentCredentialId;
|
|
6118
|
+
if (!credentialId) return;
|
|
6119
|
+
const url = new URL(
|
|
6120
|
+
`/internal/computer/runners/${encodeURIComponent(agentId)}/credentials/${encodeURIComponent(credentialId)}`,
|
|
6121
|
+
this.serverUrl
|
|
6122
|
+
);
|
|
6123
|
+
void fetch(url, {
|
|
6124
|
+
method: "DELETE",
|
|
6125
|
+
headers: {
|
|
6126
|
+
Authorization: `Bearer ${this.daemonApiKey}`,
|
|
6127
|
+
"X-Slock-Client": "daemon-server-session-worker"
|
|
6128
|
+
}
|
|
6129
|
+
}).then((res) => {
|
|
6130
|
+
this.recordDaemonTrace("daemon.runner_credential.revoke", {
|
|
6131
|
+
agentId,
|
|
6132
|
+
launchId: launchId || void 0,
|
|
6133
|
+
credentialId,
|
|
6134
|
+
status: res.status
|
|
6135
|
+
}, res.ok ? "ok" : "error");
|
|
6136
|
+
}).catch((err) => {
|
|
6137
|
+
this.recordDaemonTrace("daemon.runner_credential.revoke", {
|
|
6138
|
+
agentId,
|
|
6139
|
+
launchId: launchId || void 0,
|
|
6140
|
+
credentialId,
|
|
6141
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
6142
|
+
}, "error");
|
|
6143
|
+
});
|
|
6144
|
+
}
|
|
5498
6145
|
sameEnvVars(left, right) {
|
|
5499
6146
|
const leftKeys = Object.keys(left ?? {});
|
|
5500
6147
|
const rightKeys = Object.keys(right ?? {});
|
|
@@ -5583,6 +6230,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5583
6230
|
}
|
|
5584
6231
|
this.clearCompactionWatchdog(ap);
|
|
5585
6232
|
this.clearStalledRecoverySigtermWatchdog(ap);
|
|
6233
|
+
cleanupAgentCredentialProxy(agentId, ap.launchId);
|
|
6234
|
+
this.revokeManagedRunnerCredential(agentId, ap.config, ap.launchId);
|
|
5586
6235
|
this.agents.delete(agentId);
|
|
5587
6236
|
this.processExitTraceAttrs.set(ap.process, {
|
|
5588
6237
|
stop_source: silent ? "daemon_internal" : "explicit_request",
|
|
@@ -5666,10 +6315,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5666
6315
|
session_id_present: Boolean(cached.sessionId),
|
|
5667
6316
|
launchId: cached.launchId || void 0
|
|
5668
6317
|
}));
|
|
5669
|
-
this.startAgent(agentId, cached.config, message, void 0, void 0, cached.launchId || void 0).
|
|
6318
|
+
return this.startAgent(agentId, cached.config, message, void 0, void 0, cached.launchId || void 0).then(() => true, (err) => {
|
|
5670
6319
|
logger.error(`[Agent ${agentId}] Failed to auto-restart`, err);
|
|
6320
|
+
if (this.reportRunnerCredentialMintFailure(agentId, err, cached.launchId, "idle_auto_restart")) {
|
|
6321
|
+
return false;
|
|
6322
|
+
}
|
|
6323
|
+
this.idleAgentConfigs.set(agentId, cached);
|
|
6324
|
+
return false;
|
|
5671
6325
|
});
|
|
5672
|
-
return true;
|
|
5673
6326
|
}
|
|
5674
6327
|
logger.warn(`[Agent ${agentId}] Delivery received but no running process or cached idle config exists`);
|
|
5675
6328
|
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
@@ -5751,6 +6404,37 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5751
6404
|
}));
|
|
5752
6405
|
return true;
|
|
5753
6406
|
}
|
|
6407
|
+
if (ap.gatedSteering.compacting) {
|
|
6408
|
+
ap.pendingNotificationCount++;
|
|
6409
|
+
if (ap.notificationTimer) {
|
|
6410
|
+
clearTimeout(ap.notificationTimer);
|
|
6411
|
+
ap.notificationTimer = null;
|
|
6412
|
+
}
|
|
6413
|
+
if (ap.driver.busyDeliveryMode === "gated") {
|
|
6414
|
+
this.recordGatedSteeringEvent(agentId, ap, "buffer", {
|
|
6415
|
+
reason: "compaction_boundary",
|
|
6416
|
+
pendingMessages: ap.inbox.length
|
|
6417
|
+
});
|
|
6418
|
+
}
|
|
6419
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.compaction_boundary.delivery_buffered", {
|
|
6420
|
+
pendingNotificationCount: ap.pendingNotificationCount,
|
|
6421
|
+
pendingMessages: ap.inbox.length,
|
|
6422
|
+
busyDeliveryMode: ap.driver.busyDeliveryMode
|
|
6423
|
+
});
|
|
6424
|
+
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
6425
|
+
outcome: "queued_compaction_boundary",
|
|
6426
|
+
accepted: true,
|
|
6427
|
+
process_present: true,
|
|
6428
|
+
runtime: ap.config.runtime,
|
|
6429
|
+
session_id_present: true,
|
|
6430
|
+
launchId: ap.launchId || void 0,
|
|
6431
|
+
inbox_count: ap.inbox.length,
|
|
6432
|
+
pending_notification_count: ap.pendingNotificationCount,
|
|
6433
|
+
busy_delivery_mode: ap.driver.busyDeliveryMode,
|
|
6434
|
+
notification_timer_present: false
|
|
6435
|
+
}));
|
|
6436
|
+
return true;
|
|
6437
|
+
}
|
|
5754
6438
|
if (ap.driver.busyDeliveryMode === "gated") {
|
|
5755
6439
|
ap.pendingNotificationCount++;
|
|
5756
6440
|
this.recordGatedSteeringEvent(agentId, ap, "buffer", {
|
|
@@ -5957,19 +6641,28 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5957
6641
|
if (cached) {
|
|
5958
6642
|
logger.info(`[Agent ${agentId}] Starting from idle state for runtime profile ${kind} ${key}`);
|
|
5959
6643
|
this.idleAgentConfigs.delete(agentId);
|
|
5960
|
-
this.startAgent(agentId, cached.config, message, void 0, void 0, cached.launchId || void 0).
|
|
6644
|
+
return this.startAgent(agentId, cached.config, message, void 0, void 0, cached.launchId || void 0).then(() => true, (err) => {
|
|
5961
6645
|
logger.error(`[Agent ${agentId}] Failed to auto-restart for runtime profile notification`, err);
|
|
5962
|
-
this.
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
6646
|
+
if (this.reportRunnerCredentialMintFailure(agentId, err, cached.launchId, "runtime_profile_auto_restart")) {
|
|
6647
|
+
span.end("error", {
|
|
6648
|
+
attrs: {
|
|
6649
|
+
outcome: "runner_credential_mint_failed",
|
|
6650
|
+
runtime: cached.config.runtime,
|
|
6651
|
+
launchId: cached.launchId || void 0
|
|
6652
|
+
}
|
|
6653
|
+
});
|
|
6654
|
+
return false;
|
|
5970
6655
|
}
|
|
6656
|
+
this.idleAgentConfigs.set(agentId, cached);
|
|
6657
|
+
span.end("error", {
|
|
6658
|
+
attrs: {
|
|
6659
|
+
outcome: "restart_failed",
|
|
6660
|
+
runtime: cached.config.runtime,
|
|
6661
|
+
launchId: cached.launchId || void 0
|
|
6662
|
+
}
|
|
6663
|
+
});
|
|
6664
|
+
return false;
|
|
5971
6665
|
});
|
|
5972
|
-
return true;
|
|
5973
6666
|
}
|
|
5974
6667
|
logger.warn(`[Agent ${agentId}] Runtime profile ${kind} ${key} has no runtime injection path yet; leaving unacked for retry`);
|
|
5975
6668
|
span.end("ok", { attrs: { outcome: "no_path" } });
|
|
@@ -6419,11 +7112,23 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6419
7112
|
ap.compactionWatchdog = null;
|
|
6420
7113
|
this.broadcastActivity(agentId, "working", "Context compaction still running; no finish event observed");
|
|
6421
7114
|
}
|
|
6422
|
-
|
|
7115
|
+
completeCompactionIfActive(agentId, detail = "Context compaction finished", options = {}) {
|
|
6423
7116
|
const ap = this.agents.get(agentId);
|
|
6424
7117
|
if (!ap || !ap.compactionStartedAt) return;
|
|
6425
7118
|
this.clearCompactionWatchdog(ap);
|
|
6426
7119
|
this.broadcastActivity(agentId, "working", detail, [{ kind: "compaction_finished" }]);
|
|
7120
|
+
ap.gatedSteering.compacting = false;
|
|
7121
|
+
this.setGatedSteeringPhase(agentId, ap, "assistant_continuation", { event: "compaction_finished", inferred: true });
|
|
7122
|
+
if (options.flushBoundaryMessages ?? true) {
|
|
7123
|
+
this.flushCompactionBoundaryMessages(agentId, ap);
|
|
7124
|
+
}
|
|
7125
|
+
ap.isIdle = false;
|
|
7126
|
+
}
|
|
7127
|
+
interruptCompactionIfActive(agentId) {
|
|
7128
|
+
const ap = this.agents.get(agentId);
|
|
7129
|
+
if (!ap || !ap.compactionStartedAt && !ap.gatedSteering.compacting) return;
|
|
7130
|
+
this.clearCompactionWatchdog(ap);
|
|
7131
|
+
ap.gatedSteering.compacting = false;
|
|
6427
7132
|
}
|
|
6428
7133
|
messagesTraceAttrs(messages) {
|
|
6429
7134
|
if (!messages || messages.length === 0) return {};
|
|
@@ -6746,28 +7451,28 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6746
7451
|
this.sendRuntimeProfileReport(agentId);
|
|
6747
7452
|
break;
|
|
6748
7453
|
case "thinking": {
|
|
6749
|
-
this.
|
|
7454
|
+
if (ap) this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
|
|
7455
|
+
this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed output)");
|
|
6750
7456
|
this.queueTrajectoryText(agentId, "thinking", event.text);
|
|
6751
7457
|
if (ap) ap.isIdle = false;
|
|
6752
|
-
if (ap) this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
|
|
6753
7458
|
this.setGatedSteeringPhase(agentId, ap, "assistant_continuation", { event: "thinking" });
|
|
6754
7459
|
break;
|
|
6755
7460
|
}
|
|
6756
7461
|
case "text": {
|
|
6757
|
-
this.
|
|
7462
|
+
if (ap) this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
|
|
7463
|
+
this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed output)");
|
|
6758
7464
|
this.queueTrajectoryText(agentId, "text", event.text);
|
|
6759
7465
|
if (ap) ap.isIdle = false;
|
|
6760
|
-
if (ap) this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
|
|
6761
7466
|
this.setGatedSteeringPhase(agentId, ap, "assistant_continuation", { event: "text" });
|
|
6762
7467
|
break;
|
|
6763
7468
|
}
|
|
6764
7469
|
case "tool_call": {
|
|
6765
|
-
this.
|
|
7470
|
+
if (ap) this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
|
|
7471
|
+
this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed tool use)");
|
|
6766
7472
|
this.flushPendingTrajectory(agentId);
|
|
6767
7473
|
const invocation = normalizeToolDisplayInvocation(event.name, event.input);
|
|
6768
7474
|
if (ap) {
|
|
6769
7475
|
ap.gatedSteering.outstandingToolUses++;
|
|
6770
|
-
this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
|
|
6771
7476
|
this.noteRuntimeProfileToolCall(agentId, ap, invocation.toolName);
|
|
6772
7477
|
this.recordRuntimeTraceEvent(agentId, ap, "tool.call.started", { tool: invocation.toolName });
|
|
6773
7478
|
this.setGatedSteeringPhase(agentId, ap, "tool_wait", {
|
|
@@ -6828,7 +7533,9 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6828
7533
|
break;
|
|
6829
7534
|
case "turn_end":
|
|
6830
7535
|
if (ap) this.recordRuntimeTraceEvent(agentId, ap, "runtime.turn.completed");
|
|
6831
|
-
this.
|
|
7536
|
+
this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from turn end)", {
|
|
7537
|
+
flushBoundaryMessages: false
|
|
7538
|
+
});
|
|
6832
7539
|
this.flushPendingTrajectory(agentId);
|
|
6833
7540
|
if (ap) {
|
|
6834
7541
|
this.clearGatedInFlightBatch(agentId, ap, "turn_end");
|
|
@@ -6885,7 +7592,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6885
7592
|
}
|
|
6886
7593
|
break;
|
|
6887
7594
|
case "error": {
|
|
6888
|
-
this.
|
|
7595
|
+
this.interruptCompactionIfActive(agentId);
|
|
6889
7596
|
this.flushPendingTrajectory(agentId);
|
|
6890
7597
|
if (ap) ap.lastRuntimeError = event.message;
|
|
6891
7598
|
if (ap) {
|
|
@@ -6933,6 +7640,29 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6933
7640
|
sendAgentStatus(agentId, status, launchId) {
|
|
6934
7641
|
this.sendToServer({ type: "agent:status", agentId, status, launchId: launchId || void 0 });
|
|
6935
7642
|
}
|
|
7643
|
+
reportRunnerCredentialMintFailure(agentId, err, launchId, source) {
|
|
7644
|
+
if (!(err instanceof RunnerCredentialMintError)) return false;
|
|
7645
|
+
const detail = runnerCredentialErrorDetail(err);
|
|
7646
|
+
this.recordDaemonTrace("daemon.runner_credential_mint.hard_fail", {
|
|
7647
|
+
agentId,
|
|
7648
|
+
launchId: launchId || void 0,
|
|
7649
|
+
source,
|
|
7650
|
+
status: detail.status,
|
|
7651
|
+
code: detail.code,
|
|
7652
|
+
reason: detail.message,
|
|
7653
|
+
retryable: detail.retryable
|
|
7654
|
+
}, "error");
|
|
7655
|
+
const reason = err.message;
|
|
7656
|
+
this.sendAgentStatus(agentId, "inactive", launchId);
|
|
7657
|
+
this.broadcastActivity(
|
|
7658
|
+
agentId,
|
|
7659
|
+
"error",
|
|
7660
|
+
`Start failed: ${reason}`,
|
|
7661
|
+
[{ kind: "text", text: `Error: ${reason}` }],
|
|
7662
|
+
launchId
|
|
7663
|
+
);
|
|
7664
|
+
return true;
|
|
7665
|
+
}
|
|
6936
7666
|
noteRuntimeTraceCounter(ap, event) {
|
|
6937
7667
|
ap.runtimeTraceCounters.events++;
|
|
6938
7668
|
switch (event.kind) {
|
|
@@ -7095,6 +7825,7 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
7095
7825
|
}, "error");
|
|
7096
7826
|
return false;
|
|
7097
7827
|
}
|
|
7828
|
+
this.consumeVisibleMessages(agentId, { messages, source: `stdin_${mode}_delivery` });
|
|
7098
7829
|
const senders = [...new Set(messages.map((message) => `@${message.sender_name}`))].join(", ");
|
|
7099
7830
|
logger.info(
|
|
7100
7831
|
`[Agent ${agentId}] Delivering ${mode} ${messages.length === 1 ? "message" : `${messages.length} messages`} via stdin from ${senders}`
|
|
@@ -7432,7 +8163,7 @@ var ReminderCache = class {
|
|
|
7432
8163
|
|
|
7433
8164
|
// src/machineLock.ts
|
|
7434
8165
|
import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
|
|
7435
|
-
import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as
|
|
8166
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
7436
8167
|
import os7 from "os";
|
|
7437
8168
|
import path13 from "path";
|
|
7438
8169
|
var INCOMPLETE_LOCK_STALE_MS = 3e4;
|
|
@@ -7505,7 +8236,7 @@ function acquireDaemonMachineLock(options) {
|
|
|
7505
8236
|
writeFileSync8(ownerPath(lockDir), `${JSON.stringify(owner, null, 2)}
|
|
7506
8237
|
`, { mode: 384 });
|
|
7507
8238
|
} catch (err) {
|
|
7508
|
-
|
|
8239
|
+
rmSync3(lockDir, { recursive: true, force: true });
|
|
7509
8240
|
throw err;
|
|
7510
8241
|
}
|
|
7511
8242
|
return {
|
|
@@ -7515,7 +8246,7 @@ function acquireDaemonMachineLock(options) {
|
|
|
7515
8246
|
release: () => {
|
|
7516
8247
|
const currentOwner = readOwner(lockDir);
|
|
7517
8248
|
if (currentOwner?.pid === process.pid && currentOwner.token === token) {
|
|
7518
|
-
|
|
8249
|
+
rmSync3(lockDir, { recursive: true, force: true });
|
|
7519
8250
|
}
|
|
7520
8251
|
}
|
|
7521
8252
|
};
|
|
@@ -7532,14 +8263,14 @@ function acquireDaemonMachineLock(options) {
|
|
|
7532
8263
|
throw new DaemonMachineLockConflictError(lockDir, null);
|
|
7533
8264
|
}
|
|
7534
8265
|
}
|
|
7535
|
-
|
|
8266
|
+
rmSync3(lockDir, { recursive: true, force: true });
|
|
7536
8267
|
}
|
|
7537
8268
|
}
|
|
7538
8269
|
throw new DaemonMachineLockConflictError(lockDir, readOwner(lockDir));
|
|
7539
8270
|
}
|
|
7540
8271
|
|
|
7541
8272
|
// src/localTraceSink.ts
|
|
7542
|
-
import { appendFileSync, mkdirSync as mkdirSync6, readdirSync as readdirSync3, rmSync as
|
|
8273
|
+
import { appendFileSync, mkdirSync as mkdirSync6, readdirSync as readdirSync3, rmSync as rmSync4, statSync as statSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
7543
8274
|
import path14 from "path";
|
|
7544
8275
|
var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
7545
8276
|
var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
|
|
@@ -7621,7 +8352,7 @@ var LocalRotatingTraceSink = class {
|
|
|
7621
8352
|
const excess = files.length - this.maxFiles;
|
|
7622
8353
|
if (excess <= 0) return;
|
|
7623
8354
|
for (const file of files.slice(0, excess)) {
|
|
7624
|
-
|
|
8355
|
+
rmSync4(path14.join(this.traceDir, file), { force: true });
|
|
7625
8356
|
}
|
|
7626
8357
|
}
|
|
7627
8358
|
};
|
|
@@ -8052,7 +8783,45 @@ function readPositiveIntegerEnv2(name, fallback) {
|
|
|
8052
8783
|
|
|
8053
8784
|
// src/core.ts
|
|
8054
8785
|
var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
|
|
8786
|
+
var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels"];
|
|
8787
|
+
var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
|
|
8788
|
+
var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
|
|
8055
8789
|
var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
|
|
8790
|
+
var RunnerCredentialMintError2 = class extends Error {
|
|
8791
|
+
code;
|
|
8792
|
+
retryable;
|
|
8793
|
+
status;
|
|
8794
|
+
constructor(message, opts) {
|
|
8795
|
+
super(message);
|
|
8796
|
+
this.name = "RunnerCredentialMintError";
|
|
8797
|
+
this.code = opts.code;
|
|
8798
|
+
this.retryable = opts.retryable ?? false;
|
|
8799
|
+
this.status = opts.status;
|
|
8800
|
+
}
|
|
8801
|
+
};
|
|
8802
|
+
function isRetryableMintHttpFailure2(status, code) {
|
|
8803
|
+
if (code === "experimental_surface_disabled") return false;
|
|
8804
|
+
return status === 408 || status === 425 || status === 429 || status >= 500;
|
|
8805
|
+
}
|
|
8806
|
+
function runnerCredentialErrorDetail2(error) {
|
|
8807
|
+
if (error instanceof RunnerCredentialMintError2) {
|
|
8808
|
+
return {
|
|
8809
|
+
message: error.message,
|
|
8810
|
+
code: error.code,
|
|
8811
|
+
retryable: error.retryable,
|
|
8812
|
+
status: error.status
|
|
8813
|
+
};
|
|
8814
|
+
}
|
|
8815
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8816
|
+
return {
|
|
8817
|
+
message,
|
|
8818
|
+
code: "runner_credential_mint_network_error",
|
|
8819
|
+
retryable: true
|
|
8820
|
+
};
|
|
8821
|
+
}
|
|
8822
|
+
async function waitForRunnerCredentialRetry2() {
|
|
8823
|
+
await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
|
|
8824
|
+
}
|
|
8056
8825
|
function parseDaemonCliArgs(args) {
|
|
8057
8826
|
let serverUrl = "";
|
|
8058
8827
|
let apiKey = "";
|
|
@@ -8384,13 +9153,115 @@ var DaemonCore = class {
|
|
|
8384
9153
|
});
|
|
8385
9154
|
span.end(status);
|
|
8386
9155
|
}
|
|
9156
|
+
async requestRunnerCredentialOnce(agentId, config) {
|
|
9157
|
+
const url = new URL(`/internal/computer/runners/${encodeURIComponent(agentId)}/credentials`, this.options.serverUrl);
|
|
9158
|
+
const res = await fetch(url, {
|
|
9159
|
+
method: "POST",
|
|
9160
|
+
headers: {
|
|
9161
|
+
"Authorization": `Bearer ${this.options.apiKey}`,
|
|
9162
|
+
"Content-Type": "application/json",
|
|
9163
|
+
"X-Slock-Client": "daemon-server-session-worker"
|
|
9164
|
+
},
|
|
9165
|
+
body: JSON.stringify({
|
|
9166
|
+
scopes: RUNNER_CREDENTIAL_SCOPES,
|
|
9167
|
+
name: `runner:${config.runtime}:${agentId.slice(0, 8)}`
|
|
9168
|
+
})
|
|
9169
|
+
});
|
|
9170
|
+
if (!res.ok) {
|
|
9171
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
9172
|
+
let detail = `HTTP ${res.status}`;
|
|
9173
|
+
let code = null;
|
|
9174
|
+
if (contentType.includes("application/json")) {
|
|
9175
|
+
const body2 = await res.json().catch(() => null);
|
|
9176
|
+
const error = typeof body2?.error === "string" ? body2.error : null;
|
|
9177
|
+
code = typeof body2?.code === "string" ? body2.code : null;
|
|
9178
|
+
detail = [detail, code, error].filter(Boolean).join(" ");
|
|
9179
|
+
}
|
|
9180
|
+
throw new RunnerCredentialMintError2(detail, {
|
|
9181
|
+
code: code ?? "runner_credential_mint_http_error",
|
|
9182
|
+
retryable: isRetryableMintHttpFailure2(res.status, code),
|
|
9183
|
+
status: res.status
|
|
9184
|
+
});
|
|
9185
|
+
}
|
|
9186
|
+
const body = await res.json().catch(() => null);
|
|
9187
|
+
if (typeof body?.apiKey !== "string" || !body.apiKey.startsWith("sk_agent_")) {
|
|
9188
|
+
throw new RunnerCredentialMintError2("invalid_agent_credential_payload", {
|
|
9189
|
+
code: "invalid_agent_credential_payload"
|
|
9190
|
+
});
|
|
9191
|
+
}
|
|
9192
|
+
return {
|
|
9193
|
+
apiKey: body.apiKey,
|
|
9194
|
+
credentialId: typeof body.credentialId === "string" ? body.credentialId : null
|
|
9195
|
+
};
|
|
9196
|
+
}
|
|
9197
|
+
async mintRunnerCredential(agentId, config) {
|
|
9198
|
+
if (config.agentCredentialKey) {
|
|
9199
|
+
return { apiKey: config.agentCredentialKey, credentialId: config.agentCredentialId ?? null };
|
|
9200
|
+
}
|
|
9201
|
+
if (process.env.SLOCK_AGENT_RUNNER_CREDENTIALS_DISABLED === "1") {
|
|
9202
|
+
throw new RunnerCredentialMintError2("runner credential mint is disabled by SLOCK_AGENT_RUNNER_CREDENTIALS_DISABLED", {
|
|
9203
|
+
code: "runner_credentials_disabled"
|
|
9204
|
+
});
|
|
9205
|
+
}
|
|
9206
|
+
let lastError = null;
|
|
9207
|
+
for (let attempt = 1; attempt <= RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2; attempt += 1) {
|
|
9208
|
+
try {
|
|
9209
|
+
return await this.requestRunnerCredentialOnce(agentId, config);
|
|
9210
|
+
} catch (err) {
|
|
9211
|
+
lastError = err;
|
|
9212
|
+
const detail2 = runnerCredentialErrorDetail2(err);
|
|
9213
|
+
this.recordDaemonTrace("daemon.runner_credential_mint.retry", {
|
|
9214
|
+
agentId,
|
|
9215
|
+
runtime: config.runtime,
|
|
9216
|
+
attempt,
|
|
9217
|
+
max_attempts: RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2,
|
|
9218
|
+
status: detail2.status,
|
|
9219
|
+
code: detail2.code,
|
|
9220
|
+
reason: detail2.message,
|
|
9221
|
+
retryable: detail2.retryable
|
|
9222
|
+
}, detail2.retryable && attempt < RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 ? "ok" : "error");
|
|
9223
|
+
if (!detail2.retryable || attempt >= RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2) break;
|
|
9224
|
+
await waitForRunnerCredentialRetry2();
|
|
9225
|
+
}
|
|
9226
|
+
}
|
|
9227
|
+
const detail = runnerCredentialErrorDetail2(lastError);
|
|
9228
|
+
this.recordDaemonTrace("daemon.runner_credential_mint.failed", {
|
|
9229
|
+
agentId,
|
|
9230
|
+
runtime: config.runtime,
|
|
9231
|
+
status: detail.status,
|
|
9232
|
+
code: detail.code,
|
|
9233
|
+
reason: detail.message,
|
|
9234
|
+
retryable: detail.retryable,
|
|
9235
|
+
max_attempts: RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2
|
|
9236
|
+
}, "error");
|
|
9237
|
+
throw new RunnerCredentialMintError2(
|
|
9238
|
+
`runner_credential_mint_failed: ${detail.message}. Managed runner startup requires /internal/computer credential mint; deploy server first or roll back the daemon binary.`,
|
|
9239
|
+
{
|
|
9240
|
+
code: detail.code,
|
|
9241
|
+
retryable: detail.retryable,
|
|
9242
|
+
status: detail.status
|
|
9243
|
+
}
|
|
9244
|
+
);
|
|
9245
|
+
}
|
|
9246
|
+
async startAgentFromMessage(msg) {
|
|
9247
|
+
const agentCredential = await this.mintRunnerCredential(msg.agentId, msg.config);
|
|
9248
|
+
const config = { ...msg.config, agentCredentialKey: agentCredential.apiKey, agentCredentialId: agentCredential.credentialId };
|
|
9249
|
+
await this.agentManager.startAgent(
|
|
9250
|
+
msg.agentId,
|
|
9251
|
+
config,
|
|
9252
|
+
msg.wakeMessage,
|
|
9253
|
+
msg.unreadSummary,
|
|
9254
|
+
msg.resumePrompt,
|
|
9255
|
+
msg.launchId
|
|
9256
|
+
);
|
|
9257
|
+
}
|
|
8387
9258
|
handleMessage(msg) {
|
|
8388
9259
|
const summary = summarizeIncomingMessage(msg);
|
|
8389
9260
|
logger.info(`[Daemon] Received ${msg.type}${summary ? ` ${summary}` : ""}`);
|
|
8390
9261
|
switch (msg.type) {
|
|
8391
9262
|
case "agent:start":
|
|
8392
9263
|
logger.info(`[Agent ${msg.agentId}] Start requested (runtime=${msg.config.runtime}, model=${msg.config.model}, session=${msg.config.sessionId || "new"}${msg.wakeMessage ? ", wake=true" : ""})`);
|
|
8393
|
-
this.
|
|
9264
|
+
this.startAgentFromMessage(msg).catch((err) => {
|
|
8394
9265
|
const reason = err instanceof Error ? err.message : String(err);
|
|
8395
9266
|
logger.error(`[Agent ${msg.agentId}] Start failed (${reason})`);
|
|
8396
9267
|
this.connection.send({ type: "agent:status", agentId: msg.agentId, status: "inactive", launchId: msg.launchId });
|
|
@@ -8423,22 +9294,27 @@ var DaemonCore = class {
|
|
|
8423
9294
|
logger.info(`[Agent ${msg.agentId}] Delivery received (seq=${msg.seq}, from=@${msg.message.sender_name}, target=${formatChannelTarget(msg)})`);
|
|
8424
9295
|
try {
|
|
8425
9296
|
span.addEvent("daemon.receive", { seq: msg.seq, deliveryId: msg.deliveryId });
|
|
8426
|
-
const
|
|
8427
|
-
|
|
8428
|
-
|
|
8429
|
-
|
|
8430
|
-
|
|
8431
|
-
|
|
8432
|
-
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8437
|
-
|
|
8438
|
-
|
|
8439
|
-
|
|
9297
|
+
const acceptedOrPromise = this.agentManager.deliverMessage(msg.agentId, msg.message, { deliveryId: msg.deliveryId });
|
|
9298
|
+
Promise.resolve(acceptedOrPromise).then((accepted) => {
|
|
9299
|
+
span.addEvent("daemon.deliver_to_agent_manager", { accepted });
|
|
9300
|
+
if (!accepted) {
|
|
9301
|
+
span.end("ok", { attrs: { outcome: "not-accepted" } });
|
|
9302
|
+
return;
|
|
9303
|
+
}
|
|
9304
|
+
const ackSeq = msg.seq > 0 ? msg.seq : msg.message.seq ?? 0;
|
|
9305
|
+
span.addEvent("daemon.ack.sent", { seq: ackSeq });
|
|
9306
|
+
this.connection.send({
|
|
9307
|
+
type: "agent:deliver:ack",
|
|
9308
|
+
agentId: msg.agentId,
|
|
9309
|
+
seq: ackSeq,
|
|
9310
|
+
traceparent: formatTraceparent(span.context),
|
|
9311
|
+
deliveryId: msg.deliveryId
|
|
9312
|
+
});
|
|
9313
|
+
span.end("ok", { attrs: { outcome: "ack-sent", ackSeq, deliveryId: msg.deliveryId } });
|
|
9314
|
+
}, (err) => {
|
|
9315
|
+
logger.error(`[Agent ${msg.agentId}] Delivery handling failed`, err);
|
|
9316
|
+
span.end("error", { attrs: { error_class: err instanceof Error ? err.name : typeof err } });
|
|
8440
9317
|
});
|
|
8441
|
-
span.end("ok", { attrs: { outcome: "ack-sent", ackSeq, deliveryId: msg.deliveryId } });
|
|
8442
9318
|
} catch (err) {
|
|
8443
9319
|
span.end("error", { attrs: { error_class: err instanceof Error ? err.name : typeof err } });
|
|
8444
9320
|
throw err;
|
|
@@ -8458,8 +9334,14 @@ var DaemonCore = class {
|
|
|
8458
9334
|
}
|
|
8459
9335
|
});
|
|
8460
9336
|
logger.info(`[Agent ${msg.agentId}] Runtime profile migration received (${msg.migrationKey})`);
|
|
8461
|
-
|
|
8462
|
-
|
|
9337
|
+
Promise.resolve(
|
|
9338
|
+
this.agentManager.deliverRuntimeProfileNotification(msg.agentId, msg.migrationKey, "migration", msg.message, formatTraceparent(span.context))
|
|
9339
|
+
).then((accepted) => {
|
|
9340
|
+
span.end("ok", { attrs: { outcome: accepted ? "accepted" : "no_injection_path" } });
|
|
9341
|
+
}, (err) => {
|
|
9342
|
+
logger.error(`[Agent ${msg.agentId}] Runtime profile migration handling failed`, err);
|
|
9343
|
+
span.end("error", { attrs: { error_class: err instanceof Error ? err.name : typeof err } });
|
|
9344
|
+
});
|
|
8463
9345
|
break;
|
|
8464
9346
|
}
|
|
8465
9347
|
case "agent:runtime_profile:daemon_release_notice": {
|
|
@@ -8475,8 +9357,14 @@ var DaemonCore = class {
|
|
|
8475
9357
|
}
|
|
8476
9358
|
});
|
|
8477
9359
|
logger.info(`[Agent ${msg.agentId}] Runtime profile daemon release notice received (${msg.noticeKey})`);
|
|
8478
|
-
|
|
8479
|
-
|
|
9360
|
+
Promise.resolve(
|
|
9361
|
+
this.agentManager.deliverRuntimeProfileNotification(msg.agentId, msg.noticeKey, "daemon_release_notice", msg.message, formatTraceparent(span.context))
|
|
9362
|
+
).then((accepted) => {
|
|
9363
|
+
span.end("ok", { attrs: { outcome: accepted ? "accepted" : "no_injection_path" } });
|
|
9364
|
+
}, (err) => {
|
|
9365
|
+
logger.error(`[Agent ${msg.agentId}] Runtime profile daemon release notice handling failed`, err);
|
|
9366
|
+
span.end("error", { attrs: { error_class: err instanceof Error ? err.name : typeof err } });
|
|
9367
|
+
});
|
|
8480
9368
|
break;
|
|
8481
9369
|
}
|
|
8482
9370
|
case "agent:workspace:list":
|