@botiverse/raft-daemon 0.60.0 → 0.61.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,16 @@
1
1
  // src/core.ts
2
- import path16 from "path";
2
+ import path17 from "path";
3
3
  import os8 from "os";
4
- import { createRequire as createRequire2 } from "module";
4
+ import { createRequire as createRequire3 } from "module";
5
5
  import { accessSync } from "fs";
6
6
  import { fileURLToPath } from "url";
7
7
 
8
+ // ../shared/src/typeGuards.ts
9
+ function makeIsMember(members) {
10
+ const set = new Set(members);
11
+ return (value) => typeof value === "string" && set.has(value);
12
+ }
13
+
8
14
  // ../shared/src/slockRefs.ts
9
15
  var SLOCK_REF_CHANNEL_NAME_PATTERN = String.raw`[\p{L}\p{N}_-]+`;
10
16
  var SLOCK_REF_USER_NAME_PATTERN = SLOCK_REF_CHANNEL_NAME_PATTERN;
@@ -28,6 +34,10 @@ var CHANNEL_MESSAGE_RE = new RegExp(
28
34
  String.raw`^#(${SLOCK_REF_CHANNEL_NAME_PATTERN})(?::(${SLOCK_REF_THREAD_SHORT_ID_PATTERN}))?\s+msg=(${SLOCK_REF_MESSAGE_ID_PATTERN})$`,
29
35
  "iu"
30
36
  );
37
+ var DM_MESSAGE_RE = new RegExp(
38
+ String.raw`^dm:@(${SLOCK_REF_DM_PEER_PATTERN})(?::(${SLOCK_REF_THREAD_SHORT_ID_PATTERN}))?\s+msg=(${SLOCK_REF_MESSAGE_ID_PATTERN})$`,
39
+ "iu"
40
+ );
31
41
 
32
42
  // ../shared/src/producerFactLineage.ts
33
43
  var PRODUCER_FACT_TEXT_LABEL = "producerFactId";
@@ -1278,6 +1288,9 @@ var SERVER_CAPABILITY_MATRIX = {
1278
1288
 
1279
1289
  // ../shared/src/index.ts
1280
1290
  var RUNTIME_CONFIG_VERSION = 1;
1291
+ var AGENT_ACTIVITIES = ["online", "thinking", "working", "error", "offline"];
1292
+ var isAgentActivity = makeIsMember(AGENT_ACTIVITIES);
1293
+ var VALID_ACTIVITIES = new Set(AGENT_ACTIVITIES);
1281
1294
  var EXTERNAL_AGENT_RUNTIME_ID = "external";
1282
1295
  var EXTERNAL_AGENT_RUNTIME_MODEL = "external";
1283
1296
  var EXTERNAL_AGENT_RUNTIME_DISPLAY_NAME = "External agent";
@@ -1285,7 +1298,11 @@ var RUNTIMES = [
1285
1298
  { id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
1286
1299
  { id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
1287
1300
  { id: "antigravity", displayName: "Antigravity CLI", binary: "agy", supported: true },
1288
- { id: "kimi", displayName: "Kimi CLI", binary: "kimi", supported: true },
1301
+ // Kimi: prefer the in-process SDK (`kimi-sdk` "Kimi Code") for new agents.
1302
+ // The legacy `kimi` (kimi-cli child-process) entry stays for backward compat
1303
+ // with existing `runtime=kimi` agents but is labelled deprecated.
1304
+ { id: "kimi-sdk", displayName: "Kimi Code", binary: "", supported: true },
1305
+ { id: "kimi", displayName: "Kimi CLI (deprecated)", binary: "kimi", supported: true },
1289
1306
  { id: "copilot", displayName: "Copilot CLI", binary: "copilot", supported: true },
1290
1307
  { id: "cursor", displayName: "Cursor CLI", binary: "cursor-agent", supported: true },
1291
1308
  { id: "gemini", displayName: "Gemini CLI", binary: "gemini", supported: true },
@@ -1353,6 +1370,15 @@ var RUNTIME_MODELS = {
1353
1370
  // built-in option is to defer to whatever default model the CLI already uses.
1354
1371
  kimi: [
1355
1372
  { id: "default", label: "Configured Default" }
1373
+ ],
1374
+ // kimi-sdk runs the Kimi Code SDK in-process. Surface the canonical model
1375
+ // first so getDefaultModel("kimi-sdk") returns it (without a static entry,
1376
+ // useRuntimeModels falls back to Claude's list and getDefaultModel returns
1377
+ // "sonnet" — which the SDK driver would pass through to the Kimi session
1378
+ // and the LLM call would fail). Mirrors detectKimiSdkModels() in the daemon.
1379
+ "kimi-sdk": [
1380
+ { id: "kimi-code/kimi-for-coding", label: "Kimi-K2.6 (Kimi for Coding)", verified: "launchable" },
1381
+ { id: "kimi-k2-0905-preview", label: "Kimi K2 (preview)", verified: "suggestion_only" }
1356
1382
  ]
1357
1383
  };
1358
1384
  function getDefaultModel(runtimeId) {
@@ -1538,10 +1564,10 @@ var DISPLAY_PLAN_CONFIG = {
1538
1564
  };
1539
1565
 
1540
1566
  // src/agentProcessManager.ts
1541
- import { mkdirSync as mkdirSync4, readdirSync as readdirSync3, statSync, writeFileSync as writeFileSync4 } from "fs";
1567
+ import { mkdirSync as mkdirSync5, readdirSync as readdirSync3, statSync, writeFileSync as writeFileSync4 } from "fs";
1542
1568
  import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
1543
1569
  import { createHash as createHash3 } from "crypto";
1544
- import path12 from "path";
1570
+ import path13 from "path";
1545
1571
  import os6 from "os";
1546
1572
 
1547
1573
  // src/drivers/claude.ts
@@ -3341,6 +3367,7 @@ function routeFamilyForPath(pathname) {
3341
3367
  function daemonUpstreamTargetHostClass(url) {
3342
3368
  const hostname = url.hostname.toLowerCase();
3343
3369
  if (hostname === "api.slock.ai") return "api.slock.ai";
3370
+ if (hostname === "api.raft.build") return "api.raft.build";
3344
3371
  return "custom_server";
3345
3372
  }
3346
3373
  function upstreamLayerForProxyError(err) {
@@ -6206,11 +6233,455 @@ function detectKimiModels(home = os4.homedir()) {
6206
6233
  return { models, default: defaultModel };
6207
6234
  }
6208
6235
 
6236
+ // src/drivers/kimi-sdk.ts
6237
+ import { randomUUID as randomUUID3 } from "crypto";
6238
+ import { EventEmitter } from "events";
6239
+ import { mkdirSync as mkdirSync3 } from "fs";
6240
+ import path9 from "path";
6241
+ import { createRequire as createRequire2 } from "module";
6242
+ import {
6243
+ createKimiHarness
6244
+ } from "@botiverse/kimi-code-sdk";
6245
+ var requireFromHere = createRequire2(import.meta.url);
6246
+ var KIMI_CODE_USER_AGENT_PRODUCT = "kimi-code-cli";
6247
+ var KIMI_CODE_HOST_VERSION = "0.14.3";
6248
+ function getDaemonVersion() {
6249
+ try {
6250
+ const pkg = requireFromHere("../../package.json");
6251
+ return pkg.version || "0.0.0";
6252
+ } catch {
6253
+ return "0.0.0";
6254
+ }
6255
+ }
6256
+ var KIMI_SESSION_DIR = ".kimi-sessions";
6257
+ function createKimiSdkEventMappingState(sessionId = null) {
6258
+ return {
6259
+ sessionId,
6260
+ sessionAnnounced: false
6261
+ };
6262
+ }
6263
+ function buildKimiSessionDir(workingDirectory) {
6264
+ return path9.join(workingDirectory, KIMI_SESSION_DIR);
6265
+ }
6266
+ function kimiErrorMessage(error) {
6267
+ if (typeof error === "string" && error.trim()) return error.trim();
6268
+ if (error && typeof error === "object") {
6269
+ const record = error;
6270
+ if (typeof record.message === "string" && record.message.trim()) return record.message.trim();
6271
+ try {
6272
+ return JSON.stringify(error);
6273
+ } catch {
6274
+ }
6275
+ }
6276
+ return "Unknown Kimi error";
6277
+ }
6278
+ function pushSessionInitIfNeeded(state, events) {
6279
+ if (!state.sessionAnnounced && state.sessionId) {
6280
+ events.push({ kind: "session_init", sessionId: state.sessionId });
6281
+ state.sessionAnnounced = true;
6282
+ }
6283
+ }
6284
+ function mapKimiSdkEventToParsedEvents(event, state) {
6285
+ const events = [];
6286
+ pushSessionInitIfNeeded(state, events);
6287
+ switch (event.type) {
6288
+ // ── content streaming → ParsedEvent ──
6289
+ case "thinking.delta":
6290
+ if (typeof event.delta === "string" && event.delta.length > 0) {
6291
+ events.push({ kind: "thinking", text: event.delta });
6292
+ }
6293
+ return events;
6294
+ case "assistant.delta":
6295
+ if (typeof event.delta === "string" && event.delta.length > 0) {
6296
+ events.push({ kind: "text", text: event.delta });
6297
+ }
6298
+ return events;
6299
+ case "tool.call.started":
6300
+ events.push({
6301
+ kind: "tool_call",
6302
+ name: event.name || "unknown_tool",
6303
+ input: event.args ?? {}
6304
+ });
6305
+ return events;
6306
+ case "tool.result":
6307
+ events.push({ kind: "tool_output", name: "" });
6308
+ return events;
6309
+ case "compaction.started":
6310
+ events.push({ kind: "compaction_started" });
6311
+ return events;
6312
+ case "compaction.completed":
6313
+ events.push({ kind: "compaction_finished" });
6314
+ return events;
6315
+ case "error":
6316
+ events.push({ kind: "error", message: kimiErrorMessage(event) });
6317
+ return events;
6318
+ // ── THE single turn-triggering source class ──
6319
+ case "turn.ended":
6320
+ events.push({ kind: "turn_end", sessionId: state.sessionId || void 0 });
6321
+ return events;
6322
+ // ── explicit drops (RS-004) ──
6323
+ // `warning` is non-fatal in the SDK's vocabulary; mapping it to
6324
+ // ParsedEvent.kind="error" would latch the agent into APM's error state
6325
+ // (lastRuntimeError set by `runtime.error`, then NOT cleared by the
6326
+ // following turn_end) — so a warning would brick the agent until
6327
+ // restart. Drop it here; the SDK's own log already records it.
6328
+ case "warning":
6329
+ // turn / step lifecycle (state-only, no parsed payload)
6330
+ case "turn.started":
6331
+ case "turn.step.started":
6332
+ case "turn.step.completed":
6333
+ case "turn.step.retrying":
6334
+ case "turn.step.interrupted":
6335
+ // tool-call progress / hooks (could elevate to internal_progress in future)
6336
+ case "tool.call.delta":
6337
+ case "tool.progress":
6338
+ case "hook.result":
6339
+ // status / meta updates
6340
+ case "agent.status.updated":
6341
+ case "session.meta.updated":
6342
+ case "goal.updated":
6343
+ case "skill.activated":
6344
+ // MCP infra
6345
+ case "tool.list.updated":
6346
+ case "mcp.server.status":
6347
+ // subagent lifecycle (v0: drop; could surface later)
6348
+ case "subagent.spawned":
6349
+ case "subagent.started":
6350
+ case "subagent.suspended":
6351
+ case "subagent.completed":
6352
+ case "subagent.failed":
6353
+ // compaction sub-cases
6354
+ case "compaction.blocked":
6355
+ case "compaction.cancelled":
6356
+ // out-of-band
6357
+ case "background.task.started":
6358
+ case "background.task.terminated":
6359
+ case "cron.fired":
6360
+ return events;
6361
+ default: {
6362
+ const _exhaustive = event;
6363
+ return _exhaustive;
6364
+ }
6365
+ }
6366
+ }
6367
+ var KIMI_SDK_RUNTIME_SESSION_DESCRIPTOR = {
6368
+ transport: "sdk",
6369
+ lifecycle: "sdk_session",
6370
+ input: {
6371
+ initial: "start",
6372
+ idle: "sdk_prompt",
6373
+ busy: "sdk_steer"
6374
+ },
6375
+ readiness: "sdk_ready",
6376
+ turnBoundary: "sdk_event",
6377
+ startPolicy: "immediate",
6378
+ inFlightWake: "steer",
6379
+ busyDelivery: "direct",
6380
+ postTurn: "keep_alive"
6381
+ };
6382
+ async function createKimiAgentSessionForContext(ctx, sessionId) {
6383
+ const sessionDir = buildKimiSessionDir(ctx.workingDirectory);
6384
+ mkdirSync3(sessionDir, { recursive: true });
6385
+ const cliTransport = await prepareCliTransport(ctx, { NO_COLOR: "1" });
6386
+ const spawnEnv = cliTransport.spawnEnv;
6387
+ const wrapperPath = cliTransport.wrapperPath;
6388
+ const homeDir = spawnEnv.KIMI_HOME || (process.env.HOME ? path9.join(process.env.HOME, ".kimi") : path9.join(ctx.workingDirectory, ".kimi"));
6389
+ mkdirSync3(homeDir, { recursive: true });
6390
+ const harness = createKimiHarness({
6391
+ homeDir,
6392
+ identity: {
6393
+ userAgentProduct: KIMI_CODE_USER_AGENT_PRODUCT,
6394
+ version: KIMI_CODE_HOST_VERSION,
6395
+ userAgentSuffix: `slock-daemon/${getDaemonVersion()}`
6396
+ }
6397
+ });
6398
+ const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
6399
+ const sessionFields = {
6400
+ workDir: ctx.workingDirectory,
6401
+ ...launchRuntimeFields.model && launchRuntimeFields.model !== "default" ? { model: launchRuntimeFields.model } : {}
6402
+ };
6403
+ let session;
6404
+ if (ctx.config.sessionId) {
6405
+ try {
6406
+ session = await harness.resumeSession({ ...sessionFields, id: ctx.config.sessionId });
6407
+ } catch (resumeError) {
6408
+ void resumeError;
6409
+ session = await harness.createSession(sessionFields);
6410
+ }
6411
+ } else {
6412
+ session = await harness.createSession(sessionFields);
6413
+ }
6414
+ return { harness, session, wrapperPath };
6415
+ }
6416
+ var KimiSdkRuntimeSession = class {
6417
+ constructor(ctx, setCurrentSessionId, sessionFactory = createKimiAgentSessionForContext) {
6418
+ this.ctx = ctx;
6419
+ this.setCurrentSessionId = setCurrentSessionId;
6420
+ this.sessionFactory = sessionFactory;
6421
+ this.mappingState = createKimiSdkEventMappingState(ctx.config.sessionId || null);
6422
+ }
6423
+ descriptor = KIMI_SDK_RUNTIME_SESSION_DESCRIPTOR;
6424
+ events = new EventEmitter();
6425
+ mappingState;
6426
+ harness = null;
6427
+ session = null;
6428
+ wrapperPath = null;
6429
+ unsubscribe = null;
6430
+ started = false;
6431
+ didClose = false;
6432
+ requestedStopReason;
6433
+ exitInfo = null;
6434
+ get pid() {
6435
+ return void 0;
6436
+ }
6437
+ get currentSessionId() {
6438
+ return this.mappingState.sessionId;
6439
+ }
6440
+ get exitCode() {
6441
+ return this.exitInfo?.code ?? null;
6442
+ }
6443
+ get signalCode() {
6444
+ return this.exitInfo?.signal ?? null;
6445
+ }
6446
+ get closed() {
6447
+ return this.didClose;
6448
+ }
6449
+ on(event, cb) {
6450
+ this.events.on(event, cb);
6451
+ }
6452
+ async start(input) {
6453
+ if (this.started) {
6454
+ return { ok: false, reason: "runtime_error", error: "runtime session already started" };
6455
+ }
6456
+ if (this.didClose) return { ok: false, reason: "closed" };
6457
+ this.started = true;
6458
+ const sessionId = input.sessionId || this.ctx.config.sessionId || randomUUID3();
6459
+ this.mappingState.sessionId = sessionId;
6460
+ this.setCurrentSessionId(sessionId);
6461
+ const { harness, session, wrapperPath } = await this.sessionFactory(
6462
+ {
6463
+ ...this.ctx,
6464
+ config: {
6465
+ ...this.ctx.config,
6466
+ sessionId
6467
+ }
6468
+ },
6469
+ sessionId
6470
+ );
6471
+ this.harness = harness;
6472
+ this.session = session;
6473
+ this.wrapperPath = wrapperPath;
6474
+ this.mappingState.sessionId = session.id;
6475
+ this.setCurrentSessionId(session.id);
6476
+ session.setApprovalHandler(() => ({ decision: "approved", scope: "session" }));
6477
+ this.unsubscribe = session.onEvent((event) => {
6478
+ for (const parsed of mapKimiSdkEventToParsedEvents(event, this.mappingState)) {
6479
+ this.events.emit("runtime_event", parsed);
6480
+ }
6481
+ });
6482
+ this.emitSessionInit();
6483
+ const firstTurnText = this.composeFirstTurnPreamble(input.text);
6484
+ this.launchPrompt(firstTurnText);
6485
+ return { ok: true, acceptedAs: "prompt" };
6486
+ }
6487
+ composeFirstTurnPreamble(turnText) {
6488
+ const sections = [];
6489
+ if (this.ctx.standingPrompt) {
6490
+ sections.push(this.ctx.standingPrompt);
6491
+ }
6492
+ if (this.wrapperPath) {
6493
+ sections.push(
6494
+ `## CLI invocation note (Kimi SDK in-process runtime)
6495
+
6496
+ When you run \`raft\` CLI commands from your bash tool, **use this absolute path** because PATH is not pre-injected for the in-process Kimi SDK:
6497
+
6498
+ \`\`\`
6499
+ ${this.wrapperPath}
6500
+ \`\`\`
6501
+
6502
+ So instead of \`raft message send ...\`, run \`${this.wrapperPath} message send ...\` (every \`raft\` reference in the protocol above maps to this wrapper). The wrapper carries this agent's identity automatically; do not pass auth tokens explicitly.`
6503
+ );
6504
+ }
6505
+ sections.push(turnText);
6506
+ return sections.join("\n\n---\n\n");
6507
+ }
6508
+ send(input) {
6509
+ if (this.didClose) return { ok: false, reason: "closed" };
6510
+ const session = this.session;
6511
+ if (!session) return { ok: false, reason: "closed" };
6512
+ if (input.mode === "busy") {
6513
+ this.deferSdkCall(() => session.steer(input.text));
6514
+ return { ok: true, acceptedAs: "steer" };
6515
+ }
6516
+ this.launchPrompt(input.text);
6517
+ return { ok: true, acceptedAs: "prompt" };
6518
+ }
6519
+ async stop(opts) {
6520
+ if (this.didClose) return;
6521
+ this.requestedStopReason = opts?.reason;
6522
+ const signal = opts?.signal ?? "SIGTERM";
6523
+ const session = this.session;
6524
+ if (session) {
6525
+ try {
6526
+ await session.cancel();
6527
+ } catch (error) {
6528
+ this.events.emit("stderr", kimiErrorMessage(error));
6529
+ }
6530
+ }
6531
+ await this.disposeSession();
6532
+ this.emitExitAndClose(null, signal);
6533
+ }
6534
+ async dispose() {
6535
+ if (this.didClose) return;
6536
+ await this.disposeSession();
6537
+ this.emitExitAndClose(0, null);
6538
+ }
6539
+ emitSessionInit() {
6540
+ const sessionId = this.mappingState.sessionId;
6541
+ if (!sessionId || this.mappingState.sessionAnnounced) return;
6542
+ this.mappingState.sessionAnnounced = true;
6543
+ this.events.emit("runtime_event", { kind: "session_init", sessionId });
6544
+ }
6545
+ launchPrompt(text) {
6546
+ const session = this.session;
6547
+ if (!session) {
6548
+ this.events.emit("runtime_event", {
6549
+ kind: "error",
6550
+ message: "Kimi SDK session is not started"
6551
+ });
6552
+ return;
6553
+ }
6554
+ this.deferSdkCall(() => session.prompt(text));
6555
+ }
6556
+ deferSdkCall(invoke) {
6557
+ setImmediate(() => {
6558
+ if (this.didClose) return;
6559
+ try {
6560
+ void invoke().catch((error) => {
6561
+ if (this.didClose) return;
6562
+ this.events.emit("runtime_event", {
6563
+ kind: "error",
6564
+ message: kimiErrorMessage(error)
6565
+ });
6566
+ });
6567
+ } catch (error) {
6568
+ if (this.didClose) return;
6569
+ this.events.emit("runtime_event", {
6570
+ kind: "error",
6571
+ message: kimiErrorMessage(error)
6572
+ });
6573
+ }
6574
+ });
6575
+ }
6576
+ async disposeSession() {
6577
+ const unsubscribe = this.unsubscribe;
6578
+ this.unsubscribe = null;
6579
+ try {
6580
+ unsubscribe?.();
6581
+ } catch {
6582
+ }
6583
+ const session = this.session;
6584
+ this.session = null;
6585
+ try {
6586
+ await session?.close();
6587
+ } catch (error) {
6588
+ this.events.emit("stderr", kimiErrorMessage(error));
6589
+ }
6590
+ const harness = this.harness;
6591
+ this.harness = null;
6592
+ try {
6593
+ await harness?.close();
6594
+ } catch (error) {
6595
+ this.events.emit("stderr", kimiErrorMessage(error));
6596
+ }
6597
+ }
6598
+ emitExitAndClose(code, signal) {
6599
+ if (this.didClose) return;
6600
+ this.didClose = true;
6601
+ const info = {
6602
+ code,
6603
+ signal,
6604
+ reason: this.requestedStopReason ? "requested" : "runtime_exit"
6605
+ };
6606
+ this.exitInfo = info;
6607
+ this.events.emit("exit", info);
6608
+ this.events.emit("close", info);
6609
+ }
6610
+ };
6611
+ var KIMI_SDK_DEFAULT_MODELS = [
6612
+ // Canonical Kimi Code SDK model (matches the `default_model` upstream
6613
+ // ships in `~/.kimi/config.toml` after `kimi login`). Mirrored as the first
6614
+ // entry in `RUNTIME_MODELS["kimi-sdk"]` (packages/shared/src/index.ts) so
6615
+ // both the daemon's launch resolution and the web's getDefaultModel agree.
6616
+ { id: "kimi-code/kimi-for-coding", label: "Kimi-K2.6 (Kimi for Coding)", verified: "launchable" },
6617
+ { id: "kimi-k2-0905-preview", label: "Kimi K2 (preview)", verified: "suggestion_only" }
6618
+ ];
6619
+ function detectKimiSdkModels() {
6620
+ return { models: KIMI_SDK_DEFAULT_MODELS };
6621
+ }
6622
+ var KimiSdkDriver = class {
6623
+ id = "kimi-sdk";
6624
+ supportsNativeStandingPrompt = true;
6625
+ lifecycle = {
6626
+ kind: "persistent",
6627
+ stdin: "direct",
6628
+ inFlightWake: "steer"
6629
+ };
6630
+ communication = {
6631
+ chat: "slock_cli",
6632
+ runtimeControl: "none"
6633
+ };
6634
+ session = {
6635
+ recovery: "resume_or_fresh"
6636
+ };
6637
+ model = {
6638
+ detectedModelsVerifiedAs: "suggestion_only",
6639
+ toLaunchSpec: (modelId) => ({ params: { model: modelId } })
6640
+ };
6641
+ supportsStdinNotification = true;
6642
+ busyDeliveryMode = "direct";
6643
+ sessionId = null;
6644
+ get currentSessionId() {
6645
+ return this.sessionId;
6646
+ }
6647
+ probe() {
6648
+ return { available: true };
6649
+ }
6650
+ async detectModels() {
6651
+ return detectKimiSdkModels();
6652
+ }
6653
+ createSession(ctx) {
6654
+ this.sessionId = ctx.config.sessionId || null;
6655
+ return new KimiSdkRuntimeSession(ctx, (sessionId) => {
6656
+ this.sessionId = sessionId;
6657
+ });
6658
+ }
6659
+ async spawn(_ctx) {
6660
+ throw new Error("KimiSdkDriver uses a native RuntimeSession; child-process spawn is unsupported");
6661
+ }
6662
+ parseLine(_line) {
6663
+ return [];
6664
+ }
6665
+ encodeStdinMessage(_text, _sessionId, _opts) {
6666
+ return null;
6667
+ }
6668
+ buildSystemPrompt(config, _agentId) {
6669
+ return buildCliTransportSystemPrompt(config, {
6670
+ extraCriticalRules: [],
6671
+ postStartupNotes: [
6672
+ "**Kimi SDK runtime note:** Slock keeps Kimi running as a persistent SDK session. While you are working, Slock may send inbox-count notifications into the current turn; call `raft message check` at natural breakpoints."
6673
+ ],
6674
+ includeStdinNotificationSection: true,
6675
+ messageNotificationStyle: "direct"
6676
+ });
6677
+ }
6678
+ };
6679
+
6209
6680
  // src/drivers/opencode.ts
6210
6681
  import { spawn as spawn8, spawnSync as spawnSync2 } from "child_process";
6211
6682
  import { existsSync as existsSync8, readFileSync as readFileSync4 } from "fs";
6212
6683
  import os5 from "os";
6213
- import path9 from "path";
6684
+ import path10 from "path";
6214
6685
  var SLOCK_AGENT_NAME = "slock";
6215
6686
  var NO_MESSAGE_PROMPT = "No new messages are pending. Stop now.";
6216
6687
  var FIRST_MESSAGE_TASK_PREFIX = "First message task (system-triggered):";
@@ -6239,7 +6710,7 @@ function parseUserOpenCodeConfig(ctx) {
6239
6710
  return parseOpenCodeConfigContent(raw);
6240
6711
  }
6241
6712
  function readLocalOpenCodeConfig(home = os5.homedir()) {
6242
- const configPath = path9.join(home, ".config", "opencode", "opencode.json");
6713
+ const configPath = path10.join(home, ".config", "opencode", "opencode.json");
6243
6714
  try {
6244
6715
  return parseOpenCodeConfigContent(readFileSync4(configPath, "utf8"));
6245
6716
  } catch {
@@ -6437,11 +6908,11 @@ function runOpenCodeModelsCommand(home, deps = {}) {
6437
6908
  };
6438
6909
  }
6439
6910
  function isWindowsCommandShim(commandPath) {
6440
- const ext = path9.win32.extname(commandPath).toLowerCase();
6911
+ const ext = path10.win32.extname(commandPath).toLowerCase();
6441
6912
  return ext === ".cmd" || ext === ".bat";
6442
6913
  }
6443
6914
  function opencodePackageEntryCandidates(packageRoot) {
6444
- const winPath = path9.win32;
6915
+ const winPath = path10.win32;
6445
6916
  return [
6446
6917
  winPath.join(packageRoot, "bin", "opencode.exe"),
6447
6918
  winPath.join(packageRoot, "bin", "opencode.js"),
@@ -6450,7 +6921,7 @@ function opencodePackageEntryCandidates(packageRoot) {
6450
6921
  ];
6451
6922
  }
6452
6923
  function openCodeSpecForEntry(entry, commandArgs) {
6453
- if (path9.win32.extname(entry).toLowerCase() === ".exe") {
6924
+ if (path10.win32.extname(entry).toLowerCase() === ".exe") {
6454
6925
  return { command: entry, args: commandArgs, shell: false };
6455
6926
  }
6456
6927
  return { command: process.execPath, args: [entry, ...commandArgs], shell: false };
@@ -6459,7 +6930,7 @@ function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
6459
6930
  const existsSyncFn = deps.existsSyncFn ?? existsSync8;
6460
6931
  const execFileSyncFn = deps.execFileSyncFn;
6461
6932
  const env = deps.env ?? process.env;
6462
- const winPath = path9.win32;
6933
+ const winPath = path10.win32;
6463
6934
  const candidates = [];
6464
6935
  if (execFileSyncFn) {
6465
6936
  try {
@@ -6487,7 +6958,7 @@ function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
6487
6958
  function extractWindowsShimTargets(commandPath, deps = {}) {
6488
6959
  if (!isWindowsCommandShim(commandPath)) return [];
6489
6960
  const readFileSyncFn = deps.readFileSyncFn ?? readFileSync4;
6490
- const commandDir = path9.win32.dirname(commandPath);
6961
+ const commandDir = path10.win32.dirname(commandPath);
6491
6962
  let raw;
6492
6963
  try {
6493
6964
  raw = String(readFileSyncFn(commandPath, "utf8"));
@@ -6498,7 +6969,7 @@ function extractWindowsShimTargets(commandPath, deps = {}) {
6498
6969
  const dp0Pattern = /%~dp0\\?([^"\r\n]*?opencode\.(?:exe|js|mjs|cjs))/gi;
6499
6970
  for (const match of raw.matchAll(dp0Pattern)) {
6500
6971
  const relative = match[1]?.replace(/^\\+/, "");
6501
- if (relative) candidates.push(path9.win32.normalize(path9.win32.join(commandDir, relative)));
6972
+ if (relative) candidates.push(path10.win32.normalize(path10.win32.join(commandDir, relative)));
6502
6973
  }
6503
6974
  return candidates;
6504
6975
  }
@@ -6512,7 +6983,7 @@ function resolveOpenCodeSpawn(commandArgs, deps = {}) {
6512
6983
  };
6513
6984
  }
6514
6985
  const command = resolveCommandOnPath("opencode", deps);
6515
- if (command && path9.win32.extname(command).toLowerCase() === ".exe") {
6986
+ if (command && path10.win32.extname(command).toLowerCase() === ".exe") {
6516
6987
  return { command, args: commandArgs, shell: false };
6517
6988
  }
6518
6989
  const packageEntry = resolveWindowsOpenCodePackageEntry(command, deps);
@@ -6682,17 +7153,16 @@ var OpenCodeDriver = class {
6682
7153
  };
6683
7154
 
6684
7155
  // src/drivers/pi.ts
6685
- import { randomUUID as randomUUID3 } from "crypto";
6686
- import { EventEmitter } from "events";
6687
- import { mkdirSync as mkdirSync3, readdirSync as readdirSync2 } from "fs";
6688
- import path10 from "path";
7156
+ import { randomUUID as randomUUID4 } from "crypto";
7157
+ import { EventEmitter as EventEmitter2 } from "events";
7158
+ import { mkdirSync as mkdirSync4, readdirSync as readdirSync2 } from "fs";
7159
+ import path11 from "path";
6689
7160
  import {
6690
7161
  AuthStorage,
6691
7162
  createBashTool,
6692
- createAgentSession,
6693
- DefaultResourceLoader,
7163
+ createAgentSessionFromServices,
7164
+ createAgentSessionServices,
6694
7165
  getAgentDir,
6695
- ModelRegistry,
6696
7166
  SessionManager,
6697
7167
  SettingsManager,
6698
7168
  VERSION as PI_SDK_VERSION
@@ -6712,7 +7182,7 @@ function createPiSdkEventMappingState(sessionId = null) {
6712
7182
  };
6713
7183
  }
6714
7184
  function buildPiSessionDir(workingDirectory) {
6715
- return path10.join(workingDirectory, PI_SESSION_DIR);
7185
+ return path11.join(workingDirectory, PI_SESSION_DIR);
6716
7186
  }
6717
7187
  async function buildPiSpawnEnv(ctx) {
6718
7188
  return (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
@@ -6734,7 +7204,7 @@ function findPiSessionFile(sessionDir, sessionId) {
6734
7204
  }
6735
7205
  const suffix = `_${sessionId}.jsonl`;
6736
7206
  const match = entries.find((entry) => entry.endsWith(suffix));
6737
- return match ? path10.join(sessionDir, match) : null;
7207
+ return match ? path11.join(sessionDir, match) : null;
6738
7208
  }
6739
7209
  function detectPiModelsFromRegistry(modelRegistry) {
6740
7210
  const models = [];
@@ -6751,8 +7221,70 @@ function detectPiModelsFromRegistry(modelRegistry) {
6751
7221
  }
6752
7222
  return models.length > 0 ? { models } : null;
6753
7223
  }
6754
- function detectPiModels(modelRegistry = ModelRegistry.create(AuthStorage.create())) {
6755
- return detectPiModelsFromRegistry(modelRegistry);
7224
+ function applyPiDaemonSettingsOverrides(settingsManager) {
7225
+ settingsManager.applyOverrides({ compaction: { enabled: PI_SDK_COMPACTION_ENABLED } });
7226
+ }
7227
+ function logPiServiceDiagnostics(context, services) {
7228
+ for (const diagnostic of services.diagnostics) {
7229
+ const message = `[pi-driver] ${context} diagnostic: ${diagnostic.message}`;
7230
+ if (diagnostic.type === "info") {
7231
+ console.info(message);
7232
+ } else {
7233
+ console.warn(message);
7234
+ }
7235
+ }
7236
+ }
7237
+ function formatPiModelLogId(model) {
7238
+ return model ? `${model.provider}/${model.id}` : "default";
7239
+ }
7240
+ function piServiceDiagnosticTraceAttrs(services) {
7241
+ let info = 0;
7242
+ let warning = 0;
7243
+ for (const diagnostic of services.diagnostics) {
7244
+ if (diagnostic.type === "info") {
7245
+ info++;
7246
+ } else {
7247
+ warning++;
7248
+ }
7249
+ }
7250
+ return {
7251
+ diagnostics_count: services.diagnostics.length,
7252
+ diagnostic_info_count: info,
7253
+ diagnostic_warning_count: warning
7254
+ };
7255
+ }
7256
+ function addPiServiceTraceEvent(span, name, services, attrs = {}) {
7257
+ span?.addEvent(name, {
7258
+ available_models_count: services.modelRegistry.getAvailable().length,
7259
+ ...piServiceDiagnosticTraceAttrs(services),
7260
+ ...attrs
7261
+ });
7262
+ }
7263
+ async function detectPiModels(modelRegistry, traceContext = {}) {
7264
+ if (modelRegistry) {
7265
+ return detectPiModelsFromRegistry(modelRegistry);
7266
+ }
7267
+ const agentDir = getAgentDir();
7268
+ const services = await createAgentSessionServices({
7269
+ cwd: process.cwd(),
7270
+ agentDir
7271
+ });
7272
+ logPiServiceDiagnostics("detect_models", services);
7273
+ addPiServiceTraceEvent(traceContext.span, "daemon.pi.models.services_ready", services);
7274
+ const result = detectPiModelsFromRegistry(services.modelRegistry);
7275
+ traceContext.span?.addEvent("daemon.pi.models.result", {
7276
+ available_models_count: services.modelRegistry.getAvailable().length,
7277
+ returned_models_count: result?.models?.length ?? 0,
7278
+ outcome: result ? "models_returned" : "no_models",
7279
+ ...piServiceDiagnosticTraceAttrs(services)
7280
+ });
7281
+ console.info(
7282
+ "[pi-driver] detect_models agentDir=%s available=%d returned=%d",
7283
+ agentDir,
7284
+ services.modelRegistry.getAvailable().length,
7285
+ result?.models?.length ?? 0
7286
+ );
7287
+ return result;
6756
7288
  }
6757
7289
  function humanizePiSegment(value) {
6758
7290
  return value.split(/[-_/]/).filter(Boolean).map(formatPiLabelToken).join(" ");
@@ -6785,7 +7317,7 @@ function piErrorMessage(error) {
6785
7317
  }
6786
7318
  return "Unknown Pi error";
6787
7319
  }
6788
- function pushSessionInitIfNeeded(state, events) {
7320
+ function pushSessionInitIfNeeded2(state, events) {
6789
7321
  if (!state.sessionAnnounced && state.sessionId) {
6790
7322
  events.push({ kind: "session_init", sessionId: state.sessionId });
6791
7323
  state.sessionAnnounced = true;
@@ -6822,7 +7354,7 @@ function mapPiAssistantMessageEvent(assistantEvent, state) {
6822
7354
  }
6823
7355
  function mapPiSdkEventToParsedEvents(event, state) {
6824
7356
  const events = [];
6825
- pushSessionInitIfNeeded(state, events);
7357
+ pushSessionInitIfNeeded2(state, events);
6826
7358
  switch (event.type) {
6827
7359
  case "agent_start":
6828
7360
  case "turn_start":
@@ -6887,49 +7419,127 @@ var PI_IDLE_PROMPT_RETRY_MS = 25;
6887
7419
  var PI_IDLE_PROMPT_MAX_WAIT_MS = 1e3;
6888
7420
  async function createPiAgentSessionForContext(ctx, sessionId) {
6889
7421
  const sessionDir = buildPiSessionDir(ctx.workingDirectory);
6890
- mkdirSync3(sessionDir, { recursive: true });
6891
- const spawnEnv = await buildPiSpawnEnv(ctx);
6892
- const agentDir = spawnEnv.PI_CODING_AGENT_DIR || getAgentDir();
6893
- const authStorage = AuthStorage.create(path10.join(agentDir, "auth.json"));
6894
- const modelRegistry = ModelRegistry.create(authStorage, path10.join(agentDir, "models.json"));
7422
+ mkdirSync4(sessionDir, { recursive: true });
6895
7423
  const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
6896
- const model = resolvePiModelFromRegistry(launchRuntimeFields.model, modelRegistry);
6897
- if (launchRuntimeFields.model && launchRuntimeFields.model !== "default" && !model) {
6898
- throw new Error(`Pi model not found: ${launchRuntimeFields.model}`);
6899
- }
6900
- const settingsManager = SettingsManager.inMemory({ compaction: { enabled: PI_SDK_COMPACTION_ENABLED } });
6901
- const resourceLoader = new DefaultResourceLoader({
6902
- cwd: ctx.workingDirectory,
6903
- agentDir,
6904
- settingsManager,
6905
- systemPromptOverride: () => ctx.standingPrompt
7424
+ const requestedModel = launchRuntimeFields.model || "default";
7425
+ const traceSpan = ctx.tracer?.startSpan("daemon.pi.session.create", {
7426
+ surface: "daemon",
7427
+ kind: "internal",
7428
+ attrs: {
7429
+ agentId: ctx.agentId,
7430
+ launchId: ctx.launchId || void 0,
7431
+ runtime: ctx.config.runtime,
7432
+ model: ctx.config.model,
7433
+ session_id_present: Boolean(sessionId),
7434
+ requested_model: requestedModel
7435
+ }
6906
7436
  });
6907
- await resourceLoader.reload();
6908
- const existingSessionFile = ctx.config.sessionId ? findPiSessionFile(sessionDir, ctx.config.sessionId) : null;
6909
- const sessionManager = existingSessionFile ? SessionManager.open(existingSessionFile, sessionDir, ctx.workingDirectory) : SessionManager.create(ctx.workingDirectory, sessionDir, { id: sessionId });
6910
- const { session } = await createAgentSession({
6911
- cwd: ctx.workingDirectory,
6912
- agentDir,
6913
- model,
6914
- thinkingLevel: launchRuntimeFields.reasoningEffort,
6915
- authStorage,
6916
- modelRegistry,
6917
- resourceLoader,
6918
- customTools: [
6919
- createBashTool(ctx.workingDirectory, {
6920
- spawnHook: (spawnContext) => ({
6921
- ...spawnContext,
6922
- env: {
6923
- ...spawnContext.env,
6924
- ...spawnEnv
6925
- }
7437
+ try {
7438
+ const spawnEnv = await buildPiSpawnEnv(ctx);
7439
+ const agentDir = spawnEnv.PI_CODING_AGENT_DIR || getAgentDir();
7440
+ const authStorage = AuthStorage.create(path11.join(agentDir, "auth.json"));
7441
+ const settingsManager = SettingsManager.create(ctx.workingDirectory, agentDir);
7442
+ const services = await createAgentSessionServices({
7443
+ cwd: ctx.workingDirectory,
7444
+ agentDir,
7445
+ authStorage,
7446
+ settingsManager,
7447
+ resourceLoaderOptions: {
7448
+ systemPromptOverride: () => ctx.standingPrompt
7449
+ }
7450
+ });
7451
+ applyPiDaemonSettingsOverrides(services.settingsManager);
7452
+ logPiServiceDiagnostics("create_session", services);
7453
+ addPiServiceTraceEvent(traceSpan, "daemon.pi.session.services_ready", services, {
7454
+ agent_dir_source: spawnEnv.PI_CODING_AGENT_DIR ? "spawn_env" : "default"
7455
+ });
7456
+ const model = resolvePiModelFromRegistry(launchRuntimeFields.model, services.modelRegistry);
7457
+ const resolvedModel = formatPiModelLogId(model);
7458
+ traceSpan?.addEvent("daemon.pi.session.model_resolved", {
7459
+ available_models_count: services.modelRegistry.getAvailable().length,
7460
+ requested_model: requestedModel,
7461
+ requested_model_explicit: requestedModel !== "default",
7462
+ resolved_model: resolvedModel,
7463
+ resolved_model_present: Boolean(model)
7464
+ });
7465
+ console.info(
7466
+ "[pi-driver] create_session services_ready agentDir=%s available=%d requested=%s resolved=%s",
7467
+ agentDir,
7468
+ services.modelRegistry.getAvailable().length,
7469
+ requestedModel,
7470
+ resolvedModel
7471
+ );
7472
+ if (launchRuntimeFields.model && launchRuntimeFields.model !== "default" && !model) {
7473
+ console.warn(
7474
+ "[pi-driver] create_session missing_model requested=%s available=%d",
7475
+ requestedModel,
7476
+ services.modelRegistry.getAvailable().length
7477
+ );
7478
+ traceSpan?.addEvent("daemon.pi.session.missing_model", {
7479
+ available_models_count: services.modelRegistry.getAvailable().length,
7480
+ requested_model: requestedModel
7481
+ });
7482
+ traceSpan?.end("error", {
7483
+ attrs: {
7484
+ outcome: "missing_model",
7485
+ available_models_count: services.modelRegistry.getAvailable().length,
7486
+ requested_model: requestedModel,
7487
+ resolved_model_present: false,
7488
+ ...piServiceDiagnosticTraceAttrs(services)
7489
+ }
7490
+ });
7491
+ throw new Error(`Pi model not found: ${launchRuntimeFields.model}`);
7492
+ }
7493
+ const existingSessionFile = ctx.config.sessionId ? findPiSessionFile(sessionDir, ctx.config.sessionId) : null;
7494
+ const sessionManager = existingSessionFile ? SessionManager.open(existingSessionFile, sessionDir, ctx.workingDirectory) : SessionManager.create(ctx.workingDirectory, sessionDir, { id: sessionId });
7495
+ const { session } = await createAgentSessionFromServices({
7496
+ services,
7497
+ sessionManager,
7498
+ model,
7499
+ thinkingLevel: launchRuntimeFields.reasoningEffort,
7500
+ customTools: [
7501
+ createBashTool(ctx.workingDirectory, {
7502
+ spawnHook: (spawnContext) => ({
7503
+ ...spawnContext,
7504
+ env: {
7505
+ ...spawnContext.env,
7506
+ ...spawnEnv
7507
+ }
7508
+ })
6926
7509
  })
6927
- })
6928
- ],
6929
- sessionManager,
6930
- settingsManager
6931
- });
6932
- return session;
7510
+ ]
7511
+ });
7512
+ traceSpan?.addEvent("daemon.pi.session.started", {
7513
+ requested_model: requestedModel,
7514
+ resolved_model: resolvedModel,
7515
+ session_id_present: Boolean(session.sessionId)
7516
+ });
7517
+ traceSpan?.end("ok", {
7518
+ attrs: {
7519
+ outcome: "started",
7520
+ available_models_count: services.modelRegistry.getAvailable().length,
7521
+ requested_model: requestedModel,
7522
+ resolved_model: resolvedModel,
7523
+ resolved_model_present: Boolean(model),
7524
+ ...piServiceDiagnosticTraceAttrs(services)
7525
+ }
7526
+ });
7527
+ console.info(
7528
+ "[pi-driver] create_session started sessionId=%s requested=%s resolved=%s",
7529
+ sessionId,
7530
+ requestedModel,
7531
+ resolvedModel
7532
+ );
7533
+ return session;
7534
+ } catch (error) {
7535
+ traceSpan?.end("error", {
7536
+ attrs: {
7537
+ outcome: "error",
7538
+ error_class: error instanceof Error ? error.name : typeof error
7539
+ }
7540
+ });
7541
+ throw error;
7542
+ }
6933
7543
  }
6934
7544
  var PiSdkRuntimeSession = class {
6935
7545
  constructor(ctx, setCurrentSessionId, sessionFactory = createPiAgentSessionForContext) {
@@ -6939,7 +7549,7 @@ var PiSdkRuntimeSession = class {
6939
7549
  this.mappingState = createPiSdkEventMappingState(ctx.config.sessionId || null);
6940
7550
  }
6941
7551
  descriptor = PI_RUNTIME_SESSION_DESCRIPTOR;
6942
- events = new EventEmitter();
7552
+ events = new EventEmitter2();
6943
7553
  mappingState;
6944
7554
  session = null;
6945
7555
  unsubscribe = null;
@@ -6971,7 +7581,7 @@ var PiSdkRuntimeSession = class {
6971
7581
  }
6972
7582
  if (this.didClose) return { ok: false, reason: "closed" };
6973
7583
  this.started = true;
6974
- const sessionId = input.sessionId || this.ctx.config.sessionId || randomUUID3();
7584
+ const sessionId = input.sessionId || this.ctx.config.sessionId || randomUUID4();
6975
7585
  this.mappingState.sessionId = sessionId;
6976
7586
  this.setCurrentSessionId(sessionId);
6977
7587
  const session = await this.sessionFactory({
@@ -7145,8 +7755,8 @@ var PiDriver = class {
7145
7755
  version: PI_SDK_VERSION
7146
7756
  };
7147
7757
  }
7148
- async detectModels() {
7149
- return detectPiModels();
7758
+ async detectModels(ctx) {
7759
+ return detectPiModels(void 0, ctx);
7150
7760
  }
7151
7761
  createSession(ctx) {
7152
7762
  this.sessionId = ctx.config.sessionId || null;
@@ -7182,7 +7792,7 @@ function delay(ms) {
7182
7792
  }
7183
7793
 
7184
7794
  // src/drivers/runtimeSession.ts
7185
- import { EventEmitter as EventEmitter2 } from "events";
7795
+ import { EventEmitter as EventEmitter3 } from "events";
7186
7796
  function descriptorFromDriver(driver) {
7187
7797
  const lifecycle = driver.lifecycle.kind === "per_turn" ? "turn_based" : "persistent_stream";
7188
7798
  const idle = driver.supportsStdinNotification ? "stdin" : "unsupported";
@@ -7210,7 +7820,7 @@ var ChildProcessRuntimeSession = class {
7210
7820
  this.descriptor = descriptorFromDriver(driver);
7211
7821
  }
7212
7822
  descriptor;
7213
- events = new EventEmitter2();
7823
+ events = new EventEmitter3();
7214
7824
  process = null;
7215
7825
  started = false;
7216
7826
  stdoutBuffer = "";
@@ -7323,7 +7933,13 @@ var driverFactories = {
7323
7933
  copilot: () => new CopilotDriver(),
7324
7934
  cursor: () => new CursorDriver(),
7325
7935
  gemini: () => new GeminiDriver(),
7936
+ // Two separate Kimi runtimes (per #proj-runtime:cc818e65 6/16 consensus):
7937
+ // - `kimi` = legacy kimi-cli child-process driver. Backward-compat for
7938
+ // existing `runtime=kimi` agents. Frontend marks deprecated.
7939
+ // - `kimi-sdk` = canonical in-process SDK driver. Frontend label "Kimi Code".
7940
+ // No alias / no auto-migration; explicit pick at agent-create time.
7326
7941
  kimi: () => new KimiDriver(),
7942
+ "kimi-sdk": () => new KimiSdkDriver(),
7327
7943
  opencode: () => new OpenCodeDriver(),
7328
7944
  pi: () => new PiDriver()
7329
7945
  };
@@ -7338,7 +7954,7 @@ function getDriver(runtimeId) {
7338
7954
 
7339
7955
  // src/workspaces.ts
7340
7956
  import { readdir, rm, stat } from "fs/promises";
7341
- import path11 from "path";
7957
+ import path12 from "path";
7342
7958
  function isValidWorkspaceDirectoryName(directoryName) {
7343
7959
  return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
7344
7960
  }
@@ -7346,7 +7962,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
7346
7962
  if (!isValidWorkspaceDirectoryName(directoryName)) {
7347
7963
  return null;
7348
7964
  }
7349
- return path11.join(dataDir, directoryName);
7965
+ return path12.join(dataDir, directoryName);
7350
7966
  }
7351
7967
  function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
7352
7968
  return {
@@ -7395,7 +8011,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
7395
8011
  return summary;
7396
8012
  }
7397
8013
  const childSummaries = await Promise.all(
7398
- entries.map((entry) => summarizeWorkspaceEntry(path11.join(dirPath, entry.name), entry))
8014
+ entries.map((entry) => summarizeWorkspaceEntry(path12.join(dirPath, entry.name), entry))
7399
8015
  );
7400
8016
  for (const childSummary of childSummaries) {
7401
8017
  summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
@@ -7414,7 +8030,7 @@ async function scanWorkspaceDirectories(dataDir) {
7414
8030
  if (!entry.isDirectory()) {
7415
8031
  return null;
7416
8032
  }
7417
- const dirPath = path11.join(dataDir, entry.name);
8033
+ const dirPath = path12.join(dataDir, entry.name);
7418
8034
  try {
7419
8035
  const summary = await summarizeWorkspaceDirectory(dirPath);
7420
8036
  return {
@@ -7965,12 +8581,12 @@ function findSessionJsonl(root, predicate) {
7965
8581
  for (const entry of entries) {
7966
8582
  if (++visited > maxEntries) return null;
7967
8583
  if (!entry.isFile() || !predicate(entry.name)) continue;
7968
- return path12.join(dir, entry.name);
8584
+ return path13.join(dir, entry.name);
7969
8585
  }
7970
8586
  for (const entry of entries) {
7971
8587
  if (++visited > maxEntries) return null;
7972
8588
  if (!entry.isDirectory()) continue;
7973
- const found = visit(path12.join(dir, entry.name), depth - 1);
8589
+ const found = visit(path13.join(dir, entry.name), depth - 1);
7974
8590
  if (found) return found;
7975
8591
  }
7976
8592
  return null;
@@ -7983,9 +8599,9 @@ function safeSessionFilename(value) {
7983
8599
  }
7984
8600
  function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
7985
8601
  try {
7986
- const dir = path12.join(fallbackDir, ".slock", "runtime-sessions");
7987
- mkdirSync4(dir, { recursive: true });
7988
- const filePath = path12.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
8602
+ const dir = path13.join(fallbackDir, ".slock", "runtime-sessions");
8603
+ mkdirSync5(dir, { recursive: true });
8604
+ const filePath = path13.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
7989
8605
  writeFileSync4(filePath, JSON.stringify({
7990
8606
  type: "runtime_session_handoff",
7991
8607
  runtime,
@@ -8017,7 +8633,7 @@ function ensureRuntimeHomeDir(config, defaultHomeDir, workspacePath) {
8017
8633
  return defaultHomeDir;
8018
8634
  }
8019
8635
  function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), fallbackDir) {
8020
- const directPath = path12.isAbsolute(sessionId) ? sessionId : null;
8636
+ const directPath = path13.isAbsolute(sessionId) ? sessionId : null;
8021
8637
  if (directPath) {
8022
8638
  try {
8023
8639
  if (statSync(directPath).isFile()) {
@@ -8026,7 +8642,7 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), f
8026
8642
  } catch {
8027
8643
  }
8028
8644
  }
8029
- const resolvedPath = runtime === "claude" ? findSessionJsonl(path12.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`) : runtime === "codex" ? findSessionJsonl(path12.join(homeDir, ".codex", "sessions"), (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId)) : null;
8645
+ const resolvedPath = runtime === "claude" ? findSessionJsonl(path13.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`) : runtime === "codex" ? findSessionJsonl(path13.join(homeDir, ".codex", "sessions"), (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId)) : null;
8030
8646
  if (!resolvedPath && fallbackDir) {
8031
8647
  const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
8032
8648
  if (fallback) return fallback;
@@ -8751,6 +9367,9 @@ function resumeSessionRecoveryReason(ap) {
8751
9367
  if (candidates.some(isOpenCodeReplayRejectedByProvider)) return "provider_replay_rejected";
8752
9368
  return null;
8753
9369
  }
9370
+ if (ap.driver.id === "pi") {
9371
+ return candidates.some(isPiReplayRejectedByProvider) ? "provider_replay_rejected" : null;
9372
+ }
8754
9373
  if (ap.driver.id === "gemini") {
8755
9374
  return candidates.some(
8756
9375
  (text) => /Error resuming session:\s*Invalid session identifier/i.test(text) && text.includes(ap.sessionId)
@@ -8761,9 +9380,13 @@ function resumeSessionRecoveryReason(ap) {
8761
9380
  function isOpenCodeReplayRejectedByProvider(text) {
8762
9381
  return /Invalid request:\s*the message at position \d+ with role ['"]?assistant['"]? must not be empty/i.test(text);
8763
9382
  }
9383
+ function isPiReplayRejectedByProvider(text) {
9384
+ return /Cannot continue from message role:\s*assistant/i.test(text);
9385
+ }
8764
9386
  function resumeSessionRuntimeLabel(runtimeId) {
8765
9387
  if (runtimeId === "opencode") return "OpenCode";
8766
9388
  if (runtimeId === "gemini") return "Gemini";
9389
+ if (runtimeId === "pi") return "Pi";
8767
9390
  return "Claude";
8768
9391
  }
8769
9392
  function classifyActivityDetailForTrace(detail) {
@@ -9032,6 +9655,7 @@ var AgentProcessManager = class _AgentProcessManager {
9032
9655
  maxConcurrentAgentStarts;
9033
9656
  agentStartIntervalMs;
9034
9657
  startingInboxes = /* @__PURE__ */ new Map();
9658
+ terminalRuntimeFailures = /* @__PURE__ */ new Map();
9035
9659
  pendingStartRebinds = /* @__PURE__ */ new Map();
9036
9660
  /** Cached configs for agents whose process exited normally — enables auto-restart on next message */
9037
9661
  idleAgentConfigs = /* @__PURE__ */ new Map();
@@ -9969,7 +10593,7 @@ var AgentProcessManager = class _AgentProcessManager {
9969
10593
  );
9970
10594
  wakeMessage = void 0;
9971
10595
  }
9972
- const agentDataDir = path12.join(this.dataDir, agentId);
10596
+ const agentDataDir = path13.join(this.dataDir, agentId);
9973
10597
  await mkdir(agentDataDir, { recursive: true });
9974
10598
  let runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
9975
10599
  const legacyRuntimeProfileControl = runtimeConfig.runtimeProfileControl?.kind === "migration" ? runtimeConfig.runtimeProfileControl : null;
@@ -9983,23 +10607,23 @@ var AgentProcessManager = class _AgentProcessManager {
9983
10607
  );
9984
10608
  runtimeConfig = { ...runtimeConfig, runtimeProfileControl: null };
9985
10609
  }
9986
- const memoryMdPath = path12.join(agentDataDir, "MEMORY.md");
10610
+ const memoryMdPath = path13.join(agentDataDir, "MEMORY.md");
9987
10611
  try {
9988
10612
  await access(memoryMdPath);
9989
10613
  } catch {
9990
10614
  const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
9991
10615
  await writeFile(memoryMdPath, initialMemoryMd);
9992
10616
  }
9993
- const notesDir = path12.join(agentDataDir, "notes");
10617
+ const notesDir = path13.join(agentDataDir, "notes");
9994
10618
  await mkdir(notesDir, { recursive: true });
9995
10619
  if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
9996
10620
  const seedFiles = buildOnboardingSeedFiles();
9997
10621
  for (const { relativePath, content } of seedFiles) {
9998
- const fullPath = path12.join(agentDataDir, relativePath);
10622
+ const fullPath = path13.join(agentDataDir, relativePath);
9999
10623
  try {
10000
10624
  await access(fullPath);
10001
10625
  } catch {
10002
- await mkdir(path12.dirname(fullPath), { recursive: true });
10626
+ await mkdir(path13.dirname(fullPath), { recursive: true });
10003
10627
  await writeFile(fullPath, content);
10004
10628
  }
10005
10629
  }
@@ -10116,7 +10740,8 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
10116
10740
  daemonApiKey: this.daemonApiKey,
10117
10741
  launchId: effectiveLaunchId,
10118
10742
  agentCredentialProxyInboxCoordinator: this.createAgentProxyInboxCoordinator(agentId),
10119
- cliTransportTraceDir: this.cliTransportTraceDir
10743
+ cliTransportTraceDir: this.cliTransportTraceDir,
10744
+ tracer: this.tracer
10120
10745
  };
10121
10746
  const runtime = driver.createSession?.(runtimeContext) ?? createChildProcessRuntimeSession(driver, runtimeContext);
10122
10747
  agentProcess = {
@@ -10395,10 +11020,24 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
10395
11020
  }
10396
11021
  }
10397
11022
  });
10398
- const startResult = await runtime.start({ text: prompt, sessionId: effectiveConfig.sessionId || null });
11023
+ let startResult;
11024
+ try {
11025
+ startResult = await runtime.start({ text: prompt, sessionId: effectiveConfig.sessionId || null });
11026
+ } catch (error) {
11027
+ const message = error instanceof Error ? error.message : String(error);
11028
+ startResult = { ok: false, reason: "runtime_error", error: message };
11029
+ }
10399
11030
  if (!startResult.ok) {
11031
+ const diagnostics = startResult.error ? buildRuntimeErrorDiagnosticEnvelope(startResult.error) : null;
11032
+ this.recordDaemonTrace("daemon.agent.runtime_start.failed", {
11033
+ ...this.startQueueTraceAttrs(agentId, effectiveConfig, wakeMessage, unreadSummary, resumePrompt, agentProcess.launchId || void 0, wakeMessageTransient),
11034
+ runtime_start_reason: startResult.reason,
11035
+ error_present: Boolean(startResult.error),
11036
+ runtime_error_class: diagnostics?.spanAttrs.runtime_error_class
11037
+ }, "error");
10400
11038
  throw new Error(`Runtime session failed to start: ${startResult.reason}${startResult.error ? ` (${startResult.error})` : ""}`);
10401
11039
  }
11040
+ this.terminalRuntimeFailures.delete(agentId);
10402
11041
  this.recordDaemonTrace("daemon.agent.spawn.created", {
10403
11042
  ...this.startQueueTraceAttrs(agentId, effectiveConfig, wakeMessage, unreadSummary, resumePrompt, agentProcess.launchId || void 0, wakeMessageTransient),
10404
11043
  detached: false,
@@ -10452,6 +11091,52 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
10452
11091
  this.revokeManagedRunnerCredential(agentId, ap.config, ap.launchId);
10453
11092
  this.agents.delete(agentId);
10454
11093
  this.idleAgentConfigs.delete(agentId);
11094
+ this.terminalRuntimeFailures.delete(agentId);
11095
+ }
11096
+ cleanupTerminalRuntimeFailure(agentId, ap, detail) {
11097
+ if (this.agents.get(agentId) !== ap) return;
11098
+ ap.notifications.clear();
11099
+ this.clearRuntimeErrorDeliveryBackoff(ap);
11100
+ if (ap.pendingTrajectory?.timer) {
11101
+ clearTimeout(ap.pendingTrajectory.timer);
11102
+ ap.pendingTrajectory.timer = null;
11103
+ }
11104
+ if (ap.activityHeartbeat) {
11105
+ clearInterval(ap.activityHeartbeat);
11106
+ ap.activityHeartbeat = null;
11107
+ }
11108
+ this.clearCompactionWatchdog(ap);
11109
+ this.clearRuntimeStartupTimeout(ap);
11110
+ this.clearStalledRecoverySigtermWatchdog(ap);
11111
+ cleanupAgentCredentialProxy(agentId, ap.launchId);
11112
+ this.revokeManagedRunnerCredential(agentId, ap.config, ap.launchId);
11113
+ this.idleAgentConfigs.delete(agentId);
11114
+ const pending = this.startingInboxes.get(agentId) || [];
11115
+ if (ap.inbox.length > 0) {
11116
+ this.startingInboxes.set(agentId, [...pending, ...ap.inbox]);
11117
+ }
11118
+ this.terminalRuntimeFailures.set(agentId, { detail, launchId: ap.launchId });
11119
+ this.agents.delete(agentId);
11120
+ const diagnostics = buildRuntimeErrorDiagnosticEnvelope(detail);
11121
+ this.recordDaemonTrace("daemon.agent.terminal_runtime_error.cleanup", {
11122
+ agentId,
11123
+ launchId: ap.launchId || void 0,
11124
+ runtime: ap.config.runtime,
11125
+ model: ap.config.model,
11126
+ session_id_present: Boolean(ap.sessionId),
11127
+ process_pid_present: typeof ap.runtime.pid === "number",
11128
+ inbox_count: ap.inbox.length,
11129
+ pending_notification_count: ap.notifications.pendingCount,
11130
+ runtime_error_class: diagnostics.spanAttrs.runtime_error_class
11131
+ }, "error");
11132
+ this.runtimeExitTraceAttrs.set(ap.runtime, {
11133
+ stop_source: "terminal_runtime_error",
11134
+ runtime_error_class: diagnostics.spanAttrs.runtime_error_class
11135
+ });
11136
+ void ap.runtime.stop({ signal: "SIGTERM", reason: "terminal_runtime_error" }).catch((err) => {
11137
+ const reason = err instanceof Error ? err.message : String(err);
11138
+ logger.warn(`[Agent ${agentId}] Failed to terminate ${ap.driver.id} after terminal runtime error: ${reason}`);
11139
+ });
10455
11140
  }
10456
11141
  cacheStartupTimeoutRetryConfig(agentId, ap) {
10457
11142
  const retryConfig = {
@@ -10690,6 +11375,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
10690
11375
  this.cancelQueuedAgentStart(agentId, "stop requested");
10691
11376
  this.pendingStartRebinds.delete(agentId);
10692
11377
  this.idleAgentConfigs.delete(agentId);
11378
+ this.terminalRuntimeFailures.delete(agentId);
10693
11379
  const ap = this.agents.get(agentId);
10694
11380
  if (!ap) {
10695
11381
  if (!silent) {
@@ -10792,6 +11478,35 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
10792
11478
  }));
10793
11479
  return true;
10794
11480
  }
11481
+ const terminalRuntimeFailure = this.terminalRuntimeFailures.get(agentId);
11482
+ if (terminalRuntimeFailure) {
11483
+ if (transientDelivery) {
11484
+ this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
11485
+ outcome: "transient_dropped_terminal_runtime_error_no_process",
11486
+ accepted: true,
11487
+ process_present: false,
11488
+ cached_idle_config_present: false,
11489
+ terminal_runtime_failure: true,
11490
+ launchId: terminalRuntimeFailure.launchId || void 0
11491
+ }));
11492
+ return true;
11493
+ }
11494
+ const pending = this.startingInboxes.get(agentId) || [];
11495
+ pending.push(message);
11496
+ this.startingInboxes.set(agentId, pending);
11497
+ this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
11498
+ outcome: "queued_terminal_runtime_error_no_process",
11499
+ accepted: true,
11500
+ process_present: false,
11501
+ cached_idle_config_present: false,
11502
+ terminal_runtime_failure: true,
11503
+ starting_inbox_count: pending.length,
11504
+ launchId: terminalRuntimeFailure.launchId || void 0
11505
+ }));
11506
+ this.sendAgentStatus(agentId, "inactive", terminalRuntimeFailure.launchId);
11507
+ this.broadcastActivity(agentId, "error", terminalRuntimeFailure.detail, [], terminalRuntimeFailure.launchId);
11508
+ return true;
11509
+ }
10795
11510
  const cached = this.idleAgentConfigs.get(agentId);
10796
11511
  if (cached) {
10797
11512
  const driver = this.driverResolver(cached.config.runtime || "claude");
@@ -11135,7 +11850,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
11135
11850
  return true;
11136
11851
  }
11137
11852
  async resetWorkspace(agentId) {
11138
- const agentDataDir = path12.join(this.dataDir, agentId);
11853
+ const agentDataDir = path13.join(this.dataDir, agentId);
11139
11854
  try {
11140
11855
  await rm2(agentDataDir, { recursive: true, force: true });
11141
11856
  logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
@@ -11196,7 +11911,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
11196
11911
  return result;
11197
11912
  }
11198
11913
  buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
11199
- const workspacePath = path12.join(this.dataDir, agentId);
11914
+ const workspacePath = path13.join(this.dataDir, agentId);
11200
11915
  const runtimeHomeDir = resolveRuntimeHomeDir(config, this.runtimeSessionHomeDir, workspacePath);
11201
11916
  return {
11202
11917
  agentId,
@@ -11490,7 +12205,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
11490
12205
  }
11491
12206
  // Workspace file browsing
11492
12207
  async getFileTree(agentId, dirPath) {
11493
- const agentDir = path12.join(this.dataDir, agentId);
12208
+ const agentDir = path13.join(this.dataDir, agentId);
11494
12209
  try {
11495
12210
  await stat2(agentDir);
11496
12211
  } catch {
@@ -11498,8 +12213,8 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
11498
12213
  }
11499
12214
  let targetDir = agentDir;
11500
12215
  if (dirPath) {
11501
- const resolved = path12.resolve(agentDir, dirPath);
11502
- if (!resolved.startsWith(agentDir + path12.sep) && resolved !== agentDir) {
12216
+ const resolved = path13.resolve(agentDir, dirPath);
12217
+ if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
11503
12218
  return [];
11504
12219
  }
11505
12220
  targetDir = resolved;
@@ -11507,14 +12222,14 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
11507
12222
  return this.listDirectoryChildren(targetDir, agentDir);
11508
12223
  }
11509
12224
  async readFile(agentId, filePath) {
11510
- const agentDir = path12.join(this.dataDir, agentId);
11511
- const resolved = path12.resolve(agentDir, filePath);
11512
- if (!resolved.startsWith(agentDir + path12.sep) && resolved !== agentDir) {
12225
+ const agentDir = path13.join(this.dataDir, agentId);
12226
+ const resolved = path13.resolve(agentDir, filePath);
12227
+ if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
11513
12228
  throw new Error("Access denied");
11514
12229
  }
11515
12230
  const info = await stat2(resolved);
11516
12231
  if (info.isDirectory()) throw new Error("Cannot read a directory");
11517
- const ext = path12.extname(resolved).toLowerCase();
12232
+ const ext = path13.extname(resolved).toLowerCase();
11518
12233
  if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
11519
12234
  if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
11520
12235
  const content = await readFile(resolved, "utf-8");
@@ -11550,14 +12265,14 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
11550
12265
  const idle = this.idleAgentConfigs.get(agentId);
11551
12266
  const config = agent?.config ?? idle?.config ?? null;
11552
12267
  const runtime = runtimeHint || config?.runtime || "claude";
11553
- const workspaceDir = path12.join(this.dataDir, agentId);
12268
+ const workspaceDir = path13.join(this.dataDir, agentId);
11554
12269
  const home = config ? ensureRuntimeHomeDir(config, os6.homedir(), workspaceDir) : os6.homedir();
11555
12270
  const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
11556
12271
  const globalResults = await Promise.all(
11557
- paths.global.map((p) => this.scanSkillsDir(path12.join(home, p)))
12272
+ paths.global.map((p) => this.scanSkillsDir(path13.join(home, p)))
11558
12273
  );
11559
12274
  const workspaceResults = await Promise.all(
11560
- paths.workspace.map((p) => this.scanSkillsDir(path12.join(workspaceDir, p)))
12275
+ paths.workspace.map((p) => this.scanSkillsDir(path13.join(workspaceDir, p)))
11561
12276
  );
11562
12277
  const dedup = (skills) => {
11563
12278
  const seen = /* @__PURE__ */ new Set();
@@ -11586,7 +12301,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
11586
12301
  const skills = [];
11587
12302
  for (const entry of entries) {
11588
12303
  if (entry.isDirectory() || entry.isSymbolicLink()) {
11589
- const skillMd = path12.join(dir, entry.name, "SKILL.md");
12304
+ const skillMd = path13.join(dir, entry.name, "SKILL.md");
11590
12305
  try {
11591
12306
  const content = await readFile(skillMd, "utf-8");
11592
12307
  const skill = this.parseSkillMd(entry.name, content);
@@ -11597,7 +12312,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
11597
12312
  } else if (entry.name.endsWith(".md")) {
11598
12313
  const cmdName = entry.name.replace(/\.md$/, "");
11599
12314
  try {
11600
- const content = await readFile(path12.join(dir, entry.name), "utf-8");
12315
+ const content = await readFile(path13.join(dir, entry.name), "utf-8");
11601
12316
  const skill = this.parseSkillMd(cmdName, content);
11602
12317
  skill.sourcePath = dir;
11603
12318
  skills.push(skill);
@@ -12574,8 +13289,8 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
12574
13289
  logger.warn(`[Agent ${agentId}] Failed to terminate ${ap.driver.id} after auth error: ${reason}`);
12575
13290
  }
12576
13291
  } else if (stickyTerminalFailure) {
12577
- ap.notifications.clear();
12578
13292
  this.sendAgentStatus(agentId, "inactive", ap.launchId);
13293
+ this.cleanupTerminalRuntimeFailure(agentId, ap, stickyTerminalFailure.detail);
12579
13294
  logger.warn(`[Agent ${agentId}] ${ap.driver.id} terminal runtime error requires explicit recovery`);
12580
13295
  } else {
12581
13296
  ap.notifications.clearPending();
@@ -13077,8 +13792,8 @@ ${RESPONSE_TARGET_HINT}`);
13077
13792
  const nodes = [];
13078
13793
  for (const entry of entries) {
13079
13794
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
13080
- const fullPath = path12.join(dir, entry.name);
13081
- const relativePath = path12.relative(rootDir, fullPath);
13795
+ const fullPath = path13.join(dir, entry.name);
13796
+ const relativePath = path13.relative(rootDir, fullPath);
13082
13797
  let info;
13083
13798
  try {
13084
13799
  info = await stat2(fullPath);
@@ -13451,10 +14166,10 @@ var ReminderCache = class {
13451
14166
  };
13452
14167
 
13453
14168
  // src/machineLock.ts
13454
- import { createHash as createHash4, randomUUID as randomUUID4 } from "crypto";
13455
- import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync5 } from "fs";
14169
+ import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
14170
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync5 } from "fs";
13456
14171
  import os7 from "os";
13457
- import path13 from "path";
14172
+ import path14 from "path";
13458
14173
  var INCOMPLETE_LOCK_STALE_MS = 3e4;
13459
14174
  var DaemonMachineLockConflictError = class extends Error {
13460
14175
  code = "DAEMON_MACHINE_LOCK_HELD";
@@ -13476,7 +14191,7 @@ function resolveDefaultMachineStateRoot() {
13476
14191
  return resolveSlockHomePath("machines");
13477
14192
  }
13478
14193
  function ownerPath(lockDir) {
13479
- return path13.join(lockDir, "owner.json");
14194
+ return path14.join(lockDir, "owner.json");
13480
14195
  }
13481
14196
  function readOwner(lockDir) {
13482
14197
  try {
@@ -13506,13 +14221,13 @@ function acquireDaemonMachineLock(options) {
13506
14221
  const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
13507
14222
  const fingerprint = apiKeyFingerprint(options.apiKey);
13508
14223
  const lockId = getDaemonMachineLockId(options.apiKey);
13509
- const machineDir = path13.join(rootDir, lockId);
13510
- const lockDir = path13.join(machineDir, "daemon.lock");
13511
- const token = randomUUID4();
13512
- mkdirSync5(machineDir, { recursive: true });
14224
+ const machineDir = path14.join(rootDir, lockId);
14225
+ const lockDir = path14.join(machineDir, "daemon.lock");
14226
+ const token = randomUUID5();
14227
+ mkdirSync6(machineDir, { recursive: true });
13513
14228
  for (let attempt = 0; attempt < 2; attempt += 1) {
13514
14229
  try {
13515
- mkdirSync5(lockDir);
14230
+ mkdirSync6(lockDir);
13516
14231
  const owner = {
13517
14232
  pid: process.pid,
13518
14233
  token,
@@ -13567,8 +14282,8 @@ function acquireDaemonMachineLock(options) {
13567
14282
  }
13568
14283
 
13569
14284
  // src/localTraceSink.ts
13570
- import { appendFileSync, mkdirSync as mkdirSync6, readdirSync as readdirSync4, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
13571
- import path14 from "path";
14285
+ import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync4, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
14286
+ import path15 from "path";
13572
14287
  var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
13573
14288
  var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
13574
14289
  var DEFAULT_MAX_FILES = 8;
@@ -13605,7 +14320,7 @@ var LocalRotatingTraceSink = class {
13605
14320
  currentSize = 0;
13606
14321
  sequence = 0;
13607
14322
  constructor(options) {
13608
- this.traceDir = path14.join(options.machineDir, "traces");
14323
+ this.traceDir = path15.join(options.machineDir, "traces");
13609
14324
  this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
13610
14325
  const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
13611
14326
  const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
@@ -13631,11 +14346,11 @@ var LocalRotatingTraceSink = class {
13631
14346
  return this.currentFile;
13632
14347
  }
13633
14348
  ensureFile(nextBytes) {
13634
- mkdirSync6(this.traceDir, { recursive: true, mode: 448 });
14349
+ mkdirSync7(this.traceDir, { recursive: true, mode: 448 });
13635
14350
  const nowMs = this.nowMsProvider();
13636
14351
  const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
13637
14352
  if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
13638
- this.currentFile = path14.join(
14353
+ this.currentFile = path15.join(
13639
14354
  this.traceDir,
13640
14355
  `daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
13641
14356
  );
@@ -13650,7 +14365,7 @@ var LocalRotatingTraceSink = class {
13650
14365
  const excess = files.length - this.maxFiles;
13651
14366
  if (excess <= 0) return;
13652
14367
  for (const file of files.slice(0, excess)) {
13653
- rmSync4(path14.join(this.traceDir, file), { force: true });
14368
+ rmSync4(path15.join(this.traceDir, file), { force: true });
13654
14369
  }
13655
14370
  }
13656
14371
  };
@@ -13738,10 +14453,10 @@ function isDiagnosticErrorAttr(key) {
13738
14453
  }
13739
14454
 
13740
14455
  // src/traceBundleUpload.ts
13741
- import { createHash as createHash6, randomUUID as randomUUID5 } from "crypto";
14456
+ import { createHash as createHash6, randomUUID as randomUUID6 } from "crypto";
13742
14457
  import { gzipSync } from "zlib";
13743
14458
  import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
13744
- import path15 from "path";
14459
+ import path16 from "path";
13745
14460
 
13746
14461
  // src/chatBridgeRequest.ts
13747
14462
  var DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS = Number.parseInt(
@@ -13841,8 +14556,8 @@ async function executeResponseRequest(url, init, {
13841
14556
  }
13842
14557
 
13843
14558
  // src/directUploadCapability.ts
13844
- function joinUrl(base, path17) {
13845
- return `${base.replace(/\/+$/, "")}${path17}`;
14559
+ function joinUrl(base, path18) {
14560
+ return `${base.replace(/\/+$/, "")}${path18}`;
13846
14561
  }
13847
14562
  function jsonHeaders(apiKey) {
13848
14563
  return {
@@ -14061,7 +14776,7 @@ var DaemonTraceBundleUploader = class {
14061
14776
  }, nextMs);
14062
14777
  }
14063
14778
  async findUploadCandidates() {
14064
- const traceDir = path15.join(this.options.machineDir, "traces");
14779
+ const traceDir = path16.join(this.options.machineDir, "traces");
14065
14780
  let names;
14066
14781
  try {
14067
14782
  names = await readdir3(traceDir);
@@ -14073,8 +14788,8 @@ var DaemonTraceBundleUploader = class {
14073
14788
  const currentFile = this.options.currentFileProvider?.();
14074
14789
  const candidates = [];
14075
14790
  for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
14076
- const file = path15.join(traceDir, name);
14077
- if (currentFile && path15.resolve(file) === path15.resolve(currentFile)) continue;
14791
+ const file = path16.join(traceDir, name);
14792
+ if (currentFile && path16.resolve(file) === path16.resolve(currentFile)) continue;
14078
14793
  if (await this.isUploaded(file)) continue;
14079
14794
  try {
14080
14795
  const info = await stat3(file);
@@ -14106,7 +14821,7 @@ var DaemonTraceBundleUploader = class {
14106
14821
  }
14107
14822
  const gzipped = gzipSync(raw);
14108
14823
  const bundleSha256 = sha256Hex(gzipped);
14109
- const bundleId = randomUUID5();
14824
+ const bundleId = randomUUID6();
14110
14825
  await uploadWithSignedCapability({
14111
14826
  serverUrl: this.options.serverUrl,
14112
14827
  apiKey: this.options.apiKey,
@@ -14148,8 +14863,8 @@ var DaemonTraceBundleUploader = class {
14148
14863
  }
14149
14864
  }
14150
14865
  uploadStatePath(file) {
14151
- const stateDir = path15.join(this.options.machineDir, "trace-uploads");
14152
- return path15.join(stateDir, `${path15.basename(file)}.uploaded.json`);
14866
+ const stateDir = path16.join(this.options.machineDir, "trace-uploads");
14867
+ return path16.join(stateDir, `${path16.basename(file)}.uploaded.json`);
14153
14868
  }
14154
14869
  async isUploaded(file) {
14155
14870
  try {
@@ -14161,9 +14876,9 @@ var DaemonTraceBundleUploader = class {
14161
14876
  }
14162
14877
  async markUploaded(file, metadata) {
14163
14878
  const stateFile = this.uploadStatePath(file);
14164
- await mkdir2(path15.dirname(stateFile), { recursive: true, mode: 448 });
14879
+ await mkdir2(path16.dirname(stateFile), { recursive: true, mode: 448 });
14165
14880
  await writeFile2(stateFile, `${JSON.stringify({
14166
- file: path15.basename(file),
14881
+ file: path16.basename(file),
14167
14882
  uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
14168
14883
  ...metadata
14169
14884
  }, null, 2)}
@@ -14226,6 +14941,24 @@ var DAEMON_CORE_TRACE_ATTR_CONTRACTS = {
14226
14941
  "daemon.runtime_profile.report.sent": {
14227
14942
  spanAttrs: ["agentId", "launchId", "runtime", "report_source", "model_present", "session_ref_present", "workspace_ref_present"]
14228
14943
  },
14944
+ "daemon.runtime_models.detect": {
14945
+ spanAttrs: ["runtime", "requestId"],
14946
+ eventAttrs: {
14947
+ "daemon.pi.models.services_ready": ["available_models_count", "diagnostics_count", "diagnostic_info_count", "diagnostic_warning_count"],
14948
+ "daemon.pi.models.result": ["available_models_count", "returned_models_count", "diagnostics_count", "diagnostic_info_count", "diagnostic_warning_count", "outcome"]
14949
+ },
14950
+ endAttrs: ["outcome", "models_count", "default_model_present", "verified_as", "error_class"]
14951
+ },
14952
+ "daemon.pi.session.create": {
14953
+ spanAttrs: ["agentId", "launchId", "runtime", "model", "session_id_present", "requested_model"],
14954
+ eventAttrs: {
14955
+ "daemon.pi.session.services_ready": ["available_models_count", "diagnostics_count", "diagnostic_info_count", "diagnostic_warning_count", "agent_dir_source"],
14956
+ "daemon.pi.session.model_resolved": ["available_models_count", "requested_model", "requested_model_explicit", "resolved_model", "resolved_model_present"],
14957
+ "daemon.pi.session.missing_model": ["available_models_count", "requested_model"],
14958
+ "daemon.pi.session.started": ["requested_model", "resolved_model", "session_id_present"]
14959
+ },
14960
+ endAttrs: ["outcome", "available_models_count", "diagnostics_count", "diagnostic_info_count", "diagnostic_warning_count", "requested_model", "resolved_model", "resolved_model_present", "error_class"]
14961
+ },
14229
14962
  "daemon.connection.local_disconnect_observed": {
14230
14963
  spanAttrs: ["running_agents_count", "idle_agents_count"]
14231
14964
  }
@@ -14278,20 +15011,20 @@ function parseDaemonCliArgs(args) {
14278
15011
  }
14279
15012
  function readDaemonVersion(moduleUrl = import.meta.url) {
14280
15013
  try {
14281
- const require2 = createRequire2(moduleUrl);
15014
+ const require2 = createRequire3(moduleUrl);
14282
15015
  return require2("../package.json").version;
14283
15016
  } catch {
14284
15017
  return "0.0.0-dev";
14285
15018
  }
14286
15019
  }
14287
15020
  function resolveSlockCliPath(moduleUrl = import.meta.url) {
14288
- const thisDir = path16.dirname(fileURLToPath(moduleUrl));
14289
- const bundledDistPath = path16.resolve(thisDir, "cli", "index.js");
15021
+ const thisDir = path17.dirname(fileURLToPath(moduleUrl));
15022
+ const bundledDistPath = path17.resolve(thisDir, "cli", "index.js");
14290
15023
  try {
14291
15024
  accessSync(bundledDistPath);
14292
15025
  return bundledDistPath;
14293
15026
  } catch {
14294
- const workspaceDistPath = path16.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
15027
+ const workspaceDistPath = path17.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
14295
15028
  accessSync(workspaceDistPath);
14296
15029
  return workspaceDistPath;
14297
15030
  }
@@ -14305,7 +15038,7 @@ function resolveSlockCliPathOrEmpty(moduleUrl = import.meta.url) {
14305
15038
  }
14306
15039
  async function runBundledSlockCli(argv) {
14307
15040
  process.argv = [process.execPath, "slock", ...argv];
14308
- await import("./dist-JVLCJ2QO.js");
15041
+ await import("./dist-6YUWBDWX.js");
14309
15042
  }
14310
15043
  function detectRuntimes(tracer = noopTracer) {
14311
15044
  const ids = [];
@@ -14497,7 +15230,7 @@ var DaemonCore = class {
14497
15230
  }
14498
15231
  resolveMachineStateRoot() {
14499
15232
  if (this.options.machineStateDir) return this.options.machineStateDir;
14500
- if (this.options.dataDir) return path16.join(path16.dirname(this.options.dataDir), "machines");
15233
+ if (this.options.dataDir) return path17.join(path17.dirname(this.options.dataDir), "machines");
14501
15234
  return resolveDefaultMachineStateRoot();
14502
15235
  }
14503
15236
  shouldEnableLocalTrace() {
@@ -14524,7 +15257,7 @@ var DaemonCore = class {
14524
15257
  sink: this.localTraceSink
14525
15258
  }));
14526
15259
  this.agentManager.setTracer(this.tracer);
14527
- this.agentManager.setCliTransportTraceDir(path16.join(machineDir, "traces"));
15260
+ this.agentManager.setCliTransportTraceDir(path17.join(machineDir, "traces"));
14528
15261
  }
14529
15262
  installTraceBundleUploader(machineDir) {
14530
15263
  if (!this.shouldEnableLocalTrace()) return;
@@ -14926,7 +15659,15 @@ var DaemonCore = class {
14926
15659
  break;
14927
15660
  case "machine:runtime_models:detect": {
14928
15661
  const driver = getDriver(msg.runtime);
14929
- const detect = typeof driver?.detectModels === "function" ? driver.detectModels() : Promise.resolve(null);
15662
+ const span = this.tracer.startSpan("daemon.runtime_models.detect", {
15663
+ surface: "daemon",
15664
+ kind: "internal",
15665
+ attrs: {
15666
+ runtime: msg.runtime,
15667
+ requestId: msg.requestId
15668
+ }
15669
+ });
15670
+ const detect = typeof driver?.detectModels === "function" ? driver.detectModels({ tracer: this.tracer, span }) : Promise.resolve(null);
14930
15671
  Promise.resolve(detect).then((result) => {
14931
15672
  if (result) {
14932
15673
  const verified = driver.model.detectedModelsVerifiedAs;
@@ -14935,12 +15676,32 @@ var DaemonCore = class {
14935
15676
  verified: model.verified ?? verified
14936
15677
  }));
14937
15678
  this.connection.send({ type: "machine:runtime_models:result", requestId: msg.requestId, models, default: result.default });
15679
+ span.end("ok", {
15680
+ attrs: {
15681
+ outcome: "models_returned",
15682
+ models_count: models.length,
15683
+ default_model_present: Boolean(result.default),
15684
+ verified_as: verified
15685
+ }
15686
+ });
14938
15687
  } else {
14939
15688
  this.connection.send({ type: "machine:runtime_models:result", requestId: msg.requestId, error: "unsupported" });
15689
+ span.end("ok", {
15690
+ attrs: {
15691
+ outcome: "unsupported",
15692
+ models_count: 0
15693
+ }
15694
+ });
14940
15695
  }
14941
15696
  }).catch((err) => {
14942
15697
  const reason = err instanceof Error ? err.message : String(err);
14943
15698
  this.connection.send({ type: "machine:runtime_models:result", requestId: msg.requestId, error: reason });
15699
+ span.end("error", {
15700
+ attrs: {
15701
+ outcome: "error",
15702
+ error_class: err instanceof Error ? err.name : typeof err
15703
+ }
15704
+ });
14944
15705
  });
14945
15706
  break;
14946
15707
  }