@slock-ai/daemon 0.57.5 → 0.57.7

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,6 +1,6 @@
1
1
  // src/core.ts
2
- import path15 from "path";
3
- import os7 from "os";
2
+ import path16 from "path";
3
+ import os8 from "os";
4
4
  import { createRequire as createRequire2 } from "module";
5
5
  import { accessSync } from "fs";
6
6
  import { fileURLToPath } from "url";
@@ -1504,11 +1504,11 @@ var DISPLAY_PLAN_CONFIG = {
1504
1504
  };
1505
1505
 
1506
1506
  // src/agentProcessManager.ts
1507
- import { mkdirSync as mkdirSync3, readdirSync as readdirSync2, statSync, writeFileSync as writeFileSync4 } from "fs";
1507
+ import { mkdirSync as mkdirSync4, readdirSync as readdirSync2, statSync, writeFileSync as writeFileSync4 } from "fs";
1508
1508
  import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
1509
1509
  import { createHash as createHash3 } from "crypto";
1510
- import path11 from "path";
1511
- import os5 from "os";
1510
+ import path12 from "path";
1511
+ import os6 from "os";
1512
1512
 
1513
1513
  // src/drivers/claude.ts
1514
1514
  import { spawn } from "child_process";
@@ -1842,9 +1842,6 @@ function buildSlockCliGuideSections(options) {
1842
1842
  }
1843
1843
 
1844
1844
  // src/drivers/systemPrompt.ts
1845
- function toolRef(prefix, name) {
1846
- return `${prefix}${name}`;
1847
- }
1848
1845
  function runtimeContextLines(config) {
1849
1846
  const ctx = config.runtimeContext;
1850
1847
  if (!ctx) return [];
@@ -1866,10 +1863,8 @@ function runtimeContextLines(config) {
1866
1863
  if (ctx.workspacePath) lines.push(`- Workspace: ${ctx.workspacePath}`);
1867
1864
  return lines.length > 4 ? lines : [];
1868
1865
  }
1869
- function buildPrompt(config, variant, opts) {
1870
- const isCli = variant === "cli";
1871
- const t = (name) => toolRef(opts.toolPrefix, name);
1872
- const taskClaimCmd = isCli ? "`slock task claim`" : `\`${t("claim_tasks")}\``;
1866
+ function buildPrompt(config, opts) {
1867
+ const taskClaimCmd = "`slock task claim`";
1873
1868
  const cliGuideSections = buildSlockCliGuideSections({
1874
1869
  // The daemon prompt audience is always managed-runner regardless of
1875
1870
  // CLI vs MCP variant — both are daemon-spawned. The self-hosted-runner
@@ -1881,162 +1876,42 @@ function buildPrompt(config, variant, opts) {
1881
1876
  },
1882
1877
  taskClaimCmd
1883
1878
  });
1884
- const sendCmd = isCli ? "`slock message send`" : `\`${t("send_message")}\``;
1885
- const readCmd = isCli ? "`slock message read`" : `\`${t("read_history")}\``;
1886
- const checkCmd = isCli ? "`slock message check`" : `\`${t("check_messages")}\``;
1887
- const inboxCheckCmd = isCli ? "`slock inbox check`" : checkCmd;
1888
- const taskCreateCmd = isCli ? "`slock task create`" : `\`${t("create_tasks")}\``;
1889
- const taskUpdateCmd = isCli ? "`slock task update`" : `\`${t("update_task_status")}\``;
1890
- const serverInfoCmd = isCli ? "`slock server info`" : `\`${t("list_server")}\``;
1891
- const scheduleReminderCmd = isCli ? "`slock reminder schedule`" : `\`${t("schedule_reminder")}\``;
1892
- const listRemindersCmd = isCli ? "`slock reminder list`" : `\`${t("list_reminders")}\``;
1893
- const cancelReminderCmd = isCli ? "`slock reminder cancel`" : `\`${t("cancel_reminder")}\``;
1879
+ const checkCmd = "`slock message check`";
1880
+ const inboxCheckCmd = "`slock inbox check`";
1894
1881
  const messageDeliveryText = opts.includeStdinNotificationSection ? "New messages may be delivered to you automatically while your process stays alive." : "The daemon will automatically restart you when new messages arrive.";
1895
- const criticalRules = isCli ? [
1882
+ const noConcreteMessageStartupStep = opts.includeStdinNotificationSection ? `3. If there is no concrete incoming message to handle but this turn includes a Slock inbox notice: the notice means messages exist that you have not seen \u2014 their bodies are withheld to avoid flooding you, not absent (unobserved is not the same as nonexistent). Whether and when to read them is your judgment, now or later; ${checkCmd} reads them and the notice metadata (who, where, how many) helps you triage. Never derive "no work" from a content-free notice alone \u2014 if you choose not to read, that is a deferral to report honestly, not a conclusion that nothing is pending. If there is neither a concrete message nor an inbox notice, stop and wait. ${messageDeliveryText}` : `3. If there is no concrete incoming message to handle, stop and wait. ${messageDeliveryText}`;
1883
+ const criticalRules = [
1896
1884
  "- Always communicate through `slock` CLI commands. This is your only output channel: text you produce outside a `slock` command is not delivered to anyone.",
1897
1885
  ...opts.extraCriticalRules,
1898
1886
  "- Use only the provided `slock` CLI commands for messaging.",
1899
1887
  "- Do not combine multiple `slock` CLI commands in one shell command. Run one `slock` command per tool call, read its output, then decide the next command.",
1900
1888
  "- Always claim a task via `slock task claim` before starting work on it. If the claim fails, move on to a different task."
1901
- ] : [
1902
- `- Always communicate through ${sendCmd}. This is your only output channel: text you produce outside a messaging tool is not delivered to anyone.`,
1903
- ...opts.extraCriticalRules,
1904
- "- Use only the provided MCP tools for messaging \u2014 they are already available and ready.",
1905
- `- Always claim a task via ${taskClaimCmd} before starting work on it. If the claim fails, move on to a different task.`
1906
1889
  ];
1907
1890
  const runtimeProfileControl = config.runtimeProfileControl?.kind === "daemon_release_notice" ? config.runtimeProfileControl : null;
1908
1891
  const runtimeProfileControlStartupStep = runtimeProfileControl ? [
1909
1892
  "0. If this system prompt contains a **Runtime Profile Control** section, read that notice first. It is informational only; no runtime control action or chat reply is required. Do not read MEMORY.md, check messages, or respond to inbox messages before reading it."
1910
1893
  ] : [];
1911
- const startupSteps = isCli ? [
1894
+ const startupSteps = [
1912
1895
  ...runtimeProfileControlStartupStep,
1913
1896
  "1. If this turn already includes a concrete incoming message, first decide whether that message needs a visible acknowledgment, blocker question, or ownership signal. If it does, send it early with `slock message send` before deep context gathering.",
1914
1897
  "2. Read MEMORY.md (in your cwd) and then only the additional memory/files you need to handle the current turn well.",
1915
- `3. If there is no concrete incoming message to handle, stop and wait. ${messageDeliveryText}`,
1898
+ noConcreteMessageStartupStep,
1916
1899
  "4. When you receive a message, process it and reply with `slock message send`.",
1917
1900
  "5. **Complete ALL your work before stopping.** If a task requires multi-step work (research, code changes, testing), finish everything, report results, then stop. New messages arrive automatically \u2014 you do not need to poll or wait for them."
1918
- ] : [
1919
- ...runtimeProfileControlStartupStep,
1920
- `1. If this turn already includes a concrete incoming message, first decide whether that message needs a visible acknowledgment, blocker question, or ownership signal. If it does, send it early with ${sendCmd} before deep context gathering.`,
1921
- "2. Read MEMORY.md (in your cwd) and then only the additional memory/files you need to handle the current turn well.",
1922
- `3. If there is no concrete incoming message to handle, stop and wait. ${messageDeliveryText}`,
1923
- `4. When you receive a message, process it and reply with ${sendCmd}.`,
1924
- "5. **Complete ALL your work before stopping.** If a task requires multi-step work (research, code changes, testing), finish everything, report results, then stop. New messages arrive automatically \u2014 you do not need to poll or wait for them."
1925
1901
  ];
1926
- const communicationSection = isCli ? cliGuideSections.communication : `## Communication \u2014 MCP tools ONLY
1927
-
1928
- You have MCP tools from the "chat" server. Use ONLY these for communication:
1929
-
1930
- 1. **${checkCmd}** \u2014 Non-blocking check for new messages. Use freely during work \u2014 at natural breakpoints or after notifications.
1931
- 2. **${sendCmd}** \u2014 Send a message to a channel or DM.
1932
- 3. **${serverInfoCmd}** \u2014 List all channels in this server, which ones you have joined, plus all agents and humans.
1933
- 4. **\`${t("join_channel")}\`** \u2014 Join a visible public channel. This only affects your own agent membership.
1934
- 5. **\`${t("leave_channel")}\`** \u2014 Leave a regular channel you have joined. This only affects your own agent membership.
1935
- 6. **${readCmd}** \u2014 Read past messages from a channel, DM, or thread. Supports \`before\` / \`after\` pagination and \`around\` for centered context.
1936
- 7. **\`${t("search_messages")}\`** \u2014 Search messages visible to you, then inspect a hit with ${readCmd}.
1937
- 8. **\`${t("list_tasks")}\`** \u2014 View a channel's task board.
1938
- 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).
1939
- 10. **${taskClaimCmd}** \u2014 Claim tasks by number or message ID (supports batch, handles conflicts).
1940
- 11. **\`${t("unclaim_task")}\`** \u2014 Release your claim on a task.
1941
- 12. **${taskUpdateCmd}** \u2014 Change a task's status (e.g. to in_review or done).
1942
- 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.
1943
- 14. **\`${t("view_file")}\`** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
1944
- 15. **${scheduleReminderCmd}** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
1945
- 16. **${listRemindersCmd}** \u2014 List your reminders.
1946
- 17. **${cancelReminderCmd}** \u2014 Cancel one of your reminders by ID.`;
1947
- const credentialHygieneSection = isCli ? cliGuideSections.credentialHygiene : `### Credential hygiene
1948
-
1949
- **Never paste credentials into public Slock channels, public-channel threads, or public-channel task/attachment fields.** Agent tokens (\`sk_agent_*\`), legacy machine API keys (\`sk_machine_*\`), session bearers, JWTs, \`.env\` files, or \`credential.json\` contents must not appear in public channel chat. DMs and private channels are allowed for authorized secret handoff, but verify the audience first. If you accidentally paste one into a public channel, immediately tell the credential owner so they can rotate it.
1950
-
1951
- If a tool or error output contains credential-shaped strings, redact them to \`sk_agent_<redacted>\` / \`sk_machine_<redacted>\` shape before posting to a public channel.`;
1952
- const reminderSection = isCli ? cliGuideSections.reminders : `### Reminders
1953
-
1954
- 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.
1955
- When a reminder already exists, prefer \`slock reminder snooze\` to push it later, \`slock reminder update\` to change its meaning or schedule, and \`slock reminder cancel\` only when it is truly no longer needed.
1956
- Use ${scheduleReminderCmd} rather than runtime-native wake or cron tools such as ScheduleWakeup or CronCreate for user-visible reminders, so reminders stay author-owned, persistent, observable, snoozable, updatable, and cancelable in Slock.
1957
- Create agent reminders only after resolving the anchor message from the current conversation and passing its msgId explicitly; if no anchor can be resolved, consider posting a status update in the relevant thread so the intent is visible, then revisit when context is available.`;
1958
- const sendingMessagesSection = isCli ? cliGuideSections.sendingMessages : `### Sending messages
1959
-
1960
- - **Reply to a channel**: \`${t("send_message")}(target="#channel-name", content="...")\`
1961
- - **Reply to a DM**: \`${t("send_message")}(target="dm:@peer-name", content="...")\`
1962
- - **Reply in a thread**: \`${t("send_message")}(target="#channel:shortid", content="...")\` or \`${t("send_message")}(target="dm:@peer:shortid", content="...")\`
1963
- - **Start a NEW DM**: \`${t("send_message")}(target="dm:@person-name", content="...")\`
1964
-
1965
- **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.`;
1966
- const threadsSection = isCli ? cliGuideSections.threads : `### Threads
1967
-
1968
- Threads are sub-conversations attached to a specific message. They let you discuss a topic without cluttering the main channel.
1969
-
1970
- - **Thread targets** have a colon and short ID suffix: \`#general:00000000\` (thread in #general) or \`dm:@richard:11111111\` (thread in a DM).
1971
- - When you receive a message from a thread (the target has a \`:shortid\` suffix), **always reply using that same target** to keep the conversation in the thread.
1972
- - **Start a new thread**: Use the \`msg=\` field from the header as the thread suffix. For example, if you see \`[target=#general msg=00000000 ...]\`, call \`${t("send_message")}(target="#general:00000000", content="...")\`. The thread will be auto-created if it doesn't exist yet. Example IDs like \`00000000\` are placeholders; real message IDs come from received messages.
1973
- - When you send a message, the response includes the message ID. You can use it to start a thread on your own message.
1974
- - You can read thread history via ${readCmd} with the same thread target.
1975
- - Threads cannot be nested \u2014 you cannot start a thread inside a thread.`;
1976
- const discoverySection = isCli ? cliGuideSections.discovery : `### Discovering people and channels
1977
-
1978
- Call ${serverInfoCmd} to see all channels in this server, which ones you have joined, other agents, and humans.
1979
- 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")}\`.
1980
- 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.`;
1981
- const channelAwarenessSection = isCli ? cliGuideSections.channelAwareness : `### Channel awareness
1982
-
1983
- Each channel has a **name** and optionally a **description** that define its purpose (visible via ${serverInfoCmd}). Respect them:
1984
- - **Reply in context** \u2014 always respond in the channel/thread the message came from.
1985
- - **Stay on topic** \u2014 when proactively sharing results or updates, post in the channel most relevant to the work. Don't scatter messages across unrelated channels.
1986
- - If unsure where something belongs, call ${serverInfoCmd} to review channel descriptions.`;
1987
- const thirdPartyIntegrationsSection = isCli ? `### Third-party integrations
1988
-
1989
- If a built-in Slock app or registered third-party service requires login, use Slock Agent Login through the CLI instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app or built-in Slock app, first run \`slock integration list\` and match the app to a listed service before browsing the app. Use \`slock integration login --service <service>\` to provision or reuse your agent login for that service. If the service exposes an agent behavior manifest and you need to run its local CLI, run \`slock integration env --service <service>\` before invoking that CLI; if it prints exports, apply them first so service credentials stay under a per-agent profile HOME/XDG tree instead of the host user's global HOME. If it reports that no local env is required, do not invent HOME/XDG overrides. If it fails, do not run that local CLI with the host user's HOME; report that the service manifest is unsupported. Slock does not execute commands from remote manifests automatically. If the CLI reports that the \`integration\` command is unknown, the local daemon/CLI is too old for Slock Agent Login; report that the machine must be upgraded/restarted instead of calling internal HTTP endpoints yourself. When the command returns \`Agent login ready\` or \`Already logged in\`, the agent-side login is ready. If the output includes an app URL, open that URL as the service-provided app surface; it should look like the service's normal Login with Slock callback and not require you to understand Slock's internal grant/request protocol. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow, use internal request IDs as OAuth callback codes, call internal Slock integration endpoints directly, or call third-party exchange endpoints unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use \`slock profile show\`. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the listed service / Slock Agent Login path.` : `### Third-party integrations
1990
-
1991
- If a built-in Slock app or registered third-party service requires login, use Slock Agent Login through the available registered-service interface instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app or built-in Slock app, first inspect the registered-service interface and match the app to a listed service before browsing the app. Once the registered-service interface reports the agent login is ready, the agent-side login is ready. If that interface provides an app URL, use it as the service-provided app surface; it should look like the service's normal Login with Slock callback and not require you to understand Slock's internal grant/request protocol. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow or treat internal request IDs as OAuth callback codes unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use your Slock profile view. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the listed service / Slock Agent Login path.`;
1992
- const readingHistorySection = isCli ? cliGuideSections.readingHistory : `### Reading history
1993
-
1994
- Use ${readCmd} with the \`channel\` parameter set to \`"#channel-name"\`, \`"dm:@peer-name"\`, or a thread target like \`"#channel:shortid"\`.
1995
-
1996
- To jump directly to a specific hit with nearby context, pass \`around\` set to a message ID or seq number.`;
1997
- const historicalReferenceSection = isCli ? cliGuideSections.historicalReferences : `### Historical references
1998
-
1999
- When a user refers to prior Slock discussion and the relevant context is not already available, first use \`${t("search_messages")}\` and ${readCmd} to find the original thread, decision, or owner before answering. If you find it, summarize the original conclusion with the source thread/message; if you cannot find it, say that explicitly.`;
2000
- const tasksSection = isCli ? cliGuideSections.tasks : `### Tasks
2001
-
2002
- When someone sends a message that asks you to do something \u2014 fix a bug, write code, review a PR, deploy, investigate an issue \u2014 that is work. Claim it before you start.
2003
-
2004
- **Decision rule:** if fulfilling a message requires you to take action beyond just replying (running tools, writing code, making changes), claim the message first. If you're only answering a question or having a conversation, no claim needed.
2005
-
2006
- **What you see in messages:**
2007
- - A message already marked as a task: \`@Alice: Fix the login bug [task #3 status=in_progress]\`
2008
- - A regular message (no task suffix): \`@Alice: Can someone look into the login bug?\`
2009
- - A system notification about task changes: \`\u{1F4CB} Alice converted a message to task #3 "Fix the login bug"\`
2010
-
2011
- Only top-level channel / DM messages can become tasks. Messages inside threads are discussion context \u2014 reply there, but keep claims and conversions to top-level messages.
2012
-
2013
- ${readCmd} shows messages in their current state. If a message was later converted to a task, it will show the \`[task #N ...]\` suffix.
2014
-
2015
- **Status flow:** \`todo\` \u2192 \`in_progress\` \u2192 \`in_review\` \u2192 \`done\`
2016
-
2017
- **Assignee** is independent from status \u2014 a task can be claimed or unclaimed at any status except \`done\`.
2018
-
2019
- **Workflow:**
2020
- 1. Receive a message that requires action \u2192 claim it first (by task number if already a task, or by message ID if it's a regular message)
2021
- 2. If the claim fails, someone else is working on it \u2014 move on to another task
2022
- 3. Post updates in the task's thread via ${sendCmd} with \`target="#channel:msgShortId"\`
2023
- 4. When done, set status to \`in_review\` so a human can validate via ${taskUpdateCmd}
2024
- 5. After approval (e.g. "looks good", "merge it"), set status to \`done\`
2025
-
2026
- **What ${taskCreateCmd} really means:**
2027
- - Tasks live in the same chat flow as messages. A task is just a message with task metadata, not a separate source of truth.
2028
- - ${taskCreateCmd} is a convenience helper for a specific sequence: create a brand-new message, then publish that new message as a task-message.
2029
- - ${taskCreateCmd} only creates the task \u2014 to own it, call ${taskClaimCmd} afterward.
2030
- - Typical uses for ${taskCreateCmd} are breaking down a larger task into parallel subtasks, or batch-creating genuinely new work for others to claim.
2031
- - If someone already sent the work item as a message, just claim that existing message/task instead of creating a new one.
2032
- - If the work already exists as a message, reuse it via ${taskClaimCmd} with the message ID.
2033
-
2034
- **Creating new tasks:**
2035
- - The task system exists to prevent duplicate work. If you see an existing task for the work, either claim that task or leave it alone.
2036
- - If a message already shows a \`[task #N ...]\` suffix, claim \`#N\` if it is yours to take; otherwise move on.
2037
- - Before calling ${taskCreateCmd}, first check whether the work already exists on the task board or is already being handled.
2038
- - Reuse existing tasks and threads instead of creating duplicates.
2039
- - Use ${taskCreateCmd} only for genuinely new subtasks or follow-up work that does not already have a canonical task.`;
1902
+ const communicationSection = cliGuideSections.communication;
1903
+ const credentialHygieneSection = cliGuideSections.credentialHygiene;
1904
+ const reminderSection = cliGuideSections.reminders;
1905
+ const sendingMessagesSection = cliGuideSections.sendingMessages;
1906
+ const threadsSection = cliGuideSections.threads;
1907
+ const discoverySection = cliGuideSections.discovery;
1908
+ const channelAwarenessSection = cliGuideSections.channelAwareness;
1909
+ const thirdPartyIntegrationsSection = `### Third-party integrations
1910
+
1911
+ If a built-in Slock app or registered third-party service requires login, use Slock Agent Login through the CLI instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app or built-in Slock app, first run \`slock integration list\` and match the app to a listed service before browsing the app. Use \`slock integration login --service <service>\` to provision or reuse your agent login for that service. If the service exposes an agent behavior manifest and you need to run its local CLI, run \`slock integration env --service <service>\` before invoking that CLI; if it prints exports, apply them first so service credentials stay under a per-agent profile HOME/XDG tree instead of the host user's global HOME. If it reports that no local env is required, do not invent HOME/XDG overrides. If it fails, do not run that local CLI with the host user's HOME; report that the service manifest is unsupported. Slock does not execute commands from remote manifests automatically. If the CLI reports that the \`integration\` command is unknown, the local daemon/CLI is too old for Slock Agent Login; report that the machine must be upgraded/restarted instead of calling internal HTTP endpoints yourself. When the command returns \`Agent login ready\` or \`Already logged in\`, the agent-side login is ready. If the output includes an app URL, open that URL as the service-provided app surface; it should look like the service's normal Login with Slock callback and not require you to understand Slock's internal grant/request protocol. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow, use internal request IDs as OAuth callback codes, call internal Slock integration endpoints directly, or call third-party exchange endpoints unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use \`slock profile show\`. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the listed service / Slock Agent Login path.`;
1912
+ const readingHistorySection = cliGuideSections.readingHistory;
1913
+ const historicalReferenceSection = cliGuideSections.historicalReferences;
1914
+ const tasksSection = cliGuideSections.tasks;
2040
1915
  let prompt = `You are "${config.displayName || config.name}", an AI agent in Slock \u2014 a collaborative platform for human-AI collaboration, serving as a shared message service for humans and agents who may be running on different computers.
2041
1916
 
2042
1917
  ## Who you are
@@ -2150,10 +2025,11 @@ While you are working, the daemon may write a batched, content-free inbox update
2150
2025
 
2151
2026
  How to handle these:
2152
2027
  - Treat the notification as a non-urgent signal that new Slock messages are waiting; it does not include the message content and does not require an immediate interruption.
2153
- - Keep working until a natural breakpoint. If you then choose to inspect pending targets, call ${inboxCheckCmd}; use ${checkCmd} / ${readCmd} when you choose to inspect message content.
2028
+ - A content-free notice means messages exist that you have not seen \u2014 not that there is no content or no action. Whether and when to read them is your judgment, now or later; ${checkCmd} is one cheap command and the notice metadata helps you triage. If you defer, report the deferral honestly; never derive "no work" from a content-free notice alone.
2029
+ - Keep working until a natural breakpoint. If you then choose to inspect pending targets, call ${inboxCheckCmd}; use ${checkCmd} / \`slock message read\` when you choose to inspect message content.
2154
2030
  - If a message you explicitly read is higher priority, pivot to it. If not, continue your current work.`;
2155
2031
  } else {
2156
- const notifyExample = isCli ? `\`[Slock inbox notice:\\nInbox update: N unread messages total; M changed targets\\n#channel pending: K messages ...]\`` : `\`[Slock inbox notice:\\nInbox update: N unread messages total; M changed targets\\n#channel pending: K messages ...]\``;
2032
+ const notifyExample = `\`[Slock inbox notice:\\nInbox update: N unread messages total; M changed targets\\n#channel pending: K messages ...]\``;
2157
2033
  prompt += `
2158
2034
 
2159
2035
  ## Message Notifications
@@ -2164,7 +2040,8 @@ ${notifyExample}
2164
2040
 
2165
2041
  How to handle these:
2166
2042
  - The notice is target-scoped and content-free. Its header may show total unread/pending count, while detail rows list the targets changed by this update; it never includes message bodies.
2167
- - Do not interrupt the current step just because a notice arrived. At a natural breakpoint, call ${inboxCheckCmd} if you choose to inspect pending targets, or use ${checkCmd} / ${readCmd} when you choose to inspect message content.
2043
+ - A content-free notice means messages exist that you have not seen \u2014 not that there is no content or no action. Whether and when to read them is your judgment, now or later; ${checkCmd} is one cheap command and the notice metadata helps you triage. If you defer, report the deferral honestly; never derive "no work" from a content-free notice alone.
2044
+ - Do not interrupt the current step just because a notice arrived. At a natural breakpoint, call ${inboxCheckCmd} if you choose to inspect pending targets, or use ${checkCmd} / \`slock message read\` when you choose to inspect message content.
2168
2045
  - If a message you explicitly read is higher priority, you may pivot to it. If not, continue your current work.`;
2169
2046
  }
2170
2047
  }
@@ -2177,7 +2054,7 @@ ${config.description}. This may evolve.`;
2177
2054
  return prompt;
2178
2055
  }
2179
2056
  function buildCliSystemPrompt(config, opts) {
2180
- return buildPrompt(config, "cli", opts);
2057
+ return buildPrompt(config, opts);
2181
2058
  }
2182
2059
 
2183
2060
  // src/slockHome.ts
@@ -3407,6 +3284,7 @@ function routeFamilyForPath(pathname) {
3407
3284
  if (pathname === "/internal/agent-api/tasks/claim") return "tasks/claim";
3408
3285
  if (pathname === "/internal/agent-api/tasks/update-status") return "tasks/update";
3409
3286
  if (pathname === "/internal/agent-api/tasks" || pathname.startsWith("/internal/agent-api/tasks/")) return "tasks";
3287
+ if (/^\/internal\/agent-api\/attachments\/[^/]+\/comments/.test(pathname)) return "agent-api/attachments/comments";
3410
3288
  if (pathname.startsWith("/internal/agent-api/attachments/")) return "agent-api/attachments";
3411
3289
  if (/^\/internal\/agent-api\/messages\/[^/]+\/resolve$/.test(pathname)) return "agent-api/messages/resolve";
3412
3290
  if (/^\/internal\/agent-api\/messages\/[^/]+\/reactions$/.test(pathname)) return "agent-api/messages/reactions";
@@ -4469,6 +4347,52 @@ function buildClaudeSpawnSpec(claudeCommand, platform = process.platform) {
4469
4347
  };
4470
4348
  }
4471
4349
 
4350
+ // src/drivers/claudeProviderIsolation.ts
4351
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, symlinkSync } from "fs";
4352
+ import os2 from "os";
4353
+ import path5 from "path";
4354
+ function isClaudeCustomProviderConfig(config) {
4355
+ const launchRuntimeFields = runtimeConfigToLaunchFields(config);
4356
+ return launchRuntimeFields.runtime === "claude" && Boolean(launchRuntimeFields.envVars?.ANTHROPIC_BASE_URL) && Boolean(launchRuntimeFields.envVars?.ANTHROPIC_API_KEY);
4357
+ }
4358
+ function getClaudeProviderStatePaths(workingDirectory) {
4359
+ const root = path5.join(workingDirectory, ".slock", "claude-provider");
4360
+ const home = path5.join(root, "home");
4361
+ return {
4362
+ root,
4363
+ home,
4364
+ configDir: path5.join(home, ".claude")
4365
+ };
4366
+ }
4367
+ function linkIfPresent(source, target) {
4368
+ try {
4369
+ if (!existsSync3(source) || existsSync3(target)) return;
4370
+ mkdirSync2(path5.dirname(target), { recursive: true, mode: 448 });
4371
+ symlinkSync(source, target, "dir");
4372
+ } catch {
4373
+ }
4374
+ }
4375
+ function ensureClaudeProviderStatePaths(workingDirectory, hostClaudeHome = path5.join(os2.homedir(), ".claude")) {
4376
+ const paths = getClaudeProviderStatePaths(workingDirectory);
4377
+ mkdirSync2(paths.home, { recursive: true, mode: 448 });
4378
+ mkdirSync2(paths.configDir, { recursive: true, mode: 448 });
4379
+ linkIfPresent(path5.join(hostClaudeHome, "skills"), path5.join(paths.configDir, "skills"));
4380
+ linkIfPresent(path5.join(hostClaudeHome, "commands"), path5.join(paths.configDir, "commands"));
4381
+ return paths;
4382
+ }
4383
+ function buildClaudeProviderIsolationEnv(ctx) {
4384
+ if (!isClaudeCustomProviderConfig(ctx.config)) {
4385
+ return {};
4386
+ }
4387
+ const paths = ensureClaudeProviderStatePaths(ctx.workingDirectory);
4388
+ return {
4389
+ HOME: paths.home,
4390
+ USERPROFILE: paths.home,
4391
+ CLAUDE_CONFIG_DIR: paths.configDir,
4392
+ CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST: "1"
4393
+ };
4394
+ }
4395
+
4472
4396
  // src/drivers/claude.ts
4473
4397
  var ClaudeDriver = class {
4474
4398
  id = "claude";
@@ -4489,8 +4413,6 @@ var ClaudeDriver = class {
4489
4413
  toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
4490
4414
  };
4491
4415
  supportsStdinNotification = true;
4492
- mcpToolPrefix = "";
4493
- usesSlockCliForCommunication = true;
4494
4416
  // Claude Code supports same-turn steering, but raw stdin injection at an
4495
4417
  // arbitrary busy instant can collide with active signed thinking blocks. The
4496
4418
  // daemon therefore gates busy delivery on Claude stream-json boundaries.
@@ -4504,7 +4426,10 @@ var ClaudeDriver = class {
4504
4426
  return buildClaudeArgs(config, opts);
4505
4427
  }
4506
4428
  async spawn(ctx) {
4507
- const { slockDir, tokenFile, spawnEnv } = await prepareCliTransport(ctx);
4429
+ const { slockDir, tokenFile, spawnEnv } = await prepareCliTransport(
4430
+ ctx,
4431
+ buildClaudeProviderIsolationEnv(ctx)
4432
+ );
4508
4433
  const systemPromptPath = writeClaudeSystemPromptFile(ctx.standingPrompt, slockDir);
4509
4434
  const args = this.buildClaudeArgs(ctx.config, {
4510
4435
  standingPromptFilePath: systemPromptPath
@@ -4550,7 +4475,6 @@ var ClaudeDriver = class {
4550
4475
  }
4551
4476
  buildSystemPrompt(config, _agentId) {
4552
4477
  return buildCliTransportSystemPrompt(config, {
4553
- toolPrefix: "",
4554
4478
  extraCriticalRules: [],
4555
4479
  postStartupNotes: [
4556
4480
  "**Claude runtime note:** While you are busy, Slock batches inbox-count notifications instead of injecting message content. Use `slock message check` at natural breakpoints to pull the pending messages before side-effect actions that depend on current context."
@@ -4563,9 +4487,9 @@ var ClaudeDriver = class {
4563
4487
 
4564
4488
  // src/drivers/codex.ts
4565
4489
  import { spawn as spawn2, execFileSync as execFileSync2, execSync } from "child_process";
4566
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
4567
- import os2 from "os";
4568
- import path5 from "path";
4490
+ import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
4491
+ import os3 from "os";
4492
+ import path6 from "path";
4569
4493
 
4570
4494
  // src/runtimeTurnState.ts
4571
4495
  var RuntimeTurnState = class {
@@ -4722,16 +4646,17 @@ function rawResponseItemProgressEvent(message) {
4722
4646
  function nonEmptyString2(value) {
4723
4647
  return typeof value === "string" && value.length > 0 ? value : void 0;
4724
4648
  }
4649
+ function codexMcpToolName(item) {
4650
+ const tool = nonEmptyString2(item.tool) ?? "unknown";
4651
+ const server = nonEmptyString2(item.server);
4652
+ return server ? `mcp_${server}_${tool}` : `mcp_${tool}`;
4653
+ }
4725
4654
  var CodexEventNormalizer = class {
4726
- mcpToolPrefix;
4727
4655
  currentThreadId = null;
4728
4656
  sessionAnnounced = false;
4729
4657
  streamedAgentMessageIds = /* @__PURE__ */ new Set();
4730
4658
  streamedReasoningIds = /* @__PURE__ */ new Set();
4731
4659
  turnState = new RuntimeTurnState();
4732
- constructor(opts) {
4733
- this.mcpToolPrefix = opts.mcpToolPrefix;
4734
- }
4735
4660
  reset(opts = {}) {
4736
4661
  this.currentThreadId = opts.threadId ?? null;
4737
4662
  this.turnState.reset();
@@ -4891,11 +4816,11 @@ var CodexEventNormalizer = class {
4891
4816
  break;
4892
4817
  case "mcpToolCall":
4893
4818
  if (isStarted) {
4894
- const toolName = item.server === "chat" ? `${this.mcpToolPrefix}${item.tool}` : `${this.mcpToolPrefix.replace(/_$/, "")}_${item.server}_${item.tool}`;
4819
+ const toolName = codexMcpToolName(item);
4895
4820
  events.push({ kind: "tool_call", name: toolName, input: item.arguments });
4896
4821
  }
4897
4822
  if (isCompleted) {
4898
- const toolName = item.server === "chat" ? `${this.mcpToolPrefix}${item.tool}` : `${this.mcpToolPrefix.replace(/_$/, "")}_${item.server}_${item.tool}`;
4823
+ const toolName = codexMcpToolName(item);
4899
4824
  events.push({ kind: "tool_output", name: toolName });
4900
4825
  this.turnState.markToolBoundary();
4901
4826
  }
@@ -4951,9 +4876,9 @@ var CodexEventNormalizer = class {
4951
4876
 
4952
4877
  // src/drivers/codex.ts
4953
4878
  function ensureGitRepoForCodex(workingDirectory, deps = {}) {
4954
- const existsSyncFn = deps.existsSyncFn ?? existsSync3;
4879
+ const existsSyncFn = deps.existsSyncFn ?? existsSync4;
4955
4880
  const execSyncFn = deps.execSyncFn ?? execSync;
4956
- const gitDir = path5.join(workingDirectory, ".git");
4881
+ const gitDir = path6.join(workingDirectory, ".git");
4957
4882
  if (existsSyncFn(gitDir)) return;
4958
4883
  execSyncFn("git init", { cwd: workingDirectory, stdio: "pipe" });
4959
4884
  execSyncFn(
@@ -4966,13 +4891,13 @@ function ensureGitRepoForCodex(workingDirectory, deps = {}) {
4966
4891
  }
4967
4892
  var CODEX_DESKTOP_BUNDLE_PATH = "/Applications/Codex.app/Contents/Resources/codex";
4968
4893
  function isWindowsSandboxRunner(commandPath) {
4969
- return path5.basename(commandPath).toLowerCase().startsWith("codex-command-runner");
4894
+ return path6.basename(commandPath).toLowerCase().startsWith("codex-command-runner");
4970
4895
  }
4971
4896
  function resolveWindowsNpmCodexEntry(deps = {}) {
4972
- const existsSyncFn = deps.existsSyncFn ?? existsSync3;
4897
+ const existsSyncFn = deps.existsSyncFn ?? existsSync4;
4973
4898
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
4974
4899
  const env = deps.env ?? process.env;
4975
- const winPath = path5.win32;
4900
+ const winPath = path6.win32;
4976
4901
  try {
4977
4902
  const globalRoot = String(execFileSyncFn("npm", ["root", "-g"], {
4978
4903
  encoding: "utf8",
@@ -4991,10 +4916,10 @@ function resolveWindowsNpmCodexEntry(deps = {}) {
4991
4916
  return null;
4992
4917
  }
4993
4918
  function resolveWindowsCodexDesktopEntry(deps = {}) {
4994
- const existsSyncFn = deps.existsSyncFn ?? existsSync3;
4919
+ const existsSyncFn = deps.existsSyncFn ?? existsSync4;
4995
4920
  const env = deps.env ?? process.env;
4996
4921
  const homeDir = deps.homeDir;
4997
- const winPath = path5.win32;
4922
+ const winPath = path6.win32;
4998
4923
  const candidates = [
4999
4924
  env.LOCALAPPDATA ? winPath.join(env.LOCALAPPDATA, "OpenAI", "Codex", "bin", "codex.exe") : null,
5000
4925
  env.USERPROFILE ? winPath.join(env.USERPROFILE, "AppData", "Local", "OpenAI", "Codex", "bin", "codex.exe") : null,
@@ -5082,8 +5007,6 @@ var CodexDriver = class {
5082
5007
  toLaunchSpec: (modelId) => ({ params: { model: modelId } })
5083
5008
  };
5084
5009
  supportsStdinNotification = true;
5085
- mcpToolPrefix = "mcp_chat_";
5086
- usesSlockCliForCommunication = true;
5087
5010
  busyDeliveryMode = "direct";
5088
5011
  supportsNativeStandingPrompt = true;
5089
5012
  probe() {
@@ -5130,7 +5053,7 @@ var CodexDriver = class {
5130
5053
  initializeRequestId = null;
5131
5054
  pendingThreadRequest = null;
5132
5055
  initialTurnStarted = false;
5133
- normalizer = new CodexEventNormalizer({ mcpToolPrefix: this.mcpToolPrefix });
5056
+ normalizer = new CodexEventNormalizer();
5134
5057
  async spawn(ctx) {
5135
5058
  ensureGitRepoForCodex(ctx.workingDirectory);
5136
5059
  const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" });
@@ -5222,7 +5145,6 @@ var CodexDriver = class {
5222
5145
  }
5223
5146
  buildSystemPrompt(config, _agentId) {
5224
5147
  return buildCliTransportSystemPrompt(config, {
5225
- toolPrefix: "",
5226
5148
  extraCriticalRules: [],
5227
5149
  postStartupNotes: [
5228
5150
  "**IMPORTANT**: Your process stays alive across turns. While you are working, Slock may write batched inbox-count notifications into the current turn; call `slock message check` at natural breakpoints to read the pending messages."
@@ -5266,9 +5188,9 @@ var CodexDriver = class {
5266
5188
  return detectCodexModels();
5267
5189
  }
5268
5190
  };
5269
- function detectCodexModels(home = os2.homedir()) {
5270
- const cachePath = path5.join(home, ".codex", "models_cache.json");
5271
- const configPath = path5.join(home, ".codex", "config.toml");
5191
+ function detectCodexModels(home = os3.homedir()) {
5192
+ const cachePath = path6.join(home, ".codex", "models_cache.json");
5193
+ const configPath = path6.join(home, ".codex", "config.toml");
5272
5194
  let models = [];
5273
5195
  try {
5274
5196
  const raw = readFileSync2(cachePath, "utf8");
@@ -5362,9 +5284,7 @@ var AntigravityDriver = class {
5362
5284
  toLaunchSpec: (_modelId) => ({ args: [] })
5363
5285
  };
5364
5286
  supportsStdinNotification = false;
5365
- mcpToolPrefix = "";
5366
5287
  busyDeliveryMode = "none";
5367
- usesSlockCliForCommunication = true;
5368
5288
  sessionId = null;
5369
5289
  sessionAnnounced = false;
5370
5290
  probe() {
@@ -5412,7 +5332,6 @@ var AntigravityDriver = class {
5412
5332
  }
5413
5333
  buildSystemPrompt(config, _agentId) {
5414
5334
  return buildCliTransportSystemPrompt(config, {
5415
- toolPrefix: "",
5416
5335
  extraCriticalRules: [],
5417
5336
  postStartupNotes: [
5418
5337
  "**Antigravity 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."
@@ -5451,9 +5370,7 @@ var CopilotDriver = class {
5451
5370
  toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
5452
5371
  };
5453
5372
  supportsStdinNotification = false;
5454
- mcpToolPrefix = "";
5455
5373
  busyDeliveryMode = "none";
5456
- usesSlockCliForCommunication = true;
5457
5374
  sessionId = null;
5458
5375
  sessionAnnounced = false;
5459
5376
  async spawn(ctx) {
@@ -5559,7 +5476,6 @@ var CopilotDriver = class {
5559
5476
  }
5560
5477
  buildSystemPrompt(config, _agentId) {
5561
5478
  return buildCliTransportSystemPrompt(config, {
5562
- toolPrefix: "",
5563
5479
  extraCriticalRules: [],
5564
5480
  postStartupNotes: [
5565
5481
  "**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."
@@ -5596,9 +5512,7 @@ var CursorDriver = class {
5596
5512
  toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
5597
5513
  };
5598
5514
  supportsStdinNotification = false;
5599
- mcpToolPrefix = "mcp__chat__";
5600
5515
  busyDeliveryMode = "none";
5601
- usesSlockCliForCommunication = true;
5602
5516
  async spawn(ctx) {
5603
5517
  const args = [
5604
5518
  "--print",
@@ -5684,7 +5598,6 @@ var CursorDriver = class {
5684
5598
  }
5685
5599
  buildSystemPrompt(config, _agentId) {
5686
5600
  return buildCliTransportSystemPrompt(config, {
5687
- toolPrefix: "mcp__chat__",
5688
5601
  extraCriticalRules: [],
5689
5602
  postStartupNotes: [
5690
5603
  "**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."
@@ -5745,8 +5658,8 @@ function runCursorModelsCommand() {
5745
5658
 
5746
5659
  // src/drivers/gemini.ts
5747
5660
  import { execFileSync as execFileSync3, spawn as spawn6 } from "child_process";
5748
- import { existsSync as existsSync4 } from "fs";
5749
- import path6 from "path";
5661
+ import { existsSync as existsSync5 } from "fs";
5662
+ import path7 from "path";
5750
5663
  async function buildGeminiSpawnEnv(ctx, platform = process.platform) {
5751
5664
  const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" }, platform);
5752
5665
  const launchEnvVars = runtimeConfigToLaunchFields(ctx.config).envVars;
@@ -5788,9 +5701,9 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
5788
5701
  return { command: resolveCommandOnPath("gemini", deps) ?? "gemini", args: commandArgs };
5789
5702
  }
5790
5703
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
5791
- const existsSyncFn = deps.existsSyncFn ?? existsSync4;
5704
+ const existsSyncFn = deps.existsSyncFn ?? existsSync5;
5792
5705
  const env = deps.env ?? process.env;
5793
- const winPath = path6.win32;
5706
+ const winPath = path7.win32;
5794
5707
  let geminiEntry = null;
5795
5708
  try {
5796
5709
  const globalRoot = normalizeExecOutput2(execFileSyncFn("npm", ["root", "-g"], {
@@ -5844,9 +5757,7 @@ var GeminiDriver = class {
5844
5757
  toLaunchSpec: (modelId) => modelId && modelId !== "default" ? { args: ["--model", modelId] } : { args: [] }
5845
5758
  };
5846
5759
  supportsStdinNotification = false;
5847
- mcpToolPrefix = "";
5848
5760
  busyDeliveryMode = "none";
5849
- usesSlockCliForCommunication = true;
5850
5761
  sessionId = null;
5851
5762
  sessionAnnounced = false;
5852
5763
  async spawn(ctx) {
@@ -5915,7 +5826,6 @@ var GeminiDriver = class {
5915
5826
  }
5916
5827
  buildSystemPrompt(config, _agentId) {
5917
5828
  return buildCliTransportSystemPrompt(config, {
5918
- toolPrefix: "",
5919
5829
  extraCriticalRules: [],
5920
5830
  postStartupNotes: [
5921
5831
  "**Gemini 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."
@@ -5929,9 +5839,9 @@ var GeminiDriver = class {
5929
5839
  // src/drivers/kimi.ts
5930
5840
  import { randomUUID as randomUUID2 } from "crypto";
5931
5841
  import { spawn as spawn7 } from "child_process";
5932
- import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
5933
- import os3 from "os";
5934
- import path7 from "path";
5842
+ import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
5843
+ import os4 from "os";
5844
+ import path8 from "path";
5935
5845
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
5936
5846
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
5937
5847
  var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
@@ -5969,9 +5879,7 @@ var KimiDriver = class {
5969
5879
  toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
5970
5880
  };
5971
5881
  supportsStdinNotification = true;
5972
- mcpToolPrefix = "";
5973
5882
  busyDeliveryMode = "direct";
5974
- usesSlockCliForCommunication = true;
5975
5883
  sessionId = null;
5976
5884
  sessionAnnounced = false;
5977
5885
  promptRequestId = null;
@@ -5980,9 +5888,9 @@ var KimiDriver = class {
5980
5888
  this.sessionId = ctx.config.sessionId || randomUUID2();
5981
5889
  this.sessionAnnounced = false;
5982
5890
  this.promptRequestId = randomUUID2();
5983
- const systemPromptPath = path7.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
5984
- const agentFilePath = path7.join(ctx.workingDirectory, KIMI_AGENT_FILE);
5985
- if (!isResume || !existsSync5(systemPromptPath)) {
5891
+ const systemPromptPath = path8.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
5892
+ const agentFilePath = path8.join(ctx.workingDirectory, KIMI_AGENT_FILE);
5893
+ if (!isResume || !existsSync6(systemPromptPath)) {
5986
5894
  writeFileSync3(systemPromptPath, ctx.prompt, "utf8");
5987
5895
  }
5988
5896
  writeFileSync3(agentFilePath, [
@@ -6117,7 +6025,6 @@ var KimiDriver = class {
6117
6025
  }
6118
6026
  buildSystemPrompt(config, _agentId) {
6119
6027
  return buildCliTransportSystemPrompt(config, {
6120
- toolPrefix: "",
6121
6028
  extraCriticalRules: [],
6122
6029
  postStartupNotes: [],
6123
6030
  includeStdinNotificationSection: true,
@@ -6128,8 +6035,8 @@ var KimiDriver = class {
6128
6035
  return detectKimiModels();
6129
6036
  }
6130
6037
  };
6131
- function detectKimiModels(home = os3.homedir()) {
6132
- const configPath = path7.join(home, ".kimi", "config.toml");
6038
+ function detectKimiModels(home = os4.homedir()) {
6039
+ const configPath = path8.join(home, ".kimi", "config.toml");
6133
6040
  let raw;
6134
6041
  try {
6135
6042
  raw = readFileSync3(configPath, "utf8");
@@ -6156,11 +6063,9 @@ function detectKimiModels(home = os3.homedir()) {
6156
6063
 
6157
6064
  // src/drivers/opencode.ts
6158
6065
  import { spawn as spawn8, spawnSync as spawnSync2 } from "child_process";
6159
- import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
6160
- import os4 from "os";
6161
- import path8 from "path";
6162
- var CHAT_MCP_SERVER_NAME = "chat";
6163
- var CHAT_MCP_TOOL_PREFIX = `${CHAT_MCP_SERVER_NAME}_`;
6066
+ import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
6067
+ import os5 from "os";
6068
+ import path9 from "path";
6164
6069
  var SLOCK_AGENT_NAME = "slock";
6165
6070
  var NO_MESSAGE_PROMPT = "No new messages are pending. Stop now.";
6166
6071
  var FIRST_MESSAGE_TASK_PREFIX = "First message task (system-triggered):";
@@ -6188,8 +6093,8 @@ function parseUserOpenCodeConfig(ctx) {
6188
6093
  const raw = runtimeConfigToLaunchFields(ctx.config).envVars?.OPENCODE_CONFIG_CONTENT;
6189
6094
  return parseOpenCodeConfigContent(raw);
6190
6095
  }
6191
- function readLocalOpenCodeConfig(home = os4.homedir()) {
6192
- const configPath = path8.join(home, ".config", "opencode", "opencode.json");
6096
+ function readLocalOpenCodeConfig(home = os5.homedir()) {
6097
+ const configPath = path9.join(home, ".config", "opencode", "opencode.json");
6193
6098
  try {
6194
6099
  return parseOpenCodeConfigContent(readFileSync4(configPath, "utf8"));
6195
6100
  } catch {
@@ -6249,7 +6154,7 @@ function mergeOpenCodeConfigs(localConfig, envConfig) {
6249
6154
  }
6250
6155
  };
6251
6156
  }
6252
- function buildOpenCodeConfig(ctx, home = os4.homedir()) {
6157
+ function buildOpenCodeConfig(ctx, home = os5.homedir()) {
6253
6158
  const userConfig = mergeOpenCodeConfigs(readLocalOpenCodeConfig(home), parseUserOpenCodeConfig(ctx));
6254
6159
  const userAgents = recordField(userConfig.agent);
6255
6160
  const userSlockAgent = recordField(userAgents[SLOCK_AGENT_NAME]);
@@ -6267,7 +6172,7 @@ function buildOpenCodeConfig(ctx, home = os4.homedir()) {
6267
6172
  mcp: recordField(userConfig.mcp)
6268
6173
  };
6269
6174
  }
6270
- async function buildOpenCodeLaunchOptions(ctx, home = os4.homedir(), version = null) {
6175
+ async function buildOpenCodeLaunchOptions(ctx, home = os5.homedir(), version = null) {
6271
6176
  const slock = await prepareCliTransport(ctx, { NO_COLOR: "1" });
6272
6177
  const config = buildOpenCodeConfig(ctx, home);
6273
6178
  const env = {
@@ -6366,7 +6271,7 @@ function formatOpenCodeLabelToken(token) {
6366
6271
  if (/^\d/.test(token)) return token;
6367
6272
  return normalized.charAt(0).toUpperCase() + normalized.slice(1);
6368
6273
  }
6369
- function detectOpenCodeModels(home = os4.homedir(), runCommand = runOpenCodeModelsCommand) {
6274
+ function detectOpenCodeModels(home = os5.homedir(), runCommand = runOpenCodeModelsCommand) {
6370
6275
  const commandResult = runCommand(home);
6371
6276
  if (commandResult.error || commandResult.status !== 0) return null;
6372
6277
  return parseOpenCodeModelsOutput(commandResult.stdout);
@@ -6387,11 +6292,11 @@ function runOpenCodeModelsCommand(home, deps = {}) {
6387
6292
  };
6388
6293
  }
6389
6294
  function isWindowsCommandShim(commandPath) {
6390
- const ext = path8.win32.extname(commandPath).toLowerCase();
6295
+ const ext = path9.win32.extname(commandPath).toLowerCase();
6391
6296
  return ext === ".cmd" || ext === ".bat";
6392
6297
  }
6393
6298
  function opencodePackageEntryCandidates(packageRoot) {
6394
- const winPath = path8.win32;
6299
+ const winPath = path9.win32;
6395
6300
  return [
6396
6301
  winPath.join(packageRoot, "bin", "opencode.exe"),
6397
6302
  winPath.join(packageRoot, "bin", "opencode.js"),
@@ -6400,16 +6305,16 @@ function opencodePackageEntryCandidates(packageRoot) {
6400
6305
  ];
6401
6306
  }
6402
6307
  function openCodeSpecForEntry(entry, commandArgs) {
6403
- if (path8.win32.extname(entry).toLowerCase() === ".exe") {
6308
+ if (path9.win32.extname(entry).toLowerCase() === ".exe") {
6404
6309
  return { command: entry, args: commandArgs, shell: false };
6405
6310
  }
6406
6311
  return { command: process.execPath, args: [entry, ...commandArgs], shell: false };
6407
6312
  }
6408
6313
  function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
6409
- const existsSyncFn = deps.existsSyncFn ?? existsSync6;
6314
+ const existsSyncFn = deps.existsSyncFn ?? existsSync7;
6410
6315
  const execFileSyncFn = deps.execFileSyncFn;
6411
6316
  const env = deps.env ?? process.env;
6412
- const winPath = path8.win32;
6317
+ const winPath = path9.win32;
6413
6318
  const candidates = [];
6414
6319
  if (execFileSyncFn) {
6415
6320
  try {
@@ -6437,7 +6342,7 @@ function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
6437
6342
  function extractWindowsShimTargets(commandPath, deps = {}) {
6438
6343
  if (!isWindowsCommandShim(commandPath)) return [];
6439
6344
  const readFileSyncFn = deps.readFileSyncFn ?? readFileSync4;
6440
- const commandDir = path8.win32.dirname(commandPath);
6345
+ const commandDir = path9.win32.dirname(commandPath);
6441
6346
  let raw;
6442
6347
  try {
6443
6348
  raw = String(readFileSyncFn(commandPath, "utf8"));
@@ -6448,7 +6353,7 @@ function extractWindowsShimTargets(commandPath, deps = {}) {
6448
6353
  const dp0Pattern = /%~dp0\\?([^"\r\n]*?opencode\.(?:exe|js|mjs|cjs))/gi;
6449
6354
  for (const match of raw.matchAll(dp0Pattern)) {
6450
6355
  const relative = match[1]?.replace(/^\\+/, "");
6451
- if (relative) candidates.push(path8.win32.normalize(path8.win32.join(commandDir, relative)));
6356
+ if (relative) candidates.push(path9.win32.normalize(path9.win32.join(commandDir, relative)));
6452
6357
  }
6453
6358
  return candidates;
6454
6359
  }
@@ -6462,7 +6367,7 @@ function resolveOpenCodeSpawn(commandArgs, deps = {}) {
6462
6367
  };
6463
6368
  }
6464
6369
  const command = resolveCommandOnPath("opencode", deps);
6465
- if (command && path8.win32.extname(command).toLowerCase() === ".exe") {
6370
+ if (command && path9.win32.extname(command).toLowerCase() === ".exe") {
6466
6371
  return { command, args: commandArgs, shell: false };
6467
6372
  }
6468
6373
  const packageEntry = resolveWindowsOpenCodePackageEntry(command, deps);
@@ -6487,7 +6392,6 @@ function isSystemFirstMessageTask(message) {
6487
6392
  }
6488
6393
  function buildOpenCodeSystemPrompt(config) {
6489
6394
  return buildCliTransportSystemPrompt(config, {
6490
- toolPrefix: CHAT_MCP_TOOL_PREFIX,
6491
6395
  extraCriticalRules: [],
6492
6396
  postStartupNotes: [
6493
6397
  "**OpenCode 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."
@@ -6532,11 +6436,9 @@ var OpenCodeDriver = class {
6532
6436
  }
6533
6437
  };
6534
6438
  supportsStdinNotification = false;
6535
- mcpToolPrefix = CHAT_MCP_TOOL_PREFIX;
6536
6439
  busyDeliveryMode = "none";
6537
6440
  terminateProcessOnTurnEnd = true;
6538
6441
  deferSpawnUntilMessage = true;
6539
- usesSlockCliForCommunication = true;
6540
6442
  shouldDeferWakeMessage(message) {
6541
6443
  return isSystemFirstMessageTask(message);
6542
6444
  }
@@ -6570,7 +6472,7 @@ var OpenCodeDriver = class {
6570
6472
  if (unsupportedMessage) {
6571
6473
  throw new Error(unsupportedMessage);
6572
6474
  }
6573
- const launch = await buildOpenCodeLaunchOptions(ctx, os4.homedir(), version);
6475
+ const launch = await buildOpenCodeLaunchOptions(ctx, os5.homedir(), version);
6574
6476
  const spawnSpec = resolveOpenCodeSpawn(launch.args);
6575
6477
  const proc = spawn8(spawnSpec.command, spawnSpec.args, {
6576
6478
  cwd: ctx.workingDirectory,
@@ -6637,8 +6539,8 @@ var OpenCodeDriver = class {
6637
6539
  // src/drivers/pi.ts
6638
6540
  import { randomUUID as randomUUID3 } from "crypto";
6639
6541
  import { EventEmitter } from "events";
6640
- import { mkdirSync as mkdirSync2, readdirSync } from "fs";
6641
- import path9 from "path";
6542
+ import { mkdirSync as mkdirSync3, readdirSync } from "fs";
6543
+ import path10 from "path";
6642
6544
  import {
6643
6545
  AuthStorage,
6644
6546
  createBashTool,
@@ -6665,7 +6567,7 @@ function createPiSdkEventMappingState(sessionId = null) {
6665
6567
  };
6666
6568
  }
6667
6569
  function buildPiSessionDir(workingDirectory) {
6668
- return path9.join(workingDirectory, PI_SESSION_DIR);
6570
+ return path10.join(workingDirectory, PI_SESSION_DIR);
6669
6571
  }
6670
6572
  async function buildPiSpawnEnv(ctx) {
6671
6573
  return (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
@@ -6687,7 +6589,7 @@ function findPiSessionFile(sessionDir, sessionId) {
6687
6589
  }
6688
6590
  const suffix = `_${sessionId}.jsonl`;
6689
6591
  const match = entries.find((entry) => entry.endsWith(suffix));
6690
- return match ? path9.join(sessionDir, match) : null;
6592
+ return match ? path10.join(sessionDir, match) : null;
6691
6593
  }
6692
6594
  function detectPiModelsFromRegistry(modelRegistry) {
6693
6595
  const models = [];
@@ -6840,11 +6742,11 @@ var PI_IDLE_PROMPT_RETRY_MS = 25;
6840
6742
  var PI_IDLE_PROMPT_MAX_WAIT_MS = 1e3;
6841
6743
  async function createPiAgentSessionForContext(ctx, sessionId) {
6842
6744
  const sessionDir = buildPiSessionDir(ctx.workingDirectory);
6843
- mkdirSync2(sessionDir, { recursive: true });
6745
+ mkdirSync3(sessionDir, { recursive: true });
6844
6746
  const spawnEnv = await buildPiSpawnEnv(ctx);
6845
6747
  const agentDir = spawnEnv.PI_CODING_AGENT_DIR || getAgentDir();
6846
- const authStorage = AuthStorage.create(path9.join(agentDir, "auth.json"));
6847
- const modelRegistry = ModelRegistry.create(authStorage, path9.join(agentDir, "models.json"));
6748
+ const authStorage = AuthStorage.create(path10.join(agentDir, "auth.json"));
6749
+ const modelRegistry = ModelRegistry.create(authStorage, path10.join(agentDir, "models.json"));
6848
6750
  const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
6849
6751
  const model = resolvePiModelFromRegistry(launchRuntimeFields.model, modelRegistry);
6850
6752
  if (launchRuntimeFields.model && launchRuntimeFields.model !== "default" && !model) {
@@ -7087,9 +6989,7 @@ var PiDriver = class {
7087
6989
  toLaunchSpec: (modelId) => ({ params: { model: modelId } })
7088
6990
  };
7089
6991
  supportsStdinNotification = true;
7090
- mcpToolPrefix = "";
7091
6992
  busyDeliveryMode = "direct";
7092
- usesSlockCliForCommunication = true;
7093
6993
  sessionId = null;
7094
6994
  get currentSessionId() {
7095
6995
  return this.sessionId;
@@ -7120,7 +7020,6 @@ var PiDriver = class {
7120
7020
  }
7121
7021
  buildSystemPrompt(config, _agentId) {
7122
7022
  return buildCliTransportSystemPrompt(config, {
7123
- toolPrefix: "",
7124
7023
  extraCriticalRules: [],
7125
7024
  postStartupNotes: [
7126
7025
  "**Pi runtime note:** Slock keeps Pi running as a persistent SDK session. While you are working, Slock may send inbox-count notifications into the current turn; call `slock message check` at natural breakpoints."
@@ -7294,7 +7193,7 @@ function getDriver(runtimeId) {
7294
7193
 
7295
7194
  // src/workspaces.ts
7296
7195
  import { readdir, rm, stat } from "fs/promises";
7297
- import path10 from "path";
7196
+ import path11 from "path";
7298
7197
  function isValidWorkspaceDirectoryName(directoryName) {
7299
7198
  return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
7300
7199
  }
@@ -7302,7 +7201,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
7302
7201
  if (!isValidWorkspaceDirectoryName(directoryName)) {
7303
7202
  return null;
7304
7203
  }
7305
- return path10.join(dataDir, directoryName);
7204
+ return path11.join(dataDir, directoryName);
7306
7205
  }
7307
7206
  function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
7308
7207
  return {
@@ -7351,7 +7250,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
7351
7250
  return summary;
7352
7251
  }
7353
7252
  const childSummaries = await Promise.all(
7354
- entries.map((entry) => summarizeWorkspaceEntry(path10.join(dirPath, entry.name), entry))
7253
+ entries.map((entry) => summarizeWorkspaceEntry(path11.join(dirPath, entry.name), entry))
7355
7254
  );
7356
7255
  for (const childSummary of childSummaries) {
7357
7256
  summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
@@ -7370,7 +7269,7 @@ async function scanWorkspaceDirectories(dataDir) {
7370
7269
  if (!entry.isDirectory()) {
7371
7270
  return null;
7372
7271
  }
7373
- const dirPath = path10.join(dataDir, entry.name);
7272
+ const dirPath = path11.join(dataDir, entry.name);
7374
7273
  try {
7375
7274
  const summary = await summarizeWorkspaceDirectory(dirPath);
7376
7275
  return {
@@ -7921,12 +7820,12 @@ function findSessionJsonl(root, predicate) {
7921
7820
  for (const entry of entries) {
7922
7821
  if (++visited > maxEntries) return null;
7923
7822
  if (!entry.isFile() || !predicate(entry.name)) continue;
7924
- return path11.join(dir, entry.name);
7823
+ return path12.join(dir, entry.name);
7925
7824
  }
7926
7825
  for (const entry of entries) {
7927
7826
  if (++visited > maxEntries) return null;
7928
7827
  if (!entry.isDirectory()) continue;
7929
- const found = visit(path11.join(dir, entry.name), depth - 1);
7828
+ const found = visit(path12.join(dir, entry.name), depth - 1);
7930
7829
  if (found) return found;
7931
7830
  }
7932
7831
  return null;
@@ -7939,9 +7838,9 @@ function safeSessionFilename(value) {
7939
7838
  }
7940
7839
  function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
7941
7840
  try {
7942
- const dir = path11.join(fallbackDir, ".slock", "runtime-sessions");
7943
- mkdirSync3(dir, { recursive: true });
7944
- const filePath = path11.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
7841
+ const dir = path12.join(fallbackDir, ".slock", "runtime-sessions");
7842
+ mkdirSync4(dir, { recursive: true });
7843
+ const filePath = path12.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
7945
7844
  writeFileSync4(filePath, JSON.stringify({
7946
7845
  type: "runtime_session_handoff",
7947
7846
  runtime,
@@ -7960,8 +7859,20 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
7960
7859
  return null;
7961
7860
  }
7962
7861
  }
7963
- function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), fallbackDir) {
7964
- const directPath = path11.isAbsolute(sessionId) ? sessionId : null;
7862
+ function resolveRuntimeHomeDir(config, defaultHomeDir, workspacePath) {
7863
+ if (isClaudeCustomProviderConfig(config)) {
7864
+ return getClaudeProviderStatePaths(workspacePath).home;
7865
+ }
7866
+ return defaultHomeDir;
7867
+ }
7868
+ function ensureRuntimeHomeDir(config, defaultHomeDir, workspacePath) {
7869
+ if (isClaudeCustomProviderConfig(config)) {
7870
+ return ensureClaudeProviderStatePaths(workspacePath).home;
7871
+ }
7872
+ return defaultHomeDir;
7873
+ }
7874
+ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), fallbackDir) {
7875
+ const directPath = path12.isAbsolute(sessionId) ? sessionId : null;
7965
7876
  if (directPath) {
7966
7877
  try {
7967
7878
  if (statSync(directPath).isFile()) {
@@ -7970,7 +7881,7 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), f
7970
7881
  } catch {
7971
7882
  }
7972
7883
  }
7973
- 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;
7884
+ 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;
7974
7885
  if (!resolvedPath && fallbackDir) {
7975
7886
  const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
7976
7887
  if (fallback) return fallback;
@@ -8031,13 +7942,7 @@ function formatThreadContextMessage(message) {
8031
7942
  const senderType = formatVisibleActorType(message.sender_type);
8032
7943
  return `- [msg=${msgId} time=${time}${senderType}] ${formatSenderHandle(message)}: ${message.content}`;
8033
7944
  }
8034
- function mcpToolName(driver, name) {
8035
- return `${driver?.mcpToolPrefix ?? ""}${name}`;
8036
- }
8037
- function communicationCommand(driver, name) {
8038
- if (!driver?.usesSlockCliForCommunication) {
8039
- return mcpToolName(driver, name);
8040
- }
7945
+ function communicationCommand(name) {
8041
7946
  switch (name) {
8042
7947
  case "send_message":
8043
7948
  return "slock message send";
@@ -8051,18 +7956,18 @@ function communicationCommand(driver, name) {
8051
7956
  return `slock ${name.replace(/_/g, " ")}`;
8052
7957
  }
8053
7958
  }
8054
- function dynamicReplyInstruction(driver) {
8055
- return driver?.usesSlockCliForCommunication ? "reply using `slock message send --target <exact target>`" : `reply with ${communicationCommand(driver, "send_message")}`;
7959
+ function dynamicReplyInstruction() {
7960
+ return "reply using `slock message send --target <exact target>`";
8056
7961
  }
8057
- function dynamicClaimInstruction(driver) {
8058
- return driver?.usesSlockCliForCommunication ? "claim the relevant task with `slock task claim`" : `claim the relevant task with ${communicationCommand(driver, "claim_tasks")}`;
7962
+ function dynamicClaimInstruction() {
7963
+ return "claim the relevant task with `slock task claim`";
8059
7964
  }
8060
- function formatIncomingMessage(message, driver) {
7965
+ function formatIncomingMessage(message) {
8061
7966
  const threadJoinPrefix = message.thread_join_context ? [
8062
7967
  `[Slock thread context: you were added to a new thread via @mention.]`,
8063
7968
  `parent: ${message.thread_join_context.parent_target}`,
8064
7969
  `thread: ${message.thread_join_context.thread_target}`,
8065
- `suggested next step: ${driver?.usesSlockCliForCommunication ? `slock message read --channel "${message.thread_join_context.suggested_read_history_target}"` : `${communicationCommand(driver, "read_history")}(channel="${message.thread_join_context.suggested_read_history_target}")`}`,
7970
+ `suggested next step: slock message read --channel "${message.thread_join_context.suggested_read_history_target}"`,
8066
7971
  "",
8067
7972
  "Parent message:",
8068
7973
  formatThreadContextMessage(message.thread_join_context.parent_message),
@@ -9017,7 +8922,7 @@ var AgentProcessManager = class _AgentProcessManager {
9017
8922
  this.daemonApiKey = daemonApiKey;
9018
8923
  this.serverUrl = opts.serverUrl;
9019
8924
  this.dataDir = opts.dataDir || resolveSlockHomePath("agents");
9020
- this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os5.homedir();
8925
+ this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os6.homedir();
9021
8926
  this.driverResolver = opts.driverResolver || getDriver;
9022
8927
  this.defaultAgentEnvVarsProvider = opts.defaultAgentEnvVarsProvider || null;
9023
8928
  this.tracer = opts.tracer ?? noopTracer;
@@ -9919,7 +9824,7 @@ var AgentProcessManager = class _AgentProcessManager {
9919
9824
  );
9920
9825
  wakeMessage = void 0;
9921
9826
  }
9922
- const agentDataDir = path11.join(this.dataDir, agentId);
9827
+ const agentDataDir = path12.join(this.dataDir, agentId);
9923
9828
  await mkdir(agentDataDir, { recursive: true });
9924
9829
  let runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
9925
9830
  const legacyRuntimeProfileControl = runtimeConfig.runtimeProfileControl?.kind === "migration" ? runtimeConfig.runtimeProfileControl : null;
@@ -9933,23 +9838,23 @@ var AgentProcessManager = class _AgentProcessManager {
9933
9838
  );
9934
9839
  runtimeConfig = { ...runtimeConfig, runtimeProfileControl: null };
9935
9840
  }
9936
- const memoryMdPath = path11.join(agentDataDir, "MEMORY.md");
9841
+ const memoryMdPath = path12.join(agentDataDir, "MEMORY.md");
9937
9842
  try {
9938
9843
  await access(memoryMdPath);
9939
9844
  } catch {
9940
9845
  const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
9941
9846
  await writeFile(memoryMdPath, initialMemoryMd);
9942
9847
  }
9943
- const notesDir = path11.join(agentDataDir, "notes");
9848
+ const notesDir = path12.join(agentDataDir, "notes");
9944
9849
  await mkdir(notesDir, { recursive: true });
9945
9850
  if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
9946
9851
  const seedFiles = buildOnboardingSeedFiles();
9947
9852
  for (const { relativePath, content } of seedFiles) {
9948
- const fullPath = path11.join(agentDataDir, relativePath);
9853
+ const fullPath = path12.join(agentDataDir, relativePath);
9949
9854
  try {
9950
9855
  await access(fullPath);
9951
9856
  } catch {
9952
- await mkdir(path11.dirname(fullPath), { recursive: true });
9857
+ await mkdir(path12.dirname(fullPath), { recursive: true });
9953
9858
  await writeFile(fullPath, content);
9954
9859
  }
9955
9860
  }
@@ -9973,7 +9878,7 @@ var AgentProcessManager = class _AgentProcessManager {
9973
9878
  if (transientWakeMessage) {
9974
9879
  prompt = `System notice received:
9975
9880
 
9976
- ${formatIncomingMessage(wakeMessage, driver)}
9881
+ ${formatIncomingMessage(wakeMessage)}
9977
9882
 
9978
9883
  Respond as appropriate. Complete all your work before stopping.
9979
9884
  ${RESPONSE_TARGET_HINT}`;
@@ -10007,7 +9912,7 @@ Use the inbox/read commands at a natural breakpoint if you choose to inspect tho
10007
9912
  }
10008
9913
  prompt += `
10009
9914
 
10010
- Use ${communicationCommand(driver, "read_history")} to catch up on the channels listed above, then stop. Read each listed channel at most once unless a read fails. Do NOT call ${communicationCommand(driver, "check_messages")} in this mode. If the history reveals a direct request, assignment, @mention, review request, or task clearly addressed to you, switch into active handling instead of stopping: ${dynamicReplyInstruction(driver)} and ${dynamicClaimInstruction(driver)} before starting work. Otherwise, do NOT send any message in this mode. ${getMessageDeliveryText(driver)}`;
9915
+ Use ${communicationCommand("read_history")} to catch up on the channels listed above, then stop. Read each listed channel at most once unless a read fails. Do NOT call ${communicationCommand("check_messages")} in this mode. If the history reveals a direct request, assignment, @mention, review request, or task clearly addressed to you, switch into active handling instead of stopping: ${dynamicReplyInstruction()} and ${dynamicClaimInstruction()} before starting work. Otherwise, do NOT send any message in this mode. ${getMessageDeliveryText(driver)}`;
10011
9916
  promptSource = "resume_unread_summary";
10012
9917
  } else if (isResume) {
10013
9918
  prompt = `No new messages while you were away. Nothing to do \u2014 just stop. ${getMessageDeliveryText(driver)}`;
@@ -11085,7 +10990,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11085
10990
  return true;
11086
10991
  }
11087
10992
  async resetWorkspace(agentId) {
11088
- const agentDataDir = path11.join(this.dataDir, agentId);
10993
+ const agentDataDir = path12.join(this.dataDir, agentId);
11089
10994
  try {
11090
10995
  await rm2(agentDataDir, { recursive: true, force: true });
11091
10996
  logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
@@ -11146,7 +11051,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11146
11051
  return result;
11147
11052
  }
11148
11053
  buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
11149
- const workspacePath = path11.join(this.dataDir, agentId);
11054
+ const workspacePath = path12.join(this.dataDir, agentId);
11055
+ const runtimeHomeDir = resolveRuntimeHomeDir(config, this.runtimeSessionHomeDir, workspacePath);
11150
11056
  return {
11151
11057
  agentId,
11152
11058
  launchId,
@@ -11160,7 +11066,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11160
11066
  path: workspacePath,
11161
11067
  reachable: true
11162
11068
  },
11163
- sessionRef: sessionId ? resolveRuntimeSessionRef(config.runtime, sessionId, this.runtimeSessionHomeDir, workspacePath) : null
11069
+ sessionRef: sessionId ? resolveRuntimeSessionRef(config.runtime, sessionId, runtimeHomeDir, workspacePath) : null
11164
11070
  }
11165
11071
  };
11166
11072
  }
@@ -11439,7 +11345,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11439
11345
  }
11440
11346
  // Workspace file browsing
11441
11347
  async getFileTree(agentId, dirPath) {
11442
- const agentDir = path11.join(this.dataDir, agentId);
11348
+ const agentDir = path12.join(this.dataDir, agentId);
11443
11349
  try {
11444
11350
  await stat2(agentDir);
11445
11351
  } catch {
@@ -11447,8 +11353,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11447
11353
  }
11448
11354
  let targetDir = agentDir;
11449
11355
  if (dirPath) {
11450
- const resolved = path11.resolve(agentDir, dirPath);
11451
- if (!resolved.startsWith(agentDir + path11.sep) && resolved !== agentDir) {
11356
+ const resolved = path12.resolve(agentDir, dirPath);
11357
+ if (!resolved.startsWith(agentDir + path12.sep) && resolved !== agentDir) {
11452
11358
  return [];
11453
11359
  }
11454
11360
  targetDir = resolved;
@@ -11456,14 +11362,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11456
11362
  return this.listDirectoryChildren(targetDir, agentDir);
11457
11363
  }
11458
11364
  async readFile(agentId, filePath) {
11459
- const agentDir = path11.join(this.dataDir, agentId);
11460
- const resolved = path11.resolve(agentDir, filePath);
11461
- if (!resolved.startsWith(agentDir + path11.sep) && resolved !== agentDir) {
11365
+ const agentDir = path12.join(this.dataDir, agentId);
11366
+ const resolved = path12.resolve(agentDir, filePath);
11367
+ if (!resolved.startsWith(agentDir + path12.sep) && resolved !== agentDir) {
11462
11368
  throw new Error("Access denied");
11463
11369
  }
11464
11370
  const info = await stat2(resolved);
11465
11371
  if (info.isDirectory()) throw new Error("Cannot read a directory");
11466
- const ext = path11.extname(resolved).toLowerCase();
11372
+ const ext = path12.extname(resolved).toLowerCase();
11467
11373
  if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
11468
11374
  if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
11469
11375
  const content = await readFile(resolved, "utf-8");
@@ -11496,15 +11402,17 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11496
11402
  };
11497
11403
  async listSkills(agentId, runtimeHint) {
11498
11404
  const agent = this.agents.get(agentId);
11499
- const runtime = runtimeHint || agent?.config.runtime || "claude";
11500
- const home = os5.homedir();
11501
- const workspaceDir = path11.join(this.dataDir, agentId);
11405
+ const idle = this.idleAgentConfigs.get(agentId);
11406
+ const config = agent?.config ?? idle?.config ?? null;
11407
+ const runtime = runtimeHint || config?.runtime || "claude";
11408
+ const workspaceDir = path12.join(this.dataDir, agentId);
11409
+ const home = config ? ensureRuntimeHomeDir(config, os6.homedir(), workspaceDir) : os6.homedir();
11502
11410
  const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
11503
11411
  const globalResults = await Promise.all(
11504
- paths.global.map((p) => this.scanSkillsDir(path11.join(home, p)))
11412
+ paths.global.map((p) => this.scanSkillsDir(path12.join(home, p)))
11505
11413
  );
11506
11414
  const workspaceResults = await Promise.all(
11507
- paths.workspace.map((p) => this.scanSkillsDir(path11.join(workspaceDir, p)))
11415
+ paths.workspace.map((p) => this.scanSkillsDir(path12.join(workspaceDir, p)))
11508
11416
  );
11509
11417
  const dedup = (skills) => {
11510
11418
  const seen = /* @__PURE__ */ new Set();
@@ -11533,7 +11441,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11533
11441
  const skills = [];
11534
11442
  for (const entry of entries) {
11535
11443
  if (entry.isDirectory() || entry.isSymbolicLink()) {
11536
- const skillMd = path11.join(dir, entry.name, "SKILL.md");
11444
+ const skillMd = path12.join(dir, entry.name, "SKILL.md");
11537
11445
  try {
11538
11446
  const content = await readFile(skillMd, "utf-8");
11539
11447
  const skill = this.parseSkillMd(entry.name, content);
@@ -11544,7 +11452,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11544
11452
  } else if (entry.name.endsWith(".md")) {
11545
11453
  const cmdName = entry.name.replace(/\.md$/, "");
11546
11454
  try {
11547
- const content = await readFile(path11.join(dir, entry.name), "utf-8");
11455
+ const content = await readFile(path12.join(dir, entry.name), "utf-8");
11548
11456
  const skill = this.parseSkillMd(cmdName, content);
11549
11457
  skill.sourcePath = dir;
11550
11458
  skills.push(skill);
@@ -12948,12 +12856,12 @@ ${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
12948
12856
  const traceSource = options.transient ? `stdin_${mode}_transient_delivery` : `stdin_${mode}_delivery`;
12949
12857
  const prompt = formatRuntimeProfileControlPrompt(messages) ?? (messages.length === 1 ? `New message received:
12950
12858
 
12951
- ${formatIncomingMessage(messages[0], ap.driver)}
12859
+ ${formatIncomingMessage(messages[0])}
12952
12860
 
12953
12861
  Respond as appropriate. Complete all your work before stopping.
12954
12862
  ${RESPONSE_TARGET_HINT}` : `New messages received:
12955
12863
 
12956
- ${messages.map((message) => formatIncomingMessage(message, ap.driver)).join("\n")}
12864
+ ${messages.map((message) => formatIncomingMessage(message)).join("\n")}
12957
12865
 
12958
12866
  Respond as appropriate. Complete all your work before stopping.
12959
12867
  ${RESPONSE_TARGET_HINT}`);
@@ -13024,8 +12932,8 @@ ${RESPONSE_TARGET_HINT}`);
13024
12932
  const nodes = [];
13025
12933
  for (const entry of entries) {
13026
12934
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
13027
- const fullPath = path11.join(dir, entry.name);
13028
- const relativePath = path11.relative(rootDir, fullPath);
12935
+ const fullPath = path12.join(dir, entry.name);
12936
+ const relativePath = path12.relative(rootDir, fullPath);
13029
12937
  let info;
13030
12938
  try {
13031
12939
  info = await stat2(fullPath);
@@ -13399,9 +13307,9 @@ var ReminderCache = class {
13399
13307
 
13400
13308
  // src/machineLock.ts
13401
13309
  import { createHash as createHash4, randomUUID as randomUUID4 } from "crypto";
13402
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync5 } from "fs";
13403
- import os6 from "os";
13404
- import path12 from "path";
13310
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync5 } from "fs";
13311
+ import os7 from "os";
13312
+ import path13 from "path";
13405
13313
  var INCOMPLETE_LOCK_STALE_MS = 3e4;
13406
13314
  var DaemonMachineLockConflictError = class extends Error {
13407
13315
  code = "DAEMON_MACHINE_LOCK_HELD";
@@ -13423,7 +13331,7 @@ function resolveDefaultMachineStateRoot() {
13423
13331
  return resolveSlockHomePath("machines");
13424
13332
  }
13425
13333
  function ownerPath(lockDir) {
13426
- return path12.join(lockDir, "owner.json");
13334
+ return path13.join(lockDir, "owner.json");
13427
13335
  }
13428
13336
  function readOwner(lockDir) {
13429
13337
  try {
@@ -13453,17 +13361,17 @@ function acquireDaemonMachineLock(options) {
13453
13361
  const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
13454
13362
  const fingerprint = apiKeyFingerprint(options.apiKey);
13455
13363
  const lockId = getDaemonMachineLockId(options.apiKey);
13456
- const machineDir = path12.join(rootDir, lockId);
13457
- const lockDir = path12.join(machineDir, "daemon.lock");
13364
+ const machineDir = path13.join(rootDir, lockId);
13365
+ const lockDir = path13.join(machineDir, "daemon.lock");
13458
13366
  const token = randomUUID4();
13459
- mkdirSync4(machineDir, { recursive: true });
13367
+ mkdirSync5(machineDir, { recursive: true });
13460
13368
  for (let attempt = 0; attempt < 2; attempt += 1) {
13461
13369
  try {
13462
- mkdirSync4(lockDir);
13370
+ mkdirSync5(lockDir);
13463
13371
  const owner = {
13464
13372
  pid: process.pid,
13465
13373
  token,
13466
- hostname: os6.hostname(),
13374
+ hostname: os7.hostname(),
13467
13375
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
13468
13376
  serverUrl: options.serverUrl,
13469
13377
  apiKeyFingerprint: fingerprint.slice(0, 16)
@@ -13514,8 +13422,8 @@ function acquireDaemonMachineLock(options) {
13514
13422
  }
13515
13423
 
13516
13424
  // src/localTraceSink.ts
13517
- import { appendFileSync, mkdirSync as mkdirSync5, readdirSync as readdirSync3, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
13518
- import path13 from "path";
13425
+ import { appendFileSync, mkdirSync as mkdirSync6, readdirSync as readdirSync3, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
13426
+ import path14 from "path";
13519
13427
  var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
13520
13428
  var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
13521
13429
  var DEFAULT_MAX_FILES = 8;
@@ -13552,7 +13460,7 @@ var LocalRotatingTraceSink = class {
13552
13460
  currentSize = 0;
13553
13461
  sequence = 0;
13554
13462
  constructor(options) {
13555
- this.traceDir = path13.join(options.machineDir, "traces");
13463
+ this.traceDir = path14.join(options.machineDir, "traces");
13556
13464
  this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
13557
13465
  const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
13558
13466
  const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
@@ -13578,11 +13486,11 @@ var LocalRotatingTraceSink = class {
13578
13486
  return this.currentFile;
13579
13487
  }
13580
13488
  ensureFile(nextBytes) {
13581
- mkdirSync5(this.traceDir, { recursive: true, mode: 448 });
13489
+ mkdirSync6(this.traceDir, { recursive: true, mode: 448 });
13582
13490
  const nowMs = this.nowMsProvider();
13583
13491
  const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
13584
13492
  if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
13585
- this.currentFile = path13.join(
13493
+ this.currentFile = path14.join(
13586
13494
  this.traceDir,
13587
13495
  `daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
13588
13496
  );
@@ -13597,7 +13505,7 @@ var LocalRotatingTraceSink = class {
13597
13505
  const excess = files.length - this.maxFiles;
13598
13506
  if (excess <= 0) return;
13599
13507
  for (const file of files.slice(0, excess)) {
13600
- rmSync4(path13.join(this.traceDir, file), { force: true });
13508
+ rmSync4(path14.join(this.traceDir, file), { force: true });
13601
13509
  }
13602
13510
  }
13603
13511
  };
@@ -13688,7 +13596,7 @@ function isDiagnosticErrorAttr(key) {
13688
13596
  import { createHash as createHash6, randomUUID as randomUUID5 } from "crypto";
13689
13597
  import { gzipSync } from "zlib";
13690
13598
  import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
13691
- import path14 from "path";
13599
+ import path15 from "path";
13692
13600
 
13693
13601
  // src/chatBridgeRequest.ts
13694
13602
  var DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS = Number.parseInt(
@@ -13788,8 +13696,8 @@ async function executeResponseRequest(url, init, {
13788
13696
  }
13789
13697
 
13790
13698
  // src/directUploadCapability.ts
13791
- function joinUrl(base, path16) {
13792
- return `${base.replace(/\/+$/, "")}${path16}`;
13699
+ function joinUrl(base, path17) {
13700
+ return `${base.replace(/\/+$/, "")}${path17}`;
13793
13701
  }
13794
13702
  function jsonHeaders(apiKey) {
13795
13703
  return {
@@ -14008,7 +13916,7 @@ var DaemonTraceBundleUploader = class {
14008
13916
  }, nextMs);
14009
13917
  }
14010
13918
  async findUploadCandidates() {
14011
- const traceDir = path14.join(this.options.machineDir, "traces");
13919
+ const traceDir = path15.join(this.options.machineDir, "traces");
14012
13920
  let names;
14013
13921
  try {
14014
13922
  names = await readdir3(traceDir);
@@ -14020,8 +13928,8 @@ var DaemonTraceBundleUploader = class {
14020
13928
  const currentFile = this.options.currentFileProvider?.();
14021
13929
  const candidates = [];
14022
13930
  for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
14023
- const file = path14.join(traceDir, name);
14024
- if (currentFile && path14.resolve(file) === path14.resolve(currentFile)) continue;
13931
+ const file = path15.join(traceDir, name);
13932
+ if (currentFile && path15.resolve(file) === path15.resolve(currentFile)) continue;
14025
13933
  if (await this.isUploaded(file)) continue;
14026
13934
  try {
14027
13935
  const info = await stat3(file);
@@ -14095,8 +14003,8 @@ var DaemonTraceBundleUploader = class {
14095
14003
  }
14096
14004
  }
14097
14005
  uploadStatePath(file) {
14098
- const stateDir = path14.join(this.options.machineDir, "trace-uploads");
14099
- return path14.join(stateDir, `${path14.basename(file)}.uploaded.json`);
14006
+ const stateDir = path15.join(this.options.machineDir, "trace-uploads");
14007
+ return path15.join(stateDir, `${path15.basename(file)}.uploaded.json`);
14100
14008
  }
14101
14009
  async isUploaded(file) {
14102
14010
  try {
@@ -14108,9 +14016,9 @@ var DaemonTraceBundleUploader = class {
14108
14016
  }
14109
14017
  async markUploaded(file, metadata) {
14110
14018
  const stateFile = this.uploadStatePath(file);
14111
- await mkdir2(path14.dirname(stateFile), { recursive: true, mode: 448 });
14019
+ await mkdir2(path15.dirname(stateFile), { recursive: true, mode: 448 });
14112
14020
  await writeFile2(stateFile, `${JSON.stringify({
14113
- file: path14.basename(file),
14021
+ file: path15.basename(file),
14114
14022
  uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
14115
14023
  ...metadata
14116
14024
  }, null, 2)}
@@ -14232,17 +14140,28 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
14232
14140
  }
14233
14141
  }
14234
14142
  function resolveSlockCliPath(moduleUrl = import.meta.url) {
14235
- const thisDir = path15.dirname(fileURLToPath(moduleUrl));
14236
- const bundledDistPath = path15.resolve(thisDir, "cli", "index.js");
14143
+ const thisDir = path16.dirname(fileURLToPath(moduleUrl));
14144
+ const bundledDistPath = path16.resolve(thisDir, "cli", "index.js");
14237
14145
  try {
14238
14146
  accessSync(bundledDistPath);
14239
14147
  return bundledDistPath;
14240
14148
  } catch {
14241
- const workspaceDistPath = path15.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
14149
+ const workspaceDistPath = path16.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
14242
14150
  accessSync(workspaceDistPath);
14243
14151
  return workspaceDistPath;
14244
14152
  }
14245
14153
  }
14154
+ function resolveSlockCliPathOrEmpty(moduleUrl = import.meta.url) {
14155
+ try {
14156
+ return resolveSlockCliPath(moduleUrl);
14157
+ } catch {
14158
+ return "";
14159
+ }
14160
+ }
14161
+ async function runBundledSlockCli(argv) {
14162
+ process.argv = [process.execPath, "slock", ...argv];
14163
+ await import("./dist-CYKZS5JA.js");
14164
+ }
14246
14165
  function detectRuntimes(tracer = noopTracer) {
14247
14166
  const ids = [];
14248
14167
  const versions = {};
@@ -14392,7 +14311,7 @@ var DaemonCore = class {
14392
14311
  this.options = options;
14393
14312
  this.daemonVersion = options.daemonVersion ?? readDaemonVersion();
14394
14313
  this.computerVersion = (process.env.SLOCK_COMPUTER_VERSION || "").trim() || null;
14395
- this.slockCliPath = options.slockCliPath ?? resolveSlockCliPath();
14314
+ this.slockCliPath = options.slockCliPath ?? resolveSlockCliPathOrEmpty();
14396
14315
  this.slockHome = resolveSlockHome();
14397
14316
  process.env[SLOCK_HOME_ENV] = this.slockHome;
14398
14317
  this.injectedTracer = Boolean(options.tracer);
@@ -14427,7 +14346,7 @@ var DaemonCore = class {
14427
14346
  }
14428
14347
  resolveMachineStateRoot() {
14429
14348
  if (this.options.machineStateDir) return this.options.machineStateDir;
14430
- if (this.options.dataDir) return path15.join(path15.dirname(this.options.dataDir), "machines");
14349
+ if (this.options.dataDir) return path16.join(path16.dirname(this.options.dataDir), "machines");
14431
14350
  return resolveDefaultMachineStateRoot();
14432
14351
  }
14433
14352
  shouldEnableLocalTrace() {
@@ -14454,7 +14373,7 @@ var DaemonCore = class {
14454
14373
  sink: this.localTraceSink
14455
14374
  }));
14456
14375
  this.agentManager.setTracer(this.tracer);
14457
- this.agentManager.setCliTransportTraceDir(path15.join(machineDir, "traces"));
14376
+ this.agentManager.setCliTransportTraceDir(path16.join(machineDir, "traces"));
14458
14377
  }
14459
14378
  installTraceBundleUploader(machineDir) {
14460
14379
  if (!this.shouldEnableLocalTrace()) return;
@@ -14890,18 +14809,29 @@ var DaemonCore = class {
14890
14809
  case "computer:restart":
14891
14810
  case "computer:upgrade": {
14892
14811
  const action = msg.type === "computer:restart" ? "restart" : "upgrade";
14812
+ const requestId = msg.requestId;
14893
14813
  this.recordDaemonTrace("daemon.computer_control.received", {
14894
14814
  action,
14895
- handled: Boolean(this.options.onComputerControl)
14815
+ handled: Boolean(this.options.onComputerControl),
14816
+ ...requestId ? { request_id: requestId } : {}
14896
14817
  });
14897
14818
  if (this.options.onComputerControl) {
14898
- try {
14899
- this.options.onComputerControl(action);
14900
- } catch (err) {
14819
+ const ctx = {
14820
+ requestId,
14821
+ emitUpgradeProgress: (ev) => {
14822
+ if (!requestId) return;
14823
+ this.connection.send({ type: "computer:upgrade:progress", requestId, ...ev });
14824
+ },
14825
+ emitUpgradeDone: (ev) => {
14826
+ if (!requestId) return;
14827
+ this.connection.send({ type: "computer:upgrade:done", requestId, ...ev });
14828
+ }
14829
+ };
14830
+ void Promise.resolve().then(() => this.options.onComputerControl(action, ctx)).catch((err) => {
14901
14831
  logger.error(
14902
14832
  `[Daemon] computer:${action} control handler failed: ${err instanceof Error ? err.message : String(err)}`
14903
14833
  );
14904
- }
14834
+ });
14905
14835
  } else {
14906
14836
  logger.info(`[Daemon] Ignoring computer:${action} \u2014 not launched by a Computer service.`);
14907
14837
  }
@@ -14931,8 +14861,8 @@ var DaemonCore = class {
14931
14861
  capabilities: ["agent:start", "agent:stop", "agent:deliver", "workspace:files"],
14932
14862
  runtimes,
14933
14863
  runningAgents: runningAgentIds,
14934
- hostname: this.options.hostname ?? os7.hostname(),
14935
- os: this.options.osDescription ?? `${os7.platform()} ${os7.arch()}`,
14864
+ hostname: this.options.hostname ?? os8.hostname(),
14865
+ os: this.options.osDescription ?? `${os8.platform()} ${os8.arch()}`,
14936
14866
  daemonVersion: this.daemonVersion,
14937
14867
  ...this.computerVersion ? { computerVersion: this.computerVersion } : {}
14938
14868
  });
@@ -14942,6 +14872,22 @@ var DaemonCore = class {
14942
14872
  idle_agents_count: idleAgentSessions.length,
14943
14873
  runtime_profile_reports_count: runtimeProfileReports.length
14944
14874
  });
14875
+ if (this.options.onComputerUpgradeReconcile) {
14876
+ void Promise.resolve().then(
14877
+ () => this.options.onComputerUpgradeReconcile((done) => {
14878
+ this.connection.send({ type: "computer:upgrade:done", ...done });
14879
+ this.recordDaemonTrace("daemon.computer_upgrade.reconciled", {
14880
+ request_id: done.requestId,
14881
+ ok: done.ok,
14882
+ ...done.newVersion ? { new_version: done.newVersion } : {}
14883
+ });
14884
+ })
14885
+ ).catch((err) => {
14886
+ logger.error(
14887
+ `[Daemon] computer upgrade reconcile failed: ${err instanceof Error ? err.message : String(err)}`
14888
+ );
14889
+ });
14890
+ }
14945
14891
  for (const agentId of runningAgentIds) {
14946
14892
  const sessionId = this.agentManager.getAgentSessionId(agentId);
14947
14893
  const launchId = this.agentManager.getAgentLaunchId(agentId);
@@ -15004,6 +14950,8 @@ export {
15004
14950
  parseDaemonCliArgs,
15005
14951
  readDaemonVersion,
15006
14952
  resolveSlockCliPath,
14953
+ resolveSlockCliPathOrEmpty,
14954
+ runBundledSlockCli,
15007
14955
  detectRuntimes,
15008
14956
  DaemonCore
15009
14957
  };