@slock-ai/daemon 0.55.6 → 0.56.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-HAZSZDOZ.js → chunk-4H5XFLYA.js} +676 -139
- package/dist/cli/index.js +55 -1
- package/dist/core.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +3 -1
|
@@ -360,6 +360,40 @@ var NoopActiveSpan = class {
|
|
|
360
360
|
}
|
|
361
361
|
};
|
|
362
362
|
var noopTracer = new NoopTracer();
|
|
363
|
+
function createScopedTracer(tracer, scopeAttrs) {
|
|
364
|
+
if (!Object.keys(scopeAttrs).length) return tracer;
|
|
365
|
+
return new ScopedTracer(tracer, scopeAttrs);
|
|
366
|
+
}
|
|
367
|
+
var ScopedTracer = class {
|
|
368
|
+
constructor(tracer, scopeAttrs) {
|
|
369
|
+
this.tracer = tracer;
|
|
370
|
+
this.scopeAttrs = scopeAttrs;
|
|
371
|
+
}
|
|
372
|
+
startSpan(name, options) {
|
|
373
|
+
const span = this.tracer.startSpan(name, {
|
|
374
|
+
...options,
|
|
375
|
+
attrs: mergeAttrs(options.attrs, this.scopeAttrs)
|
|
376
|
+
});
|
|
377
|
+
return new ScopedActiveSpan(span, this.scopeAttrs);
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
var ScopedActiveSpan = class {
|
|
381
|
+
constructor(span, scopeAttrs) {
|
|
382
|
+
this.span = span;
|
|
383
|
+
this.scopeAttrs = scopeAttrs;
|
|
384
|
+
this.context = span.context;
|
|
385
|
+
}
|
|
386
|
+
context;
|
|
387
|
+
addEvent(name, attrs) {
|
|
388
|
+
this.span.addEvent(name, mergeAttrs(attrs, this.scopeAttrs));
|
|
389
|
+
}
|
|
390
|
+
end(status, options) {
|
|
391
|
+
this.span.end(status, options ? {
|
|
392
|
+
...options,
|
|
393
|
+
attrs: mergeAttrs(options.attrs, this.scopeAttrs)
|
|
394
|
+
} : void 0);
|
|
395
|
+
}
|
|
396
|
+
};
|
|
363
397
|
var BasicTracer = class {
|
|
364
398
|
sink;
|
|
365
399
|
clock;
|
|
@@ -904,6 +938,28 @@ var actionCardActionSchema = z.discriminatedUnion("type", [
|
|
|
904
938
|
channelAddMemberOperationSchema
|
|
905
939
|
]);
|
|
906
940
|
|
|
941
|
+
// ../shared/src/agentInbox.ts
|
|
942
|
+
function formatAgentInboxDelta(rows, options = {}) {
|
|
943
|
+
const totalPendingMessages = options.totalPendingMessages;
|
|
944
|
+
const header = typeof totalPendingMessages === "number" ? `Inbox update: ${totalPendingMessages} unread message${totalPendingMessages === 1 ? "" : "s"} total; ${rows.length === 0 ? "no" : rows.length} changed target${rows.length === 1 ? "" : "s"}` : `Inbox update: ${rows.length} changed target${rows.length === 1 ? "" : "s"}`;
|
|
945
|
+
if (rows.length === 0) return typeof totalPendingMessages === "number" ? header : "Inbox update: no pending targets";
|
|
946
|
+
return [
|
|
947
|
+
header,
|
|
948
|
+
...rows.map((row) => `${row.target} ${formatAgentInboxRowDetails(row)}`)
|
|
949
|
+
].join("\n");
|
|
950
|
+
}
|
|
951
|
+
function formatAgentInboxRowDetails(row) {
|
|
952
|
+
const parts = [`pending: ${row.pendingCount} message${row.pendingCount === 1 ? "" : "s"}`];
|
|
953
|
+
if (row.firstPendingMsgId) parts.push(`first msg=${shortMessageId(row.firstPendingMsgId)}`);
|
|
954
|
+
if (row.latestSenderName) parts.push(`latest @${row.latestSenderName}`);
|
|
955
|
+
if (row.latestMsgId) parts.push(`latest msg=${shortMessageId(row.latestMsgId)}`);
|
|
956
|
+
parts.push(...row.flags);
|
|
957
|
+
return parts.join(" \xB7 ");
|
|
958
|
+
}
|
|
959
|
+
function shortMessageId(value) {
|
|
960
|
+
return value.slice(0, 8);
|
|
961
|
+
}
|
|
962
|
+
|
|
907
963
|
// ../shared/src/translationLanguages.ts
|
|
908
964
|
var SUPPORTED_TRANSLATION_LANGUAGE_CODES = [
|
|
909
965
|
"en",
|
|
@@ -996,7 +1052,7 @@ var RUNTIMES = [
|
|
|
996
1052
|
{ id: "cursor", displayName: "Cursor CLI", binary: "cursor-agent", supported: true },
|
|
997
1053
|
{ id: "gemini", displayName: "Gemini CLI", binary: "gemini", supported: true },
|
|
998
1054
|
{ id: "opencode", displayName: "OpenCode", binary: "opencode", supported: true },
|
|
999
|
-
{ id: "pi", displayName: "Pi
|
|
1055
|
+
{ id: "pi", displayName: "Pi", binary: "pi", supported: true }
|
|
1000
1056
|
];
|
|
1001
1057
|
var RUNTIME_MODELS = {
|
|
1002
1058
|
claude: [
|
|
@@ -1049,9 +1105,7 @@ var RUNTIME_MODELS = {
|
|
|
1049
1105
|
{ id: "fusecode/opus[1m]", label: "Opus 1M via FuseCode", verified: "suggestion_only" }
|
|
1050
1106
|
],
|
|
1051
1107
|
pi: [
|
|
1052
|
-
{ id: "default", label: "Configured Default / Auto", verified: "suggestion_only" }
|
|
1053
|
-
{ id: "deepseek/deepseek-v4-pro", label: "DeepSeek V4 Pro (Pi)", verified: "suggestion_only" },
|
|
1054
|
-
{ id: "deepseek/deepseek-v4-flash", label: "DeepSeek V4 Flash (Pi)", verified: "suggestion_only" }
|
|
1108
|
+
{ id: "default", label: "Configured Default / Auto", verified: "suggestion_only" }
|
|
1055
1109
|
],
|
|
1056
1110
|
// Kimi CLI resolves model keys from each user's local config, so the safest
|
|
1057
1111
|
// built-in option is to defer to whatever default model the CLI already uses.
|
|
@@ -1208,7 +1262,7 @@ var DISPLAY_PLAN_CONFIG = {
|
|
|
1208
1262
|
};
|
|
1209
1263
|
|
|
1210
1264
|
// src/agentProcessManager.ts
|
|
1211
|
-
import { mkdirSync as mkdirSync5, readdirSync, statSync, writeFileSync as writeFileSync7 } from "fs";
|
|
1265
|
+
import { mkdirSync as mkdirSync5, readdirSync as readdirSync2, statSync, writeFileSync as writeFileSync7 } from "fs";
|
|
1212
1266
|
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
|
|
1213
1267
|
import { createHash as createHash3 } from "crypto";
|
|
1214
1268
|
import path13 from "path";
|
|
@@ -1253,6 +1307,7 @@ function buildPrompt(config, variant, opts) {
|
|
|
1253
1307
|
const sendCmd = isCli ? "`slock message send`" : `\`${t("send_message")}\``;
|
|
1254
1308
|
const readCmd = isCli ? "`slock message read`" : `\`${t("read_history")}\``;
|
|
1255
1309
|
const checkCmd = isCli ? "`slock message check`" : `\`${t("check_messages")}\``;
|
|
1310
|
+
const inboxCheckCmd = isCli ? "`slock inbox check`" : checkCmd;
|
|
1256
1311
|
const taskClaimCmd = isCli ? "`slock task claim`" : `\`${t("claim_tasks")}\``;
|
|
1257
1312
|
const taskCreateCmd = isCli ? "`slock task create`" : `\`${t("create_tasks")}\``;
|
|
1258
1313
|
const taskUpdateCmd = isCli ? "`slock task update`" : `\`${t("update_task_status")}\``;
|
|
@@ -1733,14 +1788,14 @@ You may develop a specialized role over time through your interactions. Embrace
|
|
|
1733
1788
|
|
|
1734
1789
|
## Message Notifications
|
|
1735
1790
|
|
|
1736
|
-
While you are working, the daemon may write a batched
|
|
1791
|
+
While you are working, the daemon may write a batched, content-free inbox update into your current turn.
|
|
1737
1792
|
|
|
1738
1793
|
How to handle these:
|
|
1739
|
-
- Treat the notification as a signal that new Slock messages are waiting; it does not include the message content.
|
|
1740
|
-
-
|
|
1741
|
-
- If
|
|
1794
|
+
- Treat the notification as a non-urgent signal that new Slock messages are waiting; it does not include the message content and does not require an immediate interruption.
|
|
1795
|
+
- Keep working until a natural breakpoint. If you then choose to inspect pending targets, call ${inboxCheckCmd}; use ${checkCmd} / ${readCmd} when you choose to inspect message content.
|
|
1796
|
+
- If a message you explicitly read is higher priority, pivot to it. If not, continue your current work.`;
|
|
1742
1797
|
} else {
|
|
1743
|
-
const notifyExample = isCli ? `\`[Slock inbox notice:
|
|
1798
|
+
const notifyExample = isCli ? `\`[Slock inbox notice:\\nInbox update: N unread messages total; M changed targets\\n#channel pending: K messages ...]\`` : `\`[Slock inbox notice:\\nInbox update: N unread messages total; M changed targets\\n#channel pending: K messages ...]\``;
|
|
1744
1799
|
prompt += `
|
|
1745
1800
|
|
|
1746
1801
|
## Message Notifications
|
|
@@ -1750,9 +1805,9 @@ While you are busy (executing tools, thinking, etc.), new messages may arrive. W
|
|
|
1750
1805
|
${notifyExample}
|
|
1751
1806
|
|
|
1752
1807
|
How to handle these:
|
|
1753
|
-
-
|
|
1754
|
-
-
|
|
1755
|
-
-
|
|
1808
|
+
- The notice is target-scoped and content-free. Its header may show total unread/pending count, while detail rows list the targets changed by this update; it never includes message bodies.
|
|
1809
|
+
- Do not interrupt the current step just because a notice arrived. At a natural breakpoint, call ${inboxCheckCmd} if you choose to inspect pending targets, or use ${checkCmd} / ${readCmd} when you choose to inspect message content.
|
|
1810
|
+
- If a message you explicitly read is higher priority, you may pivot to it. If not, continue your current work.`;
|
|
1756
1811
|
}
|
|
1757
1812
|
}
|
|
1758
1813
|
if (config.description) {
|
|
@@ -2061,6 +2116,80 @@ function reduceApmGatedFlushReadiness(state, input) {
|
|
|
2061
2116
|
};
|
|
2062
2117
|
}
|
|
2063
2118
|
|
|
2119
|
+
// src/agentInboxProjection.ts
|
|
2120
|
+
function projectAgentInboxSnapshot(messages) {
|
|
2121
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
2122
|
+
for (const message of messages) {
|
|
2123
|
+
const target = formatInboxMessageTarget(message);
|
|
2124
|
+
if (!target) continue;
|
|
2125
|
+
const bucket = buckets.get(target) ?? [];
|
|
2126
|
+
bucket.push(message);
|
|
2127
|
+
buckets.set(target, bucket);
|
|
2128
|
+
}
|
|
2129
|
+
return [...buckets.entries()].map(([target, bucket]) => projectBucket(target, bucket)).sort((a, b) => (b.latestSeq ?? 0) - (a.latestSeq ?? 0) || a.target.localeCompare(b.target));
|
|
2130
|
+
}
|
|
2131
|
+
function projectBucket(target, messages) {
|
|
2132
|
+
const sorted = [...messages].sort(compareInboxMessages);
|
|
2133
|
+
const first = sorted[0];
|
|
2134
|
+
const latest = sorted[sorted.length - 1];
|
|
2135
|
+
const flags = /* @__PURE__ */ new Set();
|
|
2136
|
+
for (const message of messages) {
|
|
2137
|
+
if (message.channel_type === "thread") flags.add("thread");
|
|
2138
|
+
if (message.channel_type === "dm") flags.add("dm");
|
|
2139
|
+
if (message.task_number || message.task_status) flags.add("task");
|
|
2140
|
+
if (message.mention === true || message.mentioned === true) flags.add("mention");
|
|
2141
|
+
}
|
|
2142
|
+
return stripUndefined({
|
|
2143
|
+
target,
|
|
2144
|
+
channelId: latest.channel_id ?? latest.parent_channel_id,
|
|
2145
|
+
channelType: latest.channel_type,
|
|
2146
|
+
pendingCount: messages.length,
|
|
2147
|
+
firstPendingMsgId: messageId(first),
|
|
2148
|
+
firstPendingSeq: messageSeq(first),
|
|
2149
|
+
latestMsgId: messageId(latest),
|
|
2150
|
+
latestSeq: messageSeq(latest),
|
|
2151
|
+
latestSenderName: latest.sender_name ?? latest.senderName,
|
|
2152
|
+
latestSenderType: normalizeSenderType(latest.sender_type ?? latest.senderType),
|
|
2153
|
+
flags: [...flags].sort()
|
|
2154
|
+
});
|
|
2155
|
+
}
|
|
2156
|
+
function compareInboxMessages(a, b) {
|
|
2157
|
+
return (messageSeq(a) ?? 0) - (messageSeq(b) ?? 0) || (messageId(a) ?? "").localeCompare(messageId(b) ?? "");
|
|
2158
|
+
}
|
|
2159
|
+
function formatInboxMessageTarget(message) {
|
|
2160
|
+
if (message.channel_type === "thread" && message.parent_channel_name && message.channel_name) {
|
|
2161
|
+
const shortId = shortMessageId2(String(message.channel_name).startsWith("thread-") ? String(message.channel_name).slice("thread-".length) : String(message.channel_name));
|
|
2162
|
+
if (message.parent_channel_type === "dm") return `dm:@${message.parent_channel_name}:${shortId}`;
|
|
2163
|
+
return `#${message.parent_channel_name}:${shortId}`;
|
|
2164
|
+
}
|
|
2165
|
+
if (message.channel_type === "dm" && message.channel_name) return `dm:@${message.channel_name}`;
|
|
2166
|
+
if (message.channel_name) return `#${message.channel_name}`;
|
|
2167
|
+
return null;
|
|
2168
|
+
}
|
|
2169
|
+
function messageId(message) {
|
|
2170
|
+
if (!message) return void 0;
|
|
2171
|
+
return nonEmptyString(message.message_id) ?? nonEmptyString(message.id);
|
|
2172
|
+
}
|
|
2173
|
+
function messageSeq(message) {
|
|
2174
|
+
if (!message || typeof message.seq !== "number" || !Number.isFinite(message.seq) || message.seq <= 0) return void 0;
|
|
2175
|
+
return Math.floor(message.seq);
|
|
2176
|
+
}
|
|
2177
|
+
function shortMessageId2(value) {
|
|
2178
|
+
return value.slice(0, 8);
|
|
2179
|
+
}
|
|
2180
|
+
function normalizeSenderType(value) {
|
|
2181
|
+
return value === "human" || value === "agent" || value === "system" ? value : void 0;
|
|
2182
|
+
}
|
|
2183
|
+
function nonEmptyString(value) {
|
|
2184
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
2185
|
+
}
|
|
2186
|
+
function stripUndefined(value) {
|
|
2187
|
+
for (const key of Object.keys(value)) {
|
|
2188
|
+
if (value[key] === void 0) delete value[key];
|
|
2189
|
+
}
|
|
2190
|
+
return value;
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2064
2193
|
// src/agentCredentialProxy.ts
|
|
2065
2194
|
var registrations = /* @__PURE__ */ new Map();
|
|
2066
2195
|
var proxyServerState = null;
|
|
@@ -2204,6 +2333,14 @@ async function handleProxyRequest(req, res) {
|
|
|
2204
2333
|
}
|
|
2205
2334
|
let sendTarget;
|
|
2206
2335
|
const sideEffectAction = agentApiSideEffectAction(target.pathname);
|
|
2336
|
+
if (method === "GET" && target.pathname === "/internal/agent-api/inbox") {
|
|
2337
|
+
const localInbox = localAgentApiInboxResponse(registration);
|
|
2338
|
+
if (localInbox) {
|
|
2339
|
+
res.writeHead(localInbox.status, { "content-type": "application/json" });
|
|
2340
|
+
res.end(JSON.stringify(localInbox.body));
|
|
2341
|
+
return;
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2207
2344
|
if (method === "GET" && target.pathname === "/internal/agent-api/events") {
|
|
2208
2345
|
const localEvents = localAgentApiEventsResponse(registration, target);
|
|
2209
2346
|
if (localEvents) {
|
|
@@ -2347,6 +2484,7 @@ function transportNormalizedErrorForError(target, err, launchId) {
|
|
|
2347
2484
|
function routeFamilyForPath(pathname) {
|
|
2348
2485
|
if (pathname === "/internal/agent-api/send") return "agent-api/send";
|
|
2349
2486
|
if (pathname === "/internal/agent-api/events") return "agent-api/events";
|
|
2487
|
+
if (pathname === "/internal/agent-api/inbox") return "agent-api/inbox";
|
|
2350
2488
|
if (pathname === "/internal/agent-api/receive-ack") return "agent-api/events";
|
|
2351
2489
|
if (pathname === "/internal/agent-api/tasks/claim") return "tasks/claim";
|
|
2352
2490
|
if (pathname === "/internal/agent-api/tasks/update-status") return "tasks/update";
|
|
@@ -2403,13 +2541,13 @@ async function readRequestBody(req) {
|
|
|
2403
2541
|
}
|
|
2404
2542
|
return Buffer.concat(chunks);
|
|
2405
2543
|
}
|
|
2406
|
-
function
|
|
2544
|
+
function messageSeq2(message) {
|
|
2407
2545
|
return Number(message.seq ?? 0);
|
|
2408
2546
|
}
|
|
2409
2547
|
function maxMessageSeq(messages) {
|
|
2410
2548
|
let maxSeq = 0;
|
|
2411
2549
|
for (const message of messages) {
|
|
2412
|
-
const seq = Math.floor(
|
|
2550
|
+
const seq = Math.floor(messageSeq2(message));
|
|
2413
2551
|
if (Number.isFinite(seq) && seq > 0) maxSeq = Math.max(maxSeq, seq);
|
|
2414
2552
|
}
|
|
2415
2553
|
return maxSeq > 0 ? maxSeq : void 0;
|
|
@@ -2423,7 +2561,7 @@ function isSelfAuthoredMessage(registration, message) {
|
|
|
2423
2561
|
return messageSenderId(message) === registration.agentId;
|
|
2424
2562
|
}
|
|
2425
2563
|
function isMessageModelSeen(coordinator, target, message) {
|
|
2426
|
-
const seq = Math.floor(
|
|
2564
|
+
const seq = Math.floor(messageSeq2(message));
|
|
2427
2565
|
const boundary = coordinator.getBoundary(target);
|
|
2428
2566
|
if (Number.isFinite(seq) && seq > 0 && typeof boundary === "number" && boundary >= seq) return true;
|
|
2429
2567
|
return coordinator.isMessageModelSeen?.({ target, message }) === true;
|
|
@@ -2433,12 +2571,31 @@ function resolveFreshnessBoundary(messages) {
|
|
|
2433
2571
|
return typeof seenUpToSeq === "number" ? { ok: true, seenUpToSeq } : { ok: false, reason: "missing_seq_boundary" };
|
|
2434
2572
|
}
|
|
2435
2573
|
function sortBySeq(messages) {
|
|
2436
|
-
return [...messages].sort((a, b) =>
|
|
2574
|
+
return [...messages].sort((a, b) => messageSeq2(a) - messageSeq2(b));
|
|
2437
2575
|
}
|
|
2438
2576
|
function latestVisibleMessages(messages, limit) {
|
|
2439
2577
|
const sorted = sortBySeq(messages);
|
|
2440
2578
|
return sorted.slice(Math.max(0, sorted.length - limit));
|
|
2441
2579
|
}
|
|
2580
|
+
function localAgentApiInboxResponse(registration) {
|
|
2581
|
+
const coordinator = registration.inboxCoordinator;
|
|
2582
|
+
if (!coordinator) return void 0;
|
|
2583
|
+
const pending = coordinator.getAllPendingMessages?.() ?? [];
|
|
2584
|
+
const rows = projectAgentInboxSnapshot(pending);
|
|
2585
|
+
coordinator.recordInboxSnapshot?.({
|
|
2586
|
+
source: "agent_api_inbox_check",
|
|
2587
|
+
rows,
|
|
2588
|
+
pendingMessageCount: pending.length
|
|
2589
|
+
});
|
|
2590
|
+
return {
|
|
2591
|
+
status: 200,
|
|
2592
|
+
body: {
|
|
2593
|
+
rows,
|
|
2594
|
+
pending_targets: rows.length,
|
|
2595
|
+
pending_messages: pending.length
|
|
2596
|
+
}
|
|
2597
|
+
};
|
|
2598
|
+
}
|
|
2442
2599
|
function parseAgentApiEventsQuery(target) {
|
|
2443
2600
|
const limit = Math.min(Math.max(Number(target.searchParams.get("limit")) || 50, 1), 200);
|
|
2444
2601
|
const sinceRaw = target.searchParams.get("since")?.trim() ?? "";
|
|
@@ -2469,7 +2626,7 @@ function localAgentApiEventsResponse(registration, target) {
|
|
|
2469
2626
|
if (pending.length === 0) return void 0;
|
|
2470
2627
|
const normalized = sortBySeq(normalizeVisibleMessages(pending));
|
|
2471
2628
|
const filtered = parsedQuery.sinceSeq !== null ? normalized.filter((message) => {
|
|
2472
|
-
const seq =
|
|
2629
|
+
const seq = messageSeq2(message);
|
|
2473
2630
|
return Number.isFinite(seq) && seq > parsedQuery.sinceSeq;
|
|
2474
2631
|
}) : normalized;
|
|
2475
2632
|
const events = filtered.slice(0, parsedQuery.limit);
|
|
@@ -2794,12 +2951,12 @@ function consumeVisibleResponse(registration, targetUrl, sendTarget, responseTex
|
|
|
2794
2951
|
return;
|
|
2795
2952
|
}
|
|
2796
2953
|
if (targetUrl.pathname === "/internal/agent-api/send" && parsed.state === "sent") {
|
|
2797
|
-
const
|
|
2798
|
-
if (sendTarget &&
|
|
2954
|
+
const messageSeq3 = typeof parsed.messageSeq === "number" && Number.isFinite(parsed.messageSeq) ? Math.floor(parsed.messageSeq) : void 0;
|
|
2955
|
+
if (sendTarget && messageSeq3 && messageSeq3 > 0) {
|
|
2799
2956
|
coordinator.consumeVisibleMessages({
|
|
2800
2957
|
target: sendTarget,
|
|
2801
|
-
messages: normalizeVisibleMessages([{ seq:
|
|
2802
|
-
boundarySeq:
|
|
2958
|
+
messages: normalizeVisibleMessages([{ seq: messageSeq3, id: parsed.messageId }], sendTarget),
|
|
2959
|
+
boundarySeq: messageSeq3,
|
|
2803
2960
|
source: "agent_api_send_commit"
|
|
2804
2961
|
});
|
|
2805
2962
|
}
|
|
@@ -3873,7 +4030,7 @@ function rawResponseItemProgressEvent(message) {
|
|
|
3873
4030
|
payloadBytes
|
|
3874
4031
|
};
|
|
3875
4032
|
}
|
|
3876
|
-
function
|
|
4033
|
+
function nonEmptyString2(value) {
|
|
3877
4034
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
3878
4035
|
}
|
|
3879
4036
|
var CodexEventNormalizer = class {
|
|
@@ -3928,8 +4085,8 @@ var CodexEventNormalizer = class {
|
|
|
3928
4085
|
}
|
|
3929
4086
|
const telemetry = parseCodexTelemetryEvent(message);
|
|
3930
4087
|
if (telemetry) {
|
|
3931
|
-
const telemetrySessionId =
|
|
3932
|
-
const telemetryTurnId =
|
|
4088
|
+
const telemetrySessionId = nonEmptyString2(message.params?.threadId) ?? nonEmptyString2(message.params?.thread?.id) ?? nonEmptyString2(message.params?.sessionId);
|
|
4089
|
+
const telemetryTurnId = nonEmptyString2(message.params?.turnId) ?? nonEmptyString2(message.params?.turn?.id);
|
|
3933
4090
|
if (telemetrySessionId) {
|
|
3934
4091
|
this.currentThreadId = telemetrySessionId;
|
|
3935
4092
|
}
|
|
@@ -5986,12 +6143,23 @@ var OpenCodeDriver = class {
|
|
|
5986
6143
|
|
|
5987
6144
|
// src/drivers/pi.ts
|
|
5988
6145
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
5989
|
-
import {
|
|
5990
|
-
import { mkdirSync as mkdirSync4 } from "fs";
|
|
6146
|
+
import { EventEmitter } from "events";
|
|
6147
|
+
import { mkdirSync as mkdirSync4, readdirSync } from "fs";
|
|
6148
|
+
import { PassThrough, Writable } from "stream";
|
|
5991
6149
|
import path11 from "path";
|
|
6150
|
+
import {
|
|
6151
|
+
AuthStorage,
|
|
6152
|
+
createBashTool,
|
|
6153
|
+
createAgentSession,
|
|
6154
|
+
DefaultResourceLoader,
|
|
6155
|
+
getAgentDir,
|
|
6156
|
+
ModelRegistry,
|
|
6157
|
+
SessionManager,
|
|
6158
|
+
SettingsManager,
|
|
6159
|
+
VERSION as PI_SDK_VERSION
|
|
6160
|
+
} from "@earendil-works/pi-coding-agent";
|
|
5992
6161
|
var PI_SESSION_DIR = ".pi-sessions";
|
|
5993
6162
|
var PI_PROVIDER_LABELS = {
|
|
5994
|
-
deepseek: "DeepSeek",
|
|
5995
6163
|
google: "Google",
|
|
5996
6164
|
openai: "OpenAI",
|
|
5997
6165
|
openrouter: "OpenRouter"
|
|
@@ -5999,69 +6167,51 @@ var PI_PROVIDER_LABELS = {
|
|
|
5999
6167
|
function buildPiSessionDir(workingDirectory) {
|
|
6000
6168
|
return path11.join(workingDirectory, PI_SESSION_DIR);
|
|
6001
6169
|
}
|
|
6002
|
-
function
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
"
|
|
6010
|
-
ctx.standingPrompt
|
|
6011
|
-
];
|
|
6012
|
-
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
6013
|
-
args.push("--model", launchRuntimeFields.model);
|
|
6014
|
-
}
|
|
6015
|
-
if (launchRuntimeFields.reasoningEffort) {
|
|
6016
|
-
args.push("--thinking", launchRuntimeFields.reasoningEffort);
|
|
6170
|
+
async function buildPiSpawnEnv(ctx) {
|
|
6171
|
+
return (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
|
|
6172
|
+
}
|
|
6173
|
+
function resolvePiModelFromRegistry(modelId, modelRegistry) {
|
|
6174
|
+
if (!modelId || modelId === "default") return void 0;
|
|
6175
|
+
const [provider, ...modelParts] = modelId.split("/");
|
|
6176
|
+
if (provider && modelParts.length > 0) {
|
|
6177
|
+
return modelRegistry.find(provider, modelParts.join("/"));
|
|
6017
6178
|
}
|
|
6018
|
-
|
|
6019
|
-
|
|
6179
|
+
return modelRegistry.getAll().find((model) => model.id === modelId);
|
|
6180
|
+
}
|
|
6181
|
+
function findPiSessionFile(sessionDir, sessionId) {
|
|
6182
|
+
let entries;
|
|
6183
|
+
try {
|
|
6184
|
+
entries = readdirSync(sessionDir);
|
|
6185
|
+
} catch {
|
|
6186
|
+
return null;
|
|
6020
6187
|
}
|
|
6021
|
-
|
|
6188
|
+
const suffix = `_${sessionId}.jsonl`;
|
|
6189
|
+
const match = entries.find((entry) => entry.endsWith(suffix));
|
|
6190
|
+
return match ? path11.join(sessionDir, match) : null;
|
|
6022
6191
|
}
|
|
6023
|
-
|
|
6024
|
-
|
|
6192
|
+
function piSdkEventToJsonLine(event) {
|
|
6193
|
+
if (event.type === "agent_end") {
|
|
6194
|
+
return JSON.stringify({ type: "agent_end" });
|
|
6195
|
+
}
|
|
6196
|
+
return JSON.stringify(event);
|
|
6025
6197
|
}
|
|
6026
|
-
function
|
|
6027
|
-
const stripAnsi = (value) => value.replace(/\u001b\[[0-9;]*m/g, "");
|
|
6198
|
+
function detectPiModelsFromRegistry(modelRegistry) {
|
|
6028
6199
|
const models = [];
|
|
6029
6200
|
const seen = /* @__PURE__ */ new Set();
|
|
6030
|
-
for (const
|
|
6031
|
-
const
|
|
6032
|
-
if (!line || /^provider\s+model\s+/i.test(line) || /^[-\s]+$/.test(line)) continue;
|
|
6033
|
-
const columns = line.split(/\s+/);
|
|
6034
|
-
const provider = columns[0];
|
|
6035
|
-
const model = columns[1];
|
|
6036
|
-
if (!provider || !model || provider.startsWith("-") || model.startsWith("-")) continue;
|
|
6037
|
-
if (/^(yes|no)$/i.test(model)) continue;
|
|
6038
|
-
const id = `${provider}/${model}`;
|
|
6201
|
+
for (const model of modelRegistry.getAvailable()) {
|
|
6202
|
+
const id = `${model.provider}/${model.id}`;
|
|
6039
6203
|
if (seen.has(id)) continue;
|
|
6040
6204
|
seen.add(id);
|
|
6041
6205
|
models.push({
|
|
6042
6206
|
id,
|
|
6043
|
-
label: `${humanizePiSegment(model)} \xB7 ${PI_PROVIDER_LABELS[provider] || humanizePiSegment(provider)}`,
|
|
6207
|
+
label: `${model.name || humanizePiSegment(model.id)} \xB7 ${PI_PROVIDER_LABELS[model.provider] || humanizePiSegment(model.provider)}`,
|
|
6044
6208
|
verified: "launchable"
|
|
6045
6209
|
});
|
|
6046
6210
|
}
|
|
6047
6211
|
return models.length > 0 ? { models } : null;
|
|
6048
6212
|
}
|
|
6049
|
-
function detectPiModels(
|
|
6050
|
-
|
|
6051
|
-
if (result.error || result.status !== 0) return null;
|
|
6052
|
-
return parsePiModelsOutput(result.stdout);
|
|
6053
|
-
}
|
|
6054
|
-
function runPiModelsCommand() {
|
|
6055
|
-
const result = spawnSync3("pi", ["--list-models"], {
|
|
6056
|
-
env: { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
6057
|
-
encoding: "utf8",
|
|
6058
|
-
timeout: 5e3
|
|
6059
|
-
});
|
|
6060
|
-
return {
|
|
6061
|
-
status: result.status,
|
|
6062
|
-
stdout: String(result.stdout || ""),
|
|
6063
|
-
error: result.error
|
|
6064
|
-
};
|
|
6213
|
+
function detectPiModels(modelRegistry = ModelRegistry.create(AuthStorage.create())) {
|
|
6214
|
+
return detectPiModelsFromRegistry(modelRegistry);
|
|
6065
6215
|
}
|
|
6066
6216
|
function humanizePiSegment(value) {
|
|
6067
6217
|
return value.split(/[-_/]/).filter(Boolean).map(formatPiLabelToken).join(" ");
|
|
@@ -6071,7 +6221,6 @@ function formatPiLabelToken(token) {
|
|
|
6071
6221
|
const specialCases = {
|
|
6072
6222
|
ai: "AI",
|
|
6073
6223
|
api: "API",
|
|
6074
|
-
deepseek: "DeepSeek",
|
|
6075
6224
|
flash: "Flash",
|
|
6076
6225
|
gpt: "GPT",
|
|
6077
6226
|
pro: "Pro",
|
|
@@ -6095,6 +6244,116 @@ function piErrorMessage(error) {
|
|
|
6095
6244
|
}
|
|
6096
6245
|
return "Unknown Pi error";
|
|
6097
6246
|
}
|
|
6247
|
+
var PiSdkProcess = class extends EventEmitter {
|
|
6248
|
+
constructor(session) {
|
|
6249
|
+
super();
|
|
6250
|
+
this.session = session;
|
|
6251
|
+
this.stdin = new Writable({
|
|
6252
|
+
write: (chunk, _encoding, callback) => {
|
|
6253
|
+
this.handleInput(String(chunk)).then(
|
|
6254
|
+
() => callback(),
|
|
6255
|
+
(error) => {
|
|
6256
|
+
this.writeError(error);
|
|
6257
|
+
callback();
|
|
6258
|
+
}
|
|
6259
|
+
);
|
|
6260
|
+
}
|
|
6261
|
+
});
|
|
6262
|
+
this.session.subscribe((event) => {
|
|
6263
|
+
this.writeStdout(piSdkEventToJsonLine(event));
|
|
6264
|
+
});
|
|
6265
|
+
}
|
|
6266
|
+
stdout = new PassThrough();
|
|
6267
|
+
stderr = new PassThrough();
|
|
6268
|
+
stdin;
|
|
6269
|
+
pid = void 0;
|
|
6270
|
+
exitCode = null;
|
|
6271
|
+
signalCode = null;
|
|
6272
|
+
killed = false;
|
|
6273
|
+
buffer = "";
|
|
6274
|
+
closed = false;
|
|
6275
|
+
kill(signal = "SIGTERM") {
|
|
6276
|
+
if (this.closed) return false;
|
|
6277
|
+
this.killed = true;
|
|
6278
|
+
this.signalCode = typeof signal === "string" ? signal : null;
|
|
6279
|
+
void this.shutdown(null, this.signalCode);
|
|
6280
|
+
return true;
|
|
6281
|
+
}
|
|
6282
|
+
ref() {
|
|
6283
|
+
return this;
|
|
6284
|
+
}
|
|
6285
|
+
unref() {
|
|
6286
|
+
return this;
|
|
6287
|
+
}
|
|
6288
|
+
async handleInput(chunk) {
|
|
6289
|
+
this.buffer += chunk;
|
|
6290
|
+
const lines = this.buffer.split("\n");
|
|
6291
|
+
this.buffer = lines.pop() || "";
|
|
6292
|
+
for (const line of lines) {
|
|
6293
|
+
if (!line.trim() || this.closed) continue;
|
|
6294
|
+
await this.handleCommand(line);
|
|
6295
|
+
}
|
|
6296
|
+
}
|
|
6297
|
+
async handleCommand(raw) {
|
|
6298
|
+
let command;
|
|
6299
|
+
try {
|
|
6300
|
+
command = JSON.parse(raw);
|
|
6301
|
+
} catch (error) {
|
|
6302
|
+
this.writeError(error);
|
|
6303
|
+
return;
|
|
6304
|
+
}
|
|
6305
|
+
const id = typeof command.id === "string" ? command.id : void 0;
|
|
6306
|
+
try {
|
|
6307
|
+
if (command.type === "prompt") {
|
|
6308
|
+
await this.session.prompt(String(command.message ?? ""));
|
|
6309
|
+
this.writeStdout(JSON.stringify({ id, type: "response", command: "prompt", success: true }));
|
|
6310
|
+
} else if (command.type === "steer") {
|
|
6311
|
+
await this.session.steer(String(command.message ?? ""));
|
|
6312
|
+
this.writeStdout(JSON.stringify({ id, type: "response", command: "steer", success: true }));
|
|
6313
|
+
} else {
|
|
6314
|
+
throw new Error(`Unsupported Pi SDK command: ${command.type || "unknown"}`);
|
|
6315
|
+
}
|
|
6316
|
+
} catch (error) {
|
|
6317
|
+
this.writeStdout(JSON.stringify({
|
|
6318
|
+
id,
|
|
6319
|
+
type: "response",
|
|
6320
|
+
command: command.type || "unknown",
|
|
6321
|
+
success: false,
|
|
6322
|
+
error: piErrorMessage(error)
|
|
6323
|
+
}));
|
|
6324
|
+
}
|
|
6325
|
+
}
|
|
6326
|
+
writeStdout(line) {
|
|
6327
|
+
if (this.closed) return;
|
|
6328
|
+
this.stdout.write(line + "\n");
|
|
6329
|
+
}
|
|
6330
|
+
writeError(error) {
|
|
6331
|
+
if (this.closed) return;
|
|
6332
|
+
this.stderr.write(piErrorMessage(error) + "\n");
|
|
6333
|
+
}
|
|
6334
|
+
async shutdown(code, signal) {
|
|
6335
|
+
if (this.closed) return;
|
|
6336
|
+
this.closed = true;
|
|
6337
|
+
this.exitCode = code;
|
|
6338
|
+
this.signalCode = signal;
|
|
6339
|
+
try {
|
|
6340
|
+
if (this.session.isStreaming) {
|
|
6341
|
+
await this.session.abort();
|
|
6342
|
+
}
|
|
6343
|
+
} catch (error) {
|
|
6344
|
+
this.stderr.write(piErrorMessage(error) + "\n");
|
|
6345
|
+
}
|
|
6346
|
+
try {
|
|
6347
|
+
this.session.dispose();
|
|
6348
|
+
} catch {
|
|
6349
|
+
}
|
|
6350
|
+
this.stdin.destroy();
|
|
6351
|
+
this.stdout.end();
|
|
6352
|
+
this.stderr.end();
|
|
6353
|
+
this.emit("exit", code, signal);
|
|
6354
|
+
this.emit("close", code, signal);
|
|
6355
|
+
}
|
|
6356
|
+
};
|
|
6098
6357
|
var PiDriver = class {
|
|
6099
6358
|
id = "pi";
|
|
6100
6359
|
supportsNativeStandingPrompt = true;
|
|
@@ -6112,7 +6371,7 @@ var PiDriver = class {
|
|
|
6112
6371
|
};
|
|
6113
6372
|
model = {
|
|
6114
6373
|
detectedModelsVerifiedAs: "launchable",
|
|
6115
|
-
toLaunchSpec: (modelId) => ({
|
|
6374
|
+
toLaunchSpec: (modelId) => ({ params: { model: modelId } })
|
|
6116
6375
|
};
|
|
6117
6376
|
supportsStdinNotification = true;
|
|
6118
6377
|
mcpToolPrefix = "";
|
|
@@ -6124,11 +6383,9 @@ var PiDriver = class {
|
|
|
6124
6383
|
requestId = 0;
|
|
6125
6384
|
process = null;
|
|
6126
6385
|
probe() {
|
|
6127
|
-
const command = resolveCommandOnPath("pi");
|
|
6128
|
-
if (!command) return { available: false };
|
|
6129
6386
|
return {
|
|
6130
6387
|
available: true,
|
|
6131
|
-
version:
|
|
6388
|
+
version: PI_SDK_VERSION
|
|
6132
6389
|
};
|
|
6133
6390
|
}
|
|
6134
6391
|
async detectModels() {
|
|
@@ -6139,15 +6396,57 @@ var PiDriver = class {
|
|
|
6139
6396
|
this.sessionAnnounced = false;
|
|
6140
6397
|
this.sawTextDelta = false;
|
|
6141
6398
|
this.requestId = 0;
|
|
6142
|
-
|
|
6143
|
-
|
|
6399
|
+
const sessionDir = buildPiSessionDir(ctx.workingDirectory);
|
|
6400
|
+
mkdirSync4(sessionDir, { recursive: true });
|
|
6401
|
+
const spawnEnv = await buildPiSpawnEnv(ctx);
|
|
6402
|
+
const agentDir = spawnEnv.PI_CODING_AGENT_DIR || getAgentDir();
|
|
6403
|
+
const authStorage = AuthStorage.create(path11.join(agentDir, "auth.json"));
|
|
6404
|
+
const modelRegistry = ModelRegistry.create(authStorage, path11.join(agentDir, "models.json"));
|
|
6405
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
6406
|
+
const model = resolvePiModelFromRegistry(launchRuntimeFields.model, modelRegistry);
|
|
6407
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default" && !model) {
|
|
6408
|
+
throw new Error(`Pi model not found: ${launchRuntimeFields.model}`);
|
|
6409
|
+
}
|
|
6410
|
+
const settingsManager = SettingsManager.inMemory({ compaction: { enabled: false } });
|
|
6411
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
6144
6412
|
cwd: ctx.workingDirectory,
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6413
|
+
agentDir,
|
|
6414
|
+
settingsManager,
|
|
6415
|
+
systemPromptOverride: () => ctx.standingPrompt
|
|
6148
6416
|
});
|
|
6417
|
+
await resourceLoader.reload();
|
|
6418
|
+
const existingSessionFile = ctx.config.sessionId ? findPiSessionFile(sessionDir, ctx.config.sessionId) : null;
|
|
6419
|
+
const sessionManager = existingSessionFile ? SessionManager.open(existingSessionFile, sessionDir, ctx.workingDirectory) : SessionManager.create(ctx.workingDirectory, sessionDir, { id: this.sessionId });
|
|
6420
|
+
const { session } = await createAgentSession({
|
|
6421
|
+
cwd: ctx.workingDirectory,
|
|
6422
|
+
agentDir,
|
|
6423
|
+
model,
|
|
6424
|
+
thinkingLevel: launchRuntimeFields.reasoningEffort,
|
|
6425
|
+
authStorage,
|
|
6426
|
+
modelRegistry,
|
|
6427
|
+
resourceLoader,
|
|
6428
|
+
customTools: [
|
|
6429
|
+
createBashTool(ctx.workingDirectory, {
|
|
6430
|
+
spawnHook: (spawnContext) => ({
|
|
6431
|
+
...spawnContext,
|
|
6432
|
+
env: {
|
|
6433
|
+
...spawnContext.env,
|
|
6434
|
+
...spawnEnv
|
|
6435
|
+
}
|
|
6436
|
+
})
|
|
6437
|
+
})
|
|
6438
|
+
],
|
|
6439
|
+
sessionManager,
|
|
6440
|
+
settingsManager
|
|
6441
|
+
});
|
|
6442
|
+
this.sessionId = session.sessionId;
|
|
6443
|
+
const proc = new PiSdkProcess(session);
|
|
6149
6444
|
this.process = proc;
|
|
6150
|
-
|
|
6445
|
+
setImmediate(() => {
|
|
6446
|
+
if (this.process === proc && !proc.killed) {
|
|
6447
|
+
this.sendRpcCommand("prompt", { message: ctx.prompt });
|
|
6448
|
+
}
|
|
6449
|
+
});
|
|
6151
6450
|
return { process: proc };
|
|
6152
6451
|
}
|
|
6153
6452
|
parseLine(line) {
|
|
@@ -6236,7 +6535,7 @@ var PiDriver = class {
|
|
|
6236
6535
|
toolPrefix: "",
|
|
6237
6536
|
extraCriticalRules: [],
|
|
6238
6537
|
postStartupNotes: [
|
|
6239
|
-
"**Pi runtime note:** Slock keeps Pi running
|
|
6538
|
+
"**Pi runtime note:** Slock keeps Pi running as a persistent SDK session. While you are working, Slock may send inbox-count notifications into the current turn; call `slock message check` at natural breakpoints."
|
|
6240
6539
|
],
|
|
6241
6540
|
includeStdinNotificationSection: true,
|
|
6242
6541
|
messageNotificationStyle: "direct"
|
|
@@ -6390,7 +6689,9 @@ import { createHash as createHash2 } from "crypto";
|
|
|
6390
6689
|
var MAX_RUNTIME_ERROR_MESSAGE_EXCERPT_CHARS = 4096;
|
|
6391
6690
|
var RUNTIME_AUTH_ACTION_REQUIRED_PATTERNS = [
|
|
6392
6691
|
/access token could not be refreshed/i,
|
|
6692
|
+
/\btoken_(?:revoked|invalidated)\b/i,
|
|
6393
6693
|
/refresh token was already used/i,
|
|
6694
|
+
/access token.*invalidated/i,
|
|
6394
6695
|
/authentication token has been invalidated/i,
|
|
6395
6696
|
/logged out or signed in to another account/i,
|
|
6396
6697
|
/not logged in/i,
|
|
@@ -6472,6 +6773,9 @@ function classifyRuntimeError(message, httpStatus) {
|
|
|
6472
6773
|
return "ProviderApiError";
|
|
6473
6774
|
}
|
|
6474
6775
|
if (isRuntimeAuthActionRequiredText(message)) return "AuthError";
|
|
6776
|
+
if (/\bmodel\b.*\bnot supported\b/i.test(message) || /\bunsupported\b.*\bmodel\b/i.test(message) || /\bmodel\b.*\bnot available\b/i.test(message)) {
|
|
6777
|
+
return "ModelConfigError";
|
|
6778
|
+
}
|
|
6475
6779
|
if (/\b(?:ETIMEDOUT|timeout|timed out)\b/i.test(message)) return "TimeoutError";
|
|
6476
6780
|
if (/\b(?:ECONNRESET|EPIPE|ECONNREFUSED|ENOTFOUND|EAI_AGAIN)\b/i.test(message) || /\bUnable to connect to API\b/i.test(message)) {
|
|
6477
6781
|
return "ProviderConnectionError";
|
|
@@ -6504,6 +6808,8 @@ function classifyRuntimeErrorReason(runtimeErrorClass) {
|
|
|
6504
6808
|
return "auth_failed";
|
|
6505
6809
|
case "NotFoundError":
|
|
6506
6810
|
return "not_found";
|
|
6811
|
+
case "ModelConfigError":
|
|
6812
|
+
return "model_config_error";
|
|
6507
6813
|
case "ProviderServerError":
|
|
6508
6814
|
return "provider_server_error";
|
|
6509
6815
|
case "ProviderApiError":
|
|
@@ -6531,7 +6837,7 @@ function runtimeDisplayName(runtimeId) {
|
|
|
6531
6837
|
case "opencode":
|
|
6532
6838
|
return "OpenCode";
|
|
6533
6839
|
case "pi":
|
|
6534
|
-
return "Pi
|
|
6840
|
+
return "Pi";
|
|
6535
6841
|
default:
|
|
6536
6842
|
return runtimeId || "This runtime";
|
|
6537
6843
|
}
|
|
@@ -6777,8 +7083,8 @@ function formatVisibleMessageTarget(message) {
|
|
|
6777
7083
|
}
|
|
6778
7084
|
return null;
|
|
6779
7085
|
}
|
|
6780
|
-
function getMessageShortId(
|
|
6781
|
-
return
|
|
7086
|
+
function getMessageShortId(messageId2) {
|
|
7087
|
+
return messageId2.startsWith("thread-") ? messageId2.slice(7) : messageId2.slice(0, 8);
|
|
6782
7088
|
}
|
|
6783
7089
|
var RESPONSE_TARGET_HINT = "Reply in the channel or create/reply in a thread as appropriate; use each message's `target` and `msg` fields to choose the exact target.";
|
|
6784
7090
|
function findSessionJsonl(root, predicate) {
|
|
@@ -6789,7 +7095,7 @@ function findSessionJsonl(root, predicate) {
|
|
|
6789
7095
|
if (depth < 0 || visited >= maxEntries) return null;
|
|
6790
7096
|
let entries;
|
|
6791
7097
|
try {
|
|
6792
|
-
entries =
|
|
7098
|
+
entries = readdirSync2(dir, { withFileTypes: true }).sort((a, b) => b.name.localeCompare(a.name));
|
|
6793
7099
|
} catch {
|
|
6794
7100
|
return null;
|
|
6795
7101
|
}
|
|
@@ -7505,7 +7811,7 @@ function classifyTerminalFailure(ap) {
|
|
|
7505
7811
|
for (const text of candidates) {
|
|
7506
7812
|
const diagnostics = buildRuntimeErrorDiagnosticEnvelope(text);
|
|
7507
7813
|
const lower = text.toLowerCase();
|
|
7508
|
-
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)) {
|
|
7814
|
+
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") || lower.includes("model is not supported") || lower.includes("unsupported model") || /\bmodel\b.*\bnot supported\b/i.test(text) || diagnostics.spanAttrs.runtime_error_action_required === true || isProviderStreamFailureText(text) || isRuntimeStartTimeoutText(text)) {
|
|
7509
7815
|
const actionRequired = diagnostics.spanAttrs.runtime_error_action_required === true;
|
|
7510
7816
|
return {
|
|
7511
7817
|
detail: actionRequired ? formatRuntimeLoginRequiredMessage(ap.driver.id) : text,
|
|
@@ -7515,6 +7821,16 @@ function classifyTerminalFailure(ap) {
|
|
|
7515
7821
|
}
|
|
7516
7822
|
return null;
|
|
7517
7823
|
}
|
|
7824
|
+
function classifyStickyTerminalFailure(ap) {
|
|
7825
|
+
const terminalFailure = classifyTerminalFailure(ap);
|
|
7826
|
+
if (!terminalFailure) return null;
|
|
7827
|
+
if (terminalFailure.actionRequired) return terminalFailure;
|
|
7828
|
+
if (/\bmodel\b.*\bnot supported\b/i.test(terminalFailure.detail)) return terminalFailure;
|
|
7829
|
+
if (/\bunsupported\b.*\bmodel\b/i.test(terminalFailure.detail)) return terminalFailure;
|
|
7830
|
+
if (isProviderStreamFailureText(terminalFailure.detail)) return null;
|
|
7831
|
+
if (isRuntimeStartTimeoutText(terminalFailure.detail)) return null;
|
|
7832
|
+
return null;
|
|
7833
|
+
}
|
|
7518
7834
|
function isProviderStreamFailureText(text) {
|
|
7519
7835
|
return /stream closed before response\.completed|error decoding response body/i.test(text);
|
|
7520
7836
|
}
|
|
@@ -7759,7 +8075,11 @@ var RUNTIME_TELEMETRY_RESERVED_ATTR_KEYS = /* @__PURE__ */ new Set([
|
|
|
7759
8075
|
"usageKind",
|
|
7760
8076
|
"sessionId",
|
|
7761
8077
|
"turnId",
|
|
7762
|
-
"runtimeResultId"
|
|
8078
|
+
"runtimeResultId",
|
|
8079
|
+
"daemonVersion",
|
|
8080
|
+
"daemon_version",
|
|
8081
|
+
"computerVersion",
|
|
8082
|
+
"computer_version"
|
|
7763
8083
|
]);
|
|
7764
8084
|
function sanitizeRuntimeTelemetryPayloadAttrs(attrs) {
|
|
7765
8085
|
const sanitized = {};
|
|
@@ -7770,17 +8090,17 @@ function sanitizeRuntimeTelemetryPayloadAttrs(attrs) {
|
|
|
7770
8090
|
return sanitized;
|
|
7771
8091
|
}
|
|
7772
8092
|
function getMessageDeliveryText(driver) {
|
|
7773
|
-
return driver.supportsStdinNotification ? "New
|
|
8093
|
+
return driver.supportsStdinNotification ? "New inbox updates may be delivered to you automatically while your process stays alive." : "The daemon will automatically restart you with an inbox update when new messages arrive.";
|
|
7774
8094
|
}
|
|
7775
8095
|
function getBusyDeliveryNote(driver) {
|
|
7776
8096
|
if (!driver.supportsStdinNotification) return "";
|
|
7777
8097
|
if (driver.busyDeliveryMode === "direct") {
|
|
7778
|
-
return "\n\nNote: While you are busy, the daemon may write batched
|
|
8098
|
+
return "\n\nNote: While you are busy, the daemon may write batched content-free inbox updates into your active turn. Finish your current step, then check the inbox or read messages at a natural breakpoint.";
|
|
7779
8099
|
}
|
|
7780
8100
|
if (driver.busyDeliveryMode === "gated") {
|
|
7781
|
-
return "\n\nNote: While you are busy, the daemon may write batched
|
|
8101
|
+
return "\n\nNote: While you are busy, the daemon may write batched content-free inbox updates into your active turn at runtime-observed safe boundaries. Finish your current step, then check the inbox or read messages at a natural breakpoint.";
|
|
7782
8102
|
}
|
|
7783
|
-
return "\n\nNote: While you are busy, you may receive [Slock inbox notice: ...] messages. Finish your current step, then
|
|
8103
|
+
return "\n\nNote: While you are busy, you may receive [Slock inbox notice: ...] messages. Finish your current step, then check the inbox or read messages at a natural breakpoint.";
|
|
7784
8104
|
}
|
|
7785
8105
|
var NATIVE_STANDING_PROMPT_STARTUP_INPUT = (
|
|
7786
8106
|
// Claude Code 2.1.114 treats "follow your system prompt" style user turns as
|
|
@@ -7979,6 +8299,13 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
7979
8299
|
getPendingMessages: (target) => this.pendingVisibleMessages(agentId, target),
|
|
7980
8300
|
isMessageModelSeen: ({ target, message }) => this.isVisibleMessageModelSeen(agentId, target, message),
|
|
7981
8301
|
getAllPendingMessages: () => this.allPendingVisibleMessages(agentId),
|
|
8302
|
+
recordInboxSnapshot: (input) => {
|
|
8303
|
+
this.recordDaemonTrace("daemon.agent.inbox_projection.snapshot", {
|
|
8304
|
+
agentId,
|
|
8305
|
+
source: input.source,
|
|
8306
|
+
...this.inboxProjectionTraceAttrs(input.rows, input.pendingMessageCount)
|
|
8307
|
+
});
|
|
8308
|
+
},
|
|
7982
8309
|
consumeVisibleMessages: (input) => this.consumeVisibleMessages(agentId, input),
|
|
7983
8310
|
recordDrainOutcome: (input) => {
|
|
7984
8311
|
this.recordDaemonTrace("daemon.agent.drain.outcome", {
|
|
@@ -8328,6 +8655,8 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8328
8655
|
const standingPrompt = driver.buildSystemPrompt(runtimeConfig, agentId);
|
|
8329
8656
|
let prompt;
|
|
8330
8657
|
let promptSource;
|
|
8658
|
+
let wakeMessageDeliveredAsInboxUpdate = false;
|
|
8659
|
+
const startingInboxMessages = this.startingInboxes.get(agentId) || [];
|
|
8331
8660
|
if (runtimeConfig.runtimeProfileControl && !wakeMessage) {
|
|
8332
8661
|
prompt = driver.supportsNativeStandingPrompt ? NATIVE_STANDING_PROMPT_STARTUP_INPUT : formatRuntimeProfileControlStartupInput(runtimeConfig.runtimeProfileControl, driver);
|
|
8333
8662
|
promptSource = "runtime_profile_control";
|
|
@@ -8337,13 +8666,15 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8337
8666
|
promptSource = "resume_prompt";
|
|
8338
8667
|
} else if (wakeMessage) {
|
|
8339
8668
|
const runtimeProfileControlPrompt = formatRuntimeProfileControlPrompt([wakeMessage]);
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8669
|
+
if (runtimeProfileControlPrompt) {
|
|
8670
|
+
prompt = runtimeProfileControlPrompt;
|
|
8671
|
+
} else {
|
|
8672
|
+
wakeMessageDeliveredAsInboxUpdate = true;
|
|
8673
|
+
prompt = this.formatInboxUpdateRuntimeInput([wakeMessage, ...startingInboxMessages], driver);
|
|
8674
|
+
}
|
|
8675
|
+
promptSource = runtimeProfileControlPrompt ? "runtime_profile_control_message" : "wake_inbox_update";
|
|
8345
8676
|
if (!runtimeProfileControlPrompt && unreadSummary && Object.keys(unreadSummary).length > 0) {
|
|
8346
|
-
const otherUnread = Object.entries(unreadSummary)
|
|
8677
|
+
const otherUnread = Object.entries(unreadSummary);
|
|
8347
8678
|
if (otherUnread.length > 0) {
|
|
8348
8679
|
prompt += `
|
|
8349
8680
|
|
|
@@ -8354,18 +8685,9 @@ You also have unread messages in other channels:`;
|
|
|
8354
8685
|
}
|
|
8355
8686
|
prompt += `
|
|
8356
8687
|
|
|
8357
|
-
Use
|
|
8688
|
+
Use the inbox/read commands at a natural breakpoint if you choose to inspect those targets.`;
|
|
8358
8689
|
}
|
|
8359
8690
|
}
|
|
8360
|
-
if (!runtimeProfileControlPrompt) {
|
|
8361
|
-
prompt += `
|
|
8362
|
-
|
|
8363
|
-
Respond as appropriate \u2014 ${dynamicReplyInstruction(driver)}, or take action as needed. Complete ALL your work before stopping.
|
|
8364
|
-
${RESPONSE_TARGET_HINT}
|
|
8365
|
-
|
|
8366
|
-
IMPORTANT: If the message requires multi-step work (e.g. research, code changes, testing), complete ALL steps before stopping. Sending a progress update does NOT mean your task is done \u2014 only stop when you have NO more work to do. ${getMessageDeliveryText(driver)}`;
|
|
8367
|
-
prompt += getBusyDeliveryNote(driver);
|
|
8368
|
-
}
|
|
8369
8691
|
} else if (isResume && unreadSummary && Object.keys(unreadSummary).length > 0) {
|
|
8370
8692
|
prompt = `You have unread messages from while you were offline:`;
|
|
8371
8693
|
for (const [ch, count] of Object.entries(unreadSummary)) {
|
|
@@ -8440,7 +8762,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8440
8762
|
const agentProcess = {
|
|
8441
8763
|
process: proc,
|
|
8442
8764
|
driver,
|
|
8443
|
-
inbox:
|
|
8765
|
+
inbox: wakeMessageDeliveredAsInboxUpdate && wakeMessage ? [wakeMessage, ...startingInboxMessages] : startingInboxMessages,
|
|
8444
8766
|
config: runtimeConfig,
|
|
8445
8767
|
sessionId: runtimeConfig.sessionId || null,
|
|
8446
8768
|
launchId: launchId || null,
|
|
@@ -8483,7 +8805,9 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8483
8805
|
if (runtimeConfig.runtimeProfileControl) {
|
|
8484
8806
|
this.ackInjectedRuntimeProfileControl(agentId, runtimeConfig.runtimeProfileControl, agentProcess.launchId);
|
|
8485
8807
|
}
|
|
8486
|
-
if (
|
|
8808
|
+
if (wakeMessageDeliveredAsInboxUpdate) {
|
|
8809
|
+
this.recordInboxUpdateProjection(agentId, agentProcess, agentProcess.inbox, "spawn_wake_inbox_update", "wake", prompt);
|
|
8810
|
+
} else if (wakeMessage) {
|
|
8487
8811
|
this.consumeVisibleMessages(agentId, { messages: [wakeMessage], source: "spawn_wake_message" });
|
|
8488
8812
|
this.ackInjectedRuntimeProfileMessages(agentId, [wakeMessage], agentProcess.launchId);
|
|
8489
8813
|
}
|
|
@@ -8584,8 +8908,9 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8584
8908
|
const finalSignal = ap.exitSignal ?? signal;
|
|
8585
8909
|
const startupTimeoutTermination = ap.expectedTerminationReason === "startup_timeout";
|
|
8586
8910
|
const expectedTermination = Boolean(ap.expectedTerminationReason);
|
|
8587
|
-
const
|
|
8588
|
-
const
|
|
8911
|
+
const stickyTerminalFailureDetail = classifyStickyTerminalFailure(ap);
|
|
8912
|
+
const processEndedCleanly = !stickyTerminalFailureDetail && (finalCode === 0 || expectedTermination && !ap.lastRuntimeError);
|
|
8913
|
+
const terminalFailureDetail = processEndedCleanly ? null : stickyTerminalFailureDetail ?? classifyTerminalFailure(ap);
|
|
8589
8914
|
const resumeRecoveryReason = resumeSessionRecoveryReason(ap);
|
|
8590
8915
|
const shouldColdStartResumeSession = resumeRecoveryReason !== null;
|
|
8591
8916
|
const summary = summarizeCrash(finalCode, finalSignal);
|
|
@@ -9068,13 +9393,36 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9068
9393
|
}));
|
|
9069
9394
|
return true;
|
|
9070
9395
|
}
|
|
9396
|
+
const stickyTerminalFailure = classifyStickyTerminalFailure(ap);
|
|
9397
|
+
if (stickyTerminalFailure) {
|
|
9398
|
+
ap.inbox.push(message);
|
|
9399
|
+
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
9400
|
+
outcome: "queued_terminal_runtime_error",
|
|
9401
|
+
accepted: true,
|
|
9402
|
+
process_present: true,
|
|
9403
|
+
runtime: ap.config.runtime,
|
|
9404
|
+
session_id_present: Boolean(ap.sessionId),
|
|
9405
|
+
launchId: ap.launchId || void 0,
|
|
9406
|
+
is_idle: ap.isIdle,
|
|
9407
|
+
inbox_count: ap.inbox.length
|
|
9408
|
+
}));
|
|
9409
|
+
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
9410
|
+
this.broadcastActivity(agentId, "error", stickyTerminalFailure.detail);
|
|
9411
|
+
return true;
|
|
9412
|
+
}
|
|
9071
9413
|
if (ap.isIdle && ap.driver.supportsStdinNotification && ap.sessionId) {
|
|
9072
|
-
|
|
9073
|
-
nextMessages.
|
|
9414
|
+
ap.inbox.push(message);
|
|
9415
|
+
const nextMessages = [...ap.inbox];
|
|
9074
9416
|
this.commitApmIdleState(agentId, ap, false);
|
|
9075
|
-
this.startRuntimeTrace(agentId, ap, "stdin-idle-delivery",
|
|
9417
|
+
this.startRuntimeTrace(agentId, ap, "stdin-idle-delivery", [message]);
|
|
9076
9418
|
this.broadcastActivity(agentId, "working", "Message received");
|
|
9077
|
-
const stdinAccepted = this.
|
|
9419
|
+
const stdinAccepted = this.deliverInboxUpdateViaStdin(
|
|
9420
|
+
agentId,
|
|
9421
|
+
ap,
|
|
9422
|
+
[message],
|
|
9423
|
+
"idle",
|
|
9424
|
+
"stdin_idle_delivery"
|
|
9425
|
+
);
|
|
9078
9426
|
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
9079
9427
|
outcome: "stdin_idle_delivery",
|
|
9080
9428
|
accepted: true,
|
|
@@ -9905,6 +10253,29 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9905
10253
|
...messageProducerFactTraceAttrs(messages)
|
|
9906
10254
|
};
|
|
9907
10255
|
}
|
|
10256
|
+
inboxProjectionTraceAttrs(rows, pendingMessageCount) {
|
|
10257
|
+
let maxPendingCount = 0;
|
|
10258
|
+
let mentionTargetCount = 0;
|
|
10259
|
+
let threadTargetCount = 0;
|
|
10260
|
+
let dmTargetCount = 0;
|
|
10261
|
+
let taskTargetCount = 0;
|
|
10262
|
+
for (const row of rows) {
|
|
10263
|
+
maxPendingCount = Math.max(maxPendingCount, row.pendingCount);
|
|
10264
|
+
if (row.flags.includes("mention")) mentionTargetCount += 1;
|
|
10265
|
+
if (row.flags.includes("thread")) threadTargetCount += 1;
|
|
10266
|
+
if (row.flags.includes("dm")) dmTargetCount += 1;
|
|
10267
|
+
if (row.flags.includes("task")) taskTargetCount += 1;
|
|
10268
|
+
}
|
|
10269
|
+
return {
|
|
10270
|
+
inbox_target_count: rows.length,
|
|
10271
|
+
pending_message_count: pendingMessageCount,
|
|
10272
|
+
max_pending_per_target: maxPendingCount,
|
|
10273
|
+
mention_target_count: mentionTargetCount,
|
|
10274
|
+
thread_target_count: threadTargetCount,
|
|
10275
|
+
dm_target_count: dmTargetCount,
|
|
10276
|
+
task_target_count: taskTargetCount
|
|
10277
|
+
};
|
|
10278
|
+
}
|
|
9908
10279
|
runtimeProfileTurnControlTraceAttrs(control) {
|
|
9909
10280
|
if (!control) return {};
|
|
9910
10281
|
const pendingAgeMs = Math.max(0, Date.now() - control.injectedAtMs);
|
|
@@ -10066,7 +10437,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10066
10437
|
return written;
|
|
10067
10438
|
}
|
|
10068
10439
|
case "deliver_stdin": {
|
|
10069
|
-
const messages = ap.inbox
|
|
10440
|
+
const messages = [...ap.inbox];
|
|
10070
10441
|
ap.notifications.clear();
|
|
10071
10442
|
if (messages.length === 0) {
|
|
10072
10443
|
this.recordApmGatedSteeringEffectTrace(agentId, ap, effect, {
|
|
@@ -10084,7 +10455,22 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10084
10455
|
});
|
|
10085
10456
|
}
|
|
10086
10457
|
this.broadcastActivity(agentId, "working", "Message received");
|
|
10087
|
-
const
|
|
10458
|
+
const runtimeProfileMessages = messages.filter((message) => runtimeProfileNotificationFromMessage(message));
|
|
10459
|
+
const ordinaryMessages = messages.filter((message) => !runtimeProfileNotificationFromMessage(message));
|
|
10460
|
+
let accepted = true;
|
|
10461
|
+
if (runtimeProfileMessages.length > 0) {
|
|
10462
|
+
ap.inbox.splice(0, ap.inbox.length, ...ordinaryMessages);
|
|
10463
|
+
accepted = this.deliverMessagesViaStdin(agentId, ap, runtimeProfileMessages, effect.stdinMode);
|
|
10464
|
+
}
|
|
10465
|
+
if (ordinaryMessages.length > 0) {
|
|
10466
|
+
accepted = this.deliverInboxUpdateViaStdin(
|
|
10467
|
+
agentId,
|
|
10468
|
+
ap,
|
|
10469
|
+
ordinaryMessages,
|
|
10470
|
+
effect.stdinMode,
|
|
10471
|
+
`stdin_${effect.stdinMode}_delivery`
|
|
10472
|
+
) && accepted;
|
|
10473
|
+
}
|
|
10088
10474
|
this.recordApmGatedSteeringEffectTrace(agentId, ap, effect, {
|
|
10089
10475
|
outcome: accepted ? "written" : "not_written",
|
|
10090
10476
|
delivered_messages_count: messages.length
|
|
@@ -10396,30 +10782,41 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10396
10782
|
this.flushPendingTrajectory(agentId);
|
|
10397
10783
|
if (ap) {
|
|
10398
10784
|
if (event.sessionId) ap.sessionId = event.sessionId;
|
|
10785
|
+
const stickyTerminalFailure = classifyStickyTerminalFailure(ap);
|
|
10399
10786
|
const reduction = reduceApmGatedTurnEnd(ap.gatedSteering, {
|
|
10400
|
-
inboxLength: ap.inbox.length,
|
|
10787
|
+
inboxLength: stickyTerminalFailure ? 0 : ap.inbox.length,
|
|
10401
10788
|
supportsStdinNotification: ap.driver.supportsStdinNotification,
|
|
10402
10789
|
hasSession: Boolean(ap.sessionId),
|
|
10403
10790
|
terminateProcessOnTurnEnd: ap.driver.terminateProcessOnTurnEnd === true
|
|
10404
10791
|
});
|
|
10405
10792
|
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "turn_end" });
|
|
10406
10793
|
const deliverStdinEffect = reduction.effects.find((effect) => effect.kind === "deliver_stdin");
|
|
10794
|
+
if (stickyTerminalFailure) {
|
|
10795
|
+
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
10796
|
+
}
|
|
10407
10797
|
if (deliverStdinEffect) {
|
|
10408
10798
|
if (!this.executeApmGatedSteeringEffect(agentId, ap, deliverStdinEffect)) {
|
|
10409
10799
|
this.commitApmIdleState(agentId, ap, true);
|
|
10410
|
-
|
|
10800
|
+
if (stickyTerminalFailure) {
|
|
10801
|
+
this.broadcastActivity(agentId, "error", stickyTerminalFailure.detail);
|
|
10802
|
+
} else {
|
|
10803
|
+
this.broadcastActivity(agentId, "online", "Idle");
|
|
10804
|
+
}
|
|
10411
10805
|
}
|
|
10412
10806
|
} else {
|
|
10413
|
-
if (
|
|
10807
|
+
if (stickyTerminalFailure) {
|
|
10808
|
+
this.broadcastActivity(agentId, "error", stickyTerminalFailure.detail);
|
|
10809
|
+
} else if (ap.lastRuntimeError) {
|
|
10414
10810
|
this.broadcastActivity(agentId, "error", ap.lastRuntimeError);
|
|
10415
10811
|
} else {
|
|
10416
10812
|
this.broadcastActivity(agentId, "online", "Idle");
|
|
10417
10813
|
}
|
|
10418
10814
|
}
|
|
10419
|
-
this.endRuntimeTrace(ap, "ok", {
|
|
10420
|
-
outcome: "turn-completed",
|
|
10815
|
+
this.endRuntimeTrace(ap, stickyTerminalFailure ? "error" : "ok", {
|
|
10816
|
+
outcome: stickyTerminalFailure ? "runtime-error-with-turn-end" : "turn-completed",
|
|
10817
|
+
...stickyTerminalFailure ? { terminalRuntimeError: true } : {},
|
|
10421
10818
|
...runtimeTraceCounterAttrs(ap),
|
|
10422
|
-
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "turn_end")
|
|
10819
|
+
...this.finalizeRuntimeProfileTurnControl(agentId, ap, stickyTerminalFailure ? "runtime_error" : "turn_end")
|
|
10423
10820
|
});
|
|
10424
10821
|
if (ap.driver.terminateProcessOnTurnEnd) {
|
|
10425
10822
|
logger.info(`[Agent ${agentId}] Turn completed; terminating ${ap.driver.id} process`);
|
|
@@ -10477,6 +10874,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10477
10874
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_error")
|
|
10478
10875
|
});
|
|
10479
10876
|
if (ap.driver.supportsStdinNotification && terminalFailure) {
|
|
10877
|
+
const stickyTerminalFailure = classifyStickyTerminalFailure(ap);
|
|
10480
10878
|
if (terminalFailure.actionRequired) {
|
|
10481
10879
|
logger.warn(`[Agent ${agentId}] ${ap.driver.id} auth requires user action; terminating runtime process`);
|
|
10482
10880
|
try {
|
|
@@ -10489,6 +10887,10 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10489
10887
|
const reason = err instanceof Error ? err.message : String(err);
|
|
10490
10888
|
logger.warn(`[Agent ${agentId}] Failed to terminate ${ap.driver.id} after auth error: ${reason}`);
|
|
10491
10889
|
}
|
|
10890
|
+
} else if (stickyTerminalFailure) {
|
|
10891
|
+
ap.notifications.clear();
|
|
10892
|
+
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
10893
|
+
logger.warn(`[Agent ${agentId}] ${ap.driver.id} terminal runtime error requires explicit recovery`);
|
|
10492
10894
|
} else {
|
|
10493
10895
|
ap.notifications.clear();
|
|
10494
10896
|
logger.info(`[Agent ${agentId}] Marked ${ap.driver.id} wakeable after terminal runtime error`);
|
|
@@ -10595,11 +10997,30 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10595
10997
|
}
|
|
10596
10998
|
const inboxCount = ap.inbox.length;
|
|
10597
10999
|
if (inboxCount === 0) return false;
|
|
10598
|
-
const
|
|
10599
|
-
|
|
11000
|
+
const changedMessages = ap.inbox.slice(Math.max(0, ap.inbox.length - count));
|
|
11001
|
+
const inboxRows = projectAgentInboxSnapshot(changedMessages);
|
|
11002
|
+
const notification = `[Slock inbox notice:
|
|
11003
|
+
${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
|
|
11004
|
+
const notificationByteCount = Buffer.byteLength(notification, "utf8");
|
|
11005
|
+
const projectionAttrs = this.inboxProjectionTraceAttrs(inboxRows, inboxCount);
|
|
11006
|
+
this.recordDaemonTrace("daemon.agent.inbox_projection.delta", {
|
|
11007
|
+
agentId,
|
|
11008
|
+
source: "busy_stdin_notification",
|
|
11009
|
+
...projectionAttrs
|
|
11010
|
+
});
|
|
11011
|
+
logger.info(`[Agent ${agentId}] Sending stdin inbox update: ${inboxRows.length} changed target(s), ${inboxCount} pending message(s)`);
|
|
10600
11012
|
const encoded = ap.driver.encodeStdinMessage(notification, ap.sessionId, { mode: "busy" });
|
|
10601
11013
|
if (encoded) {
|
|
10602
11014
|
ap.process.stdin?.write(encoded + "\n");
|
|
11015
|
+
this.recordDaemonTrace("daemon.agent.inbox_update.pushed", {
|
|
11016
|
+
agentId,
|
|
11017
|
+
runtime: ap.config.runtime,
|
|
11018
|
+
model: ap.config.model,
|
|
11019
|
+
launchId: ap.launchId || void 0,
|
|
11020
|
+
mode: "busy",
|
|
11021
|
+
notification_byte_count: notificationByteCount,
|
|
11022
|
+
...projectionAttrs
|
|
11023
|
+
});
|
|
10603
11024
|
this.recordDaemonTrace("daemon.agent.stdin_notification", {
|
|
10604
11025
|
agentId,
|
|
10605
11026
|
runtime: ap.config.runtime,
|
|
@@ -10609,6 +11030,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10609
11030
|
mode: "busy",
|
|
10610
11031
|
pending_notification_count: count,
|
|
10611
11032
|
inbox_count: inboxCount,
|
|
11033
|
+
inbox_target_count: inboxRows.length,
|
|
10612
11034
|
session_id_present: true
|
|
10613
11035
|
});
|
|
10614
11036
|
return true;
|
|
@@ -10626,11 +11048,110 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10626
11048
|
retry_scheduled: retryScheduled,
|
|
10627
11049
|
notification_timer_present: ap.notifications.hasTimer,
|
|
10628
11050
|
inbox_count: inboxCount,
|
|
11051
|
+
inbox_target_count: inboxRows.length,
|
|
10629
11052
|
session_id_present: true
|
|
10630
11053
|
}, "error");
|
|
10631
11054
|
return false;
|
|
10632
11055
|
}
|
|
10633
11056
|
}
|
|
11057
|
+
formatInboxUpdateRuntimeInput(messages, driver, totalPendingMessages = messages.length) {
|
|
11058
|
+
const rows = projectAgentInboxSnapshot(messages);
|
|
11059
|
+
return [
|
|
11060
|
+
"[Slock inbox notice:",
|
|
11061
|
+
formatAgentInboxDelta(rows, { totalPendingMessages }),
|
|
11062
|
+
"]"
|
|
11063
|
+
].join("\n");
|
|
11064
|
+
}
|
|
11065
|
+
recordInboxUpdateProjection(agentId, ap, messages, source, mode, renderedInput, totalPendingMessages = messages.length) {
|
|
11066
|
+
const rows = projectAgentInboxSnapshot(messages);
|
|
11067
|
+
const projectionAttrs = this.inboxProjectionTraceAttrs(rows, totalPendingMessages);
|
|
11068
|
+
this.recordDaemonTrace("daemon.agent.inbox_projection.delta", {
|
|
11069
|
+
agentId,
|
|
11070
|
+
source,
|
|
11071
|
+
...projectionAttrs
|
|
11072
|
+
});
|
|
11073
|
+
this.recordDaemonTrace("daemon.agent.inbox_update.pushed", {
|
|
11074
|
+
agentId,
|
|
11075
|
+
runtime: ap.config.runtime,
|
|
11076
|
+
model: ap.config.model,
|
|
11077
|
+
launchId: ap.launchId || void 0,
|
|
11078
|
+
mode,
|
|
11079
|
+
notification_byte_count: Buffer.byteLength(renderedInput, "utf8"),
|
|
11080
|
+
cursors_advanced: "none",
|
|
11081
|
+
...projectionAttrs
|
|
11082
|
+
});
|
|
11083
|
+
return projectionAttrs;
|
|
11084
|
+
}
|
|
11085
|
+
deliverInboxUpdateViaStdin(agentId, ap, messages, mode, source) {
|
|
11086
|
+
if (messages.length === 0) return true;
|
|
11087
|
+
const prompt = this.formatInboxUpdateRuntimeInput(messages, ap.driver, ap.inbox.length);
|
|
11088
|
+
const projectionAttrs = this.recordInboxUpdateProjection(agentId, ap, messages, source, mode, prompt, ap.inbox.length);
|
|
11089
|
+
const inputTraceAttrs = buildRuntimeInputTraceAttrs({
|
|
11090
|
+
source,
|
|
11091
|
+
prompt,
|
|
11092
|
+
messages,
|
|
11093
|
+
sessionIdPresent: Boolean(ap.sessionId),
|
|
11094
|
+
nativeStandingPrompt: Boolean(ap.driver.supportsNativeStandingPrompt)
|
|
11095
|
+
});
|
|
11096
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.input.prepared", inputTraceAttrs);
|
|
11097
|
+
const encoded = ap.driver.encodeStdinMessage(prompt, ap.sessionId, { mode });
|
|
11098
|
+
if (!encoded) {
|
|
11099
|
+
if (mode === "idle") {
|
|
11100
|
+
this.commitApmIdleState(agentId, ap, true);
|
|
11101
|
+
}
|
|
11102
|
+
logger.warn(
|
|
11103
|
+
`[Agent ${agentId}] Failed to encode ${mode} inbox update; ${messages.length === 1 ? "message remains" : "messages remain"} pending`
|
|
11104
|
+
);
|
|
11105
|
+
this.recordDaemonTrace("daemon.agent.stdin_delivery", {
|
|
11106
|
+
agentId,
|
|
11107
|
+
launchId: ap.launchId || void 0,
|
|
11108
|
+
runtime: ap.config.runtime,
|
|
11109
|
+
model: ap.config.model,
|
|
11110
|
+
mode,
|
|
11111
|
+
messages_count: messages.length,
|
|
11112
|
+
session_id_present: Boolean(ap.sessionId),
|
|
11113
|
+
inbox_count: ap.inbox.length,
|
|
11114
|
+
pending_notification_count: ap.notifications.pendingCount,
|
|
11115
|
+
busy_delivery_mode: ap.driver.busyDeliveryMode,
|
|
11116
|
+
supports_stdin_notification: ap.driver.supportsStdinNotification,
|
|
11117
|
+
...this.messagesTraceAttrs(messages),
|
|
11118
|
+
...inputTraceAttrs,
|
|
11119
|
+
...projectionAttrs,
|
|
11120
|
+
outcome: "encode_failed",
|
|
11121
|
+
requeued_messages_count: 0,
|
|
11122
|
+
cursors_advanced: "none"
|
|
11123
|
+
}, "error");
|
|
11124
|
+
return false;
|
|
11125
|
+
}
|
|
11126
|
+
const senders = [...new Set(messages.map((message) => `@${message.sender_name}`))].join(", ");
|
|
11127
|
+
logger.info(
|
|
11128
|
+
`[Agent ${agentId}] Delivering ${mode} inbox update for ${messages.length === 1 ? "message" : `${messages.length} messages`} from ${senders}`
|
|
11129
|
+
);
|
|
11130
|
+
if (this.containsOrdinaryInboxMessage(messages)) {
|
|
11131
|
+
ap.lastRuntimeError = null;
|
|
11132
|
+
}
|
|
11133
|
+
ap.process.stdin?.write(encoded + "\n");
|
|
11134
|
+
this.recordDaemonTrace("daemon.agent.stdin_delivery", {
|
|
11135
|
+
agentId,
|
|
11136
|
+
launchId: ap.launchId || void 0,
|
|
11137
|
+
runtime: ap.config.runtime,
|
|
11138
|
+
model: ap.config.model,
|
|
11139
|
+
mode,
|
|
11140
|
+
messages_count: messages.length,
|
|
11141
|
+
session_id_present: Boolean(ap.sessionId),
|
|
11142
|
+
inbox_count: ap.inbox.length,
|
|
11143
|
+
pending_notification_count: ap.notifications.pendingCount,
|
|
11144
|
+
busy_delivery_mode: ap.driver.busyDeliveryMode,
|
|
11145
|
+
supports_stdin_notification: ap.driver.supportsStdinNotification,
|
|
11146
|
+
...this.messagesTraceAttrs(messages),
|
|
11147
|
+
...inputTraceAttrs,
|
|
11148
|
+
...projectionAttrs,
|
|
11149
|
+
outcome: "written",
|
|
11150
|
+
stdin_write_attempted: true,
|
|
11151
|
+
cursors_advanced: "none"
|
|
11152
|
+
});
|
|
11153
|
+
return true;
|
|
11154
|
+
}
|
|
10634
11155
|
/** Deliver a message to an agent via stdin, formatting it the same way as the MCP bridge */
|
|
10635
11156
|
deliverMessagesViaStdin(agentId, ap, messages, mode) {
|
|
10636
11157
|
if (messages.length === 0) return true;
|
|
@@ -11163,7 +11684,7 @@ function acquireDaemonMachineLock(options) {
|
|
|
11163
11684
|
}
|
|
11164
11685
|
|
|
11165
11686
|
// src/localTraceSink.ts
|
|
11166
|
-
import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as
|
|
11687
|
+
import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync3, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
|
|
11167
11688
|
import path15 from "path";
|
|
11168
11689
|
var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
11169
11690
|
var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
|
|
@@ -11242,7 +11763,7 @@ var LocalRotatingTraceSink = class {
|
|
|
11242
11763
|
}
|
|
11243
11764
|
}
|
|
11244
11765
|
pruneOldFiles() {
|
|
11245
|
-
const files =
|
|
11766
|
+
const files = readdirSync3(this.traceDir).filter((name) => name.startsWith("daemon-trace-") && name.endsWith(".jsonl")).sort();
|
|
11246
11767
|
const excess = files.length - this.maxFiles;
|
|
11247
11768
|
if (excess <= 0) return;
|
|
11248
11769
|
for (const file of files.slice(0, excess)) {
|
|
@@ -11911,7 +12432,7 @@ var DaemonCore = class {
|
|
|
11911
12432
|
this.slockHome = resolveSlockHome();
|
|
11912
12433
|
process.env[SLOCK_HOME_ENV] = this.slockHome;
|
|
11913
12434
|
this.injectedTracer = Boolean(options.tracer);
|
|
11914
|
-
this.tracer = options.tracer ?? noopTracer;
|
|
12435
|
+
this.tracer = this.withDaemonTraceScope(options.tracer ?? noopTracer);
|
|
11915
12436
|
this.runtimeDetector = options.runtimeDetector ?? (() => detectRuntimes(this.tracer));
|
|
11916
12437
|
this.reminderCache = new ReminderCache({
|
|
11917
12438
|
clock: options.reminderClock,
|
|
@@ -11962,9 +12483,9 @@ var DaemonCore = class {
|
|
|
11962
12483
|
maxFileAgeJitterMs: jitter.maxFileAgeJitterMs,
|
|
11963
12484
|
maxFiles: this.options.localTraceMaxFiles ?? readPositiveIntegerEnv3("SLOCK_DAEMON_TRACE_MAX_FILES", 8)
|
|
11964
12485
|
});
|
|
11965
|
-
this.tracer = new BasicTracer({
|
|
12486
|
+
this.tracer = this.withDaemonTraceScope(new BasicTracer({
|
|
11966
12487
|
sink: this.localTraceSink
|
|
11967
|
-
});
|
|
12488
|
+
}));
|
|
11968
12489
|
this.agentManager.setTracer(this.tracer);
|
|
11969
12490
|
this.agentManager.setCliTransportTraceDir(path17.join(machineDir, "traces"));
|
|
11970
12491
|
}
|
|
@@ -12058,6 +12579,23 @@ var DaemonCore = class {
|
|
|
12058
12579
|
});
|
|
12059
12580
|
span.end(status);
|
|
12060
12581
|
}
|
|
12582
|
+
withDaemonTraceScope(tracer) {
|
|
12583
|
+
return createScopedTracer(tracer, this.daemonTraceScopeAttrs());
|
|
12584
|
+
}
|
|
12585
|
+
daemonTraceScopeAttrs() {
|
|
12586
|
+
return {
|
|
12587
|
+
daemonVersion: this.daemonVersion,
|
|
12588
|
+
daemon_version: this.daemonVersion,
|
|
12589
|
+
daemon_version_present: Boolean(this.daemonVersion),
|
|
12590
|
+
...this.computerVersion ? {
|
|
12591
|
+
computerVersion: this.computerVersion,
|
|
12592
|
+
computer_version: this.computerVersion,
|
|
12593
|
+
computer_version_present: true
|
|
12594
|
+
} : {
|
|
12595
|
+
computer_version_present: false
|
|
12596
|
+
}
|
|
12597
|
+
};
|
|
12598
|
+
}
|
|
12061
12599
|
async requestRunnerCredentialOnce(agentId, config) {
|
|
12062
12600
|
const url = new URL(`/internal/computer/runners/${encodeURIComponent(agentId)}/credentials`, this.options.serverUrl);
|
|
12063
12601
|
const res = await daemonFetch(url, {
|
|
@@ -12418,8 +12956,7 @@ var DaemonCore = class {
|
|
|
12418
12956
|
runtimes_count: runtimes.length,
|
|
12419
12957
|
running_agents_count: runningAgentIds.length,
|
|
12420
12958
|
idle_agents_count: idleAgentSessions.length,
|
|
12421
|
-
runtime_profile_reports_count: runtimeProfileReports.length
|
|
12422
|
-
daemon_version_present: Boolean(this.daemonVersion)
|
|
12959
|
+
runtime_profile_reports_count: runtimeProfileReports.length
|
|
12423
12960
|
});
|
|
12424
12961
|
for (const agentId of runningAgentIds) {
|
|
12425
12962
|
const sessionId = this.agentManager.getAgentSessionId(agentId);
|