@slock-ai/daemon 0.57.5 → 0.57.6
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.
- package/dist/{chunk-BGWXECWO.js → chunk-XQ3HLMCZ.js} +281 -333
- package/dist/cli/index.js +977 -323
- package/dist/cli/package.json +1 -1
- package/dist/core.js +5 -1
- package/dist/dist-CYKZS5JA.js +20188 -0
- package/dist/index.js +1 -1
- package/package.json +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/core.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
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
|
|
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
|
|
1511
|
-
import
|
|
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,
|
|
1870
|
-
const
|
|
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
|
|
1885
|
-
const
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
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
|
-
-
|
|
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 =
|
|
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
|
-
-
|
|
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,
|
|
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(
|
|
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
|
|
4567
|
-
import
|
|
4568
|
-
import
|
|
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
|
|
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
|
|
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 ??
|
|
4879
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync4;
|
|
4955
4880
|
const execSyncFn = deps.execSyncFn ?? execSync;
|
|
4956
|
-
const gitDir =
|
|
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
|
|
4894
|
+
return path6.basename(commandPath).toLowerCase().startsWith("codex-command-runner");
|
|
4970
4895
|
}
|
|
4971
4896
|
function resolveWindowsNpmCodexEntry(deps = {}) {
|
|
4972
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
4897
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync4;
|
|
4973
4898
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
|
|
4974
4899
|
const env = deps.env ?? process.env;
|
|
4975
|
-
const winPath =
|
|
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 ??
|
|
4919
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync4;
|
|
4995
4920
|
const env = deps.env ?? process.env;
|
|
4996
4921
|
const homeDir = deps.homeDir;
|
|
4997
|
-
const winPath =
|
|
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(
|
|
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 =
|
|
5270
|
-
const cachePath =
|
|
5271
|
-
const configPath =
|
|
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
|
|
5749
|
-
import
|
|
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 ??
|
|
5704
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync5;
|
|
5792
5705
|
const env = deps.env ?? process.env;
|
|
5793
|
-
const winPath =
|
|
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
|
|
5933
|
-
import
|
|
5934
|
-
import
|
|
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 =
|
|
5984
|
-
const agentFilePath =
|
|
5985
|
-
if (!isResume || !
|
|
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 =
|
|
6132
|
-
const configPath =
|
|
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
|
|
6160
|
-
import
|
|
6161
|
-
import
|
|
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 =
|
|
6192
|
-
const configPath =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 (
|
|
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 ??
|
|
6314
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync7;
|
|
6410
6315
|
const execFileSyncFn = deps.execFileSyncFn;
|
|
6411
6316
|
const env = deps.env ?? process.env;
|
|
6412
|
-
const winPath =
|
|
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 =
|
|
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(
|
|
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 &&
|
|
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,
|
|
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
|
|
6641
|
-
import
|
|
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
|
|
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 ?
|
|
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
|
-
|
|
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(
|
|
6847
|
-
const modelRegistry = ModelRegistry.create(authStorage,
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
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
|
|
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(
|
|
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 =
|
|
7943
|
-
|
|
7944
|
-
const filePath =
|
|
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
|
|
7964
|
-
|
|
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(
|
|
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
|
|
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(
|
|
8055
|
-
return
|
|
7959
|
+
function dynamicReplyInstruction() {
|
|
7960
|
+
return "reply using `slock message send --target <exact target>`";
|
|
8056
7961
|
}
|
|
8057
|
-
function dynamicClaimInstruction(
|
|
8058
|
-
return
|
|
7962
|
+
function dynamicClaimInstruction() {
|
|
7963
|
+
return "claim the relevant task with `slock task claim`";
|
|
8059
7964
|
}
|
|
8060
|
-
function formatIncomingMessage(message
|
|
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:
|
|
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 ||
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
9853
|
+
const fullPath = path12.join(agentDataDir, relativePath);
|
|
9949
9854
|
try {
|
|
9950
9855
|
await access(fullPath);
|
|
9951
9856
|
} catch {
|
|
9952
|
-
await mkdir(
|
|
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
|
|
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(
|
|
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 =
|
|
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 =
|
|
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,
|
|
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 =
|
|
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 =
|
|
11451
|
-
if (!resolved.startsWith(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 =
|
|
11460
|
-
const resolved =
|
|
11461
|
-
if (!resolved.startsWith(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 =
|
|
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
|
|
11500
|
-
const
|
|
11501
|
-
const
|
|
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(
|
|
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(
|
|
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 =
|
|
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(
|
|
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]
|
|
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
|
|
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 =
|
|
13028
|
-
const relativePath =
|
|
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
|
|
13403
|
-
import
|
|
13404
|
-
import
|
|
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
|
|
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 =
|
|
13457
|
-
const lockDir =
|
|
13364
|
+
const machineDir = path13.join(rootDir, lockId);
|
|
13365
|
+
const lockDir = path13.join(machineDir, "daemon.lock");
|
|
13458
13366
|
const token = randomUUID4();
|
|
13459
|
-
|
|
13367
|
+
mkdirSync5(machineDir, { recursive: true });
|
|
13460
13368
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
13461
13369
|
try {
|
|
13462
|
-
|
|
13370
|
+
mkdirSync5(lockDir);
|
|
13463
13371
|
const owner = {
|
|
13464
13372
|
pid: process.pid,
|
|
13465
13373
|
token,
|
|
13466
|
-
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
|
|
13518
|
-
import
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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
|
|
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,
|
|
13792
|
-
return `${base.replace(/\/+$/, "")}${
|
|
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 =
|
|
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 =
|
|
14024
|
-
if (currentFile &&
|
|
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 =
|
|
14099
|
-
return
|
|
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(
|
|
14019
|
+
await mkdir2(path15.dirname(stateFile), { recursive: true, mode: 448 });
|
|
14112
14020
|
await writeFile2(stateFile, `${JSON.stringify({
|
|
14113
|
-
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 =
|
|
14236
|
-
const bundledDistPath =
|
|
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 =
|
|
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 ??
|
|
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
|
|
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(
|
|
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
|
-
|
|
14899
|
-
|
|
14900
|
-
|
|
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 ??
|
|
14935
|
-
os: this.options.osDescription ?? `${
|
|
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
|
};
|