@slock-ai/daemon 0.55.6 → 0.55.7-play.20260602150229

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,26 @@ var actionCardActionSchema = z.discriminatedUnion("type", [
904
938
  channelAddMemberOperationSchema
905
939
  ]);
906
940
 
941
+ // ../shared/src/agentInbox.ts
942
+ function formatAgentInboxDelta(rows) {
943
+ if (rows.length === 0) return "Inbox update: no pending targets";
944
+ return [
945
+ `Inbox update: ${rows.length} changed target${rows.length === 1 ? "" : "s"}`,
946
+ ...rows.map((row) => `${row.target} ${formatAgentInboxRowDetails(row)}`)
947
+ ].join("\n");
948
+ }
949
+ function formatAgentInboxRowDetails(row) {
950
+ const parts = [`pending: ${row.pendingCount} message${row.pendingCount === 1 ? "" : "s"}`];
951
+ if (row.firstPendingMsgId) parts.push(`first msg=${shortMessageId(row.firstPendingMsgId)}`);
952
+ if (row.latestSenderName) parts.push(`latest @${row.latestSenderName}`);
953
+ if (row.latestMsgId) parts.push(`latest msg=${shortMessageId(row.latestMsgId)}`);
954
+ parts.push(...row.flags);
955
+ return parts.join(" \xB7 ");
956
+ }
957
+ function shortMessageId(value) {
958
+ return value.slice(0, 8);
959
+ }
960
+
907
961
  // ../shared/src/translationLanguages.ts
908
962
  var SUPPORTED_TRANSLATION_LANGUAGE_CODES = [
909
963
  "en",
@@ -996,7 +1050,7 @@ var RUNTIMES = [
996
1050
  { id: "cursor", displayName: "Cursor CLI", binary: "cursor-agent", supported: true },
997
1051
  { id: "gemini", displayName: "Gemini CLI", binary: "gemini", supported: true },
998
1052
  { id: "opencode", displayName: "OpenCode", binary: "opencode", supported: true },
999
- { id: "pi", displayName: "Pi CLI", binary: "pi", supported: true }
1053
+ { id: "pi", displayName: "Pi", binary: "pi", supported: true }
1000
1054
  ];
1001
1055
  var RUNTIME_MODELS = {
1002
1056
  claude: [
@@ -1049,9 +1103,7 @@ var RUNTIME_MODELS = {
1049
1103
  { id: "fusecode/opus[1m]", label: "Opus 1M via FuseCode", verified: "suggestion_only" }
1050
1104
  ],
1051
1105
  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" }
1106
+ { id: "default", label: "Configured Default / Auto", verified: "suggestion_only" }
1055
1107
  ],
1056
1108
  // Kimi CLI resolves model keys from each user's local config, so the safest
1057
1109
  // built-in option is to defer to whatever default model the CLI already uses.
@@ -1208,7 +1260,7 @@ var DISPLAY_PLAN_CONFIG = {
1208
1260
  };
1209
1261
 
1210
1262
  // src/agentProcessManager.ts
1211
- import { mkdirSync as mkdirSync5, readdirSync, statSync, writeFileSync as writeFileSync7 } from "fs";
1263
+ import { mkdirSync as mkdirSync5, readdirSync as readdirSync2, statSync, writeFileSync as writeFileSync7 } from "fs";
1212
1264
  import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
1213
1265
  import { createHash as createHash3 } from "crypto";
1214
1266
  import path13 from "path";
@@ -1806,6 +1858,19 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
1806
1858
  return candidates.filter((candidate) => existsSync(candidate.path));
1807
1859
  }
1808
1860
 
1861
+ // src/authEnv.ts
1862
+ var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
1863
+ var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
1864
+ function scrubDaemonAuthEnv(env) {
1865
+ delete env[DAEMON_API_KEY_ENV];
1866
+ return env;
1867
+ }
1868
+ function scrubDaemonChildEnv(env) {
1869
+ delete env[DAEMON_API_KEY_ENV];
1870
+ delete env[SLOCK_AGENT_TOKEN_ENV];
1871
+ return env;
1872
+ }
1873
+
1809
1874
  // src/agentCredentialProxy.ts
1810
1875
  import { randomBytes } from "crypto";
1811
1876
  import http from "http";
@@ -2061,6 +2126,80 @@ function reduceApmGatedFlushReadiness(state, input) {
2061
2126
  };
2062
2127
  }
2063
2128
 
2129
+ // src/agentInboxProjection.ts
2130
+ function projectAgentInboxSnapshot(messages) {
2131
+ const buckets = /* @__PURE__ */ new Map();
2132
+ for (const message of messages) {
2133
+ const target = formatInboxMessageTarget(message);
2134
+ if (!target) continue;
2135
+ const bucket = buckets.get(target) ?? [];
2136
+ bucket.push(message);
2137
+ buckets.set(target, bucket);
2138
+ }
2139
+ return [...buckets.entries()].map(([target, bucket]) => projectBucket(target, bucket)).sort((a, b) => (b.latestSeq ?? 0) - (a.latestSeq ?? 0) || a.target.localeCompare(b.target));
2140
+ }
2141
+ function projectBucket(target, messages) {
2142
+ const sorted = [...messages].sort(compareInboxMessages);
2143
+ const first = sorted[0];
2144
+ const latest = sorted[sorted.length - 1];
2145
+ const flags = /* @__PURE__ */ new Set();
2146
+ for (const message of messages) {
2147
+ if (message.channel_type === "thread") flags.add("thread");
2148
+ if (message.channel_type === "dm") flags.add("dm");
2149
+ if (message.task_number || message.task_status) flags.add("task");
2150
+ if (message.mention === true || message.mentioned === true) flags.add("mention");
2151
+ }
2152
+ return stripUndefined({
2153
+ target,
2154
+ channelId: latest.channel_id ?? latest.parent_channel_id,
2155
+ channelType: latest.channel_type,
2156
+ pendingCount: messages.length,
2157
+ firstPendingMsgId: messageId(first),
2158
+ firstPendingSeq: messageSeq(first),
2159
+ latestMsgId: messageId(latest),
2160
+ latestSeq: messageSeq(latest),
2161
+ latestSenderName: latest.sender_name ?? latest.senderName,
2162
+ latestSenderType: normalizeSenderType(latest.sender_type ?? latest.senderType),
2163
+ flags: [...flags].sort()
2164
+ });
2165
+ }
2166
+ function compareInboxMessages(a, b) {
2167
+ return (messageSeq(a) ?? 0) - (messageSeq(b) ?? 0) || (messageId(a) ?? "").localeCompare(messageId(b) ?? "");
2168
+ }
2169
+ function formatInboxMessageTarget(message) {
2170
+ if (message.channel_type === "thread" && message.parent_channel_name && message.channel_name) {
2171
+ const shortId = shortMessageId2(String(message.channel_name).startsWith("thread-") ? String(message.channel_name).slice("thread-".length) : String(message.channel_name));
2172
+ if (message.parent_channel_type === "dm") return `dm:@${message.parent_channel_name}:${shortId}`;
2173
+ return `#${message.parent_channel_name}:${shortId}`;
2174
+ }
2175
+ if (message.channel_type === "dm" && message.channel_name) return `dm:@${message.channel_name}`;
2176
+ if (message.channel_name) return `#${message.channel_name}`;
2177
+ return null;
2178
+ }
2179
+ function messageId(message) {
2180
+ if (!message) return void 0;
2181
+ return nonEmptyString(message.message_id) ?? nonEmptyString(message.id);
2182
+ }
2183
+ function messageSeq(message) {
2184
+ if (!message || typeof message.seq !== "number" || !Number.isFinite(message.seq) || message.seq <= 0) return void 0;
2185
+ return Math.floor(message.seq);
2186
+ }
2187
+ function shortMessageId2(value) {
2188
+ return value.slice(0, 8);
2189
+ }
2190
+ function normalizeSenderType(value) {
2191
+ return value === "human" || value === "agent" || value === "system" ? value : void 0;
2192
+ }
2193
+ function nonEmptyString(value) {
2194
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
2195
+ }
2196
+ function stripUndefined(value) {
2197
+ for (const key of Object.keys(value)) {
2198
+ if (value[key] === void 0) delete value[key];
2199
+ }
2200
+ return value;
2201
+ }
2202
+
2064
2203
  // src/agentCredentialProxy.ts
2065
2204
  var registrations = /* @__PURE__ */ new Map();
2066
2205
  var proxyServerState = null;
@@ -2204,6 +2343,14 @@ async function handleProxyRequest(req, res) {
2204
2343
  }
2205
2344
  let sendTarget;
2206
2345
  const sideEffectAction = agentApiSideEffectAction(target.pathname);
2346
+ if (method === "GET" && target.pathname === "/internal/agent-api/inbox") {
2347
+ const localInbox = localAgentApiInboxResponse(registration);
2348
+ if (localInbox) {
2349
+ res.writeHead(localInbox.status, { "content-type": "application/json" });
2350
+ res.end(JSON.stringify(localInbox.body));
2351
+ return;
2352
+ }
2353
+ }
2207
2354
  if (method === "GET" && target.pathname === "/internal/agent-api/events") {
2208
2355
  const localEvents = localAgentApiEventsResponse(registration, target);
2209
2356
  if (localEvents) {
@@ -2347,6 +2494,7 @@ function transportNormalizedErrorForError(target, err, launchId) {
2347
2494
  function routeFamilyForPath(pathname) {
2348
2495
  if (pathname === "/internal/agent-api/send") return "agent-api/send";
2349
2496
  if (pathname === "/internal/agent-api/events") return "agent-api/events";
2497
+ if (pathname === "/internal/agent-api/inbox") return "agent-api/inbox";
2350
2498
  if (pathname === "/internal/agent-api/receive-ack") return "agent-api/events";
2351
2499
  if (pathname === "/internal/agent-api/tasks/claim") return "tasks/claim";
2352
2500
  if (pathname === "/internal/agent-api/tasks/update-status") return "tasks/update";
@@ -2403,13 +2551,13 @@ async function readRequestBody(req) {
2403
2551
  }
2404
2552
  return Buffer.concat(chunks);
2405
2553
  }
2406
- function messageSeq(message) {
2554
+ function messageSeq2(message) {
2407
2555
  return Number(message.seq ?? 0);
2408
2556
  }
2409
2557
  function maxMessageSeq(messages) {
2410
2558
  let maxSeq = 0;
2411
2559
  for (const message of messages) {
2412
- const seq = Math.floor(messageSeq(message));
2560
+ const seq = Math.floor(messageSeq2(message));
2413
2561
  if (Number.isFinite(seq) && seq > 0) maxSeq = Math.max(maxSeq, seq);
2414
2562
  }
2415
2563
  return maxSeq > 0 ? maxSeq : void 0;
@@ -2423,7 +2571,7 @@ function isSelfAuthoredMessage(registration, message) {
2423
2571
  return messageSenderId(message) === registration.agentId;
2424
2572
  }
2425
2573
  function isMessageModelSeen(coordinator, target, message) {
2426
- const seq = Math.floor(messageSeq(message));
2574
+ const seq = Math.floor(messageSeq2(message));
2427
2575
  const boundary = coordinator.getBoundary(target);
2428
2576
  if (Number.isFinite(seq) && seq > 0 && typeof boundary === "number" && boundary >= seq) return true;
2429
2577
  return coordinator.isMessageModelSeen?.({ target, message }) === true;
@@ -2433,12 +2581,31 @@ function resolveFreshnessBoundary(messages) {
2433
2581
  return typeof seenUpToSeq === "number" ? { ok: true, seenUpToSeq } : { ok: false, reason: "missing_seq_boundary" };
2434
2582
  }
2435
2583
  function sortBySeq(messages) {
2436
- return [...messages].sort((a, b) => messageSeq(a) - messageSeq(b));
2584
+ return [...messages].sort((a, b) => messageSeq2(a) - messageSeq2(b));
2437
2585
  }
2438
2586
  function latestVisibleMessages(messages, limit) {
2439
2587
  const sorted = sortBySeq(messages);
2440
2588
  return sorted.slice(Math.max(0, sorted.length - limit));
2441
2589
  }
2590
+ function localAgentApiInboxResponse(registration) {
2591
+ const coordinator = registration.inboxCoordinator;
2592
+ if (!coordinator) return void 0;
2593
+ const pending = coordinator.getAllPendingMessages?.() ?? [];
2594
+ const rows = projectAgentInboxSnapshot(pending);
2595
+ coordinator.recordInboxSnapshot?.({
2596
+ source: "agent_api_inbox_check",
2597
+ rows,
2598
+ pendingMessageCount: pending.length
2599
+ });
2600
+ return {
2601
+ status: 200,
2602
+ body: {
2603
+ rows,
2604
+ pending_targets: rows.length,
2605
+ pending_messages: pending.length
2606
+ }
2607
+ };
2608
+ }
2442
2609
  function parseAgentApiEventsQuery(target) {
2443
2610
  const limit = Math.min(Math.max(Number(target.searchParams.get("limit")) || 50, 1), 200);
2444
2611
  const sinceRaw = target.searchParams.get("since")?.trim() ?? "";
@@ -2469,7 +2636,7 @@ function localAgentApiEventsResponse(registration, target) {
2469
2636
  if (pending.length === 0) return void 0;
2470
2637
  const normalized = sortBySeq(normalizeVisibleMessages(pending));
2471
2638
  const filtered = parsedQuery.sinceSeq !== null ? normalized.filter((message) => {
2472
- const seq = messageSeq(message);
2639
+ const seq = messageSeq2(message);
2473
2640
  return Number.isFinite(seq) && seq > parsedQuery.sinceSeq;
2474
2641
  }) : normalized;
2475
2642
  const events = filtered.slice(0, parsedQuery.limit);
@@ -2794,12 +2961,12 @@ function consumeVisibleResponse(registration, targetUrl, sendTarget, responseTex
2794
2961
  return;
2795
2962
  }
2796
2963
  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) {
2964
+ const messageSeq3 = typeof parsed.messageSeq === "number" && Number.isFinite(parsed.messageSeq) ? Math.floor(parsed.messageSeq) : void 0;
2965
+ if (sendTarget && messageSeq3 && messageSeq3 > 0) {
2799
2966
  coordinator.consumeVisibleMessages({
2800
2967
  target: sendTarget,
2801
- messages: normalizeVisibleMessages([{ seq: messageSeq2, id: parsed.messageId }], sendTarget),
2802
- boundarySeq: messageSeq2,
2968
+ messages: normalizeVisibleMessages([{ seq: messageSeq3, id: parsed.messageId }], sendTarget),
2969
+ boundarySeq: messageSeq3,
2803
2970
  source: "agent_api_send_commit"
2804
2971
  });
2805
2972
  }
@@ -2859,7 +3026,9 @@ var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
2859
3026
  var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
2860
3027
  var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
2861
3028
  var RAW_CREDENTIAL_ENV_DENYLIST = [
2862
- "SLOCK_AGENT_CREDENTIAL_KEY"
3029
+ "SLOCK_AGENT_TOKEN",
3030
+ "SLOCK_AGENT_CREDENTIAL_KEY",
3031
+ "SLOCK_AGENT_CREDENTIAL_KEY_FILE"
2863
3032
  ];
2864
3033
  var cachedOpencliBinPath;
2865
3034
  function resolveOpencliBinPath() {
@@ -3074,7 +3243,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
3074
3243
  ...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
3075
3244
  PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
3076
3245
  };
3077
- delete spawnEnv.SLOCK_AGENT_TOKEN;
3246
+ scrubDaemonChildEnv(spawnEnv);
3078
3247
  for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
3079
3248
  delete spawnEnv[key];
3080
3249
  }
@@ -3503,7 +3672,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn, existsSyncFn) {
3503
3672
  }
3504
3673
  function resolveCommandOnPath(command, deps = {}) {
3505
3674
  const platform = deps.platform ?? process.platform;
3506
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
3675
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
3507
3676
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
3508
3677
  const existsSyncFn = deps.existsSyncFn ?? existsSync2;
3509
3678
  if (platform === "win32") {
@@ -3529,7 +3698,7 @@ function firstExistingPath(candidates, deps = {}) {
3529
3698
  return null;
3530
3699
  }
3531
3700
  function readCommandVersion(command, args = [], deps = {}) {
3532
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
3701
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
3533
3702
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
3534
3703
  try {
3535
3704
  const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
@@ -3873,7 +4042,7 @@ function rawResponseItemProgressEvent(message) {
3873
4042
  payloadBytes
3874
4043
  };
3875
4044
  }
3876
- function nonEmptyString(value) {
4045
+ function nonEmptyString2(value) {
3877
4046
  return typeof value === "string" && value.length > 0 ? value : void 0;
3878
4047
  }
3879
4048
  var CodexEventNormalizer = class {
@@ -3928,8 +4097,8 @@ var CodexEventNormalizer = class {
3928
4097
  }
3929
4098
  const telemetry = parseCodexTelemetryEvent(message);
3930
4099
  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);
4100
+ const telemetrySessionId = nonEmptyString2(message.params?.threadId) ?? nonEmptyString2(message.params?.thread?.id) ?? nonEmptyString2(message.params?.sessionId);
4101
+ const telemetryTurnId = nonEmptyString2(message.params?.turnId) ?? nonEmptyString2(message.params?.turn?.id);
3933
4102
  if (telemetrySessionId) {
3934
4103
  this.currentThreadId = telemetrySessionId;
3935
4104
  }
@@ -4992,11 +5161,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
4992
5161
  return parseCursorModelsOutput(String(result.stdout || ""));
4993
5162
  }
4994
5163
  function buildCursorModelProbeEnv(deps = {}) {
4995
- return withWindowsUserEnvironment({
5164
+ return scrubDaemonChildEnv(withWindowsUserEnvironment({
4996
5165
  ...deps.env ?? process.env,
4997
5166
  FORCE_COLOR: "0",
4998
5167
  NO_COLOR: "1"
4999
- }, deps);
5168
+ }, deps));
5000
5169
  }
5001
5170
  function runCursorModelsCommand() {
5002
5171
  return spawnSync("cursor-agent", ["models"], {
@@ -5052,7 +5221,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
5052
5221
  }
5053
5222
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
5054
5223
  const existsSyncFn = deps.existsSyncFn ?? existsSync5;
5055
- const env = deps.env ?? process.env;
5224
+ const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
5056
5225
  const winPath = path8.win32;
5057
5226
  let geminiEntry = null;
5058
5227
  try {
@@ -5224,13 +5393,16 @@ var GeminiDriver = class {
5224
5393
  // src/drivers/kimi.ts
5225
5394
  import { randomUUID as randomUUID2 } from "crypto";
5226
5395
  import { spawn as spawn7 } from "child_process";
5227
- import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
5396
+ import { chmodSync, existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
5228
5397
  import os3 from "os";
5229
5398
  import path9 from "path";
5230
5399
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
5231
5400
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
5232
5401
  var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
5233
5402
  var KIMI_MCP_FILE = ".slock-kimi-mcp.json";
5403
+ var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
5404
+ var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
5405
+ var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
5234
5406
  function parseToolArguments(raw) {
5235
5407
  if (typeof raw !== "string") return raw;
5236
5408
  try {
@@ -5239,6 +5411,73 @@ function parseToolArguments(raw) {
5239
5411
  return raw;
5240
5412
  }
5241
5413
  }
5414
+ function readKimiConfigSource(home = os3.homedir(), env = process.env) {
5415
+ const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
5416
+ if (inlineConfig && inlineConfig.trim()) {
5417
+ return {
5418
+ raw: inlineConfig,
5419
+ explicitPath: null,
5420
+ sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
5421
+ };
5422
+ }
5423
+ const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
5424
+ const configPath = explicitPath && explicitPath.trim() ? explicitPath : path9.join(home, ".kimi", "config.toml");
5425
+ try {
5426
+ return {
5427
+ raw: readFileSync3(configPath, "utf8"),
5428
+ explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
5429
+ sourcePath: configPath
5430
+ };
5431
+ } catch {
5432
+ return {
5433
+ raw: null,
5434
+ explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
5435
+ sourcePath: configPath
5436
+ };
5437
+ }
5438
+ }
5439
+ function buildKimiSpawnEnv(env = process.env) {
5440
+ const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
5441
+ delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
5442
+ delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
5443
+ return scrubDaemonChildEnv(spawnEnv);
5444
+ }
5445
+ function buildKimiEffectiveEnv(ctx, overrideEnv) {
5446
+ return {
5447
+ ...process.env,
5448
+ ...ctx.config.envVars || {},
5449
+ ...overrideEnv || {}
5450
+ };
5451
+ }
5452
+ function buildKimiLaunchOptions(ctx, opts = {}) {
5453
+ const env = buildKimiEffectiveEnv(ctx, opts.env);
5454
+ const source = readKimiConfigSource(opts.home ?? os3.homedir(), env);
5455
+ const args = [];
5456
+ let configFilePath = null;
5457
+ let configContent = null;
5458
+ if (source.explicitPath) {
5459
+ configFilePath = source.explicitPath;
5460
+ } else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
5461
+ configFilePath = path9.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
5462
+ configContent = source.raw;
5463
+ if (opts.writeGeneratedConfig !== false) {
5464
+ writeFileSync6(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
5465
+ chmodSync(configFilePath, 384);
5466
+ }
5467
+ }
5468
+ if (configFilePath) {
5469
+ args.push("--config-file", configFilePath);
5470
+ }
5471
+ if (ctx.config.model && ctx.config.model !== "default") {
5472
+ args.push("--model", ctx.config.model);
5473
+ }
5474
+ return {
5475
+ args,
5476
+ env: buildKimiSpawnEnv(env),
5477
+ configFilePath,
5478
+ configContent
5479
+ };
5480
+ }
5242
5481
  function resolveKimiSpawn(commandArgs, deps = {}) {
5243
5482
  return {
5244
5483
  command: resolveCommandOnPath("kimi", deps) ?? "kimi",
@@ -5262,7 +5501,25 @@ var KimiDriver = class {
5262
5501
  };
5263
5502
  model = {
5264
5503
  detectedModelsVerifiedAs: "launchable",
5265
- toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
5504
+ toLaunchSpec: (modelId, ctx, opts) => {
5505
+ if (!ctx) return { args: ["--model", modelId] };
5506
+ const launchCtx = {
5507
+ ...ctx,
5508
+ config: {
5509
+ ...ctx.config,
5510
+ model: modelId
5511
+ }
5512
+ };
5513
+ const launch = buildKimiLaunchOptions(launchCtx, {
5514
+ home: opts?.home,
5515
+ writeGeneratedConfig: false
5516
+ });
5517
+ return {
5518
+ args: launch.args,
5519
+ env: launch.env,
5520
+ configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
5521
+ };
5522
+ }
5266
5523
  };
5267
5524
  supportsStdinNotification = true;
5268
5525
  mcpToolPrefix = "";
@@ -5316,6 +5573,7 @@ var KimiDriver = class {
5316
5573
  }
5317
5574
  }
5318
5575
  }), "utf8");
5576
+ const launch = buildKimiLaunchOptions(ctx);
5319
5577
  const args = [
5320
5578
  "--wire",
5321
5579
  "--yolo",
@@ -5324,15 +5582,16 @@ var KimiDriver = class {
5324
5582
  "--mcp-config-file",
5325
5583
  mcpConfigPath,
5326
5584
  "--session",
5327
- this.sessionId
5585
+ this.sessionId,
5586
+ ...launch.args
5328
5587
  ];
5329
5588
  const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
5330
5589
  if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
5331
5590
  args.push("--model", launchRuntimeFields.model);
5332
5591
  }
5333
5592
  const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
5334
- const launch = resolveKimiSpawn(args);
5335
- const proc = spawn7(launch.command, launch.args, {
5593
+ const spawnTarget = resolveKimiSpawn(args);
5594
+ const proc = spawn7(spawnTarget.command, spawnTarget.args, {
5336
5595
  cwd: ctx.workingDirectory,
5337
5596
  stdio: ["pipe", "pipe", "pipe"],
5338
5597
  env: spawnEnv,
@@ -5340,7 +5599,7 @@ var KimiDriver = class {
5340
5599
  // and has an 8191-character command-line limit. Kimi's official
5341
5600
  // installer/uv entrypoint is an executable, so launch it directly and
5342
5601
  // keep prompts on stdin / files instead of routing through cmd.exe.
5343
- shell: launch.shell
5602
+ shell: spawnTarget.shell
5344
5603
  });
5345
5604
  proc.stdin?.write(JSON.stringify({
5346
5605
  jsonrpc: "2.0",
@@ -5454,14 +5713,9 @@ var KimiDriver = class {
5454
5713
  return detectKimiModels();
5455
5714
  }
5456
5715
  };
5457
- function detectKimiModels(home = os3.homedir()) {
5458
- const configPath = path9.join(home, ".kimi", "config.toml");
5459
- let raw;
5460
- try {
5461
- raw = readFileSync3(configPath, "utf8");
5462
- } catch {
5463
- return null;
5464
- }
5716
+ function detectKimiModels(home = os3.homedir(), opts = {}) {
5717
+ const raw = readKimiConfigSource(home, opts.env).raw;
5718
+ if (raw === null) return null;
5465
5719
  const models = [];
5466
5720
  const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
5467
5721
  const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
@@ -5725,7 +5979,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
5725
5979
  const platform = deps.platform ?? process.platform;
5726
5980
  const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
5727
5981
  const result = spawnSyncFn("opencode", ["models"], {
5728
- env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
5982
+ env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
5729
5983
  encoding: "utf8",
5730
5984
  timeout: 5e3,
5731
5985
  shell: platform === "win32"
@@ -5986,12 +6240,23 @@ var OpenCodeDriver = class {
5986
6240
 
5987
6241
  // src/drivers/pi.ts
5988
6242
  import { randomUUID as randomUUID3 } from "crypto";
5989
- import { spawn as spawn9, spawnSync as spawnSync3 } from "child_process";
5990
- import { mkdirSync as mkdirSync4 } from "fs";
6243
+ import { EventEmitter } from "events";
6244
+ import { mkdirSync as mkdirSync4, readdirSync } from "fs";
6245
+ import { PassThrough, Writable } from "stream";
5991
6246
  import path11 from "path";
6247
+ import {
6248
+ AuthStorage,
6249
+ createBashTool,
6250
+ createAgentSession,
6251
+ DefaultResourceLoader,
6252
+ getAgentDir,
6253
+ ModelRegistry,
6254
+ SessionManager,
6255
+ SettingsManager,
6256
+ VERSION as PI_SDK_VERSION
6257
+ } from "@earendil-works/pi-coding-agent";
5992
6258
  var PI_SESSION_DIR = ".pi-sessions";
5993
6259
  var PI_PROVIDER_LABELS = {
5994
- deepseek: "DeepSeek",
5995
6260
  google: "Google",
5996
6261
  openai: "OpenAI",
5997
6262
  openrouter: "OpenRouter"
@@ -5999,69 +6264,51 @@ var PI_PROVIDER_LABELS = {
5999
6264
  function buildPiSessionDir(workingDirectory) {
6000
6265
  return path11.join(workingDirectory, PI_SESSION_DIR);
6001
6266
  }
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);
6267
+ async function buildPiSpawnEnv(ctx) {
6268
+ return (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
6269
+ }
6270
+ function resolvePiModelFromRegistry(modelId, modelRegistry) {
6271
+ if (!modelId || modelId === "default") return void 0;
6272
+ const [provider, ...modelParts] = modelId.split("/");
6273
+ if (provider && modelParts.length > 0) {
6274
+ return modelRegistry.find(provider, modelParts.join("/"));
6017
6275
  }
6018
- if (sessionId) {
6019
- args.push("--session-id", sessionId);
6276
+ return modelRegistry.getAll().find((model) => model.id === modelId);
6277
+ }
6278
+ function findPiSessionFile(sessionDir, sessionId) {
6279
+ let entries;
6280
+ try {
6281
+ entries = readdirSync(sessionDir);
6282
+ } catch {
6283
+ return null;
6020
6284
  }
6021
- return args;
6285
+ const suffix = `_${sessionId}.jsonl`;
6286
+ const match = entries.find((entry) => entry.endsWith(suffix));
6287
+ return match ? path11.join(sessionDir, match) : null;
6022
6288
  }
6023
- async function buildPiSpawnEnv(ctx) {
6024
- return (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
6289
+ function piSdkEventToJsonLine(event) {
6290
+ if (event.type === "agent_end") {
6291
+ return JSON.stringify({ type: "agent_end" });
6292
+ }
6293
+ return JSON.stringify(event);
6025
6294
  }
6026
- function parsePiModelsOutput(output) {
6027
- const stripAnsi = (value) => value.replace(/\u001b\[[0-9;]*m/g, "");
6295
+ function detectPiModelsFromRegistry(modelRegistry) {
6028
6296
  const models = [];
6029
6297
  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}`;
6298
+ for (const model of modelRegistry.getAvailable()) {
6299
+ const id = `${model.provider}/${model.id}`;
6039
6300
  if (seen.has(id)) continue;
6040
6301
  seen.add(id);
6041
6302
  models.push({
6042
6303
  id,
6043
- label: `${humanizePiSegment(model)} \xB7 ${PI_PROVIDER_LABELS[provider] || humanizePiSegment(provider)}`,
6304
+ label: `${model.name || humanizePiSegment(model.id)} \xB7 ${PI_PROVIDER_LABELS[model.provider] || humanizePiSegment(model.provider)}`,
6044
6305
  verified: "launchable"
6045
6306
  });
6046
6307
  }
6047
6308
  return models.length > 0 ? { models } : null;
6048
6309
  }
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
- };
6310
+ function detectPiModels(modelRegistry = ModelRegistry.create(AuthStorage.create())) {
6311
+ return detectPiModelsFromRegistry(modelRegistry);
6065
6312
  }
6066
6313
  function humanizePiSegment(value) {
6067
6314
  return value.split(/[-_/]/).filter(Boolean).map(formatPiLabelToken).join(" ");
@@ -6071,7 +6318,6 @@ function formatPiLabelToken(token) {
6071
6318
  const specialCases = {
6072
6319
  ai: "AI",
6073
6320
  api: "API",
6074
- deepseek: "DeepSeek",
6075
6321
  flash: "Flash",
6076
6322
  gpt: "GPT",
6077
6323
  pro: "Pro",
@@ -6095,6 +6341,116 @@ function piErrorMessage(error) {
6095
6341
  }
6096
6342
  return "Unknown Pi error";
6097
6343
  }
6344
+ var PiSdkProcess = class extends EventEmitter {
6345
+ constructor(session) {
6346
+ super();
6347
+ this.session = session;
6348
+ this.stdin = new Writable({
6349
+ write: (chunk, _encoding, callback) => {
6350
+ this.handleInput(String(chunk)).then(
6351
+ () => callback(),
6352
+ (error) => {
6353
+ this.writeError(error);
6354
+ callback();
6355
+ }
6356
+ );
6357
+ }
6358
+ });
6359
+ this.session.subscribe((event) => {
6360
+ this.writeStdout(piSdkEventToJsonLine(event));
6361
+ });
6362
+ }
6363
+ stdout = new PassThrough();
6364
+ stderr = new PassThrough();
6365
+ stdin;
6366
+ pid = void 0;
6367
+ exitCode = null;
6368
+ signalCode = null;
6369
+ killed = false;
6370
+ buffer = "";
6371
+ closed = false;
6372
+ kill(signal = "SIGTERM") {
6373
+ if (this.closed) return false;
6374
+ this.killed = true;
6375
+ this.signalCode = typeof signal === "string" ? signal : null;
6376
+ void this.shutdown(null, this.signalCode);
6377
+ return true;
6378
+ }
6379
+ ref() {
6380
+ return this;
6381
+ }
6382
+ unref() {
6383
+ return this;
6384
+ }
6385
+ async handleInput(chunk) {
6386
+ this.buffer += chunk;
6387
+ const lines = this.buffer.split("\n");
6388
+ this.buffer = lines.pop() || "";
6389
+ for (const line of lines) {
6390
+ if (!line.trim() || this.closed) continue;
6391
+ await this.handleCommand(line);
6392
+ }
6393
+ }
6394
+ async handleCommand(raw) {
6395
+ let command;
6396
+ try {
6397
+ command = JSON.parse(raw);
6398
+ } catch (error) {
6399
+ this.writeError(error);
6400
+ return;
6401
+ }
6402
+ const id = typeof command.id === "string" ? command.id : void 0;
6403
+ try {
6404
+ if (command.type === "prompt") {
6405
+ await this.session.prompt(String(command.message ?? ""));
6406
+ this.writeStdout(JSON.stringify({ id, type: "response", command: "prompt", success: true }));
6407
+ } else if (command.type === "steer") {
6408
+ await this.session.steer(String(command.message ?? ""));
6409
+ this.writeStdout(JSON.stringify({ id, type: "response", command: "steer", success: true }));
6410
+ } else {
6411
+ throw new Error(`Unsupported Pi SDK command: ${command.type || "unknown"}`);
6412
+ }
6413
+ } catch (error) {
6414
+ this.writeStdout(JSON.stringify({
6415
+ id,
6416
+ type: "response",
6417
+ command: command.type || "unknown",
6418
+ success: false,
6419
+ error: piErrorMessage(error)
6420
+ }));
6421
+ }
6422
+ }
6423
+ writeStdout(line) {
6424
+ if (this.closed) return;
6425
+ this.stdout.write(line + "\n");
6426
+ }
6427
+ writeError(error) {
6428
+ if (this.closed) return;
6429
+ this.stderr.write(piErrorMessage(error) + "\n");
6430
+ }
6431
+ async shutdown(code, signal) {
6432
+ if (this.closed) return;
6433
+ this.closed = true;
6434
+ this.exitCode = code;
6435
+ this.signalCode = signal;
6436
+ try {
6437
+ if (this.session.isStreaming) {
6438
+ await this.session.abort();
6439
+ }
6440
+ } catch (error) {
6441
+ this.stderr.write(piErrorMessage(error) + "\n");
6442
+ }
6443
+ try {
6444
+ this.session.dispose();
6445
+ } catch {
6446
+ }
6447
+ this.stdin.destroy();
6448
+ this.stdout.end();
6449
+ this.stderr.end();
6450
+ this.emit("exit", code, signal);
6451
+ this.emit("close", code, signal);
6452
+ }
6453
+ };
6098
6454
  var PiDriver = class {
6099
6455
  id = "pi";
6100
6456
  supportsNativeStandingPrompt = true;
@@ -6112,7 +6468,7 @@ var PiDriver = class {
6112
6468
  };
6113
6469
  model = {
6114
6470
  detectedModelsVerifiedAs: "launchable",
6115
- toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
6471
+ toLaunchSpec: (modelId) => ({ params: { model: modelId } })
6116
6472
  };
6117
6473
  supportsStdinNotification = true;
6118
6474
  mcpToolPrefix = "";
@@ -6124,11 +6480,9 @@ var PiDriver = class {
6124
6480
  requestId = 0;
6125
6481
  process = null;
6126
6482
  probe() {
6127
- const command = resolveCommandOnPath("pi");
6128
- if (!command) return { available: false };
6129
6483
  return {
6130
6484
  available: true,
6131
- version: readCommandVersion(command) ?? void 0
6485
+ version: PI_SDK_VERSION
6132
6486
  };
6133
6487
  }
6134
6488
  async detectModels() {
@@ -6139,15 +6493,57 @@ var PiDriver = class {
6139
6493
  this.sessionAnnounced = false;
6140
6494
  this.sawTextDelta = false;
6141
6495
  this.requestId = 0;
6142
- mkdirSync4(buildPiSessionDir(ctx.workingDirectory), { recursive: true });
6143
- const proc = spawn9(resolveCommandOnPath("pi") ?? "pi", buildPiRpcArgs(ctx, this.sessionId), {
6496
+ const sessionDir = buildPiSessionDir(ctx.workingDirectory);
6497
+ mkdirSync4(sessionDir, { recursive: true });
6498
+ const spawnEnv = await buildPiSpawnEnv(ctx);
6499
+ const agentDir = spawnEnv.PI_CODING_AGENT_DIR || getAgentDir();
6500
+ const authStorage = AuthStorage.create(path11.join(agentDir, "auth.json"));
6501
+ const modelRegistry = ModelRegistry.create(authStorage, path11.join(agentDir, "models.json"));
6502
+ const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
6503
+ const model = resolvePiModelFromRegistry(launchRuntimeFields.model, modelRegistry);
6504
+ if (launchRuntimeFields.model && launchRuntimeFields.model !== "default" && !model) {
6505
+ throw new Error(`Pi model not found: ${launchRuntimeFields.model}`);
6506
+ }
6507
+ const settingsManager = SettingsManager.inMemory({ compaction: { enabled: false } });
6508
+ const resourceLoader = new DefaultResourceLoader({
6144
6509
  cwd: ctx.workingDirectory,
6145
- stdio: ["pipe", "pipe", "pipe"],
6146
- env: await buildPiSpawnEnv(ctx),
6147
- shell: process.platform === "win32"
6510
+ agentDir,
6511
+ settingsManager,
6512
+ systemPromptOverride: () => ctx.standingPrompt
6513
+ });
6514
+ await resourceLoader.reload();
6515
+ const existingSessionFile = ctx.config.sessionId ? findPiSessionFile(sessionDir, ctx.config.sessionId) : null;
6516
+ const sessionManager = existingSessionFile ? SessionManager.open(existingSessionFile, sessionDir, ctx.workingDirectory) : SessionManager.create(ctx.workingDirectory, sessionDir, { id: this.sessionId });
6517
+ const { session } = await createAgentSession({
6518
+ cwd: ctx.workingDirectory,
6519
+ agentDir,
6520
+ model,
6521
+ thinkingLevel: launchRuntimeFields.reasoningEffort,
6522
+ authStorage,
6523
+ modelRegistry,
6524
+ resourceLoader,
6525
+ customTools: [
6526
+ createBashTool(ctx.workingDirectory, {
6527
+ spawnHook: (spawnContext) => ({
6528
+ ...spawnContext,
6529
+ env: {
6530
+ ...spawnContext.env,
6531
+ ...spawnEnv
6532
+ }
6533
+ })
6534
+ })
6535
+ ],
6536
+ sessionManager,
6537
+ settingsManager
6148
6538
  });
6539
+ this.sessionId = session.sessionId;
6540
+ const proc = new PiSdkProcess(session);
6149
6541
  this.process = proc;
6150
- this.sendRpcCommand("prompt", { message: ctx.prompt });
6542
+ setImmediate(() => {
6543
+ if (this.process === proc && !proc.killed) {
6544
+ this.sendRpcCommand("prompt", { message: ctx.prompt });
6545
+ }
6546
+ });
6151
6547
  return { process: proc };
6152
6548
  }
6153
6549
  parseLine(line) {
@@ -6236,7 +6632,7 @@ var PiDriver = class {
6236
6632
  toolPrefix: "",
6237
6633
  extraCriticalRules: [],
6238
6634
  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."
6635
+ "**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
6636
  ],
6241
6637
  includeStdinNotificationSection: true,
6242
6638
  messageNotificationStyle: "direct"
@@ -6531,7 +6927,7 @@ function runtimeDisplayName(runtimeId) {
6531
6927
  case "opencode":
6532
6928
  return "OpenCode";
6533
6929
  case "pi":
6534
- return "Pi CLI";
6930
+ return "Pi";
6535
6931
  default:
6536
6932
  return runtimeId || "This runtime";
6537
6933
  }
@@ -6777,8 +7173,8 @@ function formatVisibleMessageTarget(message) {
6777
7173
  }
6778
7174
  return null;
6779
7175
  }
6780
- function getMessageShortId(messageId) {
6781
- return messageId.startsWith("thread-") ? messageId.slice(7) : messageId.slice(0, 8);
7176
+ function getMessageShortId(messageId2) {
7177
+ return messageId2.startsWith("thread-") ? messageId2.slice(7) : messageId2.slice(0, 8);
6782
7178
  }
6783
7179
  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
7180
  function findSessionJsonl(root, predicate) {
@@ -6789,7 +7185,7 @@ function findSessionJsonl(root, predicate) {
6789
7185
  if (depth < 0 || visited >= maxEntries) return null;
6790
7186
  let entries;
6791
7187
  try {
6792
- entries = readdirSync(dir, { withFileTypes: true }).sort((a, b) => b.name.localeCompare(a.name));
7188
+ entries = readdirSync2(dir, { withFileTypes: true }).sort((a, b) => b.name.localeCompare(a.name));
6793
7189
  } catch {
6794
7190
  return null;
6795
7191
  }
@@ -7759,7 +8155,11 @@ var RUNTIME_TELEMETRY_RESERVED_ATTR_KEYS = /* @__PURE__ */ new Set([
7759
8155
  "usageKind",
7760
8156
  "sessionId",
7761
8157
  "turnId",
7762
- "runtimeResultId"
8158
+ "runtimeResultId",
8159
+ "daemonVersion",
8160
+ "daemon_version",
8161
+ "computerVersion",
8162
+ "computer_version"
7763
8163
  ]);
7764
8164
  function sanitizeRuntimeTelemetryPayloadAttrs(attrs) {
7765
8165
  const sanitized = {};
@@ -7979,6 +8379,13 @@ var AgentProcessManager = class _AgentProcessManager {
7979
8379
  getPendingMessages: (target) => this.pendingVisibleMessages(agentId, target),
7980
8380
  isMessageModelSeen: ({ target, message }) => this.isVisibleMessageModelSeen(agentId, target, message),
7981
8381
  getAllPendingMessages: () => this.allPendingVisibleMessages(agentId),
8382
+ recordInboxSnapshot: (input) => {
8383
+ this.recordDaemonTrace("daemon.agent.inbox_projection.snapshot", {
8384
+ agentId,
8385
+ source: input.source,
8386
+ ...this.inboxProjectionTraceAttrs(input.rows, input.pendingMessageCount)
8387
+ });
8388
+ },
7982
8389
  consumeVisibleMessages: (input) => this.consumeVisibleMessages(agentId, input),
7983
8390
  recordDrainOutcome: (input) => {
7984
8391
  this.recordDaemonTrace("daemon.agent.drain.outcome", {
@@ -9905,6 +10312,29 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9905
10312
  ...messageProducerFactTraceAttrs(messages)
9906
10313
  };
9907
10314
  }
10315
+ inboxProjectionTraceAttrs(rows, pendingMessageCount) {
10316
+ let maxPendingCount = 0;
10317
+ let mentionTargetCount = 0;
10318
+ let threadTargetCount = 0;
10319
+ let dmTargetCount = 0;
10320
+ let taskTargetCount = 0;
10321
+ for (const row of rows) {
10322
+ maxPendingCount = Math.max(maxPendingCount, row.pendingCount);
10323
+ if (row.flags.includes("mention")) mentionTargetCount += 1;
10324
+ if (row.flags.includes("thread")) threadTargetCount += 1;
10325
+ if (row.flags.includes("dm")) dmTargetCount += 1;
10326
+ if (row.flags.includes("task")) taskTargetCount += 1;
10327
+ }
10328
+ return {
10329
+ inbox_target_count: rows.length,
10330
+ pending_message_count: pendingMessageCount,
10331
+ max_pending_per_target: maxPendingCount,
10332
+ mention_target_count: mentionTargetCount,
10333
+ thread_target_count: threadTargetCount,
10334
+ dm_target_count: dmTargetCount,
10335
+ task_target_count: taskTargetCount
10336
+ };
10337
+ }
9908
10338
  runtimeProfileTurnControlTraceAttrs(control) {
9909
10339
  if (!control) return {};
9910
10340
  const pendingAgeMs = Math.max(0, Date.now() - control.injectedAtMs);
@@ -10595,11 +11025,29 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10595
11025
  }
10596
11026
  const inboxCount = ap.inbox.length;
10597
11027
  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)`);
11028
+ const inboxRows = projectAgentInboxSnapshot(ap.inbox);
11029
+ const notification = `[Slock inbox notice:
11030
+ ${formatAgentInboxDelta(inboxRows)}]`;
11031
+ const notificationByteCount = Buffer.byteLength(notification, "utf8");
11032
+ const projectionAttrs = this.inboxProjectionTraceAttrs(inboxRows, inboxCount);
11033
+ this.recordDaemonTrace("daemon.agent.inbox_projection.delta", {
11034
+ agentId,
11035
+ source: "busy_stdin_notification",
11036
+ ...projectionAttrs
11037
+ });
11038
+ logger.info(`[Agent ${agentId}] Sending stdin inbox update: ${inboxRows.length} changed target(s), ${inboxCount} pending message(s)`);
10600
11039
  const encoded = ap.driver.encodeStdinMessage(notification, ap.sessionId, { mode: "busy" });
10601
11040
  if (encoded) {
10602
11041
  ap.process.stdin?.write(encoded + "\n");
11042
+ this.recordDaemonTrace("daemon.agent.inbox_update.pushed", {
11043
+ agentId,
11044
+ runtime: ap.config.runtime,
11045
+ model: ap.config.model,
11046
+ launchId: ap.launchId || void 0,
11047
+ mode: "busy",
11048
+ notification_byte_count: notificationByteCount,
11049
+ ...projectionAttrs
11050
+ });
10603
11051
  this.recordDaemonTrace("daemon.agent.stdin_notification", {
10604
11052
  agentId,
10605
11053
  runtime: ap.config.runtime,
@@ -10609,6 +11057,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10609
11057
  mode: "busy",
10610
11058
  pending_notification_count: count,
10611
11059
  inbox_count: inboxCount,
11060
+ inbox_target_count: inboxRows.length,
10612
11061
  session_id_present: true
10613
11062
  });
10614
11063
  return true;
@@ -10626,6 +11075,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10626
11075
  retry_scheduled: retryScheduled,
10627
11076
  notification_timer_present: ap.notifications.hasTimer,
10628
11077
  inbox_count: inboxCount,
11078
+ inbox_target_count: inboxRows.length,
10629
11079
  session_id_present: true
10630
11080
  }, "error");
10631
11081
  return false;
@@ -11163,7 +11613,7 @@ function acquireDaemonMachineLock(options) {
11163
11613
  }
11164
11614
 
11165
11615
  // src/localTraceSink.ts
11166
- import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync2, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
11616
+ import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync3, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
11167
11617
  import path15 from "path";
11168
11618
  var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
11169
11619
  var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
@@ -11242,7 +11692,7 @@ var LocalRotatingTraceSink = class {
11242
11692
  }
11243
11693
  }
11244
11694
  pruneOldFiles() {
11245
- const files = readdirSync2(this.traceDir).filter((name) => name.startsWith("daemon-trace-") && name.endsWith(".jsonl")).sort();
11695
+ const files = readdirSync3(this.traceDir).filter((name) => name.startsWith("daemon-trace-") && name.endsWith(".jsonl")).sort();
11246
11696
  const excess = files.length - this.maxFiles;
11247
11697
  if (excess <= 0) return;
11248
11698
  for (const file of files.slice(0, excess)) {
@@ -11684,7 +12134,7 @@ var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
11684
12134
  var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"];
11685
12135
  var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
11686
12136
  var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
11687
- var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
12137
+ var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
11688
12138
  var RunnerCredentialMintError2 = class extends Error {
11689
12139
  code;
11690
12140
  retryable;
@@ -11720,9 +12170,9 @@ function runnerCredentialErrorDetail2(error) {
11720
12170
  async function waitForRunnerCredentialRetry2() {
11721
12171
  await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
11722
12172
  }
11723
- function parseDaemonCliArgs(args) {
12173
+ function parseDaemonCliArgs(args, env = {}) {
11724
12174
  let serverUrl = "";
11725
- let apiKey = "";
12175
+ let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
11726
12176
  for (let i = 0; i < args.length; i++) {
11727
12177
  if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
11728
12178
  if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
@@ -11911,7 +12361,7 @@ var DaemonCore = class {
11911
12361
  this.slockHome = resolveSlockHome();
11912
12362
  process.env[SLOCK_HOME_ENV] = this.slockHome;
11913
12363
  this.injectedTracer = Boolean(options.tracer);
11914
- this.tracer = options.tracer ?? noopTracer;
12364
+ this.tracer = this.withDaemonTraceScope(options.tracer ?? noopTracer);
11915
12365
  this.runtimeDetector = options.runtimeDetector ?? (() => detectRuntimes(this.tracer));
11916
12366
  this.reminderCache = new ReminderCache({
11917
12367
  clock: options.reminderClock,
@@ -11962,9 +12412,9 @@ var DaemonCore = class {
11962
12412
  maxFileAgeJitterMs: jitter.maxFileAgeJitterMs,
11963
12413
  maxFiles: this.options.localTraceMaxFiles ?? readPositiveIntegerEnv3("SLOCK_DAEMON_TRACE_MAX_FILES", 8)
11964
12414
  });
11965
- this.tracer = new BasicTracer({
12415
+ this.tracer = this.withDaemonTraceScope(new BasicTracer({
11966
12416
  sink: this.localTraceSink
11967
- });
12417
+ }));
11968
12418
  this.agentManager.setTracer(this.tracer);
11969
12419
  this.agentManager.setCliTransportTraceDir(path17.join(machineDir, "traces"));
11970
12420
  }
@@ -12058,6 +12508,23 @@ var DaemonCore = class {
12058
12508
  });
12059
12509
  span.end(status);
12060
12510
  }
12511
+ withDaemonTraceScope(tracer) {
12512
+ return createScopedTracer(tracer, this.daemonTraceScopeAttrs());
12513
+ }
12514
+ daemonTraceScopeAttrs() {
12515
+ return {
12516
+ daemonVersion: this.daemonVersion,
12517
+ daemon_version: this.daemonVersion,
12518
+ daemon_version_present: Boolean(this.daemonVersion),
12519
+ ...this.computerVersion ? {
12520
+ computerVersion: this.computerVersion,
12521
+ computer_version: this.computerVersion,
12522
+ computer_version_present: true
12523
+ } : {
12524
+ computer_version_present: false
12525
+ }
12526
+ };
12527
+ }
12061
12528
  async requestRunnerCredentialOnce(agentId, config) {
12062
12529
  const url = new URL(`/internal/computer/runners/${encodeURIComponent(agentId)}/credentials`, this.options.serverUrl);
12063
12530
  const res = await daemonFetch(url, {
@@ -12418,8 +12885,7 @@ var DaemonCore = class {
12418
12885
  runtimes_count: runtimes.length,
12419
12886
  running_agents_count: runningAgentIds.length,
12420
12887
  idle_agents_count: idleAgentSessions.length,
12421
- runtime_profile_reports_count: runtimeProfileReports.length,
12422
- daemon_version_present: Boolean(this.daemonVersion)
12888
+ runtime_profile_reports_count: runtimeProfileReports.length
12423
12889
  });
12424
12890
  for (const agentId of runningAgentIds) {
12425
12891
  const sessionId = this.agentManager.getAgentSessionId(agentId);
@@ -12473,6 +12939,8 @@ var DaemonCore = class {
12473
12939
  };
12474
12940
 
12475
12941
  export {
12942
+ DAEMON_API_KEY_ENV,
12943
+ scrubDaemonAuthEnv,
12476
12944
  resolveWorkspaceDirectoryPath,
12477
12945
  scanWorkspaceDirectories,
12478
12946
  deleteWorkspaceDirectory,