@slock-ai/daemon 0.48.0 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat-bridge.js +3 -1396
- package/dist/{chunk-B7XIMLOT.js → chunk-KNMCE6WB.js} +66 -109
- package/dist/{chunk-EDE2E6QR.js → chunk-M4A5QPUN.js} +639 -274
- package/dist/cli/index.js +287 -53
- package/dist/core.js +2 -2
- package/dist/index.js +2 -2
- package/package.json +1 -1
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS,
|
|
3
|
-
SLOCK_HOME_ENV,
|
|
4
3
|
buildWebSocketOptions,
|
|
5
4
|
executeJsonRequest,
|
|
6
5
|
executeResponseRequest,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
resolveSlockHome,
|
|
10
|
-
resolveSlockHomePath
|
|
11
|
-
} from "./chunk-B7XIMLOT.js";
|
|
6
|
+
logger
|
|
7
|
+
} from "./chunk-KNMCE6WB.js";
|
|
12
8
|
|
|
13
9
|
// src/core.ts
|
|
14
|
-
import
|
|
15
|
-
import
|
|
10
|
+
import path16 from "path";
|
|
11
|
+
import os8 from "os";
|
|
16
12
|
import { createRequire } from "module";
|
|
17
13
|
import { accessSync } from "fs";
|
|
18
14
|
import { fileURLToPath } from "url";
|
|
@@ -481,6 +477,8 @@ function resolveSlockCliInvocation(toolName, input) {
|
|
|
481
477
|
return { toolName: "list_server", input: {} };
|
|
482
478
|
case "channel members":
|
|
483
479
|
return { toolName: "list_channel_members", input: { channel: rest[0] } };
|
|
480
|
+
case "channel join":
|
|
481
|
+
return { toolName: "join_channel", input: { target: readOptionValue(rest, "--target") } };
|
|
484
482
|
case "channel leave":
|
|
485
483
|
return { toolName: "leave_channel", input: { target: readOptionValue(rest, "--target") } };
|
|
486
484
|
case "task list":
|
|
@@ -766,21 +764,21 @@ var DISPLAY_PLAN_CONFIG = {
|
|
|
766
764
|
};
|
|
767
765
|
|
|
768
766
|
// src/agentProcessManager.ts
|
|
769
|
-
import { mkdirSync as mkdirSync4, readdirSync as
|
|
767
|
+
import { mkdirSync as mkdirSync4, readdirSync as readdirSync2, statSync as statSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
770
768
|
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
|
|
771
769
|
import { createHash as createHash2 } from "crypto";
|
|
772
|
-
import
|
|
773
|
-
import
|
|
770
|
+
import path12 from "path";
|
|
771
|
+
import os6 from "os";
|
|
774
772
|
|
|
775
773
|
// src/drivers/claude.ts
|
|
776
774
|
import { spawn } from "child_process";
|
|
777
|
-
import { existsSync as
|
|
778
|
-
import
|
|
779
|
-
import
|
|
775
|
+
import { existsSync as existsSync3, readdirSync, readFileSync, statSync, writeFileSync as writeFileSync2 } from "fs";
|
|
776
|
+
import os2 from "os";
|
|
777
|
+
import path4 from "path";
|
|
780
778
|
|
|
781
779
|
// src/drivers/cliTransport.ts
|
|
782
780
|
import { mkdirSync, writeFileSync } from "fs";
|
|
783
|
-
import
|
|
781
|
+
import path2 from "path";
|
|
784
782
|
|
|
785
783
|
// src/drivers/systemPrompt.ts
|
|
786
784
|
function toolRef(prefix, name) {
|
|
@@ -858,27 +856,28 @@ Use the \`slock\` CLI for chat / task / attachment operations. The daemon inject
|
|
|
858
856
|
2. **\`slock message send\`** \u2014 Send a message to a channel or DM.
|
|
859
857
|
3. **\`slock server info\`** \u2014 List channels in this server, which ones you have joined, plus all agents and humans.
|
|
860
858
|
4. **\`slock channel members\`** \u2014 List the members (agents and humans) of a specific channel, DM, or thread target.
|
|
861
|
-
5. **\`slock channel
|
|
862
|
-
6. **\`slock
|
|
863
|
-
7. **\`slock
|
|
864
|
-
8. **\`slock message
|
|
865
|
-
9. **\`slock message
|
|
866
|
-
10. **\`slock
|
|
867
|
-
11. **\`slock task
|
|
868
|
-
12. **\`slock task
|
|
869
|
-
13. **\`slock task
|
|
870
|
-
14. **\`slock task
|
|
871
|
-
15. **\`slock
|
|
872
|
-
16. **\`slock attachment
|
|
873
|
-
17. **\`slock
|
|
874
|
-
18. **\`slock profile
|
|
875
|
-
19. **\`slock
|
|
876
|
-
20. **\`slock reminder
|
|
877
|
-
21. **\`slock reminder
|
|
878
|
-
22. **\`slock reminder
|
|
879
|
-
23. **\`slock reminder
|
|
880
|
-
24. **\`slock reminder
|
|
881
|
-
25. **\`slock
|
|
859
|
+
5. **\`slock channel join\`** \u2014 Join a visible public channel. This only affects your own agent membership.
|
|
860
|
+
6. **\`slock channel leave\`** \u2014 Leave a regular channel you have joined. This only affects your own agent membership.
|
|
861
|
+
7. **\`slock thread unfollow\`** \u2014 Stop receiving ordinary delivery for a thread you no longer need to follow. This only affects your own agent attention state.
|
|
862
|
+
8. **\`slock message read\`** \u2014 Read past messages from a channel, DM, or thread. Supports \`before\` / \`after\` pagination and \`around\` for centered context.
|
|
863
|
+
9. **\`slock message search\`** \u2014 Search messages visible to you, then inspect a hit with \`slock message read\`.
|
|
864
|
+
10. **\`slock message react\`** \u2014 Add or remove your reaction on a message. Use sparingly: prefer acknowledgement/follow-up signals like \u{1F440}, and do not auto-react to every merge, deploy, or task completion with celebratory emoji.
|
|
865
|
+
11. **\`slock task list\`** \u2014 View a channel's task board.
|
|
866
|
+
12. **\`slock task create\`** \u2014 Create new task-messages in a channel (supports batch titles; equivalent to sending a new message and publishing it as a task-message, not claiming it for yourself).
|
|
867
|
+
13. **\`slock task claim\`** \u2014 Claim tasks by number or message ID (supports batch, handles conflicts).
|
|
868
|
+
14. **\`slock task unclaim\`** \u2014 Release your claim on a task.
|
|
869
|
+
15. **\`slock task update\`** \u2014 Change a task's status (e.g. to in_review or done).
|
|
870
|
+
16. **\`slock attachment upload\`** \u2014 Upload a file to attach to a message. Uses content sniffing for image previews; pass \`--mime-type\` only when you know the exact type. Returns an attachment ID to pass to \`slock message send\`.
|
|
871
|
+
17. **\`slock attachment view\`** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
|
|
872
|
+
18. **\`slock profile show\`** \u2014 Show your own profile, or another visible profile via \`@handle\`. Mirrors the canonical Slock profile view.
|
|
873
|
+
19. **\`slock profile update\`** \u2014 Update your own profile. Supports \`--avatar-file <path>\`, \`--display-name <name>\`, and \`--description <text>\`. Values must be non-empty. Provide at least one flag per call; multiple flags can be combined.
|
|
874
|
+
20. **\`slock reminder schedule\`** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
|
|
875
|
+
21. **\`slock reminder list\`** \u2014 List your reminders, including lifecycle history for each reminder.
|
|
876
|
+
22. **\`slock reminder snooze\`** \u2014 Push a reminder later without replacing it.
|
|
877
|
+
23. **\`slock reminder update\`** \u2014 Change a reminder's title, schedule, or recurrence without creating a new reminder.
|
|
878
|
+
24. **\`slock reminder cancel\`** \u2014 Cancel one of your reminders by ID.
|
|
879
|
+
25. **\`slock reminder log\`** \u2014 Show the event log for a reminder, including fires, dismissals, and reschedules.
|
|
880
|
+
26. **\`slock action prepare\`** \u2014 Prepare an action card for a human to commit (B-mode quick-commit shortcut). Posts a card the human can click to execute the action under their own identity. Pass \`--target <ch>\` and pipe the action JSON on stdin (variants: \`channel:create\`, \`agent:create\`).
|
|
882
881
|
|
|
883
882
|
The CLI prints human-readable canonical text on success (matching the format you see in received messages and history). On failure it prints JSON to stderr:
|
|
884
883
|
- failure \u2192 stderr \`{"ok":false,"code":"...","message":"..."}\` with non-zero exit
|
|
@@ -893,19 +892,20 @@ You have MCP tools from the "chat" server. Use ONLY these for communication:
|
|
|
893
892
|
1. **${checkCmd}** \u2014 Non-blocking check for new messages. Use freely during work \u2014 at natural breakpoints or after notifications.
|
|
894
893
|
2. **${sendCmd}** \u2014 Send a message to a channel or DM.
|
|
895
894
|
3. **${serverInfoCmd}** \u2014 List all channels in this server, which ones you have joined, plus all agents and humans.
|
|
896
|
-
4. **\`${t("
|
|
897
|
-
5.
|
|
898
|
-
6.
|
|
899
|
-
7. **\`${t("
|
|
900
|
-
8.
|
|
901
|
-
9. **${
|
|
902
|
-
10.
|
|
903
|
-
11.
|
|
904
|
-
12.
|
|
905
|
-
13. **\`${t("
|
|
906
|
-
14.
|
|
907
|
-
15. **${
|
|
908
|
-
16. **${
|
|
895
|
+
4. **\`${t("join_channel")}\`** \u2014 Join a visible public channel. This only affects your own agent membership.
|
|
896
|
+
5. **\`${t("leave_channel")}\`** \u2014 Leave a regular channel you have joined. This only affects your own agent membership.
|
|
897
|
+
6. **${readCmd}** \u2014 Read past messages from a channel, DM, or thread. Supports \`before\` / \`after\` pagination and \`around\` for centered context.
|
|
898
|
+
7. **\`${t("search_messages")}\`** \u2014 Search messages visible to you, then inspect a hit with ${readCmd}.
|
|
899
|
+
8. **\`${t("list_tasks")}\`** \u2014 View a channel's task board.
|
|
900
|
+
9. **${taskCreateCmd}** \u2014 Create new task-messages in a channel (supports batch titles; equivalent to sending a new message and publishing it as a task-message, not claiming it for yourself).
|
|
901
|
+
10. **${taskClaimCmd}** \u2014 Claim tasks by number or message ID (supports batch, handles conflicts).
|
|
902
|
+
11. **\`${t("unclaim_task")}\`** \u2014 Release your claim on a task.
|
|
903
|
+
12. **${taskUpdateCmd}** \u2014 Change a task's status (e.g. to in_review or done).
|
|
904
|
+
13. **\`${t("upload_file")}\`** \u2014 Upload a file up to 50MB to attach to a message. Returns an attachment ID to pass to ${sendCmd}. Videos are downloadable attachments and are not parsed by agents; large PDFs may need to be downloaded and inspected in smaller chunks.
|
|
905
|
+
14. **\`${t("view_file")}\`** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
|
|
906
|
+
15. **${scheduleReminderCmd}** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
|
|
907
|
+
16. **${listRemindersCmd}** \u2014 List your reminders.
|
|
908
|
+
17. **${cancelReminderCmd}** \u2014 Cancel one of your reminders by ID.`;
|
|
909
909
|
const reminderSection = `### Reminders
|
|
910
910
|
|
|
911
911
|
Use reminders for follow-up that depends on future state you cannot resolve now, whether user-requested or self-driven. A reminder is an author-owned, persistent, observable, snoozable, updatable, and cancelable wake-up signal anchored to a Slock message or thread; when it fires, it wakes the author who scheduled it, not other people. If anchored to a message or thread, the receipt/fire system message is visible in that surface, but wake ownership does not transfer. To notify another human or agent later, schedule your own reminder and then @mention them when it fires. Use reminders instead of keeping the current turn alive with a long sleep or relying on MEMORY to wake you. If you expect the wait to finish within about 1 minute, you may briefly poll, but say so in the relevant thread first.
|
|
@@ -926,6 +926,10 @@ Long message with "quotes", $vars, \`backticks\`, and code blocks.
|
|
|
926
926
|
EOF
|
|
927
927
|
\`\`\`
|
|
928
928
|
|
|
929
|
+
If Slock says a message was not sent and was saved as a draft, choose one path:
|
|
930
|
+
- To update the draft, use a normal \`slock message send --target <target>\` with the revised content.
|
|
931
|
+
- To send the current draft unchanged, use \`slock message send --send-draft --target <target>\` with no stdin. Do not use \`--send-draft\` when changing content.
|
|
932
|
+
|
|
929
933
|
**IMPORTANT**: To reply to any message, always reuse the exact \`target\` from the received message. This ensures your reply goes to the right place \u2014 whether it's a channel, DM, or thread.` : `### Sending messages
|
|
930
934
|
|
|
931
935
|
- **Reply to a channel**: \`${t("send_message")}(target="#channel-name", content="...")\`
|
|
@@ -957,11 +961,11 @@ Threads are sub-conversations attached to a specific message. They let you discu
|
|
|
957
961
|
const discoverySection = isCli ? `### Discovering people and channels
|
|
958
962
|
|
|
959
963
|
Call \`slock server info\` to see all channels in this server, which ones you have joined, other agents, and humans.
|
|
960
|
-
Visible public channels may appear even when \`joined=false\`. In that state you can still inspect them with \`slock message read\` and \`slock channel members\`, but you cannot send messages there or receive ordinary channel delivery until a human
|
|
964
|
+
Visible public channels may appear even when \`joined=false\`. In that state you can still inspect them with \`slock message read\` and \`slock channel members\`, but you cannot send messages there or receive ordinary channel delivery until you join with \`slock channel join --target "#channel-name"\`. Private channels require a human with access to add you. To leave a regular channel you have joined, use \`slock channel leave --target "#channel-name"\`. To stop following a thread without leaving its parent channel, use \`slock thread unfollow --target "#channel-name:shortid"\`.
|
|
961
965
|
Private channels are membership-gated. If \`slock server info\` shows a channel as private, treat its name, members, and content as private to that channel; do not disclose that information in other channels, DMs, summaries, or task reports unless a human explicitly asks within an authorized context. In \`slock channel members\`, human role labels such as owner/admin show server-level authority; no role label means ordinary member.` : `### Discovering people and channels
|
|
962
966
|
|
|
963
967
|
Call ${serverInfoCmd} to see all channels in this server, which ones you have joined, other agents, and humans.
|
|
964
|
-
Visible public channels may appear even when \`joined=false\`. In that state you can still inspect them with ${readCmd}, but you cannot send messages there or receive ordinary channel delivery until a human
|
|
968
|
+
Visible public channels may appear even when \`joined=false\`. In that state you can still inspect them with ${readCmd}, but you cannot send messages there or receive ordinary channel delivery until you join with \`${t("join_channel")}\`. Private channels require a human with access to add you. To leave a regular channel you have joined, use \`${t("leave_channel")}\`.
|
|
965
969
|
Private channels are membership-gated. If ${serverInfoCmd} shows a channel as private, treat its name, members, and content as private to that channel; do not disclose that information in other channels, DMs, summaries, or task reports unless a human explicitly asks within an authorized context. In channel member listings, human role labels such as owner/admin show server-level authority; no role label means ordinary member.`;
|
|
966
970
|
const channelAwarenessSection = isCli ? `### Channel awareness
|
|
967
971
|
|
|
@@ -1305,8 +1309,44 @@ ${config.description}. This may evolve.`;
|
|
|
1305
1309
|
function buildCliSystemPrompt(config, opts) {
|
|
1306
1310
|
return buildPrompt(config, "cli", opts);
|
|
1307
1311
|
}
|
|
1308
|
-
|
|
1309
|
-
|
|
1312
|
+
|
|
1313
|
+
// src/slockHome.ts
|
|
1314
|
+
import os from "os";
|
|
1315
|
+
import path from "path";
|
|
1316
|
+
import { existsSync } from "fs";
|
|
1317
|
+
var SLOCK_HOME_ENV = "SLOCK_HOME";
|
|
1318
|
+
function resolveDefaultSlockHome(homeDir = os.homedir()) {
|
|
1319
|
+
return path.resolve(path.join(homeDir, ".slock"));
|
|
1320
|
+
}
|
|
1321
|
+
function resolveSlockHome(env = process.env, homeDir = os.homedir()) {
|
|
1322
|
+
const raw = env[SLOCK_HOME_ENV]?.trim();
|
|
1323
|
+
const root = raw && raw.length > 0 ? raw : resolveDefaultSlockHome(homeDir);
|
|
1324
|
+
return path.resolve(root);
|
|
1325
|
+
}
|
|
1326
|
+
function resolveSlockHomePath(childPath, slockHome = resolveSlockHome()) {
|
|
1327
|
+
return path.join(slockHome, childPath);
|
|
1328
|
+
}
|
|
1329
|
+
function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.homedir()) {
|
|
1330
|
+
const defaultHome = resolveDefaultSlockHome(homeDir);
|
|
1331
|
+
if (path.resolve(slockHome) === defaultHome) return [];
|
|
1332
|
+
const candidates = [
|
|
1333
|
+
{
|
|
1334
|
+
path: path.join(defaultHome, "agents"),
|
|
1335
|
+
destination: path.join(slockHome, "agents"),
|
|
1336
|
+
description: "agent workspaces and per-agent runtime wrapper state"
|
|
1337
|
+
},
|
|
1338
|
+
{
|
|
1339
|
+
path: path.join(defaultHome, "machines"),
|
|
1340
|
+
destination: path.join(slockHome, "machines"),
|
|
1341
|
+
description: "daemon machine locks, local traces, and machine-scoped state"
|
|
1342
|
+
},
|
|
1343
|
+
{
|
|
1344
|
+
path: path.join(defaultHome, "attachments"),
|
|
1345
|
+
destination: path.join(slockHome, "attachments"),
|
|
1346
|
+
description: "chat bridge attachment download cache"
|
|
1347
|
+
}
|
|
1348
|
+
];
|
|
1349
|
+
return candidates.filter((candidate) => existsSync(candidate.path));
|
|
1310
1350
|
}
|
|
1311
1351
|
|
|
1312
1352
|
// src/drivers/cliTransport.ts
|
|
@@ -1332,23 +1372,23 @@ function prepareCliTransport(ctx, extraEnv = {}, platform = process.platform) {
|
|
|
1332
1372
|
if (!ctx.slockCliPath) {
|
|
1333
1373
|
throw new Error(`${ctx.config.runtime} driver: slockCliPath is required (daemon must inject it)`);
|
|
1334
1374
|
}
|
|
1335
|
-
const slockDir =
|
|
1375
|
+
const slockDir = path2.join(ctx.workingDirectory, ".slock");
|
|
1336
1376
|
mkdirSync(slockDir, { recursive: true });
|
|
1337
|
-
const tokenFile =
|
|
1377
|
+
const tokenFile = path2.join(slockDir, "agent-token");
|
|
1338
1378
|
writeFileSync(tokenFile, ctx.config.authToken || ctx.daemonApiKey, { mode: 384 });
|
|
1339
|
-
const posixWrapper =
|
|
1379
|
+
const posixWrapper = path2.join(slockDir, "slock");
|
|
1340
1380
|
const posixBody = `#!/usr/bin/env bash
|
|
1341
1381
|
exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(ctx.slockCliPath)} "$@"
|
|
1342
1382
|
`;
|
|
1343
1383
|
writeFileSync(posixWrapper, posixBody, { mode: 493 });
|
|
1344
1384
|
if (platform === "win32") {
|
|
1345
|
-
const cmdWrapper =
|
|
1385
|
+
const cmdWrapper = path2.join(slockDir, "slock.cmd");
|
|
1346
1386
|
const cmdBody = `@echo off\r
|
|
1347
1387
|
"${process.execPath}" "${ctx.slockCliPath}" %*\r
|
|
1348
1388
|
`;
|
|
1349
1389
|
writeFileSync(cmdWrapper, cmdBody);
|
|
1350
1390
|
}
|
|
1351
|
-
const wrapperPath = platform === "win32" ?
|
|
1391
|
+
const wrapperPath = platform === "win32" ? path2.join(slockDir, "slock.cmd") : posixWrapper;
|
|
1352
1392
|
const spawnEnv = {
|
|
1353
1393
|
...process.env,
|
|
1354
1394
|
FORCE_COLOR: "0",
|
|
@@ -1360,7 +1400,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(ctx.slockCliPath)}
|
|
|
1360
1400
|
...ctx.launchId ? { SLOCK_AGENT_LAUNCH_ID: ctx.launchId } : {},
|
|
1361
1401
|
SLOCK_SERVER_URL: ctx.config.serverUrl,
|
|
1362
1402
|
SLOCK_AGENT_TOKEN_FILE: tokenFile,
|
|
1363
|
-
PATH: `${slockDir}${
|
|
1403
|
+
PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
|
|
1364
1404
|
};
|
|
1365
1405
|
delete spawnEnv.SLOCK_AGENT_TOKEN;
|
|
1366
1406
|
return {
|
|
@@ -1373,8 +1413,8 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(ctx.slockCliPath)}
|
|
|
1373
1413
|
|
|
1374
1414
|
// src/drivers/probe.ts
|
|
1375
1415
|
import { execFileSync } from "child_process";
|
|
1376
|
-
import { existsSync } from "fs";
|
|
1377
|
-
import
|
|
1416
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1417
|
+
import path3 from "path";
|
|
1378
1418
|
function normalizeExecOutput(raw) {
|
|
1379
1419
|
return Buffer.isBuffer(raw) ? raw.toString("utf8") : String(raw ?? "");
|
|
1380
1420
|
}
|
|
@@ -1417,7 +1457,7 @@ function resolveCommandOnPath(command, deps = {}) {
|
|
|
1417
1457
|
}
|
|
1418
1458
|
}
|
|
1419
1459
|
function firstExistingPath(candidates, deps = {}) {
|
|
1420
|
-
const exists = deps.existsSyncFn ??
|
|
1460
|
+
const exists = deps.existsSyncFn ?? existsSync2;
|
|
1421
1461
|
for (const candidate of candidates) {
|
|
1422
1462
|
if (exists(candidate)) return candidate;
|
|
1423
1463
|
}
|
|
@@ -1439,11 +1479,11 @@ function readCommandVersion(command, args = [], deps = {}) {
|
|
|
1439
1479
|
}
|
|
1440
1480
|
function resolveHomePath(relativePath, deps = {}) {
|
|
1441
1481
|
const homeDir = deps.homeDir ?? deps.env?.HOME ?? process.env.HOME ?? "";
|
|
1442
|
-
return
|
|
1482
|
+
return path3.join(homeDir, relativePath);
|
|
1443
1483
|
}
|
|
1444
1484
|
|
|
1445
1485
|
// src/drivers/claude.ts
|
|
1446
|
-
var CLAUDE_DESKTOP_CLI_RELATIVE_PATH =
|
|
1486
|
+
var CLAUDE_DESKTOP_CLI_RELATIVE_PATH = path4.join("Applications", "Claude Code URL Handler.app", "Contents", "MacOS", "claude");
|
|
1447
1487
|
var CLAUDE_DESKTOP_CLI_SYSTEM_PATH = "/Applications/Claude Code URL Handler.app/Contents/MacOS/claude";
|
|
1448
1488
|
var CLAUDE_SYSTEM_PROMPT_FILE = "claude-system-prompt.md";
|
|
1449
1489
|
var CLAUDE_MCP_CONFIG_FILE = "claude-mcp-config.json";
|
|
@@ -1499,27 +1539,27 @@ function readClaudeMcpServers(configPath, vars = {}) {
|
|
|
1499
1539
|
}
|
|
1500
1540
|
function resolveClaudeConfigDir(ctx, home) {
|
|
1501
1541
|
const configured = ctx.config.envVars?.CLAUDE_CONFIG_DIR || process.env.CLAUDE_CONFIG_DIR;
|
|
1502
|
-
return configured &&
|
|
1542
|
+
return configured && path4.isAbsolute(configured) ? configured : path4.join(home, ".claude");
|
|
1503
1543
|
}
|
|
1504
1544
|
function collectClaudeMcpConfigFiles(ctx, home) {
|
|
1505
1545
|
const files = [];
|
|
1506
1546
|
const pushIfFile = (candidate) => {
|
|
1507
1547
|
try {
|
|
1508
|
-
if (
|
|
1548
|
+
if (existsSync3(candidate) && statSync(candidate).isFile()) {
|
|
1509
1549
|
files.push({ path: candidate });
|
|
1510
1550
|
}
|
|
1511
1551
|
} catch {
|
|
1512
1552
|
}
|
|
1513
1553
|
};
|
|
1514
|
-
pushIfFile(
|
|
1515
|
-
pushIfFile(
|
|
1516
|
-
const pluginRoot =
|
|
1554
|
+
pushIfFile(path4.join(home, ".claude.json"));
|
|
1555
|
+
pushIfFile(path4.join(ctx.workingDirectory, ".mcp.json"));
|
|
1556
|
+
const pluginRoot = path4.join(resolveClaudeConfigDir(ctx, home), "plugins");
|
|
1517
1557
|
try {
|
|
1518
1558
|
for (const entry of readdirSync(pluginRoot)) {
|
|
1519
|
-
const pluginPath =
|
|
1520
|
-
const configPath =
|
|
1559
|
+
const pluginPath = path4.join(pluginRoot, entry);
|
|
1560
|
+
const configPath = path4.join(pluginPath, ".mcp.json");
|
|
1521
1561
|
try {
|
|
1522
|
-
if (
|
|
1562
|
+
if (existsSync3(configPath) && statSync(configPath).isFile()) {
|
|
1523
1563
|
files.push({
|
|
1524
1564
|
path: configPath,
|
|
1525
1565
|
vars: { CLAUDE_PLUGIN_ROOT: pluginPath }
|
|
@@ -1623,7 +1663,7 @@ var ClaudeDriver = class {
|
|
|
1623
1663
|
]
|
|
1624
1664
|
};
|
|
1625
1665
|
}
|
|
1626
|
-
buildRuntimeActionsMcpConfig(ctx, home =
|
|
1666
|
+
buildRuntimeActionsMcpConfig(ctx, home = os2.homedir()) {
|
|
1627
1667
|
const userMcpServers = buildClaudeUserMcpServers(ctx, home);
|
|
1628
1668
|
if (Object.prototype.hasOwnProperty.call(userMcpServers, SLOCK_RUNTIME_ACTIONS_MCP_SERVER_NAME)) {
|
|
1629
1669
|
logger.warn(
|
|
@@ -1637,9 +1677,9 @@ var ClaudeDriver = class {
|
|
|
1637
1677
|
}
|
|
1638
1678
|
});
|
|
1639
1679
|
}
|
|
1640
|
-
writeClaudeLaunchFiles(ctx, slockDir, home =
|
|
1641
|
-
const systemPromptPath =
|
|
1642
|
-
const mcpConfigPath =
|
|
1680
|
+
writeClaudeLaunchFiles(ctx, slockDir, home = os2.homedir()) {
|
|
1681
|
+
const systemPromptPath = path4.join(slockDir, CLAUDE_SYSTEM_PROMPT_FILE);
|
|
1682
|
+
const mcpConfigPath = path4.join(slockDir, CLAUDE_MCP_CONFIG_FILE);
|
|
1643
1683
|
writeFileSync2(systemPromptPath, ctx.standingPrompt, { mode: 384 });
|
|
1644
1684
|
writeFileSync2(mcpConfigPath, this.buildRuntimeActionsMcpConfig(ctx, home), { mode: 384 });
|
|
1645
1685
|
return { systemPromptPath, mcpConfigPath };
|
|
@@ -1788,10 +1828,10 @@ var ClaudeDriver = class {
|
|
|
1788
1828
|
};
|
|
1789
1829
|
|
|
1790
1830
|
// src/drivers/codex.ts
|
|
1791
|
-
import { spawn as spawn2, execSync } from "child_process";
|
|
1792
|
-
import { existsSync as
|
|
1793
|
-
import
|
|
1794
|
-
import
|
|
1831
|
+
import { spawn as spawn2, execFileSync as execFileSync2, execSync } from "child_process";
|
|
1832
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
1833
|
+
import os3 from "os";
|
|
1834
|
+
import path5 from "path";
|
|
1795
1835
|
function getCodexNotificationErrorMessage(params) {
|
|
1796
1836
|
const topLevelMessage = params?.message;
|
|
1797
1837
|
if (typeof topLevelMessage === "string" && topLevelMessage.trim()) {
|
|
@@ -1804,9 +1844,9 @@ function getCodexNotificationErrorMessage(params) {
|
|
|
1804
1844
|
return null;
|
|
1805
1845
|
}
|
|
1806
1846
|
function ensureGitRepoForCodex(workingDirectory, deps = {}) {
|
|
1807
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
1847
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync4;
|
|
1808
1848
|
const execSyncFn = deps.execSyncFn ?? execSync;
|
|
1809
|
-
const gitDir =
|
|
1849
|
+
const gitDir = path5.join(workingDirectory, ".git");
|
|
1810
1850
|
if (existsSyncFn(gitDir)) return;
|
|
1811
1851
|
execSyncFn("git init", { cwd: workingDirectory, stdio: "pipe" });
|
|
1812
1852
|
execSyncFn(
|
|
@@ -1818,28 +1858,44 @@ function ensureGitRepoForCodex(workingDirectory, deps = {}) {
|
|
|
1818
1858
|
);
|
|
1819
1859
|
}
|
|
1820
1860
|
var CODEX_DESKTOP_BUNDLE_PATH = "/Applications/Codex.app/Contents/Resources/codex";
|
|
1821
|
-
function
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1861
|
+
function isWindowsSandboxRunner(commandPath) {
|
|
1862
|
+
return path5.basename(commandPath).toLowerCase().startsWith("codex-command-runner");
|
|
1863
|
+
}
|
|
1864
|
+
function resolveWindowsNpmCodexEntry(deps = {}) {
|
|
1865
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync4;
|
|
1866
|
+
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
|
|
1867
|
+
const env = deps.env ?? process.env;
|
|
1868
|
+
const winPath = path5.win32;
|
|
1825
1869
|
try {
|
|
1826
|
-
const
|
|
1827
|
-
|
|
1828
|
-
|
|
1870
|
+
const globalRoot = String(execFileSyncFn("npm", ["root", "-g"], {
|
|
1871
|
+
encoding: "utf8",
|
|
1872
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1873
|
+
env
|
|
1874
|
+
})).trim();
|
|
1875
|
+
const candidate = winPath.join(globalRoot, "@openai", "codex", "bin", "codex.js");
|
|
1876
|
+
if (existsSyncFn(candidate)) return candidate;
|
|
1829
1877
|
} catch {
|
|
1830
1878
|
}
|
|
1879
|
+
const cmdPath = resolveCommandOnPath("codex", deps);
|
|
1880
|
+
if (cmdPath) {
|
|
1881
|
+
const candidate = winPath.join(winPath.dirname(cmdPath), "node_modules", "@openai", "codex", "bin", "codex.js");
|
|
1882
|
+
if (existsSyncFn(candidate)) return candidate;
|
|
1883
|
+
}
|
|
1831
1884
|
return null;
|
|
1832
1885
|
}
|
|
1833
1886
|
function resolveCodexCommand(deps = {}) {
|
|
1887
|
+
const platform = deps.platform ?? process.platform;
|
|
1888
|
+
if (platform === "win32") {
|
|
1889
|
+
const npmEntry = resolveWindowsNpmCodexEntry(deps);
|
|
1890
|
+
if (npmEntry) return npmEntry;
|
|
1891
|
+
const command = resolveCommandOnPath("codex", deps);
|
|
1892
|
+
return command && !isWindowsSandboxRunner(command) ? command : null;
|
|
1893
|
+
}
|
|
1834
1894
|
const pathCommand = resolveCommandOnPath("codex", deps);
|
|
1835
1895
|
if (pathCommand) return pathCommand;
|
|
1836
|
-
const platform = deps.platform ?? process.platform;
|
|
1837
1896
|
if (platform === "darwin") {
|
|
1838
1897
|
return firstExistingPath([CODEX_DESKTOP_BUNDLE_PATH], deps);
|
|
1839
1898
|
}
|
|
1840
|
-
if (platform === "win32") {
|
|
1841
|
-
return resolveWindowsSandboxRunner(deps);
|
|
1842
|
-
}
|
|
1843
1899
|
return null;
|
|
1844
1900
|
}
|
|
1845
1901
|
function probeCodex(deps = {}) {
|
|
@@ -1865,37 +1921,20 @@ function resolveCodexSpawn(commandArgs, deps = {}) {
|
|
|
1865
1921
|
if ((deps.platform ?? process.platform) !== "win32") {
|
|
1866
1922
|
return { command: resolveCodexCommand(deps) ?? "codex", args: commandArgs };
|
|
1867
1923
|
}
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
}
|
|
1875
|
-
if (!codexEntry) {
|
|
1876
|
-
try {
|
|
1877
|
-
const cmdPath = execSync("where codex", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim().split(/\r?\n/)[0];
|
|
1878
|
-
const candidate = path4.join(path4.dirname(cmdPath), "node_modules", "@openai", "codex", "bin", "codex.js");
|
|
1879
|
-
if (existsSync3(candidate)) codexEntry = candidate;
|
|
1880
|
-
} catch {
|
|
1881
|
-
}
|
|
1924
|
+
const codexEntry = resolveWindowsNpmCodexEntry(deps);
|
|
1925
|
+
if (codexEntry) {
|
|
1926
|
+
return {
|
|
1927
|
+
command: process.execPath,
|
|
1928
|
+
args: [codexEntry, ...commandArgs]
|
|
1929
|
+
};
|
|
1882
1930
|
}
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
return {
|
|
1887
|
-
command: sandboxRunner,
|
|
1888
|
-
args: commandArgs
|
|
1889
|
-
};
|
|
1890
|
-
}
|
|
1891
|
-
throw new Error(
|
|
1892
|
-
"Cannot resolve Codex CLI entry point on Windows. Ensure @openai/codex is installed globally via npm (npm i -g @openai/codex)."
|
|
1893
|
-
);
|
|
1931
|
+
const command = resolveCommandOnPath("codex", deps);
|
|
1932
|
+
if (command && !isWindowsSandboxRunner(command)) {
|
|
1933
|
+
return { command, args: commandArgs };
|
|
1894
1934
|
}
|
|
1895
|
-
|
|
1896
|
-
command
|
|
1897
|
-
|
|
1898
|
-
};
|
|
1935
|
+
throw new Error(
|
|
1936
|
+
"Cannot resolve Codex CLI entry point on Windows. Install Codex Desktop or install @openai/codex globally via npm (npm i -g @openai/codex). Ignoring .codex/.sandbox-bin/codex-command-runner because it is a sandbox helper, not the Codex CLI."
|
|
1937
|
+
);
|
|
1899
1938
|
}
|
|
1900
1939
|
function joinReasoningText(item) {
|
|
1901
1940
|
const summary = Array.isArray(item.summary) ? item.summary.filter((entry) => typeof entry === "string") : [];
|
|
@@ -2303,9 +2342,9 @@ var CodexDriver = class {
|
|
|
2303
2342
|
return detectCodexModels();
|
|
2304
2343
|
}
|
|
2305
2344
|
};
|
|
2306
|
-
function detectCodexModels(home =
|
|
2307
|
-
const cachePath =
|
|
2308
|
-
const configPath =
|
|
2345
|
+
function detectCodexModels(home = os3.homedir()) {
|
|
2346
|
+
const cachePath = path5.join(home, ".codex", "models_cache.json");
|
|
2347
|
+
const configPath = path5.join(home, ".codex", "config.toml");
|
|
2309
2348
|
let models = [];
|
|
2310
2349
|
try {
|
|
2311
2350
|
const raw = readFileSync2(cachePath, "utf8");
|
|
@@ -2335,16 +2374,10 @@ function detectCodexModels(home = os2.homedir()) {
|
|
|
2335
2374
|
|
|
2336
2375
|
// src/drivers/copilot.ts
|
|
2337
2376
|
import { spawn as spawn3 } from "child_process";
|
|
2338
|
-
import
|
|
2377
|
+
import path6 from "path";
|
|
2339
2378
|
import { writeFileSync as writeFileSync3 } from "fs";
|
|
2340
2379
|
function buildCopilotSpawnEnv(ctx) {
|
|
2341
|
-
return {
|
|
2342
|
-
...process.env,
|
|
2343
|
-
FORCE_COLOR: "0",
|
|
2344
|
-
NO_COLOR: "1",
|
|
2345
|
-
...ctx.config.envVars || {},
|
|
2346
|
-
[SLOCK_HOME_ENV]: resolveSlockHome()
|
|
2347
|
-
};
|
|
2380
|
+
return prepareCliTransport(ctx, { NO_COLOR: "1" }).spawnEnv;
|
|
2348
2381
|
}
|
|
2349
2382
|
var CopilotDriver = class {
|
|
2350
2383
|
id = "copilot";
|
|
@@ -2355,7 +2388,7 @@ var CopilotDriver = class {
|
|
|
2355
2388
|
inFlightWake: "spawn_new"
|
|
2356
2389
|
};
|
|
2357
2390
|
communication = {
|
|
2358
|
-
chat: "
|
|
2391
|
+
chat: "slock_cli",
|
|
2359
2392
|
runtimeControl: "mcp_runtime_actions"
|
|
2360
2393
|
};
|
|
2361
2394
|
session = {
|
|
@@ -2368,23 +2401,39 @@ var CopilotDriver = class {
|
|
|
2368
2401
|
supportsStdinNotification = false;
|
|
2369
2402
|
mcpToolPrefix = "";
|
|
2370
2403
|
busyDeliveryMode = "none";
|
|
2404
|
+
usesSlockCliForCommunication = true;
|
|
2371
2405
|
sessionId = null;
|
|
2372
2406
|
sessionAnnounced = false;
|
|
2373
|
-
|
|
2374
|
-
this.sessionId = ctx.config.sessionId || null;
|
|
2375
|
-
this.sessionAnnounced = false;
|
|
2407
|
+
buildRuntimeActionsMcpConfig(ctx) {
|
|
2376
2408
|
const isTsSource = ctx.chatBridgePath.endsWith(".ts");
|
|
2377
|
-
const
|
|
2378
|
-
const
|
|
2379
|
-
|
|
2380
|
-
writeFileSync3(mcpConfigPath, JSON.stringify({
|
|
2409
|
+
const command = isTsSource ? "npx" : "node";
|
|
2410
|
+
const bridgeArgs = isTsSource ? ["tsx", ctx.chatBridgePath] : [ctx.chatBridgePath];
|
|
2411
|
+
return JSON.stringify({
|
|
2381
2412
|
mcpServers: {
|
|
2382
2413
|
chat: {
|
|
2383
|
-
command
|
|
2384
|
-
args:
|
|
2414
|
+
command,
|
|
2415
|
+
args: [
|
|
2416
|
+
...bridgeArgs,
|
|
2417
|
+
"--agent-id",
|
|
2418
|
+
ctx.agentId,
|
|
2419
|
+
"--server-url",
|
|
2420
|
+
ctx.config.serverUrl,
|
|
2421
|
+
"--auth-token",
|
|
2422
|
+
ctx.config.authToken || ctx.daemonApiKey,
|
|
2423
|
+
"--runtime",
|
|
2424
|
+
this.id,
|
|
2425
|
+
...ctx.launchId ? ["--launch-id", ctx.launchId] : [],
|
|
2426
|
+
"--runtime-actions-only"
|
|
2427
|
+
]
|
|
2385
2428
|
}
|
|
2386
2429
|
}
|
|
2387
|
-
})
|
|
2430
|
+
});
|
|
2431
|
+
}
|
|
2432
|
+
spawn(ctx) {
|
|
2433
|
+
this.sessionId = ctx.config.sessionId || null;
|
|
2434
|
+
this.sessionAnnounced = false;
|
|
2435
|
+
const mcpConfigPath = path6.join(ctx.workingDirectory, ".slock-copilot-mcp.json");
|
|
2436
|
+
writeFileSync3(mcpConfigPath, this.buildRuntimeActionsMcpConfig(ctx), "utf8");
|
|
2388
2437
|
const args = [
|
|
2389
2438
|
"--output-format",
|
|
2390
2439
|
"json",
|
|
@@ -2485,12 +2534,14 @@ var CopilotDriver = class {
|
|
|
2485
2534
|
return null;
|
|
2486
2535
|
}
|
|
2487
2536
|
buildSystemPrompt(config, _agentId) {
|
|
2488
|
-
return
|
|
2537
|
+
return buildCliTransportSystemPrompt(config, {
|
|
2489
2538
|
toolPrefix: "",
|
|
2490
2539
|
extraCriticalRules: [
|
|
2491
|
-
"-
|
|
2540
|
+
"- Runtime Profile migration completion is the only exception to CLI-only operation: when a migration notice tells you to acknowledge with `runtime_profile_migration_done`, call the `runtime_profile_migration_done` tool with the exact `migration_key`; do not use `slock` CLI or reply in chat as the acknowledgment."
|
|
2541
|
+
],
|
|
2542
|
+
postStartupNotes: [
|
|
2543
|
+
"**Copilot runtime note:** Slock launches you as a per-turn process. Complete the current wake using `slock` CLI commands, then stop; the daemon will restart you when new messages arrive."
|
|
2492
2544
|
],
|
|
2493
|
-
postStartupNotes: [],
|
|
2494
2545
|
includeStdinNotificationSection: false,
|
|
2495
2546
|
messageNotificationStyle: "poll"
|
|
2496
2547
|
});
|
|
@@ -2499,16 +2550,10 @@ var CopilotDriver = class {
|
|
|
2499
2550
|
|
|
2500
2551
|
// src/drivers/cursor.ts
|
|
2501
2552
|
import { spawn as spawn4, spawnSync } from "child_process";
|
|
2502
|
-
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as
|
|
2503
|
-
import
|
|
2553
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync5 } from "fs";
|
|
2554
|
+
import path7 from "path";
|
|
2504
2555
|
function buildCursorSpawnEnv(ctx) {
|
|
2505
|
-
return {
|
|
2506
|
-
...process.env,
|
|
2507
|
-
FORCE_COLOR: "0",
|
|
2508
|
-
NO_COLOR: "1",
|
|
2509
|
-
...ctx.config.envVars || {},
|
|
2510
|
-
[SLOCK_HOME_ENV]: resolveSlockHome()
|
|
2511
|
-
};
|
|
2556
|
+
return prepareCliTransport(ctx, { NO_COLOR: "1" }).spawnEnv;
|
|
2512
2557
|
}
|
|
2513
2558
|
var CursorDriver = class {
|
|
2514
2559
|
id = "cursor";
|
|
@@ -2519,7 +2564,7 @@ var CursorDriver = class {
|
|
|
2519
2564
|
inFlightWake: "spawn_new"
|
|
2520
2565
|
};
|
|
2521
2566
|
communication = {
|
|
2522
|
-
chat: "
|
|
2567
|
+
chat: "slock_cli",
|
|
2523
2568
|
runtimeControl: "mcp_runtime_actions"
|
|
2524
2569
|
};
|
|
2525
2570
|
session = {
|
|
@@ -2532,23 +2577,39 @@ var CursorDriver = class {
|
|
|
2532
2577
|
supportsStdinNotification = false;
|
|
2533
2578
|
mcpToolPrefix = "mcp__chat__";
|
|
2534
2579
|
busyDeliveryMode = "none";
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
if (!existsSync4(cursorDir)) {
|
|
2538
|
-
mkdirSync2(cursorDir, { recursive: true });
|
|
2539
|
-
}
|
|
2580
|
+
usesSlockCliForCommunication = true;
|
|
2581
|
+
buildRuntimeActionsMcpConfig(ctx) {
|
|
2540
2582
|
const isTsSource = ctx.chatBridgePath.endsWith(".ts");
|
|
2541
|
-
const
|
|
2542
|
-
const
|
|
2543
|
-
|
|
2544
|
-
writeFileSync4(mcpConfigPath, JSON.stringify({
|
|
2583
|
+
const command = isTsSource ? "npx" : "node";
|
|
2584
|
+
const bridgeArgs = isTsSource ? ["tsx", ctx.chatBridgePath] : [ctx.chatBridgePath];
|
|
2585
|
+
return JSON.stringify({
|
|
2545
2586
|
mcpServers: {
|
|
2546
2587
|
chat: {
|
|
2547
|
-
command
|
|
2548
|
-
args:
|
|
2588
|
+
command,
|
|
2589
|
+
args: [
|
|
2590
|
+
...bridgeArgs,
|
|
2591
|
+
"--agent-id",
|
|
2592
|
+
ctx.agentId,
|
|
2593
|
+
"--server-url",
|
|
2594
|
+
ctx.config.serverUrl,
|
|
2595
|
+
"--auth-token",
|
|
2596
|
+
ctx.config.authToken || ctx.daemonApiKey,
|
|
2597
|
+
"--runtime",
|
|
2598
|
+
this.id,
|
|
2599
|
+
...ctx.launchId ? ["--launch-id", ctx.launchId] : [],
|
|
2600
|
+
"--runtime-actions-only"
|
|
2601
|
+
]
|
|
2549
2602
|
}
|
|
2550
2603
|
}
|
|
2551
|
-
})
|
|
2604
|
+
});
|
|
2605
|
+
}
|
|
2606
|
+
spawn(ctx) {
|
|
2607
|
+
const cursorDir = path7.join(ctx.workingDirectory, ".cursor");
|
|
2608
|
+
if (!existsSync5(cursorDir)) {
|
|
2609
|
+
mkdirSync2(cursorDir, { recursive: true });
|
|
2610
|
+
}
|
|
2611
|
+
const mcpConfigPath = path7.join(cursorDir, "mcp.json");
|
|
2612
|
+
writeFileSync4(mcpConfigPath, this.buildRuntimeActionsMcpConfig(ctx), "utf8");
|
|
2552
2613
|
const args = [
|
|
2553
2614
|
"--print",
|
|
2554
2615
|
"--output-format",
|
|
@@ -2631,12 +2692,14 @@ var CursorDriver = class {
|
|
|
2631
2692
|
return null;
|
|
2632
2693
|
}
|
|
2633
2694
|
buildSystemPrompt(config, _agentId) {
|
|
2634
|
-
return
|
|
2695
|
+
return buildCliTransportSystemPrompt(config, {
|
|
2635
2696
|
toolPrefix: "mcp__chat__",
|
|
2636
2697
|
extraCriticalRules: [
|
|
2637
|
-
"-
|
|
2698
|
+
"- Runtime Profile migration completion is the only exception to CLI-only operation: when a migration notice tells you to acknowledge with `runtime_profile_migration_done`, call the `runtime_profile_migration_done` tool with the exact `migration_key`; do not use `slock` CLI or reply in chat as the acknowledgment."
|
|
2699
|
+
],
|
|
2700
|
+
postStartupNotes: [
|
|
2701
|
+
"**Cursor runtime note:** Slock launches you as a per-turn process. Complete the current wake using `slock` CLI commands, then stop; the daemon will restart you when new messages arrive."
|
|
2638
2702
|
],
|
|
2639
|
-
postStartupNotes: [],
|
|
2640
2703
|
includeStdinNotificationSection: false,
|
|
2641
2704
|
messageNotificationStyle: "poll"
|
|
2642
2705
|
});
|
|
@@ -2685,9 +2748,9 @@ function runCursorModelsCommand() {
|
|
|
2685
2748
|
}
|
|
2686
2749
|
|
|
2687
2750
|
// src/drivers/gemini.ts
|
|
2688
|
-
import { execFileSync as
|
|
2689
|
-
import { existsSync as
|
|
2690
|
-
import
|
|
2751
|
+
import { execFileSync as execFileSync3, spawn as spawn5 } from "child_process";
|
|
2752
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
2753
|
+
import path8 from "path";
|
|
2691
2754
|
function buildGeminiSpawnEnv(ctx, platform = process.platform) {
|
|
2692
2755
|
const { spawnEnv } = prepareCliTransport(ctx, { NO_COLOR: "1" }, platform);
|
|
2693
2756
|
if (!Object.prototype.hasOwnProperty.call(ctx.config.envVars ?? {}, "GEMINI_CLI_TRUST_WORKSPACE")) {
|
|
@@ -2723,10 +2786,10 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
|
|
|
2723
2786
|
if (platform !== "win32") {
|
|
2724
2787
|
return { command: resolveCommandOnPath("gemini", deps) ?? "gemini", args: commandArgs };
|
|
2725
2788
|
}
|
|
2726
|
-
const execFileSyncFn = deps.execFileSyncFn ??
|
|
2727
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
2789
|
+
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
|
|
2790
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync6;
|
|
2728
2791
|
const env = deps.env ?? process.env;
|
|
2729
|
-
const winPath =
|
|
2792
|
+
const winPath = path8.win32;
|
|
2730
2793
|
let geminiEntry = null;
|
|
2731
2794
|
try {
|
|
2732
2795
|
const globalRoot = normalizeExecOutput2(execFileSyncFn("npm", ["root", "-g"], {
|
|
@@ -2864,9 +2927,9 @@ var GeminiDriver = class {
|
|
|
2864
2927
|
});
|
|
2865
2928
|
}
|
|
2866
2929
|
writeGeminiSettings(ctx) {
|
|
2867
|
-
const geminiDir =
|
|
2930
|
+
const geminiDir = path8.join(ctx.workingDirectory, ".gemini");
|
|
2868
2931
|
mkdirSync3(geminiDir, { recursive: true });
|
|
2869
|
-
const settingsPath =
|
|
2932
|
+
const settingsPath = path8.join(geminiDir, "settings.json");
|
|
2870
2933
|
writeFileSync5(settingsPath, JSON.stringify(this.buildRuntimeActionsMcpSettings(ctx)), "utf8");
|
|
2871
2934
|
}
|
|
2872
2935
|
buildRuntimeActionsMcpSettings(ctx) {
|
|
@@ -2899,9 +2962,9 @@ var GeminiDriver = class {
|
|
|
2899
2962
|
// src/drivers/kimi.ts
|
|
2900
2963
|
import { randomUUID } from "crypto";
|
|
2901
2964
|
import { spawn as spawn6 } from "child_process";
|
|
2902
|
-
import { existsSync as
|
|
2903
|
-
import
|
|
2904
|
-
import
|
|
2965
|
+
import { existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
2966
|
+
import os4 from "os";
|
|
2967
|
+
import path9 from "path";
|
|
2905
2968
|
var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
|
|
2906
2969
|
var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
|
|
2907
2970
|
var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
|
|
@@ -2922,7 +2985,7 @@ var KimiDriver = class {
|
|
|
2922
2985
|
inFlightWake: "steer"
|
|
2923
2986
|
};
|
|
2924
2987
|
communication = {
|
|
2925
|
-
chat: "
|
|
2988
|
+
chat: "slock_cli",
|
|
2926
2989
|
runtimeControl: "mcp_runtime_actions"
|
|
2927
2990
|
};
|
|
2928
2991
|
session = {
|
|
@@ -2935,6 +2998,7 @@ var KimiDriver = class {
|
|
|
2935
2998
|
supportsStdinNotification = true;
|
|
2936
2999
|
mcpToolPrefix = "";
|
|
2937
3000
|
busyDeliveryMode = "direct";
|
|
3001
|
+
usesSlockCliForCommunication = true;
|
|
2938
3002
|
sessionId = null;
|
|
2939
3003
|
sessionAnnounced = false;
|
|
2940
3004
|
promptRequestId = null;
|
|
@@ -2950,7 +3014,8 @@ var KimiDriver = class {
|
|
|
2950
3014
|
ctx.config.authToken || ctx.daemonApiKey,
|
|
2951
3015
|
"--runtime",
|
|
2952
3016
|
"kimi",
|
|
2953
|
-
...ctx.launchId ? ["--launch-id", ctx.launchId] : []
|
|
3017
|
+
...ctx.launchId ? ["--launch-id", ctx.launchId] : [],
|
|
3018
|
+
"--runtime-actions-only"
|
|
2954
3019
|
];
|
|
2955
3020
|
}
|
|
2956
3021
|
spawn(ctx) {
|
|
@@ -2961,10 +3026,10 @@ var KimiDriver = class {
|
|
|
2961
3026
|
const isTsSource = ctx.chatBridgePath.endsWith(".ts");
|
|
2962
3027
|
const command = isTsSource ? "npx" : "node";
|
|
2963
3028
|
const bridgeArgs = this.buildChatBridgeArgs(ctx);
|
|
2964
|
-
const systemPromptPath =
|
|
2965
|
-
const agentFilePath =
|
|
2966
|
-
const mcpConfigPath =
|
|
2967
|
-
if (!isResume || !
|
|
3029
|
+
const systemPromptPath = path9.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
|
|
3030
|
+
const agentFilePath = path9.join(ctx.workingDirectory, KIMI_AGENT_FILE);
|
|
3031
|
+
const mcpConfigPath = path9.join(ctx.workingDirectory, KIMI_MCP_FILE);
|
|
3032
|
+
if (!isResume || !existsSync7(systemPromptPath)) {
|
|
2968
3033
|
writeFileSync6(systemPromptPath, ctx.prompt, "utf8");
|
|
2969
3034
|
}
|
|
2970
3035
|
writeFileSync6(agentFilePath, [
|
|
@@ -2995,7 +3060,7 @@ var KimiDriver = class {
|
|
|
2995
3060
|
if (ctx.config.model && ctx.config.model !== "default") {
|
|
2996
3061
|
args.push("--model", ctx.config.model);
|
|
2997
3062
|
}
|
|
2998
|
-
const spawnEnv =
|
|
3063
|
+
const spawnEnv = prepareCliTransport(ctx, { NO_COLOR: "1" }).spawnEnv;
|
|
2999
3064
|
const proc = spawn6("kimi", args, {
|
|
3000
3065
|
cwd: ctx.workingDirectory,
|
|
3001
3066
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -3102,10 +3167,10 @@ var KimiDriver = class {
|
|
|
3102
3167
|
});
|
|
3103
3168
|
}
|
|
3104
3169
|
buildSystemPrompt(config, _agentId) {
|
|
3105
|
-
return
|
|
3170
|
+
return buildCliTransportSystemPrompt(config, {
|
|
3106
3171
|
toolPrefix: "",
|
|
3107
3172
|
extraCriticalRules: [
|
|
3108
|
-
"-
|
|
3173
|
+
"- Runtime Profile migration completion is the only exception to CLI-only operation: when a migration notice tells you to acknowledge with `runtime_profile_migration_done`, call the `runtime_profile_migration_done` tool with the exact `migration_key`; do not use `slock` CLI or reply in chat as the acknowledgment."
|
|
3109
3174
|
],
|
|
3110
3175
|
postStartupNotes: [],
|
|
3111
3176
|
includeStdinNotificationSection: true,
|
|
@@ -3116,8 +3181,8 @@ var KimiDriver = class {
|
|
|
3116
3181
|
return detectKimiModels();
|
|
3117
3182
|
}
|
|
3118
3183
|
};
|
|
3119
|
-
function detectKimiModels(home =
|
|
3120
|
-
const configPath =
|
|
3184
|
+
function detectKimiModels(home = os4.homedir()) {
|
|
3185
|
+
const configPath = path9.join(home, ".kimi", "config.toml");
|
|
3121
3186
|
let raw;
|
|
3122
3187
|
try {
|
|
3123
3188
|
raw = readFileSync3(configPath, "utf8");
|
|
@@ -3145,8 +3210,8 @@ function detectKimiModels(home = os3.homedir()) {
|
|
|
3145
3210
|
// src/drivers/opencode.ts
|
|
3146
3211
|
import { spawn as spawn7, spawnSync as spawnSync2 } from "child_process";
|
|
3147
3212
|
import { readFileSync as readFileSync4 } from "fs";
|
|
3148
|
-
import
|
|
3149
|
-
import
|
|
3213
|
+
import os5 from "os";
|
|
3214
|
+
import path10 from "path";
|
|
3150
3215
|
var CHAT_MCP_SERVER_NAME = "chat";
|
|
3151
3216
|
var CHAT_MCP_TOOL_PREFIX = `${CHAT_MCP_SERVER_NAME}_`;
|
|
3152
3217
|
var SLOCK_AGENT_NAME = "slock";
|
|
@@ -3193,8 +3258,8 @@ function parseUserOpenCodeConfig(ctx) {
|
|
|
3193
3258
|
const raw = ctx.config.envVars?.OPENCODE_CONFIG_CONTENT;
|
|
3194
3259
|
return parseOpenCodeConfigContent(raw);
|
|
3195
3260
|
}
|
|
3196
|
-
function readLocalOpenCodeConfig(home =
|
|
3197
|
-
const configPath =
|
|
3261
|
+
function readLocalOpenCodeConfig(home = os5.homedir()) {
|
|
3262
|
+
const configPath = path10.join(home, ".config", "opencode", "opencode.json");
|
|
3198
3263
|
try {
|
|
3199
3264
|
return parseOpenCodeConfigContent(readFileSync4(configPath, "utf8"));
|
|
3200
3265
|
} catch {
|
|
@@ -3242,7 +3307,7 @@ function mergeOpenCodeConfigs(localConfig, envConfig) {
|
|
|
3242
3307
|
}
|
|
3243
3308
|
};
|
|
3244
3309
|
}
|
|
3245
|
-
function buildOpenCodeConfig(ctx, home =
|
|
3310
|
+
function buildOpenCodeConfig(ctx, home = os5.homedir()) {
|
|
3246
3311
|
const userConfig = mergeOpenCodeConfigs(readLocalOpenCodeConfig(home), parseUserOpenCodeConfig(ctx));
|
|
3247
3312
|
const userAgents = recordField(userConfig.agent);
|
|
3248
3313
|
const userSlockAgent = recordField(userAgents[SLOCK_AGENT_NAME]);
|
|
@@ -3267,7 +3332,7 @@ function buildOpenCodeConfig(ctx, home = os4.homedir()) {
|
|
|
3267
3332
|
}
|
|
3268
3333
|
};
|
|
3269
3334
|
}
|
|
3270
|
-
function buildOpenCodeLaunchOptions(ctx, home =
|
|
3335
|
+
function buildOpenCodeLaunchOptions(ctx, home = os5.homedir()) {
|
|
3271
3336
|
const slock = prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
3272
3337
|
const config = buildOpenCodeConfig(ctx, home);
|
|
3273
3338
|
const env = {
|
|
@@ -3363,7 +3428,7 @@ function formatOpenCodeLabelToken(token) {
|
|
|
3363
3428
|
if (/^\d/.test(token)) return token;
|
|
3364
3429
|
return normalized.charAt(0).toUpperCase() + normalized.slice(1);
|
|
3365
3430
|
}
|
|
3366
|
-
function detectOpenCodeModels(home =
|
|
3431
|
+
function detectOpenCodeModels(home = os5.homedir(), runCommand = runOpenCodeModelsCommand) {
|
|
3367
3432
|
const commandResult = runCommand(home);
|
|
3368
3433
|
if (commandResult.error || commandResult.status !== 0) return null;
|
|
3369
3434
|
return parseOpenCodeModelsOutput(commandResult.stdout);
|
|
@@ -3547,7 +3612,7 @@ function getDriver(runtimeId) {
|
|
|
3547
3612
|
|
|
3548
3613
|
// src/workspaces.ts
|
|
3549
3614
|
import { readdir, rm, stat } from "fs/promises";
|
|
3550
|
-
import
|
|
3615
|
+
import path11 from "path";
|
|
3551
3616
|
function isValidWorkspaceDirectoryName(directoryName) {
|
|
3552
3617
|
return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
|
|
3553
3618
|
}
|
|
@@ -3555,7 +3620,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
|
|
|
3555
3620
|
if (!isValidWorkspaceDirectoryName(directoryName)) {
|
|
3556
3621
|
return null;
|
|
3557
3622
|
}
|
|
3558
|
-
return
|
|
3623
|
+
return path11.join(dataDir, directoryName);
|
|
3559
3624
|
}
|
|
3560
3625
|
function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
|
|
3561
3626
|
return {
|
|
@@ -3604,7 +3669,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
|
|
|
3604
3669
|
return summary;
|
|
3605
3670
|
}
|
|
3606
3671
|
const childSummaries = await Promise.all(
|
|
3607
|
-
entries.map((entry) => summarizeWorkspaceEntry(
|
|
3672
|
+
entries.map((entry) => summarizeWorkspaceEntry(path11.join(dirPath, entry.name), entry))
|
|
3608
3673
|
);
|
|
3609
3674
|
for (const childSummary of childSummaries) {
|
|
3610
3675
|
summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
|
|
@@ -3623,7 +3688,7 @@ async function scanWorkspaceDirectories(dataDir) {
|
|
|
3623
3688
|
if (!entry.isDirectory()) {
|
|
3624
3689
|
return null;
|
|
3625
3690
|
}
|
|
3626
|
-
const dirPath =
|
|
3691
|
+
const dirPath = path11.join(dataDir, entry.name);
|
|
3627
3692
|
try {
|
|
3628
3693
|
const summary = await summarizeWorkspaceDirectory(dirPath);
|
|
3629
3694
|
return {
|
|
@@ -3780,6 +3845,12 @@ function readNonNegativeIntegerEnv(name, fallback) {
|
|
|
3780
3845
|
if (!Number.isFinite(parsed) || parsed < 0) return fallback;
|
|
3781
3846
|
return Math.floor(parsed);
|
|
3782
3847
|
}
|
|
3848
|
+
function stalledRecoverySigtermTimeoutMs() {
|
|
3849
|
+
return readNonNegativeIntegerEnv(
|
|
3850
|
+
"SLOCK_DAEMON_STALLED_RECOVERY_SIGTERM_TIMEOUT_MS",
|
|
3851
|
+
DEFAULT_STALLED_RECOVERY_SIGTERM_TIMEOUT_MS
|
|
3852
|
+
);
|
|
3853
|
+
}
|
|
3783
3854
|
function toLocalTime(iso) {
|
|
3784
3855
|
const d = new Date(iso);
|
|
3785
3856
|
if (isNaN(d.getTime())) return iso;
|
|
@@ -3814,19 +3885,19 @@ function findSessionJsonl(root, predicate) {
|
|
|
3814
3885
|
if (depth < 0 || visited >= maxEntries) return null;
|
|
3815
3886
|
let entries;
|
|
3816
3887
|
try {
|
|
3817
|
-
entries =
|
|
3888
|
+
entries = readdirSync2(dir, { withFileTypes: true }).sort((a, b) => b.name.localeCompare(a.name));
|
|
3818
3889
|
} catch {
|
|
3819
3890
|
return null;
|
|
3820
3891
|
}
|
|
3821
3892
|
for (const entry of entries) {
|
|
3822
3893
|
if (++visited > maxEntries) return null;
|
|
3823
3894
|
if (!entry.isFile() || !predicate(entry.name)) continue;
|
|
3824
|
-
return
|
|
3895
|
+
return path12.join(dir, entry.name);
|
|
3825
3896
|
}
|
|
3826
3897
|
for (const entry of entries) {
|
|
3827
3898
|
if (++visited > maxEntries) return null;
|
|
3828
3899
|
if (!entry.isDirectory()) continue;
|
|
3829
|
-
const found = visit(
|
|
3900
|
+
const found = visit(path12.join(dir, entry.name), depth - 1);
|
|
3830
3901
|
if (found) return found;
|
|
3831
3902
|
}
|
|
3832
3903
|
return null;
|
|
@@ -3839,9 +3910,9 @@ function safeSessionFilename(value) {
|
|
|
3839
3910
|
}
|
|
3840
3911
|
function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
3841
3912
|
try {
|
|
3842
|
-
const dir =
|
|
3913
|
+
const dir = path12.join(fallbackDir, ".slock", "runtime-sessions");
|
|
3843
3914
|
mkdirSync4(dir, { recursive: true });
|
|
3844
|
-
const filePath =
|
|
3915
|
+
const filePath = path12.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
|
|
3845
3916
|
writeFileSync7(filePath, JSON.stringify({
|
|
3846
3917
|
type: "runtime_session_handoff",
|
|
3847
3918
|
runtime,
|
|
@@ -3860,8 +3931,8 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
|
3860
3931
|
return null;
|
|
3861
3932
|
}
|
|
3862
3933
|
}
|
|
3863
|
-
function resolveRuntimeSessionRef(runtime, sessionId, homeDir =
|
|
3864
|
-
const directPath =
|
|
3934
|
+
function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), fallbackDir) {
|
|
3935
|
+
const directPath = path12.isAbsolute(sessionId) ? sessionId : null;
|
|
3865
3936
|
if (directPath) {
|
|
3866
3937
|
try {
|
|
3867
3938
|
if (statSync2(directPath).isFile()) {
|
|
@@ -3870,7 +3941,7 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), f
|
|
|
3870
3941
|
} catch {
|
|
3871
3942
|
}
|
|
3872
3943
|
}
|
|
3873
|
-
const resolvedPath = runtime === "claude" ? findSessionJsonl(
|
|
3944
|
+
const resolvedPath = runtime === "claude" ? findSessionJsonl(path12.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`) : runtime === "codex" ? findSessionJsonl(path12.join(homeDir, ".codex", "sessions"), (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId)) : null;
|
|
3874
3945
|
if (!resolvedPath && fallbackDir) {
|
|
3875
3946
|
const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
|
|
3876
3947
|
if (fallback) return fallback;
|
|
@@ -4011,6 +4082,7 @@ var TRAJECTORY_COALESCE_MS = 350;
|
|
|
4011
4082
|
var ACTIVITY_HEARTBEAT_MS = 6e4;
|
|
4012
4083
|
var COMPACTION_STALE_MS = 5 * 6e4;
|
|
4013
4084
|
var RUNTIME_PROGRESS_STALE_MS = 15 * 6e4;
|
|
4085
|
+
var DEFAULT_STALLED_RECOVERY_SIGTERM_TIMEOUT_MS = 1e4;
|
|
4014
4086
|
var MAX_STDOUT_LINES = 8;
|
|
4015
4087
|
var MAX_STDOUT_LINE_LENGTH = 240;
|
|
4016
4088
|
var MAX_STDERR_LINES = 8;
|
|
@@ -4410,6 +4482,17 @@ Success = user starts useful collaboration and setup progresses,
|
|
|
4410
4482
|
not finishing a long onboarding conversation in one channel.
|
|
4411
4483
|
`;
|
|
4412
4484
|
}
|
|
4485
|
+
function createRuntimeTraceCounters() {
|
|
4486
|
+
return {
|
|
4487
|
+
events: 0,
|
|
4488
|
+
toolCalls: 0,
|
|
4489
|
+
toolOutputs: 0,
|
|
4490
|
+
compactionStarts: 0,
|
|
4491
|
+
compactionFinishes: 0,
|
|
4492
|
+
textEvents: 0,
|
|
4493
|
+
thinkingEvents: 0
|
|
4494
|
+
};
|
|
4495
|
+
}
|
|
4413
4496
|
function createGatedSteeringState() {
|
|
4414
4497
|
return {
|
|
4415
4498
|
phase: "idle",
|
|
@@ -4588,8 +4671,111 @@ function buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes) {
|
|
|
4588
4671
|
outstandingToolUses: ap.gatedSteering.outstandingToolUses,
|
|
4589
4672
|
compacting: ap.gatedSteering.compacting,
|
|
4590
4673
|
recentStderrCount: ap.recentStderr.length,
|
|
4591
|
-
recentStdoutCount: ap.recentStdout.length
|
|
4674
|
+
recentStdoutCount: ap.recentStdout.length,
|
|
4675
|
+
...runtimeTraceCounterAttrs(ap)
|
|
4676
|
+
}
|
|
4677
|
+
};
|
|
4678
|
+
}
|
|
4679
|
+
function bucketBytes(value) {
|
|
4680
|
+
const bytes = typeof value === "string" ? Buffer.byteLength(value, "utf8") : Math.max(0, Math.floor(value ?? 0));
|
|
4681
|
+
if (bytes === 0) return "0";
|
|
4682
|
+
if (bytes < 1024) return "<1KB";
|
|
4683
|
+
if (bytes < 10 * 1024) return "1KB-10KB";
|
|
4684
|
+
if (bytes < 100 * 1024) return "10KB-100KB";
|
|
4685
|
+
if (bytes < 1024 * 1024) return "100KB-1MB";
|
|
4686
|
+
return "1MB+";
|
|
4687
|
+
}
|
|
4688
|
+
function attachmentBytesBucket(bytes, knownCount) {
|
|
4689
|
+
return knownCount > 0 ? bucketBytes(bytes) : "unknown";
|
|
4690
|
+
}
|
|
4691
|
+
function summarizeMessageInputBytes(messages) {
|
|
4692
|
+
if (!messages || messages.length === 0) {
|
|
4693
|
+
return {
|
|
4694
|
+
runtime_input_messages_count: 0,
|
|
4695
|
+
runtime_input_messages_content_bytes_bucket: "0",
|
|
4696
|
+
runtime_input_attachments_count: 0,
|
|
4697
|
+
runtime_input_image_attachments_count: 0,
|
|
4698
|
+
runtime_input_attachments_size_known_count: 0,
|
|
4699
|
+
runtime_input_attachments_bytes_bucket: "0",
|
|
4700
|
+
runtime_input_image_attachments_size_known_count: 0,
|
|
4701
|
+
runtime_input_image_attachments_bytes_bucket: "0",
|
|
4702
|
+
runtime_input_largest_attachment_bytes_bucket: "0",
|
|
4703
|
+
runtime_input_thread_context_messages_count: 0,
|
|
4704
|
+
runtime_input_thread_context_content_bytes_bucket: "0"
|
|
4705
|
+
};
|
|
4706
|
+
}
|
|
4707
|
+
let contentBytes = 0;
|
|
4708
|
+
let attachmentCount = 0;
|
|
4709
|
+
let imageAttachmentCount = 0;
|
|
4710
|
+
let attachmentSizeKnownCount = 0;
|
|
4711
|
+
let attachmentBytes = 0;
|
|
4712
|
+
let imageAttachmentSizeKnownCount = 0;
|
|
4713
|
+
let imageAttachmentBytes = 0;
|
|
4714
|
+
let largestAttachmentBytes = 0;
|
|
4715
|
+
let threadContextMessages = 0;
|
|
4716
|
+
let threadContextContentBytes = 0;
|
|
4717
|
+
for (const message of messages) {
|
|
4718
|
+
contentBytes += Buffer.byteLength(message.content || "", "utf8");
|
|
4719
|
+
for (const attachment of message.attachments || []) {
|
|
4720
|
+
attachmentCount++;
|
|
4721
|
+
if (typeof attachment.sizeBytes === "number" && Number.isFinite(attachment.sizeBytes) && attachment.sizeBytes >= 0) {
|
|
4722
|
+
attachmentSizeKnownCount++;
|
|
4723
|
+
attachmentBytes += attachment.sizeBytes;
|
|
4724
|
+
largestAttachmentBytes = Math.max(largestAttachmentBytes, attachment.sizeBytes);
|
|
4725
|
+
}
|
|
4726
|
+
if (attachment.mimeType?.startsWith("image/")) {
|
|
4727
|
+
imageAttachmentCount++;
|
|
4728
|
+
if (typeof attachment.sizeBytes === "number" && Number.isFinite(attachment.sizeBytes) && attachment.sizeBytes >= 0) {
|
|
4729
|
+
imageAttachmentSizeKnownCount++;
|
|
4730
|
+
imageAttachmentBytes += attachment.sizeBytes;
|
|
4731
|
+
}
|
|
4732
|
+
}
|
|
4733
|
+
}
|
|
4734
|
+
const joinContext = message.thread_join_context;
|
|
4735
|
+
if (joinContext) {
|
|
4736
|
+
const contextMessages = [joinContext.parent_message, ...joinContext.recent_messages];
|
|
4737
|
+
threadContextMessages += contextMessages.length;
|
|
4738
|
+
for (const contextMessage of contextMessages) {
|
|
4739
|
+
threadContextContentBytes += Buffer.byteLength(contextMessage.content || "", "utf8");
|
|
4740
|
+
}
|
|
4592
4741
|
}
|
|
4742
|
+
}
|
|
4743
|
+
return {
|
|
4744
|
+
runtime_input_messages_count: messages.length,
|
|
4745
|
+
runtime_input_messages_content_bytes_bucket: bucketBytes(contentBytes),
|
|
4746
|
+
runtime_input_attachments_count: attachmentCount,
|
|
4747
|
+
runtime_input_image_attachments_count: imageAttachmentCount,
|
|
4748
|
+
runtime_input_attachments_size_known_count: attachmentSizeKnownCount,
|
|
4749
|
+
runtime_input_attachments_bytes_bucket: attachmentCount > 0 ? attachmentBytesBucket(attachmentBytes, attachmentSizeKnownCount) : "0",
|
|
4750
|
+
runtime_input_image_attachments_size_known_count: imageAttachmentSizeKnownCount,
|
|
4751
|
+
runtime_input_image_attachments_bytes_bucket: imageAttachmentCount > 0 ? attachmentBytesBucket(imageAttachmentBytes, imageAttachmentSizeKnownCount) : "0",
|
|
4752
|
+
runtime_input_largest_attachment_bytes_bucket: attachmentCount > 0 ? attachmentBytesBucket(largestAttachmentBytes, attachmentSizeKnownCount) : "0",
|
|
4753
|
+
runtime_input_thread_context_messages_count: threadContextMessages,
|
|
4754
|
+
runtime_input_thread_context_content_bytes_bucket: bucketBytes(threadContextContentBytes)
|
|
4755
|
+
};
|
|
4756
|
+
}
|
|
4757
|
+
function buildRuntimeInputTraceAttrs(opts) {
|
|
4758
|
+
return {
|
|
4759
|
+
runtime_input_source: opts.source,
|
|
4760
|
+
runtime_input_prompt_bytes_bucket: bucketBytes(opts.prompt),
|
|
4761
|
+
runtime_input_standing_prompt_bytes_bucket: bucketBytes(opts.standingPrompt),
|
|
4762
|
+
runtime_input_resume_prompt_present: Boolean(opts.resumePrompt),
|
|
4763
|
+
runtime_input_resume_prompt_bytes_bucket: bucketBytes(opts.resumePrompt),
|
|
4764
|
+
runtime_input_session_present: opts.sessionIdPresent,
|
|
4765
|
+
runtime_input_native_standing_prompt_present: opts.nativeStandingPrompt,
|
|
4766
|
+
runtime_input_unread_channels_count: opts.unreadSummary ? Object.keys(opts.unreadSummary).length : 0,
|
|
4767
|
+
...summarizeMessageInputBytes(opts.messages)
|
|
4768
|
+
};
|
|
4769
|
+
}
|
|
4770
|
+
function runtimeTraceCounterAttrs(ap) {
|
|
4771
|
+
return {
|
|
4772
|
+
runtime_events_count: ap.runtimeTraceCounters.events,
|
|
4773
|
+
runtime_tool_calls_count: ap.runtimeTraceCounters.toolCalls,
|
|
4774
|
+
runtime_tool_outputs_count: ap.runtimeTraceCounters.toolOutputs,
|
|
4775
|
+
runtime_compaction_starts_count: ap.runtimeTraceCounters.compactionStarts,
|
|
4776
|
+
runtime_compaction_finishes_count: ap.runtimeTraceCounters.compactionFinishes,
|
|
4777
|
+
runtime_text_events_count: ap.runtimeTraceCounters.textEvents,
|
|
4778
|
+
runtime_thinking_events_count: ap.runtimeTraceCounters.thinkingEvents
|
|
4593
4779
|
};
|
|
4594
4780
|
}
|
|
4595
4781
|
function getMessageDeliveryText(driver) {
|
|
@@ -4645,7 +4831,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
4645
4831
|
this.daemonApiKey = daemonApiKey;
|
|
4646
4832
|
this.serverUrl = opts.serverUrl;
|
|
4647
4833
|
this.dataDir = opts.dataDir || resolveSlockHomePath("agents");
|
|
4648
|
-
this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir ||
|
|
4834
|
+
this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os6.homedir();
|
|
4649
4835
|
this.driverResolver = opts.driverResolver || getDriver;
|
|
4650
4836
|
this.defaultAgentEnvVarsProvider = opts.defaultAgentEnvVarsProvider || null;
|
|
4651
4837
|
this.tracer = opts.tracer ?? noopTracer;
|
|
@@ -4889,26 +5075,26 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
4889
5075
|
this.recordDaemonTrace("daemon.agent.spawn.started", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId));
|
|
4890
5076
|
try {
|
|
4891
5077
|
const driver = this.driverResolver(config.runtime || "claude");
|
|
4892
|
-
const agentDataDir =
|
|
5078
|
+
const agentDataDir = path12.join(this.dataDir, agentId);
|
|
4893
5079
|
await mkdir(agentDataDir, { recursive: true });
|
|
4894
5080
|
const runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
|
|
4895
|
-
const memoryMdPath =
|
|
5081
|
+
const memoryMdPath = path12.join(agentDataDir, "MEMORY.md");
|
|
4896
5082
|
try {
|
|
4897
5083
|
await access(memoryMdPath);
|
|
4898
5084
|
} catch {
|
|
4899
5085
|
const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
|
|
4900
5086
|
await writeFile(memoryMdPath, initialMemoryMd);
|
|
4901
5087
|
}
|
|
4902
|
-
const notesDir =
|
|
5088
|
+
const notesDir = path12.join(agentDataDir, "notes");
|
|
4903
5089
|
await mkdir(notesDir, { recursive: true });
|
|
4904
5090
|
if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
|
|
4905
5091
|
const seedFiles = buildOnboardingSeedFiles();
|
|
4906
5092
|
for (const { relativePath, content } of seedFiles) {
|
|
4907
|
-
const fullPath =
|
|
5093
|
+
const fullPath = path12.join(agentDataDir, relativePath);
|
|
4908
5094
|
try {
|
|
4909
5095
|
await access(fullPath);
|
|
4910
5096
|
} catch {
|
|
4911
|
-
await mkdir(
|
|
5097
|
+
await mkdir(path12.dirname(fullPath), { recursive: true });
|
|
4912
5098
|
await writeFile(fullPath, content);
|
|
4913
5099
|
}
|
|
4914
5100
|
}
|
|
@@ -4916,17 +5102,21 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
4916
5102
|
const isResume = !!runtimeConfig.sessionId;
|
|
4917
5103
|
const standingPrompt = driver.buildSystemPrompt(runtimeConfig, agentId);
|
|
4918
5104
|
let prompt;
|
|
5105
|
+
let promptSource;
|
|
4919
5106
|
if (runtimeConfig.runtimeProfileControl && !wakeMessage) {
|
|
4920
5107
|
prompt = driver.supportsNativeStandingPrompt ? NATIVE_STANDING_PROMPT_STARTUP_INPUT : formatRuntimeProfileControlStartupInput(runtimeConfig.runtimeProfileControl, driver);
|
|
5108
|
+
promptSource = "runtime_profile_control";
|
|
4921
5109
|
} else if (isResume && resumePrompt) {
|
|
4922
5110
|
prompt = resumePrompt;
|
|
4923
5111
|
prompt += getBusyDeliveryNote(driver);
|
|
5112
|
+
promptSource = "resume_prompt";
|
|
4924
5113
|
} else if (wakeMessage) {
|
|
4925
5114
|
const runtimeProfileControlPrompt = formatRuntimeProfileControlPrompt([wakeMessage]);
|
|
4926
5115
|
const channelLabel = formatChannelLabel(wakeMessage);
|
|
4927
5116
|
prompt = runtimeProfileControlPrompt ?? `New message received:
|
|
4928
5117
|
|
|
4929
5118
|
${formatIncomingMessage(wakeMessage, driver)}`;
|
|
5119
|
+
promptSource = runtimeProfileControlPrompt ? "runtime_profile_control_message" : "wake_message";
|
|
4930
5120
|
if (!runtimeProfileControlPrompt && unreadSummary && Object.keys(unreadSummary).length > 0) {
|
|
4931
5121
|
const otherUnread = Object.entries(unreadSummary).filter(([key]) => key !== channelLabel);
|
|
4932
5122
|
if (otherUnread.length > 0) {
|
|
@@ -4960,12 +5150,25 @@ IMPORTANT: If the message requires multi-step work (e.g. research, code changes,
|
|
|
4960
5150
|
prompt += `
|
|
4961
5151
|
|
|
4962
5152
|
Use ${communicationCommand(driver, "read_history")} to catch up on the channels listed above, then stop. Read each listed channel at most once unless a read fails. Do NOT call ${communicationCommand(driver, "check_messages")} in this mode. If the history reveals a direct request, assignment, @mention, review request, or task clearly addressed to you, switch into active handling instead of stopping: ${dynamicReplyInstruction(driver)} and ${dynamicClaimInstruction(driver)} before starting work. Otherwise, do NOT send any message in this mode. ${getMessageDeliveryText(driver)}`;
|
|
5153
|
+
promptSource = "resume_unread_summary";
|
|
4963
5154
|
} else if (isResume) {
|
|
4964
5155
|
prompt = `No new messages while you were away. Nothing to do \u2014 just stop. ${getMessageDeliveryText(driver)}`;
|
|
4965
5156
|
prompt += getBusyDeliveryNote(driver);
|
|
5157
|
+
promptSource = "resume_empty";
|
|
4966
5158
|
} else {
|
|
4967
5159
|
prompt = driver.supportsNativeStandingPrompt ? NATIVE_STANDING_PROMPT_STARTUP_INPUT : standingPrompt;
|
|
5160
|
+
promptSource = "cold_start";
|
|
4968
5161
|
}
|
|
5162
|
+
const runtimeInputTraceAttrs = buildRuntimeInputTraceAttrs({
|
|
5163
|
+
source: promptSource,
|
|
5164
|
+
prompt,
|
|
5165
|
+
standingPrompt,
|
|
5166
|
+
resumePrompt,
|
|
5167
|
+
messages: wakeMessage ? [wakeMessage] : void 0,
|
|
5168
|
+
unreadSummary,
|
|
5169
|
+
sessionIdPresent: isResume,
|
|
5170
|
+
nativeStandingPrompt: Boolean(driver.supportsNativeStandingPrompt)
|
|
5171
|
+
});
|
|
4969
5172
|
const effectiveConfig = await this.buildSpawnConfig(agentId, runtimeConfig);
|
|
4970
5173
|
const canDeferEmptyStart = driver.deferSpawnUntilMessage === true && !wakeMessage && !runtimeConfig.runtimeProfileControl && (!unreadSummary || Object.keys(unreadSummary).length === 0);
|
|
4971
5174
|
if (canDeferEmptyStart) {
|
|
@@ -5026,6 +5229,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5026
5229
|
lastRuntimeEventAt: Date.now(),
|
|
5027
5230
|
runtimeProgressStaleSince: null,
|
|
5028
5231
|
runtimeTraceSpan: null,
|
|
5232
|
+
runtimeTraceCounters: createRuntimeTraceCounters(),
|
|
5029
5233
|
lastActivity: "",
|
|
5030
5234
|
lastActivityDetail: "",
|
|
5031
5235
|
activityClientSeq: 0,
|
|
@@ -5036,6 +5240,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5036
5240
|
exitCode: null,
|
|
5037
5241
|
exitSignal: null,
|
|
5038
5242
|
expectedTerminationReason: null,
|
|
5243
|
+
stalledRecoverySigtermTimer: null,
|
|
5039
5244
|
runtimeProfileTurnControl: runtimeConfig.runtimeProfileControl ? runtimeProfileTurnControl(runtimeConfig.runtimeProfileControl.kind, runtimeConfig.runtimeProfileControl.key, "agent_config") : null,
|
|
5040
5245
|
pendingTrajectory: null,
|
|
5041
5246
|
gatedSteering: createGatedSteeringState()
|
|
@@ -5047,7 +5252,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5047
5252
|
sessionId: effectiveConfig.sessionId || null,
|
|
5048
5253
|
launchId: launchId || null
|
|
5049
5254
|
});
|
|
5050
|
-
this.startRuntimeTrace(agentId, agentProcess, "spawn", wakeMessage ? [wakeMessage] : void 0);
|
|
5255
|
+
this.startRuntimeTrace(agentId, agentProcess, "spawn", wakeMessage ? [wakeMessage] : void 0, runtimeInputTraceAttrs);
|
|
5051
5256
|
this.agentsStarting.delete(agentId);
|
|
5052
5257
|
if (config.runtimeProfileControl) {
|
|
5053
5258
|
this.ackInjectedRuntimeProfileControl(agentId, config.runtimeProfileControl, agentProcess.launchId);
|
|
@@ -5129,6 +5334,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5129
5334
|
if (ap.activityHeartbeat) {
|
|
5130
5335
|
clearInterval(ap.activityHeartbeat);
|
|
5131
5336
|
}
|
|
5337
|
+
this.clearStalledRecoverySigtermWatchdog(ap);
|
|
5132
5338
|
const finalCode = ap.exitCode ?? code;
|
|
5133
5339
|
const finalSignal = ap.exitSignal ?? signal;
|
|
5134
5340
|
const expectedTermination = Boolean(ap.expectedTerminationReason);
|
|
@@ -5141,6 +5347,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5141
5347
|
expectedTerminationReason: ap.expectedTerminationReason || void 0,
|
|
5142
5348
|
exitCode: finalCode,
|
|
5143
5349
|
exitSignal: finalSignal,
|
|
5350
|
+
...runtimeTraceCounterAttrs(ap),
|
|
5144
5351
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "process_exit")
|
|
5145
5352
|
});
|
|
5146
5353
|
if (processEndedCleanly) {
|
|
@@ -5363,6 +5570,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5363
5570
|
clearInterval(ap.activityHeartbeat);
|
|
5364
5571
|
}
|
|
5365
5572
|
this.clearCompactionWatchdog(ap);
|
|
5573
|
+
this.clearStalledRecoverySigtermWatchdog(ap);
|
|
5366
5574
|
this.agents.delete(agentId);
|
|
5367
5575
|
this.processExitTraceAttrs.set(ap.process, {
|
|
5368
5576
|
stop_source: silent ? "daemon_internal" : "explicit_request",
|
|
@@ -5568,7 +5776,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5568
5776
|
return true;
|
|
5569
5777
|
}
|
|
5570
5778
|
async resetWorkspace(agentId) {
|
|
5571
|
-
const agentDataDir =
|
|
5779
|
+
const agentDataDir = path12.join(this.dataDir, agentId);
|
|
5572
5780
|
try {
|
|
5573
5781
|
await rm2(agentDataDir, { recursive: true, force: true });
|
|
5574
5782
|
logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
|
|
@@ -5605,7 +5813,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5605
5813
|
return result;
|
|
5606
5814
|
}
|
|
5607
5815
|
buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
|
|
5608
|
-
const workspacePath =
|
|
5816
|
+
const workspacePath = path12.join(this.dataDir, agentId);
|
|
5609
5817
|
return {
|
|
5610
5818
|
agentId,
|
|
5611
5819
|
launchId,
|
|
@@ -5853,7 +6061,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5853
6061
|
}
|
|
5854
6062
|
// Workspace file browsing
|
|
5855
6063
|
async getFileTree(agentId, dirPath) {
|
|
5856
|
-
const agentDir =
|
|
6064
|
+
const agentDir = path12.join(this.dataDir, agentId);
|
|
5857
6065
|
try {
|
|
5858
6066
|
await stat2(agentDir);
|
|
5859
6067
|
} catch {
|
|
@@ -5861,8 +6069,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5861
6069
|
}
|
|
5862
6070
|
let targetDir = agentDir;
|
|
5863
6071
|
if (dirPath) {
|
|
5864
|
-
const resolved =
|
|
5865
|
-
if (!resolved.startsWith(agentDir +
|
|
6072
|
+
const resolved = path12.resolve(agentDir, dirPath);
|
|
6073
|
+
if (!resolved.startsWith(agentDir + path12.sep) && resolved !== agentDir) {
|
|
5866
6074
|
return [];
|
|
5867
6075
|
}
|
|
5868
6076
|
targetDir = resolved;
|
|
@@ -5870,14 +6078,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5870
6078
|
return this.listDirectoryChildren(targetDir, agentDir);
|
|
5871
6079
|
}
|
|
5872
6080
|
async readFile(agentId, filePath) {
|
|
5873
|
-
const agentDir =
|
|
5874
|
-
const resolved =
|
|
5875
|
-
if (!resolved.startsWith(agentDir +
|
|
6081
|
+
const agentDir = path12.join(this.dataDir, agentId);
|
|
6082
|
+
const resolved = path12.resolve(agentDir, filePath);
|
|
6083
|
+
if (!resolved.startsWith(agentDir + path12.sep) && resolved !== agentDir) {
|
|
5876
6084
|
throw new Error("Access denied");
|
|
5877
6085
|
}
|
|
5878
6086
|
const info = await stat2(resolved);
|
|
5879
6087
|
if (info.isDirectory()) throw new Error("Cannot read a directory");
|
|
5880
|
-
const ext =
|
|
6088
|
+
const ext = path12.extname(resolved).toLowerCase();
|
|
5881
6089
|
if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
|
|
5882
6090
|
if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
|
|
5883
6091
|
const content = await readFile(resolved, "utf-8");
|
|
@@ -5911,14 +6119,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5911
6119
|
async listSkills(agentId, runtimeHint) {
|
|
5912
6120
|
const agent = this.agents.get(agentId);
|
|
5913
6121
|
const runtime = runtimeHint || agent?.config.runtime || "claude";
|
|
5914
|
-
const home =
|
|
5915
|
-
const workspaceDir =
|
|
6122
|
+
const home = os6.homedir();
|
|
6123
|
+
const workspaceDir = path12.join(this.dataDir, agentId);
|
|
5916
6124
|
const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
|
|
5917
6125
|
const globalResults = await Promise.all(
|
|
5918
|
-
paths.global.map((p) => this.scanSkillsDir(
|
|
6126
|
+
paths.global.map((p) => this.scanSkillsDir(path12.join(home, p)))
|
|
5919
6127
|
);
|
|
5920
6128
|
const workspaceResults = await Promise.all(
|
|
5921
|
-
paths.workspace.map((p) => this.scanSkillsDir(
|
|
6129
|
+
paths.workspace.map((p) => this.scanSkillsDir(path12.join(workspaceDir, p)))
|
|
5922
6130
|
);
|
|
5923
6131
|
const dedup = (skills) => {
|
|
5924
6132
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -5947,7 +6155,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5947
6155
|
const skills = [];
|
|
5948
6156
|
for (const entry of entries) {
|
|
5949
6157
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
5950
|
-
const skillMd =
|
|
6158
|
+
const skillMd = path12.join(dir, entry.name, "SKILL.md");
|
|
5951
6159
|
try {
|
|
5952
6160
|
const content = await readFile(skillMd, "utf-8");
|
|
5953
6161
|
const skill = this.parseSkillMd(entry.name, content);
|
|
@@ -5958,7 +6166,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5958
6166
|
} else if (entry.name.endsWith(".md")) {
|
|
5959
6167
|
const cmdName = entry.name.replace(/\.md$/, "");
|
|
5960
6168
|
try {
|
|
5961
|
-
const content = await readFile(
|
|
6169
|
+
const content = await readFile(path12.join(dir, entry.name), "utf-8");
|
|
5962
6170
|
const skill = this.parseSkillMd(cmdName, content);
|
|
5963
6171
|
skill.sourcePath = dir;
|
|
5964
6172
|
skills.push(skill);
|
|
@@ -5993,6 +6201,12 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5993
6201
|
/**
|
|
5994
6202
|
* Broadcast an activity change — emits a single agent:activity event that carries
|
|
5995
6203
|
* both the status (for the dot indicator) and trajectory entries (for the activity log).
|
|
6204
|
+
*
|
|
6205
|
+
* TODO(lifecycle-v2/daemon-protocol): split this legacy frame into
|
|
6206
|
+
* structured lifecycle producers. Runtime progress, transient heartbeat,
|
|
6207
|
+
* provider/runtime error, process exit, and user-visible activity entries
|
|
6208
|
+
* should carry explicit reason/correlation/window attrs so the server no
|
|
6209
|
+
* longer infers lifecycle semantics from generic activity strings.
|
|
5996
6210
|
*/
|
|
5997
6211
|
broadcastActivity(agentId, activity, detail, extraTrajectory = [], launchIdOverride) {
|
|
5998
6212
|
const ap = this.agents.get(agentId);
|
|
@@ -6121,6 +6335,64 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6121
6335
|
}
|
|
6122
6336
|
ap.compactionStartedAt = null;
|
|
6123
6337
|
}
|
|
6338
|
+
clearStalledRecoverySigtermWatchdog(ap) {
|
|
6339
|
+
if (!ap.stalledRecoverySigtermTimer) return;
|
|
6340
|
+
clearTimeout(ap.stalledRecoverySigtermTimer);
|
|
6341
|
+
ap.stalledRecoverySigtermTimer = null;
|
|
6342
|
+
}
|
|
6343
|
+
mergeProcessExitTraceAttrs(proc, attrs) {
|
|
6344
|
+
this.processExitTraceAttrs.set(proc, {
|
|
6345
|
+
...this.processExitTraceAttrs.get(proc) ?? {},
|
|
6346
|
+
...attrs
|
|
6347
|
+
});
|
|
6348
|
+
}
|
|
6349
|
+
startStalledRecoverySigtermWatchdog(agentId, ap, runtimeLabel, queuedMessagesAtSignal, staleForMs) {
|
|
6350
|
+
this.clearStalledRecoverySigtermWatchdog(ap);
|
|
6351
|
+
const timeoutMs = stalledRecoverySigtermTimeoutMs();
|
|
6352
|
+
const processAtSignal = ap.process;
|
|
6353
|
+
ap.stalledRecoverySigtermTimer = setTimeout(() => {
|
|
6354
|
+
ap.stalledRecoverySigtermTimer = null;
|
|
6355
|
+
const current = this.agents.get(agentId);
|
|
6356
|
+
if (!current || current !== ap || current.process !== processAtSignal || current.expectedTerminationReason !== "stalled_recovery") {
|
|
6357
|
+
return;
|
|
6358
|
+
}
|
|
6359
|
+
this.mergeProcessExitTraceAttrs(processAtSignal, {
|
|
6360
|
+
stalled_recovery_sigterm_timeout: true,
|
|
6361
|
+
stalled_recovery_sigterm_timeout_ms: timeoutMs
|
|
6362
|
+
});
|
|
6363
|
+
this.recordDaemonTrace("daemon.agent.stalled_recovery.sigterm_timeout", {
|
|
6364
|
+
agentId,
|
|
6365
|
+
launchId: current.launchId || void 0,
|
|
6366
|
+
runtime: current.config.runtime,
|
|
6367
|
+
model: current.config.model,
|
|
6368
|
+
runtime_label: runtimeLabel,
|
|
6369
|
+
queued_messages_count: current.inbox.length,
|
|
6370
|
+
queued_messages_at_signal: queuedMessagesAtSignal,
|
|
6371
|
+
stale_age_ms_at_signal: staleForMs,
|
|
6372
|
+
timeout_ms: timeoutMs,
|
|
6373
|
+
process_pid_present: typeof processAtSignal.pid === "number",
|
|
6374
|
+
session_id_present: Boolean(current.sessionId),
|
|
6375
|
+
supports_stdin_notification: current.driver.supportsStdinNotification,
|
|
6376
|
+
busy_delivery_mode: current.driver.busyDeliveryMode
|
|
6377
|
+
}, "error");
|
|
6378
|
+
logger.warn(
|
|
6379
|
+
`[Agent ${agentId}] Stalled ${runtimeLabel} runtime did not exit after SIGTERM within ${timeoutMs}ms; force killing`
|
|
6380
|
+
);
|
|
6381
|
+
try {
|
|
6382
|
+
processAtSignal.kill("SIGKILL");
|
|
6383
|
+
} catch (err) {
|
|
6384
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
6385
|
+
this.recordDaemonTrace("daemon.agent.stalled_recovery.sigkill_failed", {
|
|
6386
|
+
agentId,
|
|
6387
|
+
launchId: current.launchId || void 0,
|
|
6388
|
+
runtime: current.config.runtime,
|
|
6389
|
+
model: current.config.model,
|
|
6390
|
+
reason
|
|
6391
|
+
}, "error");
|
|
6392
|
+
logger.warn(`[Agent ${agentId}] Failed to force kill stalled ${runtimeLabel} process: ${reason}`);
|
|
6393
|
+
}
|
|
6394
|
+
}, timeoutMs);
|
|
6395
|
+
}
|
|
6124
6396
|
startCompactionWatchdog(agentId, ap) {
|
|
6125
6397
|
this.clearCompactionWatchdog(ap);
|
|
6126
6398
|
const startedAt = Date.now();
|
|
@@ -6218,8 +6490,9 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6218
6490
|
runtime_profile_turn_outcome: control.kind === "migration" ? control.migrationDoneToolObserved ? "migration_done_observed" : "missing_migration_done" : "notice_only"
|
|
6219
6491
|
};
|
|
6220
6492
|
}
|
|
6221
|
-
startRuntimeTrace(agentId, ap, reason, messages) {
|
|
6493
|
+
startRuntimeTrace(agentId, ap, reason, messages, inputTraceAttrs = {}) {
|
|
6222
6494
|
if (ap.runtimeTraceSpan) return ap.runtimeTraceSpan;
|
|
6495
|
+
ap.runtimeTraceCounters = createRuntimeTraceCounters();
|
|
6223
6496
|
const messageControl = this.runtimeProfileTurnControlFromMessages(messages);
|
|
6224
6497
|
if (messageControl) {
|
|
6225
6498
|
this.activateRuntimeProfileTurnControl(ap, messageControl);
|
|
@@ -6234,12 +6507,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6234
6507
|
reason,
|
|
6235
6508
|
hasSession: Boolean(ap.sessionId),
|
|
6236
6509
|
...this.messagesTraceAttrs(messages),
|
|
6510
|
+
...inputTraceAttrs,
|
|
6237
6511
|
...this.runtimeProfileTurnControlTraceAttrs(ap.runtimeProfileTurnControl)
|
|
6238
6512
|
}
|
|
6239
6513
|
});
|
|
6240
6514
|
span.addEvent("daemon.turn.started", {
|
|
6241
6515
|
reason,
|
|
6242
6516
|
...this.messagesTraceAttrs(messages),
|
|
6517
|
+
...inputTraceAttrs,
|
|
6243
6518
|
...this.runtimeProfileTurnControlTraceAttrs(ap.runtimeProfileTurnControl)
|
|
6244
6519
|
});
|
|
6245
6520
|
ap.runtimeTraceSpan = span;
|
|
@@ -6279,7 +6554,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6279
6554
|
tryFlushGatedSteering(agentId, ap, reason) {
|
|
6280
6555
|
if (ap.driver.busyDeliveryMode !== "gated") return false;
|
|
6281
6556
|
if (!ap.sessionId || ap.inbox.length === 0) return false;
|
|
6282
|
-
if (reason
|
|
6557
|
+
if (reason !== "turn_end") {
|
|
6283
6558
|
if (ap.gatedSteering.toolBoundaryFlushDisabled) return false;
|
|
6284
6559
|
if (ap.gatedSteering.compacting || ap.gatedSteering.outstandingToolUses > 0) return false;
|
|
6285
6560
|
}
|
|
@@ -6288,7 +6563,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6288
6563
|
const nextMessages = ap.inbox.splice(0, ap.inbox.length);
|
|
6289
6564
|
ap.pendingNotificationCount = 0;
|
|
6290
6565
|
ap.gatedSteering.lastFlushReason = reason;
|
|
6291
|
-
if (reason
|
|
6566
|
+
if (reason !== "turn_end") {
|
|
6292
6567
|
ap.gatedSteering.inFlightBatch = {
|
|
6293
6568
|
reason,
|
|
6294
6569
|
messages: nextMessages
|
|
@@ -6306,12 +6581,46 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6306
6581
|
if (this.deliverMessagesViaStdin(agentId, ap, nextMessages, reason === "turn_end" ? "idle" : "busy")) {
|
|
6307
6582
|
return true;
|
|
6308
6583
|
}
|
|
6309
|
-
if (reason
|
|
6584
|
+
if (reason !== "turn_end") {
|
|
6310
6585
|
ap.gatedSteering.inFlightBatch = null;
|
|
6311
6586
|
}
|
|
6312
6587
|
ap.pendingNotificationCount += pendingNotificationCount || pendingMessages;
|
|
6313
6588
|
return false;
|
|
6314
6589
|
}
|
|
6590
|
+
flushCompactionBoundaryMessages(agentId, ap) {
|
|
6591
|
+
if (!ap.sessionId || !ap.driver.supportsStdinNotification || ap.inbox.length === 0) return false;
|
|
6592
|
+
if (ap.driver.busyDeliveryMode === "gated") {
|
|
6593
|
+
return this.tryFlushGatedSteering(agentId, ap, "compaction_finished");
|
|
6594
|
+
}
|
|
6595
|
+
if (ap.driver.busyDeliveryMode === "direct") {
|
|
6596
|
+
const pendingMessages = ap.inbox.length;
|
|
6597
|
+
const pendingNotificationCount = ap.pendingNotificationCount;
|
|
6598
|
+
const nextMessages = ap.inbox.splice(0, ap.inbox.length);
|
|
6599
|
+
ap.pendingNotificationCount = 0;
|
|
6600
|
+
if (ap.notificationTimer) {
|
|
6601
|
+
clearTimeout(ap.notificationTimer);
|
|
6602
|
+
ap.notificationTimer = null;
|
|
6603
|
+
}
|
|
6604
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.compaction_boundary.flush", {
|
|
6605
|
+
mode: "direct",
|
|
6606
|
+
messageCount: nextMessages.length
|
|
6607
|
+
});
|
|
6608
|
+
if (this.deliverMessagesViaStdin(agentId, ap, nextMessages, "busy")) {
|
|
6609
|
+
return true;
|
|
6610
|
+
}
|
|
6611
|
+
ap.pendingNotificationCount += pendingNotificationCount || pendingMessages;
|
|
6612
|
+
return false;
|
|
6613
|
+
}
|
|
6614
|
+
if (ap.pendingNotificationCount > 0) {
|
|
6615
|
+
if (ap.notificationTimer) {
|
|
6616
|
+
clearTimeout(ap.notificationTimer);
|
|
6617
|
+
ap.notificationTimer = null;
|
|
6618
|
+
}
|
|
6619
|
+
this.sendStdinNotification(agentId);
|
|
6620
|
+
return true;
|
|
6621
|
+
}
|
|
6622
|
+
return false;
|
|
6623
|
+
}
|
|
6315
6624
|
clearGatedInFlightBatch(agentId, ap, reason) {
|
|
6316
6625
|
if (ap.driver.busyDeliveryMode !== "gated" || !ap.gatedSteering.inFlightBatch) return;
|
|
6317
6626
|
const messageCount = ap.gatedSteering.inFlightBatch.messages.length;
|
|
@@ -6348,6 +6657,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6348
6657
|
lastActivity: ap.lastActivity,
|
|
6349
6658
|
lastActivityDetailPresent: Boolean(ap.lastActivityDetail),
|
|
6350
6659
|
lastActivityDetailKind: classifyActivityDetailForTrace(ap.lastActivityDetail),
|
|
6660
|
+
...runtimeTraceCounterAttrs(ap),
|
|
6351
6661
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_stalled")
|
|
6352
6662
|
});
|
|
6353
6663
|
this.broadcastActivity(agentId, "error", diagnostic.detail);
|
|
@@ -6380,6 +6690,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6380
6690
|
lastActivityDetailKind: classifyActivityDetailForTrace(ap.lastActivityDetail),
|
|
6381
6691
|
pendingMessages: ap.inbox.length,
|
|
6382
6692
|
recovery: "terminate_for_queued_message",
|
|
6693
|
+
...runtimeTraceCounterAttrs(ap),
|
|
6383
6694
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_stalled")
|
|
6384
6695
|
});
|
|
6385
6696
|
ap.expectedTerminationReason = "stalled_recovery";
|
|
@@ -6394,8 +6705,10 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6394
6705
|
expectedTerminationReason: "stalled_recovery",
|
|
6395
6706
|
queued_messages_count: ap.inbox.length
|
|
6396
6707
|
});
|
|
6708
|
+
this.startStalledRecoverySigtermWatchdog(agentId, ap, runtimeLabel, ap.inbox.length, staleForMs);
|
|
6397
6709
|
ap.process.kill("SIGTERM");
|
|
6398
6710
|
} catch (err) {
|
|
6711
|
+
this.clearStalledRecoverySigtermWatchdog(ap);
|
|
6399
6712
|
const reason = err instanceof Error ? err.message : String(err);
|
|
6400
6713
|
logger.warn(`[Agent ${agentId}] Failed to terminate stalled ${runtimeLabel} process: ${reason}`);
|
|
6401
6714
|
return false;
|
|
@@ -6407,6 +6720,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6407
6720
|
const ap = this.agents.get(agentId);
|
|
6408
6721
|
if (ap) {
|
|
6409
6722
|
const wasStalled = Boolean(ap.runtimeProgressStaleSince);
|
|
6723
|
+
this.noteRuntimeTraceCounter(ap, event);
|
|
6410
6724
|
this.recordRuntimeTraceEvent(agentId, ap, "runtime.event.received", { kind: event.kind });
|
|
6411
6725
|
if (wasStalled) {
|
|
6412
6726
|
this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.observed", { afterStall: true });
|
|
@@ -6496,6 +6810,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6496
6810
|
if (ap) {
|
|
6497
6811
|
ap.gatedSteering.compacting = false;
|
|
6498
6812
|
this.setGatedSteeringPhase(agentId, ap, "assistant_continuation", { event: "compaction_finished" });
|
|
6813
|
+
this.flushCompactionBoundaryMessages(agentId, ap);
|
|
6499
6814
|
ap.isIdle = false;
|
|
6500
6815
|
}
|
|
6501
6816
|
break;
|
|
@@ -6534,6 +6849,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6534
6849
|
}
|
|
6535
6850
|
this.endRuntimeTrace(ap, "ok", {
|
|
6536
6851
|
outcome: "turn-completed",
|
|
6852
|
+
...runtimeTraceCounterAttrs(ap),
|
|
6537
6853
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "turn_end")
|
|
6538
6854
|
});
|
|
6539
6855
|
if (ap.driver.terminateProcessOnTurnEnd) {
|
|
@@ -6575,10 +6891,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6575
6891
|
`[Agent ${agentId}] Disabled Claude tool-boundary gated steering after thinking-block mutation error; lastFlushReason=${ap.gatedSteering.lastFlushReason || "none"}`
|
|
6576
6892
|
);
|
|
6577
6893
|
}
|
|
6578
|
-
this.recordRuntimeTraceEvent(agentId, ap, "runtime.error",
|
|
6894
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.error", {
|
|
6895
|
+
...runtimeErrorDiagnostics.eventAttrs,
|
|
6896
|
+
...runtimeTraceCounterAttrs(ap)
|
|
6897
|
+
});
|
|
6579
6898
|
this.endRuntimeTrace(ap, "error", {
|
|
6580
6899
|
outcome: "runtime-error",
|
|
6581
6900
|
...runtimeErrorDiagnostics.spanAttrs,
|
|
6901
|
+
...runtimeTraceCounterAttrs(ap),
|
|
6582
6902
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_error")
|
|
6583
6903
|
});
|
|
6584
6904
|
if (ap.driver.supportsStdinNotification && classifyTerminalFailure(ap)) {
|
|
@@ -6601,6 +6921,29 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6601
6921
|
sendAgentStatus(agentId, status, launchId) {
|
|
6602
6922
|
this.sendToServer({ type: "agent:status", agentId, status, launchId: launchId || void 0 });
|
|
6603
6923
|
}
|
|
6924
|
+
noteRuntimeTraceCounter(ap, event) {
|
|
6925
|
+
ap.runtimeTraceCounters.events++;
|
|
6926
|
+
switch (event.kind) {
|
|
6927
|
+
case "tool_call":
|
|
6928
|
+
ap.runtimeTraceCounters.toolCalls++;
|
|
6929
|
+
break;
|
|
6930
|
+
case "tool_output":
|
|
6931
|
+
ap.runtimeTraceCounters.toolOutputs++;
|
|
6932
|
+
break;
|
|
6933
|
+
case "compaction_started":
|
|
6934
|
+
ap.runtimeTraceCounters.compactionStarts++;
|
|
6935
|
+
break;
|
|
6936
|
+
case "compaction_finished":
|
|
6937
|
+
ap.runtimeTraceCounters.compactionFinishes++;
|
|
6938
|
+
break;
|
|
6939
|
+
case "text":
|
|
6940
|
+
ap.runtimeTraceCounters.textEvents++;
|
|
6941
|
+
break;
|
|
6942
|
+
case "thinking":
|
|
6943
|
+
ap.runtimeTraceCounters.thinkingEvents++;
|
|
6944
|
+
break;
|
|
6945
|
+
}
|
|
6946
|
+
}
|
|
6604
6947
|
/** Send a batched notification to the agent via stdin about pending messages */
|
|
6605
6948
|
sendStdinNotification(agentId) {
|
|
6606
6949
|
const ap = this.agents.get(agentId);
|
|
@@ -6611,6 +6954,18 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6611
6954
|
if (count === 0) return;
|
|
6612
6955
|
if (ap.isIdle) return;
|
|
6613
6956
|
if (!ap.sessionId) return;
|
|
6957
|
+
if (ap.gatedSteering.compacting) {
|
|
6958
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.compaction_boundary.delivery_suppressed", {
|
|
6959
|
+
pendingNotificationCount: count,
|
|
6960
|
+
pendingMessages: ap.inbox.length,
|
|
6961
|
+
busyDeliveryMode: ap.driver.busyDeliveryMode
|
|
6962
|
+
});
|
|
6963
|
+
ap.pendingNotificationCount += count;
|
|
6964
|
+
logger.info(
|
|
6965
|
+
`[Agent ${agentId}] Suppressing stdin delivery until context compaction finishes; pending=${ap.inbox.length}`
|
|
6966
|
+
);
|
|
6967
|
+
return;
|
|
6968
|
+
}
|
|
6614
6969
|
if (ap.driver.busyDeliveryMode === "gated") {
|
|
6615
6970
|
this.recordGatedSteeringEvent(agentId, ap, "suppress", {
|
|
6616
6971
|
reason: "timer_notification_not_safe_boundary",
|
|
@@ -6703,6 +7058,14 @@ ${messages.map((message) => formatIncomingMessage(message, ap.driver)).join("\n"
|
|
|
6703
7058
|
|
|
6704
7059
|
Respond as appropriate. Complete all your work before stopping.
|
|
6705
7060
|
${RESPONSE_TARGET_HINT}`);
|
|
7061
|
+
const inputTraceAttrs = buildRuntimeInputTraceAttrs({
|
|
7062
|
+
source: `stdin_${mode}_delivery`,
|
|
7063
|
+
prompt,
|
|
7064
|
+
messages,
|
|
7065
|
+
sessionIdPresent: Boolean(ap.sessionId),
|
|
7066
|
+
nativeStandingPrompt: Boolean(ap.driver.supportsNativeStandingPrompt)
|
|
7067
|
+
});
|
|
7068
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.input.prepared", inputTraceAttrs);
|
|
6706
7069
|
const encoded = ap.driver.encodeStdinMessage(prompt, ap.sessionId, { mode });
|
|
6707
7070
|
if (!encoded) {
|
|
6708
7071
|
ap.inbox.unshift(...messages);
|
|
@@ -6714,6 +7077,7 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
6714
7077
|
);
|
|
6715
7078
|
this.recordDaemonTrace("daemon.agent.stdin_delivery", {
|
|
6716
7079
|
...traceAttrs,
|
|
7080
|
+
...inputTraceAttrs,
|
|
6717
7081
|
outcome: "encode_failed",
|
|
6718
7082
|
requeued_messages_count: messages.length
|
|
6719
7083
|
}, "error");
|
|
@@ -6730,6 +7094,7 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
6730
7094
|
this.ackInjectedRuntimeProfileMessages(agentId, messages, ap.launchId);
|
|
6731
7095
|
this.recordDaemonTrace("daemon.agent.stdin_delivery", {
|
|
6732
7096
|
...traceAttrs,
|
|
7097
|
+
...inputTraceAttrs,
|
|
6733
7098
|
outcome: "written",
|
|
6734
7099
|
stdin_write_attempted: true
|
|
6735
7100
|
});
|
|
@@ -6751,8 +7116,8 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
6751
7116
|
const nodes = [];
|
|
6752
7117
|
for (const entry of entries) {
|
|
6753
7118
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
6754
|
-
const fullPath =
|
|
6755
|
-
const relativePath =
|
|
7119
|
+
const fullPath = path12.join(dir, entry.name);
|
|
7120
|
+
const relativePath = path12.relative(rootDir, fullPath);
|
|
6756
7121
|
let info;
|
|
6757
7122
|
try {
|
|
6758
7123
|
info = await stat2(fullPath);
|
|
@@ -7056,8 +7421,8 @@ var ReminderCache = class {
|
|
|
7056
7421
|
// src/machineLock.ts
|
|
7057
7422
|
import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
|
|
7058
7423
|
import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
7059
|
-
import
|
|
7060
|
-
import
|
|
7424
|
+
import os7 from "os";
|
|
7425
|
+
import path13 from "path";
|
|
7061
7426
|
var INCOMPLETE_LOCK_STALE_MS = 3e4;
|
|
7062
7427
|
var DaemonMachineLockConflictError = class extends Error {
|
|
7063
7428
|
code = "DAEMON_MACHINE_LOCK_HELD";
|
|
@@ -7079,7 +7444,7 @@ function resolveDefaultMachineStateRoot() {
|
|
|
7079
7444
|
return resolveSlockHomePath("machines");
|
|
7080
7445
|
}
|
|
7081
7446
|
function ownerPath(lockDir) {
|
|
7082
|
-
return
|
|
7447
|
+
return path13.join(lockDir, "owner.json");
|
|
7083
7448
|
}
|
|
7084
7449
|
function readOwner(lockDir) {
|
|
7085
7450
|
try {
|
|
@@ -7109,8 +7474,8 @@ function acquireDaemonMachineLock(options) {
|
|
|
7109
7474
|
const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
|
|
7110
7475
|
const fingerprint = apiKeyFingerprint(options.apiKey);
|
|
7111
7476
|
const lockId = getDaemonMachineLockId(options.apiKey);
|
|
7112
|
-
const machineDir =
|
|
7113
|
-
const lockDir =
|
|
7477
|
+
const machineDir = path13.join(rootDir, lockId);
|
|
7478
|
+
const lockDir = path13.join(machineDir, "daemon.lock");
|
|
7114
7479
|
const token = randomUUID2();
|
|
7115
7480
|
mkdirSync5(machineDir, { recursive: true });
|
|
7116
7481
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
@@ -7119,7 +7484,7 @@ function acquireDaemonMachineLock(options) {
|
|
|
7119
7484
|
const owner = {
|
|
7120
7485
|
pid: process.pid,
|
|
7121
7486
|
token,
|
|
7122
|
-
hostname:
|
|
7487
|
+
hostname: os7.hostname(),
|
|
7123
7488
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7124
7489
|
serverUrl: options.serverUrl,
|
|
7125
7490
|
apiKeyFingerprint: fingerprint.slice(0, 16)
|
|
@@ -7162,8 +7527,8 @@ function acquireDaemonMachineLock(options) {
|
|
|
7162
7527
|
}
|
|
7163
7528
|
|
|
7164
7529
|
// src/localTraceSink.ts
|
|
7165
|
-
import { appendFileSync, mkdirSync as mkdirSync6, readdirSync as
|
|
7166
|
-
import
|
|
7530
|
+
import { appendFileSync, mkdirSync as mkdirSync6, readdirSync as readdirSync3, rmSync as rmSync3, statSync as statSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
7531
|
+
import path14 from "path";
|
|
7167
7532
|
var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
7168
7533
|
var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
|
|
7169
7534
|
var DEFAULT_MAX_FILES = 8;
|
|
@@ -7199,7 +7564,7 @@ var LocalRotatingTraceSink = class {
|
|
|
7199
7564
|
currentSize = 0;
|
|
7200
7565
|
sequence = 0;
|
|
7201
7566
|
constructor(options) {
|
|
7202
|
-
this.traceDir =
|
|
7567
|
+
this.traceDir = path14.join(options.machineDir, "traces");
|
|
7203
7568
|
this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
|
|
7204
7569
|
const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
|
|
7205
7570
|
const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
|
|
@@ -7229,7 +7594,7 @@ var LocalRotatingTraceSink = class {
|
|
|
7229
7594
|
const nowMs = this.nowMsProvider();
|
|
7230
7595
|
const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
|
|
7231
7596
|
if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
|
|
7232
|
-
this.currentFile =
|
|
7597
|
+
this.currentFile = path14.join(
|
|
7233
7598
|
this.traceDir,
|
|
7234
7599
|
`daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
|
|
7235
7600
|
);
|
|
@@ -7240,11 +7605,11 @@ var LocalRotatingTraceSink = class {
|
|
|
7240
7605
|
}
|
|
7241
7606
|
}
|
|
7242
7607
|
pruneOldFiles() {
|
|
7243
|
-
const files =
|
|
7608
|
+
const files = readdirSync3(this.traceDir).filter((name) => name.startsWith("daemon-trace-") && name.endsWith(".jsonl")).sort();
|
|
7244
7609
|
const excess = files.length - this.maxFiles;
|
|
7245
7610
|
if (excess <= 0) return;
|
|
7246
7611
|
for (const file of files.slice(0, excess)) {
|
|
7247
|
-
rmSync3(
|
|
7612
|
+
rmSync3(path14.join(this.traceDir, file), { force: true });
|
|
7248
7613
|
}
|
|
7249
7614
|
}
|
|
7250
7615
|
};
|
|
@@ -7331,11 +7696,11 @@ function isDiagnosticErrorAttr(key) {
|
|
|
7331
7696
|
import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
|
|
7332
7697
|
import { gzipSync } from "zlib";
|
|
7333
7698
|
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
7334
|
-
import
|
|
7699
|
+
import path15 from "path";
|
|
7335
7700
|
|
|
7336
7701
|
// src/directUploadCapability.ts
|
|
7337
|
-
function joinUrl(base,
|
|
7338
|
-
return `${base.replace(/\/+$/, "")}${
|
|
7702
|
+
function joinUrl(base, path17) {
|
|
7703
|
+
return `${base.replace(/\/+$/, "")}${path17}`;
|
|
7339
7704
|
}
|
|
7340
7705
|
function jsonHeaders(apiKey) {
|
|
7341
7706
|
return {
|
|
@@ -7554,7 +7919,7 @@ var DaemonTraceBundleUploader = class {
|
|
|
7554
7919
|
}, nextMs);
|
|
7555
7920
|
}
|
|
7556
7921
|
async findUploadCandidates() {
|
|
7557
|
-
const traceDir =
|
|
7922
|
+
const traceDir = path15.join(this.options.machineDir, "traces");
|
|
7558
7923
|
let names;
|
|
7559
7924
|
try {
|
|
7560
7925
|
names = await readdir3(traceDir);
|
|
@@ -7566,8 +7931,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
7566
7931
|
const currentFile = this.options.currentFileProvider?.();
|
|
7567
7932
|
const candidates = [];
|
|
7568
7933
|
for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
|
|
7569
|
-
const file =
|
|
7570
|
-
if (currentFile &&
|
|
7934
|
+
const file = path15.join(traceDir, name);
|
|
7935
|
+
if (currentFile && path15.resolve(file) === path15.resolve(currentFile)) continue;
|
|
7571
7936
|
if (await this.isUploaded(file)) continue;
|
|
7572
7937
|
try {
|
|
7573
7938
|
const info = await stat3(file);
|
|
@@ -7641,8 +8006,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
7641
8006
|
}
|
|
7642
8007
|
}
|
|
7643
8008
|
uploadStatePath(file) {
|
|
7644
|
-
const stateDir =
|
|
7645
|
-
return
|
|
8009
|
+
const stateDir = path15.join(this.options.machineDir, "trace-uploads");
|
|
8010
|
+
return path15.join(stateDir, `${path15.basename(file)}.uploaded.json`);
|
|
7646
8011
|
}
|
|
7647
8012
|
async isUploaded(file) {
|
|
7648
8013
|
try {
|
|
@@ -7654,9 +8019,9 @@ var DaemonTraceBundleUploader = class {
|
|
|
7654
8019
|
}
|
|
7655
8020
|
async markUploaded(file, metadata) {
|
|
7656
8021
|
const stateFile = this.uploadStatePath(file);
|
|
7657
|
-
await mkdir2(
|
|
8022
|
+
await mkdir2(path15.dirname(stateFile), { recursive: true, mode: 448 });
|
|
7658
8023
|
await writeFile2(stateFile, `${JSON.stringify({
|
|
7659
|
-
file:
|
|
8024
|
+
file: path15.basename(file),
|
|
7660
8025
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7661
8026
|
...metadata
|
|
7662
8027
|
}, null, 2)}
|
|
@@ -7695,23 +8060,23 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
|
|
|
7695
8060
|
}
|
|
7696
8061
|
}
|
|
7697
8062
|
function resolveChatBridgePath(moduleUrl = import.meta.url) {
|
|
7698
|
-
const dirname =
|
|
7699
|
-
const jsPath =
|
|
8063
|
+
const dirname = path16.dirname(fileURLToPath(moduleUrl));
|
|
8064
|
+
const jsPath = path16.resolve(dirname, "chat-bridge.js");
|
|
7700
8065
|
try {
|
|
7701
8066
|
accessSync(jsPath);
|
|
7702
8067
|
return jsPath;
|
|
7703
8068
|
} catch {
|
|
7704
|
-
return
|
|
8069
|
+
return path16.resolve(dirname, "chat-bridge.ts");
|
|
7705
8070
|
}
|
|
7706
8071
|
}
|
|
7707
8072
|
function resolveSlockCliPath(moduleUrl = import.meta.url) {
|
|
7708
|
-
const thisDir =
|
|
7709
|
-
const bundledDistPath =
|
|
8073
|
+
const thisDir = path16.dirname(fileURLToPath(moduleUrl));
|
|
8074
|
+
const bundledDistPath = path16.resolve(thisDir, "cli", "index.js");
|
|
7710
8075
|
try {
|
|
7711
8076
|
accessSync(bundledDistPath);
|
|
7712
8077
|
return bundledDistPath;
|
|
7713
8078
|
} catch {
|
|
7714
|
-
const workspaceDistPath =
|
|
8079
|
+
const workspaceDistPath = path16.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
|
|
7715
8080
|
accessSync(workspaceDistPath);
|
|
7716
8081
|
return workspaceDistPath;
|
|
7717
8082
|
}
|
|
@@ -7890,7 +8255,7 @@ var DaemonCore = class {
|
|
|
7890
8255
|
}
|
|
7891
8256
|
resolveMachineStateRoot() {
|
|
7892
8257
|
if (this.options.machineStateDir) return this.options.machineStateDir;
|
|
7893
|
-
if (this.options.dataDir) return
|
|
8258
|
+
if (this.options.dataDir) return path16.join(path16.dirname(this.options.dataDir), "machines");
|
|
7894
8259
|
return resolveDefaultMachineStateRoot();
|
|
7895
8260
|
}
|
|
7896
8261
|
shouldEnableLocalTrace() {
|
|
@@ -8210,8 +8575,8 @@ var DaemonCore = class {
|
|
|
8210
8575
|
capabilities: ["agent:start", "agent:stop", "agent:deliver", "workspace:files"],
|
|
8211
8576
|
runtimes,
|
|
8212
8577
|
runningAgents: runningAgentIds,
|
|
8213
|
-
hostname: this.options.hostname ??
|
|
8214
|
-
os: this.options.osDescription ?? `${
|
|
8578
|
+
hostname: this.options.hostname ?? os8.hostname(),
|
|
8579
|
+
os: this.options.osDescription ?? `${os8.platform()} ${os8.arch()}`,
|
|
8215
8580
|
daemonVersion: this.daemonVersion
|
|
8216
8581
|
});
|
|
8217
8582
|
this.recordDaemonTrace("daemon.ready.sent", {
|