@slock-ai/daemon 0.55.5 → 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
  }
@@ -3175,7 +3344,7 @@ function collectModelUsageAttrs(value) {
3175
3344
  modelUsageMaxOutputTokens: aggregate.maxOutputTokens
3176
3345
  });
3177
3346
  }
3178
- function parseClaudeResultUsageTelemetry(event) {
3347
+ function parseClaudeResultUsageTelemetry(event, sessionId) {
3179
3348
  const usage = event.usage && typeof event.usage === "object" ? event.usage : void 0;
3180
3349
  const totalCostUsd = finiteNumber(event.total_cost_usd);
3181
3350
  const modelUsageAttrs = collectModelUsageAttrs(event.modelUsage);
@@ -3224,12 +3393,16 @@ function parseClaudeResultUsageTelemetry(event) {
3224
3393
  name: "token_usage",
3225
3394
  source: "claude_result_usage",
3226
3395
  usageKind: "per_turn",
3227
- ...typeof event.session_id === "string" && event.session_id ? { sessionId: event.session_id } : {},
3396
+ ...sessionId ? { sessionId } : {},
3228
3397
  ...typeof event.uuid === "string" && event.uuid ? { runtimeResultId: event.uuid } : {},
3229
3398
  attrs
3230
3399
  };
3231
3400
  }
3232
3401
  var ClaudeEventNormalizer = class {
3402
+ currentSession = null;
3403
+ get currentSessionId() {
3404
+ return this.currentSession;
3405
+ }
3233
3406
  normalizeLine(line) {
3234
3407
  let event;
3235
3408
  try {
@@ -3238,13 +3411,17 @@ var ClaudeEventNormalizer = class {
3238
3411
  return [];
3239
3412
  }
3240
3413
  const events = [];
3414
+ const eventSessionId = finiteString(event.session_id);
3415
+ if (eventSessionId) {
3416
+ this.currentSession = eventSessionId;
3417
+ }
3241
3418
  const pushResultError = (message, fallback) => {
3242
3419
  events.push({ kind: "error", message: collectResultErrorDetail(message, fallback) });
3243
3420
  };
3244
3421
  switch (event.type) {
3245
3422
  case "system":
3246
- if (event.subtype === "init" && event.session_id) {
3247
- events.push({ kind: "session_init", sessionId: event.session_id });
3423
+ if (event.subtype === "init" && eventSessionId) {
3424
+ events.push({ kind: "session_init", sessionId: eventSessionId });
3248
3425
  }
3249
3426
  if (event.subtype === "status" && event.status === "compacting") {
3250
3427
  events.push({ kind: "compaction_started" });
@@ -3303,7 +3480,8 @@ var ClaudeEventNormalizer = class {
3303
3480
  case "result": {
3304
3481
  const subtype = typeof event.subtype === "string" ? event.subtype : "success";
3305
3482
  const stopReason = typeof event.stop_reason === "string" ? event.stop_reason : null;
3306
- const usageTelemetry = parseClaudeResultUsageTelemetry(event);
3483
+ const resultSessionId = eventSessionId ?? this.currentSession;
3484
+ const usageTelemetry = parseClaudeResultUsageTelemetry(event, resultSessionId);
3307
3485
  switch (subtype) {
3308
3486
  case "success":
3309
3487
  if (event.is_error && stopReason !== "max_tokens") {
@@ -3326,7 +3504,7 @@ var ClaudeEventNormalizer = class {
3326
3504
  break;
3327
3505
  }
3328
3506
  if (usageTelemetry) events.push(usageTelemetry);
3329
- events.push({ kind: "turn_end", sessionId: event.session_id });
3507
+ events.push({ kind: "turn_end", sessionId: resultSessionId || void 0 });
3330
3508
  break;
3331
3509
  }
3332
3510
  }
@@ -3494,7 +3672,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn, existsSyncFn) {
3494
3672
  }
3495
3673
  function resolveCommandOnPath(command, deps = {}) {
3496
3674
  const platform = deps.platform ?? process.platform;
3497
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
3675
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
3498
3676
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
3499
3677
  const existsSyncFn = deps.existsSyncFn ?? existsSync2;
3500
3678
  if (platform === "win32") {
@@ -3520,7 +3698,7 @@ function firstExistingPath(candidates, deps = {}) {
3520
3698
  return null;
3521
3699
  }
3522
3700
  function readCommandVersion(command, args = [], deps = {}) {
3523
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
3701
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
3524
3702
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
3525
3703
  try {
3526
3704
  const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
@@ -3680,6 +3858,9 @@ var ClaudeDriver = class {
3680
3858
  parseLine(line) {
3681
3859
  return this.eventNormalizer.normalizeLine(line);
3682
3860
  }
3861
+ get currentSessionId() {
3862
+ return this.eventNormalizer.currentSessionId;
3863
+ }
3683
3864
  encodeStdinMessage(text, sessionId, _opts) {
3684
3865
  return JSON.stringify({
3685
3866
  type: "user",
@@ -3861,6 +4042,9 @@ function rawResponseItemProgressEvent(message) {
3861
4042
  payloadBytes
3862
4043
  };
3863
4044
  }
4045
+ function nonEmptyString2(value) {
4046
+ return typeof value === "string" && value.length > 0 ? value : void 0;
4047
+ }
3864
4048
  var CodexEventNormalizer = class {
3865
4049
  mcpToolPrefix;
3866
4050
  currentThreadId = null;
@@ -3913,10 +4097,17 @@ var CodexEventNormalizer = class {
3913
4097
  }
3914
4098
  const telemetry = parseCodexTelemetryEvent(message);
3915
4099
  if (telemetry) {
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);
4102
+ if (telemetrySessionId) {
4103
+ this.currentThreadId = telemetrySessionId;
4104
+ }
4105
+ const sessionId = telemetrySessionId ?? this.currentThreadId ?? void 0;
4106
+ const turnId = telemetryTurnId ?? this.turnState.activeTurnId ?? void 0;
3916
4107
  events.push({
3917
4108
  ...telemetry,
3918
- ...this.currentThreadId ? { sessionId: this.currentThreadId } : {},
3919
- ...this.turnState.activeTurnId ? { turnId: this.turnState.activeTurnId } : {}
4109
+ ...sessionId ? { sessionId } : {},
4110
+ ...turnId ? { turnId } : {}
3920
4111
  });
3921
4112
  return { events };
3922
4113
  }
@@ -4365,6 +4556,9 @@ var CodexDriver = class {
4365
4556
  }
4366
4557
  return result.events;
4367
4558
  }
4559
+ get currentSessionId() {
4560
+ return this.normalizer.threadId;
4561
+ }
4368
4562
  encodeStdinMessage(text, sessionId, opts) {
4369
4563
  if (!this.normalizer.threadId && sessionId) {
4370
4564
  this.normalizer.adoptThreadId(sessionId);
@@ -4967,11 +5161,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
4967
5161
  return parseCursorModelsOutput(String(result.stdout || ""));
4968
5162
  }
4969
5163
  function buildCursorModelProbeEnv(deps = {}) {
4970
- return withWindowsUserEnvironment({
5164
+ return scrubDaemonChildEnv(withWindowsUserEnvironment({
4971
5165
  ...deps.env ?? process.env,
4972
5166
  FORCE_COLOR: "0",
4973
5167
  NO_COLOR: "1"
4974
- }, deps);
5168
+ }, deps));
4975
5169
  }
4976
5170
  function runCursorModelsCommand() {
4977
5171
  return spawnSync("cursor-agent", ["models"], {
@@ -5027,7 +5221,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
5027
5221
  }
5028
5222
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
5029
5223
  const existsSyncFn = deps.existsSyncFn ?? existsSync5;
5030
- const env = deps.env ?? process.env;
5224
+ const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
5031
5225
  const winPath = path8.win32;
5032
5226
  let geminiEntry = null;
5033
5227
  try {
@@ -5199,13 +5393,16 @@ var GeminiDriver = class {
5199
5393
  // src/drivers/kimi.ts
5200
5394
  import { randomUUID as randomUUID2 } from "crypto";
5201
5395
  import { spawn as spawn7 } from "child_process";
5202
- 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";
5203
5397
  import os3 from "os";
5204
5398
  import path9 from "path";
5205
5399
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
5206
5400
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
5207
5401
  var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
5208
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";
5209
5406
  function parseToolArguments(raw) {
5210
5407
  if (typeof raw !== "string") return raw;
5211
5408
  try {
@@ -5214,6 +5411,73 @@ function parseToolArguments(raw) {
5214
5411
  return raw;
5215
5412
  }
5216
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
+ }
5217
5481
  function resolveKimiSpawn(commandArgs, deps = {}) {
5218
5482
  return {
5219
5483
  command: resolveCommandOnPath("kimi", deps) ?? "kimi",
@@ -5237,7 +5501,25 @@ var KimiDriver = class {
5237
5501
  };
5238
5502
  model = {
5239
5503
  detectedModelsVerifiedAs: "launchable",
5240
- 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
+ }
5241
5523
  };
5242
5524
  supportsStdinNotification = true;
5243
5525
  mcpToolPrefix = "";
@@ -5291,6 +5573,7 @@ var KimiDriver = class {
5291
5573
  }
5292
5574
  }
5293
5575
  }), "utf8");
5576
+ const launch = buildKimiLaunchOptions(ctx);
5294
5577
  const args = [
5295
5578
  "--wire",
5296
5579
  "--yolo",
@@ -5299,15 +5582,16 @@ var KimiDriver = class {
5299
5582
  "--mcp-config-file",
5300
5583
  mcpConfigPath,
5301
5584
  "--session",
5302
- this.sessionId
5585
+ this.sessionId,
5586
+ ...launch.args
5303
5587
  ];
5304
5588
  const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
5305
5589
  if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
5306
5590
  args.push("--model", launchRuntimeFields.model);
5307
5591
  }
5308
5592
  const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
5309
- const launch = resolveKimiSpawn(args);
5310
- const proc = spawn7(launch.command, launch.args, {
5593
+ const spawnTarget = resolveKimiSpawn(args);
5594
+ const proc = spawn7(spawnTarget.command, spawnTarget.args, {
5311
5595
  cwd: ctx.workingDirectory,
5312
5596
  stdio: ["pipe", "pipe", "pipe"],
5313
5597
  env: spawnEnv,
@@ -5315,7 +5599,7 @@ var KimiDriver = class {
5315
5599
  // and has an 8191-character command-line limit. Kimi's official
5316
5600
  // installer/uv entrypoint is an executable, so launch it directly and
5317
5601
  // keep prompts on stdin / files instead of routing through cmd.exe.
5318
- shell: launch.shell
5602
+ shell: spawnTarget.shell
5319
5603
  });
5320
5604
  proc.stdin?.write(JSON.stringify({
5321
5605
  jsonrpc: "2.0",
@@ -5429,14 +5713,9 @@ var KimiDriver = class {
5429
5713
  return detectKimiModels();
5430
5714
  }
5431
5715
  };
5432
- function detectKimiModels(home = os3.homedir()) {
5433
- const configPath = path9.join(home, ".kimi", "config.toml");
5434
- let raw;
5435
- try {
5436
- raw = readFileSync3(configPath, "utf8");
5437
- } catch {
5438
- return null;
5439
- }
5716
+ function detectKimiModels(home = os3.homedir(), opts = {}) {
5717
+ const raw = readKimiConfigSource(home, opts.env).raw;
5718
+ if (raw === null) return null;
5440
5719
  const models = [];
5441
5720
  const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
5442
5721
  const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
@@ -5700,7 +5979,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
5700
5979
  const platform = deps.platform ?? process.platform;
5701
5980
  const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
5702
5981
  const result = spawnSyncFn("opencode", ["models"], {
5703
- env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
5982
+ env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
5704
5983
  encoding: "utf8",
5705
5984
  timeout: 5e3,
5706
5985
  shell: platform === "win32"
@@ -5961,12 +6240,23 @@ var OpenCodeDriver = class {
5961
6240
 
5962
6241
  // src/drivers/pi.ts
5963
6242
  import { randomUUID as randomUUID3 } from "crypto";
5964
- import { spawn as spawn9, spawnSync as spawnSync3 } from "child_process";
5965
- 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";
5966
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";
5967
6258
  var PI_SESSION_DIR = ".pi-sessions";
5968
6259
  var PI_PROVIDER_LABELS = {
5969
- deepseek: "DeepSeek",
5970
6260
  google: "Google",
5971
6261
  openai: "OpenAI",
5972
6262
  openrouter: "OpenRouter"
@@ -5974,69 +6264,51 @@ var PI_PROVIDER_LABELS = {
5974
6264
  function buildPiSessionDir(workingDirectory) {
5975
6265
  return path11.join(workingDirectory, PI_SESSION_DIR);
5976
6266
  }
5977
- function buildPiRpcArgs(ctx, sessionId) {
5978
- const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
5979
- const args = [
5980
- "--mode",
5981
- "rpc",
5982
- "--session-dir",
5983
- buildPiSessionDir(ctx.workingDirectory),
5984
- "--system-prompt",
5985
- ctx.standingPrompt
5986
- ];
5987
- if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
5988
- args.push("--model", launchRuntimeFields.model);
5989
- }
5990
- if (launchRuntimeFields.reasoningEffort) {
5991
- 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("/"));
5992
6275
  }
5993
- if (sessionId) {
5994
- 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;
5995
6284
  }
5996
- return args;
6285
+ const suffix = `_${sessionId}.jsonl`;
6286
+ const match = entries.find((entry) => entry.endsWith(suffix));
6287
+ return match ? path11.join(sessionDir, match) : null;
5997
6288
  }
5998
- async function buildPiSpawnEnv(ctx) {
5999
- 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);
6000
6294
  }
6001
- function parsePiModelsOutput(output) {
6002
- const stripAnsi = (value) => value.replace(/\u001b\[[0-9;]*m/g, "");
6295
+ function detectPiModelsFromRegistry(modelRegistry) {
6003
6296
  const models = [];
6004
6297
  const seen = /* @__PURE__ */ new Set();
6005
- for (const rawLine of stripAnsi(output).split(/\r?\n/)) {
6006
- const line = rawLine.trim();
6007
- if (!line || /^provider\s+model\s+/i.test(line) || /^[-\s]+$/.test(line)) continue;
6008
- const columns = line.split(/\s+/);
6009
- const provider = columns[0];
6010
- const model = columns[1];
6011
- if (!provider || !model || provider.startsWith("-") || model.startsWith("-")) continue;
6012
- if (/^(yes|no)$/i.test(model)) continue;
6013
- const id = `${provider}/${model}`;
6298
+ for (const model of modelRegistry.getAvailable()) {
6299
+ const id = `${model.provider}/${model.id}`;
6014
6300
  if (seen.has(id)) continue;
6015
6301
  seen.add(id);
6016
6302
  models.push({
6017
6303
  id,
6018
- 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)}`,
6019
6305
  verified: "launchable"
6020
6306
  });
6021
6307
  }
6022
6308
  return models.length > 0 ? { models } : null;
6023
6309
  }
6024
- function detectPiModels(runCommand = runPiModelsCommand) {
6025
- const result = runCommand();
6026
- if (result.error || result.status !== 0) return null;
6027
- return parsePiModelsOutput(result.stdout);
6028
- }
6029
- function runPiModelsCommand() {
6030
- const result = spawnSync3("pi", ["--list-models"], {
6031
- env: { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" },
6032
- encoding: "utf8",
6033
- timeout: 5e3
6034
- });
6035
- return {
6036
- status: result.status,
6037
- stdout: String(result.stdout || ""),
6038
- error: result.error
6039
- };
6310
+ function detectPiModels(modelRegistry = ModelRegistry.create(AuthStorage.create())) {
6311
+ return detectPiModelsFromRegistry(modelRegistry);
6040
6312
  }
6041
6313
  function humanizePiSegment(value) {
6042
6314
  return value.split(/[-_/]/).filter(Boolean).map(formatPiLabelToken).join(" ");
@@ -6046,7 +6318,6 @@ function formatPiLabelToken(token) {
6046
6318
  const specialCases = {
6047
6319
  ai: "AI",
6048
6320
  api: "API",
6049
- deepseek: "DeepSeek",
6050
6321
  flash: "Flash",
6051
6322
  gpt: "GPT",
6052
6323
  pro: "Pro",
@@ -6070,6 +6341,116 @@ function piErrorMessage(error) {
6070
6341
  }
6071
6342
  return "Unknown Pi error";
6072
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
+ };
6073
6454
  var PiDriver = class {
6074
6455
  id = "pi";
6075
6456
  supportsNativeStandingPrompt = true;
@@ -6087,7 +6468,7 @@ var PiDriver = class {
6087
6468
  };
6088
6469
  model = {
6089
6470
  detectedModelsVerifiedAs: "launchable",
6090
- toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
6471
+ toLaunchSpec: (modelId) => ({ params: { model: modelId } })
6091
6472
  };
6092
6473
  supportsStdinNotification = true;
6093
6474
  mcpToolPrefix = "";
@@ -6099,11 +6480,9 @@ var PiDriver = class {
6099
6480
  requestId = 0;
6100
6481
  process = null;
6101
6482
  probe() {
6102
- const command = resolveCommandOnPath("pi");
6103
- if (!command) return { available: false };
6104
6483
  return {
6105
6484
  available: true,
6106
- version: readCommandVersion(command) ?? void 0
6485
+ version: PI_SDK_VERSION
6107
6486
  };
6108
6487
  }
6109
6488
  async detectModels() {
@@ -6114,15 +6493,57 @@ var PiDriver = class {
6114
6493
  this.sessionAnnounced = false;
6115
6494
  this.sawTextDelta = false;
6116
6495
  this.requestId = 0;
6117
- mkdirSync4(buildPiSessionDir(ctx.workingDirectory), { recursive: true });
6118
- 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({
6119
6509
  cwd: ctx.workingDirectory,
6120
- stdio: ["pipe", "pipe", "pipe"],
6121
- env: await buildPiSpawnEnv(ctx),
6122
- 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
6123
6538
  });
6539
+ this.sessionId = session.sessionId;
6540
+ const proc = new PiSdkProcess(session);
6124
6541
  this.process = proc;
6125
- this.sendRpcCommand("prompt", { message: ctx.prompt });
6542
+ setImmediate(() => {
6543
+ if (this.process === proc && !proc.killed) {
6544
+ this.sendRpcCommand("prompt", { message: ctx.prompt });
6545
+ }
6546
+ });
6126
6547
  return { process: proc };
6127
6548
  }
6128
6549
  parseLine(line) {
@@ -6211,7 +6632,7 @@ var PiDriver = class {
6211
6632
  toolPrefix: "",
6212
6633
  extraCriticalRules: [],
6213
6634
  postStartupNotes: [
6214
- "**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."
6215
6636
  ],
6216
6637
  includeStdinNotificationSection: true,
6217
6638
  messageNotificationStyle: "direct"
@@ -6506,7 +6927,7 @@ function runtimeDisplayName(runtimeId) {
6506
6927
  case "opencode":
6507
6928
  return "OpenCode";
6508
6929
  case "pi":
6509
- return "Pi CLI";
6930
+ return "Pi";
6510
6931
  default:
6511
6932
  return runtimeId || "This runtime";
6512
6933
  }
@@ -6752,8 +7173,8 @@ function formatVisibleMessageTarget(message) {
6752
7173
  }
6753
7174
  return null;
6754
7175
  }
6755
- function getMessageShortId(messageId) {
6756
- 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);
6757
7178
  }
6758
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.";
6759
7180
  function findSessionJsonl(root, predicate) {
@@ -6764,7 +7185,7 @@ function findSessionJsonl(root, predicate) {
6764
7185
  if (depth < 0 || visited >= maxEntries) return null;
6765
7186
  let entries;
6766
7187
  try {
6767
- 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));
6768
7189
  } catch {
6769
7190
  return null;
6770
7191
  }
@@ -7724,6 +8145,30 @@ function runtimeTraceCounterAttrs(ap) {
7724
8145
  runtime_thinking_events_count: ap.runtimeTraceCounters.thinkingEvents
7725
8146
  };
7726
8147
  }
8148
+ var RUNTIME_TELEMETRY_RESERVED_ATTR_KEYS = /* @__PURE__ */ new Set([
8149
+ "agentId",
8150
+ "launchId",
8151
+ "runtime",
8152
+ "model",
8153
+ "telemetry_name",
8154
+ "source",
8155
+ "usageKind",
8156
+ "sessionId",
8157
+ "turnId",
8158
+ "runtimeResultId",
8159
+ "daemonVersion",
8160
+ "daemon_version",
8161
+ "computerVersion",
8162
+ "computer_version"
8163
+ ]);
8164
+ function sanitizeRuntimeTelemetryPayloadAttrs(attrs) {
8165
+ const sanitized = {};
8166
+ for (const [key, value] of Object.entries(attrs)) {
8167
+ if (RUNTIME_TELEMETRY_RESERVED_ATTR_KEYS.has(key)) continue;
8168
+ sanitized[key] = value;
8169
+ }
8170
+ return sanitized;
8171
+ }
7727
8172
  function getMessageDeliveryText(driver) {
7728
8173
  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.";
7729
8174
  }
@@ -7934,6 +8379,13 @@ var AgentProcessManager = class _AgentProcessManager {
7934
8379
  getPendingMessages: (target) => this.pendingVisibleMessages(agentId, target),
7935
8380
  isMessageModelSeen: ({ target, message }) => this.isVisibleMessageModelSeen(agentId, target, message),
7936
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
+ },
7937
8389
  consumeVisibleMessages: (input) => this.consumeVisibleMessages(agentId, input),
7938
8390
  recordDrainOutcome: (input) => {
7939
8391
  this.recordDaemonTrace("daemon.agent.drain.outcome", {
@@ -9860,6 +10312,29 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9860
10312
  ...messageProducerFactTraceAttrs(messages)
9861
10313
  };
9862
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
+ }
9863
10338
  runtimeProfileTurnControlTraceAttrs(control) {
9864
10339
  if (!control) return {};
9865
10340
  const pendingAgeMs = Math.max(0, Date.now() - control.injectedAtMs);
@@ -10458,11 +10933,13 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10458
10933
  }
10459
10934
  }
10460
10935
  recordRuntimeTelemetry(agentId, ap, event) {
10936
+ const sessionId = ap.driver.currentSessionId ?? event.sessionId;
10937
+ const payloadAttrs = sanitizeRuntimeTelemetryPayloadAttrs(event.attrs);
10461
10938
  const telemetryAttrs = {
10462
- ...event.attrs,
10939
+ ...payloadAttrs,
10463
10940
  ...event.source ? { source: event.source } : {},
10464
10941
  ...event.usageKind ? { usageKind: event.usageKind } : {},
10465
- ...event.sessionId ? { sessionId: event.sessionId } : {},
10942
+ ...sessionId ? { sessionId } : {},
10466
10943
  ...event.turnId ? { turnId: event.turnId } : {},
10467
10944
  ...event.runtimeResultId ? { runtimeResultId: event.runtimeResultId } : {}
10468
10945
  };
@@ -10548,11 +11025,29 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10548
11025
  }
10549
11026
  const inboxCount = ap.inbox.length;
10550
11027
  if (inboxCount === 0) return false;
10551
- 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.]`;
10552
- 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)`);
10553
11039
  const encoded = ap.driver.encodeStdinMessage(notification, ap.sessionId, { mode: "busy" });
10554
11040
  if (encoded) {
10555
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
+ });
10556
11051
  this.recordDaemonTrace("daemon.agent.stdin_notification", {
10557
11052
  agentId,
10558
11053
  runtime: ap.config.runtime,
@@ -10562,6 +11057,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10562
11057
  mode: "busy",
10563
11058
  pending_notification_count: count,
10564
11059
  inbox_count: inboxCount,
11060
+ inbox_target_count: inboxRows.length,
10565
11061
  session_id_present: true
10566
11062
  });
10567
11063
  return true;
@@ -10579,6 +11075,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10579
11075
  retry_scheduled: retryScheduled,
10580
11076
  notification_timer_present: ap.notifications.hasTimer,
10581
11077
  inbox_count: inboxCount,
11078
+ inbox_target_count: inboxRows.length,
10582
11079
  session_id_present: true
10583
11080
  }, "error");
10584
11081
  return false;
@@ -11116,7 +11613,7 @@ function acquireDaemonMachineLock(options) {
11116
11613
  }
11117
11614
 
11118
11615
  // src/localTraceSink.ts
11119
- 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";
11120
11617
  import path15 from "path";
11121
11618
  var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
11122
11619
  var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
@@ -11195,7 +11692,7 @@ var LocalRotatingTraceSink = class {
11195
11692
  }
11196
11693
  }
11197
11694
  pruneOldFiles() {
11198
- 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();
11199
11696
  const excess = files.length - this.maxFiles;
11200
11697
  if (excess <= 0) return;
11201
11698
  for (const file of files.slice(0, excess)) {
@@ -11637,7 +12134,7 @@ var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
11637
12134
  var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"];
11638
12135
  var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
11639
12136
  var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
11640
- 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>)`;
11641
12138
  var RunnerCredentialMintError2 = class extends Error {
11642
12139
  code;
11643
12140
  retryable;
@@ -11673,9 +12170,9 @@ function runnerCredentialErrorDetail2(error) {
11673
12170
  async function waitForRunnerCredentialRetry2() {
11674
12171
  await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
11675
12172
  }
11676
- function parseDaemonCliArgs(args) {
12173
+ function parseDaemonCliArgs(args, env = {}) {
11677
12174
  let serverUrl = "";
11678
- let apiKey = "";
12175
+ let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
11679
12176
  for (let i = 0; i < args.length; i++) {
11680
12177
  if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
11681
12178
  if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
@@ -11864,7 +12361,7 @@ var DaemonCore = class {
11864
12361
  this.slockHome = resolveSlockHome();
11865
12362
  process.env[SLOCK_HOME_ENV] = this.slockHome;
11866
12363
  this.injectedTracer = Boolean(options.tracer);
11867
- this.tracer = options.tracer ?? noopTracer;
12364
+ this.tracer = this.withDaemonTraceScope(options.tracer ?? noopTracer);
11868
12365
  this.runtimeDetector = options.runtimeDetector ?? (() => detectRuntimes(this.tracer));
11869
12366
  this.reminderCache = new ReminderCache({
11870
12367
  clock: options.reminderClock,
@@ -11915,9 +12412,9 @@ var DaemonCore = class {
11915
12412
  maxFileAgeJitterMs: jitter.maxFileAgeJitterMs,
11916
12413
  maxFiles: this.options.localTraceMaxFiles ?? readPositiveIntegerEnv3("SLOCK_DAEMON_TRACE_MAX_FILES", 8)
11917
12414
  });
11918
- this.tracer = new BasicTracer({
12415
+ this.tracer = this.withDaemonTraceScope(new BasicTracer({
11919
12416
  sink: this.localTraceSink
11920
- });
12417
+ }));
11921
12418
  this.agentManager.setTracer(this.tracer);
11922
12419
  this.agentManager.setCliTransportTraceDir(path17.join(machineDir, "traces"));
11923
12420
  }
@@ -12011,6 +12508,23 @@ var DaemonCore = class {
12011
12508
  });
12012
12509
  span.end(status);
12013
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
+ }
12014
12528
  async requestRunnerCredentialOnce(agentId, config) {
12015
12529
  const url = new URL(`/internal/computer/runners/${encodeURIComponent(agentId)}/credentials`, this.options.serverUrl);
12016
12530
  const res = await daemonFetch(url, {
@@ -12371,8 +12885,7 @@ var DaemonCore = class {
12371
12885
  runtimes_count: runtimes.length,
12372
12886
  running_agents_count: runningAgentIds.length,
12373
12887
  idle_agents_count: idleAgentSessions.length,
12374
- runtime_profile_reports_count: runtimeProfileReports.length,
12375
- daemon_version_present: Boolean(this.daemonVersion)
12888
+ runtime_profile_reports_count: runtimeProfileReports.length
12376
12889
  });
12377
12890
  for (const agentId of runningAgentIds) {
12378
12891
  const sessionId = this.agentManager.getAgentSessionId(agentId);
@@ -12426,6 +12939,8 @@ var DaemonCore = class {
12426
12939
  };
12427
12940
 
12428
12941
  export {
12942
+ DAEMON_API_KEY_ENV,
12943
+ scrubDaemonAuthEnv,
12429
12944
  resolveWorkspaceDirectoryPath,
12430
12945
  scanWorkspaceDirectories,
12431
12946
  deleteWorkspaceDirectory,