@slock-ai/daemon 0.48.1 → 0.49.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,18 +1,14 @@
1
1
  import {
2
2
  DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS,
3
- SLOCK_HOME_ENV,
4
3
  buildWebSocketOptions,
5
4
  executeJsonRequest,
6
5
  executeResponseRequest,
7
- listLegacySlockStatePaths,
8
- logger,
9
- resolveSlockHome,
10
- resolveSlockHomePath
11
- } from "./chunk-B7XIMLOT.js";
6
+ logger
7
+ } from "./chunk-KNMCE6WB.js";
12
8
 
13
9
  // src/core.ts
14
- import path15 from "path";
15
- import os7 from "os";
10
+ import path16 from "path";
11
+ import os8 from "os";
16
12
  import { createRequire } from "module";
17
13
  import { accessSync } from "fs";
18
14
  import { fileURLToPath } from "url";
@@ -481,6 +477,8 @@ function resolveSlockCliInvocation(toolName, input) {
481
477
  return { toolName: "list_server", input: {} };
482
478
  case "channel members":
483
479
  return { toolName: "list_channel_members", input: { channel: rest[0] } };
480
+ case "channel join":
481
+ return { toolName: "join_channel", input: { target: readOptionValue(rest, "--target") } };
484
482
  case "channel leave":
485
483
  return { toolName: "leave_channel", input: { target: readOptionValue(rest, "--target") } };
486
484
  case "task list":
@@ -766,21 +764,21 @@ var DISPLAY_PLAN_CONFIG = {
766
764
  };
767
765
 
768
766
  // src/agentProcessManager.ts
769
- import { mkdirSync as mkdirSync4, readdirSync as readdirSync3, statSync as statSync2, writeFileSync as writeFileSync7 } from "fs";
767
+ import { mkdirSync as mkdirSync4, readdirSync as readdirSync2, statSync as statSync2, writeFileSync as writeFileSync7 } from "fs";
770
768
  import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
771
769
  import { createHash as createHash2 } from "crypto";
772
- import path11 from "path";
773
- import os5 from "os";
770
+ import path12 from "path";
771
+ import os6 from "os";
774
772
 
775
773
  // src/drivers/claude.ts
776
774
  import { spawn } from "child_process";
777
- import { existsSync as existsSync2, readdirSync, readFileSync, statSync, writeFileSync as writeFileSync2 } from "fs";
778
- import os from "os";
779
- import path3 from "path";
775
+ import { existsSync as existsSync3, readdirSync, readFileSync, statSync, writeFileSync as writeFileSync2 } from "fs";
776
+ import os2 from "os";
777
+ import path4 from "path";
780
778
 
781
779
  // src/drivers/cliTransport.ts
782
780
  import { mkdirSync, writeFileSync } from "fs";
783
- import path from "path";
781
+ import path2 from "path";
784
782
 
785
783
  // src/drivers/systemPrompt.ts
786
784
  function toolRef(prefix, name) {
@@ -858,27 +856,28 @@ Use the \`slock\` CLI for chat / task / attachment operations. The daemon inject
858
856
  2. **\`slock message send\`** \u2014 Send a message to a channel or DM.
859
857
  3. **\`slock server info\`** \u2014 List channels in this server, which ones you have joined, plus all agents and humans.
860
858
  4. **\`slock channel members\`** \u2014 List the members (agents and humans) of a specific channel, DM, or thread target.
861
- 5. **\`slock channel leave\`** \u2014 Leave a regular channel you have joined. This only affects your own agent membership.
862
- 6. **\`slock thread unfollow\`** \u2014 Stop receiving ordinary delivery for a thread you no longer need to follow. This only affects your own agent attention state.
863
- 7. **\`slock message read\`** \u2014 Read past messages from a channel, DM, or thread. Supports \`before\` / \`after\` pagination and \`around\` for centered context.
864
- 8. **\`slock message search\`** \u2014 Search messages visible to you, then inspect a hit with \`slock message read\`.
865
- 9. **\`slock message react\`** \u2014 Add or remove your reaction on a message. Use sparingly: prefer acknowledgement/follow-up signals like \u{1F440}, and do not auto-react to every merge, deploy, or task completion with celebratory emoji.
866
- 10. **\`slock task list\`** \u2014 View a channel's task board.
867
- 11. **\`slock task create\`** \u2014 Create new task-messages in a channel (supports batch titles; equivalent to sending a new message and publishing it as a task-message, not claiming it for yourself).
868
- 12. **\`slock task claim\`** \u2014 Claim tasks by number or message ID (supports batch, handles conflicts).
869
- 13. **\`slock task unclaim\`** \u2014 Release your claim on a task.
870
- 14. **\`slock task update\`** \u2014 Change a task's status (e.g. to in_review or done).
871
- 15. **\`slock attachment upload\`** \u2014 Upload a file to attach to a message. Uses content sniffing for image previews; pass \`--mime-type\` only when you know the exact type. Returns an attachment ID to pass to \`slock message send\`.
872
- 16. **\`slock attachment view\`** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
873
- 17. **\`slock profile show\`** \u2014 Show your own profile, or another visible profile via \`@handle\`. Mirrors the canonical Slock profile view.
874
- 18. **\`slock profile update\`** \u2014 Update your own profile. Supports \`--avatar-file <path>\`, \`--display-name <name>\`, and \`--description <text>\`. Values must be non-empty. Provide at least one flag per call; multiple flags can be combined.
875
- 19. **\`slock reminder schedule\`** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
876
- 20. **\`slock reminder list\`** \u2014 List your reminders, including lifecycle history for each reminder.
877
- 21. **\`slock reminder snooze\`** \u2014 Push a reminder later without replacing it.
878
- 22. **\`slock reminder update\`** \u2014 Change a reminder's title, schedule, or recurrence without creating a new reminder.
879
- 23. **\`slock reminder cancel\`** \u2014 Cancel one of your reminders by ID.
880
- 24. **\`slock reminder log\`** \u2014 Show the event log for a reminder, including fires, dismissals, and reschedules.
881
- 25. **\`slock action prepare\`** \u2014 Prepare an action card for a human to commit (B-mode quick-commit shortcut). Posts a card the human can click to execute the action under their own identity. Pass \`--target <ch>\` and pipe the action JSON on stdin (variants: \`channel:create\`, \`agent:create\`).
859
+ 5. **\`slock channel join\`** \u2014 Join a visible public channel. This only affects your own agent membership.
860
+ 6. **\`slock channel leave\`** \u2014 Leave a regular channel you have joined. This only affects your own agent membership.
861
+ 7. **\`slock thread unfollow\`** \u2014 Stop receiving ordinary delivery for a thread you no longer need to follow. This only affects your own agent attention state.
862
+ 8. **\`slock message read\`** \u2014 Read past messages from a channel, DM, or thread. Supports \`before\` / \`after\` pagination and \`around\` for centered context.
863
+ 9. **\`slock message search\`** \u2014 Search messages visible to you, then inspect a hit with \`slock message read\`.
864
+ 10. **\`slock message react\`** \u2014 Add or remove your reaction on a message. Use sparingly: prefer acknowledgement/follow-up signals like \u{1F440}, and do not auto-react to every merge, deploy, or task completion with celebratory emoji.
865
+ 11. **\`slock task list\`** \u2014 View a channel's task board.
866
+ 12. **\`slock task create\`** \u2014 Create new task-messages in a channel (supports batch titles; equivalent to sending a new message and publishing it as a task-message, not claiming it for yourself).
867
+ 13. **\`slock task claim\`** \u2014 Claim tasks by number or message ID (supports batch, handles conflicts).
868
+ 14. **\`slock task unclaim\`** \u2014 Release your claim on a task.
869
+ 15. **\`slock task update\`** \u2014 Change a task's status (e.g. to in_review or done).
870
+ 16. **\`slock attachment upload\`** \u2014 Upload a file to attach to a message. Uses content sniffing for image previews; pass \`--mime-type\` only when you know the exact type. Returns an attachment ID to pass to \`slock message send\`.
871
+ 17. **\`slock attachment view\`** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
872
+ 18. **\`slock profile show\`** \u2014 Show your own profile, or another visible profile via \`@handle\`. Mirrors the canonical Slock profile view.
873
+ 19. **\`slock profile update\`** \u2014 Update your own profile. Supports \`--avatar-file <path>\`, \`--display-name <name>\`, and \`--description <text>\`. Values must be non-empty. Provide at least one flag per call; multiple flags can be combined.
874
+ 20. **\`slock reminder schedule\`** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
875
+ 21. **\`slock reminder list\`** \u2014 List your reminders, including lifecycle history for each reminder.
876
+ 22. **\`slock reminder snooze\`** \u2014 Push a reminder later without replacing it.
877
+ 23. **\`slock reminder update\`** \u2014 Change a reminder's title, schedule, or recurrence without creating a new reminder.
878
+ 24. **\`slock reminder cancel\`** \u2014 Cancel one of your reminders by ID.
879
+ 25. **\`slock reminder log\`** \u2014 Show the event log for a reminder, including fires, dismissals, and reschedules.
880
+ 26. **\`slock action prepare\`** \u2014 Prepare an action card for a human to commit (B-mode quick-commit shortcut). Posts a card the human can click to execute the action under their own identity. Pass \`--target <ch>\` and pipe the action JSON on stdin (variants: \`channel:create\`, \`agent:create\`).
882
881
 
883
882
  The CLI prints human-readable canonical text on success (matching the format you see in received messages and history). On failure it prints JSON to stderr:
884
883
  - failure \u2192 stderr \`{"ok":false,"code":"...","message":"..."}\` with non-zero exit
@@ -893,19 +892,20 @@ You have MCP tools from the "chat" server. Use ONLY these for communication:
893
892
  1. **${checkCmd}** \u2014 Non-blocking check for new messages. Use freely during work \u2014 at natural breakpoints or after notifications.
894
893
  2. **${sendCmd}** \u2014 Send a message to a channel or DM.
895
894
  3. **${serverInfoCmd}** \u2014 List all channels in this server, which ones you have joined, plus all agents and humans.
896
- 4. **\`${t("leave_channel")}\`** \u2014 Leave a regular channel you have joined. This only affects your own agent membership.
897
- 5. **${readCmd}** \u2014 Read past messages from a channel, DM, or thread. Supports \`before\` / \`after\` pagination and \`around\` for centered context.
898
- 6. **\`${t("search_messages")}\`** \u2014 Search messages visible to you, then inspect a hit with ${readCmd}.
899
- 7. **\`${t("list_tasks")}\`** \u2014 View a channel's task board.
900
- 8. **${taskCreateCmd}** \u2014 Create new task-messages in a channel (supports batch titles; equivalent to sending a new message and publishing it as a task-message, not claiming it for yourself).
901
- 9. **${taskClaimCmd}** \u2014 Claim tasks by number or message ID (supports batch, handles conflicts).
902
- 10. **\`${t("unclaim_task")}\`** \u2014 Release your claim on a task.
903
- 11. **${taskUpdateCmd}** \u2014 Change a task's status (e.g. to in_review or done).
904
- 12. **\`${t("upload_file")}\`** \u2014 Upload a file up to 50MB to attach to a message. Returns an attachment ID to pass to ${sendCmd}. Videos are downloadable attachments and are not parsed by agents; large PDFs may need to be downloaded and inspected in smaller chunks.
905
- 13. **\`${t("view_file")}\`** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
906
- 14. **${scheduleReminderCmd}** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
907
- 15. **${listRemindersCmd}** \u2014 List your reminders.
908
- 16. **${cancelReminderCmd}** \u2014 Cancel one of your reminders by ID.`;
895
+ 4. **\`${t("join_channel")}\`** \u2014 Join a visible public channel. This only affects your own agent membership.
896
+ 5. **\`${t("leave_channel")}\`** \u2014 Leave a regular channel you have joined. This only affects your own agent membership.
897
+ 6. **${readCmd}** \u2014 Read past messages from a channel, DM, or thread. Supports \`before\` / \`after\` pagination and \`around\` for centered context.
898
+ 7. **\`${t("search_messages")}\`** \u2014 Search messages visible to you, then inspect a hit with ${readCmd}.
899
+ 8. **\`${t("list_tasks")}\`** \u2014 View a channel's task board.
900
+ 9. **${taskCreateCmd}** \u2014 Create new task-messages in a channel (supports batch titles; equivalent to sending a new message and publishing it as a task-message, not claiming it for yourself).
901
+ 10. **${taskClaimCmd}** \u2014 Claim tasks by number or message ID (supports batch, handles conflicts).
902
+ 11. **\`${t("unclaim_task")}\`** \u2014 Release your claim on a task.
903
+ 12. **${taskUpdateCmd}** \u2014 Change a task's status (e.g. to in_review or done).
904
+ 13. **\`${t("upload_file")}\`** \u2014 Upload a file up to 50MB to attach to a message. Returns an attachment ID to pass to ${sendCmd}. Videos are downloadable attachments and are not parsed by agents; large PDFs may need to be downloaded and inspected in smaller chunks.
905
+ 14. **\`${t("view_file")}\`** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
906
+ 15. **${scheduleReminderCmd}** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
907
+ 16. **${listRemindersCmd}** \u2014 List your reminders.
908
+ 17. **${cancelReminderCmd}** \u2014 Cancel one of your reminders by ID.`;
909
909
  const reminderSection = `### Reminders
910
910
 
911
911
  Use reminders for follow-up that depends on future state you cannot resolve now, whether user-requested or self-driven. A reminder is an author-owned, persistent, observable, snoozable, updatable, and cancelable wake-up signal anchored to a Slock message or thread; when it fires, it wakes the author who scheduled it, not other people. If anchored to a message or thread, the receipt/fire system message is visible in that surface, but wake ownership does not transfer. To notify another human or agent later, schedule your own reminder and then @mention them when it fires. Use reminders instead of keeping the current turn alive with a long sleep or relying on MEMORY to wake you. If you expect the wait to finish within about 1 minute, you may briefly poll, but say so in the relevant thread first.
@@ -926,6 +926,10 @@ Long message with "quotes", $vars, \`backticks\`, and code blocks.
926
926
  EOF
927
927
  \`\`\`
928
928
 
929
+ If Slock says a message was not sent and was saved as a draft, choose one path:
930
+ - To update the draft, use a normal \`slock message send --target <target>\` with the revised content.
931
+ - To send the current draft unchanged, use \`slock message send --send-draft --target <target>\` with no stdin. Do not use \`--send-draft\` when changing content.
932
+
929
933
  **IMPORTANT**: To reply to any message, always reuse the exact \`target\` from the received message. This ensures your reply goes to the right place \u2014 whether it's a channel, DM, or thread.` : `### Sending messages
930
934
 
931
935
  - **Reply to a channel**: \`${t("send_message")}(target="#channel-name", content="...")\`
@@ -957,11 +961,11 @@ Threads are sub-conversations attached to a specific message. They let you discu
957
961
  const discoverySection = isCli ? `### Discovering people and channels
958
962
 
959
963
  Call \`slock server info\` to see all channels in this server, which ones you have joined, other agents, and humans.
960
- Visible public channels may appear even when \`joined=false\`. In that state you can still inspect them with \`slock message read\` and \`slock channel members\`, but you cannot send messages there or receive ordinary channel delivery until a human adds you to the channel. To leave a regular channel you have joined, use \`slock channel leave --target "#channel-name"\`. To stop following a thread without leaving its parent channel, use \`slock thread unfollow --target "#channel-name:shortid"\`.
964
+ Visible public channels may appear even when \`joined=false\`. In that state you can still inspect them with \`slock message read\` and \`slock channel members\`, but you cannot send messages there or receive ordinary channel delivery until you join with \`slock channel join --target "#channel-name"\`. Private channels require a human with access to add you. To leave a regular channel you have joined, use \`slock channel leave --target "#channel-name"\`. To stop following a thread without leaving its parent channel, use \`slock thread unfollow --target "#channel-name:shortid"\`.
961
965
  Private channels are membership-gated. If \`slock server info\` shows a channel as private, treat its name, members, and content as private to that channel; do not disclose that information in other channels, DMs, summaries, or task reports unless a human explicitly asks within an authorized context. In \`slock channel members\`, human role labels such as owner/admin show server-level authority; no role label means ordinary member.` : `### Discovering people and channels
962
966
 
963
967
  Call ${serverInfoCmd} to see all channels in this server, which ones you have joined, other agents, and humans.
964
- Visible public channels may appear even when \`joined=false\`. In that state you can still inspect them with ${readCmd}, but you cannot send messages there or receive ordinary channel delivery until a human adds you to the channel. To leave a regular channel you have joined, use \`${t("leave_channel")}\`.
968
+ Visible public channels may appear even when \`joined=false\`. In that state you can still inspect them with ${readCmd}, but you cannot send messages there or receive ordinary channel delivery until you join with \`${t("join_channel")}\`. Private channels require a human with access to add you. To leave a regular channel you have joined, use \`${t("leave_channel")}\`.
965
969
  Private channels are membership-gated. If ${serverInfoCmd} shows a channel as private, treat its name, members, and content as private to that channel; do not disclose that information in other channels, DMs, summaries, or task reports unless a human explicitly asks within an authorized context. In channel member listings, human role labels such as owner/admin show server-level authority; no role label means ordinary member.`;
966
970
  const channelAwarenessSection = isCli ? `### Channel awareness
967
971
 
@@ -1305,8 +1309,44 @@ ${config.description}. This may evolve.`;
1305
1309
  function buildCliSystemPrompt(config, opts) {
1306
1310
  return buildPrompt(config, "cli", opts);
1307
1311
  }
1308
- function buildMcpSystemPrompt(config, opts) {
1309
- return buildPrompt(config, "mcp", opts);
1312
+
1313
+ // src/slockHome.ts
1314
+ import os from "os";
1315
+ import path from "path";
1316
+ import { existsSync } from "fs";
1317
+ var SLOCK_HOME_ENV = "SLOCK_HOME";
1318
+ function resolveDefaultSlockHome(homeDir = os.homedir()) {
1319
+ return path.resolve(path.join(homeDir, ".slock"));
1320
+ }
1321
+ function resolveSlockHome(env = process.env, homeDir = os.homedir()) {
1322
+ const raw = env[SLOCK_HOME_ENV]?.trim();
1323
+ const root = raw && raw.length > 0 ? raw : resolveDefaultSlockHome(homeDir);
1324
+ return path.resolve(root);
1325
+ }
1326
+ function resolveSlockHomePath(childPath, slockHome = resolveSlockHome()) {
1327
+ return path.join(slockHome, childPath);
1328
+ }
1329
+ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.homedir()) {
1330
+ const defaultHome = resolveDefaultSlockHome(homeDir);
1331
+ if (path.resolve(slockHome) === defaultHome) return [];
1332
+ const candidates = [
1333
+ {
1334
+ path: path.join(defaultHome, "agents"),
1335
+ destination: path.join(slockHome, "agents"),
1336
+ description: "agent workspaces and per-agent runtime wrapper state"
1337
+ },
1338
+ {
1339
+ path: path.join(defaultHome, "machines"),
1340
+ destination: path.join(slockHome, "machines"),
1341
+ description: "daemon machine locks, local traces, and machine-scoped state"
1342
+ },
1343
+ {
1344
+ path: path.join(defaultHome, "attachments"),
1345
+ destination: path.join(slockHome, "attachments"),
1346
+ description: "chat bridge attachment download cache"
1347
+ }
1348
+ ];
1349
+ return candidates.filter((candidate) => existsSync(candidate.path));
1310
1350
  }
1311
1351
 
1312
1352
  // src/drivers/cliTransport.ts
@@ -1332,23 +1372,23 @@ function prepareCliTransport(ctx, extraEnv = {}, platform = process.platform) {
1332
1372
  if (!ctx.slockCliPath) {
1333
1373
  throw new Error(`${ctx.config.runtime} driver: slockCliPath is required (daemon must inject it)`);
1334
1374
  }
1335
- const slockDir = path.join(ctx.workingDirectory, ".slock");
1375
+ const slockDir = path2.join(ctx.workingDirectory, ".slock");
1336
1376
  mkdirSync(slockDir, { recursive: true });
1337
- const tokenFile = path.join(slockDir, "agent-token");
1377
+ const tokenFile = path2.join(slockDir, "agent-token");
1338
1378
  writeFileSync(tokenFile, ctx.config.authToken || ctx.daemonApiKey, { mode: 384 });
1339
- const posixWrapper = path.join(slockDir, "slock");
1379
+ const posixWrapper = path2.join(slockDir, "slock");
1340
1380
  const posixBody = `#!/usr/bin/env bash
1341
1381
  exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(ctx.slockCliPath)} "$@"
1342
1382
  `;
1343
1383
  writeFileSync(posixWrapper, posixBody, { mode: 493 });
1344
1384
  if (platform === "win32") {
1345
- const cmdWrapper = path.join(slockDir, "slock.cmd");
1385
+ const cmdWrapper = path2.join(slockDir, "slock.cmd");
1346
1386
  const cmdBody = `@echo off\r
1347
1387
  "${process.execPath}" "${ctx.slockCliPath}" %*\r
1348
1388
  `;
1349
1389
  writeFileSync(cmdWrapper, cmdBody);
1350
1390
  }
1351
- const wrapperPath = platform === "win32" ? path.join(slockDir, "slock.cmd") : posixWrapper;
1391
+ const wrapperPath = platform === "win32" ? path2.join(slockDir, "slock.cmd") : posixWrapper;
1352
1392
  const spawnEnv = {
1353
1393
  ...process.env,
1354
1394
  FORCE_COLOR: "0",
@@ -1360,7 +1400,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(ctx.slockCliPath)}
1360
1400
  ...ctx.launchId ? { SLOCK_AGENT_LAUNCH_ID: ctx.launchId } : {},
1361
1401
  SLOCK_SERVER_URL: ctx.config.serverUrl,
1362
1402
  SLOCK_AGENT_TOKEN_FILE: tokenFile,
1363
- PATH: `${slockDir}${path.delimiter}${process.env.PATH ?? ""}`
1403
+ PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
1364
1404
  };
1365
1405
  delete spawnEnv.SLOCK_AGENT_TOKEN;
1366
1406
  return {
@@ -1373,8 +1413,8 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(ctx.slockCliPath)}
1373
1413
 
1374
1414
  // src/drivers/probe.ts
1375
1415
  import { execFileSync } from "child_process";
1376
- import { existsSync } from "fs";
1377
- import path2 from "path";
1416
+ import { existsSync as existsSync2 } from "fs";
1417
+ import path3 from "path";
1378
1418
  function normalizeExecOutput(raw) {
1379
1419
  return Buffer.isBuffer(raw) ? raw.toString("utf8") : String(raw ?? "");
1380
1420
  }
@@ -1417,7 +1457,7 @@ function resolveCommandOnPath(command, deps = {}) {
1417
1457
  }
1418
1458
  }
1419
1459
  function firstExistingPath(candidates, deps = {}) {
1420
- const exists = deps.existsSyncFn ?? existsSync;
1460
+ const exists = deps.existsSyncFn ?? existsSync2;
1421
1461
  for (const candidate of candidates) {
1422
1462
  if (exists(candidate)) return candidate;
1423
1463
  }
@@ -1439,11 +1479,11 @@ function readCommandVersion(command, args = [], deps = {}) {
1439
1479
  }
1440
1480
  function resolveHomePath(relativePath, deps = {}) {
1441
1481
  const homeDir = deps.homeDir ?? deps.env?.HOME ?? process.env.HOME ?? "";
1442
- return path2.join(homeDir, relativePath);
1482
+ return path3.join(homeDir, relativePath);
1443
1483
  }
1444
1484
 
1445
1485
  // src/drivers/claude.ts
1446
- var CLAUDE_DESKTOP_CLI_RELATIVE_PATH = path3.join("Applications", "Claude Code URL Handler.app", "Contents", "MacOS", "claude");
1486
+ var CLAUDE_DESKTOP_CLI_RELATIVE_PATH = path4.join("Applications", "Claude Code URL Handler.app", "Contents", "MacOS", "claude");
1447
1487
  var CLAUDE_DESKTOP_CLI_SYSTEM_PATH = "/Applications/Claude Code URL Handler.app/Contents/MacOS/claude";
1448
1488
  var CLAUDE_SYSTEM_PROMPT_FILE = "claude-system-prompt.md";
1449
1489
  var CLAUDE_MCP_CONFIG_FILE = "claude-mcp-config.json";
@@ -1499,27 +1539,27 @@ function readClaudeMcpServers(configPath, vars = {}) {
1499
1539
  }
1500
1540
  function resolveClaudeConfigDir(ctx, home) {
1501
1541
  const configured = ctx.config.envVars?.CLAUDE_CONFIG_DIR || process.env.CLAUDE_CONFIG_DIR;
1502
- return configured && path3.isAbsolute(configured) ? configured : path3.join(home, ".claude");
1542
+ return configured && path4.isAbsolute(configured) ? configured : path4.join(home, ".claude");
1503
1543
  }
1504
1544
  function collectClaudeMcpConfigFiles(ctx, home) {
1505
1545
  const files = [];
1506
1546
  const pushIfFile = (candidate) => {
1507
1547
  try {
1508
- if (existsSync2(candidate) && statSync(candidate).isFile()) {
1548
+ if (existsSync3(candidate) && statSync(candidate).isFile()) {
1509
1549
  files.push({ path: candidate });
1510
1550
  }
1511
1551
  } catch {
1512
1552
  }
1513
1553
  };
1514
- pushIfFile(path3.join(home, ".claude.json"));
1515
- pushIfFile(path3.join(ctx.workingDirectory, ".mcp.json"));
1516
- const pluginRoot = path3.join(resolveClaudeConfigDir(ctx, home), "plugins");
1554
+ pushIfFile(path4.join(home, ".claude.json"));
1555
+ pushIfFile(path4.join(ctx.workingDirectory, ".mcp.json"));
1556
+ const pluginRoot = path4.join(resolveClaudeConfigDir(ctx, home), "plugins");
1517
1557
  try {
1518
1558
  for (const entry of readdirSync(pluginRoot)) {
1519
- const pluginPath = path3.join(pluginRoot, entry);
1520
- const configPath = path3.join(pluginPath, ".mcp.json");
1559
+ const pluginPath = path4.join(pluginRoot, entry);
1560
+ const configPath = path4.join(pluginPath, ".mcp.json");
1521
1561
  try {
1522
- if (existsSync2(configPath) && statSync(configPath).isFile()) {
1562
+ if (existsSync3(configPath) && statSync(configPath).isFile()) {
1523
1563
  files.push({
1524
1564
  path: configPath,
1525
1565
  vars: { CLAUDE_PLUGIN_ROOT: pluginPath }
@@ -1623,7 +1663,7 @@ var ClaudeDriver = class {
1623
1663
  ]
1624
1664
  };
1625
1665
  }
1626
- buildRuntimeActionsMcpConfig(ctx, home = os.homedir()) {
1666
+ buildRuntimeActionsMcpConfig(ctx, home = os2.homedir()) {
1627
1667
  const userMcpServers = buildClaudeUserMcpServers(ctx, home);
1628
1668
  if (Object.prototype.hasOwnProperty.call(userMcpServers, SLOCK_RUNTIME_ACTIONS_MCP_SERVER_NAME)) {
1629
1669
  logger.warn(
@@ -1637,9 +1677,9 @@ var ClaudeDriver = class {
1637
1677
  }
1638
1678
  });
1639
1679
  }
1640
- writeClaudeLaunchFiles(ctx, slockDir, home = os.homedir()) {
1641
- const systemPromptPath = path3.join(slockDir, CLAUDE_SYSTEM_PROMPT_FILE);
1642
- const mcpConfigPath = path3.join(slockDir, CLAUDE_MCP_CONFIG_FILE);
1680
+ writeClaudeLaunchFiles(ctx, slockDir, home = os2.homedir()) {
1681
+ const systemPromptPath = path4.join(slockDir, CLAUDE_SYSTEM_PROMPT_FILE);
1682
+ const mcpConfigPath = path4.join(slockDir, CLAUDE_MCP_CONFIG_FILE);
1643
1683
  writeFileSync2(systemPromptPath, ctx.standingPrompt, { mode: 384 });
1644
1684
  writeFileSync2(mcpConfigPath, this.buildRuntimeActionsMcpConfig(ctx, home), { mode: 384 });
1645
1685
  return { systemPromptPath, mcpConfigPath };
@@ -1788,10 +1828,10 @@ var ClaudeDriver = class {
1788
1828
  };
1789
1829
 
1790
1830
  // src/drivers/codex.ts
1791
- import { spawn as spawn2, execSync } from "child_process";
1792
- import { existsSync as existsSync3, readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
1793
- import os2 from "os";
1794
- import path4 from "path";
1831
+ import { spawn as spawn2, execFileSync as execFileSync2, execSync } from "child_process";
1832
+ import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
1833
+ import os3 from "os";
1834
+ import path5 from "path";
1795
1835
  function getCodexNotificationErrorMessage(params) {
1796
1836
  const topLevelMessage = params?.message;
1797
1837
  if (typeof topLevelMessage === "string" && topLevelMessage.trim()) {
@@ -1804,9 +1844,9 @@ function getCodexNotificationErrorMessage(params) {
1804
1844
  return null;
1805
1845
  }
1806
1846
  function ensureGitRepoForCodex(workingDirectory, deps = {}) {
1807
- const existsSyncFn = deps.existsSyncFn ?? existsSync3;
1847
+ const existsSyncFn = deps.existsSyncFn ?? existsSync4;
1808
1848
  const execSyncFn = deps.execSyncFn ?? execSync;
1809
- const gitDir = path4.join(workingDirectory, ".git");
1849
+ const gitDir = path5.join(workingDirectory, ".git");
1810
1850
  if (existsSyncFn(gitDir)) return;
1811
1851
  execSyncFn("git init", { cwd: workingDirectory, stdio: "pipe" });
1812
1852
  execSyncFn(
@@ -1818,28 +1858,44 @@ function ensureGitRepoForCodex(workingDirectory, deps = {}) {
1818
1858
  );
1819
1859
  }
1820
1860
  var CODEX_DESKTOP_BUNDLE_PATH = "/Applications/Codex.app/Contents/Resources/codex";
1821
- function resolveWindowsSandboxRunner(deps = {}) {
1822
- const homeDir = deps.homeDir ?? os2.homedir();
1823
- const sandboxBinDir = path4.join(homeDir, ".codex", ".sandbox-bin");
1824
- if (!(deps.existsSyncFn ?? existsSync3)(sandboxBinDir)) return null;
1861
+ function isWindowsSandboxRunner(commandPath) {
1862
+ return path5.basename(commandPath).toLowerCase().startsWith("codex-command-runner");
1863
+ }
1864
+ function resolveWindowsNpmCodexEntry(deps = {}) {
1865
+ const existsSyncFn = deps.existsSyncFn ?? existsSync4;
1866
+ const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
1867
+ const env = deps.env ?? process.env;
1868
+ const winPath = path5.win32;
1825
1869
  try {
1826
- const files = readdirSync2(sandboxBinDir);
1827
- const runners = files.filter((f) => f.startsWith("codex-command-runner") && f.endsWith(".exe")).sort((a, b) => b.localeCompare(a));
1828
- if (runners.length > 0) return path4.join(sandboxBinDir, runners[0]);
1870
+ const globalRoot = String(execFileSyncFn("npm", ["root", "-g"], {
1871
+ encoding: "utf8",
1872
+ stdio: ["ignore", "pipe", "ignore"],
1873
+ env
1874
+ })).trim();
1875
+ const candidate = winPath.join(globalRoot, "@openai", "codex", "bin", "codex.js");
1876
+ if (existsSyncFn(candidate)) return candidate;
1829
1877
  } catch {
1830
1878
  }
1879
+ const cmdPath = resolveCommandOnPath("codex", deps);
1880
+ if (cmdPath) {
1881
+ const candidate = winPath.join(winPath.dirname(cmdPath), "node_modules", "@openai", "codex", "bin", "codex.js");
1882
+ if (existsSyncFn(candidate)) return candidate;
1883
+ }
1831
1884
  return null;
1832
1885
  }
1833
1886
  function resolveCodexCommand(deps = {}) {
1887
+ const platform = deps.platform ?? process.platform;
1888
+ if (platform === "win32") {
1889
+ const npmEntry = resolveWindowsNpmCodexEntry(deps);
1890
+ if (npmEntry) return npmEntry;
1891
+ const command = resolveCommandOnPath("codex", deps);
1892
+ return command && !isWindowsSandboxRunner(command) ? command : null;
1893
+ }
1834
1894
  const pathCommand = resolveCommandOnPath("codex", deps);
1835
1895
  if (pathCommand) return pathCommand;
1836
- const platform = deps.platform ?? process.platform;
1837
1896
  if (platform === "darwin") {
1838
1897
  return firstExistingPath([CODEX_DESKTOP_BUNDLE_PATH], deps);
1839
1898
  }
1840
- if (platform === "win32") {
1841
- return resolveWindowsSandboxRunner(deps);
1842
- }
1843
1899
  return null;
1844
1900
  }
1845
1901
  function probeCodex(deps = {}) {
@@ -1865,37 +1921,20 @@ function resolveCodexSpawn(commandArgs, deps = {}) {
1865
1921
  if ((deps.platform ?? process.platform) !== "win32") {
1866
1922
  return { command: resolveCodexCommand(deps) ?? "codex", args: commandArgs };
1867
1923
  }
1868
- let codexEntry = null;
1869
- try {
1870
- const globalRoot = execSync("npm root -g", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
1871
- const candidate = path4.join(globalRoot, "@openai", "codex", "bin", "codex.js");
1872
- if (existsSync3(candidate)) codexEntry = candidate;
1873
- } catch {
1874
- }
1875
- if (!codexEntry) {
1876
- try {
1877
- const cmdPath = execSync("where codex", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim().split(/\r?\n/)[0];
1878
- const candidate = path4.join(path4.dirname(cmdPath), "node_modules", "@openai", "codex", "bin", "codex.js");
1879
- if (existsSync3(candidate)) codexEntry = candidate;
1880
- } catch {
1881
- }
1924
+ const codexEntry = resolveWindowsNpmCodexEntry(deps);
1925
+ if (codexEntry) {
1926
+ return {
1927
+ command: process.execPath,
1928
+ args: [codexEntry, ...commandArgs]
1929
+ };
1882
1930
  }
1883
- if (!codexEntry) {
1884
- const sandboxRunner = resolveWindowsSandboxRunner(deps);
1885
- if (sandboxRunner) {
1886
- return {
1887
- command: sandboxRunner,
1888
- args: commandArgs
1889
- };
1890
- }
1891
- throw new Error(
1892
- "Cannot resolve Codex CLI entry point on Windows. Ensure @openai/codex is installed globally via npm (npm i -g @openai/codex)."
1893
- );
1931
+ const command = resolveCommandOnPath("codex", deps);
1932
+ if (command && !isWindowsSandboxRunner(command)) {
1933
+ return { command, args: commandArgs };
1894
1934
  }
1895
- return {
1896
- command: process.execPath,
1897
- args: [codexEntry, ...commandArgs]
1898
- };
1935
+ throw new Error(
1936
+ "Cannot resolve Codex CLI entry point on Windows. Install Codex Desktop or install @openai/codex globally via npm (npm i -g @openai/codex). Ignoring .codex/.sandbox-bin/codex-command-runner because it is a sandbox helper, not the Codex CLI."
1937
+ );
1899
1938
  }
1900
1939
  function joinReasoningText(item) {
1901
1940
  const summary = Array.isArray(item.summary) ? item.summary.filter((entry) => typeof entry === "string") : [];
@@ -2303,9 +2342,9 @@ var CodexDriver = class {
2303
2342
  return detectCodexModels();
2304
2343
  }
2305
2344
  };
2306
- function detectCodexModels(home = os2.homedir()) {
2307
- const cachePath = path4.join(home, ".codex", "models_cache.json");
2308
- const configPath = path4.join(home, ".codex", "config.toml");
2345
+ function detectCodexModels(home = os3.homedir()) {
2346
+ const cachePath = path5.join(home, ".codex", "models_cache.json");
2347
+ const configPath = path5.join(home, ".codex", "config.toml");
2309
2348
  let models = [];
2310
2349
  try {
2311
2350
  const raw = readFileSync2(cachePath, "utf8");
@@ -2335,16 +2374,10 @@ function detectCodexModels(home = os2.homedir()) {
2335
2374
 
2336
2375
  // src/drivers/copilot.ts
2337
2376
  import { spawn as spawn3 } from "child_process";
2338
- import path5 from "path";
2377
+ import path6 from "path";
2339
2378
  import { writeFileSync as writeFileSync3 } from "fs";
2340
2379
  function buildCopilotSpawnEnv(ctx) {
2341
- return {
2342
- ...process.env,
2343
- FORCE_COLOR: "0",
2344
- NO_COLOR: "1",
2345
- ...ctx.config.envVars || {},
2346
- [SLOCK_HOME_ENV]: resolveSlockHome()
2347
- };
2380
+ return prepareCliTransport(ctx, { NO_COLOR: "1" }).spawnEnv;
2348
2381
  }
2349
2382
  var CopilotDriver = class {
2350
2383
  id = "copilot";
@@ -2355,7 +2388,7 @@ var CopilotDriver = class {
2355
2388
  inFlightWake: "spawn_new"
2356
2389
  };
2357
2390
  communication = {
2358
- chat: "mcp_chat_bridge",
2391
+ chat: "slock_cli",
2359
2392
  runtimeControl: "mcp_runtime_actions"
2360
2393
  };
2361
2394
  session = {
@@ -2368,23 +2401,39 @@ var CopilotDriver = class {
2368
2401
  supportsStdinNotification = false;
2369
2402
  mcpToolPrefix = "";
2370
2403
  busyDeliveryMode = "none";
2404
+ usesSlockCliForCommunication = true;
2371
2405
  sessionId = null;
2372
2406
  sessionAnnounced = false;
2373
- spawn(ctx) {
2374
- this.sessionId = ctx.config.sessionId || null;
2375
- this.sessionAnnounced = false;
2407
+ buildRuntimeActionsMcpConfig(ctx) {
2376
2408
  const isTsSource = ctx.chatBridgePath.endsWith(".ts");
2377
- const mcpCommand = isTsSource ? "npx" : "node";
2378
- const mcpArgs = isTsSource ? ["tsx", ctx.chatBridgePath, "--agent-id", ctx.agentId, "--server-url", ctx.config.serverUrl, "--auth-token", ctx.config.authToken || ctx.daemonApiKey] : [ctx.chatBridgePath, "--agent-id", ctx.agentId, "--server-url", ctx.config.serverUrl, "--auth-token", ctx.config.authToken || ctx.daemonApiKey];
2379
- const mcpConfigPath = path5.join(ctx.workingDirectory, ".slock-copilot-mcp.json");
2380
- writeFileSync3(mcpConfigPath, JSON.stringify({
2409
+ const command = isTsSource ? "npx" : "node";
2410
+ const bridgeArgs = isTsSource ? ["tsx", ctx.chatBridgePath] : [ctx.chatBridgePath];
2411
+ return JSON.stringify({
2381
2412
  mcpServers: {
2382
2413
  chat: {
2383
- command: mcpCommand,
2384
- args: mcpArgs
2414
+ command,
2415
+ args: [
2416
+ ...bridgeArgs,
2417
+ "--agent-id",
2418
+ ctx.agentId,
2419
+ "--server-url",
2420
+ ctx.config.serverUrl,
2421
+ "--auth-token",
2422
+ ctx.config.authToken || ctx.daemonApiKey,
2423
+ "--runtime",
2424
+ this.id,
2425
+ ...ctx.launchId ? ["--launch-id", ctx.launchId] : [],
2426
+ "--runtime-actions-only"
2427
+ ]
2385
2428
  }
2386
2429
  }
2387
- }), "utf8");
2430
+ });
2431
+ }
2432
+ spawn(ctx) {
2433
+ this.sessionId = ctx.config.sessionId || null;
2434
+ this.sessionAnnounced = false;
2435
+ const mcpConfigPath = path6.join(ctx.workingDirectory, ".slock-copilot-mcp.json");
2436
+ writeFileSync3(mcpConfigPath, this.buildRuntimeActionsMcpConfig(ctx), "utf8");
2388
2437
  const args = [
2389
2438
  "--output-format",
2390
2439
  "json",
@@ -2485,12 +2534,14 @@ var CopilotDriver = class {
2485
2534
  return null;
2486
2535
  }
2487
2536
  buildSystemPrompt(config, _agentId) {
2488
- return buildMcpSystemPrompt(config, {
2537
+ return buildCliTransportSystemPrompt(config, {
2489
2538
  toolPrefix: "",
2490
2539
  extraCriticalRules: [
2491
- "- Do NOT use shell commands to send or receive messages. The MCP tools handle everything."
2540
+ "- Runtime Profile migration completion is the only exception to CLI-only operation: when a migration notice tells you to acknowledge with `runtime_profile_migration_done`, call the `runtime_profile_migration_done` tool with the exact `migration_key`; do not use `slock` CLI or reply in chat as the acknowledgment."
2541
+ ],
2542
+ postStartupNotes: [
2543
+ "**Copilot runtime note:** Slock launches you as a per-turn process. Complete the current wake using `slock` CLI commands, then stop; the daemon will restart you when new messages arrive."
2492
2544
  ],
2493
- postStartupNotes: [],
2494
2545
  includeStdinNotificationSection: false,
2495
2546
  messageNotificationStyle: "poll"
2496
2547
  });
@@ -2499,16 +2550,10 @@ var CopilotDriver = class {
2499
2550
 
2500
2551
  // src/drivers/cursor.ts
2501
2552
  import { spawn as spawn4, spawnSync } from "child_process";
2502
- import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
2503
- import path6 from "path";
2553
+ import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync5 } from "fs";
2554
+ import path7 from "path";
2504
2555
  function buildCursorSpawnEnv(ctx) {
2505
- return {
2506
- ...process.env,
2507
- FORCE_COLOR: "0",
2508
- NO_COLOR: "1",
2509
- ...ctx.config.envVars || {},
2510
- [SLOCK_HOME_ENV]: resolveSlockHome()
2511
- };
2556
+ return prepareCliTransport(ctx, { NO_COLOR: "1" }).spawnEnv;
2512
2557
  }
2513
2558
  var CursorDriver = class {
2514
2559
  id = "cursor";
@@ -2519,7 +2564,7 @@ var CursorDriver = class {
2519
2564
  inFlightWake: "spawn_new"
2520
2565
  };
2521
2566
  communication = {
2522
- chat: "mcp_chat_bridge",
2567
+ chat: "slock_cli",
2523
2568
  runtimeControl: "mcp_runtime_actions"
2524
2569
  };
2525
2570
  session = {
@@ -2532,23 +2577,39 @@ var CursorDriver = class {
2532
2577
  supportsStdinNotification = false;
2533
2578
  mcpToolPrefix = "mcp__chat__";
2534
2579
  busyDeliveryMode = "none";
2535
- spawn(ctx) {
2536
- const cursorDir = path6.join(ctx.workingDirectory, ".cursor");
2537
- if (!existsSync4(cursorDir)) {
2538
- mkdirSync2(cursorDir, { recursive: true });
2539
- }
2580
+ usesSlockCliForCommunication = true;
2581
+ buildRuntimeActionsMcpConfig(ctx) {
2540
2582
  const isTsSource = ctx.chatBridgePath.endsWith(".ts");
2541
- const mcpCommand = isTsSource ? "npx" : "node";
2542
- const mcpArgs = isTsSource ? ["tsx", ctx.chatBridgePath, "--agent-id", ctx.agentId, "--server-url", ctx.config.serverUrl, "--auth-token", ctx.config.authToken || ctx.daemonApiKey] : [ctx.chatBridgePath, "--agent-id", ctx.agentId, "--server-url", ctx.config.serverUrl, "--auth-token", ctx.config.authToken || ctx.daemonApiKey];
2543
- const mcpConfigPath = path6.join(cursorDir, "mcp.json");
2544
- writeFileSync4(mcpConfigPath, JSON.stringify({
2583
+ const command = isTsSource ? "npx" : "node";
2584
+ const bridgeArgs = isTsSource ? ["tsx", ctx.chatBridgePath] : [ctx.chatBridgePath];
2585
+ return JSON.stringify({
2545
2586
  mcpServers: {
2546
2587
  chat: {
2547
- command: mcpCommand,
2548
- args: mcpArgs
2588
+ command,
2589
+ args: [
2590
+ ...bridgeArgs,
2591
+ "--agent-id",
2592
+ ctx.agentId,
2593
+ "--server-url",
2594
+ ctx.config.serverUrl,
2595
+ "--auth-token",
2596
+ ctx.config.authToken || ctx.daemonApiKey,
2597
+ "--runtime",
2598
+ this.id,
2599
+ ...ctx.launchId ? ["--launch-id", ctx.launchId] : [],
2600
+ "--runtime-actions-only"
2601
+ ]
2549
2602
  }
2550
2603
  }
2551
- }), "utf8");
2604
+ });
2605
+ }
2606
+ spawn(ctx) {
2607
+ const cursorDir = path7.join(ctx.workingDirectory, ".cursor");
2608
+ if (!existsSync5(cursorDir)) {
2609
+ mkdirSync2(cursorDir, { recursive: true });
2610
+ }
2611
+ const mcpConfigPath = path7.join(cursorDir, "mcp.json");
2612
+ writeFileSync4(mcpConfigPath, this.buildRuntimeActionsMcpConfig(ctx), "utf8");
2552
2613
  const args = [
2553
2614
  "--print",
2554
2615
  "--output-format",
@@ -2631,12 +2692,14 @@ var CursorDriver = class {
2631
2692
  return null;
2632
2693
  }
2633
2694
  buildSystemPrompt(config, _agentId) {
2634
- return buildMcpSystemPrompt(config, {
2695
+ return buildCliTransportSystemPrompt(config, {
2635
2696
  toolPrefix: "mcp__chat__",
2636
2697
  extraCriticalRules: [
2637
- "- Do NOT use bash/curl/sqlite to send or receive messages. The MCP tools handle everything."
2698
+ "- Runtime Profile migration completion is the only exception to CLI-only operation: when a migration notice tells you to acknowledge with `runtime_profile_migration_done`, call the `runtime_profile_migration_done` tool with the exact `migration_key`; do not use `slock` CLI or reply in chat as the acknowledgment."
2699
+ ],
2700
+ postStartupNotes: [
2701
+ "**Cursor runtime note:** Slock launches you as a per-turn process. Complete the current wake using `slock` CLI commands, then stop; the daemon will restart you when new messages arrive."
2638
2702
  ],
2639
- postStartupNotes: [],
2640
2703
  includeStdinNotificationSection: false,
2641
2704
  messageNotificationStyle: "poll"
2642
2705
  });
@@ -2685,9 +2748,9 @@ function runCursorModelsCommand() {
2685
2748
  }
2686
2749
 
2687
2750
  // src/drivers/gemini.ts
2688
- import { execFileSync as execFileSync2, spawn as spawn5 } from "child_process";
2689
- import { existsSync as existsSync5, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
2690
- import path7 from "path";
2751
+ import { execFileSync as execFileSync3, spawn as spawn5 } from "child_process";
2752
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
2753
+ import path8 from "path";
2691
2754
  function buildGeminiSpawnEnv(ctx, platform = process.platform) {
2692
2755
  const { spawnEnv } = prepareCliTransport(ctx, { NO_COLOR: "1" }, platform);
2693
2756
  if (!Object.prototype.hasOwnProperty.call(ctx.config.envVars ?? {}, "GEMINI_CLI_TRUST_WORKSPACE")) {
@@ -2723,10 +2786,10 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
2723
2786
  if (platform !== "win32") {
2724
2787
  return { command: resolveCommandOnPath("gemini", deps) ?? "gemini", args: commandArgs };
2725
2788
  }
2726
- const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
2727
- const existsSyncFn = deps.existsSyncFn ?? existsSync5;
2789
+ const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
2790
+ const existsSyncFn = deps.existsSyncFn ?? existsSync6;
2728
2791
  const env = deps.env ?? process.env;
2729
- const winPath = path7.win32;
2792
+ const winPath = path8.win32;
2730
2793
  let geminiEntry = null;
2731
2794
  try {
2732
2795
  const globalRoot = normalizeExecOutput2(execFileSyncFn("npm", ["root", "-g"], {
@@ -2864,9 +2927,9 @@ var GeminiDriver = class {
2864
2927
  });
2865
2928
  }
2866
2929
  writeGeminiSettings(ctx) {
2867
- const geminiDir = path7.join(ctx.workingDirectory, ".gemini");
2930
+ const geminiDir = path8.join(ctx.workingDirectory, ".gemini");
2868
2931
  mkdirSync3(geminiDir, { recursive: true });
2869
- const settingsPath = path7.join(geminiDir, "settings.json");
2932
+ const settingsPath = path8.join(geminiDir, "settings.json");
2870
2933
  writeFileSync5(settingsPath, JSON.stringify(this.buildRuntimeActionsMcpSettings(ctx)), "utf8");
2871
2934
  }
2872
2935
  buildRuntimeActionsMcpSettings(ctx) {
@@ -2899,9 +2962,9 @@ var GeminiDriver = class {
2899
2962
  // src/drivers/kimi.ts
2900
2963
  import { randomUUID } from "crypto";
2901
2964
  import { spawn as spawn6 } from "child_process";
2902
- import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
2903
- import os3 from "os";
2904
- import path8 from "path";
2965
+ import { existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
2966
+ import os4 from "os";
2967
+ import path9 from "path";
2905
2968
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
2906
2969
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
2907
2970
  var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
@@ -2922,7 +2985,7 @@ var KimiDriver = class {
2922
2985
  inFlightWake: "steer"
2923
2986
  };
2924
2987
  communication = {
2925
- chat: "mcp_chat_bridge",
2988
+ chat: "slock_cli",
2926
2989
  runtimeControl: "mcp_runtime_actions"
2927
2990
  };
2928
2991
  session = {
@@ -2935,6 +2998,7 @@ var KimiDriver = class {
2935
2998
  supportsStdinNotification = true;
2936
2999
  mcpToolPrefix = "";
2937
3000
  busyDeliveryMode = "direct";
3001
+ usesSlockCliForCommunication = true;
2938
3002
  sessionId = null;
2939
3003
  sessionAnnounced = false;
2940
3004
  promptRequestId = null;
@@ -2950,7 +3014,8 @@ var KimiDriver = class {
2950
3014
  ctx.config.authToken || ctx.daemonApiKey,
2951
3015
  "--runtime",
2952
3016
  "kimi",
2953
- ...ctx.launchId ? ["--launch-id", ctx.launchId] : []
3017
+ ...ctx.launchId ? ["--launch-id", ctx.launchId] : [],
3018
+ "--runtime-actions-only"
2954
3019
  ];
2955
3020
  }
2956
3021
  spawn(ctx) {
@@ -2961,10 +3026,10 @@ var KimiDriver = class {
2961
3026
  const isTsSource = ctx.chatBridgePath.endsWith(".ts");
2962
3027
  const command = isTsSource ? "npx" : "node";
2963
3028
  const bridgeArgs = this.buildChatBridgeArgs(ctx);
2964
- const systemPromptPath = path8.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
2965
- const agentFilePath = path8.join(ctx.workingDirectory, KIMI_AGENT_FILE);
2966
- const mcpConfigPath = path8.join(ctx.workingDirectory, KIMI_MCP_FILE);
2967
- if (!isResume || !existsSync6(systemPromptPath)) {
3029
+ const systemPromptPath = path9.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
3030
+ const agentFilePath = path9.join(ctx.workingDirectory, KIMI_AGENT_FILE);
3031
+ const mcpConfigPath = path9.join(ctx.workingDirectory, KIMI_MCP_FILE);
3032
+ if (!isResume || !existsSync7(systemPromptPath)) {
2968
3033
  writeFileSync6(systemPromptPath, ctx.prompt, "utf8");
2969
3034
  }
2970
3035
  writeFileSync6(agentFilePath, [
@@ -2995,7 +3060,7 @@ var KimiDriver = class {
2995
3060
  if (ctx.config.model && ctx.config.model !== "default") {
2996
3061
  args.push("--model", ctx.config.model);
2997
3062
  }
2998
- const spawnEnv = { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" };
3063
+ const spawnEnv = prepareCliTransport(ctx, { NO_COLOR: "1" }).spawnEnv;
2999
3064
  const proc = spawn6("kimi", args, {
3000
3065
  cwd: ctx.workingDirectory,
3001
3066
  stdio: ["pipe", "pipe", "pipe"],
@@ -3102,10 +3167,10 @@ var KimiDriver = class {
3102
3167
  });
3103
3168
  }
3104
3169
  buildSystemPrompt(config, _agentId) {
3105
- return buildMcpSystemPrompt(config, {
3170
+ return buildCliTransportSystemPrompt(config, {
3106
3171
  toolPrefix: "",
3107
3172
  extraCriticalRules: [
3108
- "- Do NOT use shell commands to send or receive messages. The MCP tools handle everything."
3173
+ "- Runtime Profile migration completion is the only exception to CLI-only operation: when a migration notice tells you to acknowledge with `runtime_profile_migration_done`, call the `runtime_profile_migration_done` tool with the exact `migration_key`; do not use `slock` CLI or reply in chat as the acknowledgment."
3109
3174
  ],
3110
3175
  postStartupNotes: [],
3111
3176
  includeStdinNotificationSection: true,
@@ -3116,8 +3181,8 @@ var KimiDriver = class {
3116
3181
  return detectKimiModels();
3117
3182
  }
3118
3183
  };
3119
- function detectKimiModels(home = os3.homedir()) {
3120
- const configPath = path8.join(home, ".kimi", "config.toml");
3184
+ function detectKimiModels(home = os4.homedir()) {
3185
+ const configPath = path9.join(home, ".kimi", "config.toml");
3121
3186
  let raw;
3122
3187
  try {
3123
3188
  raw = readFileSync3(configPath, "utf8");
@@ -3145,8 +3210,8 @@ function detectKimiModels(home = os3.homedir()) {
3145
3210
  // src/drivers/opencode.ts
3146
3211
  import { spawn as spawn7, spawnSync as spawnSync2 } from "child_process";
3147
3212
  import { readFileSync as readFileSync4 } from "fs";
3148
- import os4 from "os";
3149
- import path9 from "path";
3213
+ import os5 from "os";
3214
+ import path10 from "path";
3150
3215
  var CHAT_MCP_SERVER_NAME = "chat";
3151
3216
  var CHAT_MCP_TOOL_PREFIX = `${CHAT_MCP_SERVER_NAME}_`;
3152
3217
  var SLOCK_AGENT_NAME = "slock";
@@ -3193,8 +3258,8 @@ function parseUserOpenCodeConfig(ctx) {
3193
3258
  const raw = ctx.config.envVars?.OPENCODE_CONFIG_CONTENT;
3194
3259
  return parseOpenCodeConfigContent(raw);
3195
3260
  }
3196
- function readLocalOpenCodeConfig(home = os4.homedir()) {
3197
- const configPath = path9.join(home, ".config", "opencode", "opencode.json");
3261
+ function readLocalOpenCodeConfig(home = os5.homedir()) {
3262
+ const configPath = path10.join(home, ".config", "opencode", "opencode.json");
3198
3263
  try {
3199
3264
  return parseOpenCodeConfigContent(readFileSync4(configPath, "utf8"));
3200
3265
  } catch {
@@ -3242,7 +3307,7 @@ function mergeOpenCodeConfigs(localConfig, envConfig) {
3242
3307
  }
3243
3308
  };
3244
3309
  }
3245
- function buildOpenCodeConfig(ctx, home = os4.homedir()) {
3310
+ function buildOpenCodeConfig(ctx, home = os5.homedir()) {
3246
3311
  const userConfig = mergeOpenCodeConfigs(readLocalOpenCodeConfig(home), parseUserOpenCodeConfig(ctx));
3247
3312
  const userAgents = recordField(userConfig.agent);
3248
3313
  const userSlockAgent = recordField(userAgents[SLOCK_AGENT_NAME]);
@@ -3267,7 +3332,7 @@ function buildOpenCodeConfig(ctx, home = os4.homedir()) {
3267
3332
  }
3268
3333
  };
3269
3334
  }
3270
- function buildOpenCodeLaunchOptions(ctx, home = os4.homedir()) {
3335
+ function buildOpenCodeLaunchOptions(ctx, home = os5.homedir()) {
3271
3336
  const slock = prepareCliTransport(ctx, { NO_COLOR: "1" });
3272
3337
  const config = buildOpenCodeConfig(ctx, home);
3273
3338
  const env = {
@@ -3363,7 +3428,7 @@ function formatOpenCodeLabelToken(token) {
3363
3428
  if (/^\d/.test(token)) return token;
3364
3429
  return normalized.charAt(0).toUpperCase() + normalized.slice(1);
3365
3430
  }
3366
- function detectOpenCodeModels(home = os4.homedir(), runCommand = runOpenCodeModelsCommand) {
3431
+ function detectOpenCodeModels(home = os5.homedir(), runCommand = runOpenCodeModelsCommand) {
3367
3432
  const commandResult = runCommand(home);
3368
3433
  if (commandResult.error || commandResult.status !== 0) return null;
3369
3434
  return parseOpenCodeModelsOutput(commandResult.stdout);
@@ -3547,7 +3612,7 @@ function getDriver(runtimeId) {
3547
3612
 
3548
3613
  // src/workspaces.ts
3549
3614
  import { readdir, rm, stat } from "fs/promises";
3550
- import path10 from "path";
3615
+ import path11 from "path";
3551
3616
  function isValidWorkspaceDirectoryName(directoryName) {
3552
3617
  return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
3553
3618
  }
@@ -3555,7 +3620,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
3555
3620
  if (!isValidWorkspaceDirectoryName(directoryName)) {
3556
3621
  return null;
3557
3622
  }
3558
- return path10.join(dataDir, directoryName);
3623
+ return path11.join(dataDir, directoryName);
3559
3624
  }
3560
3625
  function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
3561
3626
  return {
@@ -3604,7 +3669,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
3604
3669
  return summary;
3605
3670
  }
3606
3671
  const childSummaries = await Promise.all(
3607
- entries.map((entry) => summarizeWorkspaceEntry(path10.join(dirPath, entry.name), entry))
3672
+ entries.map((entry) => summarizeWorkspaceEntry(path11.join(dirPath, entry.name), entry))
3608
3673
  );
3609
3674
  for (const childSummary of childSummaries) {
3610
3675
  summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
@@ -3623,7 +3688,7 @@ async function scanWorkspaceDirectories(dataDir) {
3623
3688
  if (!entry.isDirectory()) {
3624
3689
  return null;
3625
3690
  }
3626
- const dirPath = path10.join(dataDir, entry.name);
3691
+ const dirPath = path11.join(dataDir, entry.name);
3627
3692
  try {
3628
3693
  const summary = await summarizeWorkspaceDirectory(dirPath);
3629
3694
  return {
@@ -3780,6 +3845,12 @@ function readNonNegativeIntegerEnv(name, fallback) {
3780
3845
  if (!Number.isFinite(parsed) || parsed < 0) return fallback;
3781
3846
  return Math.floor(parsed);
3782
3847
  }
3848
+ function stalledRecoverySigtermTimeoutMs() {
3849
+ return readNonNegativeIntegerEnv(
3850
+ "SLOCK_DAEMON_STALLED_RECOVERY_SIGTERM_TIMEOUT_MS",
3851
+ DEFAULT_STALLED_RECOVERY_SIGTERM_TIMEOUT_MS
3852
+ );
3853
+ }
3783
3854
  function toLocalTime(iso) {
3784
3855
  const d = new Date(iso);
3785
3856
  if (isNaN(d.getTime())) return iso;
@@ -3814,19 +3885,19 @@ function findSessionJsonl(root, predicate) {
3814
3885
  if (depth < 0 || visited >= maxEntries) return null;
3815
3886
  let entries;
3816
3887
  try {
3817
- entries = readdirSync3(dir, { withFileTypes: true }).sort((a, b) => b.name.localeCompare(a.name));
3888
+ entries = readdirSync2(dir, { withFileTypes: true }).sort((a, b) => b.name.localeCompare(a.name));
3818
3889
  } catch {
3819
3890
  return null;
3820
3891
  }
3821
3892
  for (const entry of entries) {
3822
3893
  if (++visited > maxEntries) return null;
3823
3894
  if (!entry.isFile() || !predicate(entry.name)) continue;
3824
- return path11.join(dir, entry.name);
3895
+ return path12.join(dir, entry.name);
3825
3896
  }
3826
3897
  for (const entry of entries) {
3827
3898
  if (++visited > maxEntries) return null;
3828
3899
  if (!entry.isDirectory()) continue;
3829
- const found = visit(path11.join(dir, entry.name), depth - 1);
3900
+ const found = visit(path12.join(dir, entry.name), depth - 1);
3830
3901
  if (found) return found;
3831
3902
  }
3832
3903
  return null;
@@ -3839,9 +3910,9 @@ function safeSessionFilename(value) {
3839
3910
  }
3840
3911
  function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
3841
3912
  try {
3842
- const dir = path11.join(fallbackDir, ".slock", "runtime-sessions");
3913
+ const dir = path12.join(fallbackDir, ".slock", "runtime-sessions");
3843
3914
  mkdirSync4(dir, { recursive: true });
3844
- const filePath = path11.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
3915
+ const filePath = path12.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
3845
3916
  writeFileSync7(filePath, JSON.stringify({
3846
3917
  type: "runtime_session_handoff",
3847
3918
  runtime,
@@ -3860,8 +3931,8 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
3860
3931
  return null;
3861
3932
  }
3862
3933
  }
3863
- function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), fallbackDir) {
3864
- const directPath = path11.isAbsolute(sessionId) ? sessionId : null;
3934
+ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), fallbackDir) {
3935
+ const directPath = path12.isAbsolute(sessionId) ? sessionId : null;
3865
3936
  if (directPath) {
3866
3937
  try {
3867
3938
  if (statSync2(directPath).isFile()) {
@@ -3870,7 +3941,7 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), f
3870
3941
  } catch {
3871
3942
  }
3872
3943
  }
3873
- const resolvedPath = runtime === "claude" ? findSessionJsonl(path11.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`) : runtime === "codex" ? findSessionJsonl(path11.join(homeDir, ".codex", "sessions"), (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId)) : null;
3944
+ 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;
3874
3945
  if (!resolvedPath && fallbackDir) {
3875
3946
  const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
3876
3947
  if (fallback) return fallback;
@@ -4011,6 +4082,7 @@ var TRAJECTORY_COALESCE_MS = 350;
4011
4082
  var ACTIVITY_HEARTBEAT_MS = 6e4;
4012
4083
  var COMPACTION_STALE_MS = 5 * 6e4;
4013
4084
  var RUNTIME_PROGRESS_STALE_MS = 15 * 6e4;
4085
+ var DEFAULT_STALLED_RECOVERY_SIGTERM_TIMEOUT_MS = 1e4;
4014
4086
  var MAX_STDOUT_LINES = 8;
4015
4087
  var MAX_STDOUT_LINE_LENGTH = 240;
4016
4088
  var MAX_STDERR_LINES = 8;
@@ -4759,7 +4831,7 @@ var AgentProcessManager = class _AgentProcessManager {
4759
4831
  this.daemonApiKey = daemonApiKey;
4760
4832
  this.serverUrl = opts.serverUrl;
4761
4833
  this.dataDir = opts.dataDir || resolveSlockHomePath("agents");
4762
- this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os5.homedir();
4834
+ this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os6.homedir();
4763
4835
  this.driverResolver = opts.driverResolver || getDriver;
4764
4836
  this.defaultAgentEnvVarsProvider = opts.defaultAgentEnvVarsProvider || null;
4765
4837
  this.tracer = opts.tracer ?? noopTracer;
@@ -5003,26 +5075,26 @@ var AgentProcessManager = class _AgentProcessManager {
5003
5075
  this.recordDaemonTrace("daemon.agent.spawn.started", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId));
5004
5076
  try {
5005
5077
  const driver = this.driverResolver(config.runtime || "claude");
5006
- const agentDataDir = path11.join(this.dataDir, agentId);
5078
+ const agentDataDir = path12.join(this.dataDir, agentId);
5007
5079
  await mkdir(agentDataDir, { recursive: true });
5008
5080
  const runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
5009
- const memoryMdPath = path11.join(agentDataDir, "MEMORY.md");
5081
+ const memoryMdPath = path12.join(agentDataDir, "MEMORY.md");
5010
5082
  try {
5011
5083
  await access(memoryMdPath);
5012
5084
  } catch {
5013
5085
  const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
5014
5086
  await writeFile(memoryMdPath, initialMemoryMd);
5015
5087
  }
5016
- const notesDir = path11.join(agentDataDir, "notes");
5088
+ const notesDir = path12.join(agentDataDir, "notes");
5017
5089
  await mkdir(notesDir, { recursive: true });
5018
5090
  if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
5019
5091
  const seedFiles = buildOnboardingSeedFiles();
5020
5092
  for (const { relativePath, content } of seedFiles) {
5021
- const fullPath = path11.join(agentDataDir, relativePath);
5093
+ const fullPath = path12.join(agentDataDir, relativePath);
5022
5094
  try {
5023
5095
  await access(fullPath);
5024
5096
  } catch {
5025
- await mkdir(path11.dirname(fullPath), { recursive: true });
5097
+ await mkdir(path12.dirname(fullPath), { recursive: true });
5026
5098
  await writeFile(fullPath, content);
5027
5099
  }
5028
5100
  }
@@ -5168,6 +5240,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
5168
5240
  exitCode: null,
5169
5241
  exitSignal: null,
5170
5242
  expectedTerminationReason: null,
5243
+ stalledRecoverySigtermTimer: null,
5171
5244
  runtimeProfileTurnControl: runtimeConfig.runtimeProfileControl ? runtimeProfileTurnControl(runtimeConfig.runtimeProfileControl.kind, runtimeConfig.runtimeProfileControl.key, "agent_config") : null,
5172
5245
  pendingTrajectory: null,
5173
5246
  gatedSteering: createGatedSteeringState()
@@ -5261,6 +5334,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
5261
5334
  if (ap.activityHeartbeat) {
5262
5335
  clearInterval(ap.activityHeartbeat);
5263
5336
  }
5337
+ this.clearStalledRecoverySigtermWatchdog(ap);
5264
5338
  const finalCode = ap.exitCode ?? code;
5265
5339
  const finalSignal = ap.exitSignal ?? signal;
5266
5340
  const expectedTermination = Boolean(ap.expectedTerminationReason);
@@ -5496,6 +5570,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
5496
5570
  clearInterval(ap.activityHeartbeat);
5497
5571
  }
5498
5572
  this.clearCompactionWatchdog(ap);
5573
+ this.clearStalledRecoverySigtermWatchdog(ap);
5499
5574
  this.agents.delete(agentId);
5500
5575
  this.processExitTraceAttrs.set(ap.process, {
5501
5576
  stop_source: silent ? "daemon_internal" : "explicit_request",
@@ -5701,7 +5776,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
5701
5776
  return true;
5702
5777
  }
5703
5778
  async resetWorkspace(agentId) {
5704
- const agentDataDir = path11.join(this.dataDir, agentId);
5779
+ const agentDataDir = path12.join(this.dataDir, agentId);
5705
5780
  try {
5706
5781
  await rm2(agentDataDir, { recursive: true, force: true });
5707
5782
  logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
@@ -5738,7 +5813,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
5738
5813
  return result;
5739
5814
  }
5740
5815
  buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
5741
- const workspacePath = path11.join(this.dataDir, agentId);
5816
+ const workspacePath = path12.join(this.dataDir, agentId);
5742
5817
  return {
5743
5818
  agentId,
5744
5819
  launchId,
@@ -5986,7 +6061,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
5986
6061
  }
5987
6062
  // Workspace file browsing
5988
6063
  async getFileTree(agentId, dirPath) {
5989
- const agentDir = path11.join(this.dataDir, agentId);
6064
+ const agentDir = path12.join(this.dataDir, agentId);
5990
6065
  try {
5991
6066
  await stat2(agentDir);
5992
6067
  } catch {
@@ -5994,8 +6069,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
5994
6069
  }
5995
6070
  let targetDir = agentDir;
5996
6071
  if (dirPath) {
5997
- const resolved = path11.resolve(agentDir, dirPath);
5998
- if (!resolved.startsWith(agentDir + path11.sep) && resolved !== agentDir) {
6072
+ const resolved = path12.resolve(agentDir, dirPath);
6073
+ if (!resolved.startsWith(agentDir + path12.sep) && resolved !== agentDir) {
5999
6074
  return [];
6000
6075
  }
6001
6076
  targetDir = resolved;
@@ -6003,14 +6078,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6003
6078
  return this.listDirectoryChildren(targetDir, agentDir);
6004
6079
  }
6005
6080
  async readFile(agentId, filePath) {
6006
- const agentDir = path11.join(this.dataDir, agentId);
6007
- const resolved = path11.resolve(agentDir, filePath);
6008
- if (!resolved.startsWith(agentDir + path11.sep) && resolved !== agentDir) {
6081
+ const agentDir = path12.join(this.dataDir, agentId);
6082
+ const resolved = path12.resolve(agentDir, filePath);
6083
+ if (!resolved.startsWith(agentDir + path12.sep) && resolved !== agentDir) {
6009
6084
  throw new Error("Access denied");
6010
6085
  }
6011
6086
  const info = await stat2(resolved);
6012
6087
  if (info.isDirectory()) throw new Error("Cannot read a directory");
6013
- const ext = path11.extname(resolved).toLowerCase();
6088
+ const ext = path12.extname(resolved).toLowerCase();
6014
6089
  if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
6015
6090
  if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
6016
6091
  const content = await readFile(resolved, "utf-8");
@@ -6044,14 +6119,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6044
6119
  async listSkills(agentId, runtimeHint) {
6045
6120
  const agent = this.agents.get(agentId);
6046
6121
  const runtime = runtimeHint || agent?.config.runtime || "claude";
6047
- const home = os5.homedir();
6048
- const workspaceDir = path11.join(this.dataDir, agentId);
6122
+ const home = os6.homedir();
6123
+ const workspaceDir = path12.join(this.dataDir, agentId);
6049
6124
  const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
6050
6125
  const globalResults = await Promise.all(
6051
- paths.global.map((p) => this.scanSkillsDir(path11.join(home, p)))
6126
+ paths.global.map((p) => this.scanSkillsDir(path12.join(home, p)))
6052
6127
  );
6053
6128
  const workspaceResults = await Promise.all(
6054
- paths.workspace.map((p) => this.scanSkillsDir(path11.join(workspaceDir, p)))
6129
+ paths.workspace.map((p) => this.scanSkillsDir(path12.join(workspaceDir, p)))
6055
6130
  );
6056
6131
  const dedup = (skills) => {
6057
6132
  const seen = /* @__PURE__ */ new Set();
@@ -6080,7 +6155,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6080
6155
  const skills = [];
6081
6156
  for (const entry of entries) {
6082
6157
  if (entry.isDirectory() || entry.isSymbolicLink()) {
6083
- const skillMd = path11.join(dir, entry.name, "SKILL.md");
6158
+ const skillMd = path12.join(dir, entry.name, "SKILL.md");
6084
6159
  try {
6085
6160
  const content = await readFile(skillMd, "utf-8");
6086
6161
  const skill = this.parseSkillMd(entry.name, content);
@@ -6091,7 +6166,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6091
6166
  } else if (entry.name.endsWith(".md")) {
6092
6167
  const cmdName = entry.name.replace(/\.md$/, "");
6093
6168
  try {
6094
- const content = await readFile(path11.join(dir, entry.name), "utf-8");
6169
+ const content = await readFile(path12.join(dir, entry.name), "utf-8");
6095
6170
  const skill = this.parseSkillMd(cmdName, content);
6096
6171
  skill.sourcePath = dir;
6097
6172
  skills.push(skill);
@@ -6260,6 +6335,64 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6260
6335
  }
6261
6336
  ap.compactionStartedAt = null;
6262
6337
  }
6338
+ clearStalledRecoverySigtermWatchdog(ap) {
6339
+ if (!ap.stalledRecoverySigtermTimer) return;
6340
+ clearTimeout(ap.stalledRecoverySigtermTimer);
6341
+ ap.stalledRecoverySigtermTimer = null;
6342
+ }
6343
+ mergeProcessExitTraceAttrs(proc, attrs) {
6344
+ this.processExitTraceAttrs.set(proc, {
6345
+ ...this.processExitTraceAttrs.get(proc) ?? {},
6346
+ ...attrs
6347
+ });
6348
+ }
6349
+ startStalledRecoverySigtermWatchdog(agentId, ap, runtimeLabel, queuedMessagesAtSignal, staleForMs) {
6350
+ this.clearStalledRecoverySigtermWatchdog(ap);
6351
+ const timeoutMs = stalledRecoverySigtermTimeoutMs();
6352
+ const processAtSignal = ap.process;
6353
+ ap.stalledRecoverySigtermTimer = setTimeout(() => {
6354
+ ap.stalledRecoverySigtermTimer = null;
6355
+ const current = this.agents.get(agentId);
6356
+ if (!current || current !== ap || current.process !== processAtSignal || current.expectedTerminationReason !== "stalled_recovery") {
6357
+ return;
6358
+ }
6359
+ this.mergeProcessExitTraceAttrs(processAtSignal, {
6360
+ stalled_recovery_sigterm_timeout: true,
6361
+ stalled_recovery_sigterm_timeout_ms: timeoutMs
6362
+ });
6363
+ this.recordDaemonTrace("daemon.agent.stalled_recovery.sigterm_timeout", {
6364
+ agentId,
6365
+ launchId: current.launchId || void 0,
6366
+ runtime: current.config.runtime,
6367
+ model: current.config.model,
6368
+ runtime_label: runtimeLabel,
6369
+ queued_messages_count: current.inbox.length,
6370
+ queued_messages_at_signal: queuedMessagesAtSignal,
6371
+ stale_age_ms_at_signal: staleForMs,
6372
+ timeout_ms: timeoutMs,
6373
+ process_pid_present: typeof processAtSignal.pid === "number",
6374
+ session_id_present: Boolean(current.sessionId),
6375
+ supports_stdin_notification: current.driver.supportsStdinNotification,
6376
+ busy_delivery_mode: current.driver.busyDeliveryMode
6377
+ }, "error");
6378
+ logger.warn(
6379
+ `[Agent ${agentId}] Stalled ${runtimeLabel} runtime did not exit after SIGTERM within ${timeoutMs}ms; force killing`
6380
+ );
6381
+ try {
6382
+ processAtSignal.kill("SIGKILL");
6383
+ } catch (err) {
6384
+ const reason = err instanceof Error ? err.message : String(err);
6385
+ this.recordDaemonTrace("daemon.agent.stalled_recovery.sigkill_failed", {
6386
+ agentId,
6387
+ launchId: current.launchId || void 0,
6388
+ runtime: current.config.runtime,
6389
+ model: current.config.model,
6390
+ reason
6391
+ }, "error");
6392
+ logger.warn(`[Agent ${agentId}] Failed to force kill stalled ${runtimeLabel} process: ${reason}`);
6393
+ }
6394
+ }, timeoutMs);
6395
+ }
6263
6396
  startCompactionWatchdog(agentId, ap) {
6264
6397
  this.clearCompactionWatchdog(ap);
6265
6398
  const startedAt = Date.now();
@@ -6421,7 +6554,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6421
6554
  tryFlushGatedSteering(agentId, ap, reason) {
6422
6555
  if (ap.driver.busyDeliveryMode !== "gated") return false;
6423
6556
  if (!ap.sessionId || ap.inbox.length === 0) return false;
6424
- if (reason === "tool_batch_complete") {
6557
+ if (reason !== "turn_end") {
6425
6558
  if (ap.gatedSteering.toolBoundaryFlushDisabled) return false;
6426
6559
  if (ap.gatedSteering.compacting || ap.gatedSteering.outstandingToolUses > 0) return false;
6427
6560
  }
@@ -6430,7 +6563,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6430
6563
  const nextMessages = ap.inbox.splice(0, ap.inbox.length);
6431
6564
  ap.pendingNotificationCount = 0;
6432
6565
  ap.gatedSteering.lastFlushReason = reason;
6433
- if (reason === "tool_batch_complete") {
6566
+ if (reason !== "turn_end") {
6434
6567
  ap.gatedSteering.inFlightBatch = {
6435
6568
  reason,
6436
6569
  messages: nextMessages
@@ -6448,12 +6581,46 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6448
6581
  if (this.deliverMessagesViaStdin(agentId, ap, nextMessages, reason === "turn_end" ? "idle" : "busy")) {
6449
6582
  return true;
6450
6583
  }
6451
- if (reason === "tool_batch_complete") {
6584
+ if (reason !== "turn_end") {
6452
6585
  ap.gatedSteering.inFlightBatch = null;
6453
6586
  }
6454
6587
  ap.pendingNotificationCount += pendingNotificationCount || pendingMessages;
6455
6588
  return false;
6456
6589
  }
6590
+ flushCompactionBoundaryMessages(agentId, ap) {
6591
+ if (!ap.sessionId || !ap.driver.supportsStdinNotification || ap.inbox.length === 0) return false;
6592
+ if (ap.driver.busyDeliveryMode === "gated") {
6593
+ return this.tryFlushGatedSteering(agentId, ap, "compaction_finished");
6594
+ }
6595
+ if (ap.driver.busyDeliveryMode === "direct") {
6596
+ const pendingMessages = ap.inbox.length;
6597
+ const pendingNotificationCount = ap.pendingNotificationCount;
6598
+ const nextMessages = ap.inbox.splice(0, ap.inbox.length);
6599
+ ap.pendingNotificationCount = 0;
6600
+ if (ap.notificationTimer) {
6601
+ clearTimeout(ap.notificationTimer);
6602
+ ap.notificationTimer = null;
6603
+ }
6604
+ this.recordRuntimeTraceEvent(agentId, ap, "runtime.compaction_boundary.flush", {
6605
+ mode: "direct",
6606
+ messageCount: nextMessages.length
6607
+ });
6608
+ if (this.deliverMessagesViaStdin(agentId, ap, nextMessages, "busy")) {
6609
+ return true;
6610
+ }
6611
+ ap.pendingNotificationCount += pendingNotificationCount || pendingMessages;
6612
+ return false;
6613
+ }
6614
+ if (ap.pendingNotificationCount > 0) {
6615
+ if (ap.notificationTimer) {
6616
+ clearTimeout(ap.notificationTimer);
6617
+ ap.notificationTimer = null;
6618
+ }
6619
+ this.sendStdinNotification(agentId);
6620
+ return true;
6621
+ }
6622
+ return false;
6623
+ }
6457
6624
  clearGatedInFlightBatch(agentId, ap, reason) {
6458
6625
  if (ap.driver.busyDeliveryMode !== "gated" || !ap.gatedSteering.inFlightBatch) return;
6459
6626
  const messageCount = ap.gatedSteering.inFlightBatch.messages.length;
@@ -6538,8 +6705,10 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6538
6705
  expectedTerminationReason: "stalled_recovery",
6539
6706
  queued_messages_count: ap.inbox.length
6540
6707
  });
6708
+ this.startStalledRecoverySigtermWatchdog(agentId, ap, runtimeLabel, ap.inbox.length, staleForMs);
6541
6709
  ap.process.kill("SIGTERM");
6542
6710
  } catch (err) {
6711
+ this.clearStalledRecoverySigtermWatchdog(ap);
6543
6712
  const reason = err instanceof Error ? err.message : String(err);
6544
6713
  logger.warn(`[Agent ${agentId}] Failed to terminate stalled ${runtimeLabel} process: ${reason}`);
6545
6714
  return false;
@@ -6641,6 +6810,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6641
6810
  if (ap) {
6642
6811
  ap.gatedSteering.compacting = false;
6643
6812
  this.setGatedSteeringPhase(agentId, ap, "assistant_continuation", { event: "compaction_finished" });
6813
+ this.flushCompactionBoundaryMessages(agentId, ap);
6644
6814
  ap.isIdle = false;
6645
6815
  }
6646
6816
  break;
@@ -6784,6 +6954,18 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6784
6954
  if (count === 0) return;
6785
6955
  if (ap.isIdle) return;
6786
6956
  if (!ap.sessionId) return;
6957
+ if (ap.gatedSteering.compacting) {
6958
+ this.recordRuntimeTraceEvent(agentId, ap, "runtime.compaction_boundary.delivery_suppressed", {
6959
+ pendingNotificationCount: count,
6960
+ pendingMessages: ap.inbox.length,
6961
+ busyDeliveryMode: ap.driver.busyDeliveryMode
6962
+ });
6963
+ ap.pendingNotificationCount += count;
6964
+ logger.info(
6965
+ `[Agent ${agentId}] Suppressing stdin delivery until context compaction finishes; pending=${ap.inbox.length}`
6966
+ );
6967
+ return;
6968
+ }
6787
6969
  if (ap.driver.busyDeliveryMode === "gated") {
6788
6970
  this.recordGatedSteeringEvent(agentId, ap, "suppress", {
6789
6971
  reason: "timer_notification_not_safe_boundary",
@@ -6934,8 +7116,8 @@ ${RESPONSE_TARGET_HINT}`);
6934
7116
  const nodes = [];
6935
7117
  for (const entry of entries) {
6936
7118
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
6937
- const fullPath = path11.join(dir, entry.name);
6938
- const relativePath = path11.relative(rootDir, fullPath);
7119
+ const fullPath = path12.join(dir, entry.name);
7120
+ const relativePath = path12.relative(rootDir, fullPath);
6939
7121
  let info;
6940
7122
  try {
6941
7123
  info = await stat2(fullPath);
@@ -7239,8 +7421,8 @@ var ReminderCache = class {
7239
7421
  // src/machineLock.ts
7240
7422
  import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
7241
7423
  import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync8 } from "fs";
7242
- import os6 from "os";
7243
- import path12 from "path";
7424
+ import os7 from "os";
7425
+ import path13 from "path";
7244
7426
  var INCOMPLETE_LOCK_STALE_MS = 3e4;
7245
7427
  var DaemonMachineLockConflictError = class extends Error {
7246
7428
  code = "DAEMON_MACHINE_LOCK_HELD";
@@ -7262,7 +7444,7 @@ function resolveDefaultMachineStateRoot() {
7262
7444
  return resolveSlockHomePath("machines");
7263
7445
  }
7264
7446
  function ownerPath(lockDir) {
7265
- return path12.join(lockDir, "owner.json");
7447
+ return path13.join(lockDir, "owner.json");
7266
7448
  }
7267
7449
  function readOwner(lockDir) {
7268
7450
  try {
@@ -7292,8 +7474,8 @@ function acquireDaemonMachineLock(options) {
7292
7474
  const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
7293
7475
  const fingerprint = apiKeyFingerprint(options.apiKey);
7294
7476
  const lockId = getDaemonMachineLockId(options.apiKey);
7295
- const machineDir = path12.join(rootDir, lockId);
7296
- const lockDir = path12.join(machineDir, "daemon.lock");
7477
+ const machineDir = path13.join(rootDir, lockId);
7478
+ const lockDir = path13.join(machineDir, "daemon.lock");
7297
7479
  const token = randomUUID2();
7298
7480
  mkdirSync5(machineDir, { recursive: true });
7299
7481
  for (let attempt = 0; attempt < 2; attempt += 1) {
@@ -7302,7 +7484,7 @@ function acquireDaemonMachineLock(options) {
7302
7484
  const owner = {
7303
7485
  pid: process.pid,
7304
7486
  token,
7305
- hostname: os6.hostname(),
7487
+ hostname: os7.hostname(),
7306
7488
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
7307
7489
  serverUrl: options.serverUrl,
7308
7490
  apiKeyFingerprint: fingerprint.slice(0, 16)
@@ -7345,8 +7527,8 @@ function acquireDaemonMachineLock(options) {
7345
7527
  }
7346
7528
 
7347
7529
  // src/localTraceSink.ts
7348
- import { appendFileSync, mkdirSync as mkdirSync6, readdirSync as readdirSync4, rmSync as rmSync3, statSync as statSync4, writeFileSync as writeFileSync9 } from "fs";
7349
- import path13 from "path";
7530
+ import { appendFileSync, mkdirSync as mkdirSync6, readdirSync as readdirSync3, rmSync as rmSync3, statSync as statSync4, writeFileSync as writeFileSync9 } from "fs";
7531
+ import path14 from "path";
7350
7532
  var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
7351
7533
  var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
7352
7534
  var DEFAULT_MAX_FILES = 8;
@@ -7382,7 +7564,7 @@ var LocalRotatingTraceSink = class {
7382
7564
  currentSize = 0;
7383
7565
  sequence = 0;
7384
7566
  constructor(options) {
7385
- this.traceDir = path13.join(options.machineDir, "traces");
7567
+ this.traceDir = path14.join(options.machineDir, "traces");
7386
7568
  this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
7387
7569
  const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
7388
7570
  const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
@@ -7412,7 +7594,7 @@ var LocalRotatingTraceSink = class {
7412
7594
  const nowMs = this.nowMsProvider();
7413
7595
  const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
7414
7596
  if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
7415
- this.currentFile = path13.join(
7597
+ this.currentFile = path14.join(
7416
7598
  this.traceDir,
7417
7599
  `daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
7418
7600
  );
@@ -7423,11 +7605,11 @@ var LocalRotatingTraceSink = class {
7423
7605
  }
7424
7606
  }
7425
7607
  pruneOldFiles() {
7426
- const files = readdirSync4(this.traceDir).filter((name) => name.startsWith("daemon-trace-") && name.endsWith(".jsonl")).sort();
7608
+ const files = readdirSync3(this.traceDir).filter((name) => name.startsWith("daemon-trace-") && name.endsWith(".jsonl")).sort();
7427
7609
  const excess = files.length - this.maxFiles;
7428
7610
  if (excess <= 0) return;
7429
7611
  for (const file of files.slice(0, excess)) {
7430
- rmSync3(path13.join(this.traceDir, file), { force: true });
7612
+ rmSync3(path14.join(this.traceDir, file), { force: true });
7431
7613
  }
7432
7614
  }
7433
7615
  };
@@ -7514,11 +7696,11 @@ function isDiagnosticErrorAttr(key) {
7514
7696
  import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
7515
7697
  import { gzipSync } from "zlib";
7516
7698
  import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
7517
- import path14 from "path";
7699
+ import path15 from "path";
7518
7700
 
7519
7701
  // src/directUploadCapability.ts
7520
- function joinUrl(base, path16) {
7521
- return `${base.replace(/\/+$/, "")}${path16}`;
7702
+ function joinUrl(base, path17) {
7703
+ return `${base.replace(/\/+$/, "")}${path17}`;
7522
7704
  }
7523
7705
  function jsonHeaders(apiKey) {
7524
7706
  return {
@@ -7737,7 +7919,7 @@ var DaemonTraceBundleUploader = class {
7737
7919
  }, nextMs);
7738
7920
  }
7739
7921
  async findUploadCandidates() {
7740
- const traceDir = path14.join(this.options.machineDir, "traces");
7922
+ const traceDir = path15.join(this.options.machineDir, "traces");
7741
7923
  let names;
7742
7924
  try {
7743
7925
  names = await readdir3(traceDir);
@@ -7749,8 +7931,8 @@ var DaemonTraceBundleUploader = class {
7749
7931
  const currentFile = this.options.currentFileProvider?.();
7750
7932
  const candidates = [];
7751
7933
  for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
7752
- const file = path14.join(traceDir, name);
7753
- if (currentFile && path14.resolve(file) === path14.resolve(currentFile)) continue;
7934
+ const file = path15.join(traceDir, name);
7935
+ if (currentFile && path15.resolve(file) === path15.resolve(currentFile)) continue;
7754
7936
  if (await this.isUploaded(file)) continue;
7755
7937
  try {
7756
7938
  const info = await stat3(file);
@@ -7824,8 +8006,8 @@ var DaemonTraceBundleUploader = class {
7824
8006
  }
7825
8007
  }
7826
8008
  uploadStatePath(file) {
7827
- const stateDir = path14.join(this.options.machineDir, "trace-uploads");
7828
- return path14.join(stateDir, `${path14.basename(file)}.uploaded.json`);
8009
+ const stateDir = path15.join(this.options.machineDir, "trace-uploads");
8010
+ return path15.join(stateDir, `${path15.basename(file)}.uploaded.json`);
7829
8011
  }
7830
8012
  async isUploaded(file) {
7831
8013
  try {
@@ -7837,9 +8019,9 @@ var DaemonTraceBundleUploader = class {
7837
8019
  }
7838
8020
  async markUploaded(file, metadata) {
7839
8021
  const stateFile = this.uploadStatePath(file);
7840
- await mkdir2(path14.dirname(stateFile), { recursive: true, mode: 448 });
8022
+ await mkdir2(path15.dirname(stateFile), { recursive: true, mode: 448 });
7841
8023
  await writeFile2(stateFile, `${JSON.stringify({
7842
- file: path14.basename(file),
8024
+ file: path15.basename(file),
7843
8025
  uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
7844
8026
  ...metadata
7845
8027
  }, null, 2)}
@@ -7878,23 +8060,23 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
7878
8060
  }
7879
8061
  }
7880
8062
  function resolveChatBridgePath(moduleUrl = import.meta.url) {
7881
- const dirname = path15.dirname(fileURLToPath(moduleUrl));
7882
- const jsPath = path15.resolve(dirname, "chat-bridge.js");
8063
+ const dirname = path16.dirname(fileURLToPath(moduleUrl));
8064
+ const jsPath = path16.resolve(dirname, "chat-bridge.js");
7883
8065
  try {
7884
8066
  accessSync(jsPath);
7885
8067
  return jsPath;
7886
8068
  } catch {
7887
- return path15.resolve(dirname, "chat-bridge.ts");
8069
+ return path16.resolve(dirname, "chat-bridge.ts");
7888
8070
  }
7889
8071
  }
7890
8072
  function resolveSlockCliPath(moduleUrl = import.meta.url) {
7891
- const thisDir = path15.dirname(fileURLToPath(moduleUrl));
7892
- const bundledDistPath = path15.resolve(thisDir, "cli", "index.js");
8073
+ const thisDir = path16.dirname(fileURLToPath(moduleUrl));
8074
+ const bundledDistPath = path16.resolve(thisDir, "cli", "index.js");
7893
8075
  try {
7894
8076
  accessSync(bundledDistPath);
7895
8077
  return bundledDistPath;
7896
8078
  } catch {
7897
- const workspaceDistPath = path15.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
8079
+ const workspaceDistPath = path16.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
7898
8080
  accessSync(workspaceDistPath);
7899
8081
  return workspaceDistPath;
7900
8082
  }
@@ -8073,7 +8255,7 @@ var DaemonCore = class {
8073
8255
  }
8074
8256
  resolveMachineStateRoot() {
8075
8257
  if (this.options.machineStateDir) return this.options.machineStateDir;
8076
- if (this.options.dataDir) return path15.join(path15.dirname(this.options.dataDir), "machines");
8258
+ if (this.options.dataDir) return path16.join(path16.dirname(this.options.dataDir), "machines");
8077
8259
  return resolveDefaultMachineStateRoot();
8078
8260
  }
8079
8261
  shouldEnableLocalTrace() {
@@ -8393,8 +8575,8 @@ var DaemonCore = class {
8393
8575
  capabilities: ["agent:start", "agent:stop", "agent:deliver", "workspace:files"],
8394
8576
  runtimes,
8395
8577
  runningAgents: runningAgentIds,
8396
- hostname: this.options.hostname ?? os7.hostname(),
8397
- os: this.options.osDescription ?? `${os7.platform()} ${os7.arch()}`,
8578
+ hostname: this.options.hostname ?? os8.hostname(),
8579
+ os: this.options.osDescription ?? `${os8.platform()} ${os8.arch()}`,
8398
8580
  daemonVersion: this.daemonVersion
8399
8581
  });
8400
8582
  this.recordDaemonTrace("daemon.ready.sent", {