@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.
@@ -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 CLI", binary: "pi", supported: true }
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 inbox-count notification into your current turn.
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
- - Call ${checkCmd} at the next safe breakpoint to materialize the pending messages before taking side-effect actions that depend on current context.
1741
- - If the new message is higher priority, pivot after reading it. If not, continue your current work.`;
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: You have N pending inbox message(s). Call slock message check to read them when you're ready.]\`` : `\`[Slock inbox notice: You have N pending inbox message(s). Call ${t("check_messages")} to read them when you're ready.]\``;
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
- - Call ${checkCmd} to check for new messages. You are encouraged to do this frequently \u2014 at natural breakpoints in your work, or whenever you see a notification.
1754
- - If the new message is higher priority, you may pivot to it. If not, continue your current work.
1755
- - ${checkCmd} returns instantly with any pending messages (or "no new messages"). It is always safe to call.`;
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 messageSeq(message) {
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(messageSeq(message));
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(messageSeq(message));
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) => messageSeq(a) - messageSeq(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 = messageSeq(message);
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 messageSeq2 = typeof parsed.messageSeq === "number" && Number.isFinite(parsed.messageSeq) ? Math.floor(parsed.messageSeq) : void 0;
2798
- if (sendTarget && messageSeq2 && messageSeq2 > 0) {
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: messageSeq2, id: parsed.messageId }], sendTarget),
2802
- boundarySeq: messageSeq2,
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 nonEmptyString(value) {
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 = nonEmptyString(message.params?.threadId) ?? nonEmptyString(message.params?.thread?.id) ?? nonEmptyString(message.params?.sessionId);
3932
- const telemetryTurnId = nonEmptyString(message.params?.turnId) ?? nonEmptyString(message.params?.turn?.id);
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 { spawn as spawn9, spawnSync as spawnSync3 } from "child_process";
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 buildPiRpcArgs(ctx, sessionId) {
6003
- const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
6004
- const args = [
6005
- "--mode",
6006
- "rpc",
6007
- "--session-dir",
6008
- buildPiSessionDir(ctx.workingDirectory),
6009
- "--system-prompt",
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
- if (sessionId) {
6019
- args.push("--session-id", sessionId);
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
- return args;
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
- async function buildPiSpawnEnv(ctx) {
6024
- return (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
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 parsePiModelsOutput(output) {
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 rawLine of stripAnsi(output).split(/\r?\n/)) {
6031
- const line = rawLine.trim();
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(runCommand = runPiModelsCommand) {
6050
- const result = runCommand();
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) => ({ args: ["--model", 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: readCommandVersion(command) ?? void 0
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
- mkdirSync4(buildPiSessionDir(ctx.workingDirectory), { recursive: true });
6143
- const proc = spawn9(resolveCommandOnPath("pi") ?? "pi", buildPiRpcArgs(ctx, this.sessionId), {
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
- stdio: ["pipe", "pipe", "pipe"],
6146
- env: await buildPiSpawnEnv(ctx),
6147
- shell: process.platform === "win32"
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
- this.sendRpcCommand("prompt", { message: ctx.prompt });
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 in RPC mode. While you are working, Slock may send inbox-count notifications into the current turn; call `slock message check` at natural breakpoints."
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 CLI";
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(messageId) {
6781
- return messageId.startsWith("thread-") ? messageId.slice(7) : messageId.slice(0, 8);
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 = readdirSync(dir, { withFileTypes: true }).sort((a, b) => b.name.localeCompare(a.name));
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 messages may be delivered to you automatically while your process stays alive." : "The daemon will automatically restart you when new messages arrive.";
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 inbox-count notifications into your active turn. Call check_messages to read the pending messages before context-sensitive side effects.";
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 inbox-count notifications into your active turn at runtime-observed safe boundaries. Call check_messages to read the pending messages before context-sensitive side effects.";
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 call check_messages to check for messages.";
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
- const channelLabel = formatChannelLabel(wakeMessage);
8341
- prompt = runtimeProfileControlPrompt ?? `New message received:
8342
-
8343
- ${formatIncomingMessage(wakeMessage, driver)}`;
8344
- promptSource = runtimeProfileControlPrompt ? "runtime_profile_control_message" : "wake_message";
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).filter(([key]) => key !== channelLabel);
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 ${communicationCommand(driver, "read_history")} to catch up, or respond to the message above first.`;
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: this.startingInboxes.get(agentId) || [],
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 (wakeMessage) {
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 processEndedCleanly = finalCode === 0 || expectedTermination && !ap.lastRuntimeError;
8588
- const terminalFailureDetail = processEndedCleanly ? null : classifyTerminalFailure(ap);
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
- const nextMessages = ap.inbox.splice(0, ap.inbox.length);
9073
- nextMessages.push(message);
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", nextMessages);
9417
+ this.startRuntimeTrace(agentId, ap, "stdin-idle-delivery", [message]);
9076
9418
  this.broadcastActivity(agentId, "working", "Message received");
9077
- const stdinAccepted = this.deliverMessagesViaStdin(agentId, ap, nextMessages, "idle");
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.splice(0, ap.inbox.length);
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 accepted = this.deliverMessagesViaStdin(agentId, ap, messages, effect.stdinMode);
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
- this.broadcastActivity(agentId, "online", "Idle");
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 (ap.lastRuntimeError) {
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 notification = `[Slock inbox notice: You have ${inboxCount} pending inbox message${inboxCount > 1 ? "s" : ""}. Call check_messages to read ${inboxCount > 1 ? "them" : "it"} when you're ready.]`;
10599
- logger.info(`[Agent ${agentId}] Sending stdin notification: ${inboxCount} pending inbox message(s)`);
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 readdirSync2, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
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 = readdirSync2(this.traceDir).filter((name) => name.startsWith("daemon-trace-") && name.endsWith(".jsonl")).sort();
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);